3
votes

Currently we are planning to have one "Draft" version of contract which will not be sent to the counterparty and the initiator can make any changes before send it to the network, so this should be as one "unshared fact". As we know Corda and the vault is used for shared facts, so here I am not sure whether we can still use vault to store this type of "unshared fact", my idea as below and I already can make this work in my local based on the tutorial CorDapp, but would like to get some inputs from other Corda team/experts.

The main change is in the initiator flow:

  • Initiate the create command's only with the initiator's key
  • Do not invoke the "CollectSignaturesFlow" so this will not be sent to any others
  • Invoke "FinalityFlow" after the verify(), so this will be committed to the ledger

Below are the codes for above mentioned points.

override fun call(): SignedTransaction  {
    // We create a transaction builder
    val txBuilder = TransactionBuilder()
    val notaryIdentity = serviceHub.networkMapCache.getAnyNotary()
    txBuilder.notary = notaryIdentity

    // We create the transaction's components.
    val ourIdentity = serviceHub.myInfo.legalIdentity
    val iou = TemplateState(iouValue, ourIdentity, ourIdentity)
    val txCommand = Command(TemplateContract.Create(), listOf(ourIdentity.owningKey))

    // Adding the item's to the builder.
    txBuilder.withItems(iou, txCommand)

    // Verifying the transaction.
    txBuilder.toWireTransaction().toLedgerTransaction(serviceHub).verify()

    // Signing the transaction.
    val partSignedTx = serviceHub.signInitialTransaction(txBuilder)

    // Finalising the transaction.
    return subFlow(FinalityFlow(partSignedTx)).single()
}
1

1 Answers

3
votes

You can indeed create an unshared fact in Corda! The key here is in the state's participants list. Just add yourself in the participants list and only your public key to the command. Here's a simple example:

//Contract and State.
class UnsharedFact: Contract {
    override val legalContractReference: SecureHash = SecureHash.zeroHash
    override fun verify(tx: TransactionForContract) = Unit // Stubbed out.
    class Create: CommandData

    data class State(val parties: Set<Party>): ContractState {
        override val contract: Contract get() = UnsharedFact()
        override val participants: List<AbstractParty> get() = parties.toList()
        fun addParty(newParty: Party) = copy(parties = parties + newParty)
    }
}

// Create flow.
@InitiatingFlow
@StartableByRPC
class CreateUnsharedFact(): FlowLogic<SignedTransaction>() {
    @Suspendable
    override fun call(): SignedTransaction {
        val me = serviceHub.myInfo.legalIdentity
        val notary = serviceHub.networkMapCache.getAnyNotary()
        val state = UnsharedFact.State(setOf(me))
        val command = Command(UnsharedFact.Create(), listOf(me.owningKey))
        val utx = TransactionBuilder(notary = notary).withItems(state, command)
        val stx = serviceHub.signInitialTransaction(utx)
        return subFlow(FinalityFlow(stx)).single()
    }
}

When FinalityFlow is called, you will be the only node that receives the output state.

If you wish to subsequently involve another party then you can create a new version of the state using the addParty method on UnsharedFact.State. Then, create a new transaction, adding the original state as the input and the new version (with the new party) as the output. When this transaction is finalised (notarised) then both parties will have a copy in their respective vaults. Now, I guess the name 'UnsharedFact' is inappropriate :)

You can also remove parties using a similar approach.