mirror of
https://github.com/corda/corda.git
synced 2024-12-20 21:43:14 +00:00
Updates tutorial to reflect new template structure. Clean-up. (#4216)
* Initial improvements. * Updates tutorials. * Missing imports. * Addresses review feedback.
This commit is contained in:
parent
e2a351cb70
commit
f3b09988a9
@ -2,16 +2,19 @@ package net.corda.docs.java.tutorial.helloworld;
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable;
|
||||
import com.template.TemplateContract;
|
||||
import net.corda.core.flows.*;
|
||||
import net.corda.core.flows.FlowException;
|
||||
import net.corda.core.flows.FlowLogic;
|
||||
import net.corda.core.flows.InitiatingFlow;
|
||||
import net.corda.core.flows.StartableByRPC;
|
||||
import net.corda.core.utilities.ProgressTracker;
|
||||
|
||||
// DOCSTART 01
|
||||
// Add these imports:
|
||||
import net.corda.core.contracts.Command;
|
||||
import net.corda.core.contracts.CommandData;
|
||||
import net.corda.core.flows.FinalityFlow;
|
||||
import net.corda.core.identity.Party;
|
||||
import net.corda.core.transactions.SignedTransaction;
|
||||
import net.corda.core.transactions.TransactionBuilder;
|
||||
import net.corda.core.utilities.ProgressTracker;
|
||||
|
||||
// Replace Initiator's definition with:
|
||||
@InitiatingFlow
|
||||
@ -46,13 +49,12 @@ public class IOUFlow extends FlowLogic<Void> {
|
||||
|
||||
// We create the transaction components.
|
||||
IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
|
||||
CommandData cmdType = new TemplateContract.Commands.Action();
|
||||
Command cmd = new Command<>(cmdType, getOurIdentity().getOwningKey());
|
||||
Command command = new Command<>(new TemplateContract.Commands.Action(), getOurIdentity().getOwningKey());
|
||||
|
||||
// We create a transaction builder and add the components.
|
||||
TransactionBuilder txBuilder = new TransactionBuilder(notary)
|
||||
.addOutputState(outputState, TemplateContract.ID)
|
||||
.addCommand(cmd);
|
||||
.addCommand(command);
|
||||
|
||||
// Signing the transaction.
|
||||
SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
|
||||
|
@ -2,11 +2,11 @@ package net.corda.docs.java.tutorial.helloworld;
|
||||
|
||||
import net.corda.core.contracts.ContractState;
|
||||
import net.corda.core.identity.AbstractParty;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
// DOCSTART 01
|
||||
// Add these imports:
|
||||
import com.google.common.collect.ImmutableList;
|
||||
// Add this import:
|
||||
import net.corda.core.identity.Party;
|
||||
|
||||
// Replace TemplateState's definition with:
|
||||
@ -35,7 +35,7 @@ public class IOUState implements ContractState {
|
||||
|
||||
@Override
|
||||
public List<AbstractParty> getParticipants() {
|
||||
return ImmutableList.of(lender, borrower);
|
||||
return Arrays.asList(lender, borrower);
|
||||
}
|
||||
}
|
||||
// DOCEND 01
|
@ -6,15 +6,14 @@ import net.corda.core.transactions.LedgerTransaction;
|
||||
|
||||
// DOCSTART 01
|
||||
// Add these imports:
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.corda.core.contracts.CommandWithParties;
|
||||
import net.corda.core.identity.Party;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
|
||||
import static net.corda.core.contracts.ContractsDSL.requireThat;
|
||||
|
||||
// Replace TemplateContract's definition with:
|
||||
public class IOUContract implements Contract {
|
||||
@ -28,26 +27,29 @@ public class IOUContract implements Contract {
|
||||
public void verify(LedgerTransaction tx) {
|
||||
final CommandWithParties<IOUContract.Create> command = requireSingleCommand(tx.getCommands(), IOUContract.Create.class);
|
||||
|
||||
requireThat(check -> {
|
||||
// Constraints on the shape of the transaction.
|
||||
check.using("No inputs should be consumed when issuing an IOU.", tx.getInputs().isEmpty());
|
||||
check.using("There should be one output state of type IOUState.", tx.getOutputs().size() == 1);
|
||||
// Constraints on the shape of the transaction.
|
||||
if (!tx.getInputs().isEmpty())
|
||||
throw new IllegalArgumentException("No inputs should be consumed when issuing an IOU.");
|
||||
if (!(tx.getOutputs().size() == 1))
|
||||
throw new IllegalArgumentException("There should be one output state of type IOUState.");
|
||||
|
||||
// IOU-specific constraints.
|
||||
final IOUState out = tx.outputsOfType(IOUState.class).get(0);
|
||||
final Party lender = out.getLender();
|
||||
final Party borrower = out.getBorrower();
|
||||
check.using("The IOU's value must be non-negative.", out.getValue() > 0);
|
||||
check.using("The lender and the borrower cannot be the same entity.", lender != borrower);
|
||||
// IOU-specific constraints.
|
||||
final 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.");
|
||||
|
||||
// Constraints on the signers.
|
||||
final List<PublicKey> signers = command.getSigners();
|
||||
check.using("There must be two signers.", signers.size() == 2);
|
||||
check.using("The borrower and lender must be signers.", signers.containsAll(
|
||||
ImmutableList.of(borrower.getOwningKey(), lender.getOwningKey())));
|
||||
// Constraints on the signers.
|
||||
final List<PublicKey> requiredSigners = command.getSigners();
|
||||
final List<PublicKey> expectedSigners = Arrays.asList(borrower.getOwningKey(), lender.getOwningKey());
|
||||
if (requiredSigners.size() != 2)
|
||||
throw new IllegalArgumentException("There must be two signers.");
|
||||
if (!(requiredSigners.containsAll(expectedSigners)))
|
||||
throw new IllegalArgumentException("The borrower and lender must be signers.");
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
// DOCEND 01
|
@ -2,9 +2,7 @@ package net.corda.docs.java.tutorial.twoparty;
|
||||
|
||||
// DOCSTART 01
|
||||
import co.paralleluniverse.fibers.Suspendable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.corda.core.contracts.Command;
|
||||
import net.corda.core.contracts.StateAndContract;
|
||||
import net.corda.core.flows.*;
|
||||
import net.corda.core.identity.Party;
|
||||
import net.corda.core.transactions.SignedTransaction;
|
||||
@ -12,6 +10,7 @@ import net.corda.core.transactions.TransactionBuilder;
|
||||
import net.corda.core.utilities.ProgressTracker;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
// DOCEND 01
|
||||
|
||||
@ -42,22 +41,19 @@ public class IOUFlow extends FlowLogic<Void> {
|
||||
@Suspendable
|
||||
@Override
|
||||
public Void call() throws FlowException {
|
||||
// DOCSTART 02
|
||||
// We retrieve the notary identity from the network map.
|
||||
Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
|
||||
|
||||
// DOCSTART 02
|
||||
// We create a transaction builder.
|
||||
TransactionBuilder txBuilder = new TransactionBuilder();
|
||||
txBuilder.setNotary(notary);
|
||||
|
||||
// We create the transaction components.
|
||||
IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
|
||||
StateAndContract outputContractAndState = new StateAndContract(outputState, IOUContract.ID);
|
||||
List<PublicKey> requiredSigners = ImmutableList.of(getOurIdentity().getOwningKey(), otherParty.getOwningKey());
|
||||
Command cmd = new Command<>(new IOUContract.Create(), requiredSigners);
|
||||
List<PublicKey> requiredSigners = Arrays.asList(getOurIdentity().getOwningKey(), otherParty.getOwningKey());
|
||||
Command command = new Command<>(new IOUContract.Create(), requiredSigners);
|
||||
|
||||
// We add the items to the builder.
|
||||
txBuilder.withItems(outputContractAndState, cmd);
|
||||
// We create a transaction builder and add the components.
|
||||
TransactionBuilder txBuilder = new TransactionBuilder(notary)
|
||||
.addOutputState(outputState, IOUContract.ID)
|
||||
.addCommand(command);
|
||||
|
||||
// Verifying the transaction.
|
||||
txBuilder.verify(getServiceHub());
|
||||
@ -70,7 +66,7 @@ public class IOUFlow extends FlowLogic<Void> {
|
||||
|
||||
// Obtaining the counterparty's signature.
|
||||
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
|
||||
signedTx, ImmutableList.of(otherPartySession), CollectSignaturesFlow.tracker()));
|
||||
signedTx, Arrays.asList(otherPartySession), CollectSignaturesFlow.tracker()));
|
||||
|
||||
// Finalising the transaction.
|
||||
subFlow(new FinalityFlow(fullySignedTx));
|
||||
|
@ -1,10 +1,10 @@
|
||||
package net.corda.docs.java.tutorial.twoparty;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.corda.core.contracts.ContractState;
|
||||
import net.corda.core.identity.AbstractParty;
|
||||
import net.corda.core.identity.Party;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class IOUState implements ContractState {
|
||||
@ -32,6 +32,6 @@ public class IOUState implements ContractState {
|
||||
|
||||
@Override
|
||||
public List<AbstractParty> getParticipants() {
|
||||
return ImmutableList.of(lender, borrower);
|
||||
return Arrays.asList(lender, borrower);
|
||||
}
|
||||
}
|
@ -8,13 +8,13 @@ import net.corda.core.flows.FinalityFlow
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.InitiatingFlow
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
|
||||
// DOCSTART 01
|
||||
// Add these imports:
|
||||
import net.corda.core.contracts.Command
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
|
||||
// Replace Initiator's definition with:
|
||||
@InitiatingFlow
|
||||
@ -33,12 +33,12 @@ class IOUFlow(val iouValue: Int,
|
||||
|
||||
// We create the transaction components.
|
||||
val outputState = IOUState(iouValue, ourIdentity, otherParty)
|
||||
val cmd = Command(TemplateContract.Commands.Action(), ourIdentity.owningKey)
|
||||
val command = Command(TemplateContract.Commands.Action(), ourIdentity.owningKey)
|
||||
|
||||
// We create a transaction builder and add the components.
|
||||
val txBuilder = TransactionBuilder(notary = notary)
|
||||
.addOutputState(outputState, TemplateContract.ID)
|
||||
.addCommand(cmd)
|
||||
.addCommand(command)
|
||||
|
||||
// We sign the transaction.
|
||||
val signedTx = serviceHub.signInitialTransaction(txBuilder)
|
||||
|
@ -5,7 +5,7 @@ package net.corda.docs.kotlin.tutorial.helloworld
|
||||
import net.corda.core.contracts.ContractState
|
||||
|
||||
// DOCSTART 01
|
||||
// Add these imports:
|
||||
// Add this import:
|
||||
import net.corda.core.identity.Party
|
||||
|
||||
// Replace TemplateState's definition with:
|
||||
|
@ -5,7 +5,7 @@ import net.corda.core.contracts.Contract
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
|
||||
// DOCSTART 01
|
||||
// Add these imports:
|
||||
// Add this import:
|
||||
import net.corda.core.contracts.*
|
||||
|
||||
class IOUContract : Contract {
|
||||
@ -25,14 +25,14 @@ class IOUContract : Contract {
|
||||
"There should be one output state of type IOUState." using (tx.outputs.size == 1)
|
||||
|
||||
// IOU-specific constraints.
|
||||
val out = tx.outputsOfType<IOUState>().single()
|
||||
"The IOU's value must be non-negative." using (out.value > 0)
|
||||
"The lender and the borrower cannot be the same entity." using (out.lender != out.borrower)
|
||||
val output = tx.outputsOfType<IOUState>().single()
|
||||
"The IOU's value must be non-negative." using (output.value > 0)
|
||||
"The lender and the borrower cannot be the same entity." using (output.lender != output.borrower)
|
||||
|
||||
// Constraints on the signers.
|
||||
val expectedSigners = listOf(output.borrower.owningKey, output.lender.owningKey)
|
||||
"There must be two signers." using (command.signers.toSet().size == 2)
|
||||
"The borrower and lender must be signers." using (command.signers.containsAll(listOf(
|
||||
out.borrower.owningKey, out.lender.owningKey)))
|
||||
"The borrower and lender must be signers." using (command.signers.containsAll(expectedSigners))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ package net.corda.docs.kotlin.tutorial.twoparty
|
||||
// DOCSTART 01
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.contracts.Command
|
||||
import net.corda.core.contracts.StateAndContract
|
||||
import net.corda.core.flows.CollectSignaturesFlow
|
||||
import net.corda.core.flows.FinalityFlow
|
||||
import net.corda.core.flows.FlowLogic
|
||||
@ -27,20 +26,18 @@ class IOUFlow(val iouValue: Int,
|
||||
/** The flow logic is encapsulated within the call() method. */
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
// DOCSTART 02
|
||||
// We retrieve the notary identity from the network map.
|
||||
val notary = serviceHub.networkMapCache.notaryIdentities[0]
|
||||
|
||||
// DOCSTART 02
|
||||
// We create a transaction builder.
|
||||
val txBuilder = TransactionBuilder(notary = notary)
|
||||
|
||||
// We create the transaction components.
|
||||
val outputState = IOUState(iouValue, ourIdentity, otherParty)
|
||||
val outputContractAndState = StateAndContract(outputState, IOUContract.ID)
|
||||
val cmd = Command(IOUContract.Create(), listOf(ourIdentity.owningKey, otherParty.owningKey))
|
||||
val command = Command(IOUContract.Create(), listOf(ourIdentity.owningKey, otherParty.owningKey))
|
||||
|
||||
// We add the items to the builder.
|
||||
txBuilder.withItems(outputContractAndState, cmd)
|
||||
// We create a transaction builder and add the components.
|
||||
val txBuilder = TransactionBuilder(notary = notary)
|
||||
.addOutputState(outputState, IOUContract.ID)
|
||||
.addCommand(command)
|
||||
|
||||
// Verifying the transaction.
|
||||
txBuilder.verify(serviceHub)
|
||||
|
@ -3,10 +3,7 @@
|
||||
package net.corda.docs.kotlin.tutorial.twoparty
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.flows.InitiatedBy
|
||||
import net.corda.core.flows.SignTransactionFlow
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.docs.kotlin.tutorial.helloworld.IOUFlow
|
||||
import net.corda.docs.kotlin.tutorial.helloworld.IOUState
|
||||
|
||||
|
@ -40,8 +40,7 @@ FlowLogic
|
||||
---------
|
||||
All flows must subclass ``FlowLogic``. You then define the steps taken by the flow by overriding ``FlowLogic.call``.
|
||||
|
||||
Let's define our ``IOUFlow`` in either ``Initiator.java`` or ``Flows.kt``. Delete the two existing flows in the
|
||||
template (``Initiator`` and ``Responder``), and replace them with the following:
|
||||
Let's define our ``IOUFlow``. Delete the existing ``Responder`` flow. Then replace the definition of ``Initiator`` with the following:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
|
@ -13,29 +13,38 @@ By this point, :doc:`your dev environment should be set up <getting-set-up>`, yo
|
||||
:doc:`your first CorDapp <tutorial-cordapp>`, and you're familiar with Corda's :doc:`key concepts <key-concepts>`. What
|
||||
comes next?
|
||||
|
||||
If you're a developer, the next step is to write your own CorDapp. CorDapps are plugins that are installed on one or
|
||||
more Corda nodes, and give the nodes' owners the ability to make their node conduct some new process - anything from
|
||||
If you're a developer, the next step is to write your own CorDapp. CorDapps are applications that are installed on one or
|
||||
more Corda nodes, and that allow the node's operator to instruct their node to perform some new process - anything from
|
||||
issuing a debt instrument to making a restaurant booking.
|
||||
|
||||
Our use-case
|
||||
------------
|
||||
Our CorDapp will model IOUs on-ledger. An IOU – short for “I O(we) (yo)U” – records the fact that one person owes
|
||||
another person a given amount of money. Clearly this is sensitive information that we'd only want to communicate on
|
||||
a need-to-know basis between the lender and the borrower. Fortunately, this is one of the areas where Corda excels.
|
||||
Corda makes it easy to allow a small set of parties to agree on a shared fact without needing to share this fact with
|
||||
everyone else on the network, as is the norm in blockchain platforms.
|
||||
We will write a CorDapp to model IOUs on the blockchain. Each IOU – short for “I O(we) (yo)U” – will record the fact that one node owes
|
||||
another node a certain amount. This simple CorDapp will showcase several key benefits of Corda as a blockchain platform:
|
||||
|
||||
To serve any useful function, our CorDapp will need at least two things:
|
||||
* **Privacy** - Since IOUs represent sensitive information, we will be taking advantage of Corda's ability to only share
|
||||
ledger updates with other nodes on a need-to-know basis, instead of using a gossip protocol to share this information with every node on
|
||||
the network as you would with a traditional blockchain platform
|
||||
|
||||
* **States**, the shared facts that Corda nodes reach consensus over and are then stored on the ledger
|
||||
* **Flows**, which encapsulate the procedure for carrying out a specific ledger update
|
||||
* **Well-known identities** - Each Corda node has a well-known identity on the network. This allows us to write code in terms of real
|
||||
identities, rather than anonymous public keys
|
||||
|
||||
Our IOU CorDapp is no exception. It will define both a state and a flow:
|
||||
* **Re-use of existing, proven technologies** - We will be writing our CorDapp using standard Java. It will run on a Corda node, which is
|
||||
simply a Java process and runs on a regular Java machine (e.g. on your local machine or in the cloud). The nodes will store their data in
|
||||
a standard SQL database
|
||||
|
||||
CorDapps usually define at least three things:
|
||||
|
||||
* **States** - the (possibly shared) facts that are written to the ledger
|
||||
* **Flows** - the procedures for carrying out specific ledger updates
|
||||
* **Contracts** - the constraints governing how states of a given type can evolve over time
|
||||
|
||||
Our IOU CorDapp is no exception. It will define the following components:
|
||||
|
||||
The IOUState
|
||||
^^^^^^^^^^^^
|
||||
Our state will be the ``IOUState``. It will store the value of the IOU, as well as the identities of the lender and the
|
||||
borrower. We can visualize ``IOUState`` as follows:
|
||||
Our state will be the ``IOUState``, representing an IOU. It will contain the IOU's value, its lender and its borrower. We can visualize
|
||||
``IOUState`` as follows:
|
||||
|
||||
.. image:: resources/tutorial-state.png
|
||||
:scale: 25%
|
||||
@ -43,20 +52,20 @@ borrower. We can visualize ``IOUState`` as follows:
|
||||
|
||||
The IOUFlow
|
||||
^^^^^^^^^^^
|
||||
Our flow will be the ``IOUFlow``. This flow will completely automate the process of issuing a new IOU onto a ledger. It
|
||||
is composed of the following steps:
|
||||
Our flow will be the ``IOUFlow``. This flow will completely automate the process of issuing a new IOU onto a ledger. It has the following
|
||||
steps:
|
||||
|
||||
.. image:: resources/simple-tutorial-flow.png
|
||||
:scale: 25%
|
||||
:align: center
|
||||
|
||||
In traditional distributed ledger systems, where all data is broadcast to every network participant, you don’t need to
|
||||
think about data flows – you simply package up your ledger update and send it to everyone else on the network. But in
|
||||
Corda, where privacy is a core focus, flows allow us to carefully control who sees what during the process of
|
||||
agreeing a ledger update.
|
||||
The IOUContract
|
||||
^^^^^^^^^^^^^^^
|
||||
For this tutorial, we will use the default ``TemplateContract``. We will update it to create a fully-fledged ``IOUContract`` in the next
|
||||
tutorial.
|
||||
|
||||
Progress so far
|
||||
---------------
|
||||
We've sketched out a simple CorDapp that will allow nodes to confidentially issue new IOUs onto a ledger.
|
||||
We've designed a simple CorDapp that will allow nodes to agree new IOUs on the blockchain.
|
||||
|
||||
Next, we'll be taking a look at the template project we'll be using as the basis for our CorDapp.
|
||||
Next, we'll take a look at the template project we'll be using as the basis for our CorDapp.
|
||||
|
@ -107,11 +107,9 @@ commands.
|
||||
|
||||
We want to create an IOU of 99 with PartyB. We start the ``IOUFlow`` by typing:
|
||||
|
||||
.. container:: codeset
|
||||
.. code-block:: bash
|
||||
|
||||
.. code-block:: kotlin
|
||||
|
||||
start IOUFlow iouValue: 99, otherParty: "O=PartyB,L=New York,C=US"
|
||||
start IOUFlow iouValue: 99, otherParty: "O=PartyB,L=New York,C=US"
|
||||
|
||||
This single command will cause PartyA and PartyB to automatically agree an IOU. This is one of the great advantages of
|
||||
the flow framework - it allows you to reduce complex negotiation and update processes into a single function call.
|
||||
@ -122,7 +120,7 @@ We can check the contents of each node's vault by running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
run vaultQuery contractStateType: com.template.IOUState
|
||||
run vaultQuery contractStateType: com.template.IOUState
|
||||
|
||||
The vaults of PartyA and PartyB should both display the following output:
|
||||
|
||||
@ -162,12 +160,27 @@ The vaults of PartyA and PartyB should both display the following output:
|
||||
|
||||
This is the transaction issuing our ``IOUState`` onto a ledger.
|
||||
|
||||
However, if we run the same command on the other node (the notary), we will see the following:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
{
|
||||
"states" : [ ],
|
||||
"statesMetadata" : [ ],
|
||||
"totalStatesAvailable" : -1,
|
||||
"stateTypes" : "UNCONSUMED",
|
||||
"otherResults" : [ ]
|
||||
}
|
||||
|
||||
This is the result of Corda's privacy model. Because the notary was not involved in the transaction and had no need to see the data, the
|
||||
transaction was not distributed to them.
|
||||
|
||||
Conclusion
|
||||
----------
|
||||
We have written a simple CorDapp that allows IOUs to be issued onto the ledger. Our CorDapp is made up of two key
|
||||
parts:
|
||||
|
||||
* The ``IOUState``, representing IOUs on the ledger
|
||||
* The ``IOUState``, representing IOUs on the blockchain
|
||||
* The ``IOUFlow``, orchestrating the process of agreeing the creation of an IOU on-ledger
|
||||
|
||||
After completing this tutorial, your CorDapp should look like this:
|
||||
|
@ -7,7 +7,7 @@
|
||||
Writing the state
|
||||
=================
|
||||
|
||||
In Corda, shared facts on the ledger are represented as states. Our first task will be to define a new state type to
|
||||
In Corda, shared facts on the blockchain are represented as states. Our first task will be to define a new state type to
|
||||
represent an IOU.
|
||||
|
||||
The ContractState interface
|
||||
@ -28,7 +28,7 @@ We can see that the ``ContractState`` interface has a single field, ``participan
|
||||
entities for which this state is relevant.
|
||||
|
||||
Beyond this, our state is free to define any fields, methods, helpers or inner classes it requires to accurately
|
||||
represent a given type of shared fact on the ledger.
|
||||
represent a given type of shared fact on the blockchain.
|
||||
|
||||
.. note::
|
||||
|
||||
@ -46,7 +46,7 @@ represent a given type of shared fact on the ledger.
|
||||
|
||||
Modelling IOUs
|
||||
--------------
|
||||
How should we define the ``IOUState`` representing IOUs on the ledger? Beyond implementing the ``ContractState``
|
||||
How should we define the ``IOUState`` representing IOUs on the blockchain? Beyond implementing the ``ContractState``
|
||||
interface, our ``IOUState`` will also need properties to track the relevant features of the IOU:
|
||||
|
||||
* The value of the IOU
|
||||
@ -99,7 +99,7 @@ Corda are simply classes that implement the ``ContractState`` interface. They ca
|
||||
methods you like.
|
||||
|
||||
All that's left to do is write the ``IOUFlow`` that will allow a node to orchestrate the creation of a new ``IOUState``
|
||||
on the ledger, while only sharing information on a need-to-know basis.
|
||||
on the blockchain, while only sharing information on a need-to-know basis.
|
||||
|
||||
What about the contract?
|
||||
------------------------
|
||||
|
@ -7,41 +7,39 @@
|
||||
The CorDapp Template
|
||||
====================
|
||||
|
||||
When writing a new CorDapp, you’ll generally want to base it on the standard templates:
|
||||
When writing a new CorDapp, you’ll generally want to start from one of the standard templates:
|
||||
|
||||
* The `Java Cordapp Template <https://github.com/corda/cordapp-template-java>`_
|
||||
* The `Kotlin Cordapp Template <https://github.com/corda/cordapp-template-kotlin>`_
|
||||
|
||||
The Cordapp templates provide the required boilerplate for developing a CorDapp, and allow you to quickly deploy your
|
||||
CorDapp onto a local test network of dummy nodes to test its functionality.
|
||||
The Cordapp templates provide the boilerplate for developing a new CorDapp. CorDapps can be written in either Java or Kotlin. We will be
|
||||
providing the code in both languages throughout this tutorial.
|
||||
|
||||
CorDapps can be written in both Java and Kotlin, and will be providing the code in both languages in this tutorial.
|
||||
|
||||
Note that there's no need to download and install Corda itself. Corda's required libraries will be downloaded
|
||||
automatically from an online Maven repository.
|
||||
Note that there's no need to download and install Corda itself. The required libraries are automatically downloaded from an online Maven
|
||||
repository and cached locally.
|
||||
|
||||
Downloading the template
|
||||
------------------------
|
||||
To download the template, open a terminal window in the directory where you want to download the CorDapp template, and
|
||||
run the following command:
|
||||
Open a terminal window in the directory where you want to download the CorDapp template, and run the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
.. container:: codeset
|
||||
|
||||
git clone https://github.com/corda/cordapp-template-java.git ; cd cordapp-template-java
|
||||
.. code-block:: java
|
||||
|
||||
*or*
|
||||
git clone https://github.com/corda/cordapp-template-java.git ; cd cordapp-template-java
|
||||
|
||||
git clone https://github.com/corda/cordapp-template-kotlin.git ; cd cordapp-template-kotlin
|
||||
.. code-block:: kotlin
|
||||
|
||||
git clone https://github.com/corda/cordapp-template-kotlin.git ; cd cordapp-template-kotlin
|
||||
|
||||
Opening the template in IntelliJ
|
||||
--------------------------------
|
||||
|
||||
Once the template is download, open it in IntelliJ by following the instructions here:
|
||||
https://docs.corda.net/tutorial-cordapp.html#opening-the-example-cordapp-in-intellij.
|
||||
|
||||
Template structure
|
||||
------------------
|
||||
The template has a number of files, but we can ignore most of them. We will only be modifying the following files:
|
||||
For this tutorial, we will only be modifying the following files:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
|
@ -154,7 +154,7 @@ Transaction constraints
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
We also want our transaction to have no inputs and only a single output - an issuance transaction.
|
||||
|
||||
To impose this and the subsequent constraints, we are using Corda's built-in ``requireThat`` block. ``requireThat``
|
||||
In Kotlin, we impose these and the subsequent constraints using Corda's built-in ``requireThat`` block. ``requireThat``
|
||||
provides a terse way to write the following:
|
||||
|
||||
* If the condition on the right-hand side doesn't evaluate to true...
|
||||
@ -162,6 +162,8 @@ provides a terse way to write the following:
|
||||
|
||||
As before, the act of throwing this exception causes the transaction to be considered invalid.
|
||||
|
||||
In Java, we simply throw an ``IllegalArgumentException`` manually instead.
|
||||
|
||||
IOU constraints
|
||||
~~~~~~~~~~~~~~~
|
||||
We want to impose two constraints on the ``IOUState`` itself:
|
||||
@ -169,9 +171,7 @@ We want to impose two constraints on the ``IOUState`` itself:
|
||||
* Its value must be non-negative
|
||||
* The lender and the borrower cannot be the same entity
|
||||
|
||||
We impose these constraints in the same ``requireThat`` block as before.
|
||||
|
||||
You can see that we're not restricted to only writing constraints in the ``requireThat`` block. We can also write
|
||||
You can see that we're not restricted to only writing constraints inside ``verify``. We can also write
|
||||
other statements - in this case, extracting the transaction's single ``IOUState`` and assigning it to a variable.
|
||||
|
||||
Signer constraints
|
||||
@ -180,7 +180,7 @@ Finally, we require both the lender and the borrower to be required signers on t
|
||||
required signers is equal to the union of all the signers listed on the commands. We therefore extract the signers from
|
||||
the ``Create`` command we retrieved earlier.
|
||||
|
||||
This is an absolutely essential constraint - it ensures that no ``IOUState`` can ever be created on the ledger without
|
||||
This is an absolutely essential constraint - it ensures that no ``IOUState`` can ever be created on the blockchain without
|
||||
the express agreement of both the lender and borrower nodes.
|
||||
|
||||
Progress so far
|
||||
|
@ -31,8 +31,7 @@ In ``IOUFlow.java``/``Flows.kt``, change the imports block to the following:
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
And update ``IOUFlow.call`` by changing the code following the retrieval of the notary's identity from the network as
|
||||
follows:
|
||||
And update ``IOUFlow.call`` to the following:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
@ -136,7 +135,7 @@ defined in ``IOUContract``. We can now re-run our updated CorDapp, using the
|
||||
:doc:`same instructions as before <hello-world-running>`.
|
||||
|
||||
Our CorDapp now imposes restrictions on the issuance of IOUs. Most importantly, IOU issuance now requires agreement
|
||||
from both the lender and the borrower before an IOU can be created on the ledger. This prevents either the lender or
|
||||
from both the lender and the borrower before an IOU can be created on the blockchain. This prevents either the lender or
|
||||
the borrower from unilaterally updating the ledger in a way that only benefits themselves.
|
||||
|
||||
After completing this tutorial, your CorDapp should look like this:
|
||||
|
@ -6,10 +6,10 @@ Hello, World! Pt.2 - Contract constraints
|
||||
In the Hello, World tutorial, we built a CorDapp allowing us to model IOUs on ledger. Our CorDapp was made up of two
|
||||
elements:
|
||||
|
||||
* An ``IOUState``, representing IOUs on the ledger
|
||||
* An ``IOUState``, representing IOUs on the blockchain
|
||||
* An ``IOUFlow``, orchestrating the process of agreeing the creation of an IOU on-ledger
|
||||
|
||||
However, our CorDapp did not impose any constraints on the evolution of IOUs on the ledger over time. Anyone was free
|
||||
However, our CorDapp did not impose any constraints on the evolution of IOUs on the blockchain over time. Anyone was free
|
||||
to create IOUs of any value, between any party.
|
||||
|
||||
In this tutorial, we'll write a contract to imposes rules on how an ``IOUState`` can change over time. In turn, this
|
||||
|
Loading…
Reference in New Issue
Block a user