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::
Overview
--------
Corda provides a client library that allows you to easily write clients in a JVM-compatible language to interact
with a running node. The library connects to the node using a message queue protocol and then provides a simple RPC
interface to interact with the node. You make calls on a Java object as normal, and the marshalling back and forth is
handled for you.
You should interact with your node using the `CordaRPCClient`_ library. This library that allows you to easily
write clients in a JVM-compatible language to interact with a running node. The library connects to the node using a
message queue protocol and then provides a simple RPC interface to interact with the node. You make calls on a JVM
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
that returns a `CordaRPCConnection`_. A `CordaRPCConnection`_ allows you to access an implementation of the
`CordaRPCOps`_ interface with ``proxy`` in Kotlin or ``getProxy()`` in Java. The observables that are returned by RPC
operations can be subscribed to in order to receive an ongoing stream of updates from the node. More detail on this
functionality is provided in the docs for the ``proxy`` method.
.. warning:: The built-in Corda webserver is deprecated and unsuitable for production use. If you want to interact with
your node via HTTP, you will need to stand up your own webserver, then create an RPC connection between your node
and this webserver using the `CordaRPCClient`_ library. You can find an example of how to do this
`here <https://github.com/corda/spring-webserver>`_.
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
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
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
---------------
@ -276,7 +301,7 @@ will be freed automatically.
is non-deterministic.
.. note:: Observables can only be used as return arguments of an RPC call. It is not currently possible to pass
Observables as parameters to the RPC methods.
Observables as parameters to the RPC methods.
Futures
-------
@ -306,7 +331,7 @@ side as if it was thrown from inside the called RPC method. These exceptions can
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
such situations:

View File

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

View File

@ -1,5 +1,5 @@
Building a CorDapp
==================
Building and installing a CorDapp
=================================
.. 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
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
: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
: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
:doc:`Key Concepts - Contracts <key-concepts-contracts>`). They implement the ``Contract`` interface
* Services, providing long-lived utilities within the node. They subclass ``SingletonSerializationToken``
* 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
owner may choose to install the Bond Trading CorDapp, with the following components:
But the CorDapp JAR can also include other class definitions. These may 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
* 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 ``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/codesets.js"></script>
Shell
=====
Node shell
==========
.. 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/
Using the client RPC API

View File

@ -1,58 +1,70 @@
Writing a CorDapp
CorDapp structure
=================
.. 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:
Structure and dependencies
--------------------------
You should base your project on the Java template (for CorDapps written in Java) or the Kotlin template (for CorDapps
written in Kotlin):
Modules
-------
The source code for a CorDapp is divided into one or more modules, each of which will be compiled into a separate JAR.
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>`_
* `Kotlin Template CorDapp <https://github.com/corda/cordapp-template-kotlin>`_
A common pattern is to have:
Please checkout the branch of the template that corresponds to the version of Corda you are using. For example, someone
building a CorDapp on Corda 3 should use the ``release-V3`` branch of the template.
* One module containing only the CorDapp's contracts and/or states, as well as any required dependencies
* 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
wire as part of a flow
* A ``cordapp`` module containing the remaining classes
* A library CorDapp containing only contracts and states would only need a single module
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.
* In a CorDapp with multiple sets of contracts and states that **do not** depend on each other, each independent set of
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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::
@ -73,8 +85,8 @@ These are definitions for classes that we expect to have to send over the wire.
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::
@ -116,37 +128,27 @@ The ``src`` directory is structured as follows:
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
* ``java``, which contains 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
* ``TemplateState.java``, which contains a dummy ``ContractState`` implementation
* ``TemplateContract.java``, which contains a dummy ``Contract`` implementation
* ``TemplateSerializationWhitelist.java``, which contains a dummy ``SerializationWhitelist`` implementation
* ``resources/META-INF/services``, which contains various registries:
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.
* ``net.corda.core.serialization.SerializationWhitelist``, which registers the CorDapp's serialisation whitelists
* ``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``
* ``TemplateClient.java``
* ``TemplateWebPlugin.java``
In a production CorDapp:
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
---------
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.
* We would also move ``TemplateClient.java`` into a separate module so that it is not included in the CorDapp