mirror of
https://github.com/corda/corda.git
synced 2025-06-01 07:00:54 +00:00
Updates tx API page and makes corresponding cookbook changes. (#1624)
* Updates tx API page and makes corresponding cookbook changes. * Addresses review comments. * Addresses review comments. * Addresses review comments.
This commit is contained in:
parent
54ad89e094
commit
39bc25e74d
@ -9,64 +9,63 @@ API: Transactions
|
|||||||
|
|
||||||
.. note:: Before reading this page, you should be familiar with the key concepts of :doc:`key-concepts-transactions`.
|
.. note:: Before reading this page, you should be familiar with the key concepts of :doc:`key-concepts-transactions`.
|
||||||
|
|
||||||
Transaction workflow
|
.. contents::
|
||||||
--------------------
|
|
||||||
At any time, a transaction can occupy one of three states:
|
|
||||||
|
|
||||||
* ``TransactionBuilder``, a builder for an in-construction transaction
|
Transaction lifecycle
|
||||||
* ``SignedTransaction``, an immutable transaction with 1+ associated signatures
|
---------------------
|
||||||
* ``LedgerTransaction``, an immutable transaction that can be checked for validity
|
Between its creation and its final inclusion on the ledger, a transaction will generally occupy one of three states:
|
||||||
|
|
||||||
Here are the possible transitions between transaction states:
|
* ``TransactionBuilder``. A transaction's initial state. This is the only state during which the transaction is
|
||||||
|
mutable, so we must add all the required components before moving on.
|
||||||
|
|
||||||
|
* ``SignedTransaction``. The transaction now has one or more digital signatures, making it immutable. This is the
|
||||||
|
transaction type that is passed around to collect additional signatures and that is recorded on the ledger.
|
||||||
|
|
||||||
|
* ``LedgerTransaction``. The transaction has been "resolved" - for example, its inputs have been converted from
|
||||||
|
references to actual states - allowing the transaction to be fully inspected.
|
||||||
|
|
||||||
|
We can visualise the transitions between the three stages as follows:
|
||||||
|
|
||||||
.. image:: resources/transaction-flow.png
|
.. image:: resources/transaction-flow.png
|
||||||
|
|
||||||
TransactionBuilder
|
|
||||||
------------------
|
|
||||||
Creating a builder
|
|
||||||
^^^^^^^^^^^^^^^^^^
|
|
||||||
The first step when creating a new transaction is to instantiate a ``TransactionBuilder``. We create a builder for a
|
|
||||||
transaction as follows:
|
|
||||||
|
|
||||||
.. container:: codeset
|
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
|
||||||
:language: kotlin
|
|
||||||
:start-after: DOCSTART 19
|
|
||||||
:end-before: DOCEND 19
|
|
||||||
:dedent: 12
|
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
|
||||||
:language: java
|
|
||||||
:start-after: DOCSTART 19
|
|
||||||
:end-before: DOCEND 19
|
|
||||||
:dedent: 12
|
|
||||||
|
|
||||||
Transaction components
|
Transaction components
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
----------------------
|
||||||
Once we have a ``TransactionBuilder``, we need to gather together the various transaction components the transaction
|
A transaction consists of six types of components:
|
||||||
will include.
|
|
||||||
|
* 1+ states:
|
||||||
|
|
||||||
|
* 0+ input states
|
||||||
|
* 0+ output states
|
||||||
|
|
||||||
|
* 1+ commands
|
||||||
|
* 0+ attachments
|
||||||
|
* 0 or 1 time-window
|
||||||
|
|
||||||
|
* A transaction with a time-window must also have a notary
|
||||||
|
|
||||||
|
Each component corresponds to a specific class in the Corda API. The following section describes each component class,
|
||||||
|
and how it is created.
|
||||||
|
|
||||||
Input states
|
Input states
|
||||||
~~~~~~~~~~~~
|
^^^^^^^^^^^^
|
||||||
Input states are added to a transaction as ``StateAndRef`` instances. A ``StateAndRef`` combines:
|
An input state is added to a transaction as a ``StateAndRef``, which combines:
|
||||||
|
|
||||||
* A ``ContractState`` representing the input state itself
|
* The ``ContractState`` itself
|
||||||
* A ``StateRef`` pointing to the input among the outputs of the transaction that created it
|
* A ``StateRef`` identifying this ``ContractState`` as the output of a specific transaction
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
:language: kotlin
|
:language: kotlin
|
||||||
:start-after: DOCSTART 21
|
:start-after: DOCSTART 21
|
||||||
:end-before: DOCEND 21
|
:end-before: DOCEND 21
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
:language: java
|
:language: java
|
||||||
:start-after: DOCSTART 21
|
:start-after: DOCSTART 21
|
||||||
:end-before: DOCEND 21
|
:end-before: DOCEND 21
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
A ``StateRef`` uniquely identifies an input state, allowing the notary to mark it as historic. It is made up of:
|
A ``StateRef`` uniquely identifies an input state, allowing the notary to mark it as historic. It is made up of:
|
||||||
|
|
||||||
@ -76,151 +75,218 @@ A ``StateRef`` uniquely identifies an input state, allowing the notary to mark i
|
|||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
:language: kotlin
|
:language: kotlin
|
||||||
:start-after: DOCSTART 20
|
:start-after: DOCSTART 20
|
||||||
:end-before: DOCEND 20
|
:end-before: DOCEND 20
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
:language: java
|
:language: java
|
||||||
:start-after: DOCSTART 20
|
:start-after: DOCSTART 20
|
||||||
:end-before: DOCEND 20
|
:end-before: DOCEND 20
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
The ``StateRef`` create a chain of pointers from the input states back to the transactions that created them. This
|
The ``StateRef`` links an input state back to the transaction that created it. This means that transactions form
|
||||||
allows a node to work backwards and verify the entirety of the transaction chain.
|
"chains" linking each input back to an original issuance transaction. This allows nodes verifying the transaction
|
||||||
|
to "walk the chain" and verify that each input was generated through a valid sequence of transactions.
|
||||||
|
|
||||||
Output states
|
Output states
|
||||||
~~~~~~~~~~~~~
|
^^^^^^^^^^^^^
|
||||||
Since a transaction's output states do not exist until the transaction is committed, they cannot be referenced as the
|
Since a transaction's output states do not exist until the transaction is committed, they cannot be referenced as the
|
||||||
outputs of previous transactions. Instead, we create the desired output states as ``ContractState`` instances, and
|
outputs of previous transactions. Instead, we create the desired output states as ``ContractState`` instances, and
|
||||||
add them to the transaction:
|
add them to the transaction directly:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
:language: kotlin
|
:language: kotlin
|
||||||
:start-after: DOCSTART 22
|
:start-after: DOCSTART 22
|
||||||
:end-before: DOCEND 22
|
:end-before: DOCEND 22
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
:language: java
|
:language: java
|
||||||
:start-after: DOCSTART 22
|
:start-after: DOCSTART 22
|
||||||
:end-before: DOCEND 22
|
:end-before: DOCEND 22
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
In many cases (e.g. when we have a transaction that updates an existing state), we may want to create an output by
|
In cases where an output state represents an update of an input state, we may want to create the output state by basing
|
||||||
copying from the input state:
|
it on the input state:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
:language: kotlin
|
:language: kotlin
|
||||||
:start-after: DOCSTART 23
|
:start-after: DOCSTART 23
|
||||||
:end-before: DOCEND 23
|
:end-before: DOCEND 23
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
:language: java
|
:language: java
|
||||||
:start-after: DOCSTART 23
|
:start-after: DOCSTART 23
|
||||||
:end-before: DOCEND 23
|
:end-before: DOCEND 23
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
|
Before our output state can be added to a transaction, we need to associate it with a contract. We can do this by
|
||||||
|
wrapping the output state in a ``StateAndContract``, which combines:
|
||||||
|
|
||||||
|
* The ``ContractState`` representing the output states
|
||||||
|
* A ``String`` identifying the contract governing the state
|
||||||
|
|
||||||
|
.. container:: codeset
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
|
:language: kotlin
|
||||||
|
:start-after: DOCSTART 47
|
||||||
|
:end-before: DOCEND 47
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
|
:language: java
|
||||||
|
:start-after: DOCSTART 47
|
||||||
|
:end-before: DOCEND 47
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
Commands
|
Commands
|
||||||
~~~~~~~~
|
^^^^^^^^
|
||||||
Commands are added to the transaction as ``Command`` instances. ``Command`` combines:
|
A command is added to the transaction as a ``Command``, which combines:
|
||||||
|
|
||||||
* A ``CommandData`` instance representing the type of the command
|
* A ``CommandData`` instance indicating the command's type
|
||||||
* A list of the command's required signers
|
* A ``List<PublicKey>`` representing the command's required signers
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
:language: kotlin
|
:language: kotlin
|
||||||
:start-after: DOCSTART 24
|
:start-after: DOCSTART 24
|
||||||
:end-before: DOCEND 24
|
:end-before: DOCEND 24
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
:language: java
|
:language: java
|
||||||
:start-after: DOCSTART 24
|
:start-after: DOCSTART 24
|
||||||
:end-before: DOCEND 24
|
:end-before: DOCEND 24
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
Attachments
|
Attachments
|
||||||
~~~~~~~~~~~
|
^^^^^^^^^^^
|
||||||
Attachments are identified by their hash. The attachment with the corresponding hash must have been uploaded ahead of
|
Attachments are identified by their hash:
|
||||||
time via the node's RPC interface:
|
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
:language: kotlin
|
:language: kotlin
|
||||||
:start-after: DOCSTART 25
|
:start-after: DOCSTART 25
|
||||||
:end-before: DOCEND 25
|
:end-before: DOCEND 25
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
:language: java
|
:language: java
|
||||||
:start-after: DOCSTART 25
|
:start-after: DOCSTART 25
|
||||||
:end-before: DOCEND 25
|
:end-before: DOCEND 25
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
|
The attachment with the corresponding hash must have been uploaded ahead of time via the node's RPC interface.
|
||||||
|
|
||||||
Time-windows
|
Time-windows
|
||||||
~~~~~~~~~~~~
|
^^^^^^^^^^^^
|
||||||
Time windows represent the period of time during which the transaction must be notarised. They can have a start and an
|
Time windows represent the period during which the transaction must be notarised. They can have a start and an end
|
||||||
end time, or be open at either end:
|
time, or be open at either end:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
:language: kotlin
|
:language: kotlin
|
||||||
:start-after: DOCSTART 26
|
:start-after: DOCSTART 26
|
||||||
:end-before: DOCEND 26
|
:end-before: DOCEND 26
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
:language: java
|
:language: java
|
||||||
:start-after: DOCSTART 26
|
:start-after: DOCSTART 26
|
||||||
:end-before: DOCEND 26
|
:end-before: DOCEND 26
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
We can also define a time window as an ``Instant`` +/- a time tolerance (e.g. 30 seconds):
|
We can also define a time window as an ``Instant`` plus/minus a time tolerance (e.g. 30 seconds):
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
:language: kotlin
|
:language: kotlin
|
||||||
:start-after: DOCSTART 42
|
:start-after: DOCSTART 42
|
||||||
:end-before: DOCEND 42
|
:end-before: DOCEND 42
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
:language: java
|
:language: java
|
||||||
:start-after: DOCSTART 42
|
:start-after: DOCSTART 42
|
||||||
:end-before: DOCEND 42
|
:end-before: DOCEND 42
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
Or as a start-time plus a duration:
|
Or as a start-time plus a duration:
|
||||||
|
|
||||||
|
.. container:: codeset
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
|
:language: kotlin
|
||||||
|
:start-after: DOCSTART 43
|
||||||
|
:end-before: DOCEND 43
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
|
:language: java
|
||||||
|
:start-after: DOCSTART 43
|
||||||
|
:end-before: DOCEND 43
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
|
TransactionBuilder
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Creating a builder
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
The first step when creating a transaction proposal is to instantiate a ``TransactionBuilder``.
|
||||||
|
|
||||||
|
If the transaction has input states or a time-window, we need to instantiate the builder with a reference to the notary
|
||||||
|
that will notarise the inputs and verify the time-window:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
:language: kotlin
|
:language: kotlin
|
||||||
:start-after: DOCSTART 43
|
:start-after: DOCSTART 19
|
||||||
:end-before: DOCEND 43
|
:end-before: DOCEND 19
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
:language: java
|
:language: java
|
||||||
:start-after: DOCSTART 43
|
:start-after: DOCSTART 19
|
||||||
:end-before: DOCEND 43
|
:end-before: DOCEND 19
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
|
We discuss the selection of a notary in :doc:`api-flows`.
|
||||||
|
|
||||||
|
If the transaction does not have any input states or a time-window, it does not require a notary, and can be
|
||||||
|
instantiated without one:
|
||||||
|
|
||||||
|
.. container:: codeset
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
|
:language: kotlin
|
||||||
|
:start-after: DOCSTART 46
|
||||||
|
:end-before: DOCEND 46
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
|
:language: java
|
||||||
|
:start-after: DOCSTART 46
|
||||||
|
:end-before: DOCEND 46
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
Adding items
|
Adding items
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
The transaction builder is mutable. We add items to it using the ``TransactionBuilder.withItems`` method:
|
The next step is to build up the transaction proposal by adding the desired components.
|
||||||
|
|
||||||
|
We can add components to the builder using the ``TransactionBuilder.withItems`` method:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -232,8 +298,14 @@ The transaction builder is mutable. We add items to it using the ``TransactionBu
|
|||||||
``withItems`` takes a ``vararg`` of objects and adds them to the builder based on their type:
|
``withItems`` takes a ``vararg`` of objects and adds them to the builder based on their type:
|
||||||
|
|
||||||
* ``StateAndRef`` objects are added as input states
|
* ``StateAndRef`` objects are added as input states
|
||||||
* ``TransactionState`` and ``ContractState`` objects are added as output states
|
* ``TransactionState`` and ``StateAndContract`` objects are added as output states
|
||||||
|
|
||||||
|
* Both ``TransactionState`` and ``StateAndContract`` are wrappers around a ``ContractState`` output that link the
|
||||||
|
output to a specific contract
|
||||||
|
|
||||||
* ``Command`` objects are added as commands
|
* ``Command`` objects are added as commands
|
||||||
|
* ``SecureHash`` objects are added as attachments
|
||||||
|
* A ``TimeWindow`` object replaces the transaction's existing ``TimeWindow``, if any
|
||||||
|
|
||||||
Passing in objects of any other type will cause an ``IllegalArgumentException`` to be thrown.
|
Passing in objects of any other type will cause an ``IllegalArgumentException`` to be thrown.
|
||||||
|
|
||||||
@ -253,23 +325,105 @@ Here's an example usage of ``TransactionBuilder.withItems``:
|
|||||||
:end-before: DOCEND 27
|
:end-before: DOCEND 27
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
You can also pass in objects one-by-one. This is the only way to add attachments:
|
There are also individual methods for adding components.
|
||||||
|
|
||||||
|
Here are the methods for adding inputs and attachments:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
:language: kotlin
|
:language: kotlin
|
||||||
:start-after: DOCSTART 28
|
:start-after: DOCSTART 28
|
||||||
:end-before: DOCEND 28
|
:end-before: DOCEND 28
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
:language: java
|
:language: java
|
||||||
:start-after: DOCSTART 28
|
:start-after: DOCSTART 28
|
||||||
:end-before: DOCEND 28
|
:end-before: DOCEND 28
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
To set the transaction builder's time-window, we can either set a time-window directly:
|
An output state can be added as a ``ContractState``, contract class name and notary:
|
||||||
|
|
||||||
|
.. container:: codeset
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
|
:language: kotlin
|
||||||
|
:start-after: DOCSTART 49
|
||||||
|
:end-before: DOCEND 49
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
|
:language: java
|
||||||
|
:start-after: DOCSTART 49
|
||||||
|
:end-before: DOCEND 49
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
|
We can also leave the notary field blank, in which case the transaction's default notary is used:
|
||||||
|
|
||||||
|
.. container:: codeset
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
|
:language: kotlin
|
||||||
|
:start-after: DOCSTART 50
|
||||||
|
:end-before: DOCEND 50
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
|
:language: java
|
||||||
|
:start-after: DOCSTART 50
|
||||||
|
:end-before: DOCEND 50
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
|
Or we can add the output state as a ``TransactionState``, which already specifies the output's contract and notary:
|
||||||
|
|
||||||
|
.. container:: codeset
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
|
:language: kotlin
|
||||||
|
:start-after: DOCSTART 51
|
||||||
|
:end-before: DOCEND 51
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
|
:language: java
|
||||||
|
:start-after: DOCSTART 51
|
||||||
|
:end-before: DOCEND 51
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
|
Commands can be added as a ``Command``:
|
||||||
|
|
||||||
|
.. container:: codeset
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
|
:language: kotlin
|
||||||
|
:start-after: DOCSTART 52
|
||||||
|
:end-before: DOCEND 52
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
|
:language: java
|
||||||
|
:start-after: DOCSTART 52
|
||||||
|
:end-before: DOCEND 52
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
|
Or as ``CommandData`` and a ``vararg PublicKey``:
|
||||||
|
|
||||||
|
.. container:: codeset
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
|
||||||
|
:language: kotlin
|
||||||
|
:start-after: DOCSTART 53
|
||||||
|
:end-before: DOCEND 53
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
|
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
|
||||||
|
:language: java
|
||||||
|
:start-after: DOCSTART 53
|
||||||
|
:end-before: DOCEND 53
|
||||||
|
:dedent: 12
|
||||||
|
|
||||||
|
For the time-window, we can set a time-window directly:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -303,7 +457,9 @@ Or define the time-window as a time plus a duration (e.g. 45 seconds):
|
|||||||
|
|
||||||
Signing the builder
|
Signing the builder
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
Once the builder is ready, we finalize it by signing it and converting it into a ``SignedTransaction``:
|
Once the builder is ready, we finalize it by signing it and converting it into a ``SignedTransaction``.
|
||||||
|
|
||||||
|
We can either sign with our legal identity key:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -319,7 +475,7 @@ Once the builder is ready, we finalize it by signing it and converting it into a
|
|||||||
:end-before: DOCEND 29
|
:end-before: DOCEND 29
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
This will sign the transaction with your legal identity key. You can also choose to use another one of your public keys:
|
Or we can also choose to use another one of our public keys:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -335,7 +491,7 @@ This will sign the transaction with your legal identity key. You can also choose
|
|||||||
:end-before: DOCEND 30
|
:end-before: DOCEND 30
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
Either way, the outcome of this process is to create a ``SignedTransaction``, which can no longer be modified.
|
Either way, the outcome of this process is to create an immutable ``SignedTransaction`` with our signature over it.
|
||||||
|
|
||||||
SignedTransaction
|
SignedTransaction
|
||||||
-----------------
|
-----------------
|
||||||
@ -356,12 +512,14 @@ transaction's signatures.
|
|||||||
|
|
||||||
Verifying the transaction's contents
|
Verifying the transaction's contents
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
To verify a transaction, we need to retrieve any states in the transaction chain that our node doesn't currently have
|
If a transaction has inputs, we need to retrieve all the states in the transaction's dependency chain before we can
|
||||||
in its local storage from the proposer(s) of the transaction. This process is handled by a built-in flow called
|
verify the transaction's contents. This is because the transaction is only valid if its dependency chain is also valid.
|
||||||
``ReceiveTransactionFlow``. See :doc:`api-flows` for more details.
|
We do this by requesting any states in the chain that our node doesn't currently have in its local storage from the
|
||||||
|
proposer(s) of the transaction. This process is handled by a built-in flow called ``ReceiveTransactionFlow``.
|
||||||
|
See :doc:`api-flows` for more details.
|
||||||
|
|
||||||
We can now *verify* the transaction to ensure that it satisfies the contracts of all the transaction's input and output
|
We can now verify the transaction's contents to ensure that it satisfies the contracts of all the transaction's input
|
||||||
states:
|
and output states:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -377,11 +535,16 @@ states:
|
|||||||
:end-before: DOCEND 33
|
:end-before: DOCEND 33
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
We can also conduct additional validation of the transaction, beyond what is performed by its contracts. However, the
|
Checking that the transaction meets the contract constraints is only part of verifying the transaction's contents. We
|
||||||
``SignedTransaction`` holds its inputs as ``StateRef`` instances, and its attachments as hashes. These do not provide
|
will usually also want to perform our own additional validation of the transaction contents before signing, to ensure
|
||||||
enough information to properly validate the transaction's contents. To resolve these into actual ``ContractState`` and
|
that the transaction proposal represents an agreement we wish to enter into.
|
||||||
``Attachment`` instances, we need to use the ``ServiceHub`` to convert the ``SignedTransaction`` into a
|
|
||||||
``LedgerTransaction``:
|
However, the ``SignedTransaction`` holds its inputs as ``StateRef`` instances, and its attachments as ``SecureHash``
|
||||||
|
instances, which do not provide enough information to properly validate the transaction's contents. We first need to
|
||||||
|
resolve the ``StateRef`` and ``SecureHash`` instances into actual ``ContractState`` and ``Attachment`` instances, which
|
||||||
|
we can then inspect.
|
||||||
|
|
||||||
|
We achieve this by using the ``ServiceHub`` to convert the ``SignedTransaction`` into a ``LedgerTransaction``:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -397,7 +560,7 @@ enough information to properly validate the transaction's contents. To resolve t
|
|||||||
:end-before: DOCEND 32
|
:end-before: DOCEND 32
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
We can now perform additional verification. Here's a simple example:
|
We can now perform our additional verification. Here's a simple example:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -415,8 +578,10 @@ We can now perform additional verification. Here's a simple example:
|
|||||||
|
|
||||||
Verifying the transaction's signatures
|
Verifying the transaction's signatures
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
We also need to verify that the transaction has all the required signatures, and that these signatures are valid, to
|
Aside from verifying that the transaction's contents are valid, we also need to check that the signatures are valid. A
|
||||||
prevent tampering. We do this using ``SignedTransaction.verifyRequiredSignatures``:
|
valid signature over the hash of the transaction prevents tampering.
|
||||||
|
|
||||||
|
We can verify that all the transaction's required signatures are present and valid as follows:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -432,7 +597,8 @@ prevent tampering. We do this using ``SignedTransaction.verifyRequiredSignatures
|
|||||||
:end-before: DOCEND 35
|
:end-before: DOCEND 35
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
Alternatively, we can use ``SignedTransaction.verifySignaturesExcept``, which takes a ``vararg`` of the public keys for
|
However, we'll often want to verify the transaction's existing signatures before all of them have been collected. For
|
||||||
|
this we can use ``SignedTransaction.verifySignaturesExcept``, which takes a ``vararg`` of the public keys for
|
||||||
which the signatures are allowed to be missing:
|
which the signatures are allowed to be missing:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
@ -468,13 +634,15 @@ We can also choose to simply verify the signatures that are present:
|
|||||||
:end-before: DOCEND 37
|
:end-before: DOCEND 37
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
However, BE VERY CAREFUL - this function provides no guarantees that the signatures are correct, or that none are
|
Be very careful, however - this function neither guarantees that the signatures that are present are required, nor
|
||||||
missing.
|
checks whether any signatures are missing.
|
||||||
|
|
||||||
Signing the transaction
|
Signing the transaction
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
Once we are satisfied with the contents and existing signatures over the transaction, we can add our signature to the
|
Once we are satisfied with the contents and existing signatures over the transaction, we add our signature to the
|
||||||
``SignedTransaction`` using:
|
``SignedTransaction`` to indicate that we approve the transaction.
|
||||||
|
|
||||||
|
We can sign using our legal identity key, as follows:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -490,7 +658,7 @@ Once we are satisfied with the contents and existing signatures over the transac
|
|||||||
:end-before: DOCEND 38
|
:end-before: DOCEND 38
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
As with the ``TransactionBuilder``, we can also choose to sign using another one of our public keys:
|
Or we can choose to sign using another one of our public keys:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -506,7 +674,9 @@ As with the ``TransactionBuilder``, we can also choose to sign using another one
|
|||||||
:end-before: DOCEND 39
|
:end-before: DOCEND 39
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
We can also generate a signature over the transaction without adding it to the transaction directly by using:
|
We can also generate a signature over the transaction without adding it to the transaction directly.
|
||||||
|
|
||||||
|
We can do this with our legal identity key:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -522,7 +692,7 @@ We can also generate a signature over the transaction without adding it to the t
|
|||||||
:end-before: DOCEND 40
|
:end-before: DOCEND 40
|
||||||
:dedent: 12
|
:dedent: 12
|
||||||
|
|
||||||
Or using another one of our public keys, as follows:
|
Or using another one of our public keys:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -542,22 +712,3 @@ Notarising and recording
|
|||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
Notarising and recording a transaction is handled by a built-in flow called ``FinalityFlow``. See :doc:`api-flows` for
|
Notarising and recording a transaction is handled by a built-in flow called ``FinalityFlow``. See :doc:`api-flows` for
|
||||||
more details.
|
more details.
|
||||||
|
|
||||||
Notary-change transactions
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
A single Corda network will usually have multiple notary services. To commit a transaction, we require a signature
|
|
||||||
from the notary service associated with each input state. If we tried to commit a transaction where the input
|
|
||||||
states were associated with different notary services, the transaction would require a signature from multiple notary
|
|
||||||
services, creating a complicated multi-phase commit scenario. To prevent this, every input state in a transaction
|
|
||||||
must be associated with the same notary.
|
|
||||||
|
|
||||||
However, we will often need to create a transaction involving input states associated with different notaries. Before
|
|
||||||
we can create this transaction, we will need to change the notary service associated with each state by:
|
|
||||||
|
|
||||||
* Deciding which notary service we want to notarise the transaction
|
|
||||||
* Creating a special ``NotaryChangeWireTransaction`` that consumes the input states pointing to the old notary and
|
|
||||||
produces outputs which are identical but point to the new notary service
|
|
||||||
* Using the outputs of the notary-change transactions as inputs to a standard transaction
|
|
||||||
|
|
||||||
In practice, this process is handled automatically by a built-in flow called ``NotaryChangeFlow``. See
|
|
||||||
:doc:`api-flows` for more details.
|
|
@ -34,6 +34,7 @@ import java.util.Set;
|
|||||||
|
|
||||||
import static net.corda.core.contracts.ContractsDSL.requireThat;
|
import static net.corda.core.contracts.ContractsDSL.requireThat;
|
||||||
import static net.corda.testing.TestConstants.getALICE_KEY;
|
import static net.corda.testing.TestConstants.getALICE_KEY;
|
||||||
|
import static net.corda.testing.contracts.DummyContractKt.DUMMY_PROGRAM_ID;
|
||||||
|
|
||||||
// We group our two flows inside a singleton object to indicate that they work
|
// We group our two flows inside a singleton object to indicate that they work
|
||||||
// together.
|
// together.
|
||||||
@ -251,13 +252,19 @@ public class FlowCookbookJava {
|
|||||||
|
|
||||||
// Output states are constructed from scratch.
|
// Output states are constructed from scratch.
|
||||||
// DOCSTART 22
|
// DOCSTART 22
|
||||||
DummyState ourOutput = new DummyState();
|
DummyState ourOutputState = new DummyState();
|
||||||
// DOCEND 22
|
// DOCEND 22
|
||||||
// Or as copies of other states with some properties changed.
|
// Or as copies of other states with some properties changed.
|
||||||
// DOCSTART 23
|
// DOCSTART 23
|
||||||
DummyState ourOtherOutput = ourOutput.copy(77);
|
DummyState ourOtherOutputState = ourOutputState.copy(77);
|
||||||
// DOCEND 23
|
// DOCEND 23
|
||||||
|
|
||||||
|
// We then need to pair our output state with a contract.
|
||||||
|
// DOCSTART 47
|
||||||
|
String contractName = "net.corda.testing.contracts.DummyContract";
|
||||||
|
StateAndContract ourOutput = new StateAndContract(ourOutputState, contractName);
|
||||||
|
// DOCEND 47
|
||||||
|
|
||||||
// Commands pair a ``CommandData`` instance with a list of
|
// Commands pair a ``CommandData`` instance with a list of
|
||||||
// public keys. To be valid, the transaction requires a signature
|
// public keys. To be valid, the transaction requires a signature
|
||||||
// matching every public key in all of the transaction's commands.
|
// matching every public key in all of the transaction's commands.
|
||||||
@ -297,11 +304,11 @@ public class FlowCookbookJava {
|
|||||||
// We can also define a time window as an ``Instant`` +/- a time
|
// We can also define a time window as an ``Instant`` +/- a time
|
||||||
// tolerance (e.g. 30 seconds):
|
// tolerance (e.g. 30 seconds):
|
||||||
// DOCSTART 42
|
// DOCSTART 42
|
||||||
TimeWindow ourTimeWindow2 = TimeWindow.withTolerance(Instant.now(), Duration.ofSeconds(30));
|
TimeWindow ourTimeWindow2 = TimeWindow.withTolerance(getServiceHub().getClock().instant(), Duration.ofSeconds(30));
|
||||||
// DOCEND 42
|
// DOCEND 42
|
||||||
// Or as a start-time plus a duration:
|
// Or as a start-time plus a duration:
|
||||||
// DOCSTART 43
|
// DOCSTART 43
|
||||||
TimeWindow ourTimeWindow3 = TimeWindow.fromStartAndDuration(Instant.now(), Duration.ofSeconds(30));
|
TimeWindow ourTimeWindow3 = TimeWindow.fromStartAndDuration(getServiceHub().getClock().instant(), Duration.ofSeconds(30));
|
||||||
// DOCEND 43
|
// DOCEND 43
|
||||||
|
|
||||||
/*------------------------
|
/*------------------------
|
||||||
@ -309,38 +316,72 @@ public class FlowCookbookJava {
|
|||||||
------------------------*/
|
------------------------*/
|
||||||
progressTracker.setCurrentStep(TX_BUILDING);
|
progressTracker.setCurrentStep(TX_BUILDING);
|
||||||
|
|
||||||
|
// If our transaction has input states or a time-window, we must instantiate it with a
|
||||||
|
// notary.
|
||||||
// DOCSTART 19
|
// DOCSTART 19
|
||||||
TransactionBuilder txBuilder = new TransactionBuilder(specificNotary);
|
TransactionBuilder txBuilder = new TransactionBuilder(specificNotary);
|
||||||
// DOCEND 19
|
// DOCEND 19
|
||||||
|
|
||||||
|
// Otherwise, we can choose to instantiate it without one:
|
||||||
|
// DOCSTART 46
|
||||||
|
TransactionBuilder txBuilderNoNotary = new TransactionBuilder();
|
||||||
|
// DOCEND 46
|
||||||
|
|
||||||
// We add items to the transaction builder using ``TransactionBuilder.withItems``:
|
// We add items to the transaction builder using ``TransactionBuilder.withItems``:
|
||||||
// DOCSTART 27
|
// DOCSTART 27
|
||||||
txBuilder.withItems(
|
txBuilder.withItems(
|
||||||
// Inputs, as ``StateRef``s that reference to the outputs of previous transactions
|
// Inputs, as ``StateAndRef``s that reference to the outputs of previous transactions
|
||||||
ourStateAndRef,
|
ourStateAndRef,
|
||||||
// Outputs, as ``ContractState``s
|
// Outputs, as ``StateAndContract``s
|
||||||
ourOutput,
|
ourOutput,
|
||||||
// Commands, as ``Command``s
|
// Commands, as ``Command``s
|
||||||
ourCommand
|
ourCommand,
|
||||||
|
// Attachments, as ``SecureHash``es
|
||||||
|
ourAttachment,
|
||||||
|
// A time-window, as ``TimeWindow``
|
||||||
|
ourTimeWindow
|
||||||
);
|
);
|
||||||
// DOCEND 27
|
// DOCEND 27
|
||||||
|
|
||||||
// We can also add items using methods for the individual components:
|
// We can also add items using methods for the individual components.
|
||||||
|
|
||||||
|
// The individual methods for adding input states and attachments:
|
||||||
// DOCSTART 28
|
// DOCSTART 28
|
||||||
txBuilder.addInputState(ourStateAndRef);
|
txBuilder.addInputState(ourStateAndRef);
|
||||||
txBuilder.addOutputState(ourOutput, DummyContractKt.DUMMY_PROGRAM_ID);
|
|
||||||
txBuilder.addCommand(ourCommand);
|
|
||||||
txBuilder.addAttachment(ourAttachment);
|
txBuilder.addAttachment(ourAttachment);
|
||||||
// DOCEND 28
|
// DOCEND 28
|
||||||
|
|
||||||
// There are several ways of setting the transaction's time-window.
|
// An output state can be added as a ``ContractState``, contract class name and notary.
|
||||||
// We can set a time-window directly:
|
// DOCSTART 49
|
||||||
|
txBuilder.addOutputState(ourOutputState, DUMMY_PROGRAM_ID, specificNotary);
|
||||||
|
// DOCEND 49
|
||||||
|
// We can also leave the notary field blank, in which case the transaction's default
|
||||||
|
// notary is used.
|
||||||
|
// DOCSTART 50
|
||||||
|
txBuilder.addOutputState(ourOutputState, DUMMY_PROGRAM_ID);
|
||||||
|
// DOCEND 50
|
||||||
|
// Or we can add the output state as a ``TransactionState``, which already specifies
|
||||||
|
// the output's contract and notary.
|
||||||
|
// DOCSTART 51
|
||||||
|
TransactionState txState = new TransactionState(ourOutputState, DUMMY_PROGRAM_ID, specificNotary);
|
||||||
|
// DOCEND 51
|
||||||
|
|
||||||
|
// Commands can be added as ``Command``s.
|
||||||
|
// DOCSTART 52
|
||||||
|
txBuilder.addCommand(ourCommand);
|
||||||
|
// DOCEND 52
|
||||||
|
// Or as ``CommandData`` and a ``vararg PublicKey``.
|
||||||
|
// DOCSTART 53
|
||||||
|
txBuilder.addCommand(commandData, ourPubKey, counterpartyPubKey);
|
||||||
|
// DOCEND 53
|
||||||
|
|
||||||
|
// We can set a time-window directly.
|
||||||
// DOCSTART 44
|
// DOCSTART 44
|
||||||
txBuilder.setTimeWindow(ourTimeWindow);
|
txBuilder.setTimeWindow(ourTimeWindow);
|
||||||
// DOCEND 44
|
// DOCEND 44
|
||||||
// Or as a start time plus a duration (e.g. 45 seconds):
|
// Or as a start time plus a duration (e.g. 45 seconds).
|
||||||
// DOCSTART 45
|
// DOCSTART 45
|
||||||
txBuilder.setTimeWindow(Instant.now(), Duration.ofSeconds(45));
|
txBuilder.setTimeWindow(getServiceHub().getClock().instant(), Duration.ofSeconds(45));
|
||||||
// DOCEND 45
|
// DOCEND 45
|
||||||
|
|
||||||
/*-----------------------
|
/*-----------------------
|
||||||
|
@ -106,7 +106,7 @@ object FlowCookbook {
|
|||||||
// - To serve as a timestamping authority if the transaction has a time-window
|
// - To serve as a timestamping authority if the transaction has a time-window
|
||||||
// We retrieve the notary from the network map.
|
// We retrieve the notary from the network map.
|
||||||
// DOCSTART 1
|
// DOCSTART 1
|
||||||
val specificNotary: Party? = serviceHub.networkMapCache.getNotary(CordaX500Name(organisation = "Notary Service", locality = "London", country = "UK"))
|
val specificNotary: Party = serviceHub.networkMapCache.getNotary(CordaX500Name(organisation = "Notary Service", locality = "London", country = "UK"))!!
|
||||||
// Alternatively, we can pick an arbitrary notary from the notary list. However, it is always preferable to
|
// Alternatively, we can pick an arbitrary notary from the notary list. However, it is always preferable to
|
||||||
// specify which notary to use explicitly, as the notary list might change when new notaries are introduced,
|
// specify which notary to use explicitly, as the notary list might change when new notaries are introduced,
|
||||||
// or old ones decommissioned.
|
// or old ones decommissioned.
|
||||||
@ -234,13 +234,19 @@ object FlowCookbook {
|
|||||||
|
|
||||||
// Output states are constructed from scratch.
|
// Output states are constructed from scratch.
|
||||||
// DOCSTART 22
|
// DOCSTART 22
|
||||||
val ourOutput: DummyState = DummyState()
|
val ourOutputState: DummyState = DummyState()
|
||||||
// DOCEND 22
|
// DOCEND 22
|
||||||
// Or as copies of other states with some properties changed.
|
// Or as copies of other states with some properties changed.
|
||||||
// DOCSTART 23
|
// DOCSTART 23
|
||||||
val ourOtherOutput: DummyState = ourOutput.copy(magicNumber = 77)
|
val ourOtherOutputState: DummyState = ourOutputState.copy(magicNumber = 77)
|
||||||
// DOCEND 23
|
// DOCEND 23
|
||||||
|
|
||||||
|
// We then need to pair our output state with a contract.
|
||||||
|
// DOCSTART 47
|
||||||
|
val contractName: String = "net.corda.testing.contracts.DummyContract"
|
||||||
|
val ourOutput: StateAndContract = StateAndContract(ourOutputState, contractName)
|
||||||
|
// DOCEND 47
|
||||||
|
|
||||||
// Commands pair a ``CommandData`` instance with a list of
|
// Commands pair a ``CommandData`` instance with a list of
|
||||||
// public keys. To be valid, the transaction requires a signature
|
// public keys. To be valid, the transaction requires a signature
|
||||||
// matching every public key in all of the transaction's commands.
|
// matching every public key in all of the transaction's commands.
|
||||||
@ -278,11 +284,11 @@ object FlowCookbook {
|
|||||||
// We can also define a time window as an ``Instant`` +/- a time
|
// We can also define a time window as an ``Instant`` +/- a time
|
||||||
// tolerance (e.g. 30 seconds):
|
// tolerance (e.g. 30 seconds):
|
||||||
// DOCSTART 42
|
// DOCSTART 42
|
||||||
val ourTimeWindow2: TimeWindow = TimeWindow.withTolerance(Instant.now(), 30.seconds)
|
val ourTimeWindow2: TimeWindow = TimeWindow.withTolerance(serviceHub.clock.instant(), 30.seconds)
|
||||||
// DOCEND 42
|
// DOCEND 42
|
||||||
// Or as a start-time plus a duration:
|
// Or as a start-time plus a duration:
|
||||||
// DOCSTART 43
|
// DOCSTART 43
|
||||||
val ourTimeWindow3: TimeWindow = TimeWindow.fromStartAndDuration(Instant.now(), 30.seconds)
|
val ourTimeWindow3: TimeWindow = TimeWindow.fromStartAndDuration(serviceHub.clock.instant(), 30.seconds)
|
||||||
// DOCEND 43
|
// DOCEND 43
|
||||||
|
|
||||||
/**-----------------------
|
/**-----------------------
|
||||||
@ -290,36 +296,70 @@ object FlowCookbook {
|
|||||||
-----------------------**/
|
-----------------------**/
|
||||||
progressTracker.currentStep = TX_BUILDING
|
progressTracker.currentStep = TX_BUILDING
|
||||||
|
|
||||||
|
// If our transaction has input states or a time-window, we must instantiate it with a
|
||||||
|
// notary.
|
||||||
// DOCSTART 19
|
// DOCSTART 19
|
||||||
val txBuilder: TransactionBuilder = TransactionBuilder(specificNotary)
|
val txBuilder: TransactionBuilder = TransactionBuilder(specificNotary)
|
||||||
// DOCEND 19
|
// DOCEND 19
|
||||||
|
|
||||||
|
// Otherwise, we can choose to instantiate it without one:
|
||||||
|
// DOCSTART 46
|
||||||
|
val txBuilderNoNotary: TransactionBuilder = TransactionBuilder()
|
||||||
|
// DOCEND 46
|
||||||
|
|
||||||
// We add items to the transaction builder using ``TransactionBuilder.withItems``:
|
// We add items to the transaction builder using ``TransactionBuilder.withItems``:
|
||||||
// DOCSTART 27
|
// DOCSTART 27
|
||||||
txBuilder.withItems(
|
txBuilder.withItems(
|
||||||
// Inputs, as ``StateRef``s that reference the outputs of previous transactions
|
// Inputs, as ``StateAndRef``s that reference the outputs of previous transactions
|
||||||
ourStateAndRef,
|
ourStateAndRef,
|
||||||
// Outputs, as ``ContractState``s
|
// Outputs, as ``StateAndContract``s
|
||||||
ourOutput,
|
ourOutput,
|
||||||
// Commands, as ``Command``s
|
// Commands, as ``Command``s
|
||||||
ourCommand
|
ourCommand,
|
||||||
|
// Attachments, as ``SecureHash``es
|
||||||
|
ourAttachment,
|
||||||
|
// A time-window, as ``TimeWindow``
|
||||||
|
ourTimeWindow
|
||||||
)
|
)
|
||||||
// DOCEND 27
|
// DOCEND 27
|
||||||
|
|
||||||
// We can also add items using methods for the individual components:
|
// We can also add items using methods for the individual components.
|
||||||
|
|
||||||
|
// The individual methods for adding input states and attachments:
|
||||||
// DOCSTART 28
|
// DOCSTART 28
|
||||||
txBuilder.addInputState(ourStateAndRef)
|
txBuilder.addInputState(ourStateAndRef)
|
||||||
txBuilder.addOutputState(ourOutput, DUMMY_PROGRAM_ID)
|
|
||||||
txBuilder.addCommand(ourCommand)
|
|
||||||
txBuilder.addAttachment(ourAttachment)
|
txBuilder.addAttachment(ourAttachment)
|
||||||
// DOCEND 28
|
// DOCEND 28
|
||||||
|
|
||||||
// There are several ways of setting the transaction's time-window.
|
// An output state can be added as a ``ContractState``, contract class name and notary.
|
||||||
// We can set a time-window directly:
|
// DOCSTART 49
|
||||||
|
txBuilder.addOutputState(ourOutputState, DUMMY_PROGRAM_ID, specificNotary)
|
||||||
|
// DOCEND 49
|
||||||
|
// We can also leave the notary field blank, in which case the transaction's default
|
||||||
|
// notary is used.
|
||||||
|
// DOCSTART 50
|
||||||
|
txBuilder.addOutputState(ourOutputState, DUMMY_PROGRAM_ID)
|
||||||
|
// DOCEND 50
|
||||||
|
// Or we can add the output state as a ``TransactionState``, which already specifies
|
||||||
|
// the output's contract and notary.
|
||||||
|
// DOCSTART 51
|
||||||
|
val txState: TransactionState<DummyState> = TransactionState(ourOutputState, DUMMY_PROGRAM_ID, specificNotary)
|
||||||
|
// DOCEND 51
|
||||||
|
|
||||||
|
// Commands can be added as ``Command``s.
|
||||||
|
// DOCSTART 52
|
||||||
|
txBuilder.addCommand(ourCommand)
|
||||||
|
// DOCEND 52
|
||||||
|
// Or as ``CommandData`` and a ``vararg PublicKey``.
|
||||||
|
// DOCSTART 53
|
||||||
|
txBuilder.addCommand(commandData, ourPubKey, counterpartyPubKey)
|
||||||
|
// DOCEND 53
|
||||||
|
|
||||||
|
// We can set a time-window directly.
|
||||||
// DOCSTART 44
|
// DOCSTART 44
|
||||||
txBuilder.setTimeWindow(ourTimeWindow)
|
txBuilder.setTimeWindow(ourTimeWindow)
|
||||||
// DOCEND 44
|
// DOCEND 44
|
||||||
// Or as a start time plus a duration (e.g. 45 seconds):
|
// Or as a start time plus a duration (e.g. 45 seconds).
|
||||||
// DOCSTART 45
|
// DOCSTART 45
|
||||||
txBuilder.setTimeWindow(serviceHub.clock.instant(), 45.seconds)
|
txBuilder.setTimeWindow(serviceHub.clock.instant(), 45.seconds)
|
||||||
// DOCEND 45
|
// DOCEND 45
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 199 KiB After Width: | Height: | Size: 233 KiB |
Binary file not shown.
Before Width: | Height: | Size: 275 KiB |
Loading…
x
Reference in New Issue
Block a user