mirror of
https://github.com/corda/corda.git
synced 2024-12-20 13:33:12 +00:00
Outdated diagram, new service upgrade notes, extracts tut code from files.
This commit is contained in:
parent
ea36cf12e6
commit
0d5ac6f6d8
@ -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
|
@ -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
|
@ -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
|
@ -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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
@ -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)
|
||||||
|
}
|
@ -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``.
|
||||||
|
|
||||||
|
@ -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``.
|
||||||
|
|
||||||
|
@ -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>`_.
|
||||||
|
@ -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``.
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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 |
@ -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
|
||||||
---------------
|
---------------
|
||||||
|
@ -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``.
|
||||||
|
@ -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
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user