From 3b77de67b6d65698d15e7806d07604fa24264591 Mon Sep 17 00:00:00 2001 From: Andras Slemmer Date: Tue, 4 Oct 2016 13:56:31 +0100 Subject: [PATCH] docs: add docs/source/example-code module, tutorial on the Client RPC API --- .idea/modules.xml | 3 + docs/source/example-code/build.gradle | 46 +++++++++++ .../com/r3corda/docs/ClientRpcTutorial.kt | 73 +++++++++++++++++ docs/source/index.rst | 2 +- docs/source/tutorial-clientrpc-api.rst | 82 +++++++++++++++++++ settings.gradle | 1 + 6 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 docs/source/example-code/build.gradle create mode 100644 docs/source/example-code/src/main/kotlin/com/r3corda/docs/ClientRpcTutorial.kt create mode 100644 docs/source/tutorial-clientrpc-api.rst diff --git a/.idea/modules.xml b/.idea/modules.xml index 9eaa61f172..dec18dacb0 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -15,6 +15,9 @@ + + + diff --git a/docs/source/example-code/build.gradle b/docs/source/example-code/build.gradle new file mode 100644 index 0000000000..626f36e3bb --- /dev/null +++ b/docs/source/example-code/build.gradle @@ -0,0 +1,46 @@ +apply plugin: 'kotlin' +apply plugin: 'application' + +buildscript { + repositories { + mavenCentral() + } +} + +repositories { + mavenLocal() + mavenCentral() + jcenter() + maven { + url 'http://oss.sonatype.org/content/repositories/snapshots' + } + maven { + url 'https://dl.bintray.com/kotlin/exposed' + } +} + +dependencies { + compile project(':core') + compile project(':client') + + compile "org.graphstream:gs-core:1.3" + compile("org.graphstream:gs-ui:1.3") { + exclude group: "bouncycastle" + } +} + +mainClassName = "com.r3corda.docs.ClientRpcTutorialKt" + +task getClientRpcTutorial(type: CreateStartScripts) { + dependsOn(classes) + mainClassName = "com.r3corda.docs.ClientRpcTutorialKt" + applicationName = "client-rpc-tutorial" + defaultJvmOpts = [] + outputDir = new File(project.buildDir, 'scripts') + classpath = jar.outputs.files + project.configurations.runtime +} + +applicationDistribution.into("bin") { + from(getClientRpcTutorial) + fileMode = 0755 +} diff --git a/docs/source/example-code/src/main/kotlin/com/r3corda/docs/ClientRpcTutorial.kt b/docs/source/example-code/src/main/kotlin/com/r3corda/docs/ClientRpcTutorial.kt new file mode 100644 index 0000000000..44ffdc4144 --- /dev/null +++ b/docs/source/example-code/src/main/kotlin/com/r3corda/docs/ClientRpcTutorial.kt @@ -0,0 +1,73 @@ +package com.r3corda.docs + +import com.google.common.net.HostAndPort +import com.r3corda.client.CordaRPCClient +import org.graphstream.graph.Edge +import org.graphstream.graph.Node +import org.graphstream.graph.implementations.SingleGraph +import java.nio.file.Paths +import java.util.concurrent.CompletableFuture + +/** + * This is example code for the Client RPC API tutorial. The START/END comments are important and used by the documentation! + */ + +// START 1 +enum class PrintOrVisualise { + Print, + Visualise +} + +fun main(args: Array) { + if (args.size < 2) { + throw IllegalArgumentException("Usage: [Print|Visualise]") + } + val nodeAddress = HostAndPort.fromString(args[0]) + val printOrVisualise = PrintOrVisualise.valueOf(args[1]) + val certificatesPath = Paths.get("build/trader-demo/buyer/certificates") + // END 1 + + // START 2 + val client = CordaRPCClient(nodeAddress, certificatesPath) + client.start() + val proxy = client.proxy() + // END 2 + + // START 3 + val (transactions, futureTransactions) = proxy.verifiedTransactions() + // END 3 + + // START 4 + when (printOrVisualise) { + PrintOrVisualise.Print -> { + futureTransactions.startWith(transactions).subscribe { transaction -> + println("NODE ${transaction.id}") + transaction.tx.inputs.forEach { input -> + println("EDGE ${input.txhash} ${transaction.id}") + } + } + CompletableFuture().get() // block indefinitely + } + // END 4 + // START 5 + PrintOrVisualise.Visualise -> { + val graph = SingleGraph("transactions") + transactions.forEach { transaction -> + graph.addNode("${transaction.id}") + } + transactions.forEach { transaction -> + transaction.tx.inputs.forEach { ref -> + graph.addEdge("$ref", "${ref.txhash}", "${transaction.id}") + } + } + futureTransactions.subscribe { transaction -> + graph.addNode("${transaction.id}") + transaction.tx.inputs.forEach { ref -> + graph.addEdge("${ref}", "${ref.txhash}", "${transaction.id}") + } + } + graph.display() + } + } +} +// END 5 diff --git a/docs/source/index.rst b/docs/source/index.rst index e1372461c4..83c492a821 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -47,6 +47,7 @@ Read on to learn: tutorial-contract tutorial-contract-clauses tutorial-test-dsl + tutorial-clientrpc-api protocol-state-machines oracles event-scheduling @@ -74,4 +75,3 @@ Read on to learn: network-simulator codestyle building-the-docs - diff --git a/docs/source/tutorial-clientrpc-api.rst b/docs/source/tutorial-clientrpc-api.rst new file mode 100644 index 0000000000..5f19b7f635 --- /dev/null +++ b/docs/source/tutorial-clientrpc-api.rst @@ -0,0 +1,82 @@ +.. _graphstream: http://graphstream-project.org/ + +Client RPC API +============== + +In this tutorial we will build a simple command line utility that connects to a node and dumps the transaction graph to +the standard output. We will then put some simple visualisation on top. + +We start off by connecting to the node itself. For the purposes of the tutorial we will run the Trader demo on some +local port and connect to the Buyer side. We will pass in the address as a command line argument. To connect to the node +we also need to access the certificates of the node, we will access the node's ``certificates`` directory directly. + +.. literalinclude:: example-code/src/main/kotlin/com/r3corda/docs/ClientRpcTutorial.kt + :language: kotlin + :start-after: START 1 + :end-before: END 1 + +Now we can connect to the node itself: + +.. literalinclude:: example-code/src/main/kotlin/com/r3corda/docs/ClientRpcTutorial.kt + :language: kotlin + :start-after: START 2 + :end-before: END 2 + +``proxy`` now exposes the full RPC interface of the node: + +.. literalinclude:: ../../node/src/main/kotlin/com/r3corda/node/services/messaging/CordaRPCOps.kt + :language: kotlin + :start-after: interface CordaRPCOps + :end-before: } + +The one we need in order to dump the transaction graph is ``verifiedTransactions``. The type signature tells us that the +RPC will return a list of transactions and an Observable stream. This is a general pattern, we query some data and the +node will return the current snapshot and future updates done to it. + +.. literalinclude:: example-code/src/main/kotlin/com/r3corda/docs/ClientRpcTutorial.kt + :language: kotlin + :start-after: START 3 + :end-before: END 3 + +The graph will be defined by nodes and edges between them. Each node represents a transaction and edges represent +output-input relations. For now let's just print ``NODE `` for the former and ``EDGE `` for the +latter. + +.. literalinclude:: example-code/src/main/kotlin/com/r3corda/docs/ClientRpcTutorial.kt + :language: kotlin + :start-after: START 4 + :end-before: END 4 + +Now we can start the trader demo as per described in :doc:`running-the-demos`:: + + # Build the demo + ./gradlew installDist + # Start the buyer + ./build/install/r3prototyping/bin/trader-demo --role=BUYER + +In another terminal we can connect to it with our client:: + + # Connect to localhost:31337 + ./docs/source/code/build/install/docs/source/code/bin/client-rpc-tutorial localhost:31337 Print + +We should see some ``NODE``-s printed. This is because the buyer self-issues some cash for the demo. +Unless we ran the seller before we shouldn't see any ``EDGE``-s because the cash hasn't been spent yet. + +In another terminal we can now start the seller:: + + # Start sellers in a loop + for i in {0..9} ; do ./build/install/r3prototyping/bin/trader-demo --role=SELLER ; done + +We should start seeing new ``NODE``-s and ``EDGE``-s appearing. + +Now let's try to visualise the transaction graph. We will use a graph drawing library called graphstream_ + +.. literalinclude:: example-code/src/main/kotlin/com/r3corda/docs/ClientRpcTutorial.kt + :language: kotlin + :start-after: START 5 + :end-before: END 5 + +If we run the client with ``Visualise`` we should see a simple graph being drawn as new transactions are being created +by the seller runs. + +That's it! We saw how to connect to the node and stream data from it. diff --git a/settings.gradle b/settings.gradle index 5eb65bc579..7b1a89a8ab 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,3 +8,4 @@ include 'experimental' include 'test-utils' include 'network-simulator' include 'explorer' +include 'docs/source/example-code'