Updates tutorial to reflect new template structure. Clean-up. (#4216)

* Initial improvements.

* Updates tutorials.

* Missing imports.

* Addresses review feedback.
This commit is contained in:
Joel Dudley 2018-11-13 11:50:24 +00:00 committed by GitHub
parent e2a351cb70
commit f3b09988a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 136 additions and 124 deletions

View File

@ -2,16 +2,19 @@ package net.corda.docs.java.tutorial.helloworld;
import co.paralleluniverse.fibers.Suspendable; import co.paralleluniverse.fibers.Suspendable;
import com.template.TemplateContract; 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 // DOCSTART 01
// Add these imports: // Add these imports:
import net.corda.core.contracts.Command; 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.identity.Party;
import net.corda.core.transactions.SignedTransaction; import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder; import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker;
// Replace Initiator's definition with: // Replace Initiator's definition with:
@InitiatingFlow @InitiatingFlow
@ -46,13 +49,12 @@ public class IOUFlow extends FlowLogic<Void> {
// We create the transaction components. // We create the transaction components.
IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty); IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
CommandData cmdType = new TemplateContract.Commands.Action(); Command command = new Command<>(new TemplateContract.Commands.Action(), getOurIdentity().getOwningKey());
Command cmd = new Command<>(cmdType, getOurIdentity().getOwningKey());
// We create a transaction builder and add the components. // We create a transaction builder and add the components.
TransactionBuilder txBuilder = new TransactionBuilder(notary) TransactionBuilder txBuilder = new TransactionBuilder(notary)
.addOutputState(outputState, TemplateContract.ID) .addOutputState(outputState, TemplateContract.ID)
.addCommand(cmd); .addCommand(command);
// Signing the transaction. // Signing the transaction.
SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder); SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);

View File

@ -2,11 +2,11 @@ package net.corda.docs.java.tutorial.helloworld;
import net.corda.core.contracts.ContractState; import net.corda.core.contracts.ContractState;
import net.corda.core.identity.AbstractParty; import net.corda.core.identity.AbstractParty;
import java.util.Arrays;
import java.util.List; import java.util.List;
// DOCSTART 01 // DOCSTART 01
// Add these imports: // Add this import:
import com.google.common.collect.ImmutableList;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
// Replace TemplateState's definition with: // Replace TemplateState's definition with:
@ -35,7 +35,7 @@ public class IOUState implements ContractState {
@Override @Override
public List<AbstractParty> getParticipants() { public List<AbstractParty> getParticipants() {
return ImmutableList.of(lender, borrower); return Arrays.asList(lender, borrower);
} }
} }
// DOCEND 01 // DOCEND 01

View File

@ -6,15 +6,14 @@ import net.corda.core.transactions.LedgerTransaction;
// DOCSTART 01 // DOCSTART 01
// Add these imports: // Add these imports:
import com.google.common.collect.ImmutableList;
import net.corda.core.contracts.CommandWithParties; import net.corda.core.contracts.CommandWithParties;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
import java.security.PublicKey; import java.security.PublicKey;
import java.util.Arrays;
import java.util.List; import java.util.List;
import static net.corda.core.contracts.ContractsDSL.requireSingleCommand; import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
import static net.corda.core.contracts.ContractsDSL.requireThat;
// Replace TemplateContract's definition with: // Replace TemplateContract's definition with:
public class IOUContract implements Contract { public class IOUContract implements Contract {
@ -28,26 +27,29 @@ public class IOUContract implements Contract {
public void verify(LedgerTransaction tx) { public void verify(LedgerTransaction tx) {
final CommandWithParties<IOUContract.Create> command = requireSingleCommand(tx.getCommands(), IOUContract.Create.class); final CommandWithParties<IOUContract.Create> command = requireSingleCommand(tx.getCommands(), IOUContract.Create.class);
requireThat(check -> {
// Constraints on the shape of the transaction. // Constraints on the shape of the transaction.
check.using("No inputs should be consumed when issuing an IOU.", tx.getInputs().isEmpty()); if (!tx.getInputs().isEmpty())
check.using("There should be one output state of type IOUState.", tx.getOutputs().size() == 1); 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. // IOU-specific constraints.
final IOUState out = tx.outputsOfType(IOUState.class).get(0); final IOUState output = tx.outputsOfType(IOUState.class).get(0);
final Party lender = out.getLender(); final Party lender = output.getLender();
final Party borrower = out.getBorrower(); final Party borrower = output.getBorrower();
check.using("The IOU's value must be non-negative.", out.getValue() > 0); if (output.getValue() <= 0)
check.using("The lender and the borrower cannot be the same entity.", lender != borrower); 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. // Constraints on the signers.
final List<PublicKey> signers = command.getSigners(); final List<PublicKey> requiredSigners = command.getSigners();
check.using("There must be two signers.", signers.size() == 2); final List<PublicKey> expectedSigners = Arrays.asList(borrower.getOwningKey(), lender.getOwningKey());
check.using("The borrower and lender must be signers.", signers.containsAll( if (requiredSigners.size() != 2)
ImmutableList.of(borrower.getOwningKey(), lender.getOwningKey()))); 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 // DOCEND 01

View File

@ -2,9 +2,7 @@ package net.corda.docs.java.tutorial.twoparty;
// DOCSTART 01 // DOCSTART 01
import co.paralleluniverse.fibers.Suspendable; import co.paralleluniverse.fibers.Suspendable;
import com.google.common.collect.ImmutableList;
import net.corda.core.contracts.Command; import net.corda.core.contracts.Command;
import net.corda.core.contracts.StateAndContract;
import net.corda.core.flows.*; import net.corda.core.flows.*;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
import net.corda.core.transactions.SignedTransaction; import net.corda.core.transactions.SignedTransaction;
@ -12,6 +10,7 @@ import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker; import net.corda.core.utilities.ProgressTracker;
import java.security.PublicKey; import java.security.PublicKey;
import java.util.Arrays;
import java.util.List; import java.util.List;
// DOCEND 01 // DOCEND 01
@ -42,22 +41,19 @@ public class IOUFlow extends FlowLogic<Void> {
@Suspendable @Suspendable
@Override @Override
public Void call() throws FlowException { public Void call() throws FlowException {
// DOCSTART 02
// We retrieve the notary identity from the network map. // We retrieve the notary identity from the network map.
Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0); 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. // We create the transaction components.
IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty); IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
StateAndContract outputContractAndState = new StateAndContract(outputState, IOUContract.ID); List<PublicKey> requiredSigners = Arrays.asList(getOurIdentity().getOwningKey(), otherParty.getOwningKey());
List<PublicKey> requiredSigners = ImmutableList.of(getOurIdentity().getOwningKey(), otherParty.getOwningKey()); Command command = new Command<>(new IOUContract.Create(), requiredSigners);
Command cmd = new Command<>(new IOUContract.Create(), requiredSigners);
// We add the items to the builder. // We create a transaction builder and add the components.
txBuilder.withItems(outputContractAndState, cmd); TransactionBuilder txBuilder = new TransactionBuilder(notary)
.addOutputState(outputState, IOUContract.ID)
.addCommand(command);
// Verifying the transaction. // Verifying the transaction.
txBuilder.verify(getServiceHub()); txBuilder.verify(getServiceHub());
@ -70,7 +66,7 @@ public class IOUFlow extends FlowLogic<Void> {
// Obtaining the counterparty's signature. // Obtaining the counterparty's signature.
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow( SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
signedTx, ImmutableList.of(otherPartySession), CollectSignaturesFlow.tracker())); signedTx, Arrays.asList(otherPartySession), CollectSignaturesFlow.tracker()));
// Finalising the transaction. // Finalising the transaction.
subFlow(new FinalityFlow(fullySignedTx)); subFlow(new FinalityFlow(fullySignedTx));

View File

@ -1,10 +1,10 @@
package net.corda.docs.java.tutorial.twoparty; package net.corda.docs.java.tutorial.twoparty;
import com.google.common.collect.ImmutableList;
import net.corda.core.contracts.ContractState; import net.corda.core.contracts.ContractState;
import net.corda.core.identity.AbstractParty; import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
import java.util.Arrays;
import java.util.List; import java.util.List;
public class IOUState implements ContractState { public class IOUState implements ContractState {
@ -32,6 +32,6 @@ public class IOUState implements ContractState {
@Override @Override
public List<AbstractParty> getParticipants() { public List<AbstractParty> getParticipants() {
return ImmutableList.of(lender, borrower); return Arrays.asList(lender, borrower);
} }
} }

View File

@ -8,13 +8,13 @@ import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.utilities.ProgressTracker
// DOCSTART 01 // DOCSTART 01
// Add these imports: // Add these imports:
import net.corda.core.contracts.Command import net.corda.core.contracts.Command
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker
// Replace Initiator's definition with: // Replace Initiator's definition with:
@InitiatingFlow @InitiatingFlow
@ -33,12 +33,12 @@ class IOUFlow(val iouValue: Int,
// We create the transaction components. // We create the transaction components.
val outputState = IOUState(iouValue, ourIdentity, otherParty) 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. // We create a transaction builder and add the components.
val txBuilder = TransactionBuilder(notary = notary) val txBuilder = TransactionBuilder(notary = notary)
.addOutputState(outputState, TemplateContract.ID) .addOutputState(outputState, TemplateContract.ID)
.addCommand(cmd) .addCommand(command)
// We sign the transaction. // We sign the transaction.
val signedTx = serviceHub.signInitialTransaction(txBuilder) val signedTx = serviceHub.signInitialTransaction(txBuilder)

View File

@ -5,7 +5,7 @@ package net.corda.docs.kotlin.tutorial.helloworld
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
// DOCSTART 01 // DOCSTART 01
// Add these imports: // Add this import:
import net.corda.core.identity.Party import net.corda.core.identity.Party
// Replace TemplateState's definition with: // Replace TemplateState's definition with:

View File

@ -5,7 +5,7 @@ import net.corda.core.contracts.Contract
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
// DOCSTART 01 // DOCSTART 01
// Add these imports: // Add this import:
import net.corda.core.contracts.* import net.corda.core.contracts.*
class IOUContract : Contract { class IOUContract : Contract {
@ -25,14 +25,14 @@ class IOUContract : Contract {
"There should be one output state of type IOUState." using (tx.outputs.size == 1) "There should be one output state of type IOUState." using (tx.outputs.size == 1)
// IOU-specific constraints. // IOU-specific constraints.
val out = tx.outputsOfType<IOUState>().single() val output = tx.outputsOfType<IOUState>().single()
"The IOU's value must be non-negative." using (out.value > 0) "The IOU's value must be non-negative." using (output.value > 0)
"The lender and the borrower cannot be the same entity." using (out.lender != out.borrower) "The lender and the borrower cannot be the same entity." using (output.lender != output.borrower)
// Constraints on the signers. // Constraints on the signers.
val expectedSigners = listOf(output.borrower.owningKey, output.lender.owningKey)
"There must be two signers." using (command.signers.toSet().size == 2) "There must be two signers." using (command.signers.toSet().size == 2)
"The borrower and lender must be signers." using (command.signers.containsAll(listOf( "The borrower and lender must be signers." using (command.signers.containsAll(expectedSigners))
out.borrower.owningKey, out.lender.owningKey)))
} }
} }
} }

View File

@ -5,7 +5,6 @@ package net.corda.docs.kotlin.tutorial.twoparty
// DOCSTART 01 // DOCSTART 01
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.Command import net.corda.core.contracts.Command
import net.corda.core.contracts.StateAndContract
import net.corda.core.flows.CollectSignaturesFlow import net.corda.core.flows.CollectSignaturesFlow
import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
@ -27,20 +26,18 @@ class IOUFlow(val iouValue: Int,
/** The flow logic is encapsulated within the call() method. */ /** The flow logic is encapsulated within the call() method. */
@Suspendable @Suspendable
override fun call() { override fun call() {
// DOCSTART 02
// We retrieve the notary identity from the network map. // We retrieve the notary identity from the network map.
val notary = serviceHub.networkMapCache.notaryIdentities[0] val notary = serviceHub.networkMapCache.notaryIdentities[0]
// DOCSTART 02
// We create a transaction builder.
val txBuilder = TransactionBuilder(notary = notary)
// We create the transaction components. // We create the transaction components.
val outputState = IOUState(iouValue, ourIdentity, otherParty) val outputState = IOUState(iouValue, ourIdentity, otherParty)
val outputContractAndState = StateAndContract(outputState, IOUContract.ID) val command = Command(IOUContract.Create(), listOf(ourIdentity.owningKey, otherParty.owningKey))
val cmd = Command(IOUContract.Create(), listOf(ourIdentity.owningKey, otherParty.owningKey))
// We add the items to the builder. // We create a transaction builder and add the components.
txBuilder.withItems(outputContractAndState, cmd) val txBuilder = TransactionBuilder(notary = notary)
.addOutputState(outputState, IOUContract.ID)
.addCommand(command)
// Verifying the transaction. // Verifying the transaction.
txBuilder.verify(serviceHub) txBuilder.verify(serviceHub)

View File

@ -3,10 +3,7 @@
package net.corda.docs.kotlin.tutorial.twoparty package net.corda.docs.kotlin.tutorial.twoparty
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.FlowLogic import net.corda.core.flows.*
import net.corda.core.flows.FlowSession
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.SignTransactionFlow
import net.corda.docs.kotlin.tutorial.helloworld.IOUFlow import net.corda.docs.kotlin.tutorial.helloworld.IOUFlow
import net.corda.docs.kotlin.tutorial.helloworld.IOUState import net.corda.docs.kotlin.tutorial.helloworld.IOUState

View File

@ -40,8 +40,7 @@ FlowLogic
--------- ---------
All flows must subclass ``FlowLogic``. You then define the steps taken by the flow by overriding ``FlowLogic.call``. 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 Let's define our ``IOUFlow``. Delete the existing ``Responder`` flow. Then replace the definition of ``Initiator`` with the following:
template (``Initiator`` and ``Responder``), and replace them with the following:
.. container:: codeset .. container:: codeset

View File

@ -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 :doc:`your first CorDapp <tutorial-cordapp>`, and you're familiar with Corda's :doc:`key concepts <key-concepts>`. What
comes next? 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 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 give the nodes' owners the ability to make their node conduct some new process - anything from 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. issuing a debt instrument to making a restaurant booking.
Our use-case 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 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 person a given amount of money. Clearly this is sensitive information that we'd only want to communicate on another node a certain amount. This simple CorDapp will showcase several key benefits of Corda as a blockchain platform:
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.
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 * **Well-known identities** - Each Corda node has a well-known identity on the network. This allows us to write code in terms of real
* **Flows**, which encapsulate the procedure for carrying out a specific ledger update 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 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 Our state will be the ``IOUState``, representing an IOU. It will contain the IOU's value, its lender and its borrower. We can visualize
borrower. We can visualize ``IOUState`` as follows: ``IOUState`` as follows:
.. image:: resources/tutorial-state.png .. image:: resources/tutorial-state.png
:scale: 25% :scale: 25%
@ -43,20 +52,20 @@ borrower. We can visualize ``IOUState`` as follows:
The IOUFlow The IOUFlow
^^^^^^^^^^^ ^^^^^^^^^^^
Our flow will be the ``IOUFlow``. This flow will completely automate the process of issuing a new IOU onto a ledger. It 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
is composed of the following steps: steps:
.. image:: resources/simple-tutorial-flow.png .. image:: resources/simple-tutorial-flow.png
:scale: 25% :scale: 25%
:align: center :align: center
In traditional distributed ledger systems, where all data is broadcast to every network participant, you dont need to The IOUContract
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 For this tutorial, we will use the default ``TemplateContract``. We will update it to create a fully-fledged ``IOUContract`` in the next
agreeing a ledger update. tutorial.
Progress so far 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.

View File

@ -107,9 +107,7 @@ commands.
We want to create an IOU of 99 with PartyB. We start the ``IOUFlow`` by typing: 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"
@ -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. 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 Conclusion
---------- ----------
We have written a simple CorDapp that allows IOUs to be issued onto the ledger. Our CorDapp is made up of two key We have written a simple CorDapp that allows IOUs to be issued onto the ledger. Our CorDapp is made up of two key
parts: 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 * The ``IOUFlow``, orchestrating the process of agreeing the creation of an IOU on-ledger
After completing this tutorial, your CorDapp should look like this: After completing this tutorial, your CorDapp should look like this:

View File

@ -7,7 +7,7 @@
Writing the state 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. represent an IOU.
The ContractState interface 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. 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 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:: .. note::
@ -46,7 +46,7 @@ represent a given type of shared fact on the ledger.
Modelling IOUs 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: interface, our ``IOUState`` will also need properties to track the relevant features of the IOU:
* The value 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. 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`` 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? What about the contract?
------------------------ ------------------------

View File

@ -7,41 +7,39 @@
The CorDapp Template The CorDapp Template
==================== ====================
When writing a new CorDapp, youll generally want to base it on the standard templates: When writing a new CorDapp, youll generally want to start from one of the standard templates:
* The `Java Cordapp Template <https://github.com/corda/cordapp-template-java>`_ * The `Java Cordapp Template <https://github.com/corda/cordapp-template-java>`_
* The `Kotlin Cordapp Template <https://github.com/corda/cordapp-template-kotlin>`_ * 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 The Cordapp templates provide the boilerplate for developing a new CorDapp. CorDapps can be written in either Java or Kotlin. We will be
CorDapp onto a local test network of dummy nodes to test its functionality. 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. The required libraries are automatically downloaded from an online Maven
repository and cached locally.
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.
Downloading the template Downloading the template
------------------------ ------------------------
To download the template, open a terminal window in the directory where you want to download the CorDapp template, and Open a terminal window in the directory where you want to download the CorDapp template, and run the following command:
run the following command:
.. code-block:: bash .. container:: codeset
.. code-block:: java
git clone https://github.com/corda/cordapp-template-java.git ; cd cordapp-template-java git clone https://github.com/corda/cordapp-template-java.git ; cd cordapp-template-java
*or* .. code-block:: kotlin
git clone https://github.com/corda/cordapp-template-kotlin.git ; cd cordapp-template-kotlin git clone https://github.com/corda/cordapp-template-kotlin.git ; cd cordapp-template-kotlin
Opening the template in IntelliJ Opening the template in IntelliJ
-------------------------------- --------------------------------
Once the template is download, open it in IntelliJ by following the instructions here: 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. https://docs.corda.net/tutorial-cordapp.html#opening-the-example-cordapp-in-intellij.
Template structure 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 .. container:: codeset

View File

@ -154,7 +154,7 @@ Transaction constraints
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
We also want our transaction to have no inputs and only a single output - an issuance transaction. 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: provides a terse way to write the following:
* If the condition on the right-hand side doesn't evaluate to true... * 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. 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 IOU constraints
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
We want to impose two constraints on the ``IOUState`` itself: 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 * Its value must be non-negative
* The lender and the borrower cannot be the same entity * 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 inside ``verify``. We can also write
You can see that we're not restricted to only writing constraints in the ``requireThat`` block. We can also write
other statements - in this case, extracting the transaction's single ``IOUState`` and assigning it to a variable. other statements - in this case, extracting the transaction's single ``IOUState`` and assigning it to a variable.
Signer constraints 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 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. 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. the express agreement of both the lender and borrower nodes.
Progress so far Progress so far

View File

@ -31,8 +31,7 @@ In ``IOUFlow.java``/``Flows.kt``, change the imports block to the following:
:start-after: DOCSTART 01 :start-after: DOCSTART 01
:end-before: DOCEND 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 And update ``IOUFlow.call`` to the following:
follows:
.. container:: codeset .. 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>`. :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 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. the borrower from unilaterally updating the ledger in a way that only benefits themselves.
After completing this tutorial, your CorDapp should look like this: After completing this tutorial, your CorDapp should look like this:

View File

@ -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 In the Hello, World tutorial, we built a CorDapp allowing us to model IOUs on ledger. Our CorDapp was made up of two
elements: 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 * 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. 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 In this tutorial, we'll write a contract to imposes rules on how an ``IOUState`` can change over time. In turn, this