1
votes

Recently I came across a problem when it seems that when the flow ends on initiator's node, and immediately after that I query the output state of the transaction in vaults of all transaction participant nodes, it turns out that the state is present only on initiator's node, and only after a while it appears in vaults of other participants nodes. Reading the documentations here "Send the transaction to the counterparty for recording", and it does not say that it will wait until counterparty will successfully record the transaction and its states to their vault, which sort of confirms that the issue that I am facing is actually the way Corda is implemented and not a bug or so.

On the other hand, it seems not that logical to end a flow without being sure that on the counterparty node everything was finished successfully and all the states are written in their vaults.

And also I looked into the code, and reached to this method in ServiceHubInternal, which seems to be responsible for recording states into vault.

fun recordTransactions(statesToRecord: StatesToRecord,
                           txs: Collection<SignedTransaction>,
                           validatedTransactions: WritableTransactionStorage,
                           stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage,
                           vaultService: VaultServiceInternal,
                           database: CordaPersistence) {

        database.transaction {
            require(txs.isNotEmpty()) { "No transactions passed in for recording" }

            val orderedTxs = topologicalSort(txs)
            val (recordedTransactions, previouslySeenTxs) = if (statesToRecord != StatesToRecord.ALL_VISIBLE) {
                orderedTxs.filter(validatedTransactions::addTransaction) to emptyList()
            } else {
                orderedTxs.partition(validatedTransactions::addTransaction)
            }
            val stateMachineRunId = FlowStateMachineImpl.currentStateMachine()?.id
            if (stateMachineRunId != null) {
                recordedTransactions.forEach {
                    stateMachineRecordedTransactionMapping.addMapping(stateMachineRunId, it.id)
                }
            } else {
                log.warn("Transactions recorded from outside of a state machine")
            }

            vaultService.notifyAll(statesToRecord, recordedTransactions.map { it.coreTransaction }, previouslySeenTxs.map { it.coreTransaction })
        }
    }

And it does not seem to me that this method is doing something async, so I am really confused. And so the actual question is:

Does Initiator flow in Corda actually waits until all the relevant states are recorded in vaults of all participant nodes before it finishes, or it finishes right after it sends the states to participant nodes for recording without waiting for a confirmation from their side that states were recorded?

Edited

So in case that Corda by default does not wait for counterparty flows to store states in their vaults, but my implementation needs this behavior anyways, would it be good solution to implement the following:

At the very end of Initiator flow, before returning from method call receiveAll in order to suspend and wait, then in the very end of the receiver flow, before returning from method do a vault query with trackBy to wait until the state of an interest is recorded in the vault, and whenever it is, call sendAll to notify the initiator's receiveAll method. And finish the initiator's flow only when it has received confirmation from all receivers..

Would it be a normal approach to solve this problem? can it have any drawbacks or side effects that you can think of?

2

2 Answers

2
votes

Corda is able to handle your scenario, it is actually explained here under the Error handling behaviour section; below is an excerpt, but I recommend reading the full section:

To recover from this scenario, the receiver’s finality handler will automatically be sent to the Flow Hospital where it’s suspended and retried from its last checkpoint upon node restart

1
votes

The Initiator flow is not responsible for the storage of the states in the Responder's vault. So, there is no storage confirmation from the Responder, since it has already checked the transaction and provided signatures. So, from the Initiator's point of view it's all good once the Transaction has been notarised and stored on its side, it is up to the Responder to manage errors in its storage phase, as mentioned in the previous comment.