mirror of
https://github.com/corda/corda.git
synced 2024-12-19 04:57:58 +00:00
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:
parent
911aa1381b
commit
5d39f2bb46
@ -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
|
||||
---------------
|
||||
@ -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:
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
Node configuration
|
||||
Configuring a node
|
||||
==================
|
||||
|
||||
.. contents::
|
||||
|
@ -1,5 +1,5 @@
|
||||
Building a CorDapp
|
||||
==================
|
||||
Building and installing a CorDapp
|
||||
=================================
|
||||
|
||||
.. contents::
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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::
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user