On an EVM network, the full details of every transaction added to the blockchain are broadcast to all network peers, and the format in which transactions are expressed is relatively transparent. Events written to the transaction log are widely accepted as a way of making details of the transaction that are of interest to off-network parties visible and verifiable. These events cannot easily be forged, because the validators on the network will not accept a transaction (including event log entries) that they cannot reproduce by running the contract function code which created that transaction themselves. (Note that a poorly-written contract might enable spurious log events to be produced: we must have confidence that the meaning of a given event is secured by the contract responsible for generating it).
With Corda things are more difficult, firstly because there is no equivalent global broadcast of transactions, and no central observer that sees the full details of every transaction. The notary, unless designated a "validating" notary, sees a Merkle tree from which branches containing sensitive transaction data have been "torn off" leaving only hashes behind. This is by design: it is part of Corda's privacy model.
Secondly, the format in which transactions are expressed is relatively opaque. A Corda client holding the Corda serialisation code and the application-defined JVM classes which were serialised in creating the transaction data can recover the full transaction structure as a JVM object graph, which is then inspected by the contract verification code in the CorDapp. A non-Corda (possibly non-JVM) client will have to parse an AMQP Proton data stream, containing both the serialised data values and schema information encoded in a custom XML format, and interpret the serialised values in light of the attached schema definitions, to retrieve the transaction data in a meaningfully structured format. These operations are difficult and expensive (if gas costs are a consideration) to perform in Solidity code in an EVM contract.
However, it turns out that the relative opacity of serialised Corda transactions is not a major issue in practice. This is because the reason a Solidity contract needs to inspect a Corda transaction is usually to ensure that it is associated with the same "business transaction" (eg a trade in the real world) that the Solidity contract is managing. In some cases, the Corda transaction ID can be securely furnished to the Solidity contract, which then merely needs to satisfy itself that the Transaction has been signed and notarised by the expected parties. In other cases, the Solidity contract may not know or care what the Corda transaction ID is; it just needs to know that all the parties to a Corda transaction associated with the overarching business transaction (eg trade) signed and notarised it. Is is this second scenario we are concerned with here.
An externally verifiable transaction property, in the case of obfuscated Corda transaction data, is a value appearing as a sequence of bytes at a known position within the serialised transaction byte stream, which unforgeably indicates the presence of that value as a specific property of the transaction itself. For this to be useful, we must be certain that:
- The value could not be accidentally present at the given position, or maliciously caused to appear there, even though the transaction itself did not actually contain that value for the specified property.
- If the value could have been assigned to a different property of the transaction, it would appear at a different position in that case.
The entire transaction need not be serialised and inspected. since it already has a Merkle tree structure in which input and output states appear in separate branches. We will typically want to demonstrate the presence of the required property in a particular output state, and show a Merkle inclusion proof demonstrating that this output state was indeed included in the total transaction whose root hash was signed by the notary.
An externally verifiable transaction property can be used to associate a Corda transaction with a particular approved trade identity that is then "marked off" by the observing EVM contract, so that the same Corda transaction cannot be used to complete multiple trades on the EVM side.
This approach is brittle in some regards. An upgrade to a CorDapp, for example introducing a new field to a class representing a transaction state or command, might change the position at which the value assigned to the specified property was written in the serialied transaction stream. A more resilient approach to consider in future might be to represent property values pertaining to a transaction as labelled nodes within a Merkle tree, such that other property values could be "torn off" (replaced with hashes) and a Merkle inclusion proof used to validate the presence of a particular value for a particular property.