corda/docs/source/hello-world-flow.rst

263 lines
11 KiB
ReStructuredText
Raw Normal View History

2017-06-16 13:05:52 +00:00
.. highlight:: kotlin
.. raw:: html
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/codesets.js"></script>
Writing the flow
================
A flow describes the sequence of steps for agreeing a specific ledger update. By installing new flows on our node, we
2017-07-07 11:06:28 +00:00
allow the node to handle new business processes. Our flow will allow a node to issue an ``IOUState`` onto the ledger.
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
Flow outline
------------
Our flow needs to take the following steps for a borrower to issue a new IOU onto the ledger:
1. Create a valid transaction proposal for the creation of a new IOU
2. Verify the transaction
3. Sign the transaction ourselves
4. Record the transaction in our vault
5. Send the transaction to the IOU's lender so that they can record it too
Subflows
^^^^^^^^
Although our flow requirements look complex, we can delegate to existing flows to handle many of these tasks. A flow
that is invoked within the context of a larger flow to handle a repeatable task is called a *subflow*.
2017-06-16 13:05:52 +00:00
In our initiator flow, we can automate steps 4 and 5 using ``FinalityFlow``.
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
All we need to do is write the steps to handle the creation and signing of the proposed transaction.
2017-06-16 13:05:52 +00:00
FlowLogic
---------
2017-07-07 11:06:28 +00:00
Flows are implemented as ``FlowLogic`` subclasses. You define the steps taken by the flow by overriding
2017-06-16 13:05:52 +00:00
``FlowLogic.call``.
2017-08-16 07:36:00 +00:00
We'll write our flow in either ``TemplateFlow.java`` or ``App.kt``. Overwrite both the existing flows in the template
with the following:
2017-06-16 13:05:52 +00:00
.. container:: codeset
.. code-block:: kotlin
2017-08-16 07:36:00 +00:00
...
2017-06-16 13:05:52 +00:00
import net.corda.core.utilities.ProgressTracker
2017-08-16 07:36:00 +00:00
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.flows.*
...
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
@InitiatingFlow
@StartableByRPC
class IOUFlow(val iouValue: Int,
val otherParty: Party) : FlowLogic<Unit>() {
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
/** The progress tracker provides checkpoints indicating the progress of the flow to observers. */
override val progressTracker = ProgressTracker()
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
/** 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]
2017-07-07 11:06:28 +00:00
// We create a transaction builder
val txBuilder = TransactionBuilder(notary = notary)
// We create the transaction components.
val outputState = IOUState(iouValue, ourIdentity, otherParty)
val outputContract = IOUContract::class.jvmName
val outputContractAndState = StateAndContract(outputState, outputContract)
val cmd = Command(IOUContract.Create(), ourIdentity.owningKey)
2017-07-07 11:06:28 +00:00
// We add the items to the builder.
txBuilder.withItems(outputContractAndState, cmd)
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
// Verifying the transaction.
txBuilder.verify(serviceHub)
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
// Signing the transaction.
2017-08-16 07:36:00 +00:00
val signedTx = serviceHub.signInitialTransaction(txBuilder)
2017-07-07 11:06:28 +00:00
// Finalising the transaction.
subFlow(FinalityFlow(signedTx))
2017-06-16 13:05:52 +00:00
}
}
.. code-block:: java
2017-08-16 07:36:00 +00:00
package com.template.flow;
2017-06-16 13:05:52 +00:00
import co.paralleluniverse.fibers.Suspendable;
2017-08-16 07:36:00 +00:00
import com.template.contract.IOUContract;
import com.template.state.IOUState;
2017-07-07 11:06:28 +00:00
import net.corda.core.contracts.Command;
import net.corda.core.contracts.StateAndContract;
2017-08-16 07:36:00 +00:00
import net.corda.core.flows.*;
2017-06-16 13:05:52 +00:00
import net.corda.core.identity.Party;
import net.corda.core.transactions.SignedTransaction;
2017-07-07 11:06:28 +00:00
import net.corda.core.transactions.TransactionBuilder;
2017-06-16 13:05:52 +00:00
import net.corda.core.utilities.ProgressTracker;
2017-07-07 11:06:28 +00:00
@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();
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
public IOUFlow(Integer iouValue, Party otherParty) {
this.iouValue = iouValue;
this.otherParty = otherParty;
2017-06-16 13:05:52 +00:00
}
2017-08-16 07:36:00 +00:00
@Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
2017-07-07 11:06:28 +00:00
/**
* 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.
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
2017-07-07 11:06:28 +00:00
2017-08-16 07:36:00 +00:00
// We create a transaction builder.
2017-07-07 11:06:28 +00:00
final TransactionBuilder txBuilder = new TransactionBuilder();
txBuilder.setNotary(notary);
// We create the transaction components.
IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
String outputContract = IOUContract.class.getName();
StateAndContract outputContractAndState = new StateAndContract(outputState, outputContract);
Command cmd = new Command<>(new IOUContract.Create(), getOurIdentity().getOwningKey());
2017-07-07 11:06:28 +00:00
// We add the items to the builder.
txBuilder.withItems(outputContractAndState, cmd);
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
// Verifying the transaction.
txBuilder.verify(getServiceHub());
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
// Signing the transaction.
2017-08-16 07:36:00 +00:00
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
// Finalising the transaction.
subFlow(new FinalityFlow(signedTx));
return null;
2017-06-16 13:05:52 +00:00
}
}
2017-08-16 07:36:00 +00:00
If you're following along in Java, you'll also need to rename ``TemplateFlow.java`` to ``IOUFlow.java``.
2017-07-07 11:06:28 +00:00
We now have our own ``FlowLogic`` subclass that overrides ``FlowLogic.call``. There's a few things to note:
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
* ``FlowLogic.call`` has a return type that matches the type parameter passed to ``FlowLogic`` - this is type returned
by running the flow
* ``FlowLogic`` subclasses can have constructor parameters, which can be used as arguments to ``FlowLogic.call``
2017-06-16 13:05:52 +00:00
* ``FlowLogic.call`` is annotated ``@Suspendable`` - this means that the flow will be check-pointed and serialised to
disk when it encounters a long-running operation, allowing your node to move on to running other flows. Forgetting
this annotation out will lead to some very weird error messages
2017-07-07 11:06:28 +00:00
* There are also a few more annotations, on the ``FlowLogic`` subclass itself:
2017-06-16 13:05:52 +00:00
* ``@InitiatingFlow`` means that this flow can be started directly by the node
* ``@StartableByRPC`` allows the node owner to start this flow via an RPC call
2017-06-16 13:05:52 +00:00
2017-08-16 07:36:00 +00:00
* We override the progress tracker, even though we are not providing any progress tracker steps yet. The progress
tracker is required for the node shell to establish when the flow has ended
2017-07-07 11:06:28 +00:00
Let's walk through the steps of ``FlowLogic.call`` one-by-one:
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
Retrieving participant information
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The identity of our counterparty is passed in as a constructor argument. However, we need to use the ``ServiceHub`` to
retrieve our identity, as well as the identity of the notary we'll be using for our transaction.
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
You can see that the notary's identity is being retrieved from the node's ``ServiceHub``. Whenever we need
information within a flow - whether it's about our own node, its contents, or the rest of the network - we use the
node's ``ServiceHub``. In particular, ``ServiceHub.networkMapCache`` provides information about the other nodes on the
network and the services that they offer.
2017-06-16 13:05:52 +00:00
Building the transaction
^^^^^^^^^^^^^^^^^^^^^^^^
2017-07-07 11:06:28 +00:00
We'll build our transaction proposal in two steps:
2017-06-16 13:05:52 +00:00
* Creating a transaction builder
2017-07-07 11:06:28 +00:00
* Adding the desired items to the builder
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
Creating a transaction builder
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2017-06-16 13:05:52 +00:00
To start building the proposed transaction, we need a ``TransactionBuilder``. This is a mutable transaction class to
2017-07-07 11:06:28 +00:00
which we can add inputs, outputs, commands, and any other items the transaction needs. We create a
``TransactionBuilder`` that uses the notary we retrieved earlier.
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
Transaction items
~~~~~~~~~~~~~~~~~
Now that we have our ``TransactionBuilder``, we need to add the desired items. Remember that we're trying to build
2017-06-16 13:05:52 +00:00
the following transaction:
2017-07-07 11:06:28 +00:00
.. image:: resources/simple-tutorial-transaction.png
:scale: 15%
2017-06-16 13:05:52 +00:00
:align: center
So we'll need the following:
* The output ``IOUState`` and its associated contract
* A ``Create`` command listing the IOU's lender as a signer
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
The command we use pairs the ``IOUContract.Create`` command defined earlier with our public key. Including this command
in the transaction makes us one of the transaction's required signers.
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
We add these items to the transaction using the ``TransactionBuilder.withItems`` method, which takes a ``vararg`` of:
2017-06-16 13:05:52 +00:00
* ``StateAndContract`` or ``TransactionState`` objects, which are added to the builder as output states
* ``StateAndRef`` objects (references to the outputs of previous transactions), which are added to the builder as input
2017-06-16 13:05:52 +00:00
state references
2017-07-07 11:06:28 +00:00
* ``Command`` objects, which are added to the builder as commands
* ``SecureHash`` objects, which are added to the builder as attachments
* ``TimeWindow`` objects, which set the time-window of the transaction
2017-06-16 13:05:52 +00:00
It will modify the ``TransactionBuilder`` in-place to add these components to it.
Verifying the transaction
^^^^^^^^^^^^^^^^^^^^^^^^^
We've now built our proposed transaction. Before we sign it, we should check that it represents a valid ledger update
2017-07-07 11:06:28 +00:00
proposal by verifying the transaction, which will execute each of the transaction's contracts.
2017-06-16 13:05:52 +00:00
If the verification fails, we have built an invalid transaction. Our flow will then end, throwing a
``TransactionVerificationException``.
Signing the transaction
^^^^^^^^^^^^^^^^^^^^^^^
2017-07-07 11:06:28 +00:00
Now that we have a valid transaction proposal, we need to sign it. Once the transaction is signed, no-one will be able
to modify the transaction without invalidating our signature, effectively making the transaction immutable.
2017-06-16 13:05:52 +00:00
The call to ``ServiceHub.toSignedTransaction`` returns a ``SignedTransaction`` - an object that pairs the
2017-06-16 13:05:52 +00:00
transaction itself with a list of signatures over that transaction.
Finalising the transaction
^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-07-07 11:06:28 +00:00
Now that we have a valid signed transaction, all that's left to do is to have it notarised and recorded by all the
relevant parties. By doing so, it will become a permanent part of the ledger. As discussed, we'll handle this process
automatically using a built-in flow called ``FinalityFlow``:
2017-06-16 13:05:52 +00:00
``FinalityFlow`` completely automates the process of:
2017-07-07 11:06:28 +00:00
* Notarising the transaction if required (i.e. if the transaction contains inputs and/or a time-window)
2017-06-16 13:05:52 +00:00
* Recording it in our vault
2017-07-07 11:06:28 +00:00
* Sending it to the other participants (i.e. the lender) for them to record as well
2017-06-16 13:05:52 +00:00
2017-07-07 11:06:28 +00:00
Our flow, and our CorDapp, are now ready!
2017-06-16 13:05:52 +00:00
Progress so far
---------------
2017-07-07 11:06:28 +00:00
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. The final step is to spin up some nodes and test our CorDapp.