Modify the flow

A flow encodes a sequence of steps that a node can perform to achieve a specific ledger update. Installing new flows on a node allows the node to handle new business processes. The flow that you define will allow a node to issue an IOUState onto the ledger.

Flow outline

Your goal is to create a flow that orchestrates an IOU issuance transaction. Transactions in Corda are the atomic units of change that update the ledger. Each transaction is a proposal to mark zero or more existing states as historic (the inputs), while creating zero or more new states (the outputs).

The process of creating and applying this transaction to a ledger will be conducted by the IOU’s lender, and will require the following steps:

  • Building the transaction proposal for the issuance of a new IOU onto a ledger.
  • Signing the transaction proposal.
  • Recording the transaction and sending it to the IOU’s borrower so that they can record it too.

You also need the borrower to receive the transaction and record it for themselves. At this stage, you do not require the borrower to approve and sign IOU issuance transactions. You will be able to impose this requirement when you look at contracts in the next tutorial.

Subflows

Tasks like recording a transaction or sending a transaction to a counterparty are very common in Corda. Instead of forcing each developer to reimplement their own logic to handle these tasks, Corda provides a number of library flows to handle these tasks. Flows that are invoked in the context of a larger flow to handle a repeatable task are called subflows.

Output

Take a look at this code snippet. This is how your IOU flow template should look after you have completed all the steps described below.

// Add these imports:
import net.corda.core.contracts.Command;
import net.corda.core.identity.Party;
import net.corda.core.transactions.TransactionBuilder;
import com.template.states.IOUState;
import com.template.contracts.TemplateContract;
import net.corda.core.flows.FlowSession;
import net.corda.core.flows.FinalityFlow;

// Replace Initiator's definition with:
@InitiatingFlow
@StartableByRPC
class IOUFlow(val iouValue: Int,
              val otherParty: Party) : FlowLogic<Unit>() {

    /** The progress tracker provides checkpoints indicating the progress of
    the flow to observers. */
    override val progressTracker = ProgressTracker()

    /** The flow logic is encapsulated within the call() method. */
    @Suspendable
    override fun call() {
        // We retrieve the notary identity from the network map.
        val notary = serviceHub.networkMapCache.notaryIdentities[0]

        // We create the transaction components.
        val outputState = IOUState(iouValue, ourIdentity, otherParty)
        val command = Command(TemplateContract.Commands.Action(), ourIdentity.owningKey)

        // We create a transaction builder and add the components.
        val txBuilder = TransactionBuilder(notary = notary)
                .addOutputState(outputState, TemplateContract.ID)
                .addCommand(command)

        // We sign the transaction.
        val signedTx = serviceHub.signInitialTransaction(txBuilder)

        // Creating a session with the other party.
        val otherPartySession = initiateFlow(otherParty)

        // We finalise the transaction and then send it to the counterparty.
        subFlow(FinalityFlow(signedTx, otherPartySession))
    }
}

// Add these imports:
import co.paralleluniverse.fibers.Suspendable;
import com.template.contracts.IOUContract;
import com.template.states.IOUState;
import net.corda.core.contracts.Command;
import net.corda.core.flows.*;
import net.corda.core.identity.Party;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker;
import net.corda.core.flows.SignTransactionFlow;
import net.corda.core.transactions.SignedTransaction;
import static net.corda.core.contracts.ContractsDSL.requireThat;
import net.corda.core.contracts.ContractState;
import net.corda.core.crypto.SecureHash;


import java.security.PublicKey;
import java.util.Arrays;
import java.util.List;

// Replace Initiator's definition with:
@InitiatingFlow
@StartableByRPC
public class IOUFlow extends FlowLogic<Void> {
    private final Integer iouValue;
    private final Party otherParty;

    /**
     * The progress tracker provides checkpoints indicating the progress of
     the flow to observers.
     */
    private final ProgressTracker progressTracker = new ProgressTracker();

    public IOUFlow(Integer iouValue, Party otherParty) {
        this.iouValue = iouValue;
        this.otherParty = otherParty;
    }

    @Override
    public ProgressTracker getProgressTracker() {
        return progressTracker;
    }

    /**
     * The flow logic is encapsulated within the call() method.
     */
    @Suspendable
    @Override
    public Void call() throws FlowException {
        // We retrieve the notary identity from the network map.
        Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);

        // We create the transaction components.
        IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
        Command command = new Command<>(new TemplateContract.Commands.Send(), getOurIdentity().getOwningKey());

        // We create a transaction builder and add the components.
        TransactionBuilder txBuilder = new TransactionBuilder(notary)
                .addOutputState(outputState, TemplateContract.ID)
                .addCommand(command);

        // Signing the transaction.
        SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);

        // Creating a session with the other party.
        FlowSession otherPartySession = initiateFlow(otherParty);

        // We finalise the transaction and then send it to the counterparty.
        subFlow(new FinalityFlow(signedTx, otherPartySession));

        return null;
    }
}

Define FlowLogic

  1. Open the file for for your language:

    • Java: Open Initiator.java from workflows/src/main/java/com/template/flows/Initiator.java.
    • Kotlin: Open Flows.kt from workflows/src/main/kotlin/com/template/flows/Flows.kt.
  2. If you’re following along in Java, rename Initiator.java to IOUFlow.java.

  3. Define your IOUFlow.

Let’s walk through the above IOU flow code step-by-step.

You’ve defined your own FlowLogic subclass that overrides FlowLogic.call. FlowLogic.call has a return type that must match the type parameter passed to FlowLogic - this type is returned by running the flow.

FlowLogic subclasses can optionally have constructor parameters, which can be used as arguments to FlowLogic.call. In your case, you have two:

  • iouValue, the value of the IOU being issued.
  • otherParty, the IOU’s borrower (the node running the flow is the lender).

FlowLogic.call is annotated @Suspendable - this allows the flow to be check-pointed and serialised to disk when it encounters a long-running operation, allowing your node to move on to running other flows. Leaving this annotation out will lead to some very weird error messages!

There are also a few more annotations, on the FlowLogic subclass itself:

  • @InitiatingFlow means that this flow is part of a flow pair and that it triggers the other side to run the the counterpart flow (which in our case is the IOUFlowResponder defined below).
  • @StartableByRPC allows the node owner to start this flow via an RPC call.

Let’s walk through the steps of FlowLogic.call itself. This is where we actually describe the procedure for issuing the IOUState onto a ledger.

Choose a notary

Every transaction requires a notary to prevent double spends and serve as a timestamping authority. The first thing you must do in your flow is retrieve the notary from the node’s ServiceHub.

  • Use ServiceHub.networkMapCache to get information about the other nodes on the network and the services that they offer.

Build the transaction

You can build your transaction proposal in two steps:

  1. Create the transaction’s components.
  2. Add these components to a transaction builder.

Transaction items

Our transaction will have the following structure:

simple tutorial transaction
  • The output IOUState on the right represents the state you will be adding to the ledger. As you can see, there are no inputs - you are not consuming any existing ledger states in the creation of your IOU.
  • An Action command listing the IOU’s lender as a signer.

We’ve already talked about the IOUState, but we haven’t looked at commands yet. Commands serve two functions:

  • They indicate the intent of a transaction - issuance, transfer, redemption, revocation. This will be crucial when we discuss contracts in the next tutorial.
  • They allow us to define the required signers for the transaction. For example, IOU creation might require signatures from the lender only, whereas the transfer of an IOU might require signatures from both the IOU’s borrower and lender.

Each Command contains a command type plus a list of public keys. For now, you will use the pre-defined TemplateContract.Action as your command type, and you’ll list the lender as the only public key. This means that for your the transaction to be valid, the lender is required to sign the transaction.

Create a transaction builder

To build the proposed transaction, you need a TransactionBuilder. This is a mutable transaction class to which you can add:

  • Inputs
  • Outputs
  • Commands
  • Any other items the transaction needs

You create a TransactionBuilder that uses the notary retrieved earlier.

Once you have the TransactionBuilder, you add your components:

  • The command is added directly using TransactionBuilder.addCommand.
  • The output IOUState is added using TransactionBuilder.addOutputState. As well as the output state itself, this method takes a reference to the contract that will govern the evolution of the state over time. Here you are passing in a reference to the TemplateContract, which imposes no constraints. You will define a contract imposing real constraints in the next tutorial.

Sign the transaction

Now that you have a valid transaction proposal, you need to sign it. Once the transaction is signed, no one will be able to modify the transaction without invalidating this signature. This effectively makes the transaction immutable.

You sign the transaction using ServiceHub.signInitialTransaction, which returns a SignedTransaction. A SignedTransaction is an object that pairs a transaction with a list of signatures over that transaction.

Finalise the transaction

You now have a valid signed transaction. All that’s left to do is have the notary sign it, record that locally, and then send it to all the relevant parties. The transaction then becomes a permanent part of the ledger. Here you use FinalityFlow which does all of this for the lender.

For the borrower to receive the transaction they just need a flow that responds to the seller’s flow.

Create the borrower’s flow

The borrower has to use ReceiveFinalityFlow in order to receive and record the transaction; it needs to respond to the lender’s flow.

  1. Open the Responder file.

  2. Change the Responder flow file name to IOUFlowResponder.

  3. Replace the template definition with the following:

// Replace Responder's definition with:
@InitiatedBy(IOUFlow::class)
class IOUFlowResponder(private val otherPartySession: FlowSession) : FlowLogic<Unit>() {
    @Suspendable
    override fun call() {
        subFlow(ReceiveFinalityFlow(otherPartySession))
    }
}

// Add this import:
import net.corda.core.flows.ReceiveFinalityFlow;

// Replace Responder's definition with:
@InitiatedBy(IOUFlow.class)
public class IOUFlowResponder extends FlowLogic<Void> {
    private final FlowSession otherPartySession;

    public IOUFlowResponder(FlowSession otherPartySession) {
        this.otherPartySession = otherPartySession;
    }

    @Suspendable
    @Override
    public Void call() throws FlowException {
        subFlow(new ReceiveFinalityFlow(otherPartySession));

        return null;
    }
}

As with the IOUFlow, your IOUFlowResponder flow is a FlowLogic subclass where we’ve overridden FlowLogic.call.

The flow is annotated with InitiatedBy(IOUFlow.class), which means that your node will invoke IOUFlowResponder.call when it receives a message from a instance of IOUFlow running on another node. This message will be the finalised transaction which will be recorded in the borrower’s vault.

Progress so far

Your flow, and your CorDapp, are now ready! We have now defined a flow that we can start on our node to completely automate the process of issuing an IOU onto the ledger. All that’s left is to spin up some nodes and test your CorDapp.