mirror of
https://github.com/corda/corda.git
synced 2025-04-07 19:34:41 +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>
|
||||
get() {
|
||||
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)) {
|
||||
commandKeys + notary.owningKey
|
||||
} 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
|
||||
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
|
||||
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*
|
||||
|
||||
|
@ -56,7 +56,7 @@ public class CommercialPaper implements Contract {
|
||||
});
|
||||
} else if (cmd.getValue() instanceof Commands.Issue) {
|
||||
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();
|
||||
requireThat(require -> {
|
||||
// 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.
|
||||
|
||||
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
|
||||
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,
|
||||
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
|
||||
* 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
|
||||
|
@ -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
|
||||
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
|
||||
together. It’s visible on the example image below, where ``H`` denotes sha256 function, "+" - concatenation.
|
||||
|
||||
.. 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
|
||||
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
|
||||
@ -73,7 +73,7 @@ obtained belongs to that particular transaction.
|
||||
.. 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
|
||||
``H(g)`` is also included since it's holding timestamp 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
|
||||
``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 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
|
||||
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
|
||||
* 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
|
||||
attachments and a timestamp, may look as follows:
|
||||
attachments and a time-window, may look as follows:
|
||||
|
||||
.. image:: resources/full-tx.png
|
||||
: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
|
||||
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:
|
||||
|
||||
* 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
|
||||
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
|
||||
ledger
|
||||
|
||||
|
@ -299,13 +299,13 @@ logic.
|
||||
|
||||
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.
|
||||
|
||||
.. 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
|
||||
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.
|
||||
|
||||
@ -597,7 +597,7 @@ The time-lock contract mentioned above can be implemented very simply:
|
||||
class TestTimeLock : Contract {
|
||||
...
|
||||
override fun verify(tx: LedgerTransaction) {
|
||||
val time = tx.timestamp.before ?: throw IllegalStateException(...)
|
||||
val time = tx.timeWindow?.untilTime ?: throw IllegalStateException(...)
|
||||
...
|
||||
requireThat {
|
||||
"the time specified in the time-lock has passed" by
|
||||
|
Loading…
x
Reference in New Issue
Block a user