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 .. 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
... .. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/helloworld/IOUContract.java
:language: java
import net.corda.core.contracts.* :start-after: DOCSTART 01
:end-before: 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.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;
});
}
}
If you're following along in Java, you'll also need to rename ``TemplateContract.java`` to ``IOUContract.java``. 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 Flows are implemented as ``FlowLogic`` subclasses. You define the steps taken by the flow by overriding
``FlowLogic.call``. ``FlowLogic.call``.
We'll write our flow in either ``TemplateFlow.java`` or ``App.kt``. Overwrite both the existing flows in the template We'll write our flow in either ``TemplateFlow.java`` or ``App.kt``. Delete both the existing flows in the template, and
with the following: replace them with the following:
.. container:: codeset .. 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
... .. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/helloworld/IOUFlow.java
:language: java
import net.corda.core.utilities.ProgressTracker :start-after: DOCSTART 01
import net.corda.core.transactions.TransactionBuilder :end-before: DOCEND 01
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;
}
}
If you're following along in Java, you'll also need to rename ``TemplateFlow.java`` to ``IOUFlow.java``. 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 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 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:: bash
.. code-block:: kotlin
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
directory "./build/nodes" 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: We can do that now by running the following commands from the root of the project:
.. code:: python .. code:: bash
// On Windows // On Windows
gradlew clean deployNodes 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 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: the three node folders. Each node folder has the following structure:
.. code:: python .. code:: bash
. .
|____corda.jar // The runnable node |____corda.jar // The runnable node
|____corda-webserver.jar // The node's webserver |____corda-webserver.jar // The node's webserver
|____dependencies
|____node.conf // The node's configuration file |____node.conf // The node's configuration file
|____plugins |____plugins
|____java/kotlin-source-0.1.jar // Our IOU CorDapp |____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: Let's start the nodes by running the following commands from the root of the project:
.. code:: python .. code:: bash
// On Windows // On Windows
build/nodes/runnodes.bat 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: The vaults of PartyA and PartyB should both display the following output:
.. code:: python .. code:: bash
states: states:
- state: - 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 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 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 CorDapps. You can find a list of sample CorDapps `here <https://www.corda.net/samples/>`_.
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>`.
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/>`_, 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>`_. `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 .. 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, .. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/helloworld/IOUState.java
val lender: Party, :language: java
val borrower: Party) : ContractState { :start-after: DOCSTART 01
override val participants get() = listOf(lender, borrower) :end-before: DOCEND 01
}
.. 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);
}
}
If you're following along in Java, you'll also need to rename ``TemplateState.java`` to ``IOUState.java``. 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 .. code-block:: java
// 1. The state // 1. The state
src/main/java/com/template/state/TemplateState.java src/main/java/com/template/TemplateState.java
// 2. The contract // 2. The contract
src/main/java/com/template/contract/TemplateContract.java src/main/java/com/template/TemplateContract.java
// 3. The flow // 3. The flow
src/main/java/com/template/flow/TemplateFlow.java src/main/java/com/template/TemplateFlow.java
.. code-block:: kotlin .. code-block:: kotlin
src/main/kotlin/com/template/App.kt 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 Progress so far
--------------- ---------------
We now have a template that we can build upon to define our IOU CorDapp. 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 The ``NodeAttachmentService`` provides an implementation of the
``AttachmentStorage`` interface exposed on the ``ServiceHub`` allowing ``AttachmentStorage`` interface exposed on the ``ServiceHub`` allowing
transactions to add documents, copies of the contract code and binary transactions to add documents, copies of the contract code and binary
data to transactions. The data is persisted to the local file system data to transactions. The service is also interfaced to by the web server,
inside the attachments subfolder of the node workspace. The service is which allows files to be uploaded via an HTTP post request.
also interfaced to by the web server, which allows files to be uploaded
via an HTTP post request.
Flow framework and event scheduling services 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 If the transaction does not obey the constraints of all the contracts of all its states, it cannot become a valid
ledger update. ledger update.
We need to modify our contract so that the borrower's signature is required in any IOU creation transaction. This will We need to modify our contract so that the borrower's signature is required in any IOU creation transaction.
only require changing a single line of code. In ``IOUContract.java``/``IOUContract.kt``, update the final two lines of
the ``requireThat`` block as follows: In ``IOUContract.java``/``IOUContract.kt``, change the imports block to the following:
.. container:: codeset .. 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. .. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUContract.java
"There must be two signers." using (command.signers.toSet().size == 2) :language: java
"The borrower and lender must be signers." using (command.signers.containsAll(listOf( :start-after: DOCSTART 01
out.borrower.owningKey, out.lender.owningKey))) :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
... .. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUContract.java
:language: java
// Constraints on the signers. :start-after: DOCSTART 02
check.using("There must be two signers.", command.getSigners().size() == 2); :end-before: DOCEND 02
check.using("The borrower and lender must be signers.", command.getSigners().containsAll( :dedent: 12
ImmutableList.of(borrower.getOwningKey(), lender.getOwningKey())));
Progress so far 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 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. 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 .. 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. And update ``IOUFlow.call`` by changing the code following the creation of the ``TransactionBuilder`` as follows:
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. .. container:: codeset
txBuilder.withItems(outputContractAndState, cmd)
// Verifying the transaction. .. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/twoparty/flow.kt
txBuilder.verify(serviceHub) :language: kotlin
:start-after: DOCSTART 02
:end-before: DOCEND 02
:dedent: 8
// Signing the transaction. .. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlow.java
val signedTx = serviceHub.signInitialTransaction(txBuilder) :language: java
:start-after: DOCSTART 02
// Creating a session with the other party. :end-before: DOCEND 02
val otherpartySession = initiateFlow(otherParty) :dedent: 8
// 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;
To make the borrower a required signer, we simply add the borrower's public key to the list of signers on the command. 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 .. 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
... .. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlowResponder.java
:language: java
import net.corda.core.transactions.SignedTransaction :start-after: DOCSTART 01
:end-before: DOCEND 01
...
@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;
}
}
As with the ``IOUFlow``, our ``IOUFlowResponder`` flow is a ``FlowLogic`` subclass where we've overridden As with the ``IOUFlow``, our ``IOUFlowResponder`` flow is a ``FlowLogic`` subclass where we've overridden
``FlowLogic.call``. ``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. 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. 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 Gotchas
^^^^^^^ ^^^^^^^