mirror of
https://github.com/corda/corda.git
synced 2025-01-31 16:35:43 +00:00
Updates tutorials. (#1649)
* Updates tutorials. * Addresses review comments.
This commit is contained in:
parent
569d4494d7
commit
27404005b2
@ -41,7 +41,8 @@ Just as every Corda state must implement the ``ContractState`` interface, every
|
|||||||
|
|
||||||
You can read about function declarations in Kotlin `here <https://kotlinlang.org/docs/reference/functions.html>`_.
|
You can read about function declarations in Kotlin `here <https://kotlinlang.org/docs/reference/functions.html>`_.
|
||||||
|
|
||||||
We can see that ``Contract`` expresses its constraints through a ``verify`` function that takes a transaction as input, and:
|
We can see that ``Contract`` expresses its constraints through a ``verify`` function that takes a transaction as input,
|
||||||
|
and:
|
||||||
|
|
||||||
* Throws an ``IllegalArgumentException`` if it rejects the transaction proposal
|
* Throws an ``IllegalArgumentException`` if it rejects the transaction proposal
|
||||||
* Returns silently if it accepts the transaction proposal
|
* Returns silently if it accepts the transaction proposal
|
||||||
@ -118,7 +119,6 @@ Let's write a contract that enforces these constraints. We'll do this by modifyi
|
|||||||
import net.corda.core.contracts.CommandData;
|
import net.corda.core.contracts.CommandData;
|
||||||
import net.corda.core.contracts.Contract;
|
import net.corda.core.contracts.Contract;
|
||||||
import net.corda.core.transactions.LedgerTransaction;
|
import net.corda.core.transactions.LedgerTransaction;
|
||||||
import net.corda.core.crypto.SecureHash;
|
|
||||||
import net.corda.core.identity.Party;
|
import net.corda.core.identity.Party;
|
||||||
|
|
||||||
import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
|
import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
|
||||||
@ -258,7 +258,5 @@ We've now written an ``IOUContract`` constraining the evolution of each ``IOUSta
|
|||||||
* The ``IOUState`` created by the issuance transaction must have a non-negative value, and the lender and borrower
|
* The ``IOUState`` created by the issuance transaction must have a non-negative value, and the lender and borrower
|
||||||
must be different entities
|
must be different entities
|
||||||
|
|
||||||
Before we move on, make sure you go back and modify ``IOUState`` to point to the new ``IOUContract`` class.
|
|
||||||
|
|
||||||
The final step in the creation of our CorDapp will be to write the ``IOUFlow`` that will allow a node to orchestrate
|
The final step in the creation of our CorDapp will be to write the ``IOUFlow`` that will allow a node to orchestrate
|
||||||
the creation of a new ``IOUState`` on the ledger, while only sharing information on a need-to-know basis.
|
the creation of a new ``IOUState`` on the ledger, while only sharing information on a need-to-know basis.
|
||||||
|
@ -59,15 +59,20 @@ with the following:
|
|||||||
/** The flow logic is encapsulated within the call() method. */
|
/** The flow logic is encapsulated within the call() method. */
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call() {
|
override fun call() {
|
||||||
val notary = serviceHub.networkMapCache.getAnyNotary()
|
// We retrieve the notary identity from the network map.
|
||||||
|
val notary = serviceHub.networkMapCache.notaryIdentities[0]
|
||||||
|
|
||||||
// We create a transaction builder
|
// We create a transaction builder
|
||||||
val txBuilder = TransactionBuilder(notary = notary)
|
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.
|
// We add the items to the builder.
|
||||||
val state = IOUState(iouValue, me, otherParty)
|
txBuilder.withItems(outputContractAndState, cmd)
|
||||||
val cmd = Command(IOUContract.Create(), me.owningKey)
|
|
||||||
txBuilder.withItems(state, cmd)
|
|
||||||
|
|
||||||
// Verifying the transaction.
|
// Verifying the transaction.
|
||||||
txBuilder.verify(serviceHub)
|
txBuilder.verify(serviceHub)
|
||||||
@ -88,6 +93,7 @@ with the following:
|
|||||||
import com.template.contract.IOUContract;
|
import com.template.contract.IOUContract;
|
||||||
import com.template.state.IOUState;
|
import com.template.state.IOUState;
|
||||||
import net.corda.core.contracts.Command;
|
import net.corda.core.contracts.Command;
|
||||||
|
import net.corda.core.contracts.StateAndContract;
|
||||||
import net.corda.core.flows.*;
|
import net.corda.core.flows.*;
|
||||||
import net.corda.core.identity.Party;
|
import net.corda.core.identity.Party;
|
||||||
import net.corda.core.transactions.SignedTransaction;
|
import net.corda.core.transactions.SignedTransaction;
|
||||||
@ -121,17 +127,21 @@ with the following:
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
@Override
|
@Override
|
||||||
public Void call() throws FlowException {
|
public Void call() throws FlowException {
|
||||||
// We retrieve the required identities from the network map.
|
// We retrieve the notary identity from the network map.
|
||||||
final Party notary = getServiceHub().getNetworkMapCache().getAnyNotary(null);
|
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
|
||||||
|
|
||||||
// We create a transaction builder.
|
// We create a transaction builder.
|
||||||
final TransactionBuilder txBuilder = new TransactionBuilder();
|
final TransactionBuilder txBuilder = new TransactionBuilder();
|
||||||
txBuilder.setNotary(notary);
|
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.
|
// We add the items to the builder.
|
||||||
IOUState state = new IOUState(iouValue, me, otherParty);
|
txBuilder.withItems(outputContractAndState, cmd);
|
||||||
Command cmd = new Command(new IOUContract.Create(), me.getOwningKey());
|
|
||||||
txBuilder.withItems(state, cmd);
|
|
||||||
|
|
||||||
// Verifying the transaction.
|
// Verifying the transaction.
|
||||||
txBuilder.verify(getServiceHub());
|
txBuilder.verify(getServiceHub());
|
||||||
@ -200,7 +210,7 @@ the following transaction:
|
|||||||
|
|
||||||
So we'll need the following:
|
So we'll need the following:
|
||||||
|
|
||||||
* The output ``IOUState``
|
* The output ``IOUState`` and its associated contract
|
||||||
* A ``Create`` command listing the IOU's lender as a signer
|
* A ``Create`` command listing the IOU's lender as a signer
|
||||||
|
|
||||||
The command we use pairs the ``IOUContract.Create`` command defined earlier with our public key. Including this command
|
The command we use pairs the ``IOUContract.Create`` command defined earlier with our public key. Including this command
|
||||||
@ -208,8 +218,8 @@ in the transaction makes us one of the transaction's required signers.
|
|||||||
|
|
||||||
We add these items to the transaction using the ``TransactionBuilder.withItems`` method, which takes a ``vararg`` of:
|
We add these items to the transaction using the ``TransactionBuilder.withItems`` method, which takes a ``vararg`` of:
|
||||||
|
|
||||||
* ``ContractState`` or ``TransactionState`` objects, which are added to the builder as output states
|
* ``StateAndContract`` or ``TransactionState`` objects, which are added to the builder as output states
|
||||||
* ``StateRef`` objects (references to the outputs of previous transactions), which are added to the builder as input
|
* ``StateAndRef`` objects (references to the outputs of previous transactions), which are added to the builder as input
|
||||||
state references
|
state references
|
||||||
* ``Command`` objects, which are added to the builder as commands
|
* ``Command`` objects, which are added to the builder as commands
|
||||||
* ``SecureHash`` objects, which are added to the builder as attachments
|
* ``SecureHash`` objects, which are added to the builder as attachments
|
||||||
|
@ -25,30 +25,30 @@ Let's take a look at the nodes we're going to deploy. Open the project's ``build
|
|||||||
|
|
||||||
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
||||||
directory "./build/nodes"
|
directory "./build/nodes"
|
||||||
networkMap "O=Controller,OU=corda,L=London,C=UK"
|
networkMap "O=Controller,L=London,C=GB"
|
||||||
node {
|
node {
|
||||||
name "O=Controller,OU=corda,L=London,C=UK"
|
name "O=Controller,L=London,C=GB"
|
||||||
advertisedServices = ["corda.notary.validating"]
|
advertisedServices = ["corda.notary.validating"]
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcPort 10003
|
rpcPort 10003
|
||||||
cordapps = []
|
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "CN=NodeA,O=NodeA,L=London,C=UK"
|
name "O=PartyA,L=London,C=GB"
|
||||||
advertisedServices = []
|
advertisedServices = []
|
||||||
p2pPort 10005
|
p2pPort 10005
|
||||||
rpcPort 10006
|
rpcPort 10006
|
||||||
webPort 10007
|
webPort 10007
|
||||||
cordapps = []
|
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
||||||
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "CN=NodeB,O=NodeB,L=New York,C=US"
|
name "O=PartyB,L=New York,C=US"
|
||||||
advertisedServices = []
|
advertisedServices = []
|
||||||
p2pPort 10008
|
p2pPort 10008
|
||||||
rpcPort 10009
|
rpcPort 10009
|
||||||
webPort 10010
|
webPort 10010
|
||||||
cordapps = []
|
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
||||||
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,57 +111,72 @@ Now that our nodes are running, let's order one of them to create an IOU by kick
|
|||||||
app, we'd generally provide a web API sitting on top of our node. Here, for simplicity, we'll be interacting with the
|
app, we'd generally provide a web API sitting on top of our node. Here, for simplicity, we'll be interacting with the
|
||||||
node via its built-in CRaSH shell.
|
node via its built-in CRaSH shell.
|
||||||
|
|
||||||
Go to the terminal window displaying the CRaSH shell of Node A. Typing ``help`` will display a list of the available
|
Go to the terminal window displaying the CRaSH shell of PartyA. Typing ``help`` will display a list of the available
|
||||||
commands.
|
commands.
|
||||||
|
|
||||||
We want to create an IOU of 100 with Node B. We start the ``IOUFlow`` by typing:
|
We want to create an IOU of 100 with PartyB. We start the ``IOUFlow`` by typing:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
.. code-block:: java
|
.. code-block:: java
|
||||||
|
|
||||||
start IOUFlow arg0: 99, arg1: "NodeB"
|
start IOUFlow arg0: 99, arg1: "O=PartyB,L=New York,C=US"
|
||||||
|
|
||||||
.. code-block:: kotlin
|
.. code-block:: kotlin
|
||||||
|
|
||||||
start IOUFlow iouValue: 99, otherParty: "NodeB"
|
start IOUFlow iouValue: 99, otherParty: "O=PartyB,L=New York,C=US"
|
||||||
|
|
||||||
Node A and Node B will automatically agree an IOU. If the flow worked, it should have led to the recording of a new IOU
|
PartyA and PartyB will automatically agree an IOU. If the flow worked, it should have led to the recording of a new IOU
|
||||||
in the vaults of both Node A and Node B.
|
in the vaults of both PartyA and PartyB.
|
||||||
|
|
||||||
We can check the flow has worked by using an RPC operation to check the contents of each node's vault. Typing ``run``
|
We can check the flow has worked by using an RPC operation to check the contents of each node's vault. Typing ``run``
|
||||||
will display a list of the available commands. We can examine the contents of a node's vault by running:
|
will display a list of the available commands. We can examine the contents of a node's vault by running:
|
||||||
|
|
||||||
.. code:: python
|
.. container:: codeset
|
||||||
|
|
||||||
run vaultAndUpdates
|
.. code-block:: java
|
||||||
|
|
||||||
And we can also examine a node's transaction storage, by running:
|
run vaultQuery contractStateType: com.template.state.IOUState
|
||||||
|
|
||||||
|
.. code-block:: kotlin
|
||||||
|
|
||||||
|
run vaultQuery contractStateType: com.template.IOUState
|
||||||
|
|
||||||
|
The vaults of PartyA and PartyB should both display the following output:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
run verifiedTransactions
|
states:
|
||||||
|
|
||||||
The vaults of Node A and Node B should both display the following output:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
first:
|
|
||||||
- state:
|
- state:
|
||||||
data:
|
data:
|
||||||
value: 99
|
value: 99
|
||||||
lender: "CN=NodeA,O=NodeA,L=London,C=GB"
|
lender: "C=GB,L=London,O=PartyA"
|
||||||
borrower: "CN=NodeB,O=NodeB,L=New York,C=US"
|
borrower: "C=US,L=New York,O=PartyB"
|
||||||
contract: {}
|
|
||||||
participants:
|
participants:
|
||||||
- "CN=NodeA,O=NodeA,L=London,C=GB"
|
- "C=GB,L=London,O=PartyA"
|
||||||
- "CN=NodeB,O=NodeB,L=New York,C=US"
|
- "C=US,L=New York,O=PartyB"
|
||||||
notary: "O=Controller,OU=corda,L=London,C=GB,OU=corda.notary.validating"
|
contract: "com.template.contract.IOUContract"
|
||||||
|
notary: "C=GB,L=London,O=Controller,CN=corda.notary.validating"
|
||||||
encumbrance: null
|
encumbrance: null
|
||||||
|
constraint:
|
||||||
|
attachmentId: "F578320232CAB87BB1E919F3E5DB9D81B7346F9D7EA6D9155DC0F7BA8E472552"
|
||||||
ref:
|
ref:
|
||||||
txhash: "656A1BF64D5AEEC6F6C944E287F34EF133336F5FC2C5BFB9A0BFAE25E826125F"
|
txhash: "5CED068E790A347B0DD1C6BB5B2B463406807F95E080037208627565E6A2103B"
|
||||||
index: 0
|
index: 0
|
||||||
second: "(observable)"
|
statesMetadata:
|
||||||
|
- ref:
|
||||||
|
txhash: "5CED068E790A347B0DD1C6BB5B2B463406807F95E080037208627565E6A2103B"
|
||||||
|
index: 0
|
||||||
|
contractStateClassName: "com.template.state.IOUState"
|
||||||
|
recordedTime: 1506415268.875000000
|
||||||
|
consumedTime: null
|
||||||
|
status: "UNCONSUMED"
|
||||||
|
notary: "C=GB,L=London,O=Controller,CN=corda.notary.validating"
|
||||||
|
lockId: null
|
||||||
|
lockUpdateTime: 1506415269.548000000
|
||||||
|
totalStatesAvailable: -1
|
||||||
|
stateTypes: "UNCONSUMED"
|
||||||
|
otherResults: []
|
||||||
|
|
||||||
Conclusion
|
Conclusion
|
||||||
----------
|
----------
|
||||||
|
@ -20,9 +20,6 @@ defined as follows:
|
|||||||
.. code-block:: kotlin
|
.. code-block:: kotlin
|
||||||
|
|
||||||
interface ContractState {
|
interface ContractState {
|
||||||
// The contract that imposes constraints on how this state can evolve over time.
|
|
||||||
val contract: Contract
|
|
||||||
|
|
||||||
// The list of entities considered to have a stake in this state.
|
// The list of entities considered to have a stake in this state.
|
||||||
val participants: List<AbstractParty>
|
val participants: List<AbstractParty>
|
||||||
}
|
}
|
||||||
@ -38,13 +35,10 @@ If you do want to dive into Kotlin, there's an official
|
|||||||
`getting started guide <https://kotlinlang.org/docs/tutorials/>`_, and a series of
|
`getting started guide <https://kotlinlang.org/docs/tutorials/>`_, and a series of
|
||||||
`Kotlin Koans <https://kotlinlang.org/docs/tutorials/koans.html>`_.
|
`Kotlin Koans <https://kotlinlang.org/docs/tutorials/koans.html>`_.
|
||||||
|
|
||||||
We can see that the ``ContractState`` interface declares two properties:
|
We can see that the ``ContractState`` interface has a single field, ``participants``. ``participants`` is a list of the
|
||||||
|
entities for which this state is relevant.
|
||||||
|
|
||||||
* ``contract``: the contract controlling transactions involving this state
|
Beyond this, our state is free to define any fields, methods, helpers or inner classes it requires to accurately
|
||||||
* ``participants``: the list of entities that have to approve state changes such as changing the state's notary or
|
|
||||||
upgrading the state's contract
|
|
||||||
|
|
||||||
Beyond this, our state is free to define any properties, methods, helpers or inner classes it requires to accurately
|
|
||||||
represent a given class of shared facts on the ledger.
|
represent a given class of shared facts on the ledger.
|
||||||
|
|
||||||
``ContractState`` also has several child interfaces that you may wish to implement depending on your state, such as
|
``ContractState`` also has several child interfaces that you may wish to implement depending on your state, such as
|
||||||
@ -74,7 +68,6 @@ define an ``IOUState``:
|
|||||||
class IOUState(val value: Int,
|
class IOUState(val value: Int,
|
||||||
val lender: Party,
|
val lender: Party,
|
||||||
val borrower: Party) : ContractState {
|
val borrower: Party) : ContractState {
|
||||||
override val contract = "net.corda.contract.TemplateContract"
|
|
||||||
override val participants get() = listOf(lender, borrower)
|
override val participants get() = listOf(lender, borrower)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +76,6 @@ define an ``IOUState``:
|
|||||||
package com.template.state;
|
package com.template.state;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.template.contract.TemplateContract;
|
|
||||||
import net.corda.core.contracts.ContractState;
|
import net.corda.core.contracts.ContractState;
|
||||||
import net.corda.core.identity.AbstractParty;
|
import net.corda.core.identity.AbstractParty;
|
||||||
import net.corda.core.identity.Party;
|
import net.corda.core.identity.Party;
|
||||||
@ -94,7 +86,6 @@ define an ``IOUState``:
|
|||||||
private final int value;
|
private final int value;
|
||||||
private final Party lender;
|
private final Party lender;
|
||||||
private final Party borrower;
|
private final Party borrower;
|
||||||
private final TemplateContract contract = new TemplateContract();
|
|
||||||
|
|
||||||
public IOUState(int value, Party lender, Party borrower) {
|
public IOUState(int value, Party lender, Party borrower) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
@ -114,12 +105,6 @@ define an ``IOUState``:
|
|||||||
return borrower;
|
return borrower;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
// TODO: Once we've defined IOUContract, come back and update this.
|
|
||||||
public TemplateContract getContract() {
|
|
||||||
return contract;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AbstractParty> getParticipants() {
|
public List<AbstractParty> getParticipants() {
|
||||||
return ImmutableList.of(lender, borrower);
|
return ImmutableList.of(lender, borrower);
|
||||||
@ -141,9 +126,6 @@ We've made the following changes:
|
|||||||
|
|
||||||
* Actions such as changing a state's contract or notary will require approval from all the ``participants``
|
* Actions such as changing a state's contract or notary will require approval from all the ``participants``
|
||||||
|
|
||||||
We've left ``IOUState``'s contract as ``TemplateContract`` for now. We'll update this once we've defined the
|
|
||||||
``IOUContract``.
|
|
||||||
|
|
||||||
Progress so far
|
Progress so far
|
||||||
---------------
|
---------------
|
||||||
We've defined an ``IOUState`` that can be used to represent IOUs as shared facts on the ledger. As we've seen, states in
|
We've defined an ``IOUState`` that can be used to represent IOUs as shared facts on the ledger. As we've seen, states in
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 156 KiB |
@ -29,10 +29,14 @@ In ``IOUFlow.java``/``IOUFlow.kt``, update ``IOUFlow.call`` as follows:
|
|||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
// 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.
|
// We add the items to the builder.
|
||||||
val state = IOUState(iouValue, me, otherParty)
|
txBuilder.withItems(outputContractAndState, cmd)
|
||||||
val cmd = Command(IOUContract.Create(), listOf(me.owningKey, otherParty.owningKey))
|
|
||||||
txBuilder.withItems(state, cmd)
|
|
||||||
|
|
||||||
// Verifying the transaction.
|
// Verifying the transaction.
|
||||||
txBuilder.verify(serviceHub)
|
txBuilder.verify(serviceHub)
|
||||||
@ -40,9 +44,11 @@ In ``IOUFlow.java``/``IOUFlow.kt``, update ``IOUFlow.call`` as follows:
|
|||||||
// Signing the transaction.
|
// Signing the transaction.
|
||||||
val signedTx = serviceHub.signInitialTransaction(txBuilder)
|
val signedTx = serviceHub.signInitialTransaction(txBuilder)
|
||||||
|
|
||||||
// Obtaining the counterparty's signature
|
// Creating a session with the other party.
|
||||||
val otherSession = initiateFlow(otherParty)
|
val otherpartySession = initiateFlow(otherParty)
|
||||||
val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, setOf(otherSession), CollectSignaturesFlow.tracker()))
|
|
||||||
|
// Obtaining the counterparty's signature.
|
||||||
|
val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, listOf(otherpartySession), CollectSignaturesFlow.tracker()))
|
||||||
|
|
||||||
// Finalising the transaction.
|
// Finalising the transaction.
|
||||||
subFlow(FinalityFlow(fullySignedTx))
|
subFlow(FinalityFlow(fullySignedTx))
|
||||||
@ -58,11 +64,15 @@ In ``IOUFlow.java``/``IOUFlow.kt``, update ``IOUFlow.call`` as follows:
|
|||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
// 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.
|
// We add the items to the builder.
|
||||||
IOUState state = new IOUState(iouValue, me, otherParty);
|
txBuilder.withItems(outputContractAndState, cmd);
|
||||||
List<PublicKey> requiredSigners = ImmutableList.of(me.getOwningKey(), otherParty.getOwningKey());
|
|
||||||
Command cmd = new Command(new IOUContract.Create(), requiredSigners);
|
|
||||||
txBuilder.withItems(state, cmd);
|
|
||||||
|
|
||||||
// Verifying the transaction.
|
// Verifying the transaction.
|
||||||
txBuilder.verify(getServiceHub());
|
txBuilder.verify(getServiceHub());
|
||||||
@ -70,20 +80,34 @@ In ``IOUFlow.java``/``IOUFlow.kt``, update ``IOUFlow.call`` as follows:
|
|||||||
// Signing the transaction.
|
// Signing the transaction.
|
||||||
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
|
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
|
||||||
|
|
||||||
// Obtaining the counterparty's signature
|
// Creating a session with the other party.
|
||||||
final FlowSession otherSession = initiateFlow(otherParty)
|
FlowSession otherpartySession = initiateFlow(otherParty);
|
||||||
final SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(signedTx, Collections.singleton(otherSession), CollectSignaturesFlow.Companion.tracker()));
|
|
||||||
|
// Obtaining the counterparty's signature.
|
||||||
|
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
|
||||||
|
signedTx, ImmutableList.of(otherpartySession), CollectSignaturesFlow.tracker()));
|
||||||
|
|
||||||
// Finalising the transaction.
|
// Finalising the transaction.
|
||||||
subFlow(new FinalityFlow(fullySignedTx));
|
subFlow(new FinalityFlow(signedTx));
|
||||||
|
|
||||||
return null;
|
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.
|
||||||
|
|
||||||
``CollectSignaturesFlow``, meanwhile, takes a transaction signed by the flow initiator, and returns a transaction
|
We now need to communicate with the borrower to request their signature. Whenever you want to communicate with another
|
||||||
signed by all the transaction's other required signers. We then pass this fully-signed transaction into
|
party in the context of a flow, you first need to establish a flow session with them. If the counterparty has a
|
||||||
``FinalityFlow``.
|
``FlowLogic`` registered to respond to the ``FlowLogic`` initiating the session, a session will be established. All
|
||||||
|
communication between the two ``FlowLogic`` instances will then place as part of this session.
|
||||||
|
|
||||||
|
Once we have a session with the borrower, we gather the borrower's signature using ``CollectSignaturesFlow``, which
|
||||||
|
takes:
|
||||||
|
|
||||||
|
* A transaction signed by the flow initiator
|
||||||
|
* A list of flow-sessions between the flow initiator and the required signers
|
||||||
|
|
||||||
|
And returns a transaction signed by all the required signers.
|
||||||
|
|
||||||
|
We then pass this fully-signed transaction into ``FinalityFlow``.
|
||||||
|
|
||||||
Creating the borrower's flow
|
Creating the borrower's flow
|
||||||
----------------------------
|
----------------------------
|
||||||
|
Loading…
x
Reference in New Issue
Block a user