mirror of
https://github.com/corda/corda.git
synced 2024-12-24 15:16:45 +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
|
||||
|
||||
.. code-block:: kotlin
|
||||
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/helloworld/contract.kt
|
||||
:language: kotlin
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
...
|
||||
|
||||
import net.corda.core.contracts.*
|
||||
|
||||
...
|
||||
|
||||
class IOUContract : Contract {
|
||||
// Our Create command.
|
||||
class Create : CommandData
|
||||
|
||||
override fun verify(tx: LedgerTransaction) {
|
||||
val command = tx.commands.requireSingleCommand<Create>()
|
||||
|
||||
requireThat {
|
||||
// Constraints on the shape of the transaction.
|
||||
"No inputs should be consumed when issuing an IOU." using (tx.inputs.isEmpty())
|
||||
"There should be one output state of type IOUState." using (tx.outputs.size == 1)
|
||||
|
||||
// IOU-specific constraints.
|
||||
val out = tx.outputs.single().data as IOUState
|
||||
"The IOU's value must be non-negative." using (out.value > 0)
|
||||
"The lender and the borrower cannot be the same entity." using (out.lender != out.borrower)
|
||||
|
||||
// Constraints on the signers.
|
||||
"There must only be one signer." using (command.signers.toSet().size == 1)
|
||||
"The signer must be the lender." using (command.signers.contains(out.lender.owningKey))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
package com.template.contract;
|
||||
|
||||
import com.template.state.IOUState;
|
||||
import net.corda.core.contracts.CommandWithParties;
|
||||
import net.corda.core.contracts.CommandData;
|
||||
import net.corda.core.contracts.Contract;
|
||||
import net.corda.core.transactions.LedgerTransaction;
|
||||
import net.corda.core.identity.Party;
|
||||
|
||||
import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
|
||||
import static net.corda.core.contracts.ContractsDSL.requireThat;
|
||||
|
||||
public class IOUContract implements Contract {
|
||||
// Our Create command.
|
||||
public static class Create implements CommandData {}
|
||||
|
||||
@Override
|
||||
public void verify(LedgerTransaction tx) {
|
||||
final CommandWithParties<Create> command = requireSingleCommand(tx.getCommands(), Create.class);
|
||||
|
||||
requireThat(check -> {
|
||||
// Constraints on the shape of the transaction.
|
||||
check.using("No inputs should be consumed when issuing an IOU.", tx.getInputs().isEmpty());
|
||||
check.using("There should be one output state of type IOUState.", tx.getOutputs().size() == 1);
|
||||
|
||||
// IOU-specific constraints.
|
||||
final IOUState out = (IOUState) tx.getOutputs().get(0).getData();
|
||||
final Party lender = out.getLender();
|
||||
final Party borrower = out.getBorrower();
|
||||
check.using("The IOU's value must be non-negative.",out.getValue() > 0);
|
||||
check.using("The lender and the borrower cannot be the same entity.", lender != borrower);
|
||||
|
||||
// Constraints on the signers.
|
||||
check.using("There must only be one signer.", command.getSigners().size() == 1);
|
||||
check.using("The signer must be the lender.", command.getSigners().contains(lender.getOwningKey()));
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/helloworld/IOUContract.java
|
||||
:language: java
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
If you're following along in Java, you'll also need to rename ``TemplateContract.java`` to ``IOUContract.java``.
|
||||
|
||||
|
@ -33,128 +33,20 @@ FlowLogic
|
||||
Flows are implemented as ``FlowLogic`` subclasses. You define the steps taken by the flow by overriding
|
||||
``FlowLogic.call``.
|
||||
|
||||
We'll write our flow in either ``TemplateFlow.java`` or ``App.kt``. Overwrite both the existing flows in the template
|
||||
with the following:
|
||||
We'll write our flow in either ``TemplateFlow.java`` or ``App.kt``. Delete both the existing flows in the template, and
|
||||
replace them with the following:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. code-block:: kotlin
|
||||
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/helloworld/flow.kt
|
||||
:language: kotlin
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
...
|
||||
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.flows.*
|
||||
|
||||
...
|
||||
|
||||
@InitiatingFlow
|
||||
@StartableByRPC
|
||||
class IOUFlow(val iouValue: Int,
|
||||
val otherParty: Party) : FlowLogic<Unit>() {
|
||||
|
||||
/** The progress tracker provides checkpoints indicating the progress of the flow to observers. */
|
||||
override val progressTracker = ProgressTracker()
|
||||
|
||||
/** The flow logic is encapsulated within the call() method. */
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
// We retrieve the notary identity from the network map.
|
||||
val notary = serviceHub.networkMapCache.notaryIdentities[0]
|
||||
|
||||
// We create a transaction builder
|
||||
val txBuilder = TransactionBuilder(notary = notary)
|
||||
|
||||
// We create the transaction components.
|
||||
val outputState = IOUState(iouValue, ourIdentity, otherParty)
|
||||
val outputContract = IOUContract::class.jvmName
|
||||
val outputContractAndState = StateAndContract(outputState, outputContract)
|
||||
val cmd = Command(IOUContract.Create(), ourIdentity.owningKey)
|
||||
|
||||
// We add the items to the builder.
|
||||
txBuilder.withItems(outputContractAndState, cmd)
|
||||
|
||||
// Verifying the transaction.
|
||||
txBuilder.verify(serviceHub)
|
||||
|
||||
// Signing the transaction.
|
||||
val signedTx = serviceHub.signInitialTransaction(txBuilder)
|
||||
|
||||
// Finalising the transaction.
|
||||
subFlow(FinalityFlow(signedTx))
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
package com.template.flow;
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable;
|
||||
import com.template.contract.IOUContract;
|
||||
import com.template.state.IOUState;
|
||||
import net.corda.core.contracts.Command;
|
||||
import net.corda.core.contracts.StateAndContract;
|
||||
import net.corda.core.flows.*;
|
||||
import net.corda.core.identity.Party;
|
||||
import net.corda.core.transactions.SignedTransaction;
|
||||
import net.corda.core.transactions.TransactionBuilder;
|
||||
import net.corda.core.utilities.ProgressTracker;
|
||||
|
||||
@InitiatingFlow
|
||||
@StartableByRPC
|
||||
public class IOUFlow extends FlowLogic<Void> {
|
||||
private final Integer iouValue;
|
||||
private final Party otherParty;
|
||||
|
||||
/**
|
||||
* The progress tracker provides checkpoints indicating the progress of the flow to observers.
|
||||
*/
|
||||
private final ProgressTracker progressTracker = new ProgressTracker();
|
||||
|
||||
public IOUFlow(Integer iouValue, Party otherParty) {
|
||||
this.iouValue = iouValue;
|
||||
this.otherParty = otherParty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgressTracker getProgressTracker() {
|
||||
return progressTracker;
|
||||
}
|
||||
|
||||
/**
|
||||
* The flow logic is encapsulated within the call() method.
|
||||
*/
|
||||
@Suspendable
|
||||
@Override
|
||||
public Void call() throws FlowException {
|
||||
// We retrieve the notary identity from the network map.
|
||||
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
|
||||
|
||||
// We create a transaction builder.
|
||||
final TransactionBuilder txBuilder = new TransactionBuilder();
|
||||
txBuilder.setNotary(notary);
|
||||
|
||||
// We create the transaction components.
|
||||
IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
|
||||
String outputContract = IOUContract.class.getName();
|
||||
StateAndContract outputContractAndState = new StateAndContract(outputState, outputContract);
|
||||
Command cmd = new Command<>(new IOUContract.Create(), getOurIdentity().getOwningKey());
|
||||
|
||||
// We add the items to the builder.
|
||||
txBuilder.withItems(outputContractAndState, cmd);
|
||||
|
||||
// Verifying the transaction.
|
||||
txBuilder.verify(getServiceHub());
|
||||
|
||||
// Signing the transaction.
|
||||
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
|
||||
|
||||
// Finalising the transaction.
|
||||
subFlow(new FinalityFlow(signedTx));
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/helloworld/IOUFlow.java
|
||||
:language: java
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
If you're following along in Java, you'll also need to rename ``TemplateFlow.java`` to ``IOUFlow.java``.
|
||||
|
||||
|
@ -17,11 +17,9 @@ Kotlin) file. We won't be using it, and it will cause build errors unless we rem
|
||||
Deploying our CorDapp
|
||||
---------------------
|
||||
Let's take a look at the nodes we're going to deploy. Open the project's ``build.gradle`` file and scroll down to the
|
||||
``task deployNodes`` section. This section defines three nodes - the Controller, NodeA, and NodeB:
|
||||
``task deployNodes`` section. This section defines three nodes - the Controller, PartyA, and PartyB:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. code-block:: kotlin
|
||||
.. code:: bash
|
||||
|
||||
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
||||
directory "./build/nodes"
|
||||
@ -64,7 +62,7 @@ We can run this ``deployNodes`` task using Gradle. For each node definition, Gra
|
||||
|
||||
We can do that now by running the following commands from the root of the project:
|
||||
|
||||
.. code:: python
|
||||
.. code:: bash
|
||||
|
||||
// On Windows
|
||||
gradlew clean deployNodes
|
||||
@ -77,19 +75,18 @@ Running the nodes
|
||||
Running ``deployNodes`` will build the nodes under ``build/nodes``. If we navigate to one of these folders, we'll see
|
||||
the three node folders. Each node folder has the following structure:
|
||||
|
||||
.. code:: python
|
||||
.. code:: bash
|
||||
|
||||
.
|
||||
|____corda.jar // The runnable node
|
||||
|____corda-webserver.jar // The node's webserver
|
||||
|____dependencies
|
||||
|____node.conf // The node's configuration file
|
||||
|____plugins
|
||||
|____java/kotlin-source-0.1.jar // Our IOU CorDapp
|
||||
|
||||
Let's start the nodes by running the following commands from the root of the project:
|
||||
|
||||
.. code:: python
|
||||
.. code:: bash
|
||||
|
||||
// On Windows
|
||||
build/nodes/runnodes.bat
|
||||
@ -138,7 +135,7 @@ will display a list of the available commands. We can examine the contents of a
|
||||
|
||||
The vaults of PartyA and PartyB should both display the following output:
|
||||
|
||||
.. code:: python
|
||||
.. code:: bash
|
||||
|
||||
states:
|
||||
- state:
|
||||
@ -194,11 +191,9 @@ There are a number of improvements we could make to this CorDapp:
|
||||
* We could add an API, to make it easier to interact with the CorDapp
|
||||
|
||||
We will explore some of these improvements in future tutorials. But you should now be ready to develop your own
|
||||
CorDapps. There's `a more fleshed-out version of the IOU CorDapp <https://github.com/corda/cordapp-example>`_ with an
|
||||
API and web front-end, and a set of example CorDapps in `the main Corda repo <https://github.com/corda/corda>`_, under
|
||||
``samples``. An explanation of how to run these samples :doc:`here <running-the-demos>`.
|
||||
CorDapps. You can find a list of sample CorDapps `here <https://www.corda.net/samples/>`_.
|
||||
|
||||
As you write CorDapps, you can learn more about the API available :doc:`here <api>`.
|
||||
As you write CorDapps, you can learn more about the Corda API :doc:`here <corda-api>`.
|
||||
|
||||
If you get stuck at any point, please reach out on `Slack <https://slack.corda.net/>`_,
|
||||
`Discourse <https://discourse.corda.net/>`_, or `Stack Overflow <https://stackoverflow.com/questions/tagged/corda>`_.
|
||||
|
@ -63,53 +63,15 @@ define an ``IOUState``:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. code-block:: kotlin
|
||||
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/helloworld/state.kt
|
||||
:language: kotlin
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
class IOUState(val value: Int,
|
||||
val lender: Party,
|
||||
val borrower: Party) : ContractState {
|
||||
override val participants get() = listOf(lender, borrower)
|
||||
}
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
package com.template.state;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.corda.core.contracts.ContractState;
|
||||
import net.corda.core.identity.AbstractParty;
|
||||
import net.corda.core.identity.Party;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class IOUState implements ContractState {
|
||||
private final int value;
|
||||
private final Party lender;
|
||||
private final Party borrower;
|
||||
|
||||
public IOUState(int value, Party lender, Party borrower) {
|
||||
this.value = value;
|
||||
this.lender = lender;
|
||||
this.borrower = borrower;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Party getLender() {
|
||||
return lender;
|
||||
}
|
||||
|
||||
public Party getBorrower() {
|
||||
return borrower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AbstractParty> getParticipants() {
|
||||
return ImmutableList.of(lender, borrower);
|
||||
}
|
||||
}
|
||||
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/helloworld/IOUState.java
|
||||
:language: java
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
If you're following along in Java, you'll also need to rename ``TemplateState.java`` to ``IOUState.java``.
|
||||
|
||||
|
@ -42,18 +42,23 @@ implement our IOU CorDapp in Java, we'll need to modify three files. For Kotlin,
|
||||
.. code-block:: java
|
||||
|
||||
// 1. The state
|
||||
src/main/java/com/template/state/TemplateState.java
|
||||
src/main/java/com/template/TemplateState.java
|
||||
|
||||
// 2. The contract
|
||||
src/main/java/com/template/contract/TemplateContract.java
|
||||
src/main/java/com/template/TemplateContract.java
|
||||
|
||||
// 3. The flow
|
||||
src/main/java/com/template/flow/TemplateFlow.java
|
||||
src/main/java/com/template/TemplateFlow.java
|
||||
|
||||
.. code-block:: kotlin
|
||||
|
||||
src/main/kotlin/com/template/App.kt
|
||||
|
||||
To prevent build errors later on, you should delete the following file:
|
||||
|
||||
* Java: ``src/test/java/com/template/FlowTests.java``
|
||||
* Kotlin: ``src/test/kotlin/com/template/FlowTests.kt``
|
||||
|
||||
Progress so far
|
||||
---------------
|
||||
We now have a template that we can build upon to define our IOU CorDapp.
|
||||
|
@ -209,10 +209,8 @@ NodeAttachmentService
|
||||
The ``NodeAttachmentService`` provides an implementation of the
|
||||
``AttachmentStorage`` interface exposed on the ``ServiceHub`` allowing
|
||||
transactions to add documents, copies of the contract code and binary
|
||||
data to transactions. The data is persisted to the local file system
|
||||
inside the attachments subfolder of the node workspace. The service is
|
||||
also interfaced to by the web server, which allows files to be uploaded
|
||||
via an HTTP post request.
|
||||
data to transactions. The service is also interfaced to by the web server,
|
||||
which allows files to be uploaded via an HTTP post request.
|
||||
|
||||
Flow framework and event scheduling services
|
||||
--------------------------------------------
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 363 KiB After Width: | Height: | Size: 282 KiB |
@ -11,31 +11,37 @@ Remember that each state references a contract. The contract imposes constraints
|
||||
If the transaction does not obey the constraints of all the contracts of all its states, it cannot become a valid
|
||||
ledger update.
|
||||
|
||||
We need to modify our contract so that the borrower's signature is required in any IOU creation transaction. This will
|
||||
only require changing a single line of code. In ``IOUContract.java``/``IOUContract.kt``, update the final two lines of
|
||||
the ``requireThat`` block as follows:
|
||||
We need to modify our contract so that the borrower's signature is required in any IOU creation transaction.
|
||||
|
||||
In ``IOUContract.java``/``IOUContract.kt``, change the imports block to the following:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. code-block:: kotlin
|
||||
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/twoparty/contract.kt
|
||||
:language: kotlin
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
// Constraints on the signers.
|
||||
"There must be two signers." using (command.signers.toSet().size == 2)
|
||||
"The borrower and lender must be signers." using (command.signers.containsAll(listOf(
|
||||
out.borrower.owningKey, out.lender.owningKey)))
|
||||
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUContract.java
|
||||
:language: java
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
.. code-block:: java
|
||||
And update the final block of constraints in the ``requireThat`` block as follows:
|
||||
|
||||
...
|
||||
.. container:: codeset
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/twoparty/contract.kt
|
||||
:language: kotlin
|
||||
:start-after: DOCSTART 02
|
||||
:end-before: DOCEND 02
|
||||
:dedent: 12
|
||||
|
||||
...
|
||||
|
||||
// Constraints on the signers.
|
||||
check.using("There must be two signers.", command.getSigners().size() == 2);
|
||||
check.using("The borrower and lender must be signers.", command.getSigners().containsAll(
|
||||
ImmutableList.of(borrower.getOwningKey(), lender.getOwningKey())));
|
||||
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUContract.java
|
||||
:language: java
|
||||
:start-after: DOCSTART 02
|
||||
:end-before: DOCEND 02
|
||||
:dedent: 12
|
||||
|
||||
Progress so far
|
||||
---------------
|
||||
|
@ -21,76 +21,35 @@ by invoking a built-in flow called ``FinalityFlow`` as a subflow. We're going to
|
||||
We also need to add the borrower's public key to the transaction's command, making the borrower one of the required
|
||||
signers on the transaction.
|
||||
|
||||
In ``IOUFlow.java``/``IOUFlow.kt``, update ``IOUFlow.call`` as follows:
|
||||
In ``IOUFlow.java``/``IOUFlow.kt``, change the imports block to the following:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. code-block:: kotlin
|
||||
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/twoparty/flow.kt
|
||||
:language: kotlin
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
...
|
||||
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlow.java
|
||||
:language: java
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
// We create the transaction components.
|
||||
val outputState = IOUState(iouValue, ourIdentity, otherParty)
|
||||
val outputContract = IOUContract::class.jvmName
|
||||
val outputContractAndState = StateAndContract(outputState, outputContract)
|
||||
val cmd = Command(IOUContract.Create(), listOf(ourIdentity.owningKey, otherParty.owningKey))
|
||||
And update ``IOUFlow.call`` by changing the code following the creation of the ``TransactionBuilder`` as follows:
|
||||
|
||||
// We add the items to the builder.
|
||||
txBuilder.withItems(outputContractAndState, cmd)
|
||||
.. container:: codeset
|
||||
|
||||
// Verifying the transaction.
|
||||
txBuilder.verify(serviceHub)
|
||||
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/twoparty/flow.kt
|
||||
:language: kotlin
|
||||
:start-after: DOCSTART 02
|
||||
:end-before: DOCEND 02
|
||||
:dedent: 8
|
||||
|
||||
// Signing the transaction.
|
||||
val signedTx = serviceHub.signInitialTransaction(txBuilder)
|
||||
|
||||
// Creating a session with the other party.
|
||||
val otherpartySession = initiateFlow(otherParty)
|
||||
|
||||
// Obtaining the counterparty's signature.
|
||||
val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, listOf(otherpartySession), CollectSignaturesFlow.tracker()))
|
||||
|
||||
// Finalising the transaction.
|
||||
subFlow(FinalityFlow(fullySignedTx))
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
...
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
...
|
||||
|
||||
// We create the transaction components.
|
||||
IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
|
||||
String outputContract = IOUContract.class.getName();
|
||||
StateAndContract outputContractAndState = new StateAndContract(outputState, outputContract);
|
||||
List<PublicKey> requiredSigners = ImmutableList.of(getOurIdentity().getOwningKey(), otherParty.getOwningKey());
|
||||
Command cmd = new Command<>(new IOUContract.Create(), requiredSigners);
|
||||
|
||||
// We add the items to the builder.
|
||||
txBuilder.withItems(outputContractAndState, cmd);
|
||||
|
||||
// Verifying the transaction.
|
||||
txBuilder.verify(getServiceHub());
|
||||
|
||||
// Signing the transaction.
|
||||
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
|
||||
|
||||
// Creating a session with the other party.
|
||||
FlowSession otherpartySession = initiateFlow(otherParty);
|
||||
|
||||
// Obtaining the counterparty's signature.
|
||||
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
|
||||
signedTx, ImmutableList.of(otherpartySession), CollectSignaturesFlow.tracker()));
|
||||
|
||||
// Finalising the transaction.
|
||||
subFlow(new FinalityFlow(signedTx));
|
||||
|
||||
return null;
|
||||
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlow.java
|
||||
:language: java
|
||||
:start-after: DOCSTART 02
|
||||
:end-before: DOCEND 02
|
||||
:dedent: 8
|
||||
|
||||
To make the borrower a required signer, we simply add the borrower's public key to the list of signers on the command.
|
||||
|
||||
@ -116,81 +75,15 @@ In a new ``IOUFlowResponder.java`` file in Java, or within the ``App.kt`` file i
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. code-block:: kotlin
|
||||
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/twoparty/flowResponder.kt
|
||||
:language: kotlin
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
...
|
||||
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
|
||||
...
|
||||
|
||||
@InitiatedBy(IOUFlow::class)
|
||||
class IOUFlowResponder(val otherPartySession: FlowSession) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
val signTransactionFlow = object : SignTransactionFlow(otherPartySession, SignTransactionFlow.tracker()) {
|
||||
override fun checkTransaction(stx: SignedTransaction) = requireThat {
|
||||
val output = stx.tx.outputs.single().data
|
||||
"This must be an IOU transaction." using (output is IOUState)
|
||||
val iou = output as IOUState
|
||||
"The IOU's value can't be too high." using (iou.value < 100)
|
||||
}
|
||||
}
|
||||
|
||||
subFlow(signTransactionFlow)
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
package com.template.flow;
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable;
|
||||
import com.template.state.IOUState;
|
||||
import net.corda.core.contracts.ContractState;
|
||||
import net.corda.core.flows.FlowException;
|
||||
import net.corda.core.flows.FlowLogic;
|
||||
import net.corda.core.flows.FlowSession;
|
||||
import net.corda.core.flows.InitiatedBy;
|
||||
import net.corda.core.flows.SignTransactionFlow;
|
||||
import net.corda.core.transactions.SignedTransaction;
|
||||
import net.corda.core.utilities.ProgressTracker;
|
||||
|
||||
import static net.corda.core.contracts.ContractsDSL.requireThat;
|
||||
|
||||
@InitiatedBy(IOUFlow.class)
|
||||
public class IOUFlowResponder extends FlowLogic<Void> {
|
||||
private final FlowSession otherPartySession;
|
||||
|
||||
public IOUFlowResponder(FlowSession otherPartySession) {
|
||||
this.otherPartySession = otherPartySession;
|
||||
}
|
||||
|
||||
@Suspendable
|
||||
@Override
|
||||
public Void call() throws FlowException {
|
||||
class SignTxFlow extends SignTransactionFlow {
|
||||
private signTxFlow(FlowSession otherPartySession, ProgressTracker progressTracker) {
|
||||
super(otherPartySession, progressTracker);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkTransaction(SignedTransaction stx) {
|
||||
requireThat(require -> {
|
||||
ContractState output = stx.getTx().getOutputs().get(0).getData();
|
||||
require.using("This must be an IOU transaction.", output instanceof IOUState);
|
||||
IOUState iou = (IOUState) output;
|
||||
require.using("The IOU's value can't be too high.", iou.getValue() < 100);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
subFlow(new SignTxFlow(otherPartySession, SignTransactionFlow.Companion.tracker()));
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlowResponder.java
|
||||
:language: java
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
As with the ``IOUFlow``, our ``IOUFlowResponder`` flow is a ``FlowLogic`` subclass where we've overridden
|
||||
``FlowLogic.call``.
|
||||
|
@ -20,4 +20,4 @@ IOU onto the ledger. We'll need to make two changes:
|
||||
signature (as well as the lender's) to become valid ledger updates
|
||||
* The ``IOUFlow`` will need to be updated to allow for the gathering of the borrower's signature
|
||||
|
||||
We'll start by updating the contract.
|
||||
We'll start by updating the contract.
|
@ -220,6 +220,8 @@ Miscellaneous
|
||||
apps would not typically select random, unknown counterparties from the network map based on self-declared capabilities.
|
||||
We will introduce a replacement for this functionality, business networks, in a future release.
|
||||
|
||||
For now, your should retrieve the service by legal name using ``NetworkMapCache.getNodeByLegalName``.
|
||||
|
||||
Gotchas
|
||||
^^^^^^^
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user