2016-03-11 12:42:11 +00:00
|
|
|
.. highlight:: kotlin
|
|
|
|
.. raw:: html
|
|
|
|
|
|
|
|
<script type="text/javascript" src="_static/jquery.js"></script>
|
|
|
|
<script type="text/javascript" src="_static/codesets.js"></script>
|
|
|
|
|
|
|
|
Writing oracle services
|
|
|
|
=======================
|
|
|
|
|
|
|
|
This article covers *oracles*: network services that link the ledger to the outside world by providing facts that
|
|
|
|
affect the validity of transactions.
|
|
|
|
|
2016-08-24 13:38:43 +00:00
|
|
|
The current prototype includes an example oracle that provides an interest rate fixing service. It is used by the
|
|
|
|
IRS trading demo app.
|
2016-03-11 12:42:11 +00:00
|
|
|
|
2017-01-06 11:05:37 +00:00
|
|
|
Introduction to oracles
|
|
|
|
-----------------------
|
2016-03-11 12:42:11 +00:00
|
|
|
|
|
|
|
Oracles are a key concept in the block chain/decentralised ledger space. They can be essential for many kinds of
|
2017-10-01 22:33:15 +00:00
|
|
|
application, because we often wish to condition the validity of a transaction on some fact being true or false, but the ledger itself
|
2016-03-11 12:42:11 +00:00
|
|
|
has a design that is essentially functional: all transactions are *pure* and *immutable*. Phrased another way, a
|
2017-10-01 22:33:15 +00:00
|
|
|
contract cannot perform any input/output or depend on any state outside of the transaction itself. For example, there is no
|
|
|
|
way to download a web page or interact with the user from within a contract. It must be this way because everyone must
|
|
|
|
be able to independently check a transaction and arrive at an identical conclusion regarding its validity for the ledger to maintain its
|
2016-03-11 12:42:11 +00:00
|
|
|
integrity: if a transaction could evaluate to "valid" on one computer and then "invalid" a few minutes later on a
|
|
|
|
different computer, the entire shared ledger concept wouldn't work.
|
|
|
|
|
2017-10-01 22:33:15 +00:00
|
|
|
But transaction validity does often depend on data from the outside world - verifying that an
|
2016-03-11 12:42:11 +00:00
|
|
|
interest rate swap is paying out correctly may require data on interest rates, verifying that a loan has reached
|
|
|
|
maturity requires knowledge about the current time, knowing which side of a bet receives the payment may require
|
2017-10-01 22:33:15 +00:00
|
|
|
arbitrary facts about the real world (e.g. the bankruptcy or solvency of a company or country), and so on.
|
2016-03-11 12:42:11 +00:00
|
|
|
|
|
|
|
We can solve this problem by introducing services that create digitally signed data structures which assert facts.
|
|
|
|
These structures can then be used as an input to a transaction and distributed with the transaction data itself. Because
|
|
|
|
the statements are themselves immutable and signed, it is impossible for an oracle to change its mind later and
|
|
|
|
invalidate transactions that were previously found to be valid. In contrast, consider what would happen if a contract
|
|
|
|
could do an HTTP request: it's possible that an answer would change after being downloaded, resulting in loss of
|
2017-10-01 22:33:15 +00:00
|
|
|
consensus.
|
2016-03-11 12:42:11 +00:00
|
|
|
|
|
|
|
The two basic approaches
|
2017-01-06 11:05:37 +00:00
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
2016-03-11 12:42:11 +00:00
|
|
|
|
|
|
|
The architecture provides two ways of implementing oracles with different tradeoffs:
|
|
|
|
|
|
|
|
1. Using commands
|
|
|
|
2. Using attachments
|
|
|
|
|
|
|
|
When a fact is encoded in a command, it is embedded in the transaction itself. The oracle then acts as a co-signer to
|
|
|
|
the entire transaction. The oracle's signature is valid only for that transaction, and thus even if a fact (like a
|
|
|
|
stock price) does not change, every transaction that incorporates that fact must go back to the oracle for signing.
|
|
|
|
|
2016-08-24 13:38:43 +00:00
|
|
|
When a fact is encoded as an attachment, it is a separate object to the transaction and is referred to by hash.
|
2016-03-11 12:42:11 +00:00
|
|
|
Nodes download attachments from peers at the same time as they download transactions, unless of course the node has
|
|
|
|
already seen that attachment, in which case it won't fetch it again. Contracts have access to the contents of
|
2016-08-24 13:38:43 +00:00
|
|
|
attachments when they run.
|
|
|
|
|
|
|
|
.. note:: Currently attachments do not support digital signing, but this is a planned feature.
|
2016-03-11 12:42:11 +00:00
|
|
|
|
|
|
|
As you can see, both approaches share a few things: they both allow arbitrary binary data to be provided to transactions
|
|
|
|
(and thus contracts). The primary difference is whether the data is a freely reusable, standalone object or whether it's
|
|
|
|
integrated with a transaction.
|
|
|
|
|
|
|
|
Here's a quick way to decide which approach makes more sense for your data source:
|
|
|
|
|
|
|
|
* Is your data *continuously changing*, like a stock price, the current time, etc? If yes, use a command.
|
|
|
|
* Is your data *commercially valuable*, like a feed which you are not allowed to resell unless it's incorporated into
|
|
|
|
a business deal? If yes, use a command, so you can charge money for signing the same fact in each unique business
|
|
|
|
context.
|
|
|
|
* Is your data *very small*, like a single number? If yes, use a command.
|
|
|
|
* Is your data *large*, *static* and *commercially worthless*, for instance, a holiday calendar? If yes, use an
|
|
|
|
attachment.
|
|
|
|
* Is your data *intended for human consumption*, like a PDF of legal prose, or an Excel spreadsheet? If yes, use an
|
|
|
|
attachment.
|
|
|
|
|
2016-08-24 13:38:43 +00:00
|
|
|
Asserting continuously varying data
|
2017-01-06 11:05:37 +00:00
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2016-03-11 12:42:11 +00:00
|
|
|
|
2016-08-24 13:38:43 +00:00
|
|
|
Let's look at the interest rates oracle that can be found in the ``NodeInterestRates`` file. This is an example of
|
|
|
|
an oracle that uses a command because the current interest rate fix is a constantly changing fact.
|
2016-03-11 12:42:11 +00:00
|
|
|
|
2017-10-01 22:33:15 +00:00
|
|
|
The obvious way to implement such a service is this:
|
2016-03-11 12:42:11 +00:00
|
|
|
|
2016-08-24 13:38:43 +00:00
|
|
|
1. The creator of the transaction that depends on the interest rate sends it to the oracle.
|
|
|
|
2. The oracle inserts a command with the rate and signs the transaction.
|
|
|
|
3. The oracle sends it back.
|
2016-03-11 12:42:11 +00:00
|
|
|
|
2016-08-24 13:38:43 +00:00
|
|
|
But this has a problem - it would mean that the oracle has to be the first entity to sign the transaction, which might impose
|
|
|
|
ordering constraints we don't want to deal with (being able to get all parties to sign in parallel is a very nice thing).
|
|
|
|
So the way we actually implement it is like this:
|
2016-03-11 12:42:11 +00:00
|
|
|
|
2016-08-24 13:38:43 +00:00
|
|
|
1. The creator of the transaction that depends on the interest rate asks for the current rate. They can abort at this point
|
|
|
|
if they want to.
|
|
|
|
2. They insert a command with that rate and the time it was obtained into the transaction.
|
2016-11-25 13:35:05 +00:00
|
|
|
3. They then send it to the oracle for signing, along with everyone else, potentially in parallel. The oracle checks that
|
|
|
|
the command has the correct data for the asserted time, and signs if so.
|
2016-03-11 12:42:11 +00:00
|
|
|
|
|
|
|
This same technique can be adapted to other types of oracle.
|
|
|
|
|
2016-08-24 13:38:43 +00:00
|
|
|
The oracle consists of a core class that implements the query/sign operations (for easy unit testing), and then a separate
|
|
|
|
class that binds it to the network layer.
|
2016-03-11 12:42:11 +00:00
|
|
|
|
2016-11-25 13:35:05 +00:00
|
|
|
Here is an extract from the ``NodeInterestRates.Oracle`` class and supporting types:
|
2016-03-11 12:42:11 +00:00
|
|
|
|
2017-10-01 22:33:15 +00:00
|
|
|
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt
|
|
|
|
:language: kotlin
|
|
|
|
:start-after: DOCSTART 1
|
|
|
|
:end-before: DOCEND 1
|
2016-03-11 12:42:11 +00:00
|
|
|
|
2017-10-01 22:33:15 +00:00
|
|
|
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt
|
|
|
|
:language: kotlin
|
|
|
|
:start-after: DOCSTART 2
|
|
|
|
:end-before: DOCEND 2
|
2016-03-11 12:42:11 +00:00
|
|
|
|
2017-10-01 22:33:15 +00:00
|
|
|
.. sourcecode:: kotlin
|
2016-03-11 12:42:11 +00:00
|
|
|
|
|
|
|
class Oracle {
|
2017-10-01 22:33:15 +00:00
|
|
|
fun query(queries: List<FixOf>): List<Fix>
|
2016-03-11 12:42:11 +00:00
|
|
|
|
2017-10-01 22:33:15 +00:00
|
|
|
fun sign(ftx: FilteredTransaction): TransactionSignature
|
2016-03-11 12:42:11 +00:00
|
|
|
}
|
|
|
|
|
2017-10-01 22:33:15 +00:00
|
|
|
The fix contains a timestamp (the ``forDay`` field) that identifies the version of the data being requested. Since
|
|
|
|
there can be an arbitrary delay between a fix being requested via ``query`` and the signature being requested via
|
|
|
|
``sign``, this timestamp allows the Oracle to know which, potentially historical, value it is being asked to sign for. This is an
|
|
|
|
important technique for continuously varying data.
|
2016-11-25 13:35:05 +00:00
|
|
|
|
|
|
|
Hiding transaction data from the oracle
|
2017-01-06 11:05:37 +00:00
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2016-11-25 13:35:05 +00:00
|
|
|
|
|
|
|
Because the transaction is sent to the oracle for signing, ordinarily the oracle would be able to see the entire contents
|
|
|
|
of that transaction including the inputs, output contract states and all the commands, not just the one (in this case)
|
2017-10-01 22:33:15 +00:00
|
|
|
relevant command. This is an obvious privacy leak for the other participants. We currently solve this using a
|
|
|
|
``FilteredTransaction``, which implements a Merkle Tree. These reveal only the necessary parts of the transaction to the
|
|
|
|
oracle but still allow it to sign it by providing the Merkle hashes for the remaining parts. See :doc:`key-concepts-oracles`
|
|
|
|
for more details.
|
2016-03-11 12:42:11 +00:00
|
|
|
|
2016-08-24 13:38:43 +00:00
|
|
|
Pay-per-play oracles
|
2017-01-06 11:05:37 +00:00
|
|
|
~~~~~~~~~~~~~~~~~~~~
|
2016-03-11 12:42:11 +00:00
|
|
|
|
2016-08-24 13:38:43 +00:00
|
|
|
Because the signature covers the transaction, and transactions may end up being forwarded anywhere, the fact itself
|
|
|
|
is independently checkable. However, this approach can still be useful when the data itself costs money, because the act
|
|
|
|
of issuing the signature in the first place can be charged for (e.g. by requiring the submission of a fresh
|
|
|
|
``Cash.State`` that has been re-assigned to a key owned by the oracle service). Because the signature covers the
|
2017-10-01 22:33:15 +00:00
|
|
|
*transaction* and not only the *fact*, this allows for a kind of weak pseudo-DRM over data feeds. Whilst a
|
2016-08-24 13:38:43 +00:00
|
|
|
contract could in theory include a transaction parsing and signature checking library, writing a contract in this way
|
|
|
|
would be conclusive evidence of intent to disobey the rules of the service (*res ipsa loquitur*). In an environment
|
|
|
|
where parties are legally identifiable, usage of such a contract would by itself be sufficient to trigger some sort of
|
|
|
|
punishment.
|
2016-11-25 13:35:05 +00:00
|
|
|
|
|
|
|
Implementing an oracle with continuously varying data
|
2017-01-06 11:05:37 +00:00
|
|
|
-----------------------------------------------------
|
2016-11-25 13:35:05 +00:00
|
|
|
|
|
|
|
Implement the core classes
|
2017-01-06 11:05:37 +00:00
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2016-11-25 13:35:05 +00:00
|
|
|
|
|
|
|
The key is to implement your oracle in a similar way to the ``NodeInterestRates.Oracle`` outline we gave above with
|
2017-10-01 22:33:15 +00:00
|
|
|
both a ``query`` and a ``sign`` method. Typically you would want one class that encapsulates the parameters to the ``query``
|
|
|
|
method (``FixOf``, above), and a ``CommandData`` implementation (``Fix``, above) that encapsulates both an instance of
|
2016-11-25 13:35:05 +00:00
|
|
|
that parameter class and an instance of whatever the result of the ``query`` is (``BigDecimal`` above).
|
|
|
|
|
2017-10-01 22:33:15 +00:00
|
|
|
The ``NodeInterestRates.Oracle`` allows querying for multiple ``Fix`` objects but that is not necessary and is
|
|
|
|
provided for the convenience of callers who need multiple fixes and want to be able to do it all in one query request.
|
2016-11-25 13:35:05 +00:00
|
|
|
|
|
|
|
Assuming you have a data source and can query it, it should be very easy to implement your ``query`` method and the
|
|
|
|
parameter and ``CommandData`` classes.
|
|
|
|
|
|
|
|
Let's see how the ``sign`` method for ``NodeInterestRates.Oracle`` is written:
|
|
|
|
|
2018-03-02 16:16:26 +00:00
|
|
|
.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt
|
2016-11-25 13:35:05 +00:00
|
|
|
:language: kotlin
|
|
|
|
:start-after: DOCSTART 1
|
|
|
|
:end-before: DOCEND 1
|
2017-10-01 22:33:15 +00:00
|
|
|
:dedent: 8
|
2016-11-25 13:35:05 +00:00
|
|
|
|
|
|
|
Here we can see that there are several steps:
|
|
|
|
|
|
|
|
1. Ensure that the transaction we have been sent is indeed valid and passes verification, even though we cannot see all
|
2017-10-01 22:33:15 +00:00
|
|
|
of it
|
2016-11-25 13:35:05 +00:00
|
|
|
2. Check that we only received commands as expected, and each of those commands expects us to sign for them and is of
|
2017-10-01 22:33:15 +00:00
|
|
|
the expected type (``Fix`` here)
|
2016-11-25 13:35:05 +00:00
|
|
|
3. Iterate over each of the commands we identified in the last step and check that the data they represent matches
|
|
|
|
exactly our data source. The final step, assuming we have got this far, is to generate a signature for the
|
2017-10-01 22:33:15 +00:00
|
|
|
transaction and return it
|
2016-11-25 13:35:05 +00:00
|
|
|
|
2017-05-19 15:14:48 +00:00
|
|
|
Binding to the network
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
2016-11-25 13:35:05 +00:00
|
|
|
|
|
|
|
.. note:: Before reading any further, we advise that you understand the concept of flows and how to write them and use
|
|
|
|
them. See :doc:`flow-state-machines`. Likewise some understanding of Cordapps, plugins and services will be helpful.
|
2017-06-05 12:37:23 +00:00
|
|
|
See :doc:`running-a-node`.
|
2016-11-25 13:35:05 +00:00
|
|
|
|
2017-05-19 15:14:48 +00:00
|
|
|
The first step is to create the oracle as a service by annotating its class with ``@CordaService``. Let's see how that's
|
|
|
|
done:
|
2016-11-25 13:35:05 +00:00
|
|
|
|
2018-03-02 16:16:26 +00:00
|
|
|
.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt
|
2016-11-25 13:35:05 +00:00
|
|
|
:language: kotlin
|
2017-05-19 15:14:48 +00:00
|
|
|
:start-after: DOCSTART 3
|
|
|
|
:end-before: DOCEND 3
|
2017-10-01 22:33:15 +00:00
|
|
|
:dedent: 4
|
2016-11-25 13:35:05 +00:00
|
|
|
|
2017-05-19 15:14:48 +00:00
|
|
|
The Corda node scans for any class with this annotation and initialises them. The only requirement is that the class provide
|
2017-09-14 08:00:02 +00:00
|
|
|
a constructor with a single parameter of type ``ServiceHub``.
|
2016-11-25 13:35:05 +00:00
|
|
|
|
2018-03-02 16:16:26 +00:00
|
|
|
.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt
|
2017-05-19 15:14:48 +00:00
|
|
|
:language: kotlin
|
|
|
|
:start-after: DOCSTART 2
|
|
|
|
:end-before: DOCEND 2
|
2017-10-01 22:33:15 +00:00
|
|
|
:dedent: 4
|
2016-11-25 13:35:05 +00:00
|
|
|
|
2017-05-19 15:14:48 +00:00
|
|
|
These two flows leverage the oracle to provide the querying and signing operations. They get reference to the oracle,
|
2017-10-01 22:33:15 +00:00
|
|
|
which will have already been initialised by the node, using ``ServiceHub.cordaService``. Both flows are annotated with
|
2017-05-19 15:14:48 +00:00
|
|
|
``@InitiatedBy``. This tells the node which initiating flow (which are discussed in the next section) they are meant to
|
|
|
|
be executed with.
|
2016-11-25 13:35:05 +00:00
|
|
|
|
2017-05-19 15:14:48 +00:00
|
|
|
Providing sub-flows for querying and signing
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2016-11-25 13:35:05 +00:00
|
|
|
|
|
|
|
We mentioned the client sub-flow briefly above. They are the mechanism that clients, in the form of other flows, will
|
2017-10-01 22:33:15 +00:00
|
|
|
use to interact with your oracle. Typically there will be one for querying and one for signing. Let's take a look at
|
2016-11-25 13:35:05 +00:00
|
|
|
those for ``NodeInterestRates.Oracle``.
|
|
|
|
|
2018-03-02 16:16:26 +00:00
|
|
|
.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/flows/RatesFixFlow.kt
|
2016-11-25 13:35:05 +00:00
|
|
|
:language: kotlin
|
|
|
|
:start-after: DOCSTART 1
|
|
|
|
:end-before: DOCEND 1
|
2017-10-01 22:33:15 +00:00
|
|
|
:dedent: 4
|
2016-11-25 13:35:05 +00:00
|
|
|
|
2017-02-03 14:02:51 +00:00
|
|
|
You'll note that the ``FixSignFlow`` requires a ``FilterTransaction`` instance which includes only ``Fix`` commands.
|
2017-10-01 22:33:15 +00:00
|
|
|
You can find a further explanation of this in :doc:`key-concepts-oracles`. Below you will see how to build such a
|
|
|
|
transaction with hidden fields.
|
2017-02-03 14:02:51 +00:00
|
|
|
|
|
|
|
.. _filtering_ref:
|
2016-11-25 13:35:05 +00:00
|
|
|
|
|
|
|
Using an oracle
|
2017-01-06 11:05:37 +00:00
|
|
|
---------------
|
2016-11-25 13:35:05 +00:00
|
|
|
|
|
|
|
The oracle is invoked through sub-flows to query for values, add them to the transaction as commands and then get
|
2017-01-06 11:05:37 +00:00
|
|
|
the transaction signed by the oracle. Following on from the above examples, this is all encapsulated in a sub-flow
|
2016-11-25 13:35:05 +00:00
|
|
|
called ``RatesFixFlow``. Here's the ``call`` method of that flow.
|
|
|
|
|
2018-03-02 16:16:26 +00:00
|
|
|
.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/flows/RatesFixFlow.kt
|
2016-11-25 13:35:05 +00:00
|
|
|
:language: kotlin
|
|
|
|
:start-after: DOCSTART 2
|
|
|
|
:end-before: DOCEND 2
|
2017-10-01 22:33:15 +00:00
|
|
|
:dedent: 4
|
2016-11-25 13:35:05 +00:00
|
|
|
|
|
|
|
As you can see, this:
|
|
|
|
|
2017-10-01 22:33:15 +00:00
|
|
|
1. Queries the oracle for the fact using the client sub-flow for querying defined above
|
|
|
|
2. Does some quick validation
|
|
|
|
3. Adds the command to the transaction containing the fact to be signed for by the oracle
|
|
|
|
4. Calls an extension point that allows clients to generate output states based on the fact from the oracle
|
|
|
|
5. Builds filtered transaction based on filtering function extended from ``RatesFixFlow``
|
|
|
|
6. Requests the signature from the oracle using the client sub-flow for signing from above
|
2016-11-25 13:35:05 +00:00
|
|
|
|
|
|
|
Here's an example of it in action from ``FixingFlow.Fixer``.
|
|
|
|
|
2018-03-02 16:16:26 +00:00
|
|
|
.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt
|
2016-11-25 13:35:05 +00:00
|
|
|
:language: kotlin
|
|
|
|
:start-after: DOCSTART 1
|
2017-01-18 12:42:22 +00:00
|
|
|
:end-before: DOCEND 1
|
2017-10-01 22:33:15 +00:00
|
|
|
:dedent: 4
|
2017-02-03 14:02:51 +00:00
|
|
|
|
|
|
|
.. note::
|
|
|
|
When overriding be careful when making the sub-class an anonymous or inner class (object declarations in Kotlin),
|
|
|
|
because that kind of classes can access variables from the enclosing scope and cause serialization problems when
|
|
|
|
checkpointed.
|
2017-06-02 14:47:20 +00:00
|
|
|
|
|
|
|
Testing
|
|
|
|
-------
|
|
|
|
|
2018-04-06 13:24:31 +00:00
|
|
|
The ``MockNetwork`` allows the creation of ``MockNode`` instances, which are simplified nodes which can be used for
|
|
|
|
testing (see :doc:`api-testing`). When creating the ``MockNetwork`` you supply a list of packages to scan for CorDapps.
|
|
|
|
Make sure the packages you provide include your oracle service, and it automatically be installed in the test nodes.
|
|
|
|
Then you can create an oracle node on the ``MockNetwork`` and insert any initialisation logic you want to use. In this
|
|
|
|
case, our ``Oracle`` service is in the ``net.corda.irs.api`` package, so the following test setup will install
|
|
|
|
the service in each node. Then an oracle node with an oracle service which is initialised with some data is created on
|
|
|
|
the mock network:
|
|
|
|
|
|
|
|
.. literalinclude:: ../../samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/OracleNodeTearOffTests.kt
|
|
|
|
:language: kotlin
|
|
|
|
:start-after: DOCSTART 1
|
|
|
|
:end-before: DOCEND 1
|
|
|
|
:dedent: 4
|
|
|
|
|
|
|
|
You can then write tests on your mock network to verify the nodes interact with your Oracle correctly.
|
|
|
|
|
|
|
|
.. literalinclude:: ../../samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/OracleNodeTearOffTests.kt
|
|
|
|
:language: kotlin
|
|
|
|
:start-after: DOCSTART 2
|
|
|
|
:end-before: DOCEND 2
|
|
|
|
:dedent: 4
|
|
|
|
|
|
|
|
See `here <https://github.com/corda/corda/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/OracleNodeTearOffTests.kt>`_ for more examples.
|