mirror of
https://github.com/corda/corda.git
synced 2025-01-31 00:24:59 +00:00
core: Fix tx resolution bug by topological sorting of downloaded transactions
This commit is contained in:
parent
287326b118
commit
d6f82637ad
@ -54,11 +54,13 @@ class DummyContract : Contract {
|
||||
return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owner.party.owningKey))
|
||||
}
|
||||
|
||||
fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: PublicKey): TransactionBuilder {
|
||||
val priorState = prior.state.data
|
||||
fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: PublicKey) = move(listOf(prior), newOwner)
|
||||
fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: PublicKey): TransactionBuilder {
|
||||
require(priors.size > 0)
|
||||
val priorState = priors[0].state.data
|
||||
val (cmd, state) = priorState.withNewOwner(newOwner)
|
||||
return TransactionType.General.Builder(notary = prior.state.notary).withItems(
|
||||
/* INPUT */ prior,
|
||||
return TransactionType.General.Builder(notary = priors[0].state.notary).withItems(
|
||||
/* INPUTS */ *priors.toTypedArray(),
|
||||
/* COMMAND */ Command(cmd, priorState.owner),
|
||||
/* OUTPUT */ state
|
||||
)
|
||||
|
@ -32,6 +32,44 @@ class ResolveTransactionsProtocol(private val txHashes: Set<SecureHash>,
|
||||
|
||||
companion object {
|
||||
private fun dependencyIDs(wtx: WireTransaction) = wtx.inputs.map { it.txhash }.toSet()
|
||||
|
||||
private fun topologicalSort(transactions: Iterable<SignedTransaction>): List<SignedTransaction> {
|
||||
|
||||
// Construct txhash -> dependent-txhashes map
|
||||
val forwardGraph = HashMap<SecureHash, HashSet<SecureHash>>()
|
||||
transactions.forEach { tx ->
|
||||
tx.tx.inputs.forEach { input ->
|
||||
forwardGraph.getOrPut(input.txhash) { HashSet() }.add(tx.id)
|
||||
}
|
||||
}
|
||||
|
||||
// Construct txhash -> tx map
|
||||
val transactionMap = HashMap<SecureHash, SignedTransaction>()
|
||||
transactions.forEach { transactionMap[it.tx.id] = it }
|
||||
|
||||
val visited = HashSet<SecureHash>()
|
||||
val result = LinkedList<SignedTransaction>()
|
||||
|
||||
fun visit(transactionHash: SecureHash) {
|
||||
if (transactionHash in visited) {
|
||||
return
|
||||
}
|
||||
visited.add(transactionHash)
|
||||
forwardGraph[transactionHash]?.forEach {
|
||||
visit(it)
|
||||
}
|
||||
result.addFirst(transactionMap[transactionHash]!!)
|
||||
}
|
||||
|
||||
transactions.forEach {
|
||||
visit(it.tx.id)
|
||||
}
|
||||
|
||||
require(result.size == transactionMap.size)
|
||||
require(visited.size == transactionMap.size)
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ExcessivelyLargeTransactionGraph() : Exception()
|
||||
@ -60,7 +98,7 @@ class ResolveTransactionsProtocol(private val txHashes: Set<SecureHash>,
|
||||
|
||||
@Suspendable
|
||||
override fun call(): List<LedgerTransaction> {
|
||||
val newTxns: Iterable<SignedTransaction> = downloadDependencies(txHashes)
|
||||
val newTxns: Iterable<SignedTransaction> = topologicalSort(downloadDependencies(txHashes))
|
||||
|
||||
// For each transaction, verify it and insert it into the database. As we are iterating over them in a
|
||||
// depth-first order, we should not encounter any verification failures due to missing data. If we fail
|
||||
|
@ -97,6 +97,30 @@ class ResolveTransactionsProtocolTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `triangle of transactions resolves fine`() {
|
||||
val stx1 = makeTransactions().first
|
||||
|
||||
val stx2 = DummyContract.move(stx1.tx.outRef(0), MINI_CORP_PUBKEY).run {
|
||||
signWith(MEGA_CORP_KEY)
|
||||
signWith(DUMMY_NOTARY_KEY)
|
||||
toSignedTransaction()
|
||||
}
|
||||
|
||||
val stx3 = DummyContract.move(listOf(stx1.tx.outRef(0), stx2.tx.outRef(0)), MINI_CORP_PUBKEY).run {
|
||||
signWith(MEGA_CORP_KEY)
|
||||
signWith(DUMMY_NOTARY_KEY)
|
||||
toSignedTransaction()
|
||||
}
|
||||
|
||||
a.services.recordTransactions(stx2, stx3)
|
||||
|
||||
val p = ResolveTransactionsProtocol(setOf(stx3.id), a.info.identity)
|
||||
val future = b.services.startProtocol("resolve", p)
|
||||
net.runNetwork()
|
||||
future.get()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun attachment() {
|
||||
val id = a.services.storageService.attachments.importAttachment("Some test file".toByteArray().opaque().open())
|
||||
|
Loading…
x
Reference in New Issue
Block a user