Outdated diagram, new service upgrade notes, extracts tut code from files.

This commit is contained in:
Joel Dudley 2017-10-16 16:45:48 +01:00 committed by GitHub
parent ea36cf12e6
commit 0d5ac6f6d8
25 changed files with 698 additions and 411 deletions

View File

@ -0,0 +1,46 @@
package net.corda.docs.java.tutorial.helloworld;
// DOCSTART 01
import net.corda.core.contracts.CommandData;
import net.corda.core.contracts.CommandWithParties;
import net.corda.core.contracts.Contract;
import net.corda.core.identity.Party;
import net.corda.core.transactions.LedgerTransaction;
import java.security.PublicKey;
import java.util.List;
import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
import static net.corda.core.contracts.ContractsDSL.requireThat;
public class IOUContract implements Contract {
// Our Create command.
public static class Create implements CommandData {
}
@Override
public void verify(LedgerTransaction tx) {
final CommandWithParties<Create> command = requireSingleCommand(tx.getCommands(), 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);
// 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);
// Constraints on the signers.
final List<PublicKey> signers = command.getSigners();
check.using("There must only be one signer.", signers.size() == 1);
check.using("The signer must be the lender.", signers.contains(lender.getOwningKey()));
return null;
});
}
}
// DOCEND 01

View File

@ -0,0 +1,68 @@
package net.corda.docs.java.tutorial.helloworld;
// DOCSTART 01
import co.paralleluniverse.fibers.Suspendable;
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;
import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker;
@InitiatingFlow
@StartableByRPC
public class IOUFlow extends FlowLogic<Void> {
private final Integer iouValue;
private final Party otherParty;
/**
* The progress tracker provides checkpoints indicating the progress of the flow to observers.
*/
private final ProgressTracker progressTracker = new ProgressTracker();
public IOUFlow(Integer iouValue, Party otherParty) {
this.iouValue = iouValue;
this.otherParty = otherParty;
}
@Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
/**
* The flow logic is encapsulated within the call() method.
*/
@Suspendable
@Override
public Void call() throws FlowException {
// We retrieve the notary identity from the network map.
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
// We create a transaction builder.
final TransactionBuilder txBuilder = new TransactionBuilder();
txBuilder.setNotary(notary);
// We create the transaction components.
IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
String outputContract = IOUContract.class.getName();
StateAndContract outputContractAndState = new StateAndContract(outputState, outputContract);
Command cmd = new Command<>(new IOUContract.Create(), getOurIdentity().getOwningKey());
// We add the items to the builder.
txBuilder.withItems(outputContractAndState, cmd);
// Verifying the transaction.
txBuilder.verify(getServiceHub());
// Signing the transaction.
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
// Finalising the transaction.
subFlow(new FinalityFlow(signedTx));
return null;
}
}
// DOCEND 01

View File

@ -0,0 +1,39 @@
package net.corda.docs.java.tutorial.helloworld;
// DOCSTART 01
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.List;
public class IOUState implements ContractState {
private final int value;
private final Party lender;
private final Party borrower;
public IOUState(int value, Party lender, Party borrower) {
this.value = value;
this.lender = lender;
this.borrower = borrower;
}
public int getValue() {
return value;
}
public Party getLender() {
return lender;
}
public Party getBorrower() {
return borrower;
}
@Override
public List<AbstractParty> getParticipants() {
return ImmutableList.of(lender, borrower);
}
}
// DOCEND 01

View File

@ -0,0 +1,50 @@
package net.corda.docs.java.tutorial.twoparty;
// DOCSTART 01
import com.google.common.collect.ImmutableList;
import net.corda.core.contracts.CommandData;
import net.corda.core.contracts.CommandWithParties;
import net.corda.core.contracts.Contract;
import net.corda.core.identity.Party;
import net.corda.core.transactions.LedgerTransaction;
import java.security.PublicKey;
import java.util.List;
import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
import static net.corda.core.contracts.ContractsDSL.requireThat;
// DOCEND 01
public class IOUContract implements Contract {
// Our Create command.
public static class Create implements CommandData {
}
@Override
public void verify(LedgerTransaction tx) {
final CommandWithParties<net.corda.docs.java.tutorial.helloworld.IOUContract.Create> command = requireSingleCommand(tx.getCommands(), net.corda.docs.java.tutorial.helloworld.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);
// 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);
// DOCSTART 02
// 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())));
// DOCEND 02
return null;
});
}
}

View File

@ -0,0 +1,82 @@
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;
import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker;
import java.security.PublicKey;
import java.util.List;
// DOCEND 01
@InitiatingFlow
@StartableByRPC
public class IOUFlow extends FlowLogic<Void> {
private final Integer iouValue;
private final Party otherParty;
/**
* The progress tracker provides checkpoints indicating the progress of the flow to observers.
*/
private final ProgressTracker progressTracker = new ProgressTracker();
public IOUFlow(Integer iouValue, Party otherParty) {
this.iouValue = iouValue;
this.otherParty = otherParty;
}
@Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
/**
* The flow logic is encapsulated within the call() method.
*/
@Suspendable
@Override
public Void call() throws FlowException {
// We retrieve the notary identity from the network map.
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
// We create a transaction builder.
final TransactionBuilder txBuilder = new TransactionBuilder();
txBuilder.setNotary(notary);
// DOCSTART 02
// We create the transaction components.
IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
String outputContract = IOUContract.class.getName();
StateAndContract outputContractAndState = new StateAndContract(outputState, outputContract);
List<PublicKey> requiredSigners = ImmutableList.of(getOurIdentity().getOwningKey(), otherParty.getOwningKey());
Command cmd = new Command<>(new IOUContract.Create(), requiredSigners);
// We add the items to the builder.
txBuilder.withItems(outputContractAndState, cmd);
// Verifying the transaction.
txBuilder.verify(getServiceHub());
// Signing the transaction.
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
// Creating a session with the other party.
FlowSession otherpartySession = initiateFlow(otherParty);
// Obtaining the counterparty's signature.
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
signedTx, ImmutableList.of(otherpartySession), CollectSignaturesFlow.tracker()));
// Finalising the transaction.
subFlow(new FinalityFlow(fullySignedTx));
return null;
// DOCEND 02
}
}

View File

@ -0,0 +1,47 @@
package net.corda.docs.java.tutorial.twoparty;
// DOCSTART 01
import co.paralleluniverse.fibers.Suspendable;
import net.corda.core.contracts.ContractState;
import net.corda.core.flows.*;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.utilities.ProgressTracker;
import net.corda.docs.java.tutorial.helloworld.IOUFlow;
import net.corda.docs.java.tutorial.helloworld.IOUState;
import static net.corda.core.contracts.ContractsDSL.requireThat;
@InitiatedBy(IOUFlow.class)
public class IOUFlowResponder extends FlowLogic<Void> {
private final FlowSession otherPartySession;
public IOUFlowResponder(FlowSession otherPartySession) {
this.otherPartySession = otherPartySession;
}
@Suspendable
@Override
public Void call() throws FlowException {
class SignTxFlow extends SignTransactionFlow {
private SignTxFlow(FlowSession otherPartySession, ProgressTracker progressTracker) {
super(otherPartySession, progressTracker);
}
@Override
protected void checkTransaction(SignedTransaction stx) {
requireThat(require -> {
ContractState output = stx.getTx().getOutputs().get(0).getData();
require.using("This must be an IOU transaction.", output instanceof IOUState);
IOUState iou = (IOUState) output;
require.using("The IOU's value can't be too high.", iou.getValue() < 100);
return null;
});
}
}
subFlow(new SignTxFlow(otherPartySession, SignTransactionFlow.Companion.tracker()));
return null;
}
}
// DOCEND 01

View File

@ -0,0 +1,37 @@
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.List;
public class IOUState implements ContractState {
private final int value;
private final Party lender;
private final Party borrower;
public IOUState(int value, Party lender, Party borrower) {
this.value = value;
this.lender = lender;
this.borrower = borrower;
}
public int getValue() {
return value;
}
public Party getLender() {
return lender;
}
public Party getBorrower() {
return borrower;
}
@Override
public List<AbstractParty> getParticipants() {
return ImmutableList.of(lender, borrower);
}
}

View File

@ -0,0 +1,33 @@
package net.corda.docs.tutorial.helloworld
// DOCSTART 01
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.contracts.requireSingleCommand
import net.corda.core.contracts.requireThat
import net.corda.core.transactions.LedgerTransaction
class IOUContract : Contract {
// Our Create command.
class Create : CommandData
override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Create>()
requireThat {
// Constraints on the shape of the transaction.
"No inputs should be consumed when issuing an IOU." using (tx.inputs.isEmpty())
"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)
// Constraints on the signers.
"There must only be one signer." using (command.signers.toSet().size == 1)
"The signer must be the lender." using (command.signers.contains(out.lender.owningKey))
}
}
}
// DOCEND 01

View File

@ -0,0 +1,52 @@
package net.corda.docs.tutorial.helloworld
// DOCSTART 01
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.Command
import net.corda.core.contracts.StateAndContract
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.identity.Party
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker
import kotlin.reflect.jvm.jvmName
@InitiatingFlow
@StartableByRPC
class IOUFlow(val iouValue: Int,
val otherParty: Party) : FlowLogic<Unit>() {
/** The progress tracker provides checkpoints indicating the progress of the flow to observers. */
override val progressTracker = ProgressTracker()
/** The flow logic is encapsulated within the call() method. */
@Suspendable
override fun call() {
// We retrieve the notary identity from the network map.
val notary = serviceHub.networkMapCache.notaryIdentities[0]
// We create a transaction builder
val txBuilder = TransactionBuilder(notary = notary)
// We create the transaction components.
val outputState = IOUState(iouValue, ourIdentity, otherParty)
val outputContract = IOUContract::class.jvmName
val outputContractAndState = StateAndContract(outputState, outputContract)
val cmd = Command(IOUContract.Create(), ourIdentity.owningKey)
// We add the items to the builder.
txBuilder.withItems(outputContractAndState, cmd)
// Verifying the transaction.
txBuilder.verify(serviceHub)
// Signing the transaction.
val signedTx = serviceHub.signInitialTransaction(txBuilder)
// Finalising the transaction.
subFlow(FinalityFlow(signedTx))
}
}
// DOCEND 01

View File

@ -0,0 +1,12 @@
package net.corda.docs.tutorial.helloworld
// DOCSTART 01
import net.corda.core.contracts.ContractState
import net.corda.core.identity.Party
class IOUState(val value: Int,
val lender: Party,
val borrower: Party) : ContractState {
override val participants get() = listOf(lender, borrower)
}
// DOCEND 01

View File

@ -0,0 +1,36 @@
package net.corda.docs.tutorial.twoparty
// DOCSTART 01
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.contracts.requireSingleCommand
import net.corda.core.contracts.requireThat
import net.corda.core.transactions.LedgerTransaction
// DOCEND 01
class IOUContract : Contract {
// Our Create command.
class Create : CommandData
override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Create>()
requireThat {
// Constraints on the shape of the transaction.
"No inputs should be consumed when issuing an IOU." using (tx.inputs.isEmpty())
"There should be one output state of type IOUState." using (tx.outputs.size == 1)
// IOU-specific constraints.
val out = tx.outputsOfType<net.corda.docs.tutorial.helloworld.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)
// DOCSTART 02
// Constraints on the signers.
"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)))
// DOCEND 02
}
}
}

View File

@ -0,0 +1,57 @@
package net.corda.docs.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.*
import net.corda.core.identity.Party
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker
import kotlin.reflect.jvm.jvmName
// DOCEND 01
@InitiatingFlow
@StartableByRPC
class IOUFlow(val iouValue: Int,
val otherParty: Party) : FlowLogic<Unit>() {
/** The progress tracker provides checkpoints indicating the progress of the flow to observers. */
override val progressTracker = ProgressTracker()
/** The flow logic is encapsulated within the call() method. */
@Suspendable
override fun call() {
// We retrieve the notary identity from the network map.
val notary = serviceHub.networkMapCache.notaryIdentities[0]
// We create a transaction builder
val txBuilder = TransactionBuilder(notary = notary)
// DOCSTART 02
// We create the transaction components.
val outputState = IOUState(iouValue, ourIdentity, otherParty)
val outputContract = IOUContract::class.jvmName
val outputContractAndState = StateAndContract(outputState, outputContract)
val cmd = Command(IOUContract.Create(), listOf(ourIdentity.owningKey, otherParty.owningKey))
// We add the items to the builder.
txBuilder.withItems(outputContractAndState, cmd)
// Verifying the transaction.
txBuilder.verify(serviceHub)
// Signing the transaction.
val signedTx = serviceHub.signInitialTransaction(txBuilder)
// Creating a session with the other party.
val otherpartySession = initiateFlow(otherParty)
// Obtaining the counterparty's signature.
val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, listOf(otherpartySession), CollectSignaturesFlow.tracker()))
// Finalising the transaction.
subFlow(FinalityFlow(fullySignedTx))
// DOCEND 02
}
}

View File

@ -0,0 +1,30 @@
package net.corda.docs.tutorial.twoparty
// DOCSTART 01
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.requireThat
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.transactions.SignedTransaction
import net.corda.docs.tutorial.helloworld.IOUFlow
import net.corda.docs.tutorial.helloworld.IOUState
@InitiatedBy(IOUFlow::class)
class IOUFlowResponder(val otherPartySession: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val signTransactionFlow = object : SignTransactionFlow(otherPartySession, SignTransactionFlow.tracker()) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
val output = stx.tx.outputs.single().data
"This must be an IOU transaction." using (output is IOUState)
val iou = output as IOUState
"The IOU's value can't be too high." using (iou.value < 100)
}
}
subFlow(signTransactionFlow)
}
}
// DOCEND 01

View File

@ -0,0 +1,10 @@
package net.corda.docs.tutorial.twoparty
import net.corda.core.contracts.ContractState
import net.corda.core.identity.Party
class IOUState(val value: Int,
val lender: Party,
val borrower: Party) : ContractState {
override val participants get() = listOf(lender, borrower)
}

View File

@ -78,80 +78,15 @@ Let's write a contract that enforces these constraints. We'll do this by modifyi
.. container:: codeset
.. code-block:: kotlin
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/helloworld/contract.kt
:language: kotlin
:start-after: DOCSTART 01
:end-before: DOCEND 01
...
import net.corda.core.contracts.*
...
class IOUContract : Contract {
// Our Create command.
class Create : CommandData
override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Create>()
requireThat {
// Constraints on the shape of the transaction.
"No inputs should be consumed when issuing an IOU." using (tx.inputs.isEmpty())
"There should be one output state of type IOUState." using (tx.outputs.size == 1)
// IOU-specific constraints.
val out = tx.outputs.single().data as IOUState
"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)
// Constraints on the signers.
"There must only be one signer." using (command.signers.toSet().size == 1)
"The signer must be the lender." using (command.signers.contains(out.lender.owningKey))
}
}
}
.. code-block:: java
package com.template.contract;
import com.template.state.IOUState;
import net.corda.core.contracts.CommandWithParties;
import net.corda.core.contracts.CommandData;
import net.corda.core.contracts.Contract;
import net.corda.core.transactions.LedgerTransaction;
import net.corda.core.identity.Party;
import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
import static net.corda.core.contracts.ContractsDSL.requireThat;
public class IOUContract implements Contract {
// Our Create command.
public static class Create implements CommandData {}
@Override
public void verify(LedgerTransaction tx) {
final CommandWithParties<Create> command = requireSingleCommand(tx.getCommands(), 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);
// IOU-specific constraints.
final IOUState out = (IOUState) tx.getOutputs().get(0).getData();
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);
// Constraints on the signers.
check.using("There must only be one signer.", command.getSigners().size() == 1);
check.using("The signer must be the lender.", command.getSigners().contains(lender.getOwningKey()));
return null;
});
}
}
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/helloworld/IOUContract.java
:language: java
:start-after: DOCSTART 01
:end-before: DOCEND 01
If you're following along in Java, you'll also need to rename ``TemplateContract.java`` to ``IOUContract.java``.

View File

@ -33,128 +33,20 @@ FlowLogic
Flows are implemented as ``FlowLogic`` subclasses. You define the steps taken by the flow by overriding
``FlowLogic.call``.
We'll write our flow in either ``TemplateFlow.java`` or ``App.kt``. Overwrite both the existing flows in the template
with the following:
We'll write our flow in either ``TemplateFlow.java`` or ``App.kt``. Delete both the existing flows in the template, and
replace them with the following:
.. container:: codeset
.. code-block:: kotlin
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/helloworld/flow.kt
:language: kotlin
:start-after: DOCSTART 01
:end-before: DOCEND 01
...
import net.corda.core.utilities.ProgressTracker
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.flows.*
...
@InitiatingFlow
@StartableByRPC
class IOUFlow(val iouValue: Int,
val otherParty: Party) : FlowLogic<Unit>() {
/** The progress tracker provides checkpoints indicating the progress of the flow to observers. */
override val progressTracker = ProgressTracker()
/** The flow logic is encapsulated within the call() method. */
@Suspendable
override fun call() {
// We retrieve the notary identity from the network map.
val notary = serviceHub.networkMapCache.notaryIdentities[0]
// We create a transaction builder
val txBuilder = TransactionBuilder(notary = notary)
// We create the transaction components.
val outputState = IOUState(iouValue, ourIdentity, otherParty)
val outputContract = IOUContract::class.jvmName
val outputContractAndState = StateAndContract(outputState, outputContract)
val cmd = Command(IOUContract.Create(), ourIdentity.owningKey)
// We add the items to the builder.
txBuilder.withItems(outputContractAndState, cmd)
// Verifying the transaction.
txBuilder.verify(serviceHub)
// Signing the transaction.
val signedTx = serviceHub.signInitialTransaction(txBuilder)
// Finalising the transaction.
subFlow(FinalityFlow(signedTx))
}
}
.. code-block:: java
package com.template.flow;
import co.paralleluniverse.fibers.Suspendable;
import com.template.contract.IOUContract;
import com.template.state.IOUState;
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;
import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker;
@InitiatingFlow
@StartableByRPC
public class IOUFlow extends FlowLogic<Void> {
private final Integer iouValue;
private final Party otherParty;
/**
* The progress tracker provides checkpoints indicating the progress of the flow to observers.
*/
private final ProgressTracker progressTracker = new ProgressTracker();
public IOUFlow(Integer iouValue, Party otherParty) {
this.iouValue = iouValue;
this.otherParty = otherParty;
}
@Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
/**
* The flow logic is encapsulated within the call() method.
*/
@Suspendable
@Override
public Void call() throws FlowException {
// We retrieve the notary identity from the network map.
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
// We create a transaction builder.
final TransactionBuilder txBuilder = new TransactionBuilder();
txBuilder.setNotary(notary);
// We create the transaction components.
IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
String outputContract = IOUContract.class.getName();
StateAndContract outputContractAndState = new StateAndContract(outputState, outputContract);
Command cmd = new Command<>(new IOUContract.Create(), getOurIdentity().getOwningKey());
// We add the items to the builder.
txBuilder.withItems(outputContractAndState, cmd);
// Verifying the transaction.
txBuilder.verify(getServiceHub());
// Signing the transaction.
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
// Finalising the transaction.
subFlow(new FinalityFlow(signedTx));
return null;
}
}
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/helloworld/IOUFlow.java
:language: java
:start-after: DOCSTART 01
:end-before: DOCEND 01
If you're following along in Java, you'll also need to rename ``TemplateFlow.java`` to ``IOUFlow.java``.

View File

@ -17,11 +17,9 @@ Kotlin) file. We won't be using it, and it will cause build errors unless we rem
Deploying our CorDapp
---------------------
Let's take a look at the nodes we're going to deploy. Open the project's ``build.gradle`` file and scroll down to the
``task deployNodes`` section. This section defines three nodes - the Controller, NodeA, and NodeB:
``task deployNodes`` section. This section defines three nodes - the Controller, PartyA, and PartyB:
.. container:: codeset
.. code-block:: kotlin
.. code:: bash
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
directory "./build/nodes"
@ -64,7 +62,7 @@ We can run this ``deployNodes`` task using Gradle. For each node definition, Gra
We can do that now by running the following commands from the root of the project:
.. code:: python
.. code:: bash
// On Windows
gradlew clean deployNodes
@ -77,19 +75,18 @@ Running the nodes
Running ``deployNodes`` will build the nodes under ``build/nodes``. If we navigate to one of these folders, we'll see
the three node folders. Each node folder has the following structure:
.. code:: python
.. code:: bash
.
|____corda.jar // The runnable node
|____corda-webserver.jar // The node's webserver
|____dependencies
|____node.conf // The node's configuration file
|____plugins
|____java/kotlin-source-0.1.jar // Our IOU CorDapp
Let's start the nodes by running the following commands from the root of the project:
.. code:: python
.. code:: bash
// On Windows
build/nodes/runnodes.bat
@ -138,7 +135,7 @@ will display a list of the available commands. We can examine the contents of a
The vaults of PartyA and PartyB should both display the following output:
.. code:: python
.. code:: bash
states:
- state:
@ -194,11 +191,9 @@ There are a number of improvements we could make to this CorDapp:
* We could add an API, to make it easier to interact with the CorDapp
We will explore some of these improvements in future tutorials. But you should now be ready to develop your own
CorDapps. There's `a more fleshed-out version of the IOU CorDapp <https://github.com/corda/cordapp-example>`_ with an
API and web front-end, and a set of example CorDapps in `the main Corda repo <https://github.com/corda/corda>`_, under
``samples``. An explanation of how to run these samples :doc:`here <running-the-demos>`.
CorDapps. You can find a list of sample CorDapps `here <https://www.corda.net/samples/>`_.
As you write CorDapps, you can learn more about the API available :doc:`here <api>`.
As you write CorDapps, you can learn more about the Corda API :doc:`here <corda-api>`.
If you get stuck at any point, please reach out on `Slack <https://slack.corda.net/>`_,
`Discourse <https://discourse.corda.net/>`_, or `Stack Overflow <https://stackoverflow.com/questions/tagged/corda>`_.

View File

@ -63,53 +63,15 @@ define an ``IOUState``:
.. container:: codeset
.. code-block:: kotlin
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/helloworld/state.kt
:language: kotlin
:start-after: DOCSTART 01
:end-before: DOCEND 01
class IOUState(val value: Int,
val lender: Party,
val borrower: Party) : ContractState {
override val participants get() = listOf(lender, borrower)
}
.. code-block:: java
package com.template.state;
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.List;
public class IOUState implements ContractState {
private final int value;
private final Party lender;
private final Party borrower;
public IOUState(int value, Party lender, Party borrower) {
this.value = value;
this.lender = lender;
this.borrower = borrower;
}
public int getValue() {
return value;
}
public Party getLender() {
return lender;
}
public Party getBorrower() {
return borrower;
}
@Override
public List<AbstractParty> getParticipants() {
return ImmutableList.of(lender, borrower);
}
}
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/helloworld/IOUState.java
:language: java
:start-after: DOCSTART 01
:end-before: DOCEND 01
If you're following along in Java, you'll also need to rename ``TemplateState.java`` to ``IOUState.java``.

View File

@ -42,18 +42,23 @@ implement our IOU CorDapp in Java, we'll need to modify three files. For Kotlin,
.. code-block:: java
// 1. The state
src/main/java/com/template/state/TemplateState.java
src/main/java/com/template/TemplateState.java
// 2. The contract
src/main/java/com/template/contract/TemplateContract.java
src/main/java/com/template/TemplateContract.java
// 3. The flow
src/main/java/com/template/flow/TemplateFlow.java
src/main/java/com/template/TemplateFlow.java
.. code-block:: kotlin
src/main/kotlin/com/template/App.kt
To prevent build errors later on, you should delete the following file:
* Java: ``src/test/java/com/template/FlowTests.java``
* Kotlin: ``src/test/kotlin/com/template/FlowTests.kt``
Progress so far
---------------
We now have a template that we can build upon to define our IOU CorDapp.

View File

@ -209,10 +209,8 @@ NodeAttachmentService
The ``NodeAttachmentService`` provides an implementation of the
``AttachmentStorage`` interface exposed on the ``ServiceHub`` allowing
transactions to add documents, copies of the contract code and binary
data to transactions. The data is persisted to the local file system
inside the attachments subfolder of the node workspace. The service is
also interfaced to by the web server, which allows files to be uploaded
via an HTTP post request.
data to transactions. The service is also interfaced to by the web server,
which allows files to be uploaded via an HTTP post request.
Flow framework and event scheduling services
--------------------------------------------

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 KiB

After

Width:  |  Height:  |  Size: 282 KiB

View File

@ -11,31 +11,37 @@ Remember that each state references a contract. The contract imposes constraints
If the transaction does not obey the constraints of all the contracts of all its states, it cannot become a valid
ledger update.
We need to modify our contract so that the borrower's signature is required in any IOU creation transaction. This will
only require changing a single line of code. In ``IOUContract.java``/``IOUContract.kt``, update the final two lines of
the ``requireThat`` block as follows:
We need to modify our contract so that the borrower's signature is required in any IOU creation transaction.
In ``IOUContract.java``/``IOUContract.kt``, change the imports block to the following:
.. container:: codeset
.. code-block:: kotlin
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/twoparty/contract.kt
:language: kotlin
:start-after: DOCSTART 01
:end-before: DOCEND 01
// Constraints on the signers.
"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)))
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUContract.java
:language: java
:start-after: DOCSTART 01
:end-before: DOCEND 01
.. code-block:: java
And update the final block of constraints in the ``requireThat`` block as follows:
...
.. container:: codeset
import com.google.common.collect.ImmutableList;
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/twoparty/contract.kt
:language: kotlin
:start-after: DOCSTART 02
:end-before: DOCEND 02
:dedent: 12
...
// Constraints on the signers.
check.using("There must be two signers.", command.getSigners().size() == 2);
check.using("The borrower and lender must be signers.", command.getSigners().containsAll(
ImmutableList.of(borrower.getOwningKey(), lender.getOwningKey())));
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUContract.java
:language: java
:start-after: DOCSTART 02
:end-before: DOCEND 02
:dedent: 12
Progress so far
---------------

View File

@ -21,76 +21,35 @@ by invoking a built-in flow called ``FinalityFlow`` as a subflow. We're going to
We also need to add the borrower's public key to the transaction's command, making the borrower one of the required
signers on the transaction.
In ``IOUFlow.java``/``IOUFlow.kt``, update ``IOUFlow.call`` as follows:
In ``IOUFlow.java``/``IOUFlow.kt``, change the imports block to the following:
.. container:: codeset
.. code-block:: kotlin
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/twoparty/flow.kt
:language: kotlin
:start-after: DOCSTART 01
:end-before: DOCEND 01
...
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlow.java
:language: java
:start-after: DOCSTART 01
:end-before: DOCEND 01
// We create the transaction components.
val outputState = IOUState(iouValue, ourIdentity, otherParty)
val outputContract = IOUContract::class.jvmName
val outputContractAndState = StateAndContract(outputState, outputContract)
val cmd = Command(IOUContract.Create(), listOf(ourIdentity.owningKey, otherParty.owningKey))
And update ``IOUFlow.call`` by changing the code following the creation of the ``TransactionBuilder`` as follows:
// We add the items to the builder.
txBuilder.withItems(outputContractAndState, cmd)
.. container:: codeset
// Verifying the transaction.
txBuilder.verify(serviceHub)
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/twoparty/flow.kt
:language: kotlin
:start-after: DOCSTART 02
:end-before: DOCEND 02
:dedent: 8
// Signing the transaction.
val signedTx = serviceHub.signInitialTransaction(txBuilder)
// Creating a session with the other party.
val otherpartySession = initiateFlow(otherParty)
// Obtaining the counterparty's signature.
val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, listOf(otherpartySession), CollectSignaturesFlow.tracker()))
// Finalising the transaction.
subFlow(FinalityFlow(fullySignedTx))
.. code-block:: java
...
import com.google.common.collect.ImmutableList;
import java.security.PublicKey;
import java.util.Collections;
import java.util.List;
...
// We create the transaction components.
IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
String outputContract = IOUContract.class.getName();
StateAndContract outputContractAndState = new StateAndContract(outputState, outputContract);
List<PublicKey> requiredSigners = ImmutableList.of(getOurIdentity().getOwningKey(), otherParty.getOwningKey());
Command cmd = new Command<>(new IOUContract.Create(), requiredSigners);
// We add the items to the builder.
txBuilder.withItems(outputContractAndState, cmd);
// Verifying the transaction.
txBuilder.verify(getServiceHub());
// Signing the transaction.
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
// Creating a session with the other party.
FlowSession otherpartySession = initiateFlow(otherParty);
// Obtaining the counterparty's signature.
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
signedTx, ImmutableList.of(otherpartySession), CollectSignaturesFlow.tracker()));
// Finalising the transaction.
subFlow(new FinalityFlow(signedTx));
return null;
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlow.java
:language: java
:start-after: DOCSTART 02
:end-before: DOCEND 02
:dedent: 8
To make the borrower a required signer, we simply add the borrower's public key to the list of signers on the command.
@ -116,81 +75,15 @@ In a new ``IOUFlowResponder.java`` file in Java, or within the ``App.kt`` file i
.. container:: codeset
.. code-block:: kotlin
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/twoparty/flowResponder.kt
:language: kotlin
:start-after: DOCSTART 01
:end-before: DOCEND 01
...
import net.corda.core.transactions.SignedTransaction
...
@InitiatedBy(IOUFlow::class)
class IOUFlowResponder(val otherPartySession: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val signTransactionFlow = object : SignTransactionFlow(otherPartySession, SignTransactionFlow.tracker()) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
val output = stx.tx.outputs.single().data
"This must be an IOU transaction." using (output is IOUState)
val iou = output as IOUState
"The IOU's value can't be too high." using (iou.value < 100)
}
}
subFlow(signTransactionFlow)
}
}
.. code-block:: java
package com.template.flow;
import co.paralleluniverse.fibers.Suspendable;
import com.template.state.IOUState;
import net.corda.core.contracts.ContractState;
import net.corda.core.flows.FlowException;
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.transactions.SignedTransaction;
import net.corda.core.utilities.ProgressTracker;
import static net.corda.core.contracts.ContractsDSL.requireThat;
@InitiatedBy(IOUFlow.class)
public class IOUFlowResponder extends FlowLogic<Void> {
private final FlowSession otherPartySession;
public IOUFlowResponder(FlowSession otherPartySession) {
this.otherPartySession = otherPartySession;
}
@Suspendable
@Override
public Void call() throws FlowException {
class SignTxFlow extends SignTransactionFlow {
private signTxFlow(FlowSession otherPartySession, ProgressTracker progressTracker) {
super(otherPartySession, progressTracker);
}
@Override
protected void checkTransaction(SignedTransaction stx) {
requireThat(require -> {
ContractState output = stx.getTx().getOutputs().get(0).getData();
require.using("This must be an IOU transaction.", output instanceof IOUState);
IOUState iou = (IOUState) output;
require.using("The IOU's value can't be too high.", iou.getValue() < 100);
return null;
});
}
}
subFlow(new SignTxFlow(otherPartySession, SignTransactionFlow.Companion.tracker()));
return null;
}
}
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlowResponder.java
:language: java
:start-after: DOCSTART 01
:end-before: DOCEND 01
As with the ``IOUFlow``, our ``IOUFlowResponder`` flow is a ``FlowLogic`` subclass where we've overridden
``FlowLogic.call``.

View File

@ -220,6 +220,8 @@ Miscellaneous
apps would not typically select random, unknown counterparties from the network map based on self-declared capabilities.
We will introduce a replacement for this functionality, business networks, in a future release.
For now, your should retrieve the service by legal name using ``NetworkMapCache.getNodeByLegalName``.
Gotchas
^^^^^^^