Example Code to Create Output State


(satish kumar) #1

Can some explain the flow along with sample codes for creating a single output state based on an input state?


(Roger Willis) #2

The flow required would be very similar to the TwoPartyDealFlow or the flow in the cordapp-template.

You’ll need a StateAndRef to the input state required. Perhaps this reference could be provided as a parameter to your flow or it could be inferred from some kind of ID provided to the flow which will be used to look up the State from the vault.

You can then add the StateAndRef to a transaction using the withItems method, the implementation is as such:

    fun withItems(vararg items: Any): TransactionBuilder {
    for (t in items) {
        when (t) {
            is StateAndRef<*> -> addInputState(t)
            is TransactionState<*> -> addOutputState(t)
            is ContractState -> addOutputState(t)
            is Command -> addCommand(t)
            is CommandData -> throw IllegalArgumentException("You passed an instance of CommandData, but that lacks the pubkey. You need to wrap it in a Command object first.")
            else -> throw IllegalArgumentException("Wrong argument type: ${t.javaClass}")
        }
    }
    return this
}

You can use it like this:

        return TransactionType.General.Builder(notaryRef).withItems(inputStateAndRef, outputState, Command(Commands.DoSomething(), listOfPubKeys))

(satish kumar) #3

I am trying to change the state of an existing object, in this case order number 1. Below is the Flow code which results in error (null) during execution. of the line " subFlow(FinalityFlow(ntx, setOf(otherParty)))"
What is causing this issue?.

object TestFlow2 {

class Initiator2(val po: DealState,
                 val otherParty: Party,
                 override val progressTracker: ProgressTracker = Initiator2.tracker()): FlowLogic<TestFlowResult2>() {

    companion object {
        object CONSTRUCTING_OFFER : ProgressTracker.Step("Constructing proposed purchase order.")
        object SENDING_OFFER : ProgressTracker.Step("Sending purchase order to seller for review.")
        object RECEIVED_PARTIAL_TRANSACTION : ProgressTracker.Step("Received partially signed transaction from seller.")
        object VERIFYING : ProgressTracker.Step("Verifying signatures and contract constraints.")
        object SIGNING : ProgressTracker.Step("Signing transaction with our private key.")
        object NOTARY : ProgressTracker.Step("Obtaining notary signature.")
        object RECORDING : ProgressTracker.Step("Recording transaction in vault.")
        object SENDING_FINAL_TRANSACTION : ProgressTracker.Step("Sending fully signed transaction to seller.")

        fun tracker() = ProgressTracker(
                CONSTRUCTING_OFFER,
                SENDING_OFFER,
                RECEIVED_PARTIAL_TRANSACTION,
                VERIFYING,
                SIGNING,
                NOTARY,
                RECORDING,
                SENDING_FINAL_TRANSACTION
        )
    }

    @Suspendable
    override fun call(): TestFlowResult2 {
        try {
            val myKeyPair = serviceHub.legalIdentityKey
            val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity
            val notaryPubKey = notary.owningKey
            // Stage 1.
            progressTracker.currentStep = CONSTRUCTING_OFFER
            // Construct a state object which encapsulates the TestOrder object.
            // We add the public keys for us and the counterparty as well as a reference to the contract code.

            val cashStates = serviceHub.vaultService.currentVault.statesOfType<TestOrderState>()
            println("$cashStates")
            // 1.Input States are arrived
            val inputStates = cashStates.filter {
                val state = it.state.data
                (state.po.orderNumber == 1)
            }
            println("$inputStates")
            val inHashTXIds = inputStates[0].ref.txhash

            subFlow(ResolveTransactionsFlow(setOf(inHashTXIds), otherParty))

            val offerMessage = TransactionState(po, notary)


            val ptxNew = TransactionType.General.Builder(notary)
            ptxNew.addInputState(inputStates[0])
            ptxNew.addOutputState(offerMessage)
            ptxNew.addCommand(TestOrderContract.Commands.Place(),myKeyPair.public.composite)
            ptxNew.signWith(serviceHub.legalIdentityKey)

            val signedTransaction = ptxNew.toSignedTransaction(checkSufficientSignatures = false)

            // Stage 2.
            progressTracker.currentStep = SENDING_OFFER
            // Send the state across the wire to the designated counterparty.
            // -----------------------
            // Flow jumps to Acceptor.
            // -----------------------
            progressTracker.currentStep = RECEIVED_PARTIAL_TRANSACTION
            val allPartySignedTx = sendAndReceive<DigitalSignature.WithKey>(otherParty, signedTransaction).unwrap {

                val withNewSignature = signedTransaction + it
                // check all signatures are present except the notary
                withNewSignature.verifySignatures(withNewSignature.tx.notary!!.owningKey)

                progressTracker.currentStep = VERIFYING

                withNewSignature // return the almost complete transaction
            }

            val notarySignature = subFlow(NotaryFlow.Client(allPartySignedTx))

            val ntx = allPartySignedTx + notarySignature
            // Stage 8.
            progressTracker.currentStep = SIGNING

            progressTracker.currentStep = NOTARY

            progressTracker.currentStep = RECORDING
            subFlow(FinalityFlow(ntx, setOf(otherParty)))
            // Stage 11.
            progressTracker.currentStep = SENDING_FINAL_TRANSACTION
            // Send a copy of the transaction to our counter-party.
            //send(otherParty, ptx)
            return TestFlowResult2.Success("Transaction id ${allPartySignedTx.id} committed to ledger.")


        } catch(ex: Exception) {
            // Just catch all exception types.
            return TestFlowResult2.Failure(ex.message)
        }
    }


}

class Acceptor2(val otherParty: Party,
                override val progressTracker: ProgressTracker = Acceptor2.tracker()): FlowLogic<Unit>() {
    companion object {
        object WAITING_FOR_PROPOSAL : ProgressTracker.Step("Receiving proposed purchase order from buyer.")
        object GENERATING_TRANSACTION : ProgressTracker.Step("Generating transaction based on proposed purchase order.")
        object SIGNING : ProgressTracker.Step("Signing proposed transaction with our private key.")
        object SEND_TRANSACTION_AND_WAIT_FOR_RESPONSE : ProgressTracker.Step("Sending partially signed transaction to buyer and wait for a response.")
        object VERIFYING_TRANSACTION : ProgressTracker.Step("Verifying signatures and contract constraints.")
        object RECORDING : ProgressTracker.Step("Recording transaction in vault.")

        fun tracker() = ProgressTracker(
                WAITING_FOR_PROPOSAL,
                GENERATING_TRANSACTION,
                SIGNING,
                SEND_TRANSACTION_AND_WAIT_FOR_RESPONSE,
                VERIFYING_TRANSACTION,
                RECORDING
        )
    }

    @Suspendable
    override fun call() {
        try {
            // Prep.
            // Obtain a reference to our key pair.
            val keyPair = serviceHub.legalIdentityKey
            // Stage 3.
            progressTracker.currentStep = WAITING_FOR_PROPOSAL
            // All messages come off the wire as UntrustworthyData. You need to 'unwrap' it. This is an appropriate
            // place to perform some validation over what you have just received.
            val message = receive<SignedTransaction>(otherParty).unwrap {
                it // return the SignedTransaction
            }


            progressTracker.currentStep = GENERATING_TRANSACTION
            val ourSignature = serviceHub.legalIdentityKey.signWithECDSA(message.id.bytes)
            progressTracker.currentStep = SIGNING
            // send the other side our signature.


            progressTracker.currentStep = SEND_TRANSACTION_AND_WAIT_FOR_RESPONSE
            progressTracker.currentStep = VERIFYING_TRANSACTION
            // Record the transaction.
            progressTracker.currentStep = RECORDING
            send(otherParty, ourSignature)
        }
        catch (ex: Exception) {
        }
    }

}

}


(Roger Willis) #4

Couple of things to check:

  1. Is inputStates[0] definitely a valid reference, you can println the state?

  2. po is the amended state? Does it conform to the rules of the contract code?

  3. I notice you are using the Place command, is this right? Or did you change the logic in Place?

  4. This is probably the issue: offerMessage is a tuple containing a notary ref and a TransactionState. You just need the TransactionState, i.e. po. Not the tuple. See the definition of TransactionBuilder.addOutputState:

    fun addOutputState(state: TransactionState<*>): Int {
    check(currentSigs.isEmpty())
    outputs.add(state)
    return outputs.size - 1
    }

  5. If the state is unilaterally amended, you don’t need to send the signed transaction to the other side before you notarise. it. The flow on the other side doesn’t do anything! Assuming the other signers are listed in paricipants for the state object in question then the final notarsied transaction is sent to the otherside when FinalityFlow is called. It does this via BroadcastTransactionFlow.
    6 ) you are notarising via NotaryFlow then calling the FinalityFlow. You don’t need the NotaryFlow subFlow as the FinalityFlow calls the NotaryFlow. Definition of FinalityFlow:

     @Suspendable
    

    override fun call() {
    // TODO: Resolve the tx here: it’s probably already been done, but re-resolution is a no-op and it’ll make the API more forgiving.

     progressTracker.currentStep = NOTARISING
     // Notarise the transaction if needed
     val notarisedTransaction = if (needsNotarySignature(transaction)) {
         val notarySig = subFlow(NotaryFlow.Client(transaction))
         transaction.withAdditionalSignature(notarySig)
     } else {
         transaction
     }
    
     // Let everyone else know about the transaction
     progressTracker.currentStep = BROADCASTING
     subFlow(BroadcastTransactionFlow(notarisedTransaction, participants))
    

    }

  6. Can you dump the contents of ntx?

Also, a stack trace always helps…!

Cheers


(satish kumar) #5

Roger,

  1. yes, it is a valid reference
  2. po has the amended state
  3. removed the Place command
    4)Unable to add only TransactionState(po) on addOutputState,ie expects notary ref as well.
  4. &6) removed subflow call for notoaryflow

I am getting the below error. Invalid Transaction

InputState[0]
StateAndRef(state=TransactionState(data=TestOrderState(po=TestOrder(orderNumber=1, deliveryDate=Fri Jan 20 05:30:00 IST 2017, deliveryAddress=Address(city=NEW CASTLE, sta=NEW YORK, country=UK), items=[Item(name=watches, amount=9)]), buyer=NodeA, seller=NodeB, contract=com.test.contract.TestOrderContract@40e6fccd, linearId=1_429cb8fa-3a95-4595-a757-16d705eb83aa), notary=corda.notary.validating|Controller), ref=EC491414E19D1C3D216AF3D00F00B776628B8FE3222D890CD2A677B6DD607DF3(0))

OuputState
TransactionState(data=TestOrderState(po=TestOrder(orderNumber=1, deliveryDate=Fri Jan 20 05:30:00 IST 2017, deliveryAddress=Address(city=NEW CASTLE, sta=NEW YORK, country=UK), items=[Item(name=watches, amount=10)]), buyer=NodeA, seller=NodeB, contract=com.test.contract.TestOrderContract@4790bba1, linearId=1_1f2233e9-95cf-402d-a45c-627f6ba9e253), notary=corda.notary.validating|Controller)


(Roger Willis) #6

Hmm… The notary is throwing an TransactionInvalid exception which is thrown if the Notary encounters a SignatureException. It’s thrown in this method:

/**
 * Verifies the signatures on this transaction and throws if any are missing which aren't passed as parameters.
 * In this context, "verifying" means checking they are valid signatures and that their public keys are in
 * the contained transactions [BaseTransaction.mustSign] property.
 *
 * Normally you would not provide any keys to this function, but if you're in the process of building a partial
 * transaction and you want to access the contents before you've signed it, you can specify your own keys here
 * to bypass that check.
 *
 * @throws SignatureException if any signatures are invalid or unrecognised.
 * @throws SignaturesMissingException if any signatures should have been present but were not.
 */
@Throws(SignatureException::class)
fun verifySignatures(vararg allowedToBeMissing: CompositeKey): WireTransaction {
    // Embedded WireTransaction is not deserialised until after we check the signatures.
    checkSignaturesAreValid()

    val missing = getMissingSignatures()
    if (missing.isNotEmpty()) {
        val allowed = setOf(*allowedToBeMissing)
        val needed = missing - allowed
        if (needed.isNotEmpty())
            throw SignaturesMissingException(needed, getMissingKeyDescriptions(needed), id)
    }
    check(tx.id == id)
    return tx
}

Either a signature is not recognised or there is a signature missing, it seems.


(satish kumar) #7

Thanks Roger. Signature issue is resolved now. I have modified the code now.

class Initiator2(val po: DealState,
                val otherParty: Party,
                 override val progressTracker: ProgressTracker = Initiator2.tracker()): FlowLogic<TestFlowResult2>() {

    companion object {
        object CONSTRUCTING_OFFER : ProgressTracker.Step("Constructing proposed purchase order.")
        object SENDING_FINAL_TRANSACTION : ProgressTracker.Step("Sending fully signed transaction to seller.")

        fun tracker() = ProgressTracker(
                CONSTRUCTING_OFFER,
                SENDING_FINAL_TRANSACTION
        )
    }

    @Suspendable
    override fun call(): TestFlowResult2 {
        try {
            val myKeyPair = serviceHub.legalIdentityKey
            val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity
            val notaryPubKey = notary.owningKey
            // Stage 1.
            progressTracker.currentStep = CONSTRUCTING_OFFER
            // Construct a state object which encapsulates the TestOrder object.
            // We add the public keys for us and the counterparty as well as a reference to the contract code.

            val cashStates = serviceHub.vaultService.currentVault.statesOfType<TestOrderState>()
            println("$cashStates")
            // 1.Input States are arrived
            val inputStates = cashStates.filter {
                val state = it.state.data
                (state.po.orderNumber == 1)
            }
            println("$inputStates")
            val inHashTXIds = inputStates[0].ref.txhash

            subFlow(ResolveTransactionsFlow(setOf(inHashTXIds), serviceHub.myInfo.legalIdentity))

            val outputState = listOf(po)

            val ptxNew = TransactionType.General.Builder(notary)
            ptxNew.withItems(*inputStates.toTypedArray())
            ptxNew.withItems(*outputState.toTypedArray())
    
            ptxNew.addCommand(TestOrderContract.Commands.Amend(), listOf(myKeyPair.public.composite , otherParty.owningKey))
     

            val currentTime = serviceHub.clock.instant()
            ptxNew.setTime(currentTime, 30.seconds)

            ptxNew.signWith(serviceHub.legalIdentityKey)

            val signedTransaction = ptxNew.toSignedTransaction(checkSufficientSignatures = false)

           val allPartySignedTx = sendAndReceive<DigitalSignature.WithKey>(otherParty, signedTransaction).unwrap {
                val withNewSignature = signedTransaction + it
                // check all signatures are present except the notary
                withNewSignature.verifySignatures(withNewSignature.tx.notary!!.owningKey)
                withNewSignature.tx.toLedgerTransaction(serviceHub).verify()
                withNewSignature // return the almost complete transaction
            }
            val ntx = allPartySignedTx
            println("$ntx")
            // Stage 8.
            subFlow(FinalityFlow(ntx,setOf(serviceHub.myInfo.legalIdentity,otherParty)))
            // Stage 11.
            progressTracker.currentStep = SENDING_FINAL_TRANSACTION
            return TestFlowResult2.Success("Transaction id ${ntx.id} committed to ledger.")


        } catch(ex: Exception) {
            // Just catch all exception types.
            println(ex.stackTrace)
            return TestFlowResult2.Failure(ex.message)
        }
    }     
}


class Acceptor2(val otherParty: Party,
                override val progressTracker: ProgressTracker = Acceptor2.tracker()): FlowLogic<Unit>() {
    companion object {
        object WAITING_FOR_PROPOSAL : ProgressTracker.Step("Receiving proposed purchase order from buyer.")

        object RECORDING : ProgressTracker.Step("Recording transaction in vault.")

        fun tracker() = ProgressTracker(
                WAITING_FOR_PROPOSAL,
                RECORDING
        )
    }

    @Suspendable
    override fun call() {
        try {
            val keyPair = serviceHub.legalIdentityKey
            progressTracker.currentStep = WAITING_FOR_PROPOSAL
            val message = receive<SignedTransaction>(otherParty).unwrap {
                val wtx = it.tx
                it.verifySignatures(keyPair.public.composite, wtx.notary!!.owningKey)
                progressTracker.currentStep = RECORDING
                it // return the SignedTransaction
            }

            val ourSignature = serviceHub.legalIdentityKey.signWithECDSA(message.id)
            send(otherParty, ourSignature)
        }
        catch (ex: Exception) {
            //return TestFlowResult2.Failure(ex.message)
            //return ex
        }
    }

}

In the verify() function of Amend, the output object becomes empty.What is causing this issue?.
.


(Joel Dudley) #8

Good stuff. For the implementation of Amend, please see Deepak’s question here: Implementation Of Amend Command.