mirror of
https://github.com/corda/corda.git
synced 2025-01-23 04:48:09 +00:00
Reflects tutorial changes and CorDapp build docs changes from release-V1.
This commit is contained in:
parent
55bcf295ee
commit
85071f227a
@ -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
|
||||||
|
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
Building a CorDapp
|
CorDapps
|
||||||
==================
|
========
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
cordapp-overview
|
cordapp-overview
|
||||||
writing-cordapps
|
writing-a-cordapp
|
||||||
cordapp-build-systems
|
cordapp-build-systems
|
||||||
|
building-against-master
|
||||||
corda-api
|
corda-api
|
||||||
flow-cookbook
|
flow-cookbook
|
||||||
cheat-sheet
|
cheat-sheet
|
@ -6,9 +6,9 @@ Corda nodes
|
|||||||
|
|
||||||
deploying-a-node
|
deploying-a-node
|
||||||
running-a-node
|
running-a-node
|
||||||
|
corda-configuration-file
|
||||||
clientrpc
|
clientrpc
|
||||||
shell
|
shell
|
||||||
node-database
|
node-database
|
||||||
node-administration
|
node-administration
|
||||||
corda-configuration-file
|
|
||||||
out-of-process-verification
|
out-of-process-verification
|
@ -1,47 +1,30 @@
|
|||||||
CorDapp Build Systems
|
Building a CorDapp
|
||||||
=====================
|
==================
|
||||||
|
|
||||||
Cordapps run on the Corda platform and integrate with it and each other. To learn more about the basics of a Cordapp
|
.. contents::
|
||||||
please read :doc:`cordapp-overview`. To learn about writing a Cordapp as a developer please read :doc:`writing-cordapps`.
|
|
||||||
|
|
||||||
This article will specifically deal with how to build cordapps, specifically with Gradle.
|
Cordapps run on the Corda platform and integrate with it and each other. This article explains how to build CorDapps.
|
||||||
|
To learn what a CorDapp is, please read :doc:`cordapp-overview`.
|
||||||
|
|
||||||
CorDapp JAR format
|
CorDapp format
|
||||||
------------------
|
--------------
|
||||||
|
A CorDapp is a semi-fat JAR that contains all of the CorDapp's dependencies *except* the Corda core libraries and any
|
||||||
|
other CorDapps it depends on.
|
||||||
|
|
||||||
The first step to integrating a Cordapp with Corda is to ensure it is in the correct format. The correct format of a JAR
|
For example, if a Cordapp depends on ``corda-core``, ``your-other-cordapp`` and ``apache-commons``, then the Cordapp
|
||||||
is a semi-fat JAR that contains all of its own dependencies *except* the Corda core libraries and other Cordapps.
|
JAR will contain:
|
||||||
|
|
||||||
For example if your Cordapp depends on ``corda-core``, ``your-other-cordapp`` and ``apache-commons`` then the Cordapp
|
* All classes and resources from the ``apache-commons`` JAR and its dependencies
|
||||||
JAR will contain all classes and resources from the ``apache-commons`` JAR and its dependencies and *nothing* from the
|
* *Nothing* from the other two JARs
|
||||||
other two JARs.
|
|
||||||
|
|
||||||
.. note:: The rest of this tutorial assumes you are using ``gradle``, the ``cordapp`` plugin and have forked from
|
Build tools
|
||||||
one of our cordapp templates.
|
-----------
|
||||||
|
In the instructions that follow, we assume you are using ``gradle`` and the ``cordformation`` plugin to build your
|
||||||
|
CorDapp. See the `example build file <https://github.com/corda/cordapp-template-kotlin/blob/release-V1/build.gradle>`_
|
||||||
|
from the CorDapp template.
|
||||||
|
|
||||||
The ``jar`` task included by default in the cordapp templates will automatically build your JAR in this format as long
|
Setting your dependencies
|
||||||
as your dependencies are correctly set.
|
-------------------------
|
||||||
|
|
||||||
The filename of the jar must include some sort of unique identifier to deduplicate it from other releases of the same
|
|
||||||
CorDapp. This is typically done by appending the version string. It should not change once the jar has been deployed on
|
|
||||||
a node. If it is then make sure no one is checking ``FlowContext.appName`` (see :doc:`versioning`).
|
|
||||||
|
|
||||||
Building against Corda
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
To build against Corda you must do the following to your ``build.gradle``;
|
|
||||||
|
|
||||||
* Add the ``net.corda:corda:<version>`` JAR as a ``cordaRuntime`` dependency.
|
|
||||||
* Add each compile dependency (eg ``corda-core``) as a ``cordaCompile`` dependency.
|
|
||||||
|
|
||||||
To make use of the Corda test facilities you must;
|
|
||||||
|
|
||||||
* Add ``net.corda:corda-test-utils:<version>`` as a ``testCompile`` dependency (eg; a default Java/Kotlin compile task).
|
|
||||||
|
|
||||||
.. warning:: Never include ``corda-test-utils`` as a ``compile`` or ``cordaCompile`` dependency.
|
|
||||||
|
|
||||||
These configurations work by the ``cordapp`` plugin adding ``cordaCompile`` as a new configuration that ``compile``
|
|
||||||
extends from, and ``cordaRuntime`` which ``runtime`` extends from.
|
|
||||||
|
|
||||||
Choosing your Corda version
|
Choosing your Corda version
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -49,11 +32,15 @@ The following two lines of the ``build.gradle`` file define the Corda version us
|
|||||||
|
|
||||||
.. sourcecode:: groovy
|
.. sourcecode:: groovy
|
||||||
|
|
||||||
ext.corda_release_version = '0.13.0'
|
ext.corda_release_version = '1.0.0'
|
||||||
ext.corda_gradle_plugins_version = '0.13.3'
|
ext.corda_gradle_plugins_version = '1.0.0'
|
||||||
|
|
||||||
In this case, our CorDapp will use the Milestone 13 release of Corda, and version 13.3 of the Corda gradle plugins. You
|
In this case, our CorDapp will use:
|
||||||
can find the latest published version of both here: https://bintray.com/r3/corda.
|
|
||||||
|
* Version 1.0 of Corda
|
||||||
|
* Version 1.0 of the Corda gradle plugins
|
||||||
|
|
||||||
|
You can find the latest published version of both here: https://bintray.com/r3/corda.
|
||||||
|
|
||||||
``corda_gradle_plugins_versions`` are given in the form ``major.minor.patch``. You should use the same ``major`` and
|
``corda_gradle_plugins_versions`` are given in the form ``major.minor.patch``. You should use the same ``major`` and
|
||||||
``minor`` versions as the Corda version you are using, and the latest ``patch`` version. A list of all the available
|
``minor`` versions as the Corda version you are using, and the latest ``patch`` version. A list of all the available
|
||||||
@ -61,34 +48,39 @@ versions can be found here: https://bintray.com/r3/corda/cordapp.
|
|||||||
|
|
||||||
In certain cases, you may also wish to build against the unstable Master branch. See :doc:`building-against-master`.
|
In certain cases, you may also wish to build against the unstable Master branch. See :doc:`building-against-master`.
|
||||||
|
|
||||||
Building against CorDapps
|
Corda dependencies
|
||||||
-------------------------
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
The ``cordformation`` plugin adds:
|
||||||
|
|
||||||
To build against a Cordapp you must add it as a ``cordapp`` dependency to your ``build.gradle``.
|
* ``cordaCompile`` as a new configuration that ``compile`` extends from
|
||||||
|
* ``cordaRuntime`` which ``runtime`` extends from
|
||||||
|
|
||||||
Other Dependencies
|
To build against Corda you must add the following to your ``build.gradle`` file;
|
||||||
------------------
|
|
||||||
|
|
||||||
|
* The ``net.corda:corda:<version>`` JAR as a ``cordaRuntime`` dependency
|
||||||
|
* Each compile dependency (eg ``corda-core``) as a ``cordaCompile`` dependency
|
||||||
|
|
||||||
|
To use Corda's test facilities you must add ``net.corda:corda-test-utils:<version>`` as a ``testCompile`` dependency
|
||||||
|
(i.e. a default Java/Kotlin test compile task).
|
||||||
|
|
||||||
|
.. warning:: Never include ``corda-test-utils`` as a ``compile`` or ``cordaCompile`` dependency.
|
||||||
|
|
||||||
|
Dependencies on other CorDapps
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Sometimes, a CorDapp you build will depend on states, contracts or flows defined in another CorDapp. You must include
|
||||||
|
the CorDapp your CorDapp depends upon as a ``cordapp`` dependency in your ``build.gradle`` file.
|
||||||
|
|
||||||
|
Other dependencies
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
If your CorDapps have any additional external dependencies, they can be specified like normal Kotlin/Java dependencies
|
If your CorDapps have any additional external dependencies, they can be specified like normal Kotlin/Java dependencies
|
||||||
in Gradle. See the example below, specifically the ``apache-commons`` include.
|
in Gradle. See the example below, specifically the ``apache-commons`` include.
|
||||||
|
|
||||||
For further information about managing dependencies, see
|
For further information about managing dependencies, see
|
||||||
`the Gradle docs <https://docs.gradle.org/current/userguide/dependency_management.html>`_.
|
`the Gradle docs <https://docs.gradle.org/current/userguide/dependency_management.html>`_.
|
||||||
|
|
||||||
Installing CorDapps
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
At runtime, nodes will load any CorDapp JARs present in their ``cordapps`` folder. Therefore in order to install a CorDapp to
|
|
||||||
a node the CorDapp JAR must be added to the ``<node_dir>/cordapps/`` folder, where ``node_dir`` is the folder in which the
|
|
||||||
node's JAR and configuration files are stored).
|
|
||||||
|
|
||||||
The ``deployNodes`` gradle task, if correctly configured, will automatically place your CorDapp JAR as well as any
|
|
||||||
dependent cordapp JARs specified into the directory automatically.
|
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
^^^^^^^
|
||||||
|
The following is a sample of what a gradle dependencies block for a CorDapp could look like. The CorDapp template
|
||||||
The following is a sample of what a gradle dependencies block for a cordapp could look like. The cordapp template
|
|
||||||
is already correctly configured and this is for reference only;
|
is already correctly configured and this is for reference only;
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
@ -108,6 +100,9 @@ is already correctly configured and this is for reference only;
|
|||||||
testCompile "net.corda:corda-test-utils:$corda_release_version"
|
testCompile "net.corda:corda-test-utils:$corda_release_version"
|
||||||
|
|
||||||
// Corda Plugins: dependent flows and services
|
// Corda Plugins: dependent flows and services
|
||||||
|
// Identifying a CorDapp by its module in the same project.
|
||||||
|
cordapp project(":cordapp-contracts-states")
|
||||||
|
// Identifying a CorDapp by its fully-qualified name.
|
||||||
cordapp "net.corda:bank-of-corda-demo:1.0"
|
cordapp "net.corda:bank-of-corda-demo:1.0"
|
||||||
|
|
||||||
// Some other dependencies
|
// Some other dependencies
|
||||||
@ -118,3 +113,29 @@ is already correctly configured and this is for reference only;
|
|||||||
compile "org.apache.commons:commons-lang3:3.6"
|
compile "org.apache.commons:commons-lang3:3.6"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Creating the CorDapp JAR
|
||||||
|
------------------------
|
||||||
|
The gradle ``jar`` task included in the CorDapp template build file will automatically build your CorDapp JAR correctly
|
||||||
|
as long as your dependencies are set correctly.
|
||||||
|
|
||||||
|
Note that the hash of the resulting CorDapp JAR is not deterministic, as it depends on variables such as the timestamp
|
||||||
|
at creation. Nodes running the same CorDapp must therefore ensure they are using the exact same CorDapp jar, and not
|
||||||
|
different versions of the JAR created from identical sources.
|
||||||
|
|
||||||
|
The filename of the JAR must include a unique identifier to deduplicate it from other releases of the same CorDapp.
|
||||||
|
This is typically done by appending the version string to the CorDapp's name. This unique identifier should not change
|
||||||
|
once the JAR has been deployed on a node. If it does, make sure no one is relying on ``FlowContext.appName`` in their
|
||||||
|
flows (see :doc:`versioning`).
|
||||||
|
|
||||||
|
Installing the CorDapp jar
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
.. note:: Before installing a CorDapp, you must create one or more nodes to install it on. For instructions, please see
|
||||||
|
:doc:`deploying-a-node`.
|
||||||
|
|
||||||
|
At runtime, nodes will load any CorDapps present in their ``cordapps`` folder. Therefore in order to install a CorDapp on
|
||||||
|
a node, the CorDapp JAR must be added to the ``<node_dir>/cordapps/`` folder, where ``node_dir`` is the folder in which
|
||||||
|
the node's JAR and configuration files are stored.
|
||||||
|
|
||||||
|
The ``deployNodes`` gradle task, if correctly configured, will automatically place your CorDapp JAR as well as any
|
||||||
|
dependent CorDapp JARs specified into the ``cordapps`` folder automatically.
|
@ -1,25 +1,30 @@
|
|||||||
What is a CorDapp?
|
What is a CorDapp?
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Corda is a platform. Its functionality is extended by developers through the writing of Corda distributed
|
CorDapps (Corda Distributed Applications) are distributed applications that run on the Corda platform. The goal of a
|
||||||
applications (CorDapps). CorDapps are installed at the level of the individual node, rather than on the network
|
CorDapp is to allow nodes to reach agreement on updates to the ledger. They achieve this goal by defining flows that
|
||||||
itself.
|
Corda node owners can invoke through RPC calls:
|
||||||
|
|
||||||
Each CorDapp allows a node to handle new business processes, for example asset trading (see :ref:`irs-demo`).
|
|
||||||
It does so by defining new flows on the node that, once started by the node owner, conduct the process of negotiating
|
|
||||||
a specific ledger update with other nodes on the network. The node's owner can then start these flows as required,
|
|
||||||
either through remote procedure calls (RPC) or HTTP requests that leverage the RPC interface.
|
|
||||||
|
|
||||||
.. image:: resources/node-diagram.png
|
.. image:: resources/node-diagram.png
|
||||||
|
|
||||||
CorDapp developers will usually define not only these flows, but also any states and contracts that these flows use.
|
CorDapps are made up of the following key components:
|
||||||
They will also have to define any web APIs that will run on the node's standalone web server, any static web content,
|
|
||||||
and any new services that they want their CorDapp to offer.
|
|
||||||
|
|
||||||
CorDapps are made up of definitions for the following components:
|
* States, defining the facts over which agreement is reached (see :doc:`Key Concepts - States <key-concepts-states>`)
|
||||||
|
* Contracts, defining what constitutes a valid ledger update (see
|
||||||
|
:doc:`Key Concepts - Contracts <key-concepts-contracts>`)
|
||||||
|
* Services, providing long-lived utilities within the node
|
||||||
|
* Serialisation whitelists, restricting what types your node will receive off the wire
|
||||||
|
|
||||||
* States
|
Each CorDapp is installed at the level of the individual node, rather than on the network itself. For example, a node
|
||||||
* Contracts
|
owner may choose to install the Bond Trading CorDapp, with the following components:
|
||||||
* Flows
|
|
||||||
* Web APIs and static web content
|
* A ``BondState``, used to represent bonds as shared facts on the ledger
|
||||||
* Services
|
* A ``BondContract``, used to govern which ledger updates involving ``BondState`` states are valid
|
||||||
|
* Three flows:
|
||||||
|
|
||||||
|
* An ``IssueBondFlow``, allowing new ``BondState`` states to be issued onto the ledger
|
||||||
|
* A ``TradeBondFlow``, allowing existing ``BondState`` states to be bought and sold on the ledger
|
||||||
|
* An ``ExitBondFlow``, allowing existing ``BondState`` states to be exited from the ledger
|
||||||
|
|
||||||
|
After installing this CorDapp, the node owner will be able to use the flows defined by the CorDapp to agree ledger
|
||||||
|
updates related to issuance, sale, purchase and exit of bonds.
|
@ -1,97 +1,141 @@
|
|||||||
Deploying a node
|
Deploying a node
|
||||||
================
|
================
|
||||||
|
|
||||||
Using Gradle to build nodes
|
Node structure
|
||||||
---------------------------
|
--------------
|
||||||
Nodes are usually built using a Gradle task. The canonical Gradle file for building nodes is the one used by the
|
Each Corda node has the following structure:
|
||||||
CorDapp template. Both a `Java version <https://github.com/corda/cordapp-template-java/blob/master/build.gradle>`_ and
|
|
||||||
a `Kotlin version <https://github.com/corda/cordapp-template-kotlin/blob/master/build.gradle>`_ are available.
|
|
||||||
|
|
||||||
Cordform is the local node deployment system for CorDapps. The nodes generated are intended for experimenting,
|
.. sourcecode:: none
|
||||||
debugging, and testing node configurations, but not for production or testnet deployment.
|
|
||||||
|
|
||||||
Here is an example Gradle task called ``deployNodes`` that uses the Cordform plugin to deploy three nodes, plus a
|
.
|
||||||
notary node:
|
├── certificates // The node's doorman certificates
|
||||||
|
├── corda-webserver.jar // The built-in node webserver
|
||||||
|
├── corda.jar // The core Corda libraries
|
||||||
|
├── logs // The node logs
|
||||||
|
├── node.conf // The node's configuration files
|
||||||
|
├── persistence.mv.db // The node's database
|
||||||
|
└── cordapps // The CorDapps jars installed on the node
|
||||||
|
|
||||||
|
The node is configured by editing its ``node.conf`` file. You install CorDapps on the node by dropping the CorDapp JARs
|
||||||
|
into the ``cordapps`` folder.
|
||||||
|
|
||||||
|
Node naming
|
||||||
|
-----------
|
||||||
|
A node's name must be a valid X500 name that obeys the following additional constraints:
|
||||||
|
|
||||||
|
* The fields of the name have the following maximum character lengths:
|
||||||
|
|
||||||
|
* Common name: 64
|
||||||
|
* Organisation: 128
|
||||||
|
* Organisation unit: 64
|
||||||
|
* Locality: 64
|
||||||
|
* State: 64
|
||||||
|
|
||||||
|
* The country code is a valid ISO 3166-1 two letter code in upper-case
|
||||||
|
|
||||||
|
* The organisation, locality and country attributes are present
|
||||||
|
|
||||||
|
* The organisation field of the name obeys the following constraints:
|
||||||
|
|
||||||
|
* Has at least two letters
|
||||||
|
* No leading or trailing whitespace
|
||||||
|
* No double-spacing
|
||||||
|
* Upper-case first letter
|
||||||
|
* Does not contain the words "node" or "server"
|
||||||
|
* Does not include the characters ',' or '=' or '$' or '"' or '\'' or '\\'
|
||||||
|
* Is in NFKC normalization form
|
||||||
|
* Only the latin, common and inherited unicode scripts are supported
|
||||||
|
|
||||||
|
The deployNodes task
|
||||||
|
--------------------
|
||||||
|
The CorDapp template defines a ``deployNodes`` task that allows you to automatically generate and configure a set of
|
||||||
|
nodes:
|
||||||
|
|
||||||
.. sourcecode:: groovy
|
.. sourcecode:: groovy
|
||||||
|
|
||||||
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
||||||
directory "./build/nodes"
|
directory "./build/nodes"
|
||||||
|
networkMap "O=Controller,L=London,C=GB"
|
||||||
node {
|
node {
|
||||||
name "O=Controller,OU=corda,L=London,C=UK"
|
name "O=Controller,L=London,C=GB"
|
||||||
|
// The notary will offer a validating notary service.
|
||||||
notary = [validating : true]
|
notary = [validating : true]
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcPort 10003
|
rpcPort 10003
|
||||||
webPort 10004
|
// No webport property, so no webserver will be created.
|
||||||
cordapps = []
|
h2Port 10004
|
||||||
|
sshdPort 22
|
||||||
|
// Includes the corda-finance CorDapp on our node.
|
||||||
|
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "CN=NodeA,O=NodeA,L=London,C=UK"
|
name "O=PartyA,L=London,C=GB"
|
||||||
|
advertisedServices = []
|
||||||
p2pPort 10005
|
p2pPort 10005
|
||||||
rpcPort 10006
|
rpcPort 10006
|
||||||
webPort 10007
|
webPort 10007
|
||||||
cordapps = []
|
h2Port 10008
|
||||||
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
sshdPort 22
|
||||||
|
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
||||||
|
// Grants user1 all RPC permissions.
|
||||||
|
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "CN=NodeB,O=NodeB,L=New York,C=US"
|
name "O=PartyB,L=New York,C=US"
|
||||||
p2pPort 10008
|
advertisedServices = []
|
||||||
rpcPort 10009
|
p2pPort 10009
|
||||||
webPort 10010
|
rpcPort 10010
|
||||||
cordapps = []
|
webPort 10011
|
||||||
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
h2Port 10012
|
||||||
}
|
sshdPort 22
|
||||||
// Example of explicit addresses being used.
|
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
||||||
node {
|
// Grants user1 the ability to start the MyFlow flow.
|
||||||
name "CN=NodeC,O=NodeC,L=Paris,C=FR"
|
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["StartFlow.net.corda.flows.MyFlow"]]]
|
||||||
p2pAddress "localhost:10011"
|
|
||||||
rpcAddress "localhost:10012"
|
|
||||||
webAddress "localhost:10013"
|
|
||||||
cordapps = []
|
|
||||||
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
You can extend ``deployNodes`` to generate any number of nodes you like.
|
Running this task will create three nodes in the ``build/nodes`` folder:
|
||||||
|
|
||||||
|
* A ``Controller`` node that:
|
||||||
|
|
||||||
|
* Serves as the network map
|
||||||
|
* Offers a validating notary service
|
||||||
|
* Will not have a webserver (since ``webPort`` is not defined)
|
||||||
|
* Is running the ``corda-finance`` CorDapp
|
||||||
|
|
||||||
|
* ``PartyA`` and ``PartyB`` nodes that:
|
||||||
|
|
||||||
|
* Are pointing at the ``Controller`` as the network map service
|
||||||
|
* Are not offering any services
|
||||||
|
* Will have a webserver (since ``webPort`` is defined)
|
||||||
|
* Are running the ``corda-finance`` CorDapp
|
||||||
|
* Have an RPC user, ``user1``, that can be used to log into the node via RPC
|
||||||
|
|
||||||
|
Additionally, all three nodes will include any CorDapps defined in the project's source folders, even though these
|
||||||
|
CorDapps are not listed in each node's ``cordapps`` entry. This means that running the ``deployNodes`` task from the
|
||||||
|
template CorDapp, for example, would automatically build and add the template CorDapp to each node.
|
||||||
|
|
||||||
|
You can extend ``deployNodes`` to generate additional nodes. The only requirement is that you must specify
|
||||||
|
a single node to run the network map service, by putting their name in the ``networkMap`` field.
|
||||||
|
|
||||||
.. warning:: When adding nodes, make sure that there are no port clashes!
|
.. warning:: When adding nodes, make sure that there are no port clashes!
|
||||||
|
|
||||||
If your CorDapp is written in Java, you should also add the following Gradle snippet so that you can pass named arguments to your flows via the Corda shell:
|
Running deployNodes
|
||||||
|
-------------------
|
||||||
.. sourcecode:: groovy
|
To create the nodes defined in our ``deployNodes`` task, we'd run the following command in a terminal window from the
|
||||||
|
root of the project:
|
||||||
tasks.withType(JavaCompile) {
|
|
||||||
options.compilerArgs << "-parameters"
|
|
||||||
}
|
|
||||||
|
|
||||||
Any CorDapps defined in the project's source folders are also automatically registered with all the nodes defined in
|
|
||||||
``deployNodes``, even if the CorDapps are not listed in each node's ``cordapps`` entry.
|
|
||||||
|
|
||||||
Deploying your nodes
|
|
||||||
--------------------
|
|
||||||
You deploy a set of nodes by running your ``build.gradle`` file's Cordform task. For example, if we were using the
|
|
||||||
standard ``deployNodes`` task defined above, we'd create our nodes by running the following commands in a terminal
|
|
||||||
window from the root of the project:
|
|
||||||
|
|
||||||
* Unix/Mac OSX: ``./gradlew deployNodes``
|
* Unix/Mac OSX: ``./gradlew deployNodes``
|
||||||
* Windows: ``gradlew.bat deployNodes``
|
* Windows: ``gradlew.bat deployNodes``
|
||||||
|
|
||||||
After the build process has finished, you will find the newly-built nodes under ``kotlin-source/build/nodes``. There
|
This will create the nodes in the ``build/nodes`` folder.
|
||||||
will be one folder generated for each node you built, plus a ``runnodes`` shell script (or batch file on Windows) to
|
|
||||||
run all the nodes at once. Each node in the ``nodes`` folder has the following structure:
|
|
||||||
|
|
||||||
.. sourcecode:: none
|
|
||||||
|
|
||||||
. nodeName
|
|
||||||
├── corda.jar // The Corda runtime
|
|
||||||
├── node.conf // The node's configuration
|
|
||||||
├── cordapps // Any installed CorDapps
|
|
||||||
└── additional-node-infos // Directory containing all the addresses and certificates of the other nodes.
|
|
||||||
|
|
||||||
.. note:: During the build process each node generates a NodeInfo file which is written in its own root directory,
|
.. note:: During the build process each node generates a NodeInfo file which is written in its own root directory,
|
||||||
the plug-in proceeds and copies each node NodeInfo to every other node ``additional-node-infos`` directory.
|
the plug-in proceeds and copies each node NodeInfo to every other node ``additional-node-infos`` directory.
|
||||||
The NodeInfo file contains a node hostname and port, legal name and security certificate.
|
The NodeInfo file contains a node hostname and port, legal name and security certificate.
|
||||||
|
|
||||||
.. note:: Outside of development environments, do not store your node directories in the build folder.
|
There will be a node folder generated for each node you defined, plus a ``runnodes`` shell script (or batch file on
|
||||||
|
Windows) to run all the nodes at once. If you make any changes to your ``deployNodes`` task, you will need to re-run
|
||||||
|
the task to see the changes take effect.
|
||||||
|
|
||||||
If you make any changes to your ``deployNodes`` task, you will need to re-run the task to see the changes take effect.
|
You can now run the nodes by following the instructions in :doc:`Running a node <running-a-node>`.
|
@ -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
|
|
@ -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);
|
||||||
|
@ -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 {}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
@ -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
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
@ -168,8 +168,8 @@ 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
|
||||||
|
@ -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.
|
|
@ -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 IOU’s 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.
|
@ -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
|
|
@ -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 JAR 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 don’t even
|
In traditional distributed ledger systems, where all data is broadcast to every network participant, you don’t 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.
|
@ -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
|
||||||
|
|
||||||
@ -48,10 +45,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
|
||||||
@ -114,11 +107,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
|
||||||
|
|
||||||
@ -166,31 +160,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>`_.
|
|
||||||
|
@ -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,6 +24,7 @@ defined as follows:
|
|||||||
val participants: List<AbstractParty>
|
val participants: List<AbstractParty>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
The first thing you'll probably notice about this interface declaration is that its not written in Java or another
|
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.
|
common language. The core Corda platform, including the interface declaration above, is entirely written in Kotlin.
|
||||||
|
|
||||||
@ -35,31 +36,42 @@ If you do want to dive into Kotlin, there's an official
|
|||||||
`getting started guide <https://kotlinlang.org/docs/tutorials/>`_, and a series of
|
`getting started guide <https://kotlinlang.org/docs/tutorials/>`_, and a series of
|
||||||
`Kotlin Koans <https://kotlinlang.org/docs/tutorials/koans.html>`_.
|
`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
|
We can see that the ``ContractState`` interface has a single field, ``participants``. ``participants`` is a list of the
|
||||||
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 +87,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.
|
@ -7,35 +7,42 @@
|
|||||||
The CorDapp Template
|
The CorDapp Template
|
||||||
====================
|
====================
|
||||||
|
|
||||||
When writing a new CorDapp, you’ll generally want to base it on the
|
When writing a new CorDapp, you’ll 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``.
|
|
||||||
|
@ -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
|
||||||
|
@ -7,5 +7,4 @@ Quickstart
|
|||||||
getting-set-up
|
getting-set-up
|
||||||
tutorial-cordapp
|
tutorial-cordapp
|
||||||
Sample CorDapps <https://www.corda.net/samples/>
|
Sample CorDapps <https://www.corda.net/samples/>
|
||||||
building-against-master
|
|
||||||
CLI-vs-IDE
|
CLI-vs-IDE
|
Binary file not shown.
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 181 KiB |
BIN
docs/source/resources/simple-tutorial-transaction-2.png
Normal file
BIN
docs/source/resources/simple-tutorial-transaction-2.png
Normal file
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 |
@ -23,7 +23,6 @@ Classes get onto the whitelist via one of three mechanisms:
|
|||||||
class itself, on any of the super classes or on any interface implemented by the class or super classes or any
|
class itself, on any of the super classes or on any interface implemented by the class or super classes or any
|
||||||
interface extended by an interface implemented by the class or superclasses.
|
interface extended by an interface implemented by the class or superclasses.
|
||||||
#. By implementing the ``SerializationWhitelist`` interface and specifying a list of `whitelist` classes.
|
#. By implementing the ``SerializationWhitelist`` interface and specifying a list of `whitelist` classes.
|
||||||
See :doc:`writing-cordapps`.
|
|
||||||
#. Via the built in Corda whitelist (see the class ``DefaultWhitelist``). Whilst this is not user editable, it does list
|
#. Via the built in Corda whitelist (see the class ``DefaultWhitelist``). Whilst this is not user editable, it does list
|
||||||
common JDK classes that have been whitelisted for your convenience.
|
common JDK classes that have been whitelisted for your convenience.
|
||||||
|
|
||||||
|
@ -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.
|
@ -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>`_.
|
@ -1,9 +0,0 @@
|
|||||||
Two-party flows
|
|
||||||
===============
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
tut-two-party-introduction
|
|
||||||
tut-two-party-contract
|
|
||||||
tut-two-party-flow
|
|
@ -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.
|
|
@ -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
|
||||||
|
146
docs/source/writing-a-cordapp.rst
Normal file
146
docs/source/writing-a-cordapp.rst
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
Writing a CorDapp
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
CorDapps can be written in either Java, Kotlin, or a combination of the two. Each CorDapp component takes the form
|
||||||
|
of a JVM class that subclasses or implements a Corda library type:
|
||||||
|
|
||||||
|
* Flows subclass ``FlowLogic``
|
||||||
|
* States implement ``ContractState``
|
||||||
|
* Contracts implement ``Contract``
|
||||||
|
* Services subclass ``SingletonSerializationToken``
|
||||||
|
* Serialisation whitelists implement ``SerializationWhitelist``
|
||||||
|
|
||||||
|
Web content and RPC clients
|
||||||
|
---------------------------
|
||||||
|
For testing purposes, CorDapps may also include:
|
||||||
|
|
||||||
|
* **APIs and static web content**: These are served by Corda's built-in webserver. This webserver is not
|
||||||
|
production-ready, and should be used for testing purposes only
|
||||||
|
|
||||||
|
* **RPC clients**: These are programs that automate the process of interacting with a node via RPC
|
||||||
|
|
||||||
|
In production, a production-ready webserver should be used, and these files should be moved into a different module or
|
||||||
|
project so that they do not bloat the CorDapp at build time.
|
||||||
|
|
||||||
|
Structure
|
||||||
|
---------
|
||||||
|
You should base the structure of your project on the Java or Kotlin templates:
|
||||||
|
|
||||||
|
* `Java Template CorDapp <https://github.com/corda/cordapp-template-java>`_
|
||||||
|
* `Kotlin Template CorDapp <https://github.com/corda/cordapp-template-kotlin>`_
|
||||||
|
|
||||||
|
The project should be split into two modules:
|
||||||
|
|
||||||
|
* A ``cordapp-contracts-states`` module containing classes such as contracts and states that will be sent across the
|
||||||
|
wire as part of a flow
|
||||||
|
* A ``cordapp`` module containing the remaining classes
|
||||||
|
|
||||||
|
Each module will be compiled into its own CorDapp. This minimises the size of the JAR that has to be sent across the
|
||||||
|
wire when nodes are agreeing ledger updates.
|
||||||
|
|
||||||
|
Module one - cordapp-contracts-states
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Here is the structure of the ``src`` directory for the ``cordapp-contracts-states`` module:
|
||||||
|
|
||||||
|
.. parsed-literal::
|
||||||
|
|
||||||
|
.
|
||||||
|
└── main
|
||||||
|
└── java
|
||||||
|
└── com
|
||||||
|
└── template
|
||||||
|
├── TemplateContract.java
|
||||||
|
└── TemplateState.java
|
||||||
|
|
||||||
|
The directory only contains two class definitions:
|
||||||
|
|
||||||
|
* ``TemplateContract``
|
||||||
|
* ``TemplateState``
|
||||||
|
|
||||||
|
These are definitions for classes that we expect to have to send over the wire. They will be compiled into their own
|
||||||
|
CorDapp.
|
||||||
|
|
||||||
|
Module two - cordapp
|
||||||
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Here is the structure of the ``src`` directory for the ``cordapp`` module:
|
||||||
|
|
||||||
|
.. parsed-literal::
|
||||||
|
|
||||||
|
.
|
||||||
|
├── main
|
||||||
|
│ ├── java
|
||||||
|
│ │ └── com
|
||||||
|
│ │ └── template
|
||||||
|
│ │ ├── TemplateApi.java
|
||||||
|
│ │ ├── TemplateClient.java
|
||||||
|
│ │ ├── TemplateFlow.java
|
||||||
|
│ │ ├── TemplateSerializationWhitelist.java
|
||||||
|
│ │ └── TemplateWebPlugin.java
|
||||||
|
│ └── resources
|
||||||
|
│ ├── META-INF
|
||||||
|
│ │ └── services
|
||||||
|
│ │ ├── net.corda.core.serialization.SerializationWhitelist
|
||||||
|
│ │ └── net.corda.webserver.services.WebServerPluginRegistry
|
||||||
|
│ ├── certificates
|
||||||
|
│ └── templateWeb
|
||||||
|
├── test
|
||||||
|
│ └── java
|
||||||
|
│ └── com
|
||||||
|
│ └── template
|
||||||
|
│ ├── ContractTests.java
|
||||||
|
│ ├── FlowTests.java
|
||||||
|
│ └── NodeDriver.java
|
||||||
|
└── integrationTest
|
||||||
|
└── java
|
||||||
|
└── com
|
||||||
|
└── template
|
||||||
|
└── DriverBasedTest.java
|
||||||
|
|
||||||
|
The ``src`` directory is structured as follows:
|
||||||
|
|
||||||
|
* ``main`` contains the source of the CorDapp
|
||||||
|
* ``test`` contains example unit tests, as well as a node driver for running the CorDapp from IntelliJ
|
||||||
|
* ``integrationTest`` contains an example integration test
|
||||||
|
|
||||||
|
Within ``main``, we have the following directories:
|
||||||
|
|
||||||
|
* ``resources/META-INF/services`` contains registries of the CorDapp's serialisation whitelists and web plugins
|
||||||
|
* ``resources/certificates`` contains dummy certificates for test purposes
|
||||||
|
* ``resources/templateWeb`` contains a dummy front-end
|
||||||
|
* ``java`` (or ``kotlin`` in the Kotlin template), which includes the source-code for our CorDapp
|
||||||
|
|
||||||
|
The source-code for our CorDapp breaks down as follows:
|
||||||
|
|
||||||
|
* ``TemplateFlow.java``, which contains a dummy ``FlowLogic`` subclass
|
||||||
|
* ``TemplateState.java``, which contains a dummy ``ContractState`` implementation
|
||||||
|
* ``TemplateContract.java``, which contains a dummy ``Contract`` implementation
|
||||||
|
* ``TemplateSerializationWhitelist.java``, which contains a dummy ``SerializationWhitelist`` implementation
|
||||||
|
|
||||||
|
In developing your CorDapp, you should start by modifying these classes to define the components of your CorDapp. A
|
||||||
|
single CorDapp can define multiple flows, states, and contracts.
|
||||||
|
|
||||||
|
The template also includes a web API and RPC client:
|
||||||
|
|
||||||
|
* ``TemplateApi.java``
|
||||||
|
* ``TemplateClient.java``
|
||||||
|
* ``TemplateWebPlugin.java``
|
||||||
|
|
||||||
|
These are for testing purposes and would be removed in a production CorDapp.
|
||||||
|
|
||||||
|
Resources
|
||||||
|
---------
|
||||||
|
In writing a CorDapp, you should consult the following resources:
|
||||||
|
|
||||||
|
* :doc:`Getting Set Up </getting-set-up>` to set up your development environment
|
||||||
|
* The :doc:`Hello, World! tutorial </hello-world-index>` to write your first CorDapp
|
||||||
|
* :doc:`Building a CorDapp </cordapp-build-systems>` to build and run your CorDapp
|
||||||
|
* The :doc:`API docs </api-index>` to read about the API available in developing CorDapps
|
||||||
|
|
||||||
|
* There is also a :doc:`cheatsheet </cheat-sheet>` recapping the key types
|
||||||
|
|
||||||
|
* The :doc:`Flow cookbook </flow-cookbook>` to see code examples of how to perform common flow tasks
|
||||||
|
* `Sample CorDapps <https://www.corda.net/samples/>`_ showing various parts of Corda's functionality
|
@ -1,61 +0,0 @@
|
|||||||
Writing a CorDapp
|
|
||||||
=================
|
|
||||||
|
|
||||||
When writing a CorDapp, you are writing a set of files in a JVM language that defines one or more of the following
|
|
||||||
Corda components:
|
|
||||||
|
|
||||||
* States (i.e. classes implementing ``ContractState``)
|
|
||||||
* Contracts (i.e. classes implementing ``Contract``)
|
|
||||||
* Flows (i.e. classes extending ``FlowLogic``)
|
|
||||||
* Web APIs
|
|
||||||
* Services
|
|
||||||
|
|
||||||
CorDapp structure
|
|
||||||
-----------------
|
|
||||||
Your CorDapp project's structure should be based on the structure of the
|
|
||||||
`Java Template CorDapp <https://github.com/corda/cordapp-template-java>`_ or the
|
|
||||||
`Kotlin Template CorDapp <https://github.com/corda/cordapp-template-kotlin>`_, depending on which language you intend
|
|
||||||
to use.
|
|
||||||
|
|
||||||
The ``src`` directory of the Template CorDapp, where we define our CorDapp's source-code, has the following structure:
|
|
||||||
|
|
||||||
.. parsed-literal::
|
|
||||||
|
|
||||||
src
|
|
||||||
├── main
|
|
||||||
│ ├── java
|
|
||||||
│ │ └── com
|
|
||||||
│ │ └── template
|
|
||||||
│ │ ├── Main.java
|
|
||||||
│ │ ├── api
|
|
||||||
│ │ │ └── TemplateApi.java
|
|
||||||
│ │ ├── client
|
|
||||||
│ │ │ └── TemplateClientRPC.java
|
|
||||||
│ │ ├── contract
|
|
||||||
│ │ │ └── TemplateContract.java
|
|
||||||
│ │ ├── flow
|
|
||||||
│ │ │ └── TemplateFlow.java
|
|
||||||
│ │ ├── plugin
|
|
||||||
│ │ │ └── TemplatePlugin.java
|
|
||||||
│ │ ├── service
|
|
||||||
│ │ │ └── TemplateService.java
|
|
||||||
│ │ └── state
|
|
||||||
│ │ └── TemplateState.java
|
|
||||||
│ └── resources
|
|
||||||
│ ├── META-INF
|
|
||||||
│ │ └── services
|
|
||||||
│ │ ├── net.corda.core.serialization.SerializationWhitelist
|
|
||||||
│ │ └── net.corda.webserver.services.WebServerPluginRegistry
|
|
||||||
│ ├── certificates
|
|
||||||
│ │ ├── sslkeystore.jks
|
|
||||||
│ │ └── truststore.jks
|
|
||||||
│ └──templateWeb
|
|
||||||
│ ├── index.html
|
|
||||||
│ └── js
|
|
||||||
│ └── template-js.js
|
|
||||||
└── test
|
|
||||||
└── java
|
|
||||||
└── com
|
|
||||||
└── template
|
|
||||||
└── contract
|
|
||||||
└── TemplateTests.java
|
|
Loading…
Reference in New Issue
Block a user