The launch of bitcoin on January 3, 2009 set trends like digital assets and digital currencies into motion, and since then, more blockchains like Ethereum and Solana have been created. Despite having different features and use cases, these blockchains have one thing in common; they were designed to operate democratically without regulators. Therefore, this model is not suitable for regulated industries, in which data must be kept confidential and shared between trusted parties. For this reason, private blockchains exist.
A private blockchain is a permissioned blockchain. A private blockchain contains entities called network operators that control the network and can configure permissions and access controls of the other nodes. To maintain privacy and trust, only the entities participating in a transaction will have knowledge about it, whereas others will not be able to access it.
A few examples of digital platforms that utilize private blockchains include Hyperledger Fabric, Ripple, and R3’s Corda. In this article, we’ll explore Corda, learning how to create CorDapps. Let’s get started!
- Introduction to Corda
- Key components of CorDapps
- Getting started with Corda
- Running our CorDapp
- Interacting with our CorDapp
Introduction to Corda
Corda is a permissioned peer-to-peer (P2P) distributed ledger technology (DLT) that facilitates the development of applications in regulated markets. With Corda, parties may freely discover and transact with each other in a single, open network while having confidence in the identification of network participants.
In the long run, Corda aspires to be a shared worldwide distributed ledger. To do so, the many solutions that use Corda software must adhere to a set of common standards and criteria. Some of these criteria, often known as end-state principles, are as follows:
- Assured identity: Parties will have confidence in the network’s participants’ identities. Identification in Corda is represented by a certificate issued by a relevant authority
- Privacy: The only people that have access to transaction information are those who are involved in the transaction and those who need to verify transaction source
- Interoperability: Corda is intended to allow numerous applications to coexist and interoperate on the same network; a defined set of contract interfaces is supplied to optimize interoperability from a wide range of providers
Key components of CorDapps
CorDapp is a shorthand for Corda distributed application. CorDapps are distributed applications that run on the Corda node. The following are the key components of a CorDapp:
States
States are made up of data. They keep track of data between transactions and are immutable, which means they can’t be changed after they’ve been formed. Any modifications must instead result in the creation of a new successor state.
Contracts
Contracts define the validation criteria that will be applied to transaction inputs and outputs. Contracts guarantee that transaction input and output states are valid and that invalid transactions are avoided. One or more contracts may exist in a CorDapp, each of which provides rules for one or more states.
Flows
Flows are the activities that your CorDapp may take on a network, and they constitute your CorDapp’s business logic. Flows allow parties to coordinate their operations without the use of a central controller.
Transactions
A transaction is a request for the ledger to be updated. It consumes current input states and outputs new ones to update the ledger. Before a transaction is accepted by the nodes, a transaction must be unique, valid, and signed by the appropriate parties.
Notary
A notary is a Corda network service that helps prevent the duplicate expenditure of network assets. It does so by guaranteeing that each transaction only includes unique input states that have not been utilized by a previous transaction. A notary also gives a transaction finality. A transaction is regarded as finished after it is signed by the notary.
Consensus
In Corda, you may establish consensus by proving a transaction that is both valid and unique. Consensus is a method that allows nodes to agree on the network’s current state. Before a proposed transaction can be entered into the ledger, both parties must agree that it is legal.
Getting started with Corda
We’ll create a CorDapp to model the issuance of tokens on the Corda blockchain. Our CorDapp will keep track of the issuer of the token, the holder, and the amount being issued.
There are four pieces of required software for CorDapp development:
- Java 8 JDK
- IntelliJ IDEA
- Git
- Gradle, any version between 5.1 and 5.6.4
Setting up
To set up our CorDapp, first, clone the Corda Java template from their GitHub repo. Open cordapp-template-java
in any IDE of your choice. I’ll use IntelliJ IDE.
Create the State
To reiterate, states are immutable and keep track of data between transactions. Our CorDapp is going to have a set of attributes that we‘ll store on our state, like issuer
, holder
, and amount
.
Navigate to /contracts/src/main/java/com/template/states
and create a new Java class called TokenState
:
package com.template.states import com.template.contracts.TokenContract; import net.corda.core.identity.AbstractParty; import net.corda.core.contracts.BelongsToContract; import net.corda.core.contracts.ContractState; import net.corda.core.identity.Party; import org.jetbrains.annotations.NotNull; import java.util.Arrays; import java.util.List; @BelongsToContract(TokenContract.class) public class TokenState implements ContractState { private Party issuer; private Party holder; private int amount; public TokenState(Party issuer, Party holder, int amount){ this.issuer = issuer; this.holder = holder; this.amount = amount; } public Party getHolder() { return holder; } public Party getIssuer() { return issuer; } public int getAmount() { return amount; } @NotNull @Override public List<AbstractParty> getParticipants() { return Arrays.asList(issuer, holder); } }
In the code above, we create a class called TokenState
that inherits from the ContractState
class. The ContractState
class tells Corda that we are implementing a state.
Next, we add the @BelongsToContract
annotation, which establishes the relationship between a state and a contract. Without this, your state doesn’t know which contract is used to verify it. Adding this annotation triggers an error in IntelliJ because we are yet to create our TokenContract
.
Then, we create three attributes, issuer
, holder
, and amount
. The issuer
and holder
attributes are given a type of Party
because they both represent entities on the node.
Next, we create three getter methods for each of the attributes. Finally, we create a getParticipants
method that defines what parties should be aware of the transaction. In our case, we only want the issuer
and the holder
to be aware of the transaction.
Create the contract
To reiterate, contracts define the rules of how states can evolve. They make certain validations before a transaction goes through successfully. Therefore, each state is linked to a contract.
Navigate to /contracts/src/main/java/com/template/contracts
and create a new Java class called TokenContract
:
package com.template.contracts import net.corda.core.contracts.CommandData; import net.corda.core.contracts.Contract; import net.corda.core.transactions.LedgerTransaction; import org.jetbrains.annotations.NotNull; import com.template.states.TokenState; public class TokenContract implements Contract { public static final String ID = "contracts.TokenContract"; @Override public void verify(@NotNull LedgerTransaction tx) { if(tx.getCommands().size() != 1) { throw new IllegalArgumentException("expects only one command: ISSUE"); } if(tx.getCommand(0).getValue() instanceof Commands.Issue){ throw new IllegalArgumentException("issue command expected"); } TokenState state = (TokenState) tx.getOutput(0); int amountIssued = state.getAmount(); if (amountIssued <= 0){ throw new IllegalArgumentException("amount must be greater than zero"); } if(! (tx.getCommand(0).getSigners().contains(state.getIssuer().getOwningKey()))){ throw new IllegalArgumentException("transaction must be signed by issuer"); } } // Used to indicate the transaction's intent. public interface Commands extends CommandData { class Issue implements Commands {} } }
In the code above, we create a class called TokenContract
that inherits from the Contract
class. The Contract
class tells Corda that we are implementing a contract.
First, we create an attribute called ID
that will identify our contract when building our transaction in testing environments. The ID
attribute is purely optional.
One of the methods given to us by the Contract
class we inherited is the verify
method, which we must override. The verify
method takes transactions as input and evaluates them against defined rules. A transaction is valid if the verify
method does not throw an exception.
With the code above, our contract performs the following checks:
- Transactions must be signed by the issuer
- Amount must be greater than zero
- Only the
issue
command can be used - Zero initial input state since it is an issuance
Finally, since we intend to issue the token to another party, we create a class called Issue
that inherits from the Command
class. The Command
class is used to indicate the type of action being performed.
Writing the initiating flow
To reiterate, flows contain the business logic of our CorDapp. The initiator flow is run by the node initiating the transaction, which would be the issuer
in our case.
Navigate to workflows/src/main/java/com/template/flows
and create a new Java class called FlowInitiator
:
package com.template.flows; import co.paralleluniverse.fibers.Suspendable; import com.bootcamp.contracts.TokenContract; import com.bootcamp.states.TokenState; import net.corda.core.flows.*; import net.corda.core.identity.CordaX500Name; import net.corda.core.identity.Party; import net.corda.core.transactions.SignedTransaction; import net.corda.core.transactions.TransactionBuilder; import net.corda.core.utilities.ProgressTracker; import net.corda.core.contracts.CommandData; import static java.util.Collections.singletonList; @InitiatingFlow @StartableByRPC public static class TokenFlowInitiator extends FlowLogic<SignedTransaction> { private final Party owner; private final int amount; public TokenFlowInitiator(Party owner, int amount) { this.owner = owner; this.amount = amount; } private final ProgressTracker progressTracker = new ProgressTracker(); @Override public ProgressTracker getProgressTracker() { return progressTracker; } @Suspendable @Override public SignedTransaction call() throws FlowException { /** Explicit selection of notary by CordaX500Name - argument can by coded in flows or parsed from config (Preferred)*/ final Party notary = getServiceHub().getNetworkMapCache().getNotary(CordaX500Name.parse("O=Notary,L=London,C=GB")); // We get a reference to our own identity. Party issuer = getOurIdentity(); /* ============================================================================ * TODO 1 - Create our TokenState to represent on-ledger tokens! * ===========================================================================*/ // We create our new TokenState. TokenState tokenState = new TokenState(issuer, owner, amount); /* ============================================================================ * TODO 3 - Build our token issuance transaction to update the ledger! * ===========================================================================*/ // We build our transaction. TransactionBuilder transactionBuilder = new TransactionBuilder.setNotary(notary).addOutputState(tokenState).addCommand(new TokenContract.Commands.Issue(), Arrays.asList(issuer.getOwningKey(), owner.getOwningKey())); /* ============================================================================ * TODO 2 - Write our TokenContract to control token issuance! * ===========================================================================*/ // We check our transaction is valid based on its contracts. transactionBuilder.verify(getServiceHub()); FlowSession session = initiateFlow(owner); // We sign the transaction with our private key, making it immutable. SignedTransaction signedTransaction = getServiceHub().signInitialTransaction(transactionBuilder); // The counterparty signs the transaction SignedTransaction fullySignedTransaction = subFlow(new CollectSignaturesFlow(signedTransaction, singletonList(session))); // We get the transaction notarised and recorded automatically by the platform. return subFlow(new FinalityFlow(fullySignedTransaction, singletonList(session))); } }
In the code above, we create a class called TokenFlowInitiator
that inherits from the FlowLogic
class. The FlowLogic
class tells Corda that we are creating a Flow.
Then, we add two annotations, @InitiatingFlow
and @StartableByRPC
. The @InitiatingFlow
annotation indicates that this flow is the initiating flow. On the other hand, the @StartableByRPC
annotation allows RPC to start the flow.
Next, we create a variable called progressTracker
that checkpoints each stage of the flow and outputs the specified messages when each checkpoint is reached in the code.
Next, we create the call
method, which Corda calls when the flow is started. Inside the call
method, we first create a notary and store it in a variable called notary
. Since we are dealing with multiple parties, we need a notary service to reach consensus between the parties.
We get our identity and store it in a variable called issuer
. Next, we create our token by creating a new instance of TokenState
and pass the issuer
, owner
, and amount
as arguments.
Then, we build our transaction proposal by creating a new instance of TransactionBuilder
. We chain different methods to set our notary, add commands, and sign the transaction.
Finally, we start a FlowSession
with the counterparty using the InitiateFlow
method. This process enables us to send the state to the counterparty. We then call the CollectSignaturesFlow
subflow to collect signatures.
Writing the responder flow
The responder flow is run by the counterparty. It receives and records the transaction, then responds to the issuer’s flow by sending back an acknowledgement if the transaction was successful.
Navigate to workflows/src/main/java/com/template/flows
and create a new Java class called TokenFlowResponder
:
package com.template.flows; import co.paralleluniverse.fibers.Suspendable; import net.corda.core.flows.*; import net.corda.core.identity.Party; import net.corda.core.transactions.SignedTransaction; @InitiatedBy(TokenFlowInitiator.class) public static class TokenFlowResponder extends FlowLogic<Void>{ //private variable private FlowSession counterpartySession; //Constructor public TokenFlowResponder(FlowSession counterpartySession) { this.counterpartySession = counterpartySession; } @Suspendable @Override public Void call() throws FlowException { SignedTransaction signedTransaction = subFlow(new SignTransactionFlow(counterpartySession) {}); //Stored the transaction into data base. subFlow(new ReceiveFinalityFlow(counterpartySession, signedTransaction.getId())); return null; } }
In the code above, we create a class called TokenFlowResponder
that inherits from the FlowLogic
class. We then add the @InitiatedBy
annotation and pass the TokenFlowInitiator
class as an argument, telling Corda who initiated the flow.
Then, we create the call
method with a subFlow
that will verify the transaction and the signatures it received from the flow initiator. Optionally, we can conventionally create a new method called checkTransaction
to perform a series of tests just to be safe.
Running our CorDapp
To start our CorDapp, we have to first deploy it by navigating to the root of our project and running the following commands:
#Mac or Linux ./gradlew clean deployNodes #Windows gradlew.bat clean deployNodes
If our CorDapp builds successfully, it generates three nodes with the CorDapp installed on them. These can be found in the build/nodes
folder.
Launch the sample CorDapp
To start the nodes and our CorDapp, run the following command from our root directory:
#Mac or Linux ./build/nodes/runnodes #Windows .\build\nodes\runnodes.bat
The code above will start a new terminal window for each node. Give each terminal some time to start, and you’ll get a welcome message on the terminal once the node is ready.
Interacting with our CorDapp
To check if our CorDapp worked successfully, we can try to start a flow by running the following command:
flow start TokenFlowInitiator owner: PartyB, amount: 100
If it is successful, you’ll get a confirmation message.
Conclusion
The blockchain is a game-changing technology, and regulated businesses are not left out of this change thanks to systems that use private blockchains. Blockchain projects like Corda give businesses the flexibility they need while still keeping data private. In this article, we explored getting stared with Corda, learning how to make CorDapps.
I hope you enjoyed this tutorial, and feel free to leave a comment if you have any questions.
The post How to write DApps on Corda appeared first on LogRocket Blog.
from LogRocket Blog https://ift.tt/FphZeOI
via Read more