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 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);
|
||||||
|
@ -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
|
@ -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.
|
if (!tx.getInputs().isEmpty())
|
||||||
check.using("No inputs should be consumed when issuing an IOU.", tx.getInputs().isEmpty());
|
throw new IllegalArgumentException("No inputs should be consumed when issuing an IOU.");
|
||||||
check.using("There should be one output state of type IOUState.", tx.getOutputs().size() == 1);
|
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
|
@ -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));
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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)
|
||||||
|
@ -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:
|
||||||
|
@ -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)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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 don’t 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.
|
||||||
|
@ -107,11 +107,9 @@ 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"
|
|
||||||
|
|
||||||
This single command will cause PartyA and PartyB to automatically agree an IOU. This is one of the great advantages of
|
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.
|
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
|
.. 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:
|
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.
|
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:
|
||||||
|
@ -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?
|
||||||
------------------------
|
------------------------
|
||||||
|
@ -7,41 +7,39 @@
|
|||||||
The CorDapp Template
|
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 `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
|
||||||
|
|
||||||
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
|
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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user