2016-11-25 14:29:13 +00:00
|
|
|
|
Transaction tear-offs
|
|
|
|
|
=====================
|
2016-11-08 17:45:28 +00:00
|
|
|
|
|
|
|
|
|
One of the basic data structures in our platform is a transaction. It can be passed around to be signed and verified,
|
|
|
|
|
also by third parties. The construction of transactions assumes that they form a whole entity with input and output states,
|
|
|
|
|
commands and attachments inside. However all sensitive data shouldn’t be revealed to other nodes that take part in
|
|
|
|
|
the creation of transaction on validation level (a good example of this situation is the Oracle which validates only
|
2017-01-06 11:05:37 +00:00
|
|
|
|
embedded commands). How to achieve it in a way that convinces the other party the data they got for signing really did form
|
2016-11-08 17:45:28 +00:00
|
|
|
|
a part of the transaction?
|
|
|
|
|
|
|
|
|
|
We decided to use well known and described cryptographic scheme to provide proofs of inclusion and data integrity.
|
|
|
|
|
Merkle trees are widely used in peer-to-peer networks, blockchain systems and git.
|
|
|
|
|
You can read more on the concept `here <https://en.wikipedia.org/wiki/Merkle_tree>`_.
|
|
|
|
|
|
|
|
|
|
Merkle trees in Corda
|
2016-11-25 14:29:13 +00:00
|
|
|
|
---------------------
|
2016-11-08 17:45:28 +00:00
|
|
|
|
|
2017-02-03 14:02:51 +00:00
|
|
|
|
Transactions are split into leaves, each of them contains either input, output, command or attachment. Additionally, in
|
|
|
|
|
transaction id calculation we use other fields of ``WireTransaction`` like timestamp, notary, type and 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.
|
2016-11-08 17:45:28 +00:00
|
|
|
|
|
|
|
|
|
.. image:: resources/merkleTree.png
|
|
|
|
|
|
2017-02-03 14:02:51 +00:00
|
|
|
|
The transaction has two input states, one of output, attachment and command each and timestamp. 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 green above.
|
2016-11-08 17:45:28 +00:00
|
|
|
|
Finally, the hash of the root is the identifier of the transaction, it's also used for signing and verification of data integrity.
|
|
|
|
|
Every change in transaction on a leaf level will change its identifier.
|
|
|
|
|
|
|
|
|
|
Hiding data
|
|
|
|
|
-----------
|
|
|
|
|
|
|
|
|
|
Hiding data and providing the proof that it formed a part of a transaction is done by constructing Partial Merkle Trees
|
|
|
|
|
(or Merkle branches). A Merkle branch is a set of hashes, that given the leaves’ data, is used to calculate the root’s hash.
|
|
|
|
|
Then that hash is compared with the hash of a whole transaction and if they match it means that data we obtained belongs
|
|
|
|
|
to that particular transaction.
|
|
|
|
|
|
|
|
|
|
.. image:: resources/partialMerkle.png
|
|
|
|
|
|
2017-02-03 14:02:51 +00:00
|
|
|
|
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 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.
|
2016-11-08 17:45:28 +00:00
|
|
|
|
|
|
|
|
|
Example of usage
|
2016-11-25 14:29:13 +00:00
|
|
|
|
----------------
|
2016-11-08 17:45:28 +00:00
|
|
|
|
|
|
|
|
|
Let’s focus on a code example. We want to construct a transaction with commands containing interest rate fix data as in:
|
|
|
|
|
:doc:`oracles`.
|
|
|
|
|
After construction of a partial transaction, with included ``Fix`` commands in it, we want to send it to the Oracle for checking
|
|
|
|
|
and signing. To do so we need to specify which parts of the transaction are going to be revealed. That can be done by constructing
|
2017-02-03 14:02:51 +00:00
|
|
|
|
filtering function over fields of ``WireTransaction`` of type ``(Any) -> Boolean``.
|
2016-11-08 17:45:28 +00:00
|
|
|
|
|
|
|
|
|
.. container:: codeset
|
|
|
|
|
|
|
|
|
|
.. sourcecode:: kotlin
|
|
|
|
|
|
2017-02-03 14:02:51 +00:00
|
|
|
|
val partialTx = ...
|
2016-11-08 17:45:28 +00:00
|
|
|
|
val oracle: Party = ...
|
2017-02-03 14:02:51 +00:00
|
|
|
|
fun filtering(elem: Any): Boolean {
|
|
|
|
|
return when (elem) {
|
|
|
|
|
is Command -> oracleParty.owningKey in elem.signers && elem.value is Fix
|
|
|
|
|
else -> false
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-08 17:45:28 +00:00
|
|
|
|
|
|
|
|
|
Assuming that we already assembled partialTx with some commands and know the identity of Oracle service,
|
2017-02-03 14:02:51 +00:00
|
|
|
|
we construct filtering function over commands - ``filtering``. It performs type checking and filters only ``Fix`` commands
|
|
|
|
|
as in IRSDemo example. Then we can construct ``FilteredTransaction``:
|
2016-11-08 17:45:28 +00:00
|
|
|
|
|
|
|
|
|
.. container:: codeset
|
|
|
|
|
|
|
|
|
|
.. sourcecode:: kotlin
|
|
|
|
|
|
|
|
|
|
val wtx: WireTransaction = partialTx.toWireTransaction()
|
2017-02-03 14:02:51 +00:00
|
|
|
|
val ftx: FilteredTransaction = wtx.buildFilteredTransaction(filtering)
|
2016-11-08 17:45:28 +00:00
|
|
|
|
|
2017-02-03 14:02:51 +00:00
|
|
|
|
In the Oracle example this step takes place in ``RatesFixFlow`` by overriding ``filtering`` function, see: :ref:`filtering_ref`
|
2016-11-08 17:45:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
``FilteredTransaction`` holds ``filteredLeaves`` (data that we wanted to reveal) and Merkle branch for them.
|
|
|
|
|
|
|
|
|
|
.. container:: codeset
|
|
|
|
|
|
|
|
|
|
.. sourcecode:: kotlin
|
|
|
|
|
|
2017-02-03 14:02:51 +00:00
|
|
|
|
// Direct accsess to included commands, inputs, outputs, attachments etc.
|
2016-11-08 17:45:28 +00:00
|
|
|
|
val cmds: List<Command> = ftx.filteredLeaves.commands
|
|
|
|
|
val ins: List<StateRef> = ftx.filteredLeaves.inputs
|
2017-02-03 14:02:51 +00:00
|
|
|
|
val timestamp: Timestamp? = ftx.filteredLeaves.timestamp
|
|
|
|
|
...
|
2016-11-08 17:45:28 +00:00
|
|
|
|
|
2017-02-03 14:02:51 +00:00
|
|
|
|
.. literalinclude:: ../../samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt
|
|
|
|
|
:language: kotlin
|
|
|
|
|
:start-after: DOCSTART 1
|
|
|
|
|
:end-before: DOCEND 1
|
2016-11-08 17:45:28 +00:00
|
|
|
|
|
2017-02-03 14:02:51 +00:00
|
|
|
|
Above code snippet is taken from ``NodeInterestRates.kt`` file and implements a signing part of an Oracle.
|
|
|
|
|
You can check only leaves using ``leaves.checkWithFun { check(it) }`` and then verify obtained ``FilteredTransaction``
|
|
|
|
|
to see if data from ``PartialMerkleTree`` belongs to ``WireTransaction`` with provided ``id``. All you need is the root hash
|
|
|
|
|
of the full transaction:
|
2016-11-08 17:45:28 +00:00
|
|
|
|
|
|
|
|
|
.. container:: codeset
|
|
|
|
|
|
|
|
|
|
.. sourcecode:: kotlin
|
|
|
|
|
|
|
|
|
|
if (!ftx.verify(merkleRoot)){
|
|
|
|
|
throw MerkleTreeException("Rate Fix Oracle: Couldn't verify partial Merkle tree.")
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-03 14:02:51 +00:00
|
|
|
|
Or combine the two steps together:
|
|
|
|
|
|
|
|
|
|
.. container:: codeset
|
|
|
|
|
|
|
|
|
|
.. sourcecode:: kotlin
|
|
|
|
|
|
|
|
|
|
ftx.verifyWithFunction(merkleRoot, ::check)
|
2016-11-08 17:45:28 +00:00
|
|
|
|
|
|
|
|
|
.. note:: The way the ``FilteredTransaction`` is constructed ensures that after signing of the root hash it's impossible to add or remove
|
|
|
|
|
leaves. However, it can happen that having transaction with multiple commands one party reveals only subset of them to the Oracle.
|
2017-01-06 11:05:37 +00:00
|
|
|
|
As signing is done now over the Merkle root hash, the service signs all commands of given type, even though it didn't see
|
2016-11-08 17:45:28 +00:00
|
|
|
|
all of them. This issue will be handled after implementing partial signatures.
|