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
|
|
|
|
|
================
|
2017-12-13 16:22:40 +00:00
|
|
|
|
A flow encodes a sequence of steps that a node can perform to achieve a specific ledger update. By installing new flows
|
|
|
|
|
on a node, we allow the node to handle new business processes. The flow we define will allow a node to issue an
|
2017-11-16 15:31:52 +00:00
|
|
|
|
``IOUState`` onto the ledger.
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2017-07-07 11:06:28 +00:00
|
|
|
|
Flow outline
|
|
|
|
|
------------
|
2017-11-16 15:31:52 +00:00
|
|
|
|
The goal of our flow will be to orchestrate 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).
|
2017-07-07 11:06:28 +00:00
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
1. Building the transaction proposal for the issuance of a new IOU onto a ledger
|
|
|
|
|
2. Signing the transaction proposal
|
2018-11-14 14:16:22 +00:00
|
|
|
|
3. Recording the transaction and sending it to the IOU's borrower so that they can record it too
|
2017-11-16 15:31:52 +00:00
|
|
|
|
|
2018-11-14 14:16:22 +00:00
|
|
|
|
We also need the borrower to receive the transaction and record it for itself. At this stage, we do not require the borrower
|
|
|
|
|
to approve and sign IOU issuance transactions. We will be able to impose this requirement when we look at contracts in the
|
|
|
|
|
next tutorial.
|
2017-07-07 11:06:28 +00:00
|
|
|
|
|
2019-02-05 14:19:08 +00:00
|
|
|
|
.. warning:: The execution of a flow is distributed in space and time, as the flow crosses node boundaries and each
|
|
|
|
|
participant may have to wait for other participants to respond before it can complete its part of the
|
|
|
|
|
overall work. While a node is waiting, the state of its flow may be persistently recorded to disk as a
|
|
|
|
|
restorable checkpoint, enabling it to carry on where it left off when a counterparty responds. However,
|
|
|
|
|
before a node can be upgraded to a newer version of Corda, or of your Cordapp, all flows must have
|
|
|
|
|
completed, as there is no mechanism to upgrade a persisted flow checkpoint. It is therefore undesirable
|
|
|
|
|
to model a long-running business process as a single flow: it should rather be broken up into a series
|
|
|
|
|
of transactions, with flows used only to orchestrate the completion of each transaction.
|
|
|
|
|
|
2017-07-07 11:06:28 +00:00
|
|
|
|
Subflows
|
|
|
|
|
^^^^^^^^
|
2017-11-16 15:31:52 +00:00
|
|
|
|
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. We call these flows that are invoked in the context of a larger flow to handle a repeatable task
|
|
|
|
|
*subflows*.
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
|
|
|
|
FlowLogic
|
|
|
|
|
---------
|
2017-11-16 15:31:52 +00:00
|
|
|
|
All flows must subclass ``FlowLogic``. You then define the steps taken by the flow by overriding ``FlowLogic.call``.
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2018-11-14 14:16:22 +00:00
|
|
|
|
Let's define our ``IOUFlow``. Replace the definition of ``Initiator`` with the following:
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
|
|
|
|
.. container:: codeset
|
|
|
|
|
|
2018-09-24 14:00:31 +00:00
|
|
|
|
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/kotlin/tutorial/helloworld/IOUFlow.kt
|
2017-10-16 13:39:28 +00:00
|
|
|
|
:language: kotlin
|
|
|
|
|
:start-after: DOCSTART 01
|
|
|
|
|
:end-before: DOCEND 01
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2017-10-16 13:39:28 +00:00
|
|
|
|
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/helloworld/IOUFlow.java
|
|
|
|
|
:language: java
|
|
|
|
|
:start-after: DOCSTART 01
|
|
|
|
|
:end-before: DOCEND 01
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2018-11-14 14:16:22 +00:00
|
|
|
|
If you're following along in Java, you'll also need to rename ``Initiator.java`` to ``IOUFlow.java``.
|
|
|
|
|
|
|
|
|
|
Let's walk through this code step-by-step.
|
2017-11-16 15:31:52 +00:00
|
|
|
|
|
|
|
|
|
We've defined our own ``FlowLogic`` subclass that overrides ``FlowLogic.call``. ``FlowLogic.call`` has a return type
|
|
|
|
|
that must match the type parameter passed to ``FlowLogic`` - this is type returned by running the flow.
|
|
|
|
|
|
|
|
|
|
``FlowLogic`` subclasses can optionally have constructor parameters, which can be used as arguments to
|
|
|
|
|
``FlowLogic.call``. In our case, we have two:
|
2017-08-16 07:36:00 +00:00
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
* ``iouValue``, which is the value of the IOU being issued
|
|
|
|
|
* ``otherParty``, the IOU's borrower (the node running the flow is the lender)
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
``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. Forgetting this
|
|
|
|
|
annotation out will lead to some very weird error messages!
|
|
|
|
|
|
|
|
|
|
There are also a few more annotations, on the ``FlowLogic`` subclass itself:
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2018-10-05 17:05:10 +00:00
|
|
|
|
* ``@InitiatingFlow`` means that this flow is part of a flow pair and that it triggers the other side to run the
|
2018-11-14 14:16:22 +00:00
|
|
|
|
the counterpart flow (which in our case is the ``IOUFlowResponder`` defined below).
|
2017-09-05 12:11:58 +00:00
|
|
|
|
* ``@StartableByRPC`` allows the node owner to start this flow via an RPC call
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
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.
|
2017-08-16 07:36:00 +00:00
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
Choosing a notary
|
|
|
|
|
^^^^^^^^^^^^^^^^^
|
|
|
|
|
Every transaction requires a notary to prevent double-spends and serve as a timestamping authority. The first thing we
|
|
|
|
|
do in our flow is retrieve the a notary from the node's ``ServiceHub``. ``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
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
.. note::
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
Whenever we need information within a flow - whether it's about our own node's identity, the node's local storage,
|
|
|
|
|
or the rest of the network - we generally obtain it via the node's ``ServiceHub``.
|
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
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
* Creating the transaction's components
|
|
|
|
|
* Adding these components to a transaction builder
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2017-07-07 11:06:28 +00:00
|
|
|
|
Transaction items
|
|
|
|
|
~~~~~~~~~~~~~~~~~
|
2017-11-16 15:31:52 +00:00
|
|
|
|
Our transaction will have the following structure:
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
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
|
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
* The output ``IOUState`` on the right represents the state we will be adding to the ledger. As you can see, there are
|
|
|
|
|
no inputs - we are not consuming any existing ledger states in the creation of our IOU
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
* An ``Action`` command listing the IOU's lender as a signer
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
We've already talked about the ``IOUState``, but we haven't looked at commands yet. Commands serve two functions:
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
* 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
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
Each ``Command`` contains a command type plus a list of public keys. For now, we use the pre-defined
|
|
|
|
|
``TemplateContract.Action`` as our command type, and we list the lender as the only public key. This means that for
|
|
|
|
|
the transaction to be valid, the lender is required to sign the transaction.
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
Creating a transaction builder
|
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
To actually build the proposed transaction, we need a ``TransactionBuilder``. This is a mutable transaction class to
|
|
|
|
|
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-11-16 15:31:52 +00:00
|
|
|
|
Once we have the ``TransactionBuilder``, we add our components:
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
|
* 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, we are
|
|
|
|
|
passing in a reference to the ``TemplateContract``, which imposes no constraints. We will define a contract imposing
|
|
|
|
|
real constraints in the next tutorial
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
|
|
|
|
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
|
2017-11-16 15:31:52 +00:00
|
|
|
|
to modify the transaction without invalidating this signature. This effectively makes the transaction immutable.
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2018-08-10 11:28:59 +00:00
|
|
|
|
We sign the transaction using ``ServiceHub.signInitialTransaction``, which returns a ``SignedTransaction``. A
|
2017-11-16 15:31:52 +00:00
|
|
|
|
``SignedTransaction`` is an object that pairs a transaction with a list of signatures over that transaction.
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
|
|
|
|
Finalising the transaction
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
2018-11-14 14:16:22 +00:00
|
|
|
|
We now have a valid signed transaction. All that's left to do is to get the notary to sign it, have that recorded
|
|
|
|
|
locally and then send it to all the relevant parties. Once that happens the transaction will become a permanent part of the
|
|
|
|
|
ledger. We 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.
|
|
|
|
|
|
|
|
|
|
Creating 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. Let's do that by replacing ``Responder`` from the template with the following:
|
|
|
|
|
|
|
|
|
|
.. container:: codeset
|
|
|
|
|
|
|
|
|
|
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/kotlin/tutorial/helloworld/IOUFlowResponder.kt
|
|
|
|
|
:language: kotlin
|
|
|
|
|
:start-after: DOCSTART 01
|
|
|
|
|
:end-before: DOCEND 01
|
|
|
|
|
|
|
|
|
|
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/helloworld/IOUFlowResponder.java
|
|
|
|
|
:language: java
|
|
|
|
|
:start-after: DOCSTART 01
|
|
|
|
|
:end-before: DOCEND 01
|
|
|
|
|
|
|
|
|
|
As with the ``IOUFlow``, our ``IOUFlowResponder`` flow is a ``FlowLogic`` subclass where we've overridden ``FlowLogic.call``.
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
2018-11-14 14:16:22 +00:00
|
|
|
|
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 ``Initiator`` running on another node. This message
|
|
|
|
|
will be the finalised transaction which will be recorded in the borrower's vault.
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
|
|
|
|
Progress so far
|
|
|
|
|
---------------
|
2017-11-16 15:31:52 +00:00
|
|
|
|
Our flow, and our CorDapp, are now ready! We have now defined a flow that we can start on our node to completely
|
2018-11-14 14:16:22 +00:00
|
|
|
|
automate the process of issuing an IOU onto the ledger. All that's left is to spin up some nodes and test our CorDapp.
|