2017-06-16 13:05:52 +00:00
|
|
|
.. highlight:: kotlin
|
|
|
|
.. raw:: html
|
|
|
|
|
|
|
|
<script type="text/javascript" src="_static/jquery.js"></script>
|
|
|
|
<script type="text/javascript" src="_static/codesets.js"></script>
|
|
|
|
|
|
|
|
Running our CorDapp
|
|
|
|
===================
|
|
|
|
|
|
|
|
Now that we've written a CorDapp, it's time to test it by running it on some real Corda nodes.
|
|
|
|
|
|
|
|
Deploying our CorDapp
|
|
|
|
---------------------
|
2017-08-17 11:02:44 +00:00
|
|
|
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
|
2017-11-16 15:31:52 +00:00
|
|
|
``task deployNodes`` section. This section defines three nodes. There are two standard nodes (``PartyA`` and
|
2018-01-25 15:14:32 +00:00
|
|
|
``PartyB``), plus a special network map/notary node that is running the network map service and advertises a validating notary
|
2017-11-16 15:31:52 +00:00
|
|
|
service.
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2017-10-16 13:39:28 +00:00
|
|
|
.. code:: bash
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2017-10-16 13:39:28 +00:00
|
|
|
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|
|
|
directory "./build/nodes"
|
|
|
|
node {
|
2018-03-01 21:24:10 +00:00
|
|
|
name "O=Notary,L=London,C=GB"
|
2017-11-30 19:02:44 +00:00
|
|
|
notary = [validating : true]
|
2017-10-16 13:39:28 +00:00
|
|
|
p2pPort 10002
|
|
|
|
rpcPort 10003
|
|
|
|
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
|
|
|
}
|
|
|
|
node {
|
|
|
|
name "O=PartyA,L=London,C=GB"
|
|
|
|
p2pPort 10005
|
|
|
|
rpcPort 10006
|
|
|
|
webPort 10007
|
|
|
|
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
2017-11-20 17:41:38 +00:00
|
|
|
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL]]]
|
2017-06-16 13:05:52 +00:00
|
|
|
}
|
2017-10-16 13:39:28 +00:00
|
|
|
node {
|
|
|
|
name "O=PartyB,L=New York,C=US"
|
|
|
|
p2pPort 10008
|
|
|
|
rpcPort 10009
|
|
|
|
webPort 10010
|
2017-11-20 17:41:38 +00:00
|
|
|
sshdPort 10024
|
2017-10-16 13:39:28 +00:00
|
|
|
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
2017-11-20 17:41:38 +00:00
|
|
|
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
|
2017-10-16 13:39:28 +00:00
|
|
|
}
|
|
|
|
}
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
|
|
We can run this ``deployNodes`` task using Gradle. For each node definition, Gradle will:
|
|
|
|
|
|
|
|
* Package the project's source files into a CorDapp jar
|
|
|
|
* Create a new node in ``build/nodes`` with our CorDapp already installed
|
|
|
|
|
|
|
|
We can do that now by running the following commands from the root of the project:
|
|
|
|
|
2017-10-16 13:39:28 +00:00
|
|
|
.. code:: bash
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
|
|
// On Windows
|
|
|
|
gradlew clean deployNodes
|
|
|
|
|
|
|
|
// On Mac
|
|
|
|
./gradlew clean deployNodes
|
|
|
|
|
|
|
|
Running the nodes
|
|
|
|
-----------------
|
2017-08-17 11:02:44 +00:00
|
|
|
Running ``deployNodes`` will build the nodes under ``build/nodes``. If we navigate to one of these folders, we'll see
|
|
|
|
the three node folders. Each node folder has the following structure:
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2017-10-16 13:39:28 +00:00
|
|
|
.. code:: bash
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
|
|
.
|
2017-07-07 11:06:28 +00:00
|
|
|
|____corda.jar // The runnable node
|
|
|
|
|____corda-webserver.jar // The node's webserver
|
|
|
|
|____node.conf // The node's configuration file
|
2017-10-13 14:01:24 +00:00
|
|
|
|____cordapps
|
2017-10-16 13:39:28 +00:00
|
|
|
|____java/kotlin-source-0.1.jar // Our IOU CorDapp
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
|
|
Let's start the nodes by running the following commands from the root of the project:
|
|
|
|
|
2017-10-16 13:39:28 +00:00
|
|
|
.. code:: bash
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2017-08-17 11:02:44 +00:00
|
|
|
// On Windows
|
|
|
|
build/nodes/runnodes.bat
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2017-08-17 11:02:44 +00:00
|
|
|
// On Mac
|
|
|
|
build/nodes/runnodes
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
|
|
This will start a terminal window for each node, and an additional terminal window for each node's webserver - eight
|
|
|
|
terminal windows in all. Give each node a moment to start - you'll know it's ready when its terminal windows displays
|
|
|
|
the message, "Welcome to the Corda interactive shell.".
|
|
|
|
|
|
|
|
.. image:: resources/running_node.png
|
|
|
|
:scale: 25%
|
|
|
|
:align: center
|
|
|
|
|
|
|
|
Interacting with the nodes
|
|
|
|
--------------------------
|
|
|
|
Now that our nodes are running, let's order one of them to create an IOU by kicking off our ``IOUFlow``. In a larger
|
|
|
|
app, we'd generally provide a web API sitting on top of our node. Here, for simplicity, we'll be interacting with the
|
|
|
|
node via its built-in CRaSH shell.
|
|
|
|
|
2017-10-02 08:08:59 +00:00
|
|
|
Go to the terminal window displaying the CRaSH shell of PartyA. Typing ``help`` will display a list of the available
|
2017-06-16 13:05:52 +00:00
|
|
|
commands.
|
|
|
|
|
2017-11-20 17:41:38 +00:00
|
|
|
.. note:: Local terminal shell is available only in a development mode. In production environment SSH server can be enabled.
|
2018-01-03 10:00:33 +00:00
|
|
|
More about SSH and how to connect can be found on the :doc:`shell` page.
|
2017-11-20 17:41:38 +00:00
|
|
|
|
2018-04-30 12:54:14 +00:00
|
|
|
We want to create an IOU of 99 with PartyB. We start the ``IOUFlow`` by typing:
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2017-12-13 16:22:40 +00:00
|
|
|
.. container:: codeset
|
|
|
|
|
|
|
|
.. code-block:: java
|
2017-07-07 11:06:28 +00:00
|
|
|
|
2017-12-13 16:22:40 +00:00
|
|
|
start IOUFlow arg0: 99, arg1: "O=PartyB,L=New York,C=US"
|
|
|
|
|
|
|
|
.. code-block:: kotlin
|
|
|
|
|
|
|
|
start IOUFlow iouValue: 99, otherParty: "O=PartyB,L=New York,C=US"
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
This single command will cause PartyA and PartyB to automatically agree an IOU. This is one of the great advantages of
|
|
|
|
the flow framework - it allows you to reduce complex negotiation and update processes into a single function call.
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
If the flow worked, it should have recorded a new IOU in the vaults of both PartyA and PartyB. Let's check.
|
|
|
|
|
|
|
|
We can check the contents of each node's vault by running:
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2018-05-09 14:19:35 +00:00
|
|
|
.. code-block:: bash
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2017-10-02 08:08:59 +00:00
|
|
|
run vaultQuery contractStateType: com.template.IOUState
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2017-10-02 08:08:59 +00:00
|
|
|
The vaults of PartyA and PartyB should both display the following output:
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2017-10-16 13:39:28 +00:00
|
|
|
.. code:: bash
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2017-10-02 08:08:59 +00:00
|
|
|
states:
|
2017-06-16 13:05:52 +00:00
|
|
|
- state:
|
|
|
|
data:
|
|
|
|
value: 99
|
2017-10-02 08:08:59 +00:00
|
|
|
lender: "C=GB,L=London,O=PartyA"
|
|
|
|
borrower: "C=US,L=New York,O=PartyB"
|
2017-06-16 13:05:52 +00:00
|
|
|
participants:
|
2017-10-02 08:08:59 +00:00
|
|
|
- "C=GB,L=London,O=PartyA"
|
|
|
|
- "C=US,L=New York,O=PartyB"
|
|
|
|
contract: "com.template.contract.IOUContract"
|
2018-03-01 21:24:10 +00:00
|
|
|
notary: "C=GB,L=London,O=Notary"
|
2017-06-16 13:05:52 +00:00
|
|
|
encumbrance: null
|
2017-10-02 08:08:59 +00:00
|
|
|
constraint:
|
|
|
|
attachmentId: "F578320232CAB87BB1E919F3E5DB9D81B7346F9D7EA6D9155DC0F7BA8E472552"
|
2017-06-16 13:05:52 +00:00
|
|
|
ref:
|
2017-10-02 08:08:59 +00:00
|
|
|
txhash: "5CED068E790A347B0DD1C6BB5B2B463406807F95E080037208627565E6A2103B"
|
|
|
|
index: 0
|
|
|
|
statesMetadata:
|
|
|
|
- ref:
|
|
|
|
txhash: "5CED068E790A347B0DD1C6BB5B2B463406807F95E080037208627565E6A2103B"
|
2017-06-16 13:05:52 +00:00
|
|
|
index: 0
|
2017-10-02 08:08:59 +00:00
|
|
|
contractStateClassName: "com.template.state.IOUState"
|
|
|
|
recordedTime: 1506415268.875000000
|
|
|
|
consumedTime: null
|
|
|
|
status: "UNCONSUMED"
|
2018-03-01 21:24:10 +00:00
|
|
|
notary: "C=GB,L=London,O=Notary"
|
2017-10-02 08:08:59 +00:00
|
|
|
lockId: null
|
|
|
|
lockUpdateTime: 1506415269.548000000
|
|
|
|
totalStatesAvailable: -1
|
|
|
|
stateTypes: "UNCONSUMED"
|
|
|
|
otherResults: []
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
This is the transaction issuing our ``IOUState`` onto a ledger.
|
|
|
|
|
2017-06-16 13:05:52 +00:00
|
|
|
Conclusion
|
|
|
|
----------
|
2017-11-16 15:31:52 +00:00
|
|
|
We have written a simple CorDapp that allows IOUs to be issued onto the ledger. Our CorDapp is made up of two key
|
|
|
|
parts:
|
2017-06-16 13:05:52 +00:00
|
|
|
|
|
|
|
* The ``IOUState``, representing IOUs on the ledger
|
2017-07-07 11:06:28 +00:00
|
|
|
* The ``IOUFlow``, orchestrating the process of agreeing the creation of an IOU on-ledger
|
2017-06-16 13:05:52 +00:00
|
|
|
|
2017-12-13 16:22:40 +00:00
|
|
|
After completing this tutorial, your CorDapp should look like this:
|
|
|
|
|
|
|
|
* Java: https://github.com/corda/corda-tut1-solution-java
|
|
|
|
* Kotlin: https://github.com/corda/corda-tut1-solution-kotlin
|
|
|
|
|
2017-06-16 13:05:52 +00:00
|
|
|
Next steps
|
|
|
|
----------
|
2017-07-07 11:06:28 +00:00
|
|
|
There are a number of improvements we could make to this CorDapp:
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
* We chould add unit tests, using the contract-test and flow-test frameworks
|
|
|
|
* We chould change ``IOUState.value`` from an integer to a proper amount of a given currency
|
2017-07-07 11:06:28 +00:00
|
|
|
* We could add an API, to make it easier to interact with the CorDapp
|
|
|
|
|
2017-11-16 15:31:52 +00:00
|
|
|
But for now, the biggest priority is to add an ``IOUContract`` imposing constraints on the evolution of each
|
2018-04-30 12:54:14 +00:00
|
|
|
``IOUState`` over time. This will be the focus of our next tutorial.
|