userAG
userAG

Reputation: 99

Corda returning Multiple transactions in a single flow call ()

I have the scenario where One party needs to read a list of states ( DEAL STATE for ex.), Then iterate through the list to find out the matching records. If it is matching, we need to create new output state by combining the matched fields.

So once the matching is performed within the loop, we can get the list of input and output states.

But I am confused on collecting the signature from other parties since the counterparties will be different for each of the record. Also, how will I call the finality flow for multiple transactions within single flow call method?

@joel, Another problem - Suppose outputstate1 particicpants are say A, B, C, and outputstate2 paticipants are B, C ,D ,ie B & C are involved in 2 transactions. So within matching states loop, when we make flowsessions map, it will have the signers are A, B, C, D. But when we call CollectSignaturesFlow, we need to pass each of the partialsigned trxn and sessions. So how to pass the session corresponding to a trxn ?

Upvotes: 0

Views: 863

Answers (1)

Joel
Joel

Reputation: 23140

Here's an example of how we could collect the signatures for each state:

val flowSessionMap = mutableMapOf<Party, FlowSession>()

val fullySignedTransactions = matchingStates.forEach { matchingState ->
    val requiredSigners: List<Party> = TODO("Derive this from the matching state somehow.")
    val signedTransaction: SignedTransaction = TODO("Build transaction.")

    val sessions = requiredSigners.map { signer ->
        flowSessionMap.getOrPut(signer) {
            initiateFlow(signer)
        }
    }

    val fullySignedTransaction = subFlow(CollectSignaturesInitiatingFlow(
        signedTransaction, sessions)
    )
}

Where CollectSignaturesInitiatingFlow is defined as follows:

@InitiatingFlow
class CollectSignaturesInitiatingFlow(val signedTransaction: SignedTransaction, val sessions: List<FlowSession>): FlowLogic<SignedTransaction>() {
    override fun call(): SignedTransaction {
        return subFlow(CollectSignaturesFlow(signedTransaction, sessions))
    }
}

And the responder for CollectSignaturesInitiatingFlow is defined as follows:

@InitiatedBy(CollectSignaturesInitiatingFlow::class)
class CollectSignaturesInitiatingFlowResponder(val otherPartyFlow: FlowSession) : FlowLogic<SignedTransaction>() {
    @Suspendable
    override fun call(): SignedTransaction {
        val signTransactionFlow = object : SignTransactionFlow(otherPartyFlow) {
            override fun checkTransaction(stx: SignedTransaction) {
                TODO("Check the transaction here.")
            }
        }

        return subFlow(signTransactionFlow)
    }
}

Note that:

  • We're being careful to only create one session per counterparty. As of Corda 3, an error will be thrown if a flow creates multiple sessions per counterparty
  • We're wrapping the call to CollectSignaturesFlow in CollectSignaturesInitiatingFlow. Why? In Corda, there are two types of flow: Initiating and inlined. Each Initiating flow instance has a unique ID, while each inlined flow inherits the ID of the flow that called it as a subflow. As of Corda 3, an exception is thrown if a responder is invoked twice for the same flow ID. By wrapping CollectSignaturesFlow (an inlined flow) inside CollectSignaturesInitiatingFlow (an Initiating flow), we create a new flow ID for each attempt to gather signatures, and no exception is thrown

Once you have the fully-signed transactions, you can call FinalityFlow in a loop:

for (transaction in fullySignedTransactions) {
    subFlow(FinalityFlow(transaction))
}

Upvotes: 1

Related Questions