mirror of
https://github.com/corda/corda.git
synced 2025-06-03 08:00:57 +00:00
Replace timestamp with time-window (#3211)
This commit is contained in:
parent
d1e147b1c1
commit
52eef5da5b
@ -69,7 +69,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
|||||||
val requiredSigningKeys: Set<PublicKey>
|
val requiredSigningKeys: Set<PublicKey>
|
||||||
get() {
|
get() {
|
||||||
val commandKeys = commands.flatMap { it.signers }.toSet()
|
val commandKeys = commands.flatMap { it.signers }.toSet()
|
||||||
// TODO: prevent notary field from being set if there are no inputs and no timestamp.
|
// TODO: prevent notary field from being set if there are no inputs and no time-window.
|
||||||
return if (notary != null && (inputs.isNotEmpty() || timeWindow != null)) {
|
return if (notary != null && (inputs.isNotEmpty() || timeWindow != null)) {
|
||||||
commandKeys + notary.owningKey
|
commandKeys + notary.owningKey
|
||||||
} else {
|
} else {
|
||||||
|
@ -30,7 +30,7 @@ In our flow, the Initiator flow class will be doing the majority of the work:
|
|||||||
2. Create a transaction builder
|
2. Create a transaction builder
|
||||||
3. Extract any input states from the vault and add them to the builder
|
3. Extract any input states from the vault and add them to the builder
|
||||||
4. Create any output states and add them to the builder
|
4. Create any output states and add them to the builder
|
||||||
5. Add any commands, attachments and timestamps to the builder
|
5. Add any commands, attachments and time-window to the builder
|
||||||
|
|
||||||
*Part 2 - Sign the transaction*
|
*Part 2 - Sign the transaction*
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ public class CommercialPaper implements Contract {
|
|||||||
});
|
});
|
||||||
} else if (cmd.getValue() instanceof Commands.Issue) {
|
} else if (cmd.getValue() instanceof Commands.Issue) {
|
||||||
State output = outputs.get(0);
|
State output = outputs.get(0);
|
||||||
if (timeWindow == null) throw new IllegalArgumentException("Issuances must be timestamped");
|
if (timeWindow == null) throw new IllegalArgumentException("Issuances must have a time-window");
|
||||||
Instant time = timeWindow.getUntilTime();
|
Instant time = timeWindow.getUntilTime();
|
||||||
requireThat(require -> {
|
requireThat(require -> {
|
||||||
// Don't allow people to issue commercial paper under other entities identities.
|
// Don't allow people to issue commercial paper under other entities identities.
|
||||||
|
@ -228,7 +228,7 @@ Next, we call another subflow called ``SignTransactionFlow``. ``SignTransactionF
|
|||||||
* Sending the transaction back to the buyer.
|
* Sending the transaction back to the buyer.
|
||||||
|
|
||||||
The transaction then needs to be finalized. This is the the process of sending the transaction to a notary to assert
|
The transaction then needs to be finalized. This is the the process of sending the transaction to a notary to assert
|
||||||
(with another signature) that the timestamp in the transaction (if any) is valid and there are no double spends.
|
(with another signature) that the time-window in the transaction (if any) is valid and there are no double spends.
|
||||||
In this flow, finalization is handled by the buyer, so we just wait for the signed transaction to appear in our
|
In this flow, finalization is handled by the buyer, so we just wait for the signed transaction to appear in our
|
||||||
transaction storage. It will have the same ID as the one we started with but more signatures.
|
transaction storage. It will have the same ID as the one we started with but more signatures.
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ We can picture this situation as follows:
|
|||||||
The contract code can be written in any JVM language, and has access to the full capabilities of the language,
|
The contract code can be written in any JVM language, and has access to the full capabilities of the language,
|
||||||
including:
|
including:
|
||||||
|
|
||||||
* Checking the number of inputs, outputs, commands, timestamps, and/or attachments
|
* Checking the number of inputs, outputs, commands, time-window, and/or attachments
|
||||||
* Checking the contents of any of these components
|
* Checking the contents of any of these components
|
||||||
* Looping constructs, variable assignment, function calls, helper methods, etc.
|
* Looping constructs, variable assignment, function calls, helper methods, etc.
|
||||||
* Grouping similar states to validate them as a group (e.g. imposing a rule on the combined value of all the cash
|
* Grouping similar states to validate them as a group (e.g. imposing a rule on the combined value of all the cash
|
||||||
|
@ -49,14 +49,14 @@ Transaction Merkle trees
|
|||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
A Merkle tree is constructed from a transaction by splitting the transaction into leaves, where each leaf contains
|
A Merkle tree is constructed from a transaction by splitting the transaction into leaves, where each leaf contains
|
||||||
either an input, an output, a command, or an attachment. The Merkle tree also contains the other fields of the
|
either an input, an output, a command, or an attachment. The Merkle tree also contains the other fields of the
|
||||||
``WireTransaction``, such as the timestamp, the notary, the type and the signers.
|
``WireTransaction``, such as the time-window, the notary, the type and the signers.
|
||||||
|
|
||||||
Next, the Merkle tree is built in the normal way by hashing the concatenation of nodes’ hashes below the current one
|
Next, the Merkle tree is built in the normal way by hashing the concatenation of nodes’ hashes below the current one
|
||||||
together. It’s visible on the example image below, where ``H`` denotes sha256 function, "+" - concatenation.
|
together. It’s visible on the example image below, where ``H`` denotes sha256 function, "+" - concatenation.
|
||||||
|
|
||||||
.. image:: resources/merkleTree.png
|
.. image:: resources/merkleTree.png
|
||||||
|
|
||||||
The transaction has two input states, one output state, one attachment, one command and a timestamp. For brevity
|
The transaction has two input states, one output state, one attachment, one command and a time-window. For brevity
|
||||||
we didn't include all leaves on the diagram (type, notary and signers are presented as one leaf labelled Rest - in
|
we didn't include all leaves on the diagram (type, notary and signers are presented as one leaf labelled Rest - in
|
||||||
reality they are separate leaves). Notice that if a tree is not a full binary tree, leaves are padded to the nearest
|
reality they are separate leaves). Notice that if a tree is not a full binary tree, leaves are padded to the nearest
|
||||||
power of 2 with zero hash (since finding a pre-image of sha256(x) == 0 is hard computational task) - marked light
|
power of 2 with zero hash (since finding a pre-image of sha256(x) == 0 is hard computational task) - marked light
|
||||||
@ -73,7 +73,7 @@ obtained belongs to that particular transaction.
|
|||||||
.. image:: resources/partialMerkle.png
|
.. image:: resources/partialMerkle.png
|
||||||
|
|
||||||
In the example above, the node ``H(f)`` is the one holding command data for signing by Oracle service. Blue leaf
|
In the example above, the node ``H(f)`` is the one holding command data for signing by Oracle service. Blue leaf
|
||||||
``H(g)`` is also included since it's holding timestamp information. Nodes labelled ``Provided`` form the Partial
|
``H(g)`` is also included since it's holding time-window information. Nodes labelled ``Provided`` form the Partial
|
||||||
Merkle Tree, black ones are omitted. Having timestamp with the command that should be in a violet node place and
|
Merkle Tree, black ones are omitted. Having time-window with the command that should be in a violet node place and
|
||||||
branch we are able to calculate root of this tree and compare it with original transaction identifier - we have a
|
branch we are able to calculate root of this tree and compare it with original transaction identifier - we have a
|
||||||
proof that this command and timestamp belong to this transaction.
|
proof that this command and time-window belong to this transaction.
|
@ -111,10 +111,10 @@ As well as input states and output states, transactions contain:
|
|||||||
|
|
||||||
* Commands
|
* Commands
|
||||||
* Attachments
|
* Attachments
|
||||||
* Timestamps
|
* Time-Window
|
||||||
|
|
||||||
For example, a transaction where Alice pays off £5 of an IOU with Bob using a £5 cash payment, supported by two
|
For example, a transaction where Alice pays off £5 of an IOU with Bob using a £5 cash payment, supported by two
|
||||||
attachments and a timestamp, may look as follows:
|
attachments and a time-window, may look as follows:
|
||||||
|
|
||||||
.. image:: resources/full-tx.png
|
.. image:: resources/full-tx.png
|
||||||
:scale: 25%
|
:scale: 25%
|
||||||
@ -172,8 +172,8 @@ For this use case, we have *attachments*. Each transaction can refer to zero or
|
|||||||
attachments are ZIP/JAR files containing arbitrary content. The information in these files can then be
|
attachments are ZIP/JAR files containing arbitrary content. The information in these files can then be
|
||||||
used when checking the transaction's validity.
|
used when checking the transaction's validity.
|
||||||
|
|
||||||
Time-windows
|
Time-window
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
In some cases, we want a transaction proposed to only be approved during a certain time-window. For example:
|
In some cases, we want a transaction proposed to only be approved during a certain time-window. For example:
|
||||||
|
|
||||||
* An option can only be exercised after a certain date
|
* An option can only be exercised after a certain date
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 294 KiB After Width: | Height: | Size: 200 KiB |
@ -44,7 +44,7 @@ Transactions in Corda contain a number of elements:
|
|||||||
transactions to migrate the states across to a consistent notary node
|
transactions to migrate the states across to a consistent notary node
|
||||||
before being allowed to mutate any states)
|
before being allowed to mutate any states)
|
||||||
|
|
||||||
7. Optionally a timestamp that can used by the notary to bound the
|
7. Optionally a time-window that can used by the notary to bound the
|
||||||
period during which the proposed transaction can be committed to the
|
period during which the proposed transaction can be committed to the
|
||||||
ledger
|
ledger
|
||||||
|
|
||||||
|
@ -299,13 +299,13 @@ logic.
|
|||||||
|
|
||||||
This loop is the core logic of the contract.
|
This loop is the core logic of the contract.
|
||||||
|
|
||||||
The first line simply gets the timestamp out of the transaction. Timestamping of transactions is optional, so a time
|
The first line simply gets the time-window out of the transaction. Setting a time-window in transactions is optional, so a time
|
||||||
may be missing here. We check for it being null later.
|
may be missing here. We check for it being null later.
|
||||||
|
|
||||||
.. warning:: In the Kotlin version as long as we write a comparison with the transaction time first the compiler will
|
.. warning:: In the Kotlin version as long as we write a comparison with the transaction time first the compiler will
|
||||||
verify we didn't forget to check if it's missing. Unfortunately due to the need for smooth Java interop, this
|
verify we didn't forget to check if it's missing. Unfortunately due to the need for smooth Java interop, this
|
||||||
check won't happen if we write e.g. ``someDate > time``, it has to be ``time < someDate``. So it's good practice to
|
check won't happen if we write e.g. ``someDate > time``, it has to be ``time < someDate``. So it's good practice to
|
||||||
always write the transaction timestamp first.
|
always write the transaction time-window first.
|
||||||
|
|
||||||
Next, we take one of three paths, depending on what the type of the command object is.
|
Next, we take one of three paths, depending on what the type of the command object is.
|
||||||
|
|
||||||
@ -597,7 +597,7 @@ The time-lock contract mentioned above can be implemented very simply:
|
|||||||
class TestTimeLock : Contract {
|
class TestTimeLock : Contract {
|
||||||
...
|
...
|
||||||
override fun verify(tx: LedgerTransaction) {
|
override fun verify(tx: LedgerTransaction) {
|
||||||
val time = tx.timestamp.before ?: throw IllegalStateException(...)
|
val time = tx.timeWindow?.untilTime ?: throw IllegalStateException(...)
|
||||||
...
|
...
|
||||||
requireThat {
|
requireThat {
|
||||||
"the time specified in the time-lock has passed" by
|
"the time specified in the time-lock has passed" by
|
||||||
|
Loading…
x
Reference in New Issue
Block a user