Andr3ej tutorial doc (#1593)

Updated documentation: Tutorial
This commit is contained in:
Joel Dudley 2017-09-22 16:51:32 +01:00 committed by josecoll
parent ce8ea5cb50
commit 477dc42921

View File

@ -10,75 +10,80 @@ The example CorDapp
.. contents::
The example CorDapp allows nodes to agree IOUs with each other. Nodes will always agree to the creation of a new IOU
unless:
if:
* Its value is less than 1, or greater than 99
* A node tries to issue an IOU to itself
* Its value is strictly positive
* The node is not trying to issue the IOU to itself
By default, the CorDapp is deployed on 4 test nodes:
We will deploy the CorDapp on 4 test nodes:
* **Controller**, which hosts the network map service and validating notary service
* **NodeA**
* **NodeB**
* **NodeC**
* **Controller**, which hosts the network map service and a validating notary service
* **PartyA**
* **PartyB**
* **PartyC**
Because data is only propagated on a need-to-know basis, any IOUs agreed between NodeA and NodeB become "shared facts"
between NodeA and NodeB only. NodeC won't be aware of these IOUs.
Because data is only propagated on a need-to-know basis, any IOUs agreed between PartyA and PartyB become "shared
facts" between PartyA and PartyB only. PartyC won't be aware of these IOUs.
Downloading the example CorDapp
-------------------------------
If you haven't already, set up your machine by following the :doc:`quickstart guide <getting-set-up>`. Then clone the
example CorDapp from the `cordapp-tutorial repository <https://github.com/corda/cordapp-tutorial>`_ using the following
command:
We need to download the example CorDapp from GitHub.
``git clone https://github.com/corda/cordapp-tutorial``
* Set up your machine by following the :doc:`quickstart guide <getting-set-up>`
And change directories to the freshly cloned repo:
* Clone the example CorDapp from the `cordapp-example repository <https://github.com/corda/cordapp-example>`_ using
the following command: ``git clone https://github.com/corda/cordapp-example``
``cd cordapp-tutorial``
* Change directories to the freshly cloned repo: ``cd cordapp-example``
We want to work off the latest Milestone release. To enumerate all the Milestone releases, run:
* We want to work off the latest Milestone release
``git tag``
* To enumerate all the Milestone releases, run: ``git tag``
And check out the latest (highest-numbered) Milestone release using:
* Check out the latest (highest-numbered) Milestone release using: ``git checkout [tag_name]``
``git checkout [tag_name]``
Where ``tag_name`` is the name of the tag you wish to checkout
Where ``tag_name`` is the name of the tag you wish to checkout. Gradle will grab all the required dependencies for you
from our `public Maven repository <https://bintray.com/r3/corda>`_.
* Gradle will grab all the required dependencies for you from `Maven <https://bintray.com/r3/corda>`_
.. note:: If you wish to build off the latest, unstable version of the codebase, follow the instructions in
`Using a SNAPSHOT release`_.
:doc:`building against Master <building-against-master>` instead.
Opening the example CorDapp in IntelliJ
---------------------------------------
Let's open the example CorDapp in the IntelliJ IDE.
Let's open the example CorDapp in IntelliJ IDEA.
**For those completely new to IntelliJ**
**If opening a fresh IntelliJ instance**
Upon opening IntelliJ, a dialogue will appear:
* Open IntelliJ
* A dialogue box will appear:
.. image:: resources/intellij-welcome.png
:width: 400
.. image:: resources/intellij-welcome.png
:width: 400
Click open, then navigate to the folder where you cloned the ``cordapp-tutorial`` and click OK.
* Click open, navigate to the folder where you cloned the ``cordapp-example``, and click OK
Next, IntelliJ will show several pop-up windows, one of which requires our attention:
* IntelliJ will show several pop-up windows, one of which requires our attention:
.. image:: resources/unlinked-gradle-project.png
:width: 400
.. image:: resources/unlinked-gradle-project.png
:width: 400
Click the 'import gradle project' link. A dialogue will pop-up. Press OK. Gradle will now download all the
project dependencies and perform some indexing. This usually takes a minute or so.
* Click the 'import gradle project' link. Press OK on the dialogue that pops up
If the 'import gradle project' pop-up does not appear, click the small green speech bubble at the bottom-right of
the IDE, or simply close and re-open IntelliJ again to make it reappear.
* Gradle will now download all the project dependencies and perform some indexing. This usually takes a minute or so.
* If the 'import gradle project' pop-up does not appear, click the small green speech bubble at the bottom-right of
the IDE, or simply close and re-open IntelliJ again to make it reappear.
**If you already have IntelliJ open**
From the ``File`` menu, navigate to ``Open ...`` and then navigate to the directory where you cloned the
``cordapp-tutorial`` and click OK.
* Open the ``File`` menu
* Navigate to ``Open ...``
* Navigate to the directory where you cloned the ``cordapp-example``
* Click OK
Project structure
-----------------
@ -87,10 +92,6 @@ The example CorDapp has the following directory structure:
.. sourcecode:: none
.
├── LICENCE
├── README.md
├── TRADEMARK
├── build.gradle
├── config
│   ├── dev
│   │   └── log4j2.xml
@ -102,9 +103,9 @@ The example CorDapp has the following directory structure:
│   └── wrapper
│   ├── gradle-wrapper.jar
│   └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── lib
│   ├── README.txt
│   └── quasar.jar
├── java-source
│   └── ...
├── kotlin-source
@ -151,27 +152,31 @@ The example CorDapp has the following directory structure:
│   │   └── IOUContractTests.kt
│   └── flow
│   └── IOUFlowTests.kt
├── lib
│   ├── README.txt
│   └── quasar.jar
├── .gitignore
├── LICENCE
├── README.md
├── TRADEMARK
├── build.gradle
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
The most important files and directories to note are:
The key files and directories are as follows:
* The **root directory** contains some gradle files, a README and a LICENSE
* **config** contains log4j configs
* **gradle** contains the gradle wrapper, which allows the use of Gradle without installing it yourself and worrying
about which version is required
* **lib** contains the Quasar jar which is required for runtime instrumentation of classes by Quasar
* **lib** contains the Quasar jar which rewrites our CorDapp's flows to be checkpointable
* **kotlin-source** contains the source code for the example CorDapp written in Kotlin
* **kotlin-source/src/main/kotlin** contains the source code for the example CorDapp
* **kotlin-source/src/main/python** contains a python script which accesses nodes via RPC
* **kotlin-source/src/main/resources** contains the certificate store, some static web content to be served by the
nodes and the WebServerPluginRegistry file
* **kotlin-source/src/test/kotlin** contains unit tests for the contracts and flows, and the driver to run the nodes
via IntelliJ
* **java-source** contains the same source code, but written in java. This is an aid for users who do not want to
develop in Kotlin, and serves as an example of how CorDapps can be developed in any language targeting the JVM
* **java-source** contains the same source code, but written in Java. CorDapps can be developed in any language
targeting the JVM
Running the example CorDapp
---------------------------
@ -180,49 +185,57 @@ There are two ways to run the example CorDapp:
* Via the terminal
* Via IntelliJ
We explain both below.
In both cases, we will deploy a set of test nodes with our CorDapp installed, then run the nodes. You can read more
about how we define the nodes to be deployed :doc:`here <deploying-a-node>`.
Terminal: Building the example CorDapp
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Return to your terminal window and make sure you are in the ``cordapp-tutorial`` directory. To build the example
CorDapp use the following command:
Terminal
~~~~~~~~
* Unix/Mac OSX: ``./gradlew deployNodes``
* Windows: ``gradlew.bat deployNodes``
Building the example CorDapp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Open a terminal window in the ``cordapp-example`` directory
This will package up our CorDapp source files into a plugin and automatically build four pre-configured nodes that have
our CorDapp plugin installed. These nodes are meant for local testing only.
* Build the test nodes with our CorDapp using the following command:
After the build process has finished, you will see the newly-build nodes in the ``kotlin-source/build/nodes``. There
will be one folder generated for each node you built, plus a ``runnodes`` shell script (or batch file on Windows).
* Unix/Mac OSX: ``./gradlew deployNodes``
* Windows: ``gradlew.bat deployNodes``
This will automatically build four pre-configured nodes with our CorDapp installed. These nodes are meant for local
testing only
.. note:: CorDapps can be written in any language targeting the JVM. In our case, we've provided the example source in
both Kotlin (``/kotlin-source/src``) and Java (``/java-source/src``) Since both sets of source files are
functionally identical, we will refer to the Kotlin build throughout the documentation.
Each node in the ``nodes`` folder has the following structure:
* After the build process has finished, you will see the newly-build nodes in the ``kotlin-source/build/nodes`` folder
.. sourcecode:: none
* There 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 simultaneously
. nodeName
├── corda.jar
├── node.conf
└── plugins
* Each node in the ``nodes`` folder has the following structure:
``corda.jar` is the Corda runtime, ``plugins`` contains our node's CorDapps, and our node's configuration is provided
in ``node.conf``.
.. sourcecode:: none
Terminal: Running the example CorDapp
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To run our nodes, run the following command from the root of the ``cordapp-tutorial`` folder:
. nodeName
├── corda.jar
├── node.conf
└── plugins
``corda.jar`` is the Corda runtime, ``plugins`` contains our node's CorDapps, and the node's configuration is
given by ``node.conf``
Running the example CorDapp
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Start the nodes by running the following command from the root of the ``cordapp-example`` folder:
* Unix/Mac OSX: ``kotlin-source/build/nodes/runnodes``
* Windows: ``call kotlin-source\build\nodes\runnodes.bat``
On Unix/Mac OSX, do not click/change focus until all eight additional terminal windows have opened, or some nodes may
fail to start.
.. warn:: On Unix/Mac OSX, do not click/change focus until all seven additional terminal windows have opened, or some
nodes may fail to start.
The ``runnodes`` script creates a terminal tab/window for each node:
For each node, the ``runnodes`` script creates a node tab/window:
.. sourcecode:: none
@ -236,12 +249,12 @@ The ``runnodes`` script creates a terminal tab/window for each node:
📚 New! Training now available worldwide, see https://corda.net/corda-training/
Logs can be found in : /Users/joeldudley/Desktop/cordapp-tutorial/kotlin-source/build/nodes/NodeA/logs
Logs can be found in : /Users/joeldudley/Desktop/cordapp-example/kotlin-source/build/nodes/PartyA/logs
Database connection url is : jdbc:h2:tcp://10.163.199.132:54763/node
Listening on address : 127.0.0.1:10005
RPC service listening on address : localhost:10006
Loaded plugins : com.example.plugin.ExamplePlugin
Node for "NodeA" started up and registered in 35.0 sec
Node for "PartyA" started up and registered in 35.0 sec
Welcome to the Corda interactive shell.
@ -249,67 +262,69 @@ The ``runnodes`` script creates a terminal tab/window for each node:
Fri Jul 07 10:33:47 BST 2017>>>
The script will also create a webserver terminal tab for each node:
For every node except the controller, the script also creates a webserver terminal tab/window:
.. sourcecode:: none
Logs can be found in /Users/joeldudley/Desktop/cordapp-tutorial/kotlin-source/build/nodes/NodeA/logs/web
Logs can be found in /Users/joeldudley/Desktop/cordapp-example/kotlin-source/build/nodes/PartyA/logs/web
Starting as webserver: localhost:10007
Webserver started up in 42.02 sec
Depending on your machine, it usually takes around 60 seconds for the nodes to finish starting up. If you want to
ensure that all the nodes are running OK, you can query the 'status' end-point located at
``http://localhost:[port]/api/status`` (e.g. ``http://localhost:10007/api/status`` for ``NodeA``).
It usually takes around 60 seconds for the nodes to finish starting up. To ensure that all the nodes are running OK,
you can query the 'status' end-point located at ``http://localhost:[port]/api/status`` (e.g.
``http://localhost:10007/api/status`` for ``PartyA``).
IntelliJ: Building and running the example CorDapp
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To run the example CorDapp via IntelliJ you can use the ``Run Example CorDapp - Kotlin`` run configuration. Select it
from the drop-down menu at the top right-hand side of the IDE and press the green arrow to start the nodes:
IntelliJ
~~~~~~~~
* Select the ``Run Example CorDapp - Kotlin`` run configuration from the drop-down menu at the top right-hand side of
the IDE
.. image:: resources/run-config-drop-down.png
:width: 400
* Click the green arrow to start the nodes:
The node driver defined in ``/src/test/kotlin/com/example/Main.kt`` allows you to specify how many nodes you would like
to run and the configuration settings for each node. With the example CorDapp, the driver starts up four nodes
and adds an RPC user for all but the "Controller" node (which serves as the notary and network map service):
.. image:: resources/run-config-drop-down.png
:width: 400
.. sourcecode:: kotlin
The node driver defined in ``/src/test/kotlin/com/example/Main.kt`` allows you to specify how many nodes you would like
to run and the configuration settings for each node. For the example CorDapp, the driver starts up four nodes
and adds an RPC user for all but the "Controller" node (which serves as the notary and network map service):
fun main(args: Array<String>) {
// No permissions required as we are not invoking flows.
val user = User("user1", "test", permissions = setOf())
driver(isDebug = true) {
startNode(getX500Name(O="Controller",OU="corda",L="London",C='UK"), setOf(ServiceInfo(ValidatingNotaryService.type)))
val (nodeA, nodeB, nodeC) = Futures.allAsList(
startNode(getX500Name(O="NodeA",L="London",C="UK"), rpcUsers = listOf(user)),
startNode(getX500Name(O="NodeB",L="New York",C="US"), rpcUsers = listOf(user)),
startNode(getX500Name(O="NodeC",L="Paris",C="FR"), rpcUsers = listOf(user))).getOrThrow()
.. sourcecode:: kotlin
startWebserver(nodeA)
startWebserver(nodeB)
startWebserver(nodeC)
fun main(args: Array<String>) {
// No permissions required as we are not invoking flows.
val user = User("user1", "test", permissions = setOf())
driver(isDebug = true) {
startNode(getX500Name(O="Controller",L="London",C='GB"), setOf(ServiceInfo(ValidatingNotaryService.type)))
val (nodeA, nodeB, nodeC) = Futures.allAsList(
startNode(getX500Name(O="PartyA",L="London",C="GB"), rpcUsers = listOf(user)),
startNode(getX500Name(O="PartyB",L="New York",C="US"), rpcUsers = listOf(user)),
startNode(getX500Name(O="PartyC",L="Paris",C="FR"), rpcUsers = listOf(user))).getOrThrow()
waitForAllNodesToFinish()
}
}
startWebserver(nodeA)
startWebserver(nodeB)
startWebserver(nodeC)
To stop the nodes, press the red square button at the top right-hand side of the IDE, next to the run configurations.
waitForAllNodesToFinish()
}
}
We'll look later at how the node driver can be useful for `debugging your CorDapp`_.
* To stop the nodes, press the red square button at the top right-hand side of the IDE, next to the run configurations
Later, we'll look at how the node driver can be useful for `debugging your CorDapp`_.
Interacting with the example CorDapp
------------------------------------
Via HTTP
~~~~~~~~
The CorDapp defines several HTTP API end-points and a web front-end. The end-points allow you to list your existing
IOUs, agree new IOUs, and see who is on the network.
The CorDapp defines several HTTP API end-points and a web front-end. The end-points allow you to list the IOUs a node
is involved in, agree new IOUs, and see who is on the network.
The nodes are running locally on the following ports:
* NodeA: ``localhost:10007``
* NodeB: ``localhost:10010``
* NodeC: ``localhost:10013``
* PartyA: ``localhost:10007``
* PartyB: ``localhost:10010``
* PartyC: ``localhost:10013``
These ports are defined in build.gradle and in each node's node.conf file under ``kotlin-source/build/nodes/NodeX``.
@ -329,22 +344,22 @@ the web form hosted at ``/web/example``.
.. warning:: The content in ``web/example`` is only available for demonstration purposes and does not implement
anti-XSS, anti-XSRF or any other security techniques. Do not use this code in production.
**Creating an IOU via the HTTP API:**
To create an IOU between NodeA and NodeB, we would run the following from the command line:
Creating an IOU via the endpoint
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To create an IOU between PartyA and PartyB, run the following command from the command line:
.. sourcecode:: bash
echo '{"value": "1"}' | cURL -T - -H 'Content-Type: application/json' http://localhost:10007/api/example/NodeB/create-iou
curl -X PUT 'http://localhost:10007/api/example/create-iou?iouValue=1&partyName=O=PartyB,L=New%20York,C=US'
Note that both NodeA's port number (``10007``) and NodeB are referenced in the PUT request path. This command instructs
NodeA to agree an IOU with NodeB. Once the process is complete, both nodes will have a signed, notarised copy of the
IOU. NodeC will not.
Note that both PartyA's port number (``10007``) and PartyB are referenced in the PUT request path. This command
instructs PartyA to agree an IOU with PartyB. Once the process is complete, both nodes will have a signed, notarised
copy of the IOU. PartyC will not.
**Submitting an IOU via the web front-end:**
Navigate to ``/web/example``, click the "create IOU" button at the top-left of the page, and enter the IOU details into
the web-form. The IOU must have a value of between 1 and 99.
Submitting an IOU via the web front-end
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To create an IOU between PartyA and PartyB, navigate to ``/web/example``, click the "create IOU" button at the top-left
of the page, and enter the IOU details into the web-form. The IOU must have a positive value. For example:
.. sourcecode:: none
@ -353,45 +368,43 @@ the web-form. The IOU must have a value of between 1 and 99.
And click submit. Upon clicking submit, the modal dialogue will close, and the nodes will agree the IOU.
**Once an IOU has been submitted:**
Assuming all went well, you should see some activity in NodeA's web-server terminal window:
Once an IOU has been submitted
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Assuming all went well, you should see some activity in PartyA's web-server terminal window:
.. sourcecode:: none
>> Generating transaction based on new IOU.
>> Verifying contract constraints.
>> Signing transaction with our private key.
>> Gathering the counterparty's signature.
>> Structural step change in child of Gathering the counterparty's signature.
>> Collecting signatures from counter-parties.
>> Verifying collected signatures.
>> Done
>> Obtaining notary signature and recording transaction.
>> Structural step change in child of Obtaining notary signature and recording transaction.
>> Requesting signature by notary service
>> Broadcasting transaction to participants
>> Done
>> Done
>> Signing transaction with our private key.
>> Gathering the counterparty's signature.
>> Structural step change in child of Gathering the counterparty's signature.
>> Collecting signatures from counter-parties.
>> Verifying collected signatures.
>> Done
>> Obtaining notary signature and recording transaction.
>> Structural step change in child of Obtaining notary signature and recording transaction.
>> Requesting signature by notary service
>> Broadcasting transaction to participants
>> Done
>> Done
You can view the newly-created IOU by accessing the vault of NodeA or NodeB:
You can view the newly-created IOU by accessing the vault of PartyA or PartyB:
*Via the HTTP API:*
* NodeA's vault: Navigate to http://localhost:10007/api/example/ious
* NodeB's vault: Navigate to http://localhost:10010/api/example/ious
* PartyA's vault: Navigate to http://localhost:10007/api/example/ious
* PartyB's vault: Navigate to http://localhost:10010/api/example/ious
*Via web/example:*
* NodeA: Navigate to http://localhost:10007/web/example and hit the "refresh" button
* NodeA: Navigate to http://localhost:10010/web/example and hit the "refresh" button
* PartyA: Navigate to http://localhost:10007/web/example and hit the "refresh" button
* PartyA: Navigate to http://localhost:10010/web/example and hit the "refresh" button
If you access the vault or web front-end of NodeC (on ``localhost:10013``), there will be no IOUs. This is because
NodeC was not involved in this transaction.
The vault and web front-end of PartyC (on ``localhost:10013``) will not display any IOUs. This is because PartyC was
not involved in this transaction.
Via the interactive shell (terminal only)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once a node has been started via the terminal, it will display an interactive shell:
Nodes started via the terminal will display an interactive shell:
.. sourcecode:: none
@ -400,19 +413,22 @@ Once a node has been started via the terminal, it will display an interactive sh
Fri Jul 07 16:36:29 BST 2017>>>
You can see a list of the flows that your node can run using `flow list`. In our case, this will return the following
list:
Type ``flow list`` in the shell to see a list of the flows that your node can run. In our case, this will return the
following list:
.. sourcecode:: none
com.example.flow.ExampleFlow$Initiator
net.corda.finance.flows.CashExitFlow
net.corda.finance.flows.CashIssueFlow
net.corda.finance.flows.CashPaymentFlow
net.corda.finance.flows.ContractUpgradeFlow
com.example.flow.ExampleFlow$Initiator
net.corda.core.flows.ContractUpgradeFlow$Initiator
net.corda.core.flows.ContractUpgradeFlow$Initiator
net.corda.finance.flows.CashExitFlow
net.corda.finance.flows.CashIssueAndPaymentFlow
net.corda.finance.flows.CashIssueFlow
net.corda.finance.flows.CashPaymentFlow
We can create a new IOU using the ``ExampleFlow$Initiator`` flow. For example, from the interactive shell of NodeA, you
can agree an IOU of 50 with NodeB by running ``flow start Initiator iouValue: 50, otherParty: NodeB``.
We can create a new IOU using the ``ExampleFlow$Initiator`` flow. For example, from the interactive shell of PartyA,
you can agree an IOU of 50 with PartyB by running
``flow start ExampleFlow$Initiator iouValue: 50, otherParty: "O=PartyB,L=New York,C=US"``.
This will print out the following progress steps:
@ -434,82 +450,6 @@ This will print out the following progress steps:
We can also issue RPC operations to the node via the interactive shell. Type ``run`` to see the full list of available
operations.
We can see a list of the states in our node's vault using ``run vaultAndUpdates``:
.. sourcecode:: none
---
first:
- state:
data:
iou:
value: 50
sender: "CN=NodeB,O=NodeB,L=New York,C=US"
recipient: "CN=NodeA,O=NodeA,L=London,C=UK"
linearId:
externalId: null
id: "84628565-2688-45ef-bb06-aae70fcf3be7"
contract: {}
participants:
- "CN=NodeB,O=NodeB,L=New York,C=US"
- "CN=NodeA,O=NodeA,L=London,C=UK"
notary: "O=Controller,OU=corda,L=London,C=UK,OU=corda.notary.validating"
encumbrance: null
ref:
txhash: "52A1B18E6ABD535EF36B2075469B01D2EF888034F721C4BECD26F40355C8C9DC"
index: 0
second: "(observable)"
We can also see the transactions stored in our node's local storage using ``run verifiedTransactions`` (we've
abbreviated the output below):
.. sourcecode:: none
first:
- txBits: "Y29yZGEAAAEOAQEAamF2YS51dGlsLkFycmF5TGlz9AABAAABAAEBAW5ldC5jb3JkYS5jb3JlLmNvbnRyYWN0cy5UcmFuc2FjdGlvblN0YXTlA1RyYW5zYWN0aW9uU3RhdGUuZGF04VRyYW5zYWN0aW9uU3RhdGUuZW5jdW1icmFuY+VUcmFuc2FjdGlvblN0YXRlLm5vdGFy+WkBAmNvbS5leGFtcGxlLnN0YXRlLklPVVN0YXTlBElPVVN0YXRlLmlv9UlPVVN0YXRlLmxpbmVhcknkSU9VU3RhdGUucmVjaXBpZW70SU9VU3RhdGUuc2VuZGXyDQEBSU9VLnZhbHXlAWQCAQA0ADIBAlVuaXF1ZUlkZW50aWZpZXIuZXh0ZXJuYWxJ5FVuaXF1ZUlkZW50aWZpZXIuaeQBgDAvAC0BAlVVSUQubGVhc3RTaWdCaXTzVVVJRC5tb3N0U2lnQml08wmxkIaDnsaq+YkNDAsACaHovZfbpr2d9wMCAQACAQBIAEYBAkFic3RyYWN0UGFydHkub3duaW5nS2X5UGFydHkubmFt5SIuIOnhdbFQY3EL/LQD90w6y+kCfj4x8UWXaqKtW68GBPlnREMAQTkwPjEOMAwGA1UEAwwFTm9kZUExDjAMBgNVBAoMBU5vZGVBMQ8wDQYDVQQHDAZMb25kb24xCzAJBgNVBAYTAlVLAgEAJgAkASIuIHI7goTSxPMdaRgJgGJVLQbFEzE++qJeYbEbQjrYxzuVRkUAQzkwQDEOMAwGA1UEAwwFTm9kZUIxDjAMBgNVBAoMBU5vZGVCMREwDwYDVQQHDAhOZXcgWW9yazELMAkGA1UEBhMCVVMCAQABAAABAAAkASIuIMqulslvpZ0PaM6fdyFZm+JsDGkuJ7xWnL3zB6PqpzANdwB1OTByMRMwEQYDVQQDDApDb250cm9sbGVyMQswCQYDVQQKDAJSMzEOMAwGA1UECwwFY29yZGExDzANBgNVBAcMBkxvbmRvbjELMAkGA1UEBhMCVUsxIDAeBgNVBAsMF2NvcmRhLm5vdGFyeS52YWxpZGF0aW5nAQAAAQABAQNuZXQuY29yZGEuY29yZS5jb250cmFjdHMuQ29tbWFu5AJDb21tYW5kLnNpZ25lcvNDb21tYW5kLnZhbHXlRwEAAi4gcjuChNLE8x1pGAmAYlUtBsUTMT76ol5hsRtCOtjHO5UuIOnhdbFQY3EL/LQD90w6y+kCfj4x8UWXaqKtW68GBPlnADMBBGNvbS5leGFtcGxlLmNvbnRyYWN0LklPVUNvbnRyYWN0JENvbW1hbmRzJENyZWF05QAAAQVuZXQuY29yZGEuY29yZS5pZGVudGl0eS5QYXJ0+SIuIMqulslvpZ0PaM6fdyFZm+JsDGkuJ7xWnL3zB6PqpzANAHU5MHIxEzARBgNVBAMMCkNvbnRyb2xsZXIxCzAJBgNVBAoMAlIzMQ4wDAYDVQQLDAVjb3JkYTEPMA0GA1UEBwwGTG9uZG9uMQswCQYDVQQGEwJVSzEgMB4GA1UECwwXY29yZGEubm90YXJ5LnZhbGlkYXRpbmcAAQACLiByO4KE0sTzHWkYCYBiVS0GxRMxPvqiXmGxG0I62Mc7lS4g6eF1sVBjcQv8tAP3TDrL6QJ+PjHxRZdqoq1brwYE+WcBBm5ldC5jb3JkYS5jb3JlLmNvbnRyYWN0cy5UcmFuc2FjdGlvblR5cGUkR2VuZXJh7AA="
sigs:
- "cRgJlF8cUMMooyaV2OIKmR4/+3XmMsEPsbdlhU5YqngRhqgy9+tLzylh7kvWOhYZ4hjjOfrazLoZ6uOx6BAMCQ=="
- "iGLRDIbhlwguMz6yayX5p6vfQcAsp8haZc1cLGm7DPDIgq6hFyx2fzoI03DjXAV/mBT1upcUjM9UZ4gbRMedAw=="
id: "52A1B18E6ABD535EF36B2075469B01D2EF888034F721C4BECD26F40355C8C9DC"
tx:
inputs: []
attachments: []
outputs:
- data:
iou:
value: 50
sender: "CN=NodeB,O=NodeB,L=New York,C=US"
recipient: "CN=NodeA,O=NodeA,L=London,C=UK"
linearId:
externalId: null
id: "84628565-2688-45ef-bb06-aae70fcf3be7"
contract: {}
participants:
- "CN=NodeB,O=NodeB,L=New York,C=US"
- "CN=NodeA,O=NodeA,L=London,C=UK"
notary: "O=Controller,OU=corda,L=London,C=UK,OU=corda.notary.validating"
encumbrance: null
commands:
- value: {}
signers:
- "8Kqd4oWdx4KQAVc3u5qvHZTGJxMtrShFudAzLUTdZUzbF9aPQcCZD5KXViC"
- "8Kqd4oWdx4KQAVcBx98LBHwXwC3a7hNptQomrg9mq2ScY7t1Qqsyk5dCNAr"
notary: "O=Controller,OU=corda,L=London,C=UK,OU=corda.notary.validating"
type: {}
timeWindow: null
mustSign:
- "8Kqd4oWdx4KQAVc3u5qvHZTGJxMtrShFudAzLUTdZUzbF9aPQcCZD5KXViC"
- "8Kqd4oWdx4KQAVcBx98LBHwXwC3a7hNptQomrg9mq2ScY7t1Qqsyk5dCNAr"
id: "52A1B18E6ABD535EF36B2075469B01D2EF888034F721C4BECD26F40355C8C9DC"
merkleTree: ...
availableComponents: ...
availableComponentHashes: ...
serialized: "Y29yZGEAAAEOAQEAamF2YS51dGlsLkFycmF5TGlz9AABAAABAAEBAW5ldC5jb3JkYS5jb3JlLmNvbnRyYWN0cy5UcmFuc2FjdGlvblN0YXTlA1RyYW5zYWN0aW9uU3RhdGUuZGF04VRyYW5zYWN0aW9uU3RhdGUuZW5jdW1icmFuY+VUcmFuc2FjdGlvblN0YXRlLm5vdGFy+WkBAmNvbS5leGFtcGxlLnN0YXRlLklPVVN0YXTlBElPVVN0YXRlLmlv9UlPVVN0YXRlLmxpbmVhcknkSU9VU3RhdGUucmVjaXBpZW70SU9VU3RhdGUuc2VuZGXyDQEBSU9VLnZhbHXlAWQCAQA0ADIBAlVuaXF1ZUlkZW50aWZpZXIuZXh0ZXJuYWxJ5FVuaXF1ZUlkZW50aWZpZXIuaeQBgDAvAC0BAlVVSUQubGVhc3RTaWdCaXTzVVVJRC5tb3N0U2lnQml08wmxkIaDnsaq+YkNDAsACaHovZfbpr2d9wMCAQACAQBIAEYBAkFic3RyYWN0UGFydHkub3duaW5nS2X5UGFydHkubmFt5SIuIOnhdbFQY3EL/LQD90w6y+kCfj4x8UWXaqKtW68GBPlnREMAQTkwPjEOMAwGA1UEAwwFTm9kZUExDjAMBgNVBAoMBU5vZGVBMQ8wDQYDVQQHDAZMb25kb24xCzAJBgNVBAYTAlVLAgEAJgAkASIuIHI7goTSxPMdaRgJgGJVLQbFEzE++qJeYbEbQjrYxzuVRkUAQzkwQDEOMAwGA1UEAwwFTm9kZUIxDjAMBgNVBAoMBU5vZGVCMREwDwYDVQQHDAhOZXcgWW9yazELMAkGA1UEBhMCVVMCAQABAAABAAAkASIuIMqulslvpZ0PaM6fdyFZm+JsDGkuJ7xWnL3zB6PqpzANdwB1OTByMRMwEQYDVQQDDApDb250cm9sbGVyMQswCQYDVQQKDAJSMzEOMAwGA1UECwwFY29yZGExDzANBgNVBAcMBkxvbmRvbjELMAkGA1UEBhMCVUsxIDAeBgNVBAsMF2NvcmRhLm5vdGFyeS52YWxpZGF0aW5nAQAAAQABAQNuZXQuY29yZGEuY29yZS5jb250cmFjdHMuQ29tbWFu5AJDb21tYW5kLnNpZ25lcvNDb21tYW5kLnZhbHXlRwEAAi4gcjuChNLE8x1pGAmAYlUtBsUTMT76ol5hsRtCOtjHO5UuIOnhdbFQY3EL/LQD90w6y+kCfj4x8UWXaqKtW68GBPlnADMBBGNvbS5leGFtcGxlLmNvbnRyYWN0LklPVUNvbnRyYWN0JENvbW1hbmRzJENyZWF05QAAAQVuZXQuY29yZGEuY29yZS5pZGVudGl0eS5QYXJ0+SIuIMqulslvpZ0PaM6fdyFZm+JsDGkuJ7xWnL3zB6PqpzANAHU5MHIxEzARBgNVBAMMCkNvbnRyb2xsZXIxCzAJBgNVBAoMAlIzMQ4wDAYDVQQLDAVjb3JkYTEPMA0GA1UEBwwGTG9uZG9uMQswCQYDVQQGEwJVSzEgMB4GA1UECwwXY29yZGEubm90YXJ5LnZhbGlkYXRpbmcAAQACLiByO4KE0sTzHWkYCYBiVS0GxRMxPvqiXmGxG0I62Mc7lS4g6eF1sVBjcQv8tAP3TDrL6QJ+PjHxRZdqoq1brwYE+WcBBm5ldC5jb3JkYS5jb3JlLmNvbnRyYWN0cy5UcmFuc2FjdGlvblR5cGUkR2VuZXJh7AA="
second: "(observable)"
The same states and transactions will be present on NodeB, who was NodeA's counterparty in the creation of the IOU.
However, the vault and local storage of NodeC will remain empty, since NodeC was not involved in the transaction.
Via the h2 web console
~~~~~~~~~~~~~~~~~~~~~~
You can connect directly to your node's database to see its stored states, transactions and attachments. To do so,
@ -523,14 +463,16 @@ any IOUs when you first connect to one of the nodes, the client will simply log
*Running the client via IntelliJ:*
Select the 'Run Example RPC Client' run configuration which, by default, connects to NodeA (Artemis port 10007). Click the
Green Arrow to run the client. You can edit the run configuration to connect on a different port.
Select the 'Run Example RPC Client' run configuration which, by default, connects to PartyA. Click the green arrow to
run the client. You can edit the run configuration to connect on a different port.
*Running the client via the command line:*
Run the following gradle task:
``./gradlew runExampleClientRPCKotlin localhost:10007``
``./gradlew runExampleClientRPCKotlin``
This will connect the RPC client to PartyA and log their past and future IOU activity.
You can close the application using ``ctrl+C``.
@ -541,12 +483,12 @@ For more information on the client RPC interface and how to build an RPC client
Running Nodes Across Machines
-----------------------------
The nodes can also be configured to communicate across the network when residing on different machines.
The nodes can be split across machines and configured to communicate across the network.
After deploying the nodes, navigate to the build folder (``kotlin-source/build/nodes`` or ``java-source/build/nodes``)
and move some of the individual node folders to separate machines (e.g. using a USB key). It is important that none of
the nodes - including the controller node - end up on more than one machine. Each computer should also have a copy of
``runnodes`` and ``runnodes.bat``.
After deploying the nodes, navigate to the build folder (``kotlin-source/build/nodes``) and move some of the individual
node folders to a different machine (e.g. using a USB key). It is important that none of the nodes - including the
controller node - end up on more than one machine. Each computer should also have a copy of ``runnodes`` and
``runnodes.bat``.
For example, you may end up with the following layout:
@ -558,18 +500,18 @@ and make the following changes:
* Change the Artemis messaging address to the machine's IP address (e.g. ``p2pAddress="10.18.0.166:10006"``)
* Change the network map service's address to the IP address of the machine where the controller node is running
(e.g. ``networkMapService { address="10.18.0.166:10002" ...``). The controller will not have the
``networkMapService`` config
(e.g. ``networkMapService { address="10.18.0.166:10002" legalName="O=Controller,L=London,C=GB" ``). The controller
will not have the ``networkMapService`` configuration entry
After starting each node, they should be able to see one another and agree IOUs among themselves.
After starting each node, the nodes will be able to see one another and agree IOUs among themselves.
Debugging your CorDapp
----------------------
Debugging is done via IntelliJ as follows:
1. Edit the node driver code in ``Main.kt`` to reflect the number of nodes you wish to start, along with any other
configuration options. For example, the code below starts 4 nodes, with one being the network map service and notary.
It also sets up RPC credentials for the three non-notary nodes
1. Edit the node driver code in ``Main.kt`` based on the number of nodes you wish to start, along with any other
configuration options. For example, the code below starts 4 nodes, with one being the network map service and
notary. It also sets up RPC credentials for the three non-notary nodes
.. sourcecode:: kotlin
@ -577,11 +519,11 @@ Debugging is done via IntelliJ as follows:
// No permissions required as we are not invoking flows.
val user = User("user1", "test", permissions = setOf())
driver(isDebug = true) {
startNode(getX500Name(O="Controller",OU="corda",L="London",C="UK"), setOf(ServiceInfo(ValidatingNotaryService.type)))
startNode(getX500Name(O="Controller",L="London",C="GB"), setOf(ServiceInfo(ValidatingNotaryService.type)))
val (nodeA, nodeB, nodeC) = Futures.allAsList(
startNode(getX500Name(O="NodeA",L=London,C=UK"), rpcUsers = listOf(user)),
startNode(getX500Name(O="NodeB",L=New York,C=US"), rpcUsers = listOf(user)),
startNode(getX500Name(O="NodeC",L=Paris,C=FR"), rpcUsers = listOf(user))).getOrThrow()
startNode(getX500Name(O="PartyA",L=London,C=GB"), rpcUsers = listOf(user)),
startNode(getX500Name(O="PartyB",L=New York,C=US"), rpcUsers = listOf(user)),
startNode(getX500Name(O="PartyC",L=Paris,C=FR"), rpcUsers = listOf(user))).getOrThrow()
startWebserver(nodeA)
startWebserver(nodeB)
@ -592,14 +534,18 @@ Debugging is done via IntelliJ as follows:
}
2. Select and run the “Run Example CorDapp” run configuration in IntelliJ
3. IntelliJ will build and run the CorDapp. The remote debug ports for each node will be automatically generated and
printed to the terminal. For example:
.. sourcecode:: none
[INFO ] 15:27:59.533 [main] Node.logStartupInfo - Working Directory: /Users/joeldudley/cordapp-tutorial/build/20170707142746/NodeA
[INFO ] 15:27:59.533 [main] Node.logStartupInfo - Working Directory: /Users/joeldudley/cordapp-example/build/20170707142746/PartyA
[INFO ] 15:27:59.533 [main] Node.logStartupInfo - Debug port: dt_socket:5007
4. Edit the “Debug CorDapp” run configuration with the port of the node you wish to connect to
5. Run the “Debug CorDapp” run configuration
6. Set your breakpoints and start using your node. When your node hits a breakpoint, execution will pause
6. Set your breakpoints and start interacting with the node you wish to connect to. When the node hits a breakpoint,
execution will pause