Dynamically adding participants to state in Corda 4.0

Viewed 294

Can participants be dynamically added to state inside a flow so that state will be stored in thirdParty vault without using StatesToRecord.ALL_VISIBLE in ReceiveFinalityFlow?

We have done same thing in Corda 2.0, it is not working in Corda 4.0.

Is it not supported in Corda 3.2 onwards? I see @KeepForDJVM is added to ContractState.

I tried to add participants dynamically in IOUState as [iouState.participants.add(thirdParty)] after participants in IOUState is updated as mutableList as [override val participants: MutableList<AbstractParty> = mutableListOf(lender, borrower)] so that IOUState will be stored in ThirdParty vault as well. I am passing flow sessions of both borrower and thirdParty to CollectSigntaureFlow and FinalityFlow as well. IOUFlowTests [flow records the correct IOU in both parties' vaults] is failed with iouState is not found in thridParty vault.

IOUState:
@BelongsToContract(IOUContract::class)
data class IOUState(val value: Int,
                    val lender: Party,
                    val borrower: Party,
                    val thirdParty: Party,
                    override val linearId: UniqueIdentifier = UniqueIdentifier()):
        LinearState, QueryableState {
    /** The public keys of the involved parties. */
    //override val participants: MutableList<AbstractParty> get() = mutableListOf(lender, borrower)
    override val participants = mutableListOf(lender, borrower)


ExampleFlow:
var iouState = IOUState(iouValue, serviceHub.myInfo.legalIdentities.first(), otherParty, thirdParty)

iouState.participants.add(thirdParty)

val txCommand = Command(IOUContract.Commands.Create(), iouState.participants.map { it.owningKey })

val counterparties = iouState.participants.map { it as Party }.filter { it.owningKey != ourIdentity.owningKey }.toSet()

counterparties.forEach { p -> flowSessions.add(initiateFlow(p))}

val fullySignedTx = subFlow(CollectSignaturesFlow(partSignedTx, flowSessions, GATHERING_SIGS.childProgressTracker()))

            // Stage 5.
            progressTracker.currentStep = FINALISING_TRANSACTION
            // Notarise and record the transaction in both parties' vaults.
            return subFlow(FinalityFlow(fullySignedTx, flowSessions, FINALISING_TRANSACTION.childProgressTracker()))

Both Counterparties Borrower and ThirdParty receives flow and sign transaction, but does not see thirdParty in Participants list and not stored in ThirdParty vault.

I am expecting ThirdParty should be in Participants list and IOUState should be stored in ThirdParty Vault as well.

1 Answers

In Corda, states are immutable. This means that you cannot dynamically add participants to a given state in the body of a flow. There are other solutions, however, to informing a new third party of the state!

There are two ways to accomplish your goals here:

  1. Create a new IOUState tx output with an updated participant list.

In the body of the flow, you should create a new IOUState with an updated list of participants. You will have to update the IOUState so that participants is a value in the primary constructure. Then you might use a helper method like this to add a participant:

fun addParticipant(partyToAdd: Party): IOUState = copy(participants + partyToAdd)

Here's the important part: you must then include the old IOUState as an input to this transaction and the new IOUState as an output. Corda is based on the UTXO model - the only way to update a state is to mark it as history (use it as an input) and then persist an updated version to the ledger.

Note: as a participant, the informed party will now be able to propose changes to this IOUState - these must be accounted for in the Corda Contract.

  1. Use the SendStateAndRefFlow (Likely the better solution for your issue)

The SendStateAndRefFlow will (as specified in its name) send a state and its associated stateRef to the receiving node. The counterparty (receiving node) must use ReceiveStateAndRefFlow at the correct point in the flow conversation.

subFlow(new SendStateAndRefFlow(counterpartySession, dummyStates));

Both of these methods will cause the receiving node to validate the dependencies of the state (all of the inputs and transactions that comprise its history)

Related