Better docs of CorDapp structure and node interaction (#3761)

* Clean-up. Instructions on how template would be modified for production.

* Change page titles to make it clearer make they contain.

* Simple example of how to connect to node via RPC. Explanation of how to interact with node via RPC.

* Bigger warning about deprecated webserver. Makes it clear that CordaRPCClient is THE way to interact with a node.

* Review from Clinton.

* Separating template info from general info.
This commit is contained in:
Joel Dudley 2018-08-17 16:52:56 +01:00 committed by GitHub
parent 911aa1381b
commit 5d39f2bb46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 213 additions and 97 deletions

View File

@ -1,27 +1,52 @@
Client RPC .. highlight:: kotlin
========== .. raw:: html
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/codesets.js"></script>
Interacting with a node
=======================
.. contents:: .. contents::
Overview Overview
-------- --------
Corda provides a client library that allows you to easily write clients in a JVM-compatible language to interact You should interact with your node using the `CordaRPCClient`_ library. This library that allows you to easily
with a running node. The library connects to the node using a message queue protocol and then provides a simple RPC write clients in a JVM-compatible language to interact with a running node. The library connects to the node using a
interface to interact with the node. You make calls on a Java object as normal, and the marshalling back and forth is message queue protocol and then provides a simple RPC interface to interact with the node. You make calls on a JVM
handled for you. object as normal, and the marshalling back and forth is handled for you.
The starting point for the client library is the `CordaRPCClient`_ class. `CordaRPCClient`_ provides a ``start`` method .. warning:: The built-in Corda webserver is deprecated and unsuitable for production use. If you want to interact with
that returns a `CordaRPCConnection`_. A `CordaRPCConnection`_ allows you to access an implementation of the your node via HTTP, you will need to stand up your own webserver, then create an RPC connection between your node
`CordaRPCOps`_ interface with ``proxy`` in Kotlin or ``getProxy()`` in Java. The observables that are returned by RPC and this webserver using the `CordaRPCClient`_ library. You can find an example of how to do this
operations can be subscribed to in order to receive an ongoing stream of updates from the node. More detail on this `here <https://github.com/corda/spring-webserver>`_.
functionality is provided in the docs for the ``proxy`` method.
Connecting to a node via RPC
----------------------------
`CordaRPCClient`_ provides a ``start`` method that takes the node's RPC address and returns a `CordaRPCConnection`_.
`CordaRPCConnection`_ provides a ``proxy`` method that takes an RPC username and password and returns a `CordaRPCOps`_
object that you can use to interact with the node.
Here is an example of using `CordaRPCClient`_ to connect to a node and log the current time on its internal clock:
.. container:: codeset
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/ClientRpcExample.kt
:language: kotlin
:start-after: START 1
:end-before: END 1
.. literalinclude:: example-code/src/main/java/net/corda/docs/ClientRpcExampleJava.java
:language: java
:start-after: START 1
:end-before: END 1
.. warning:: The returned `CordaRPCConnection`_ is somewhat expensive to create and consumes a small amount of .. warning:: The returned `CordaRPCConnection`_ is somewhat expensive to create and consumes a small amount of
server side resources. When you're done with it, call ``close`` on it. Alternatively you may use the ``use`` server side resources. When you're done with it, call ``close`` on it. Alternatively you may use the ``use``
method on `CordaRPCClient`_ which cleans up automatically after the passed in lambda finishes. Don't create method on `CordaRPCClient`_ which cleans up automatically after the passed in lambda finishes. Don't create
a new proxy for every call you make - reuse an existing one. a new proxy for every call you make - reuse an existing one.
For a brief tutorial on using the RPC API, see :doc:`tutorial-clientrpc-api`. For further information on using the RPC API, see :doc:`tutorial-clientrpc-api`.
RPC permissions RPC permissions
--------------- ---------------
@ -306,7 +331,7 @@ side as if it was thrown from inside the called RPC method. These exceptions can
Connection management Connection management
--------------------- ---------------------
It is possible to not be able to connect to the server on the first attempt. In that case, the ``CordaRPCCLient.start()`` It is possible to not be able to connect to the server on the first attempt. In that case, the ``CordaRPCClient.start()``
method will throw an exception. The following code snippet is an example of how to write a simple retry mechanism for method will throw an exception. The following code snippet is an example of how to write a simple retry mechanism for
such situations: such situations:

View File

@ -1,4 +1,4 @@
Node configuration Configuring a node
================== ==================
.. contents:: .. contents::

View File

@ -1,5 +1,5 @@
Building a CorDapp Building and installing a CorDapp
================== =================================
.. contents:: .. contents::

View File

@ -3,20 +3,38 @@ What is a CorDapp?
CorDapps (Corda Distributed Applications) are distributed applications that run on the Corda platform. The goal of a CorDapps (Corda Distributed Applications) are distributed applications that run on the Corda platform. The goal of a
CorDapp is to allow nodes to reach agreement on updates to the ledger. They achieve this goal by defining flows that CorDapp is to allow nodes to reach agreement on updates to the ledger. They achieve this goal by defining flows that
Corda node owners can invoke through RPC calls: Corda node owners can invoke over RPC:
.. image:: resources/node-diagram.png .. image:: resources/node-diagram.png
:scale: 25%
:align: center
CorDapps are made up of the following key components: CorDapp components
------------------
CorDapps take the form of a set of JAR files containing class definitions written in Java and/or Kotlin.
* States, defining the facts over which agreement is reached (see :doc:`Key Concepts - States <key-concepts-states>`) These class definitions will commonly include the following elements:
* Flows: Define a routine for the node to run, usually to update the ledger
(see :doc:`Key Concepts - Flows <key-concepts-flows>`). They subclass ``FlowLogic``
* States: Define the facts over which agreement is reached (see :doc:`Key Concepts - States <key-concepts-states>`).
They implement the ``ContractState`` interface
* Contracts, defining what constitutes a valid ledger update (see * Contracts, defining what constitutes a valid ledger update (see
:doc:`Key Concepts - Contracts <key-concepts-contracts>`) :doc:`Key Concepts - Contracts <key-concepts-contracts>`). They implement the ``Contract`` interface
* Services, providing long-lived utilities within the node * Services, providing long-lived utilities within the node. They subclass ``SingletonSerializationToken``
* Serialisation whitelists, restricting what types your node will receive off the wire * Serialisation whitelists, restricting what types your node will receive off the wire. They implement the
``SerializationWhitelist`` interface
Each CorDapp is installed at the level of the individual node, rather than on the network itself. For example, a node But the CorDapp JAR can also include other class definitions. These may include:
owner may choose to install the Bond Trading CorDapp, with the following components:
* 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
* Utility classes
An example
----------
Suppose a node owner wants their node to be able to trade bonds. They may choose to install a Bond Trading CorDapp with
the following components:
* A ``BondState``, used to represent bonds as shared facts on the ledger * A ``BondState``, used to represent bonds as shared facts on the ledger
* A ``BondContract``, used to govern which ledger updates involving ``BondState`` states are valid * A ``BondContract``, used to govern which ledger updates involving ``BondState`` states are valid

View File

@ -0,0 +1,34 @@
package net.corda.docs;
// START 1
import net.corda.client.rpc.CordaRPCClient;
import net.corda.client.rpc.CordaRPCConnection;
import net.corda.core.messaging.CordaRPCOps;
import net.corda.core.utilities.NetworkHostAndPort;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ExecutionException;
class ExampleRpcClientJava {
private static final Logger logger = LoggerFactory.getLogger(ExampleRpcClient.class);
public static void main(String[] args) throws ActiveMQException, InterruptedException, ExecutionException {
if (args.length != 3) {
throw new IllegalArgumentException("Usage: TemplateClient <node address> <username> <password>");
}
final NetworkHostAndPort nodeAddress = NetworkHostAndPort.parse(args[0]);
String username = args[1];
String password = args[2];
final CordaRPCClient client = new CordaRPCClient(nodeAddress);
final CordaRPCConnection connection = client.start(username, password);
final CordaRPCOps cordaRPCOperations = connection.getProxy();
logger.info(cordaRPCOperations.currentNodeTime().toString());
connection.notifyServerAndClose();
}
}
// END 1

View File

@ -0,0 +1,31 @@
@file:Suppress("unused")
package net.corda.docs
// START 1
import net.corda.client.rpc.CordaRPCClient
import net.corda.core.utilities.NetworkHostAndPort.Companion.parse
import net.corda.core.utilities.loggerFor
import org.slf4j.Logger
class ExampleRpcClient {
companion object {
val logger: Logger = loggerFor<ExampleRpcClient>()
}
fun main(args: Array<String>) {
require(args.size == 3) { "Usage: TemplateClient <node address> <username> <password>" }
val nodeAddress = parse(args[0])
val username = args[1]
val password = args[2]
val client = CordaRPCClient(nodeAddress)
val connection = client.start(username, password)
val cordaRPCOperations = connection.proxy
logger.info(cordaRPCOperations.currentNodeTime().toString())
connection.notifyServerAndClose()
}
}
// END 1

View File

@ -4,8 +4,8 @@
<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>
Shell Node shell
===== ==========
.. contents:: .. contents::

View File

@ -1,3 +1,9 @@
.. highlight:: kotlin
.. raw:: html
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/codesets.js"></script>
.. _graphstream: http://graphstream-project.org/ .. _graphstream: http://graphstream-project.org/
Using the client RPC API Using the client RPC API

View File

@ -1,58 +1,70 @@
Writing a CorDapp CorDapp structure
================= =================
.. contents:: .. 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.
.. _cordapp-structure: .. _cordapp-structure:
Structure and dependencies Modules
-------------------------- -------
You should base your project on the Java template (for CorDapps written in Java) or the Kotlin template (for CorDapps The source code for a CorDapp is divided into one or more modules, each of which will be compiled into a separate JAR.
written in Kotlin): Together, these JARs represent a single CorDapp. Typically, a Cordapp contains all the classes required for it to be
used standalone. However, some Cordapps are only libraries for other Cordapps and cannot be run standalone.
* `Java Template CorDapp <https://github.com/corda/cordapp-template-java>`_ A common pattern is to have:
* `Kotlin Template CorDapp <https://github.com/corda/cordapp-template-kotlin>`_
Please checkout the branch of the template that corresponds to the version of Corda you are using. For example, someone * One module containing only the CorDapp's contracts and/or states, as well as any required dependencies
building a CorDapp on Corda 3 should use the ``release-V3`` branch of the template. * A second module containing the remaining classes that depend on these contracts and/or states
The required dependencies are defined by the ``build.gradle`` file in the root directory of the template. This is because each time a contract is used in a transaction, the entire JAR containing the contract's definition is
attached to the transaction. This is to ensure that the exact same contract and state definitions are used when
verifying this transaction at a later date. Because of this, you will want to keep this module, and therefore the
resulting JAR file, as small as possible to reduce the size of your transactions and keep your node performant.
The project should be split into two modules: However, this two-module structure is not prescriptive:
* A ``cordapp-contracts-states`` module containing classes such as contracts and states that will be sent across the * A library CorDapp containing only contracts and states would only need a single module
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 * In a CorDapp with multiple sets of contracts and states that **do not** depend on each other, each independent set of
wire when nodes are agreeing ledger updates. contracts and states would go in a separate module to reduce transaction size
* In a CorDapp with multiple sets of contracts and states that **do** depend on each other, either keep them in the
same module or create separate modules that depend on each other
* The module containing the flows and other classes can be structured in any way because it is not attached to
transactions
Template CorDapps
-----------------
You should base your project on one of the following templates:
* `Java Template CorDapp <https://github.com/corda/cordapp-template-java>`_ (for CorDapps written in Java)
* `Kotlin Template CorDapp <https://github.com/corda/cordapp-template-kotlin>`_ (for CorDapps written in Kotlin)
Please use the branch of the template that corresponds to the major version of Corda you are using. For example,
someone building a CorDapp on Corda 3.2 should use the ``release-V3`` branch of the template.
Build system
^^^^^^^^^^^^
The templates are built using Gradle. A Gradle wrapper is provided in the ``wrapper`` folder, and the dependencies are
defined in the ``build.gradle`` files. See :doc:`cordapp-build-systems` for more information.
No templates are currently provided for Maven or other build systems.
Modules
^^^^^^^
The templates are split into two modules:
* A ``cordapp-contracts-states`` module containing the contracts and states
* A ``cordapp`` module containing the remaining classes that depends on the ``cordapp-contracts-states`` module
These modules will be compiled into two JARs - a ``cordapp-contracts-states`` JAR and a ``cordapp`` JAR - which
together represent the Template CorDapp.
Module one - cordapp-contracts-states Module one - cordapp-contracts-states
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Here is the structure of the ``src`` directory for the ``cordapp-contracts-states`` module: Here is the structure of the ``src`` directory for the ``cordapp-contracts-states`` module of the Java template:
.. parsed-literal:: .. parsed-literal::
@ -73,8 +85,8 @@ These are definitions for classes that we expect to have to send over the wire.
CorDapp. CorDapp.
Module two - cordapp Module two - cordapp
^^^^^^^^^^^^^^^^^^^^ ~~~~~~~~~~~~~~~~~~~~
Here is the structure of the ``src`` directory for the ``cordapp`` module: Here is the structure of the ``src`` directory for the ``cordapp`` module of the Java template:
.. parsed-literal:: .. parsed-literal::
@ -116,37 +128,27 @@ The ``src`` directory is structured as follows:
Within ``main``, we have the following directories: Within ``main``, we have the following directories:
* ``resources/META-INF/services`` contains registries of the CorDapp's serialisation whitelists and web plugins * ``java``, which contains the source-code for our CorDapp:
* ``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 template ``FlowLogic`` subclass
* ``TemplateState.java``, which contains a template ``ContractState`` implementation
* ``TemplateContract.java``, which contains a template ``Contract`` implementation
* ``TemplateSerializationWhitelist.java``, which contains a template ``SerializationWhitelist`` implementation
* ``TemplateApi.java``, which contains a template API for the deprecated Corda webserver
* ``TemplateWebPlugin.java``, which registers the API and front-end for the deprecated Corda webserver
* ``TemplateClient.java``, which contains a template RPC client for interacting with our CorDapp
* ``TemplateFlow.java``, which contains a dummy ``FlowLogic`` subclass * ``resources/META-INF/services``, which contains various registries:
* ``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 * ``net.corda.core.serialization.SerializationWhitelist``, which registers the CorDapp's serialisation whitelists
single CorDapp can define multiple flows, states, and contracts. * ``net.corda.webserver.services.WebServerPluginRegistry``, which registers the CorDapp's web plugins
The template also includes a web API and RPC client: * ``resources/templateWeb``, which contains a template front-end
* ``TemplateApi.java`` In a production CorDapp:
* ``TemplateClient.java``
* ``TemplateWebPlugin.java``
These are for testing purposes and would be removed in a production CorDapp. * We would remove the files related to the deprecated Corda webserver (``TemplateApi.java``,
``TemplateWebPlugin.java``, ``resources/templateWeb``, and ``net.corda.webserver.services.WebServerPluginRegistry``)
and replace them with a production-ready webserver
Resources * We would also move ``TemplateClient.java`` into a separate module so that it is not included in the CorDapp
---------
In writing a CorDapp, these pages may be particularly helpful:
* :doc:`getting-set-up`, to set up your development environment.
* The :doc:`hello-world-introduction` tutorial to write your first CorDapp.
* :doc:`cordapp-build-systems` to build and run your CorDapp.
* The `API docs </api/javadoc/index.html>`_ to read about the API available in developing CorDapps.
* There is also a :doc:`cheat-sheet` recapping the key types.
* The :doc:`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.