Moves contract constraints into 2nd tutorial, to make Hello World example simpler.

This commit is contained in:
Joel Dudley 2017-11-01 11:34:59 +00:00 committed by GitHub
parent ee1e72d64c
commit 89191e9ef1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 514 additions and 606 deletions

View File

@ -20,7 +20,7 @@ Corda is a decentralised database system in which nodes trust each other as litt
1. Read the [Getting Started](https://docs.corda.net/getting-set-up.html) documentation 1. Read the [Getting Started](https://docs.corda.net/getting-set-up.html) documentation
2. Run the [Example CorDapp](https://docs.corda.net/tutorial-cordapp.html) 2. Run the [Example CorDapp](https://docs.corda.net/tutorial-cordapp.html)
3. Read about Corda's [Key Concepts](https://docs.corda.net/key-concepts.html) 3. Read about Corda's [Key Concepts](https://docs.corda.net/key-concepts.html)
3. Follow the [Hello, World! tutorial](https://docs.corda.net/hello-world-index.html) 3. Follow the [Hello, World! tutorial](https://docs.corda.net/hello-world-introduction.html)
## Useful links ## Useful links

View File

@ -1,46 +0,0 @@
package net.corda.docs.java.tutorial.helloworld;
// DOCSTART 01
import net.corda.core.contracts.CommandData;
import net.corda.core.contracts.CommandWithParties;
import net.corda.core.contracts.Contract;
import net.corda.core.identity.Party;
import net.corda.core.transactions.LedgerTransaction;
import java.security.PublicKey;
import java.util.List;
import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
import static net.corda.core.contracts.ContractsDSL.requireThat;
public class IOUContract implements Contract {
// Our Create command.
public static class Create implements CommandData {
}
@Override
public void verify(LedgerTransaction tx) {
final CommandWithParties<Create> command = requireSingleCommand(tx.getCommands(), Create.class);
requireThat(check -> {
// Constraints on the shape of the transaction.
check.using("No inputs should be consumed when issuing an IOU.", tx.getInputs().isEmpty());
check.using("There should be one output state of type IOUState.", tx.getOutputs().size() == 1);
// IOU-specific constraints.
final IOUState out = tx.outputsOfType(IOUState.class).get(0);
final Party lender = out.getLender();
final Party borrower = out.getBorrower();
check.using("The IOU's value must be non-negative.", out.getValue() > 0);
check.using("The lender and the borrower cannot be the same entity.", lender != borrower);
// Constraints on the signers.
final List<PublicKey> signers = command.getSigners();
check.using("There must only be one signer.", signers.size() == 1);
check.using("The signer must be the lender.", signers.contains(lender.getOwningKey()));
return null;
});
}
}
// DOCEND 01

View File

@ -3,13 +3,15 @@ package net.corda.docs.java.tutorial.helloworld;
// DOCSTART 01 // DOCSTART 01
import co.paralleluniverse.fibers.Suspendable; import co.paralleluniverse.fibers.Suspendable;
import net.corda.core.contracts.Command; import net.corda.core.contracts.Command;
import net.corda.core.contracts.StateAndContract; import net.corda.core.contracts.CommandData;
import net.corda.core.flows.*; import net.corda.core.flows.*;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
import net.corda.core.transactions.SignedTransaction; import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder; import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker; import net.corda.core.utilities.ProgressTracker;
import static net.corda.docs.java.tutorial.helloworld.TemplateContract.TEMPLATE_CONTRACT_ID;
@InitiatingFlow @InitiatingFlow
@StartableByRPC @StartableByRPC
public class IOUFlow extends FlowLogic<Void> { public class IOUFlow extends FlowLogic<Void> {
@ -40,21 +42,15 @@ public class IOUFlow extends FlowLogic<Void> {
// We retrieve the notary identity from the network map. // We retrieve the notary identity from the network map.
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0); final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
// We create a transaction builder.
final TransactionBuilder txBuilder = new TransactionBuilder();
txBuilder.setNotary(notary);
// We create the transaction components. // We create the transaction components.
IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty); IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
String outputContract = IOUContract.class.getName(); CommandData cmdType = new TemplateContract.Commands.Action();
StateAndContract outputContractAndState = new StateAndContract(outputState, outputContract); Command cmd = new Command<>(cmdType, getOurIdentity().getOwningKey());
Command cmd = new Command<>(new IOUContract.Create(), getOurIdentity().getOwningKey());
// We add the items to the builder. // We create a transaction builder and add the components.
txBuilder.withItems(outputContractAndState, cmd); final TransactionBuilder txBuilder = new TransactionBuilder(notary)
.addOutputState(outputState, TEMPLATE_CONTRACT_ID)
// Verifying the transaction. .addCommand(cmd);
txBuilder.verify(getServiceHub());
// Signing the transaction. // Signing the transaction.
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder); final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);

View File

@ -0,0 +1,21 @@
package net.corda.docs.java.tutorial.helloworld;
import net.corda.core.contracts.CommandData;
import net.corda.core.contracts.Contract;
import net.corda.core.transactions.LedgerTransaction;
public class TemplateContract implements Contract {
// This is used to identify our contract when building a transaction.
public static final String TEMPLATE_CONTRACT_ID = "com.template.TemplateContract";
/**
* A transaction is considered valid if the verify() function of the contract of each of the transaction's input
* and output states does not throw an exception.
*/
@Override
public void verify(LedgerTransaction tx) {}
public interface Commands extends CommandData {
class Action implements Commands {}
}
}

View File

@ -13,7 +13,6 @@ import java.util.List;
import static net.corda.core.contracts.ContractsDSL.requireSingleCommand; import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
import static net.corda.core.contracts.ContractsDSL.requireThat; import static net.corda.core.contracts.ContractsDSL.requireThat;
// DOCEND 01
public class IOUContract implements Contract { public class IOUContract implements Contract {
// Our Create command. // Our Create command.
@ -22,7 +21,7 @@ public class IOUContract implements Contract {
@Override @Override
public void verify(LedgerTransaction tx) { public void verify(LedgerTransaction tx) {
final CommandWithParties<net.corda.docs.java.tutorial.helloworld.IOUContract.Create> command = requireSingleCommand(tx.getCommands(), net.corda.docs.java.tutorial.helloworld.IOUContract.Create.class); final CommandWithParties<IOUContract.Create> command = requireSingleCommand(tx.getCommands(), IOUContract.Create.class);
requireThat(check -> { requireThat(check -> {
// Constraints on the shape of the transaction. // Constraints on the shape of the transaction.
@ -36,15 +35,14 @@ public class IOUContract implements Contract {
check.using("The IOU's value must be non-negative.", out.getValue() > 0); check.using("The IOU's value must be non-negative.", out.getValue() > 0);
check.using("The lender and the borrower cannot be the same entity.", lender != borrower); check.using("The lender and the borrower cannot be the same entity.", lender != borrower);
// DOCSTART 02
// Constraints on the signers. // Constraints on the signers.
final List<PublicKey> signers = command.getSigners(); final List<PublicKey> signers = command.getSigners();
check.using("There must be two signers.", signers.size() == 2); check.using("There must be two signers.", signers.size() == 2);
check.using("The borrower and lender must be signers.", signers.containsAll( check.using("The borrower and lender must be signers.", signers.containsAll(
ImmutableList.of(borrower.getOwningKey(), lender.getOwningKey()))); ImmutableList.of(borrower.getOwningKey(), lender.getOwningKey())));
// DOCEND 02
return null; return null;
}); });
} }
} }
// DOCEND 01

View File

@ -1,33 +0,0 @@
package net.corda.docs.tutorial.helloworld
// DOCSTART 01
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.contracts.requireSingleCommand
import net.corda.core.contracts.requireThat
import net.corda.core.transactions.LedgerTransaction
class IOUContract : Contract {
// Our Create command.
class Create : CommandData
override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Create>()
requireThat {
// Constraints on the shape of the transaction.
"No inputs should be consumed when issuing an IOU." using (tx.inputs.isEmpty())
"There should be one output state of type IOUState." using (tx.outputs.size == 1)
// IOU-specific constraints.
val out = tx.outputsOfType<IOUState>().single()
"The IOU's value must be non-negative." using (out.value > 0)
"The lender and the borrower cannot be the same entity." using (out.lender != out.borrower)
// Constraints on the signers.
"There must only be one signer." using (command.signers.toSet().size == 1)
"The signer must be the lender." using (command.signers.contains(out.lender.owningKey))
}
}
}
// DOCEND 01

View File

@ -3,7 +3,6 @@ package net.corda.docs.tutorial.helloworld
// DOCSTART 01 // DOCSTART 01
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.Command import net.corda.core.contracts.Command
import net.corda.core.contracts.StateAndContract
import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
@ -11,7 +10,6 @@ import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import kotlin.reflect.jvm.jvmName
@InitiatingFlow @InitiatingFlow
@StartableByRPC @StartableByRPC
@ -27,25 +25,19 @@ class IOUFlow(val iouValue: Int,
// We retrieve the notary identity from the network map. // We retrieve the notary identity from the network map.
val notary = serviceHub.networkMapCache.notaryIdentities[0] val notary = serviceHub.networkMapCache.notaryIdentities[0]
// We create a transaction builder
val txBuilder = TransactionBuilder(notary = notary)
// We create the transaction components. // We create the transaction components.
val outputState = IOUState(iouValue, ourIdentity, otherParty) val outputState = IOUState(iouValue, ourIdentity, otherParty)
val outputContract = IOUContract::class.jvmName val cmd = Command(TemplateContract.Commands.Action(), ourIdentity.owningKey)
val outputContractAndState = StateAndContract(outputState, outputContract)
val cmd = Command(IOUContract.Create(), ourIdentity.owningKey)
// We add the items to the builder. // We create a transaction builder and add the components.
txBuilder.withItems(outputContractAndState, cmd) val txBuilder = TransactionBuilder(notary = notary)
.addOutputState(outputState, TEMPLATE_CONTRACT_ID)
.addCommand(cmd)
// Verifying the transaction. // We sign the transaction.
txBuilder.verify(serviceHub)
// Signing the transaction.
val signedTx = serviceHub.signInitialTransaction(txBuilder) val signedTx = serviceHub.signInitialTransaction(txBuilder)
// Finalising the transaction. // We finalise the transaction.
subFlow(FinalityFlow(signedTx)) subFlow(FinalityFlow(signedTx))
} }
} }

View File

@ -0,0 +1,20 @@
package net.corda.docs.tutorial.helloworld
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.transactions.LedgerTransaction
val TEMPLATE_CONTRACT_ID = "com.template.TemplateContract"
open class TemplateContract : Contract {
// A transaction is considered valid if the verify() function of the contract of each of the transaction's input
// and output states does not throw an exception.
override fun verify(tx: LedgerTransaction) {
// Verification logic goes here.
}
// Used to indicate the transaction's intent.
interface Commands : CommandData {
class Action : Commands
}
}

View File

@ -6,7 +6,6 @@ import net.corda.core.contracts.Contract
import net.corda.core.contracts.requireSingleCommand import net.corda.core.contracts.requireSingleCommand
import net.corda.core.contracts.requireThat import net.corda.core.contracts.requireThat
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
// DOCEND 01
class IOUContract : Contract { class IOUContract : Contract {
// Our Create command. // Our Create command.
@ -25,12 +24,11 @@ class IOUContract : Contract {
"The IOU's value must be non-negative." using (out.value > 0) "The IOU's value must be non-negative." using (out.value > 0)
"The lender and the borrower cannot be the same entity." using (out.lender != out.borrower) "The lender and the borrower cannot be the same entity." using (out.lender != out.borrower)
// DOCSTART 02
// Constraints on the signers. // Constraints on the signers.
"There must be two signers." using (command.signers.toSet().size == 2) "There must be two signers." using (command.signers.toSet().size == 2)
"The borrower and lender must be signers." using (command.signers.containsAll(listOf( "The borrower and lender must be signers." using (command.signers.containsAll(listOf(
out.borrower.owningKey, out.lender.owningKey))) out.borrower.owningKey, out.lender.owningKey)))
// DOCEND 02
} }
} }
} }
// DOCEND 01

View File

@ -168,9 +168,9 @@ The best way to check that everything is working fine is by taking a deeper look
Next, you should read through :doc:`Corda Key Concepts <key-concepts>` to understand how Corda works. Next, you should read through :doc:`Corda Key Concepts <key-concepts>` to understand how Corda works.
You'll then be ready to start writing your own CorDapps. Learn how to do this in the By then, you'll be ready to start writing your own CorDapps. Learn how to do this in the
:doc:`Hello, World tutorial <hello-world-index>`. You'll want to refer to the :doc:`API docs <api-index>`, the :doc:`Hello, World tutorial <hello-world-introduction>`. You may want to refer to the :doc:`API docs <api-index>`, the
:doc:`flow cookbook <flow-cookbook>` and the `samples <https://www.corda.net/samples/>`_ along the way. :doc:`flow cookbook <flow-cookbook>` and the `samples <https://www.corda.net/samples/>`_ along the way.
If you encounter any issues, please see the :doc:`troubleshooting` page, or get in touch with us on the If you encounter any issues, please see the :doc:`troubleshooting` page, or get in touch with us on the
`forums <https://discourse.corda.net/>`_ or via `slack <http://slack.corda.net/>`_. `forums <https://discourse.corda.net/>`_ or via `slack <http://slack.corda.net/>`_.

View File

@ -1,197 +0,0 @@
.. highlight:: kotlin
.. raw:: html
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/codesets.js"></script>
Writing the contract
====================
In Corda, the ledger is updated via transactions. 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).
It's easy to imagine that most CorDapps will want to impose some constraints on how their states evolve over time:
* A cash CorDapp would not want to allow users to create transactions that generate money out of thin air (at least
without the involvement of a central bank or commercial bank)
* A loan CorDapp might not want to allow the creation of negative-valued loans
* An asset-trading CorDapp would not want to allow users to finalise a trade without the agreement of their counterparty
In Corda, we impose constraints on what transactions are allowed using contracts. These contracts are very different
to the smart contracts of other distributed ledger platforms. In Corda, contracts do not represent the current state of
the ledger. Instead, like a real-world contract, they simply impose rules on what kinds of agreements are allowed.
Every state is associated with a contract. A transaction is invalid if it does not satisfy the contract of every
input and output state in the transaction.
The Contract interface
----------------------
Just as every Corda state must implement the ``ContractState`` interface, every contract must implement the
``Contract`` interface:
.. container:: codeset
.. code-block:: kotlin
interface Contract {
// Implements the contract constraints in code.
@Throws(IllegalArgumentException::class)
fun verify(tx: LedgerTransaction)
}
You can read about function declarations in Kotlin `here <https://kotlinlang.org/docs/reference/functions.html>`_.
We can see that ``Contract`` expresses its constraints through a ``verify`` function that takes a transaction as input,
and:
* Throws an ``IllegalArgumentException`` if it rejects the transaction proposal
* Returns silently if it accepts the transaction proposal
Controlling IOU evolution
-------------------------
What would a good contract for an ``IOUState`` look like? There is no right or wrong answer - it depends on how you
want your CorDapp to behave.
For our CorDapp, let's impose the constraint that we only want to allow the creation of IOUs. We don't want nodes to
transfer them or redeem them for cash. One way to enforce this behaviour would be by imposing the following constraints:
* A transaction involving IOUs must consume zero inputs, and create one output of type ``IOUState``
* The transaction should also include a ``Create`` command, indicating the transaction's intent (more on commands
shortly)
* For the transactions's output IOU state:
* Its value must be non-negative
* The lender and the borrower cannot be the same entity
* The IOU's lender must sign the transaction
We can picture this transaction as follows:
.. image:: resources/simple-tutorial-transaction.png
:scale: 15%
:align: center
Defining IOUContract
--------------------
Let's write a contract that enforces these constraints. We'll do this by modifying either ``TemplateContract.java`` or
``App.kt`` and updating ``TemplateContract`` to define an ``IOUContract``:
.. container:: codeset
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/helloworld/contract.kt
:language: kotlin
:start-after: DOCSTART 01
:end-before: DOCEND 01
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/helloworld/IOUContract.java
:language: java
:start-after: DOCSTART 01
:end-before: DOCEND 01
If you're following along in Java, you'll also need to rename ``TemplateContract.java`` to ``IOUContract.java``.
Let's walk through this code step by step.
The Create command
^^^^^^^^^^^^^^^^^^
The first thing we add to our contract is a *command*. Commands serve two functions:
* They indicate the transaction's intent, allowing us to perform different verification given the situation. For
example, a transaction proposing the creation of an IOU could have to satisfy different constraints to one redeeming
an IOU
* 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
Our contract has one command, a ``Create`` command. All commands must implement the ``CommandData`` interface.
The ``CommandData`` interface is a simple marker interface for commands. In fact, its declaration is only two words
long (Kotlin interfaces do not require a body):
.. container:: codeset
.. code-block:: kotlin
interface CommandData
The verify logic
^^^^^^^^^^^^^^^^
Our contract also needs to define the actual contract constraints. For our IOU CorDapp, we won't concern ourselves with
writing valid legal prose to enforce the IOU agreement in court. Instead, we'll focus on implementing ``verify``.
Remember that our goal in writing the ``verify`` function is to write a function that, given a transaction:
* Throws an ``IllegalArgumentException`` if the transaction is considered invalid
* Does **not** throw an exception if the transaction is considered valid
In deciding whether the transaction is valid, the ``verify`` function only has access to the contents of the
transaction:
* ``tx.inputs``, which lists the inputs
* ``tx.outputs``, which lists the outputs
* ``tx.commands``, which lists the commands and their associated signers
Although we won't use them here, the ``verify`` function also has access to the transaction's attachments,
time-windows, notary and hash.
Based on the constraints enumerated above, we'll write a ``verify`` function that rejects a transaction if any of the
following are true:
* The transaction doesn't include a ``Create`` command
* The transaction has inputs
* The transaction doesn't have exactly one output
* The IOU itself is invalid
* The transaction doesn't require the lender's signature
Command constraints
~~~~~~~~~~~~~~~~~~~
Our first constraint is around the transaction's commands. We use Corda's ``requireSingleCommand`` function to test for
the presence of a single ``Create`` command. Here, ``requireSingleCommand`` performing a dual purpose:
* Asserting that there is exactly one ``Create`` command in the transaction
* Extracting the command and returning it
If the ``Create`` command isn't present, or if the transaction has multiple ``Create`` commands, contract
verification will fail.
Transaction constraints
~~~~~~~~~~~~~~~~~~~~~~~
We also want our transaction to have no inputs and only a single output - an issuance transaction.
To impose this and the subsequent constraints, we are using Corda's built-in ``requireThat`` function. ``requireThat``
provides a terse way to write the following:
* If the condition on the right-hand side doesn't evaluate to true...
* ...throw an ``IllegalArgumentException`` with the message on the left-hand side
As before, the act of throwing this exception would cause transaction verification to fail.
IOU constraints
~~~~~~~~~~~~~~~
We want to impose two constraints on the ``IOUState`` itself:
* Its value must be non-negative
* The lender and the borrower cannot be the same entity
We impose these constraints in the same ``requireThat`` block as before.
You can see that we're not restricted to only writing constraints in the ``requireThat`` block. We can also write
other statements - in this case, we're extracting the transaction's single ``IOUState`` and assigning it to a variable.
Signer constraints
~~~~~~~~~~~~~~~~~~
Finally, we require the lender's signature on the transaction. A transaction's required signers is equal to the union
of all the signers listed on the commands. We therefore extract the signers from the ``Create`` command we
retrieved earlier.
Progress so far
---------------
We've now written an ``IOUContract`` constraining the evolution of each ``IOUState`` over time:
* An ``IOUState`` can only be created, not transferred or redeemed
* Creating an ``IOUState`` requires an issuance transaction with no inputs, a single ``IOUState`` output, and a
``Create`` command
* The ``IOUState`` created by the issuance transaction must have a non-negative value, and the lender and borrower
must be different entities
The final step in the creation of our CorDapp will be to write the ``IOUFlow`` that will allow a node to orchestrate
the creation of a new ``IOUState`` on the ledger, while only sharing information on a need-to-know basis.

View File

@ -6,35 +6,42 @@
Writing the flow Writing the flow
================ ================
A flow describes the sequence of steps for agreeing a specific ledger update. By installing new flows on our node, we A flow encodes a sequence of steps that a node can run to achieve a specific ledger update. By installing new flows on
allow the node to handle new business processes. Our flow will allow a node to issue an ``IOUState`` onto the ledger. a node, we allow the node to handle new business processes. The flow we define will allow a node to issue an
``IOUState`` onto the ledger.
Flow outline Flow outline
------------ ------------
Our flow needs to take the following steps for a borrower to issue a new IOU onto the ledger: 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).
1. Create a valid transaction proposal for the creation of a new IOU The process of creating and applying this transaction to a ledger will be conducted by the IOU's lender, and will
2. Verify the transaction require the following steps:
3. Sign the transaction ourselves
4. Record the transaction in our vault 1. Building the transaction proposal for the issuance of a new IOU onto a ledger
5. Send the transaction to the IOU's lender so that they can record it too 2. Signing the transaction proposal
3. Recording the transaction
4. Sending the transaction to the IOU's borrower so that they can record it too
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.
Subflows Subflows
^^^^^^^^ ^^^^^^^^
Although our flow requirements look complex, we can delegate to existing flows to handle many of these tasks. A flow Tasks like recording a transaction or sending a transaction to a counterparty are very common in Corda. Instead of
that is invoked within the context of a larger flow to handle a repeatable task is called a *subflow*. 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*.
In our initiator flow, we can automate steps 4 and 5 using ``FinalityFlow``. In our case, we can automate steps 3 and 4 of the IOU issuance flow using ``FinalityFlow``.
All we need to do is write the steps to handle the creation and signing of the proposed transaction.
FlowLogic FlowLogic
--------- ---------
Flows are implemented as ``FlowLogic`` subclasses. You define the steps taken by the flow by overriding All flows must subclass ``FlowLogic``. You then define the steps taken by the flow by overriding ``FlowLogic.call``.
``FlowLogic.call``.
We'll write our flow in either ``TemplateFlow.java`` or ``App.kt``. Delete both the existing flows in the template, and Let's define our ``IOUFlow`` in either ``TemplateFlow.java`` or ``App.kt``. Delete both the existing flows in the
replace them with the following: template, and replace them with the following:
.. container:: codeset .. container:: codeset
@ -48,108 +55,105 @@ replace them with the following:
:start-after: DOCSTART 01 :start-after: DOCSTART 01
:end-before: DOCEND 01 :end-before: DOCEND 01
If you're following along in Java, you'll also need to rename ``TemplateFlow.java`` to ``IOUFlow.java``. If you're following along in Java, you'll also need to rename ``TemplateFlow.java`` to ``IOUFlow.java``. Let's walk
through this code step-by-step.
We now have our own ``FlowLogic`` subclass that overrides ``FlowLogic.call``. There's a few things to note: 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.call`` has a return type that matches the type parameter passed to ``FlowLogic`` - this is type returned ``FlowLogic`` subclasses can optionally have constructor parameters, which can be used as arguments to
by running the flow ``FlowLogic.call``. In our case, we have two:
* ``FlowLogic`` subclasses can have constructor parameters, which can be used as arguments to ``FlowLogic.call``
* ``FlowLogic.call`` is annotated ``@Suspendable`` - this means that the flow will be check-pointed and serialised to * ``iouValue``, which is the value of the IOU being issued
disk when it encounters a long-running operation, allowing your node to move on to running other flows. Forgetting * ``otherParty``, the IOU's borrower (the node running the flow is the lender)
this annotation out will lead to some very weird error messages
* There are also a few more annotations, on the ``FlowLogic`` subclass itself: ``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:
* ``@InitiatingFlow`` means that this flow can be started directly by the node * ``@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 * ``@StartableByRPC`` allows the node owner to start this flow via an RPC call
* We override the progress tracker, even though we are not providing any progress tracker steps yet. The progress Let's walk through the steps of ``FlowLogic.call`` itself. This is where we actually describe the procedure for
tracker is required for the node shell to establish when the flow has ended issuing the ``IOUState`` onto a ledger.
Let's walk through the steps of ``FlowLogic.call`` one-by-one: 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.
Retrieving participant information .. note::
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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.
You can see that the notary's identity is being retrieved from the node's ``ServiceHub``. Whenever we need Whenever we need information within a flow - whether it's about our own node's identity, the node's local storage,
information within a flow - whether it's about our own node, its contents, or the rest of the network - we use the or the rest of the network - we generally obtain it via the node's ``ServiceHub``.
node's ``ServiceHub``. In particular, ``ServiceHub.networkMapCache`` provides information about the other nodes on the
network and the services that they offer.
Building the transaction Building the transaction
^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
We'll build our transaction proposal in two steps: We'll build our transaction proposal in two steps:
* Creating a transaction builder * Creating the transaction's components
* Adding the desired items to the builder * Adding these components to a transaction builder
Creating a transaction builder
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To start building 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.
Transaction items Transaction items
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
Now that we have our ``TransactionBuilder``, we need to add the desired items. Remember that we're trying to build Our transaction will have the following structure:
the following transaction:
.. image:: resources/simple-tutorial-transaction.png .. image:: resources/simple-tutorial-transaction.png
:scale: 15% :scale: 15%
:align: center :align: center
So we'll need the following: * 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
* The output ``IOUState`` and its associated contract * An ``Action`` command listing the IOU's lender as a signer
* A ``Create`` command listing the IOU's lender as a signer
The command we use pairs the ``IOUContract.Create`` command defined earlier with our public key. Including this command We've already talked about the ``IOUState``, but we haven't looked at commands yet. Commands serve two functions:
in the transaction makes us one of the transaction's required signers.
We add these items to the transaction using the ``TransactionBuilder.withItems`` method, which takes a ``vararg`` of: * 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 IOUs borrower and lender
* ``StateAndContract`` or ``TransactionState`` objects, which are added to the builder as output states Each ``Command`` contains a command type plus a list of public keys. For now, we use the pre-defined
* ``StateAndRef`` objects (references to the outputs of previous transactions), which are added to the builder as input ``TemplateContract.Action`` as our command type, and we list the lender as the only public key. This means that for
state references the transaction to be valid, the lender is required to sign the transaction.
* ``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
It will modify the ``TransactionBuilder`` in-place to add these components to it. 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.
Verifying the transaction Once we have the ``TransactionBuilder``, we add our components:
^^^^^^^^^^^^^^^^^^^^^^^^^
We've now built our proposed transaction. Before we sign it, we should check that it represents a valid ledger update
proposal by verifying the transaction, which will execute each of the transaction's contracts.
If the verification fails, we have built an invalid transaction. Our flow will then end, throwing a * The command is added directly using ``TransactionBuilder.addCommand``
``TransactionVerificationException``. * 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
Signing the transaction Signing the transaction
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
Now that we have a valid transaction proposal, we need to sign it. Once the transaction is signed, no-one will be able 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. to modify the transaction without invalidating this signature. This effectively makes the transaction immutable.
The call to ``ServiceHub.toSignedTransaction`` returns a ``SignedTransaction`` - an object that pairs the We sign the transaction using ``ServiceHub.toSignedTransaction``, which returns a ``SignedTransaction``. A
transaction itself with a list of signatures over that transaction. ``SignedTransaction`` is an object that pairs a transaction with a list of signatures over that transaction.
Finalising the transaction Finalising the transaction
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
Now that we have a valid signed transaction, all that's left to do is to have it notarised and recorded by all the We now have a valid signed transaction. All that's left to do is to have it recorded by all the relevant parties. By
relevant parties. By doing so, it will become a permanent part of the ledger. As discussed, we'll handle this process doing so, it will become a permanent part of the ledger. As discussed, we'll handle this process automatically using a
automatically using a built-in flow called ``FinalityFlow``: built-in flow called ``FinalityFlow``. ``FinalityFlow`` completely automates the process of:
``FinalityFlow`` completely automates the process of:
* Notarising the transaction if required (i.e. if the transaction contains inputs and/or a time-window) * Notarising the transaction if required (i.e. if the transaction contains inputs and/or a time-window)
* Recording it in our vault * Recording it in our vault
* Sending it to the other participants (i.e. the lender) for them to record as well * Sending it to the other participants (i.e. the lender) for them to record as well
Our flow, and our CorDapp, are now ready!
Progress so far Progress so far
--------------- ---------------
We have now defined a flow that we can start on our node to completely automate the process of issuing an IOU onto the Our flow, and our CorDapp, are now ready! We have now defined a flow that we can start on our node to completely
ledger. The final step is to spin up some nodes and test our CorDapp. automate the process of issuing an IOU onto the ledger. All that's left is to spin up some nodes and test our CorDapp.

View File

@ -1,12 +0,0 @@
Hello, World!
=============
.. toctree::
:maxdepth: 1
hello-world-introduction
hello-world-template
hello-world-state
hello-world-contract
hello-world-flow
hello-world-running

View File

@ -1,63 +1,62 @@
Introduction Hello, World!
============ =============
.. toctree::
:maxdepth: 1
hello-world-template
hello-world-state
hello-world-flow
hello-world-running
By this point, :doc:`your dev environment should be set up <getting-set-up>`, you've run By this point, :doc:`your dev environment should be set up <getting-set-up>`, you've run
:doc:`your first CorDapp <tutorial-cordapp>`, and you're familiar with Corda's :doc:`key concepts <key-concepts>`. What :doc:`your first CorDapp <tutorial-cordapp>`, and you're familiar with Corda's :doc:`key concepts <key-concepts>`. What
comes next? comes next?
If you're a developer, the next step is to write your own CorDapp. Each CorDapp takes the form of a plugin that is If you're a developer, the next step is to write your own CorDapp. CorDapps are plugins that are installed on one or
installed on one or more Corda nodes, and gives them the ability to conduct some new process - anything from more Corda nodes, and give the nodes' owners the ability to make their node conduct some new process - anything from
issuing a debt instrument to making a restaurant booking. issuing a debt instrument to making a restaurant booking.
Our use-case Our use-case
------------ ------------
Our CorDapp will seek to model IOUs on ledger. An IOU short for “I Owe yoU” records the fact that one person owes Our CorDapp will model IOUs on-ledger. An IOU short for “I O(we) (yo)U” records the fact that one person owes
another a given amount of money. We can imagine that this is potentially sensitive information that we'd only want to another person a given amount of money. Clearly this is sensitive information that we'd only want to communicate on
communicate on a need-to-know basis. This is one of the areas where Corda excels - allowing a small set of parties to a need-to-know basis between the lender and the borrower. Fortunately, this is one of the areas where Corda excels.
agree on a fact without needing to share this fact with everyone else on the network, as you do with most other Corda makes it easy to allow a small set of parties to agree on a shared fact without needing to share this fact with
blockchain platforms. everyone else on the network, as is the norm in blockchain platforms.
To serve any useful function, a CorDapp needs three core elements: To serve any useful function, our CorDapp will need at least two things:
* **One or more states** the shared facts that will be agreed upon and stored on the ledger * **States**, the shared facts that Corda nodes reach consensus over and are then stored on the ledger
* **One or more contracts** the rules governing how these states can evolve over time * **Flows**, which encapsulate the procedure for carrying out a specific ledger update
* **One or more flows** the step-by-step process for carrying out a ledger update
Our IOU CorDapp is no exception. It will have the following elements: Our IOU CorDapp is no exception. It will define both a state and a flow:
State The IOUState
^^^^^ ^^^^^^^^^^^^
The states will be IOUStates, with each instance representing a single IOU. We can visualize an IOUState as follows: Our state will be the ``IOUState``. It will store the value of the IOU, as well as the identities of the lender and the
borrower. We can visualize ``IOUState`` as follows:
.. image:: resources/tutorial-state.png .. image:: resources/tutorial-state.png
:scale: 25% :scale: 25%
:align: center :align: center
Contract The IOUFlow
^^^^^^^^ ^^^^^^^^^^^
Our contract will be the IOUContract, imposing rules on the evolution of IOUs over time: Our flow will be the ``IOUFlow``. This flow will completely automate the process of issuing a new IOU onto a ledger. It
is composed of the following steps:
* Only the creation of new IOUs will be allowed
* Transferring existing IOUs or paying off an IOU with cash will not be allowed
However, we can easily extend our CorDapp to handle additional use-cases later on.
Flow
^^^^
Our flow will be the IOUFlow. It will allow a node to orchestrate the creation of a new IOU on the ledger, via the
following steps:
.. image:: resources/simple-tutorial-flow.png .. image:: resources/simple-tutorial-flow.png
:scale: 25% :scale: 25%
:align: center :align: center
In traditional distributed ledger systems, where all data is broadcast to every network participant, you dont even In traditional distributed ledger systems, where all data is broadcast to every network participant, you dont need to
think about this step you simply package up your ledger update and send it out into the world. But in Corda, where think about data flows you simply package up your ledger update and send it to everyone else on the network. But in
privacy is a core focus, flows are used to carefully control who sees what during the process of agreeing a Corda, where privacy is a core focus, flows allow us to carefully control who sees what during the process of
ledger update. agreeing a ledger update.
Progress so far Progress so far
--------------- ---------------
We've sketched out a simple CorDapp that will allow nodes to confidentially agree the creation of new IOUs. We've sketched out a simple CorDapp that will allow nodes to confidentially issue new IOUs onto a ledger.
Next, we'll be taking a look at the template project we'll be using as a base for our work. Next, we'll be taking a look at the template project we'll be using as the basis for our CorDapp.

View File

@ -9,15 +9,12 @@ Running our CorDapp
Now that we've written a CorDapp, it's time to test it by running it on some real Corda nodes. Now that we've written a CorDapp, it's time to test it by running it on some real Corda nodes.
Clean up
--------
Before running our node, delete the ``client/TemplateClient.java`` (for Java) or ``client/TemplateClient.kt`` (for
Kotlin) file. We won't be using it, and it will cause build errors unless we remove it.
Deploying our CorDapp Deploying our CorDapp
--------------------- ---------------------
Let's take a look at the nodes we're going to deploy. Open the project's ``build.gradle`` file and scroll down to the Let's take a look at the nodes we're going to deploy. Open the project's ``build.gradle`` file and scroll down to the
``task deployNodes`` section. This section defines three nodes - the Controller, PartyA, and PartyB: ``task deployNodes`` section. This section defines three nodes. There are two standard nodes (``PartyA`` and
``PartyB``), plus a special Controller node that is running the network map service and advertises a validating notary
service.
.. code:: bash .. code:: bash
@ -51,10 +48,6 @@ Let's take a look at the nodes we're going to deploy. Open the project's ``build
} }
} }
We have three standard nodes, plus a special Controller node that is running the network map service, and is also
advertising a validating notary service. Feel free to add additional node definitions here to expand the size of the
test network.
We can run this ``deployNodes`` task using Gradle. For each node definition, Gradle will: We can run this ``deployNodes`` task using Gradle. For each node definition, Gradle will:
* Package the project's source files into a CorDapp jar * Package the project's source files into a CorDapp jar
@ -117,11 +110,12 @@ We want to create an IOU of 100 with PartyB. We start the ``IOUFlow`` by typing:
start IOUFlow iouValue: 99, otherParty: "O=PartyB,L=New York,C=US" start IOUFlow iouValue: 99, otherParty: "O=PartyB,L=New York,C=US"
PartyA and PartyB will automatically agree an IOU. If the flow worked, it should have led to the recording of a new IOU This single command will cause PartyA and PartyB to automatically agree an IOU. This is one of the great advantages of
in the vaults of both PartyA and PartyB. the flow framework - it allows you to reduce complex negotiation and update processes into a single function call.
We can check the flow has worked by using an RPC operation to check the contents of each node's vault. Typing ``run`` If the flow worked, it should have recorded a new IOU in the vaults of both PartyA and PartyB. Let's check.
will display a list of the available commands. We can examine the contents of a node's vault by running:
We can check the contents of each node's vault by running:
.. container:: codeset .. container:: codeset
@ -169,31 +163,23 @@ The vaults of PartyA and PartyB should both display the following output:
stateTypes: "UNCONSUMED" stateTypes: "UNCONSUMED"
otherResults: [] otherResults: []
This is the transaction issuing our ``IOUState`` onto a ledger.
Conclusion Conclusion
---------- ----------
We have written a simple CorDapp that allows IOUs to be issued onto the ledger. Like all CorDapps, our We have written a simple CorDapp that allows IOUs to be issued onto the ledger. Our CorDapp is made up of two key
CorDapp is made up of three key parts: parts:
* The ``IOUState``, representing IOUs on the ledger * The ``IOUState``, representing IOUs on the ledger
* The ``IOUContract``, controlling the evolution of IOUs over time
* The ``IOUFlow``, orchestrating the process of agreeing the creation of an IOU on-ledger * The ``IOUFlow``, orchestrating the process of agreeing the creation of an IOU on-ledger
Together, these three parts completely determine how IOUs are created and evolved on the ledger.
Next steps Next steps
---------- ----------
There are a number of improvements we could make to this CorDapp: There are a number of improvements we could make to this CorDapp:
* We could require signatures from the lender as well the borrower, to give both parties a say in the creation of a new * We chould add unit tests, using the contract-test and flow-test frameworks
``IOUState`` * We chould change ``IOUState.value`` from an integer to a proper amount of a given currency
* We should add unit tests, using the contract-test and flow-test frameworks
* We should change ``IOUState.value`` from an integer to a proper amount of a given currency
* We could add an API, to make it easier to interact with the CorDapp * We could add an API, to make it easier to interact with the CorDapp
We will explore some of these improvements in future tutorials. But you should now be ready to develop your own But for now, the biggest priority is to add an ``IOUContract`` imposing constraints on the evolution of each
CorDapps. You can find a list of sample CorDapps `here <https://www.corda.net/samples/>`_. ``IOUState`` over time. This will be the focus of our next tutorial.
As you write CorDapps, you can learn more about the Corda API :doc:`here <corda-api>`.
If you get stuck at any point, please reach out on `Slack <https://slack.corda.net/>`_,
`Discourse <https://discourse.corda.net/>`_, or `Stack Overflow <https://stackoverflow.com/questions/tagged/corda>`_.

View File

@ -12,8 +12,8 @@ represent an IOU.
The ContractState interface The ContractState interface
--------------------------- ---------------------------
In Corda, any JVM class that implements the ``ContractState`` interface is a valid state. ``ContractState`` is A Corda state is any instance of a class that implements the ``ContractState`` interface. The ``ContractState``
defined as follows: interface is defined as follows:
.. container:: codeset .. container:: codeset
@ -24,42 +24,42 @@ defined as follows:
val participants: List<AbstractParty> val participants: List<AbstractParty>
} }
The first thing you'll probably notice about this interface declaration is that its not written in Java or another
common language. The core Corda platform, including the interface declaration above, is entirely written in Kotlin.
Learning some Kotlin will be very useful for understanding how Corda works internally, and usually only takes an
experienced Java developer a day or so to pick up. However, learning Kotlin isn't essential. Because Kotlin code
compiles down to JVM bytecode, CorDapps written in other JVM languages can interoperate with Corda.
If you do want to dive into Kotlin, there's an official
`getting started guide <https://kotlinlang.org/docs/tutorials/>`_, and a series of
`Kotlin Koans <https://kotlinlang.org/docs/tutorials/koans.html>`_.
We can see that the ``ContractState`` interface has a single field, ``participants``. ``participants`` is a list of the We can see that the ``ContractState`` interface has a single field, ``participants``. ``participants`` is a list of the
entities for which this state is relevant. entities for which this state is relevant.
Beyond this, our state is free to define any fields, methods, helpers or inner classes it requires to accurately Beyond this, our state is free to define any fields, methods, helpers or inner classes it requires to accurately
represent a given class of shared facts on the ledger. represent a given type of shared fact on the ledger.
``ContractState`` also has several child interfaces that you may wish to implement depending on your state, such as .. note::
``LinearState`` and ``OwnableState``. See :doc:`api-states` for more information.
The first thing you'll probably notice about the declaration of ``ContractState`` is that its not written in Java
or another common language. The core Corda platform, including the interface declaration above, is entirely written
in Kotlin.
Learning some Kotlin will be very useful for understanding how Corda works internally, and usually only takes an
experienced Java developer a day or so to pick up. However, learning Kotlin isn't essential. Because Kotlin code
compiles to JVM bytecode, CorDapps written in other JVM languages such as Java can interoperate with Corda.
If you do want to dive into Kotlin, there's an official
`getting started guide <https://kotlinlang.org/docs/tutorials/>`_, and a series of
`Kotlin Koans <https://kotlinlang.org/docs/tutorials/koans.html>`_.
Modelling IOUs Modelling IOUs
-------------- --------------
How should we define the ``IOUState`` representing IOUs on the ledger? Beyond implementing the ``ContractState`` How should we define the ``IOUState`` representing IOUs on the ledger? Beyond implementing the ``ContractState``
interface, our ``IOUState`` will also need properties to track the relevant features of the IOU: interface, our ``IOUState`` will also need properties to track the relevant features of the IOU:
* The value of the IOU
* The lender of the IOU * The lender of the IOU
* The borrower of the IOU * The borrower of the IOU
* The value of the IOU
There are many more fields you could include, such as the IOU's currency. We'll abstract them away for now. If There are many more fields you could include, such as the IOU's currency, but let's ignore those for now. Adding them
you wish to add them later, its as simple as adding an additional property to your class definition. later is often as simple as adding an additional property to your class definition.
Defining IOUState Defining IOUState
----------------- -----------------
Let's open ``TemplateState.java`` (for Java) or ``App.kt`` (for Kotlin) and update ``TemplateState`` to Let's get started by opening ``TemplateState.java`` (for Java) or ``App.kt`` (for Kotlin) and updating
define an ``IOUState``: ``TemplateState`` to define an ``IOUState``:
.. container:: codeset .. container:: codeset
@ -75,23 +75,35 @@ define an ``IOUState``:
If you're following along in Java, you'll also need to rename ``TemplateState.java`` to ``IOUState.java``. If you're following along in Java, you'll also need to rename ``TemplateState.java`` to ``IOUState.java``.
We've made the following changes: To define ``IOUState``, we've made the following changes:
* We've renamed ``TemplateState`` to ``IOUState`` * We've renamed the ``TemplateState`` class to ``IOUState``
* We've added properties for ``value``, ``lender`` and ``borrower`` (along with any getters and setters in Java): * We've added properties for ``value``, ``lender`` and ``borrower``, along with the required getters and setters in
Java:
* ``value`` is just a standard int (in Java)/Int (in Kotlin) * ``value`` is of type ``int`` (in Java)/``Int`` (in Kotlin)
* ``lender`` and ``borrower`` are of type ``Party``. ``Party`` is a built-in Corda type that represents an entity on * ``lender`` and ``borrower`` are of type ``Party``
the network.
* ``Party`` is a built-in Corda type that represents an entity on the network
* We've overridden ``participants`` to return a list of the ``lender`` and ``borrower`` * We've overridden ``participants`` to return a list of the ``lender`` and ``borrower``
* Actions such as changing a state's contract or notary will require approval from all the ``participants`` * ``participants`` is a list of all the parties who should be notified of the creation or consumption of this state
The IOUs that we issue onto a ledger will simply be instances of this class.
Progress so far Progress so far
--------------- ---------------
We've defined an ``IOUState`` that can be used to represent IOUs as shared facts on the ledger. As we've seen, states in We've defined an ``IOUState`` that can be used to represent IOUs as shared facts on a ledger. As we've seen, states in
Corda are simply JVM classes that implement the ``ContractState`` interface. They can have any additional properties and Corda are simply classes that implement the ``ContractState`` interface. They can have any additional properties and
methods you like. methods you like.
Next, we'll be writing our ``IOUContract`` to control the evolution of these shared facts over time. All that's left to do is write the ``IOUFlow`` that will allow a node to orchestrate the creation of a new ``IOUState``
on the ledger, while only sharing information on a need-to-know basis.
What about the contract?
------------------------
If you've read the white paper or Key Concepts section, you'll know that each state has an associated contract that
imposes invariants on how the state evolves over time. Including a contract isn't crucial for our first CorDapp, so
we'll just use the empty ``TemplateContract`` and ``TemplateContract.Commands.Action`` command defined by the template
for now. In the next tutorial, we'll implement our own contract and command.

View File

@ -7,35 +7,42 @@
The CorDapp Template The CorDapp Template
==================== ====================
When writing a new CorDapp, youll generally want to base it on the When writing a new CorDapp, youll generally want to base it on the standard templates:
`Java Cordapp Template <https://github.com/corda/cordapp-template-java>`_ or the equivalent
`Kotlin Cordapp Template <https://github.com/corda/cordapp-template-kotlin>`_. The Cordapp Template allows you to
quickly deploy your CorDapp onto a local test network of dummy nodes to evaluate its functionality.
Note that there's no need to download and install Corda itself. As long as you're working from a stable Milestone * The `Java Cordapp Template <https://github.com/corda/cordapp-template-java>`_
branch, the required libraries will be downloaded automatically from an online repository. * The `Kotlin Cordapp Template <https://github.com/corda/cordapp-template-kotlin>`_
If you do wish to work from the latest snapshot, please follow the instructions The Cordapp templates provide the required boilerplate for developing a CorDapp, and allow you to quickly deploy your
`here <https://docs.corda.net/tutorial-cordapp.html#using-a-snapshot-release>`_. CorDapp onto a local test network of dummy nodes to test its functionality.
CorDapps can be written in both Java and Kotlin, and will be providing the code in both languages in this tutorial.
Note that there's no need to download and install Corda itself. Corda V1.0's required libraries will be downloaded
automatically from an online Maven repository.
Downloading the template Downloading the template
------------------------ ------------------------
Open a terminal window in the directory where you want to download the CorDapp template, and run the following commands: To download the template, open a terminal window in the directory where you want to download the CorDapp template, and
run the following command:
.. code-block:: bash .. code-block:: bash
# Clone the template from GitHub:
git clone https://github.com/corda/cordapp-template-java.git ; cd cordapp-template-java git clone https://github.com/corda/cordapp-template-java.git ; cd cordapp-template-java
*or* *or*
git clone https://github.com/corda/cordapp-template-kotlin.git ; cd cordapp-template-kotlin git clone https://github.com/corda/cordapp-template-kotlin.git ; cd cordapp-template-kotlin
Opening the template in IntelliJ
--------------------------------
Once the template is download, open it in IntelliJ by following the instructions here:
https://docs.corda.net/tutorial-cordapp.html#opening-the-example-cordapp-in-intellij.
Template structure Template structure
------------------ ------------------
We can write our CorDapp in either Java or Kotlin, and will be providing the code in both languages throughout. To The template has a number of files, but we can ignore most of them. To implement our IOU CorDapp in Java, we'll only
implement our IOU CorDapp in Java, we'll need to modify three files. For Kotlin, we'll simply be modifying the need to modify two files. For Kotlin, we'll simply be modifying the ``App.kt`` file:
``App.kt`` file:
.. container:: codeset .. container:: codeset
@ -44,23 +51,25 @@ implement our IOU CorDapp in Java, we'll need to modify three files. For Kotlin,
// 1. The state // 1. The state
src/main/java/com/template/TemplateState.java src/main/java/com/template/TemplateState.java
// 2. The contract // 2. The flow
src/main/java/com/template/TemplateContract.java
// 3. The flow
src/main/java/com/template/TemplateFlow.java src/main/java/com/template/TemplateFlow.java
.. code-block:: kotlin .. code-block:: kotlin
src/main/kotlin/com/template/App.kt src/main/kotlin/com/template/App.kt
To prevent build errors later on, you should delete the following file: Clean up
--------
To prevent build errors later on, we should delete the following files before we begin:
* Java: ``src/test/java/com/template/FlowTests.java`` * Java:
* Kotlin: ``src/test/kotlin/com/template/FlowTests.kt`` * ``src/main/java/com/template/TemplateClient.java``
* ``src/test/java/com/template/FlowTests.java``
* Kotlin:
* ``src/main/kotlin/com/template/TemplateClient.kt``
* ``src/test/kotlin/com/template/FlowTests.kt``
Progress so far Progress so far
--------------- ---------------
We now have a template that we can build upon to define our IOU CorDapp. We now have a template that we can build upon to define our IOU CorDapp. Let's start by defining the ``IOUState``.
We'll begin writing the CorDapp proper by writing the definition of the ``IOUState``.

View File

@ -14,7 +14,7 @@ Want to see Corda running? Download our demonstration application `DemoBench <ht
follow our :doc:`quickstart guide </quickstart-index>`. follow our :doc:`quickstart guide </quickstart-index>`.
If you want to start coding on Corda, then familiarise yourself with the :doc:`key concepts </key-concepts>`, then read If you want to start coding on Corda, then familiarise yourself with the :doc:`key concepts </key-concepts>`, then read
our :doc:`Hello, World! tutorial </hello-world-index>`. For the background behind Corda, read the non-technical our :doc:`Hello, World! tutorial </hello-world-introduction>`. For the background behind Corda, read the non-technical
`introductory white paper`_ or for more detail, the `technical white paper`_. `introductory white paper`_ or for more detail, the `technical white paper`_.
If you have questions or comments, then get in touch on `Slack <https://slack.corda.net/>`_ or write a question on If you have questions or comments, then get in touch on `Slack <https://slack.corda.net/>`_ or write a question on

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 216 KiB

View File

@ -4,16 +4,80 @@
<script type="text/javascript" src="_static/jquery.js"></script> <script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/codesets.js"></script> <script type="text/javascript" src="_static/codesets.js"></script>
Updating the contract Writing the contract
===================== ====================
Remember that each state references a contract. The contract imposes constraints on transactions involving that state. It's easy to imagine that most CorDapps will want to impose some constraints on how their states evolve over time:
If the transaction does not obey the constraints of all the contracts of all its states, it cannot become a valid
ledger update.
We need to modify our contract so that the borrower's signature is required in any IOU creation transaction. * A cash CorDapp will not want to allow users to create transactions that generate money out of thin air (at least
without the involvement of a central bank or commercial bank)
* A loan CorDapp might not want to allow the creation of negative-valued loans
* An asset-trading CorDapp will not want to allow users to finalise a trade without the agreement of their counterparty
In ``IOUContract.java``/``IOUContract.kt``, change the imports block to the following: In Corda, we impose constraints on how states can evolve using contracts.
.. note::
Contracts in Corda are very different to the smart contracts of other distributed ledger platforms. They are not
stateful objects representing the current state of the world. Instead, like a real-world contract, they simply
impose rules on what kinds of transactions are allowed.
Every state has an associated contract. A transaction is invalid if it does not satisfy the contract of every input and
output state in the transaction.
The Contract interface
----------------------
Just as every Corda state must implement the ``ContractState`` interface, every contract must implement the
``Contract`` interface:
.. container:: codeset
.. code-block:: kotlin
interface Contract {
// Implements the contract constraints in code.
@Throws(IllegalArgumentException::class)
fun verify(tx: LedgerTransaction)
}
We can see that ``Contract`` expresses its constraints through a ``verify`` function that takes a transaction as input,
and:
* Throws an ``IllegalArgumentException`` if it rejects the transaction proposal
* Returns silently if it accepts the transaction proposal
Controlling IOU evolution
-------------------------
What would a good contract for an ``IOUState`` look like? There is no right or wrong answer - it depends on how you
want your CorDapp to behave.
For our CorDapp, let's impose the constraint that we only want to allow the creation of IOUs. We don't want nodes to
transfer them or redeem them for cash. One way to enforce this behaviour would be by imposing the following constraints:
* A transaction involving IOUs must consume zero inputs, and create one output of type ``IOUState``
* The transaction should also include a ``Create`` command, indicating the transaction's intent (more on commands
shortly)
We might also want to impose some constraints on the properties of the issued ``IOUState``:
* Its value must be non-negative
* The lender and the borrower cannot be the same entity
And finally, we'll want to impose constraints on who is required to sign the transaction:
* The IOU's lender must sign
* The IOU's borrower must sign
We can picture this transaction as follows:
.. image:: resources/simple-tutorial-transaction-2.png
:scale: 15%
:align: center
Defining IOUContract
--------------------
Let's write a contract that enforces these constraints. We'll do this by modifying either ``TemplateContract.java`` or
``App.kt`` and updating ``TemplateContract`` to define an ``IOUContract``:
.. container:: codeset .. container:: codeset
@ -27,23 +91,107 @@ In ``IOUContract.java``/``IOUContract.kt``, change the imports block to the foll
:start-after: DOCSTART 01 :start-after: DOCSTART 01
:end-before: DOCEND 01 :end-before: DOCEND 01
And update the final block of constraints in the ``requireThat`` block as follows: If you're following along in Java, you'll also need to rename ``TemplateContract.java`` to ``IOUContract.java``.
Let's walk through this code step by step.
The Create command
^^^^^^^^^^^^^^^^^^
The first thing we add to our contract is a *command*. Commands serve two functions:
* They indicate the transaction's intent, allowing us to perform different verification for different types of
transaction. For example, a transaction proposing the creation of an IOU could have to meet different constraints
to one redeeming an IOU
* 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
Our contract has one command, a ``Create`` command. All commands must implement the ``CommandData`` interface.
The ``CommandData`` interface is a simple marker interface for commands. In fact, its declaration is only two words
long (Kotlin interfaces do not require a body):
.. container:: codeset .. container:: codeset
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/twoparty/contract.kt .. code-block:: kotlin
:language: kotlin
:start-after: DOCSTART 02
:end-before: DOCEND 02
:dedent: 12
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUContract.java interface CommandData
:language: java
:start-after: DOCSTART 02 The verify logic
:end-before: DOCEND 02 ^^^^^^^^^^^^^^^^
:dedent: 12 Our contract also needs to define the actual contract constraints by implementing ``verify``. Our goal in writing the
``verify`` function is to write a function that, given a transaction:
* Throws an ``IllegalArgumentException`` if the transaction is considered invalid
* Does **not** throw an exception if the transaction is considered valid
In deciding whether the transaction is valid, the ``verify`` function only has access to the contents of the
transaction:
* ``tx.inputs``, which lists the inputs
* ``tx.outputs``, which lists the outputs
* ``tx.commands``, which lists the commands and their associated signers
As well as to the transaction's attachments and time-window, which we won't use here.
Based on the constraints enumerated above, we need to write a ``verify`` function that rejects a transaction if any of
the following are true:
* The transaction doesn't include a ``Create`` command
* The transaction has inputs
* The transaction doesn't have exactly one output
* The IOU itself is invalid
* The transaction doesn't require the lender's signature
Command constraints
~~~~~~~~~~~~~~~~~~~
Our first constraint is around the transaction's commands. We use Corda's ``requireSingleCommand`` function to test for
the presence of a single ``Create`` command.
If the ``Create`` command isn't present, or if the transaction has multiple ``Create`` commands, an exception will be
thrown and contract verification will fail.
Transaction constraints
~~~~~~~~~~~~~~~~~~~~~~~
We also want our transaction to have no inputs and only a single output - an issuance transaction.
To impose this and the subsequent constraints, we are using Corda's built-in ``requireThat`` block. ``requireThat``
provides a terse way to write the following:
* If the condition on the right-hand side doesn't evaluate to true...
* ...throw an ``IllegalArgumentException`` with the message on the left-hand side
As before, the act of throwing this exception causes the transaction to be considered invalid.
IOU constraints
~~~~~~~~~~~~~~~
We want to impose two constraints on the ``IOUState`` itself:
* Its value must be non-negative
* The lender and the borrower cannot be the same entity
We impose these constraints in the same ``requireThat`` block as before.
You can see that we're not restricted to only writing constraints in the ``requireThat`` block. We can also write
other statements - in this case, extracting the transaction's single ``IOUState`` and assigning it to a variable.
Signer constraints
~~~~~~~~~~~~~~~~~~
Finally, we require both the lender and the borrower to be required signers on the transaction. A transaction's
required signers is equal to the union of all the signers listed on the commands. We therefore extract the signers from
the ``Create`` command we retrieved earlier.
This is an absolutely essential constraint - it ensures that no ``IOUState`` can ever be created on the ledger without
the express agreement of both the lender and borrower nodes.
Progress so far Progress so far
--------------- ---------------
Our contract now imposes an additional constraint - the borrower must also sign an IOU creation transaction. Next, we We've now written an ``IOUContract`` constraining the evolution of each ``IOUState`` over time:
need to update ``IOUFlow`` so that it actually gathers the borrower's signature as part of the flow.
* An ``IOUState`` can only be created, not transferred or redeemed
* Creating an ``IOUState`` requires an issuance transaction with no inputs, a single ``IOUState`` output, and a
``Create`` command
* The ``IOUState`` created by the issuance transaction must have a non-negative value, and the lender and borrower
must be different entities
Next, we'll update the ``IOUFlow`` so that it obeys these contract constraints when issuing an ``IOUState`` onto the
ledger.

View File

@ -7,20 +7,16 @@
Updating the flow Updating the flow
================= =================
To update the flow, we'll need to do two things: We now need to update our flow to achieve three things:
* Update the lender's side of the flow to request the borrower's signature * Verifying that the transaction proposal we build fulfills the ``IOUContract`` constraints
* Create a flow for the borrower to run in response to a signature request from the lender * Updating the lender's side of the flow to request the borrower's signature
* Creating a response flow for the borrower that responds to the signature request from the lender
Updating the lender's flow We'll do this by modifying the flow we wrote in the previous tutorial.
--------------------------
In the original CorDapp, we automated the process of notarising a transaction and recording it in every party's vault
by invoking a built-in flow called ``FinalityFlow`` as a subflow. We're going to use another pre-defined flow, called
``CollectSignaturesFlow``, to gather the borrower's signature.
We also need to add the borrower's public key to the transaction's command, making the borrower one of the required
signers on the transaction.
Verifying the transaction
-------------------------
In ``IOUFlow.java``/``IOUFlow.kt``, change the imports block to the following: In ``IOUFlow.java``/``IOUFlow.kt``, change the imports block to the following:
.. container:: codeset .. container:: codeset
@ -51,12 +47,28 @@ And update ``IOUFlow.call`` by changing the code following the creation of the `
:end-before: DOCEND 02 :end-before: DOCEND 02
:dedent: 8 :dedent: 8
To make the borrower a required signer, we simply add the borrower's public key to the list of signers on the command. In the original CorDapp, we automated the process of notarising a transaction and recording it in every party's vault
by invoking a built-in flow called ``FinalityFlow`` as a subflow. We're going to use another pre-defined flow,
``CollectSignaturesFlow``, to gather the borrower's signature.
We now need to communicate with the borrower to request their signature. Whenever you want to communicate with another First, we need to update the command. We are now using ``IOUContract.Commands.Create``, rather than
party in the context of a flow, you first need to establish a flow session with them. If the counterparty has a ``TemplateContract.Commands.Action``. We also want to make the borrower a required signer, as per the contract
``FlowLogic`` registered to respond to the ``FlowLogic`` initiating the session, a session will be established. All constraints. This is as simple as adding the borrower's public key to the transaction's command.
communication between the two ``FlowLogic`` instances will then place as part of this session.
We also need to add the output state to the transaction using a reference to the ``IOUContract``, instead of to the old
``TemplateContract``.
Now that our state is governed by a real contract, we'll want to check that our transaction proposal satisfies these
requirements before kicking off the signing process. We do this by calling ``TransactionBuilder.verify`` on our
transaction proposal before finalising it by adding our signature.
Requesting the borrower's signature
-----------------------------------
We now need to communicate with the borrower to request their signature over the transaction. Whenever you want to
communicate with another party in the context of a flow, you first need to establish a flow session with them. If the
counterparty has a ``FlowLogic`` registered to respond to the ``FlowLogic`` initiating the session, a session will be
established. All communication between the two ``FlowLogic`` instances will then place as part of this session.
Once we have a session with the borrower, we gather the borrower's signature using ``CollectSignaturesFlow``, which Once we have a session with the borrower, we gather the borrower's signature using ``CollectSignaturesFlow``, which
takes: takes:
@ -66,12 +78,13 @@ takes:
And returns a transaction signed by all the required signers. And returns a transaction signed by all the required signers.
We then pass this fully-signed transaction into ``FinalityFlow``. We can then pass this fully-signed transaction into ``FinalityFlow``.
Creating the borrower's flow Creating the borrower's flow
---------------------------- ----------------------------
We're now ready to write the lender's flow, which will respond to the borrower's attempt to gather our signature. On the lender's side, we used ``CollectSignaturesFlow`` to automate the collection of signatures. To allow the lender
In a new ``IOUFlowResponder.java`` file in Java, or within the ``App.kt`` file in Kotlin, add the following class: to respond, we need to write a response flow as well. In a new ``IOUFlowResponder.java`` file in Java, or within the
``App.kt`` file in Kotlin, add the following class:
.. container:: codeset .. container:: codeset
@ -93,18 +106,15 @@ The flow is annotated with ``InitiatedBy(IOUFlow.class)``, which means that your
will this message from the ``IOUFlow`` be? If we look at the definition of ``CollectSignaturesFlow``, we can see that will this message from the ``IOUFlow`` be? If we look at the definition of ``CollectSignaturesFlow``, we can see that
we'll be sent a ``SignedTransaction``, and are expected to send back our signature over that transaction. we'll be sent a ``SignedTransaction``, and are expected to send back our signature over that transaction.
We could handle this manually. However, there is also a pre-defined flow called ``SignTransactionFlow`` that can handle We could write our own flow to handle this process. However, there is also a pre-defined flow called
this process for us automatically. ``SignTransactionFlow`` is an abstract class, and we must subclass it and override ``SignTransactionFlow`` that can handle the process automatically. The only catch is that ``SignTransactionFlow`` is an
``SignTransactionFlow.checkTransaction``. abstract class - we must subclass it and override ``SignTransactionFlow.checkTransaction``.
Once we've defined the subclass, we invoke it using ``FlowLogic.subFlow``, and the communication with the borrower's
and the lender's flow is conducted automatically.
CheckTransactions CheckTransactions
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
``SignTransactionFlow`` will automatically verify the transaction and its signatures before signing it. However, just ``SignTransactionFlow`` will automatically verify the transaction and its signatures before signing it. However, just
because a transaction is valid doesn't mean we necessarily want to sign. What if we don't want to deal with the because a transaction is contractually valid doesn't mean we necessarily want to sign. What if we don't want to deal
counterparty in question, or the value is too high, or we're not happy with the transaction's structure? with the counterparty in question, or the value is too high, or we're not happy with the transaction's structure?
Overriding ``SignTransactionFlow.checkTransaction`` allows us to define these additional checks. In our case, we are Overriding ``SignTransactionFlow.checkTransaction`` allows us to define these additional checks. In our case, we are
checking that: checking that:
@ -113,13 +123,24 @@ checking that:
* The IOU's value is less than some amount (100 in this case) * The IOU's value is less than some amount (100 in this case)
If either of these conditions are not met, we will not sign the transaction - even if the transaction and its If either of these conditions are not met, we will not sign the transaction - even if the transaction and its
signatures are valid. signatures are contractually valid.
Once we've defined the ``SignTransactionFlow`` subclass, we invoke it using ``FlowLogic.subFlow``, and the
communication with the borrower's and the lender's flow is conducted automatically.
Conclusion Conclusion
---------- ----------
We have now updated our flow to gather the lender's signature as well, in line with the constraints in ``IOUContract``. We have now updated our flow to verify the transaction and gather the lender's signature, in line with the constraints
We can now run our updated CorDapp, using the instructions :doc:`here <hello-world-running>`. defined in ``IOUContract``. We can now re-run our updated CorDapp, using the
:doc:`same instructions as before <hello-world-running>`.
Our CorDapp now requires agreement from both the lender and the borrower before an IOU can be created on the ledger. Our CorDapp now imposes restrictions on the issuance of IOUs. Most importantly, IOU issuance now requires agreement
This prevents either the lender or the borrower from unilaterally updating the ledger in a way that only benefits from both the lender and the borrower before an IOU can be created on the ledger. This prevents either the lender or
themselves. the borrower from unilaterally updating the ledger in a way that only benefits themselves.
You should now be ready to develop your own CorDapps. You can also find a list of sample CorDapps
`here <https://www.corda.net/samples/>`_. As you write CorDapps, you'll also want to learn more about the
:doc:`Corda API <corda-api>`.
If you get stuck at any point, please reach out on `Slack <https://slack.corda.net/>`_ or
`Stack Overflow <https://stackoverflow.com/questions/tagged/corda>`_.

View File

@ -1,9 +0,0 @@
Two-party flows
===============
.. toctree::
:maxdepth: 1
tut-two-party-introduction
tut-two-party-contract
tut-two-party-flow

View File

@ -1,23 +1,24 @@
Introduction Hello, World! Pt.2 - Contract constraints
============ =========================================
.. note:: This tutorial extends the CorDapp built during the :doc:`Hello, World tutorial <hello-world-index>`. .. toctree::
:maxdepth: 1
In the Hello, World tutorial, we built a CorDapp allowing us to model IOUs on ledger. Our CorDapp was made up of three tut-two-party-contract
tut-two-party-flow
.. note:: This tutorial extends the CorDapp built during the :doc:`Hello, World tutorial <hello-world-introduction>`.
In the Hello, World tutorial, we built a CorDapp allowing us to model IOUs on ledger. Our CorDapp was made up of two
elements: elements:
* An ``IOUState``, representing IOUs on the ledger * An ``IOUState``, representing IOUs on the ledger
* An ``IOUContract``, controlling the evolution of IOUs over time
* An ``IOUFlow``, orchestrating the process of agreeing the creation of an IOU on-ledger * An ``IOUFlow``, orchestrating the process of agreeing the creation of an IOU on-ledger
However, in our original CorDapp, only the IOU's lender was required to sign transactions issuing IOUs. The borrower However, our CorDapp did not impose any constraints on the evolution of IOUs on the ledger over time. Anyone was free
had no say in whether the issuance of the IOU was a valid ledger update or not. to create IOUs of any value, between any party.
In this tutorial, we'll update our code so that the lender requires the borrower's agreement before they can issue an In this tutorial, we'll write a contract to imposes rules on how an ``IOUState`` can change over time. In turn, this
IOU onto the ledger. We'll need to make two changes: will require some small changes to the flow we defined in the previous tutorial.
* The ``IOUContract`` will need to be updated so that transactions involving an ``IOUState`` will require the borrower's We'll start by writing the contract.
signature (as well as the lender's) to become valid ledger updates
* The ``IOUFlow`` will need to be updated to allow for the gathering of the borrower's signature
We'll start by updating the contract.

View File

@ -4,8 +4,8 @@ Tutorials
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
hello-world-index hello-world-introduction
tut-two-party-index tut-two-party-introduction
tutorial-contract tutorial-contract
tutorial-test-dsl tutorial-test-dsl
contract-upgrade contract-upgrade