Uncategorized

Architecture and Overview of R3 Corda

In this post, I’ll outline the concept, architecture, and a framework for Corda blockchain (which is built under an open source project) with a brief sample of application.

For your beginning, we first setup and configure from scratch on Ubuntu in this post.

Note : I’ll show you about Azure Blockchain Service (Corda protocol) in my future post. Using a cloud managed service (Azure Blockchain Service), you can eliminate your workloads for non-trivial setting, configuration, upgrading, and managing node health.

Architecture – The Ledger with Each Peer’s Perspective

The conceptual architecture of R3 Corda is so unique compared with other ledger platforms.
The idea of R3 Corda is : By focusing on enterprise financial application (banking, trading, …), the platform is simplified and eventually mitigates several risks, such as, scalability, privacy, and so on. (In Corda, non-financial applications are out of scope.)
Thus Corda can handle only fungible data (i.e, measurable quantity), such as a currency, stock price, so on and so forth.
Now let’s see how this concept is achieved in the architecture.

First of all, Corda has no single chain of all blocks unlike other ledger platforms. (i.e, There are no peers that see everything.) Instead, each node only sees and stores a subset of transactions on a ledger.
If some transaction is processed between Bob and Alice, this transaction (including related all states) is stored in Bob’s and Alice’s ledger, but it’s not stored in a Peter’s ledger.

By this design, a network in Corda (in Corda, a network is sometimes called “zone”) can process multiple transactions simultaneously, while it meets the needs for strict privacy requirements.
On contrary, other ledger platforms have a lot of duplicated data and it will get hard to keep consistent immediately.

Note : By this design, the ledger’s resilience should be achieved through HA deployment configuration of nodes in Corda.
On contrary, in other ledger platforms, every node on the network maintains its own copy and then naturally achieves redundancy (prevents a single point of failure) for a ledger.

By this design, Corda network doesn’t also use global broadcast or gossip protocol. It uses AMQP over TLS (i.e, point-to-point messaging) by a peer communication.

Corda runs on Java virtual machine (JVM) and its capabilities rely on JVM ecosystem, such as, platform independency, portability (transmission of bytecode), serialization (suspending), so on and so forth.
Thus a Corda node is a portable JVM run-time process and then you can easily run multiple nodes (i.e, multiple processes) on a single physical machine by running “java” command. (Of course, you can also run each nodes in different physical machines.)

All nodes in a network (zone) can see each other using its configuration called a network map, which is a collection of signed node information and will be maintained in a cache on each node. This cached configuration is restored from a network map server or through filesystem (i.e, copying a map with filesystem).

Note : You can also configure a dynamic compatibility zone, with which you can join a new node dynamically in a zone.

As I’ll describe later in this post, the fungible data (called “state”) is stored historically when a transaction runs. Like state database in Hyperledger Fabric (see here), Corda node also has a relational database called vault, in which the states are stored (updated) internally upon recording of a transaction on ledger. Thus each node maintains both a ledger and a vault for persistence.
A ledger is a immutable “logs”, however developers can easily extract historical states using a rich Vault Query API working with a vault.

Note : The default database management system of Corda is H2 database. Corda Enterprise also supports commercial 3rd party databases for its storage, such as, Azure SQL Database, SQL Server, Oracle, or PostgreSQL.
Azure Blockchain Service provisions Azure SQL Database (not H2 database) for performance and scalability reasons. (The database will not be directly accessible by the customer in Azure Blockchain Service.)

Flow Framework – Messaging and Communications through a CorDapp

As I mentioned above, Corda runs on Java virtual machine (JVM), then you should build your application using programming languages on JVM, such as, Java or Kotlin.
In this post, we will use Java.

Note : In Corda, Kotlin is frequently used, but I’ll use Java here, because many developers are familiar with Java…

Corda application called a CorDapp should be implemented along with a communication framework and is registered in each Corda nodes. (As you can see later in this post, a CorDapp runs state machines on multiple nodes.)

Now let’s see a flow of this framework through a Java sample code.
Here we build a CorDapp for a simple IOU deal application. (This source code is based on Corda’s official tutorial.)

1. State

First you should define static values for a contract (such as, a deal amount, a holder, …) as a contract state. Through a communication flow, an instance of contract state will be stored in storage, and will be extracted by a query.

This state class implements ContractState interface as follows.

public class IOUState implements ContractState {  private final int value;  private final Party lender;  private final Party borrower;  public IOUState(int value, Party lender, Party borrower) {this.value = value;this.lender = lender;this.borrower = borrower;  }  public int getValue() {return value;  }  public Party getLender() {return lender;  }  public Party getBorrower() {return borrower;  }  @Override  public List<AbstractParty> getParticipants() {return Arrays.asList(lender, borrower);  }}

2. Contract and Command

A contract in Corda is not like a smart contract in other ledger platforms. Instead, like a contract in our real life, it defines the following 2 types of interfaces :

  • Deal types, such as, “Create”, “Borrow”, “Issue”, …
  • Conditions (contractual validities), such as, “not larger than some threshold amount”, “a borrower cannot be the same as a lender”, …

A contract class implements Contract interface and conditions (contractual validities) are implemented by overriding verify() method in an interface. (If it doesn’t meet conditions, you can reject by throwing IllegalArgumentException.)
The deal types (“Create”, “Borrow”, “Issue”, …) are called a command and a command class (which implements CommandData interface) is a member of a contact.

In this example (see below), we simply define one command “Create” in a contract “IOUContract“. You can also define multiple commands and branch verification logic by commands in a single contract.

public class IOUContract implements Contract {  public static final String ID = "com.example.contract.IOUContract";  // "Create" command  public static class Create implements CommandData {  }  @Override  public void verify(LedgerTransaction tx) {// IOU-specific constraintsfinal IOUState output = tx.outputsOfType(IOUState.class).get(0);final Party lender = output.getLender();final Party borrower = output.getBorrower();if (output.getValue() <= 0)  throw new IllegalArgumentException("The IOU's value must be non-negative.");if (lender.equals(borrower))  throw new IllegalArgumentException("The lender and the borrower cannot be the same entity.");  }}

3. Flow – Initiator Side

Now you have implemented a state and a contract (including a command). Here you combine these instances using a logic called a flow.

There’re 2 types of flows, an initiator (sender) flow and a counterparty (acceptor) flow. In a real scenario, it’s like roles, such as, lender – borrower, buyer – seller, …
In this section, we’ll implement an initiator (sender) flow.

In an initiator flow, you should build up a transaction with states and commands (in a contract).
Let’s say, Alice creates a transaction which consumes her $15 in an input and has two outputs – one is $10 for Bob and another is $5 change for her. In this case, there exist 3 state’s instances – one input state ($15) and 2 output states ($10 for payment and $5 for change) – and one command’s instance “consume” in a contract. (See below.)
A transaction might have zero inputs (such as, issuances) or zero outputs (such as, exit finances).

In our example, we simply create a transaction with no inputs and a single output (an IOU state) with a “Create” command as follows.

A flow is a simple class which extends (inherits) FlowLogic class with only constructors and a call() override method. A logic building and initiating a transaction is written in call() method as follows. (As you can see below, you can build a transaction with built-in TransactionBuilder class.)
The following @InitiatingFlow annotation indicates that this class is an initiator’s flow.

public class ExampleFlow {  @InitiatingFlow  @StartableByRPC  public static class Initiator extends FlowLogic<Void> {private final Integer iouValue;private final Party otherParty;public Initiator(int iouValue, Party otherParty) {  this.iouValue = iouValue;  this.otherParty = otherParty;}@Suspendable@Overridepublic Void call() throws FlowException {  // Create State and Command  IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);  Command command = new Command<>(new IOUContract.Create(), ...);  // Create a builder for transaction  Party notary =getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);  TransactionBuilder txBuilder = new TransactionBuilder(notary).addOutputState(outputState, IOUContract.ID).addCommand(command);...  return null;}  }}

In a transaction, you should also declare required signers. By requesting for signings, it ensures all required approvals, since the signing is needed for a private key in the corresponding party (node).
In our example, we set 2 signers, an initiator (lender) and an acceptor (borrower). (See below.)

public class ExampleFlow {  @InitiatingFlow  @StartableByRPC  public static class Initiator extends FlowLogic<Void> {...@Suspendable@Overridepublic Void call() throws FlowException {  IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);// Set required signers in a command  List<PublicKey> requiredSigners =Arrays.asList(getOurIdentity().getOwningKey(), otherParty.getOwningKey());  Command command = new Command<>(new IOUContract.Create(), requiredSigners);  ...  return null;}  }}

Note : In Corda, there’s no concept such as “account”, and each “node” represents a corresponding “party”.

If you want to make sure that both a lender and a borrower are exactly included in these 2 signers, add a verification step in above verify() method in a contract. (See below.)

public class IOUContract implements Contract {  public static final String ID = "com.example.contract.IOUContract";  public static class Create implements CommandData {  }  @Override  public void verify(LedgerTransaction tx) {...// Check whether both borrower and lender is included in signersfinal CommandWithParties<IOUContract.Create> command =  requireSingleCommand(tx.getCommands(), IOUContract.Create.class);final List<PublicKey> requiredSigners = command.getSigners();final List<PublicKey> expectedSigners = Arrays.asList(borrower.getOwningKey(), lender.getOwningKey());if (!(requiredSigners.containsAll(expectedSigners)))  throw new IllegalArgumentException("The borrower and lender must be signers.");  }}

Note : In contract’s verify(), you cannot use the external context, then you should check several business validations in a Flow code directly. (If it’s not accepted, throw IllegalArgumentException in FlowLogic.call().)
When your app interacts with external data (such as : a market data, a bet on game, and so forth), oracle service is also available in Corda. (See “Oracles – R3 Corda“.)

After a transaction is prepared, you should process a transaction, such as, signing a transaction (for all signers), sending to notary, and broadcasting a committed transaction to all participants. These are non-trivial procedures, but a flow can contain nested sub-flows and then you can achieve these non-trivial tasks by invoking built-in flows as nested sub-flows.

In our example, we request signing to all signers by running a built-in CollectSignaturesFlow (or CollectSignatureFlow), and send to a notary and broadcast an accepted transaction by running a built-in FinalityFlow. (I’ll explain about a notary in the next section. A notary runs verification and signs a transaction if it’s accepted.)
After a transaction is accepted by a notary, each participant’s parties (an initiator and all acceptors) store the result in a corresponding ledger. This is also achieved in a FinalityFlow.

public class ExampleFlow {  @InitiatingFlow  @StartableByRPC  public static class Initiator extends FlowLogic<Void> {private final Integer iouValue;private final Party otherParty;public Initiator(int iouValue, Party otherParty) {  this.iouValue = iouValue;  this.otherParty = otherParty;}@Suspendable@Overridepublic Void call() throws FlowException {  // Create State and Command  IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);  List<PublicKey> requiredSigners =Arrays.asList(getOurIdentity().getOwningKey(), otherParty.getOwningKey());  Command command = new Command<>(new IOUContract.Create(), requiredSigners);  // Create a builder for transaction  Party notary =getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);  TransactionBuilder txBuilder = new TransactionBuilder(notary).addOutputState(outputState, IOUContract.ID).addCommand(command);// Sign the transaction  SignedTransaction signedTx =getServiceHub().signInitialTransaction(txBuilder);  // Create a session for counterparty  FlowSession otherPartySession = initiateFlow(otherParty);  // Sub Flow 1 : Request signing to counterparty  // (built-in CollectSignaturesFlow)  SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(signedTx,Arrays.asList(otherPartySession),CollectSignaturesFlow.tracker()));  // Sub Flow 2 : Request signing to notary and then send it to counterparty  // (built-in FinalityFlow)  subFlow(new FinalityFlow(fullySignedTx, otherPartySession));  return null;}  }}

4. Flow – Acceptor Side

Finally we build an acceptor’s logic responding to an initiator’s flow. Same as the previous initiator flow, the acceptor flow has also constructors and call() override method. (See below.) A constructor is invoked by initiateFlow() in the previous initiator, and you should implement the acceptance logic in call() method.
(The following @InitiatedBy annotation indicates that this class is an acceptor’s flow against a Initiator class.)

public class ExampleFlow {  @InitiatedBy(Initiator.class)  public static class Acceptor extends FlowLogic<Void> {public Acceptor(FlowSession otherPartySession) {  ...}@Suspendable@Overridepublic Void call() throws FlowException {  ...return null;}  }}

Acceptors can also use built-in sub-flows in call() method.
As you saw in the previous example, an initiator (Initiator class) has requested 2 sub-flows – CollectSignaturesFlow and FinalityFlow, then an acceptor should respond to the corresponding these sub-flows as follows.
If it’s not accepted, throw IllegalArgumentException in call() method.

public class ExampleFlow {  @InitiatedBy(Initiator.class)  public static class Acceptor extends FlowLogic<Void> {private final FlowSession otherPartySession;public Acceptor(FlowSession otherPartySession) {  this.otherPartySession = otherPartySession;}@Suspendable@Overridepublic Void call() throws FlowException {  // Extend built-in SignTransactionFlow  class SignTxFlow extends SignTransactionFlow {private SignTxFlow(FlowSession otherPartySession) {  super(otherPartySession);}@Overrideprotected void checkTransaction(SignedTransaction stx) {  ContractState output = stx.getTx().getOutputs().get(0).getData();  if (!(output instanceof IOUState))throw new IllegalArgumentException("This must be an IOU transaction.");  IOUState iou = (IOUState) output;  if (iou.getValue() >= 100)throw new IllegalArgumentException("The IOU's value can't be too high.");}  }  // Sub flow 1 : Accept CollectSignaturesFlow  SecureHash expectedTxId = subFlow(new SignTxFlow(otherPartySession)).getId();// Sub flow 2 : Accept FinalityFlow  subFlow(new ReceiveFinalityFlow(otherPartySession, expectedTxId));return null;}  }}

5. Completed Source Code

I show you our completed source code as follows for your reference.
We’ll deploy and run this flow in the following sections.

IOUState.java

package com.example.state;import com.example.contract.IOUContract;import net.corda.core.contracts.ContractState;import net.corda.core.contracts.BelongsToContract;import net.corda.core.identity.Party;import net.corda.core.identity.AbstractParty;import java.util.Arrays;import java.util.List;@BelongsToContract(IOUContract.class)public class IOUState implements ContractState {  private final int value;  private final Party lender;  private final Party borrower;  public IOUState(int value, Party lender, Party borrower) {this.value = value;this.lender = lender;this.borrower = borrower;  }  public int getValue() {return value;  }  public Party getLender() {return lender;  }  public Party getBorrower() {return borrower;  }  @Override  public List<AbstractParty> getParticipants() {return Arrays.asList(lender, borrower);  }}

IOUContract.java

package com.example.contract;import com.example.state.IOUState;import net.corda.core.contracts.Contract;import net.corda.core.contracts.CommandData;import net.corda.core.transactions.LedgerTransaction;import net.corda.core.contracts.CommandWithParties;import net.corda.core.identity.Party;import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;import java.security.PublicKey;import java.util.Arrays;import java.util.List;public class IOUContract implements Contract {  public static final String ID = "com.example.contract.IOUContract";  // "Create" command  public static class Create implements CommandData {  }  @Override  public void verify(LedgerTransaction tx) {// IOU-specific constraintsfinal IOUState output = tx.outputsOfType(IOUState.class).get(0);final Party lender = output.getLender();final Party borrower = output.getBorrower();if (output.getValue() <= 0)  throw new IllegalArgumentException("The IOU's value must be non-negative.");if (lender.equals(borrower))  throw new IllegalArgumentException("The lender and the borrower cannot be the same entity.");// Check whether both borrower and lender is included in signersfinal CommandWithParties<IOUContract.Create> command =  requireSingleCommand(tx.getCommands(), IOUContract.Create.class);final List<PublicKey> requiredSigners = command.getSigners();final List<PublicKey> expectedSigners = Arrays.asList(borrower.getOwningKey(), lender.getOwningKey());if (!(requiredSigners.containsAll(expectedSigners)))  throw new IllegalArgumentException("The borrower and lender must be signers.");  }}

ExampleFlow.java

package com.example.flow;import com.example.contract.IOUContract;import com.example.state.IOUState;import co.paralleluniverse.fibers.Suspendable;import net.corda.core.flows.*;import net.corda.core.contracts.Command;import net.corda.core.identity.Party;import net.corda.core.transactions.SignedTransaction;import net.corda.core.transactions.TransactionBuilder;import net.corda.core.contracts.ContractState;import net.corda.core.crypto.SecureHash;import java.security.PublicKey;import java.util.Arrays;import java.util.List;public class ExampleFlow {  //////////  // Initiator Flow  //////////  @InitiatingFlow  @StartableByRPC  public static class Initiator extends FlowLogic<Void> {private final Integer iouValue;private final Party otherParty;public Initiator(int iouValue, Party otherParty) {  this.iouValue = iouValue;  this.otherParty = otherParty;}@Suspendable@Overridepublic Void call() throws FlowException {  // Create State and Command  IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);  List<PublicKey> requiredSigners =Arrays.asList(getOurIdentity().getOwningKey(), otherParty.getOwningKey());  Command command = new Command<>(new IOUContract.Create(), requiredSigners);  // Create a builder for transaction  Party notary =getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);  TransactionBuilder txBuilder = new TransactionBuilder(notary).addOutputState(outputState, IOUContract.ID).addCommand(command);  // Create a session for counterparty  FlowSession otherPartySession = initiateFlow(otherParty);  // Sub Flow 1 : Request signing to counterparty  // (built-in CollectSignaturesFlow)  SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(signedTx,Arrays.asList(otherPartySession),CollectSignaturesFlow.tracker()));  // Sub Flow 2 : Request signing to notary and then send it to counterparty  // (built-in FinalityFlow)  subFlow(new FinalityFlow(fullySignedTx, otherPartySession));  return null;}  }  //////////  // Counterparty Flow  //////////  @InitiatedBy(Initiator.class)  public static class Acceptor extends FlowLogic<Void> {private final FlowSession otherPartySession;public Acceptor(FlowSession otherPartySession) {  this.otherPartySession = otherPartySession;}@Suspendable@Overridepublic Void call() throws FlowException {  // Extend built-in SignTransactionFlow  class SignTxFlow extends SignTransactionFlow {private SignTxFlow(FlowSession otherPartySession) {  super(otherPartySession);}@Overrideprotected void checkTransaction(SignedTransaction stx) {  ContractState output = stx.getTx().getOutputs().get(0).getData();  if (!(output instanceof IOUState))throw new IllegalArgumentException("This must be an IOU transaction.");  IOUState iou = (IOUState) output;  if (iou.getValue() >= 100)throw new IllegalArgumentException("The IOU's value can't be too high.");}  }  // Sub flow 1 : Accept CollectSignaturesFlow  SecureHash expectedTxId = subFlow(new SignTxFlow(otherPartySession)).getId();// Sub flow 2 : Accept FinalityFlow  subFlow(new ReceiveFinalityFlow(otherPartySession, expectedTxId));return null;}  }}

Notary

As I mentioned above, other blockchain platforms (except for Corda) share a chain of all blocks (a single ledger) among all nodes. Thus PoW (proof-of-work) or BFT is used for making a ledger consistent with decentralized manners. (See my early post “Consensus Algorithms in Blockchain“.)
Instead, Corda transaction is based on a contract among only participant’s parties. By this design, Corda provides a different standpoint for a consensus and a conflict resolution.
In Corda, these resolution is achieved in notary cluster.

Note : In Corda, you can also implement a prototype Raft-based consensus or BFT consensus experimentally. See here for details.

When a notary has received a transaction, it resolves the following 2 types of consensus and signs a transaction if a transaction is accepted. (When rejected, a transaction is not signed.)

  • Uniqueness Consensus :
    In Corda, an input state is first registered as a “unconsumed” state. Once a transaction is signed and committed, the input state is turned into a “consumed” state.
    A notary ensures that all the input states are still unconsumed. i.e, The notary prevents double-spending and ensures the transaction’s uniqueness.
  • Validity Consensus :
    When this consensus is enabled, a notary runs contract’s validity and prevents an invalid transaction, such as, “consuming states of someone else”, “spending over some limit of amount”, so on and so forth. (See verify() function in the previous section.)
    This consensus can be enabled or disabled in notary’s node configuration (node.conf). If it’s enabled, there exists a privacy trade-off, since this requires a notary to see transaction’s states.

Even when using non-validating notary, a transaction verification can be achieved during flows, then non-validating notary doesn’t mean skipping all verifications for a transaction.
You can also proactively run the verification as follows.

public static class Initiator extends FlowLogic<Void> {  ...  @Suspendable  @Override  public Void call() throws FlowException {...// Generate a transaction builder for building a transactionTransactionBuilder txBuilder = new TransactionBuilder(notary)  .addOutputState(outputState, IOUContract.ID)  .addCommand(command);// After it's generated, run verification on a transactiontxBuilder.verify(getServiceHub());...  }}

Note : A node in a network should have a certificate signed by the network’s root CA. i.e, Corda is also a permissioned blockchain. (You cannot run a false notary in a network without approval.)

Run

Now let’s build and run our CorDapp. (We use Ubuntu 18.04 on Azure virtual machine.)

1. Setup Your Environment

First of all, we install the following required components on Ubuntu.

  • JDK (Java Development Kit)
  • Git
  • IDEA Community
# Install Oracle JDK 8sudo mkdir -p /opt/jdksudo tar -C /opt/jdk -zxvf jdk-8u241-linux-x64.tar.gzsudo update-alternatives --install /usr/bin/java java /opt/jdk/jdk1.8.0_241/bin/java 100sudo update-alternatives --install /usr/bin/javac javac /opt/jdk/jdk1.8.0_241/bin/javac 100# Install Gitsudo apt install git# Enable Snapdsudo apt install snapd# Install IDEA Communitysudo snap install intellij-idea-community --classic

2. Deploy Nodes

Corda node is a simple workspace including the required folders and files. Once a network (zone) is prepared, what you have to do for deploying a node is to put the following files (folders) in each node. (Other related files are automatically generated in a run-time. See the official document “Node folder structure” for other folders and files in node.)

  • corda.jar (file) :
    This is a Corda core run-time bytecode. (You can download from here.)
    Do not use this jar file as compile dependencies, because it’s fat.
  • node.conf (file) :
    This includes a node configuration, such as, party name, host/port, and RPC users (username and password).
  • cordapps (folder) :
    You should deploy your CorDapps (jar files) in this directory. (Later I’ll show you how to build an application.)
  • network-parameters (file) :
    This includes node directory information (addresses and ports for all nodes) built by a network bootstrapper.

Note : Here I don’t go so far about configuring a network (zone), but see “Network Bootstrapper” or “Corda Network Builder” in official document to prepare your own network (zone) for testing and development purpose.

In this post, we simply provision 4 nodes (notary, party A, party B, and party C) using a gradle plugin “Cordform” (net.corda.plugins.cordformation) with a sample configuration. Using Cordform, you can deploy local nodes without preparing a network bootstrapper for testing and development purpose. (Do not use Cordform plugin in production.)

First, in order to download both a gradle build tool and a sample configuration for Cordform, clone an official sample repository with a git command as follows.
In samples/cordapp-example/workflows-java, you can find a gradle build script (build.gradle) with Cordform configuration. (See here for how to write Cordform configuration in build.gradle.)

git clone https://github.com/corda/samples

Run a gradle wrapper to deploy nodes using above Cordform configuration.
The following command runs gradle build scripts in all sub folders, then samples/cordapp-example/workflows-java/build.gradle is also executed.

cd samples/cordapp-example./gradlew deployNodes

Now you can find 4 node’s folders (Notary, PartyA, PartyB, and PartyC) in samples/cordapp-example/workflows-java/build/nodes as follows.

3. Build (Compile) Your CorDapp

Now let’s build your source code and register your application (CorDapp) in nodes.

To build your application in Java, please download a java template as follows.

git clone https://github.com/corda/cordapp-template-java

Copy your source code for a state and a contract (IOUState.java, IOUContract.java) in cordapp-template-java/contracts/src/main/java folder, and copy flow’s source code (ExampleFlow.java) in cordapp-template-java/workflows/src/main/java folder.
Please make sure to deploy with a package folder. For instance, cordapp-template-java/contracts/src/main/java/com/example/contract/IOUContract.java, when the fully qualified class name is “com.example.contract.IOUContract“.

Now, build your source with a gradle wrapper.

cd cordapp-template-java/./gradlew build

After a compilation is succeeded, please copy contracts/build/libs/*.jar and worfklows/build/libs/*.jar on cordapps directory in each nodes. (Please remove existing sample JARs in this folder, since the class name will conflict.)

4. Start Nodes

Before running your flow (CorDapp), you should start your nodes at first.
In our case, we must start the following 4 nodes.

cd workflows-java/build/nodes/PartyAjava -jar corda.jar
cd workflows-java/build/nodes/PartyBjava -jar corda.jar
cd workflows-java/build/nodes/PartyCjava -jar corda.jar
cd workflows-java/build/nodes/Notaryjava -jar corda.jar

If you want to run a node as a daemon or service in production, please refer “Deploying a node to a server” in official documents.

Note : In our example, you can also run all nodes at once with workflows-java/build/nodes/runnodes command (which is built by a previous gradle script).

6. Run Your Flow (CorDapp)

Now you’re ready to run your CorDapp flow !

As you can see in console output on running your node (see below), each node has a corresponding RPC endpoint. (In below, a RPC endpoint is localhost:10005.) Then users or external applications (e.g, your front-end business application) to run a flow can invoke this endpoint using RPC operations over AMQP protocol.
In RPC operations, user authentication is performed using username and password. (Username/password is written in node.conf file in each node.)

Developers can create a program using Client RPC API as follows. (Here we’re starting Initiator flow.)

Client.java

package com.template;import net.corda.core.utilities.NetworkHostAndPort;import net.corda.client.rpc.CordaRPCClient;import net.corda.core.messaging.CordaRPCOps;import net.corda.core.node.NodeInfo;import net.corda.core.concurrent.CordaFuture;import net.corda.core.identity.CordaX500Name;import net.corda.core.identity.Party;import java.util.concurrent.ExecutionException;import com.example.flow.ExampleFlow.Initiator;public class Client {  public static void main(String[] args) {// Connect RPC Endpoint for PartyAfinal NetworkHostAndPort nodeAddress =  NetworkHostAndPort.parse("localhost:10005");final CordaRPCClient client = new CordaRPCClient(nodeAddress);final CordaRPCOps proxy = client.start("user1", "test").getProxy();// Run a Flow (Counterparty is PartyB)final CordaX500Name counterpartyX500Name = CordaX500Name.parse("O=PartyB,L=New York,C=US");Party counterparty = proxy.wellKnownPartyFromX500Name(counterpartyX500Name);CordaFuture<Void> flowFuture =  proxy.startFlowDynamic(Initiator.class, 50, counterparty).getReturnValue();try {  flowFuture.get();} catch (InterruptedException | ExecutionException e) {  e.printStackTrace();}// OutputSystem.out.println("Done");  }}

Note : Unlike other ledger platforms, each node is not identified by a public key, and it’s identified by a well-know identity instead.

Now copy your source code (Client.java) in cordapp-template-java/clients/src/main/java/ (make sure to deploy with package folders), compile source code, and run a generated client as follows.
A flow is started, and the result is written in a ledger and a vault.

cd cordapp-template-java/# build client./gradlew build# run client./gradlew runTemplateClient

You can also run a webserver interacting with Corda RPC and provide a generic REST API endpoint for other applications. (See samples in the official tutorial.)

Note : You can accelerate your productivity using Visual Studio Code and “VSCode-Corda” extension. (You can soon deploy nodes, run nodes, run flows, and debug with IDE.)

To test a flow in development time without compiling any code, you can also use an interactive Node Shell, which has already started in each node with above “java -jar corda.jar” command. (Node Shell also invokes a RPC endpoint internally.)
For instance, you can easily invoke a flow using Node Shell on PartyA as follows. This also starts a flow, and the result will be written in a ledger and a vault.

flow start ExampleFlow$Initiator iouValue: 50, otherParty: "O=PartyB,L=New York,C=US"

Note : You can run “flow list” to see a list of available flows.

As I mentioned above, the states in transactions are stored in a vault and you can query using Vault Query API.
For instance, when you run the following command (run vaultQuery) in Node Shell, you can see the output state on both PartyA and PartyB. However, you cannot see this state on PartyC, because PartyC isn’t a member of the transaction. (PartyC is neither of an initiator or acceptors.)

run vaultQuery contractStateType: com.example.state.IOUState

Result on Party A or Party B

Result on Party C

 

As you saw above in this post, we used a Cordform development plugin to build a network (zone) for testing and development in this post. However, in production, there’re a lot of non-trivial settings and it will take a long time to provision things by handcrafting, such as, a production database, a secure CA, so on and so forth.
With Azure Blockchain Service – Corda protocol, you can mitigate these workload (including upgrading and managing node health) and soon you can start nodes in your reliable own network or start nodes joining a existing network.
In my future post, I’ll show you how to use this managed Corda platform.

 

Reference : R3 Corda Documents (Manuals and References)

 

Categories: Uncategorized

Tagged as:

Leave a Reply