diff --git a/docs/build/html/_sources/consensus.txt b/docs/build/html/_sources/consensus.txt
index 85e789acf9..8b2da03b7d 100644
--- a/docs/build/html/_sources/consensus.txt
+++ b/docs/build/html/_sources/consensus.txt
@@ -14,7 +14,7 @@ This article presents an initial model for addressing the **uniqueness** problem
Notary
------
-We introduce the concept of a **Notary**, which is an authority responsible for attesting that for a given transaction, it had not signed another transaction consuming any of its input states.
+We introduce the concept of a **notary**, which is an authority responsible for attesting that for a given transaction, it had not signed another transaction consuming any of its input states.
The data model is extended so that every **state** has an appointed notary:
.. sourcecode:: kotlin
@@ -67,16 +67,21 @@ This is an obvious privacy leak.
Our platform is flexible and we currently support both validating and non-validating notary implementations -- a party can select which one to use based on its own privacy requirements.
-.. note:: In the non-validating model the "denial of state" attack is partially alleviated by requiring the calling party to authenticate and storing its identity for the request.
-The conflict information returned by the Notary specifies the consuming transaction id along with the identity of the party that had requested the commit.
- If the conflicting transaction is valid, the current one gets aborted; if not – a dispute can be raised and the input states of the conflicting invalid transaction are "un-committed" (to be covered by legal process).
+.. note:: In the non-validating model the "denial of state" attack is partially alleviated by requiring the calling
+ party to authenticate and storing its identity for the request. The conflict information returned by the notary
+ specifies the consuming transaction ID along with the identity of the party that had requested the commit. If the
+ conflicting transaction is valid, the current one gets aborted; if not – a dispute can be raised and the input states
+ of the conflicting invalid transaction are "un-committed" (to be covered by legal process).
-.. note:: At present all notaries can see the entire contents of a transaction, but we have a separate piece of work to replace the parts of the transaction it does not require knowing about with hashes (only input references, timestamp information, overall transaction ID and the necessary digests of the rest of the transaction to prove that the referenced inputs/timestamps really do form part of the stated transaction ID should be visible).
+.. note:: At present all notaries can see the entire contents of a transaction, but we have a separate piece of work to
+ replace the parts of the transaction it does not require knowing about with hashes (only input references, timestamp
+ information, overall transaction ID and the necessary digests of the rest of the transaction to prove that the
+ referenced inputs/timestamps really do form part of the stated transaction ID should be visible).
Timestamping
------------
-In this model the notary also acts as a **Timestamping Authority**, verifying the transaction timestamp command.
+In this model the notary also acts as a *timestamping authority*, verifying the transaction timestamp command.
For a timestamp to be meaningful, its implications must be binding on the party requesting it.
A party can obtain a timestamp signature in order to prove that some event happened before/on/or after a particular point in time.
@@ -84,6 +89,31 @@ However, if the party is not also compelled to commit to the associated transact
As a result, we need to ensure that the notary either has to also sign the transaction within some time tolerance,
or perform timestamping *and* notarisation at the same time, which is the chosen behaviour for this model.
+There will never be exact clock synchronisation between the party creating the transaction and the notary.
+This is not only due to physics, network latencies etc but because between inserting the command and getting the
+notary to sign there may be many other steps, like sending the transaction to other parties involved in the trade
+as well, or even requesting human signoff. Thus the time observed by the notary may be quite different to the
+time observed in step 1.
+
+For this reason, times in transactions are specified as time *windows*, not absolute times. Time windows can be
+open-ended, i.e. specify only one of "before" and "after" or they can be fully bounded. If a time window needs to
+be converted to an absolute time for e.g. display purposes, there is a utility method on ``Timestamp`` to
+calculate the mid point - but in a distributed system there can never be "true time", only an approximation of it.
+
+In this way we express that the *true value* of the fact "the current time" is actually unknowable. Even when both before and
+after times are included, the transaction could have occurred at any point between those two timestamps. Here
+"occurrence" could mean the execution date, the value date, the trade date etc ... the notary doesn't care what precise
+meaning the timestamp has to the contract.
+
+By creating a range that can be either closed or open at one end, we allow all of the following facts to be modelled:
+
+* This transaction occurred at some point after the given time (e.g. after a maturity event)
+* This transaction occurred at any time before the given time (e.g. before a bankruptcy event)
+* This transaction occurred at some point roughly around the given time (e.g. on a specific day)
+
+.. note:: It is assumed that the time feed for a notary is GPS/NaviStar time as defined by the atomic
+ clocks at the US Naval Observatory. This time feed is extremely accurate and available globally for free.
+
Running a Notary Service
------------------------
@@ -93,14 +123,29 @@ At present we have two basic implementations that store committed input states i
- ``ValidatingNotaryService`` -- retrieves and validates the whole transaction history (including the given transaction) before committing
-To run one of these services the node has to simply specify either ``SimpleNotaryService.Type`` or ``ValidatingNotaryService.Type`` in its ``advertisedServices`` set, and the correct type will be initialised.
-
Obtaining a signature
---------------------
-To obtain a signature from a notary use ``NotaryProtocol.Client``, passing in a ``WireTransaction``.
-The protocol will work out which notary needs to be called based on the input states and the timestamp command.
-For example, the following snippet can be used when writing a custom protocol:
+Once a transaction is built and ready to be finalised, normally you would call ``FinalityProtocol`` passing in a
+``SignedTransaction`` (including signatures from the participants) and a list of participants to notify. This requests a
+notary signature if needed, and then sends a copy of the notarised transaction to all participants for them to store.
+``FinalityProtocol`` delegates to ``NotaryProtocol.Client`` followed by ``BroadcastTransactionProtocol`` to do the
+actual work of notarising and broadcasting the transaction. For example:
+
+.. sourcecode:: kotlin
+
+ fun finaliseTransaction(serviceHub: ServiceHubInternal, ptx: TransactionBuilder, participants: Set)
+ : ListenableFuture {
+ // We conclusively cannot have all the signatures, as the notary has not signed yet
+ val tx = ptx.toSignedTransaction(checkSufficientSignatures = false)
+ // The empty set would be the trigger events, which are not used here
+ val protocol = FinalityProtocol(tx, emptySet(), participants)
+ return serviceHub.startProtocol("protocol.finalisation", protocol)
+ }
+
+To manually obtain a signature from a notary you can call ``NotaryProtocol.Client`` directly. The protocol will work out
+which notary needs to be called based on the input states and the timestamp command. For example, the following snippet
+can be used when writing a custom protocol:
.. sourcecode:: kotlin
@@ -144,4 +189,6 @@ The protocol will:
3. Obtain the *old* notary signature
-4. Record and distribute the final transaction to the participants so that everyone possesses the new state
\ No newline at end of file
+4. Record and distribute the final transaction to the participants so that everyone possesses the new state
+
+.. note:: Eventually this will be handled automatically on demand.
\ No newline at end of file
diff --git a/docs/build/html/_sources/contract-catalogue.txt b/docs/build/html/_sources/contract-catalogue.txt
index 11f3b1302b..db52a161a2 100644
--- a/docs/build/html/_sources/contract-catalogue.txt
+++ b/docs/build/html/_sources/contract-catalogue.txt
@@ -1,4 +1,4 @@
-Platform contracts
+Contract catalogue
==================
There are a number of contracts supplied with Corda, which cover both core functionality (such as cash on ledger) and
@@ -19,6 +19,16 @@ objects of the correct value are received by the beneficiary as part of the sett
The cash contract supports issuing, moving and exiting (destroying) states. Note, however, that issuance cannot be part
of the same transaction as other cash commands, in order to minimise complexity in balance verification.
+Cash shares a common superclass, ``OnChainAsset``, with the Commodity contract. This implements common behaviour of
+assets which can be issued, moved and exited on chain, with the subclasses handling asset-specific data types and
+behaviour.
+
+Commodity
+---------
+
+The ``Commodity`` contract is an early stage example of a non-currency contract whose states implement the ``FungibleAsset``
+interface. This is used as a proof of concept for non-cash obligations.
+
Commercial Paper
----------------
diff --git a/docs/build/html/_sources/creating-a-cordapp.txt b/docs/build/html/_sources/creating-a-cordapp.txt
new file mode 100644
index 0000000000..3831eb9c07
--- /dev/null
+++ b/docs/build/html/_sources/creating-a-cordapp.txt
@@ -0,0 +1,65 @@
+Creating a Cordapp
+==================
+
+A Cordapp is an application that runs on the Corda platform using the platform APIs and plugin system. They are self
+contained in separate JARs from the node server JAR that are created and distributed.
+
+App Plugins
+-----------
+
+.. note:: Currently apps are only supported for JVM languages.
+
+To create an app plugin you must you must extend from `CordaPluginRegistry`_. The JavaDoc contains
+specific details of the implementation, but you can extend the server in the following ways:
+
+1. Required protocols: Specify which protocols will be whitelisted for use in your web APIs.
+2. Service plugins: Register your :ref:`services`.
+3. Web APIs: You may register your own endpoints under /api/ of the built-in web server.
+4. Static web endpoints: You may register your own static serving directories for serving web content.
+
+Services
+--------
+
+.. _services:
+
+Services are classes which are constructed after the node has started. It is provided a `ServiceHubInternal`_ which
+allows a richer API than the `ServiceHub`_ exposed to contracts. It enables adding protocols, registering
+message handlers and more. The service does not run in a separate thread, so the only entry point to the service is during
+construction, where message handlers should be registered and threads started.
+
+
+Starting Nodes
+--------------
+
+To use an app you must also have a node server. To create a node server run the gradle installTemplateNodes task.
+
+This will output the node JAR to ``build/libs/corda.jar`` and several sample/standard
+node setups to ``build/nodes``. For now you can use the ``build/nodes/nodea`` configuration as a template.
+
+Each node server must have a ``node.conf`` file in the same directory as the node JAR file. After first
+execution of the node server there will be many other configuration and persistence files created in this directory.
+
+.. note:: Outside of development environments do not store your node directories in the build folder.
+
+Installing Apps
+------------------
+
+Once you have created your app JAR you can install it to a node by adding it to ``/plugins/``. In this
+case the ``node_dir`` is the location where your node server's JAR and configuration file is.
+
+.. note:: If the directory does not exist you can create it manually.
+
+Starting your Node
+------------------
+
+Now you have a node server with your app installed, you can run it by navigating to ```` and running
+
+ java -jar corda.jar
+
+The plugin should automatically be registered and the configuration file used.
+
+.. warning:: If your working directory is not ```` your plugins and configuration will not be used.
+
+.. _CordaPluginRegistry: api/com.r3corda.core.node/-corda-plugin-registry/index.html
+.. _ServiceHubInternal: api/com.r3corda.node.services.api/-service-hub-internal/index.html
+.. _ServiceHub: api/com.r3corda.node.services.api/-service-hub/index.html
\ No newline at end of file
diff --git a/docs/build/html/_sources/data-model.txt b/docs/build/html/_sources/data-model.txt
index ecc11db4c5..0ba274072d 100644
--- a/docs/build/html/_sources/data-model.txt
+++ b/docs/build/html/_sources/data-model.txt
@@ -2,13 +2,13 @@ Data model
==========
This article covers the data model: how *states*, *transactions* and *code contracts* interact with each other and
-how they are represented in the code. It doesn't attempt to give detailed design rationales or information on future
+how they are represented in software. It doesn't attempt to give detailed design rationales or information on future
design elements: please refer to the R3 wiki for background information.
Overview
--------
-We begin with the idea of a global ledger. In our model, although the ledger is shared, it is not always the case that
+We begin with the idea of a global ledger. In our model although the ledger is shared, it is not always the case that
transactions and ledger entries are globally visible. In cases where a set of transactions stays within a small subgroup of
users it should be possible to keep the relevant data purely within that group.
@@ -22,12 +22,15 @@ States contain arbitrary data, but they always contain at minimum a hash of the
**contract code** file, which is a program expressed in JVM byte code that runs sandboxed inside a Java virtual machine.
Contract code (or just "contracts" in the rest of this document) are globally shared pieces of business logic.
+.. note:: In the current code dynamic loading of contracts is not implemented, so states currently point at
+ statically created object instances. This will change in the near future.
+
Contracts define a **verify function**, which is a pure function given the entire transaction as input. To be considered
valid, the transaction must be **accepted** by the verify function of every contract pointed to by the input and output
states.
Beyond inputs and outputs, transactions may also contain **commands**, small data packets that
-the platform does not interpret itself, but which can parameterise execution of the contracts. They can be thought of as
+the platform does not interpret itself but which can parameterise execution of the contracts. They can be thought of as
arguments to the verify function. Each command has a list of **public keys** associated with it. The platform ensures
that the transaction is signed by every key listed in the commands before the contracts start to execute. Thus, a verify
function can trust that all listed keys have signed the transaction but is responsible for verifying that any keys required
@@ -35,10 +38,15 @@ for the transaction to be valid from the verify function's perspective are inclu
may be random/identityless for privacy, or linked to a well known legal identity, for example via a
*public key infrastructure* (PKI).
+.. note:: Linkage of keys with identities via a PKI is only partially implemented in the current code.
+
Commands are always embedded inside a transaction. Sometimes, there's a larger piece of data that can be reused across
many different transactions. For this use case, we have **attachments**. Every transaction can refer to zero or more
-attachments by hash. Attachments are always ZIP/JAR files, which may contain arbitrary content. Contract code can then
-access the attachments by opening them as a JarInputStream (this is temporary and will change later).
+attachments by hash. Attachments are always ZIP/JAR files, which may contain arbitrary content. These files are
+then exposed on the classpath and so can be opened by contract code in the same manner as any JAR resources
+would be loaded.
+
+.. note:: Attachments must be opened explicitly in the current code.
Note that there is nothing that explicitly binds together specific inputs, outputs, commands or attachments. Instead
it's up to the contract code to interpret the pieces inside the transaction and ensure they fit together correctly. This
@@ -48,13 +56,23 @@ Transactions may sometimes need to provide a contract with data from the outside
prices, facts about events or the statuses of legal entities (e.g. bankruptcy), and so on. The providers of such
facts are called **oracles** and they provide facts to the ledger by signing transactions that contain commands they
recognise, or by creating signed attachments. The commands contain the fact and the signature shows agreement to that fact.
-Time is also modelled as a fact, with the signature of a special kind of oracle called a **timestamping authority** (TSA).
-A TSA signs a transaction if a pre-defined timestamping command in it defines a after/before time window that includes
-"true time" (i.e. GPS time as calibrated to the US Naval Observatory). An oracle may prefer to generate a signed
-attachment if the fact it's creating is relatively static and may be referred to over and over again.
+
+Time is also modelled as a fact, with the signature of a special kind of service called a **notary**. A notary is
+a (very likely) decentralised service which fulfils the role that miners play in other blockchain systems:
+notaries ensure only one transaction can consume any given output. Additionally they may verify a **timestamping
+command** placed inside the transaction, which specifies a time window in which the transaction is considered
+valid for notarisation. The time window can be open ended (i.e. with a start but no end or vice versa). In this
+way transactions can be linked to the notary's clock.
+
+It is possible for a single Corda network to have multiple competing notaries. Each state points to the notary that
+controls it. Whilst a single transaction may only consume states if they are all controlled by the same notary,
+a special type of transaction is provided that moves a state (or set of states) from one notary to another.
+
+.. note:: Currently the platform code will not re-assign states to a single notary as needed for you, in case of
+ a mismatch. This is a future planned feature.
As the same terminology often crops up in different distributed ledger designs, let's compare this to other
-distributed ledger systems you may be familiar with. You can find more detailed design rationales for why the platform
+systems you may be familiar with. You can find more detailed design rationales for why the platform
differs from existing systems in `the R3 wiki `_,
but to summarise, the driving factors are:
@@ -62,7 +80,7 @@ but to summarise, the driving factors are:
* Improved scalability vs Ethereum, as well as ability to keep parts of the transaction graph private (yet still uniquely addressable)
* No reliance on proof of work
* Re-use of existing sandboxing virtual machines
-* Use of type safe GCd implementation languages.
+* Use of type safe GCd implementation languages
* Simplified auditing
Comparison with Bitcoin
@@ -77,8 +95,11 @@ Similarities:
Given the same transaction, a contract's accept function always yields exactly the same result.
* Bitcoin output scripts are parameterised by the input scripts in the spending transaction. This is somewhat similar
to our notion of a *command*.
+* Bitcoin has a global distributed notary service; that's the famous block chain. However, there is only one. Whilst
+ there is a notion of a "side chain", this isn't integrated with the core Bitcoin data model and thus adds large
+ amounts of additional complexity meaning in practice side chains are not used.
* Bitcoin transactions, like ours, refer to the states they consume by using a (txhash, index) pair. The Bitcoin
- protocol calls these "outpoints". In our prototype code they are known as ``StateRefs`` but the concept is identical.
+ protocol calls these "outpoints". In our code they are known as ``StateRefs`` but the concept is identical.
* Bitcoin transactions have an associated timestamp (the time at which they are mined).
Differences:
@@ -92,8 +113,8 @@ Differences:
* A Bitcoin script can only be given a fixed set of byte arrays as the input. This means there's no way for a contract
to examine the structure of the entire transaction, which severely limits what contracts can do.
* Our contracts are Turing-complete and can be written in any ordinary programming language that targets the JVM.
-* Our transactions and contracts have to get their time from an attached timestamp rather than a block chain. This is
- important given that we are currently considering block-free conflict resolution algorithms.
+* Our transactions and contracts get their time from an attached timestamp rather than a block. This is
+ important given that we use block-free conflict resolution algorithms. The timestamp can be arbitrarily precise.
* We use the term "contract" to refer to a bundle of business logic that may handle various different tasks, beyond
transaction verification. For instance, currently our contracts also include code for creating valid transactions
(this is often called "wallet code" in Bitcoin).
@@ -252,7 +273,6 @@ to "reshape" outputs to useful/standardised sizes, for example, and to send outp
back to their issuers for reissuance to "sever" long privacy-breaching chains.
Finally, it should be noted that some of the issues described here are not really "cons" of
-the UTXO model; they're just fundamental.
-If you used many different anonymous accounts to preserve some privacy and then needed to
-spend the contents of them all simultaneously, you'd hit the same problem, so it's not
+the UTXO model; they're just fundamental. If you used many different anonymous accounts to preserve some privacy
+and then needed to spend the contents of them all simultaneously, you'd hit the same problem, so it's not
something that can be trivially fixed with data model changes.
diff --git a/docs/build/html/_sources/event-scheduling.txt b/docs/build/html/_sources/event-scheduling.txt
index 9c2cacfd92..94ed2b2189 100644
--- a/docs/build/html/_sources/event-scheduling.txt
+++ b/docs/build/html/_sources/event-scheduling.txt
@@ -7,7 +7,7 @@
Event scheduling
================
-This article explains our experimental approach to modelling time based events in code. It explains how a contract
+This article explains our approach to modelling time based events in code. It explains how a contract
state can expose an upcoming event and what action to take if the scheduled time for that event is reached.
Introduction
@@ -28,7 +28,7 @@ due. If a contract state is consumed in the UTXO model, then what *was* the nex
and the next time sensitive event is determined by any successor contract state.
Knowing when the next time sensitive event is due to occur is useful, but typically some *activity* is expected to take
-place when this event occurs. We already have a model for business processes in the form of the protocol state machines,
+place when this event occurs. We already have a model for business processes in the form of :doc:`protocols `,
so in the platform we have introduced the concept of *scheduled activities* that can invoke protocol state machines
at a scheduled time. A contract state can optionally described the next scheduled activity for itself. If it omits
to do so, then nothing will be scheduled.
@@ -42,12 +42,11 @@ There are two main steps to implementing scheduled events:
``nextScheduledActivity`` to be implemented which returns an optional ``ScheduledActivity`` instance.
``ScheduledActivity`` captures what ``ProtocolLogic`` instance each node will run, to perform the activity, and when it
will run is described by a ``java.time.Instant``. Once your state implements this interface and is tracked by the
- wallet, it can expect to be queried for the next activity when recorded via the ``ServiceHub.recordTransactions``
- method during protocols execution.
+ wallet, it can expect to be queried for the next activity when committed to the wallet.
* If nothing suitable exists, implement a ``ProtocolLogic`` to be executed by each node as the activity itself.
- The important thing to remember is that each node that is party to the transaction, in the current implementation,
- will execute the same ``ProtocolLogic`` so that needs to establish roles in the business process based on the contract
- state and the node it is running on, and follow different but complementary paths through the business logic.
+ The important thing to remember is that in the current implementation, each node that is party to the transaction
+ will execute the same ``ProtocolLogic``, so it needs to establish roles in the business process based on the contract
+ state and the node it is running on. Each side will follow different but complementary paths through the business logic.
.. note:: The scheduler's clock always operates in the UTC time zone for uniformity, so any time zone logic must be
performed by the contract, using ``ZonedDateTime``.
@@ -58,12 +57,14 @@ handler to help with obtaining a unqiue and secure random session. An example i
The production and consumption of ``ContractStates`` is observed by the scheduler and the activities associated with
any consumed states are unscheduled. Any newly produced states are then queried via the ``nextScheduledActivity``
method and if they do not return ``null`` then that activity is scheduled based on the content of the
-``ScheduledActivity`` object returned.
+``ScheduledActivity`` object returned. Be aware that this *only* happens if the wallet considers the state
+"relevant", for instance, because the owner of the node also owns that state. States that your node happens to
+encounter but which aren't related to yourself will not have any activities scheduled.
An example
----------
-Let's take an example of the Interest Rate Swap fixings for our scheduled events. The first task is to implement the
+Let's take an example of the interest rate swap fixings for our scheduled events. The first task is to implement the
``nextScheduledActivity`` method on the ``State``.
@@ -75,8 +76,6 @@ Let's take an example of the Interest Rate Swap fixings for our scheduled events
protocolLogicRefFactory: ProtocolLogicRefFactory): ScheduledActivity? {
val nextFixingOf = nextFixingOf() ?: return null
- // This is perhaps not how we should determine the time point in the business day, but instead expect the
- // schedule to detail some of these aspects.
val (instant, duration) = suggestInterestRateAnnouncementTimeWindow(index = nextFixingOf.name,
source = floatingLeg.indexSource,
date = nextFixingOf.forDay)
@@ -91,9 +90,10 @@ business process and to take on those roles. That ``ProtocolLogic`` will be han
rate swap ``State`` in question, as well as a tolerance ``Duration`` of how long to wait after the activity is triggered
for the interest rate before indicating an error.
-.. note:: The use of the factory to create a ``ProtocolLogicRef`` instance to embed in the ``ScheduledActivity``. This is a
- way to create a reference to the ``ProtocolLogic`` class and it's constructor parameters to instantiate that can be
- checked against a per node whitelist of approved and allowable types as part of our overall security sandboxing.
+.. note:: This is a way to create a reference to the ProtocolLogic class and its constructor parameters to
+ instantiate. The reference can be checked against a per-node whitelist of approved and allowable types as
+ part of our overall security sandboxing.
+
As previously mentioned, we currently need a small network handler to assist with session setup until the work to
automate that is complete. See the interest rate swap specific implementation ``FixingSessionInitiationHandler`` which
diff --git a/docs/build/html/_sources/index.txt b/docs/build/html/_sources/index.txt
index 46da547db0..3ae4bec6d4 100644
--- a/docs/build/html/_sources/index.txt
+++ b/docs/build/html/_sources/index.txt
@@ -1,6 +1,9 @@
Welcome to the Corda repository!
================================
+.. warning:: This build of the docs is from the *master branch*, not a milestone release. It may not reflect the
+ current state of the code.
+
This documentation describes Corda, a proposed architecture for distributed ledgers, the vision for which is outlined in the `Corda Introductory Whitepaper`_.
.. _`Corda Introductory Whitepaper`: _static/corda-introductory-whitepaper.pdf
@@ -31,6 +34,7 @@ Read on to learn:
transaction-data-types
consensus
messaging
+ creating-a-cordapp
running-the-demos
node-administration
diff --git a/docs/build/html/_sources/inthebox.txt b/docs/build/html/_sources/inthebox.txt
index 25ed8e2afa..3d2bf3b8b9 100644
--- a/docs/build/html/_sources/inthebox.txt
+++ b/docs/build/html/_sources/inthebox.txt
@@ -5,61 +5,26 @@ The Corda prototype currently includes:
* A peer to peer network with message persistence and delivery retries.
* Key data structures for defining contracts and states.
-* Smart contracts:
- * Cash
- * Cash obligations
- * Interest rate swaps
- * Commercial paper (implemented in both Java and Kotlin for comparison)
+* Smart contracts, which you can find in the :doc:`contract-catalogue`.
* Algorithms that work with them, such as serialising, hashing, signing, and verification of the signatures.
* API documentation and tutorials (what you're reading).
* A business process orchestration framework.
* Notary infrastructure for precise timestamping, and elimination of double spending without a blockchain.
-* A simple REST API.
+* A simple REST API, and a web app demo that uses it to present a frontend for IRS trading.
Some things it does not currently include but should gain later are:
* Sandboxing, distribution or publication of smart contract code
-* Database persistence
* A user interface for administration
-* Many other things
The prototype's goal is rapid exploration of ideas. Therefore in places it takes shortcuts that a production system
would not in order to boost productivity:
* It uses an object graph serialization framework instead of a well specified, vendor neutral protocol.
-* It uses the default, out of the box Apache Artemis MQ protocol instead of AMQP/1.0 (although switching should be easy)
-* There is no inter-node SSL or other encryption yet.
-
-Contracts
----------
-
-The primary goal of this prototype is to implement various kinds of contracts and verify that useful business logic
-can be expressed with the data model, developing and refining an API along the way. To that end there are currently
-four contracts in the repository:
-
-1. Cash
-2. Commercial paper
-3. Nettable obligations
-4. Interest rate swaps
-
-``Cash`` implements the idea of a claim on some quantity of deposits at some institutional party, denominated in some currency,
-identified by some *deposit reference*. A deposit reference is an opaque byte array which is usable by
-the issuing party for internal bookkeeping purposes.
-
-Cash states are *fungible* with each other (can be merged and split arbitrarily) if they use the same currency,
-party and deposit reference.
-
-``CommercialPaper`` implements an asset with a *face value* denominated in a certain currency, which may be redeemed at
-the issuing party after a certain time. Commercial paper states define the face value (e.g. $1000) and the time
-at which they may be redeemed. The contract allows the paper to be issued, traded and redeemed. The commercial paper
-contract is implemented twice, once in Java and once in a language called Kotlin.
-
-``InterestRateSwap`` implements a vanilla OTC same currency bilateral fixed / floating leg swap. For further details,
-see :doc:`irs`
-
-``Obligation`` implements a bilaterally or multi-laterally nettable, fungible obligation that can default.
-
-Each contract comes with unit tests.
+* There's currently no permissioning framework.
+* Some privacy techniques aren't implemented yet.
+* It uses an embedded SQL database and doesn't yet have connectivity support for mainstream SQL vendors (Oracle,
+ Postgres, MySQL, SQL Server etc).
Kotlin
------
diff --git a/docs/build/html/_sources/messaging.txt b/docs/build/html/_sources/messaging.txt
index 3c5cf56400..74cfabd871 100644
--- a/docs/build/html/_sources/messaging.txt
+++ b/docs/build/html/_sources/messaging.txt
@@ -1,123 +1,49 @@
Networking and messaging
========================
-Although the platform does not currently provide a network backend, some preliminary interfaces are defined along with
-an in-memory implementation provided for use by unit tests and other exploratory code. An implementation based on Apache
-Kafka is also being developed, which should be sufficient for real use cases to be implemented in the short run, even
-though in the long run a fully peer to peer protocol will be required.
+Corda uses AMQP/1.0 over TLS between nodes which is currently implemented using Apache Artemis, an embeddable message
+queue broker. Building on established MQ protocols gives us features like persistence to disk, automatic delivery
+retries with backoff and dead-letter routing, security, large message streaming and so on.
-This article quickly explains the basic networking interfaces in the code.
+Artemis is hidden behind a thin interface that also has an in-memory only implementation suitable for use in
+unit tests and visualisation tools.
-Messaging vs networking
------------------------
+.. note:: A future version of Corda will allow the MQ broker to be split out of the main node and run as a
+ separate server. We may also support non-Artemis implementations via JMS, allowing the broker to be swapped
+ out for alternative implementations.
-It is important to understand that the code expects any networking module to provide the following services:
+There are multiple ways of interacting with the network. When writing an application you typically won't use the
+messaging subsystem directly. Instead you will build on top of the :doc:`protocol framework `,
+which adds a layer on top of raw messaging to manage multi-step protocols and let you think in terms of identities
+rather than specific network endpoints.
-- Persistent, reliable and secure delivery of complete messages. The module is expected to retry delivery if initial
- attempts fail.
-- Ability to send messages both 1:1 and 1:many, where 'many' may mean the entire group of network users.
+Messaging types
+---------------
-The details of how this is achieved are not exposed to the rest of the code.
-
-Interfaces
-----------
-
-The most important interface is called ``MessagingService`` and is defined in the ``core/messaging/Messaging.kt`` file.
-It declares an interface with the following operations:
-
-- ``addMessageHandler(topic: String, executor: Executor, callback: (Message, MessageHandlerRegistration) -> Unit)``
-- ``createMessage(topic: String, data: ByteArray): Message``
-- ``send(message: Message, targetRecipients: MessageRecipients)``
-- ``stop()``
-
-along with a few misc others that are not important enough to discuss here.
-
-A *topic* is simply a string that identifies the kind of message that is being sent. When a message is received, the
-topic is compared exactly to the list of registered message handlers and if it matches, the callback is invoked.
-Adding a handler returns a ``MessageHandlerRegistration`` object that can be used to remove the handler, and that
-registration object is also passed to each invocation to simplify the case where a handler wishes to remove itself.
-
-Some helper functions are also provided that simplify the process of sending a message by using Kryo serialisation, and
-registering one-shot handlers that remove themselves once they finished running, but those don't need to be implemented
-by network module authors themselves.
-
-Destinations are represented using opaque classes (i.e. their contents are defined by the implementation). The
-``MessageRecipients`` interface represents any possible set of recipients: it's used when a piece of code doesn't
-care who is going to get a message, just that someone does. The ``SingleMessageRecipient`` interface inherits from
-``MessageRecipients`` and represents a handle to some specific individual receiver on the network. Whether they are
-identified by IP address, public key, message router ID or some other kind of address is not exposed at this level.
-``MessageRecipientGroup`` is not used anywhere at the moment but represents multiple simultaneous recipients. And
-finally ``AllPossibleRecipients`` is used for network wide broadcast. It's also unused right now, outside of unit tests.
-
-In memory implementation
-------------------------
-
-To ease unit testing of business logic, a simple in-memory messaging service is provided. To access this you can inherit
-your test case class from the ``TestWithInMemoryNetwork`` class. This provides a few utility methods to help test
-code that involves message passing.
-
-You can run a mock network session in one of two modes:
-
-- Manually "pumped"
-- Automatically pumped with background threads
-
-"Pumping" is the act of telling a mock network node to pop a message off its queue and process it. Typically you want
-unit tests to be fast, repeatable and you want to be able to insert your own changes into the middle of any given
-message sequence. This is what the manual mode is for. In this mode, all logic runs on the same thread (the thread
-running the unit tests). You can create and use a node like this:
-
-.. container:: codeset
-
- .. sourcecode:: kotlin
-
- val (aliceAddr, aliceNode) = makeNode(inBackground = false)
- val (bobAddr, bobNode) = makeNode(false)
-
- aliceNode.send("test.topic", aliceAddr, "foo")
- bobNode.pump(blocking = false)
-
-.. note:: Currently only Kotlin examples are available for networking and protocol state machines. Java examples may
- follow later. Naming arguments in Kotlin like above is optional but sometimes useful to make code examples clearer.
-
-The above code won't actually do anything because no message handler is registered for "test.topic" so the message will
-go into a holding area. If/when we add a handler that can accept test.topic, the message will be delivered then.
-
-Sometimes you don't want to have to call the pump method over and over again. You can use the ``runNetwork { .. }``
-construct to fix this: any code inside the block will be run, and then all nodes you created will be pumped over and
-over until all of them have reported that they have no work left to do. This means any ping-pongs of messages will
-be run until everything settles.
-
-You can see more examples of how to use this in the file ``InMemoryMessagingTests.kt``.
-
-If you specify ``inBackground = true`` to ``makeNode`` then each node will create its own background thread which will
-sit around waiting for messages to be delivered. Handlers will then be invoked on that background thread. This is a
-more difficult style of programming that can be used to increase the realism of the unit tests by ensuring multiple
-nodes run in parallel, just as they would on a real network spread over multiple machines.
+Every ``Message`` object has an associated *topic* and may have a *session ID*. These are wrapped in a ``TopicSession``.
+An implementation of ``MessagingService`` can be used to create messages and send them. You can get access to the
+messaging service via the ``ServiceHub`` object that is provided to your app. Endpoints on the network are
+identified at the lowest level using ``SingleMessageRecipient`` which may be e.g. an IP address, or in future
+versions perhaps a routing path through the network.
Network Map Service
-------------------
Supporting the messaging layer is a network map service, which is responsible for tracking public nodes on the network.
-Nodes have an internal component, the network map cache, which contains a copy of the network map. When a node starts up
-its cache fetches a copy of the full network map, and requests to be notified of changes. The node then registers itself
-with the network map service, and the service notifies subscribers that a new node has joined the network. Nodes do not
-automatically deregister themselves, so (for example) nodes going offline briefly for maintenance are retained in the
-network map, and messages for them will be queued, minimising disruption.
-Nodes submit signed changes to the map service, which then forwards them on to nodes which have requested to be notified
-of changes. This process achieves basic consensus of the overall network map, although currently it has no formal
-process for identifying or recovering from issues such as network outages. Later versions are planned to address this.
+Nodes have an internal component, the network map cache, which contains a copy of the network map (which is just a
+document). When a node starts up its cache fetches a copy of the full network map, and requests to be notified of
+changes. The node then registers itself with the network map service, and the service notifies subscribers that a new
+node has joined the network. Nodes do not automatically deregister themselves, so (for example) nodes going offline
+briefly for maintenance are retained in the network map, and messages for them will be queued, minimising disruption.
-Registration change notifications contain a serial number, which indicates their relative ordering, similar to the
-serial number on DNS records. These numbers must increase with each change, but are not expected to be sequential.
-Changes are then signed by the party whom the node represents to confirm the association between party and node.
-The change, signature and public key are then sent to the network map service, which verifies the signature and then
-updates the network map accordingly.
+Nodes submit signed changes to the map service, which then forwards update notifications on to nodes which have
+requested to be notified of changes.
-The network map cache currently supports:
+The network map currently supports:
* Looking up nodes by service
* Looking up node for a party
* Suggesting a node providing a specific service, based on suitability for a contract and parties, for example suggesting
-an appropriate interest rates oracle for a interest rate swap contract. Currently no recommendation logic is in place
-(the code simply picks the first registered node that supports the required service), however.
+ an appropriate interest rates oracle for a interest rate swap contract. Currently no recommendation logic is in place.
+ The code simply picks the first registered node that supports the required service.
\ No newline at end of file
diff --git a/docs/build/html/_sources/node-administration.txt b/docs/build/html/_sources/node-administration.txt
index d83d75c4e1..32cb71f587 100644
--- a/docs/build/html/_sources/node-administration.txt
+++ b/docs/build/html/_sources/node-administration.txt
@@ -9,7 +9,7 @@ Monitoring your node
Like most Java servers, the node exports various useful metrics and management operations via the industry-standard
`JMX infrastructure `_. JMX is a standard API
-for registering so-called _MBeans_ ... objects whose properties and methods are intended for server management. It does
+for registering so-called *MBeans* ... objects whose properties and methods are intended for server management. It does
not require any particular network protocol for export. So this data can be exported from the node in various ways:
some monitoring systems provide a "Java Agent", which is essentially a JVM plugin that finds all the MBeans and sends
them out to a statistics collector over the network. For those systems, follow the instructions provided by the vendor.
diff --git a/docs/build/html/_sources/oracles.txt b/docs/build/html/_sources/oracles.txt
index 2314111be5..06f5843818 100644
--- a/docs/build/html/_sources/oracles.txt
+++ b/docs/build/html/_sources/oracles.txt
@@ -10,12 +10,8 @@ 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.
-The current prototype includes two oracles:
-
-1. A timestamping service
-2. An interest rate fixing service
-
-We will examine the similarities and differences in their design, whilst covering how the oracle concept works.
+The current prototype includes an example oracle that provides an interest rate fixing service. It is used by the
+IRS trading demo app.
Introduction
------------
@@ -53,10 +49,12 @@ When a fact is encoded in a command, it is embedded in the transaction itself. T
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.
-When a fact is encoded as an attachment, it is a separate object to the transaction which is referred to by hash.
+When a fact is encoded as an attachment, it is a separate object to the transaction and is referred to by hash.
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
-attachments and attachments can be digitally signed (in future).
+attachments when they run.
+
+.. note:: Currently attachments do not support digital signing, but this is a planned feature.
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
@@ -74,79 +72,34 @@ Here's a quick way to decide which approach makes more sense for your data sourc
* Is your data *intended for human consumption*, like a PDF of legal prose, or an Excel spreadsheet? If yes, use an
attachment.
-Asserting continuously varying data that is publicly known
-----------------------------------------------------------
+Asserting continuously varying data
+-----------------------------------
-Let's look at the timestamping oracle that can be found in the ``TimestamperService`` class. This is an example of
-an oracle that uses a command because the current time is a constantly changing fact that everybody knows.
+.. note:: A future version of the platform will include a complete tutorial on implementing this type of oracle.
-The most obvious way to implement such a service would be:
+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.
-1. The creator of the transaction that depends on the time reads their local clock
-2. They insert a command with that time into the transaction
-3. They then send it to the oracle for signing.
+The obvious way to implement such a service is like this:
-But this approach has a problem. There will never be exact clock synchronisation between the party creating the
-transaction and the oracle. This is not only due to physics, network latencies etc but because between inserting the
-command and getting the oracle to sign there may be many other steps, like sending the transaction to other parties
-involved in the trade as well, or even requesting human signoff. Thus the time observed by the oracle may be quite
-different to the time observed in step 1. This problem can occur any time an oracle attests to a constantly changing
-value.
+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.
-.. note:: It is assumed that "true time" for a timestamping oracle means GPS/NaviStar time as defined by the atomic
- clocks at the US Naval Observatory. This time feed is extremely accurate and available globally for free.
+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:
-We fix it by including explicit tolerances in the command, which is defined like this:
-
-.. sourcecode:: kotlin
-
- data class TimestampCommand(val after: Instant?, val before: Instant?) : CommandData
- init {
- if (after == null && before == null)
- throw IllegalArgumentException("At least one of before/after must be specified")
- if (after != null && before != null)
- check(after <= before)
- }
- }
-
-This defines a class that has two optional fields: before and after, along with a constructor that imposes a couple
-more constraints that cannot be expressed in the type system, namely, that "after" actually is temporally after
-"before", and that at least one bound must be present. A timestamp command that doesn't contain anything is illegal.
-
-Thus we express that the *true value* of the fact "the current time" is actually unknowable. Even when both before and
-after times are included, the transaction could have occurred at any point between those two timestamps. In this case
-"occurrence" could mean the execution date, the value date, the trade date etc ... the oracle doesn't care what precise
-meaning the timestamp has to the contract.
-
-By creating a range that can be either closed or open at one end, we allow all of the following facts to be modelled:
-
-* This transaction occurred at some point after the given time (e.g. after a maturity event)
-* This transaction occurred at any time before the given time (e.g. before a bankruptcy event)
-* This transaction occurred at some point roughly around the given time (e.g. on a specific day)
+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.
+3. They then send it to the oracle for signing, along with everyone else in parallel. The oracle checks that the command
+ has correct data for the asserted time, and signs if so.
This same technique can be adapted to other types of oracle.
-Asserting occasionally varying data that is not publicly known
---------------------------------------------------------------
-
-Sometimes you may want a fact that changes, but is not entirely continuous. Additionally the exact value may not be
-public, or may only be semi-public (e.g. easily available to some entities on the network but not all). An example of
-this would be a LIBOR interest rate fix.
-
-In this case, the following design can be used. The oracle service provides a query API which returns the current value,
-and a signing service that signs a transaction if the data in the command matches the answer being returned by the
-query API. Probably the query response contains some sort of timestamp as well, so the service can recognise values
-that were true in the past but no longer are (this is arguably a part of the fact being asserted).
-
-Because the signature covers the transaction, and transactions may end up being forwarded anywhere, the fact itself
-is independently checkable. However, this approach can 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
-*transaction* and not only the *fact*, this allows for a kind of weak pseudo-DRM over data feeds. Whilst a smart
-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.
+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.
Here is an extract from the ``NodeService.Oracle`` class and supporting types:
@@ -167,20 +120,15 @@ Here is an extract from the ``NodeService.Oracle`` class and supporting types:
Because the fix contains a timestamp (the ``forDay`` field), there can be an arbitrary delay between a fix being
requested via ``query`` and the signature being requested via ``sign``.
-Implementing oracles in the framework
--------------------------------------
+Pay-per-play oracles
+--------------------
-Implementation involves the following steps:
-
-1. Defining a high level oracle class, that exposes the basic API operations.
-2. Defining a lower level service class, that binds network messages to the API.
-3. Defining a protocol using the :doc:`protocol-state-machines` framework to make it easy for a client to interact
- with the oracle.
-4. Constructing it (when advertised) in ``AbstractNode``.
-
-An example of how to do this can be found in the ``NodeInterestRates.Oracle``, ``NodeInterestRates.Service`` and
-``RateFixProtocol`` classes.
-
-The exact details of how this code works will change in future, so for now consulting the protocols tutorial and the
-code for the server-side oracles implementation will have to suffice. There will be more detail added once the
-platform APIs have settled down.
\ No newline at end of file
+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
+*transaction* and not only the *fact*, this allows for a kind of weak pseudo-DRM over data feeds. Whilst a smart
+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.
diff --git a/docs/build/html/_sources/protocol-state-machines.txt b/docs/build/html/_sources/protocol-state-machines.txt
index bfcb0a9ca4..802ea563a7 100644
--- a/docs/build/html/_sources/protocol-state-machines.txt
+++ b/docs/build/html/_sources/protocol-state-machines.txt
@@ -102,27 +102,19 @@ We start by defining a wrapper that namespaces the protocol code, two functions
of the protocol, and two classes that will contain the protocol definition. We also pick what data will be used by
each side.
+.. note:: The code samples in this tutorial are only available in Kotlin, but you can use any JVM language to
+ write them and the approach is the same.
+
.. container:: codeset
.. sourcecode:: kotlin
object TwoPartyTradeProtocol {
- val TRADE_TOPIC = "platform.trade"
+ val TOPIC = "platform.trade"
- fun runSeller(smm: StateMachineManager, timestampingAuthority: LegallyIdentifiableNode,
- otherSide: SingleMessageRecipient, assetToSell: StateAndRef, price: Amount,
- myKeyPair: KeyPair, buyerSessionID: Long): ListenableFuture {
- val seller = Seller(otherSide, timestampingAuthority, assetToSell, price, myKeyPair, buyerSessionID)
- smm.add("$TRADE_TOPIC.seller", seller)
- return seller.resultFuture
- }
-
- fun runBuyer(smm: StateMachineManager, timestampingAuthority: LegallyIdentifiableNode,
- otherSide: SingleMessageRecipient, acceptablePrice: Amount, typeToBuy: Class,
- sessionID: Long): ListenableFuture {
- val buyer = Buyer(otherSide, timestampingAuthority.identity, acceptablePrice, typeToBuy, sessionID)
- smm.add("$TRADE_TOPIC.buyer", buyer)
- return buyer.resultFuture
+ class UnacceptablePriceException(val givenPrice: Amount) : Exception("Unacceptable price: $givenPrice")
+ class AssetMismatchException(val expectedTypeName: String, val typeName: String) : Exception() {
+ override fun toString() = "The submitted asset didn't match the expected type: $expectedTypeName vs $typeName"
}
// This object is serialised to the network and is the first protocol message the seller sends to the buyer.
@@ -135,28 +127,24 @@ each side.
class SignaturesFromSeller(val timestampAuthoritySig: DigitalSignature.WithKey, val sellerSig: DigitalSignature.WithKey)
- class Seller(val otherSide: SingleMessageRecipient,
- val timestampingAuthority: LegallyIdentifiableNode,
- val assetToSell: StateAndRef,
- val price: Amount,
- val myKeyPair: KeyPair,
- val buyerSessionID: Long) : ProtocolLogic() {
+ open class Seller(val otherSide: Party,
+ val notaryNode: NodeInfo,
+ val assetToSell: StateAndRef,
+ val price: Amount,
+ val myKeyPair: KeyPair,
+ val buyerSessionID: Long,
+ override val progressTracker: ProgressTracker = Seller.tracker()) : ProtocolLogic() {
@Suspendable
override fun call(): SignedTransaction {
TODO()
}
}
- class UnacceptablePriceException(val givenPrice: Amount) : Exception()
- class AssetMismatchException(val expectedTypeName: String, val typeName: String) : Exception() {
- override fun toString() = "The submitted asset didn't match the expected type: $expectedTypeName vs $typeName"
- }
-
- class Buyer(val otherSide: SingleMessageRecipient,
- val timestampingAuthority: Party,
- val acceptablePrice: Amount,
- val typeToBuy: Class,
- val sessionID: Long) : ProtocolLogic() {
+ open class Buyer(val otherSide: Party,
+ val notary: Party,
+ val acceptablePrice: Amount,
+ val typeToBuy: Class,
+ val sessionID: Long) : ProtocolLogic() {
@Suspendable
override fun call(): SignedTransaction {
TODO()
@@ -166,52 +154,41 @@ each side.
Let's unpack what this code does:
-- It defines a several classes nested inside the main ``TwoPartyTradeProtocol`` singleton, and a couple of methods, one
- to run the buyer side of the protocol and one to run the seller side. Some of the classes are simply protocol messages.
+- It defines a several classes nested inside the main ``TwoPartyTradeProtocol`` singleton. Some of the classes
+ are simply protocol messages or exceptions. The other two represent the buyer and seller side of the protocol.
- It defines the "trade topic", which is just a string that namespaces this protocol. The prefix "platform." is reserved
- by the DLG, but you can define your own protocols using standard Java-style reverse DNS notation.
-- The ``runBuyer`` and ``runSeller`` methods take a number of parameters that specialise the protocol for this run,
- use them to construct a ``Buyer`` or ``Seller`` object respectively, and then add the new instances to the
- ``StateMachineManager``. The purpose of this class is described below. The ``smm.add`` method takes a logger name as
- the first parameter, this is just a standard JDK logging identifier string, and the instance to add.
+ by Corda, but you can define your own protocol namespaces using standard Java-style reverse DNS notation.
Going through the data needed to become a seller, we have:
-- ``timestampingAuthority: LegallyIdentifiableNode`` - a reference to a node on the P2P network that acts as a trusted
- timestamper. The use of timestamping is described in :doc:`data-model`.
- ``otherSide: SingleMessageRecipient`` - the network address of the node with which you are trading.
+- ``notaryNode: NodeInfo`` - the entry in the network map for the chosen notary. See ":doc:`consensus`" for more
+ information on notaries.
- ``assetToSell: StateAndRef`` - a pointer to the ledger entry that represents the thing being sold.
-- ``price: Amount`` - the agreed on price that the asset is being sold for.
+- ``price: Amount`` - the agreed on price that the asset is being sold for (without an issuer constraint).
- ``myKeyPair: KeyPair`` - the key pair that controls the asset being sold. It will be used to sign the transaction.
- ``buyerSessionID: Long`` - a unique number that identifies this trade to the buyer. It is expected that the buyer
- knows that the trade is going to take place and has sent you such a number already. (This field may go away in a future
- iteration of the framework)
+ knows that the trade is going to take place and has sent you such a number already.
-.. note:: Session IDs keep different traffic streams separated, so for security they must be large and random enough
- to be unguessable. 63 bits is good enough.
+.. note:: Session IDs will be automatically handled in a future version of the framework.
And for the buyer:
-- ``acceptablePrice: Amount`` - the price that was agreed upon out of band. If the seller specifies a price less than
- or equal to this, then the trade will go ahead.
+- ``acceptablePrice: Amount`` - the price that was agreed upon out of band. If the seller specifies
+ a price less than or equal to this, then the trade will go ahead.
- ``typeToBuy: Class`` - the type of state that is being purchased. This is used to check that the
sell side of the protocol isn't trying to sell us the wrong thing, whether by accident or on purpose.
- ``sessionID: Long`` - the session ID that was handed to the seller in order to start the protocol.
-The run methods return a ``ListenableFuture`` that will complete when the protocol has finished.
+Alright, so using this protocol shouldn't be too hard: in the simplest case we can just create a Buyer or Seller
+with the details of the trade, depending on who we are. We then have to start the protocol in some way. Just
+calling the ``call`` function ourselves won't work: instead we need to ask the framework to start the protocol for
+us. More on that in a moment.
-Alright, so using this protocol shouldn't be too hard: in the simplest case we can just pass in the details of the trade
-to either runBuyer or runSeller, depending on who we are, and then call ``.get()`` on resulting object to
-block the calling thread until the protocol has finished. Or we could register a callback on the returned future that
-will be invoked when it's done, where we could e.g. update a user interface.
+Suspendable functions
+---------------------
-Finally, we define a couple of exceptions, and two classes that will be used as a protocol message called
-``SellerTradeInfo`` and ``SignaturesFromSeller``.
-
-Suspendable methods
--------------------
-
-The ``call`` method of the buyer/seller classes is marked with the ``@Suspendable`` annotation. What does this mean?
+The ``call`` function of the buyer/seller classes is marked with the ``@Suspendable`` annotation. What does this mean?
As mentioned above, our protocol framework will at points suspend the code and serialise it to disk. For this to work,
any methods on the call stack must have been pre-marked as ``@Suspendable`` so the bytecode rewriter knows to modify
@@ -221,50 +198,52 @@ invoked, all methods on the stack must have been marked. If you forget, then in
get a useful error message telling you which methods you didn't mark. The fix is simple enough: just add the annotation
and try again.
-.. note:: A future version of Java is likely to remove this pre-marking requirement completely.
+.. note:: Java 9 is likely to remove this pre-marking requirement completely.
-The state machine manager
--------------------------
+Starting your protocol
+----------------------
-The SMM is a class responsible for taking care of all running protocols in a node. It knows how to register handlers
-with a ``MessagingService`` and iterate the right state machine when messages arrive. It provides the
-send/receive/sendAndReceive calls that let the code request network interaction and it will store a serialised copy of
-each state machine before it's suspended to wait for the network.
+The ``StateMachineManager`` is the class responsible for taking care of all running protocols in a node. It knows
+how to register handlers with the messaging system (see ":doc:`messaging`") and iterate the right state machine
+when messages arrive. It provides the send/receive/sendAndReceive calls that let the code request network
+interaction and it will save/restore serialised versions of the fiber at the right times.
-To get a ``StateMachineManager``, you currently have to build one by passing in a ``ServiceHub`` and a thread or thread
-pool which it can use. This will change in future so don't worry about the details of this too much: just check the
-unit tests to see how it's done.
+Protocols can be invoked in several ways. For instance, they can be triggered by scheduled events,
+see ":doc:`event-scheduling`" to learn more about this. Or they can be triggered via the HTTP API. Or they can
+be triggered directly via the Java-level node APIs from your app code.
+
+You request a protocol to be invoked by using the ``ServiceHub.invokeProtocolAsync`` method. This takes a
+Java reflection ``Class`` object that describes the protocol class to use (in this case, either ``Buyer`` or ``Seller``).
+It also takes a set of arguments to pass to the constructor. Because it's possible for protocol invocations to
+be requested by untrusted code (e.g. a state that you have been sent), the types that can be passed into the
+protocol are checked against a whitelist, which can be extended by apps themselves at load time.
+
+The process of starting a protocol returns a ``ListenableFuture`` that you can use to either block waiting for
+the result, or register a callback that will be invoked when the result is ready.
Implementing the seller
-----------------------
-Let's implement the ``Seller.call`` method. This will be invoked by the platform when the protocol is started by the
-``StateMachineManager``.
+Let's implement the ``Seller.call`` method. This will be run when the protocol is invoked.
.. container:: codeset
.. sourcecode:: kotlin
- val partialTX: SignedTransaction = receiveAndCheckProposedTransaction()
-
- // These two steps could be done in parallel, in theory. Our framework doesn't support that yet though.
- val ourSignature = signWithOurKey(partialTX)
- val tsaSig = subProtocol(TimestampingProtocol(timestampingAuthority, partialTX.txBits))
-
- val stx: SignedTransaction = sendSignatures(partialTX, ourSignature, tsaSig)
-
- return stx
+ @Suspendable
+ override fun call(): SignedTransaction {
+ val partialTX: SignedTransaction = receiveAndCheckProposedTransaction()
+ val ourSignature: DigitalSignature.WithKey = signWithOurKey(partialTX)
+ val notarySignature = getNotarySignature(partialTX)
+ val result: SignedTransaction = sendSignatures(partialTX, ourSignature, notarySignature)
+ return result
+ }
Here we see the outline of the procedure. We receive a proposed trade transaction from the buyer and check that it's
-valid. Then we sign with our own key, request a timestamping authority to assert with another signature that the
-timestamp in the transaction (if any) is valid, and finally we send back both our signature and the TSA's signature.
-Finally, we hand back to the code that invoked the protocol the finished transaction in a couple of different forms.
-
-.. note:: ``ProtocolLogic`` classes can be composed together. Here, we see the use of the ``subProtocol`` method, which
- is given an instance of ``TimestampingProtocol``. This protocol will run to completion and yield a result, almost
- as if it's a regular method call. In fact, under the hood, all the ``subProtocol`` method does is pass the current
- fiber object into the newly created object and then run ``call()`` on it ... so it basically _is_ just a method call.
- This is where we can see the benefits of using continuations/fibers as a programming model.
+valid. Then we sign with our own key and request a notary to assert with another signature that the
+timestamp in the transaction (if any) is valid and there are no double spends, and send back both
+our signature and the notaries signature. Finally, we hand back to the code that invoked the protocol the
+finished transaction.
Let's fill out the ``receiveAndCheckProposedTransaction()`` method.
@@ -273,56 +252,48 @@ Let's fill out the ``receiveAndCheckProposedTransaction()`` method.
.. sourcecode:: kotlin
@Suspendable
- open fun receiveAndCheckProposedTransaction(): SignedTransaction {
+ private fun receiveAndCheckProposedTransaction(): SignedTransaction {
val sessionID = random63BitValue()
// Make the first message we'll send to kick off the protocol.
val hello = SellerTradeInfo(assetToSell, price, myKeyPair.public, sessionID)
- val maybeSTX = sendAndReceive(TRADE_TOPIC, otherSide, buyerSessionID, sessionID, hello)
+ val maybeSTX = sendAndReceive(otherSide, buyerSessionID, sessionID, hello)
maybeSTX.validate {
// Check that the tx proposed by the buyer is valid.
- val missingSigs = it.verify(throwIfSignaturesAreMissing = false)
- if (missingSigs != setOf(myKeyPair.public, timestampingAuthority.identity.owningKey))
- throw SignatureException("The set of missing signatures is not as expected: $missingSigs")
+ val missingSigs: Set = it.verifySignatures(throwIfSignaturesAreMissing = false)
+ val expected = setOf(myKeyPair.public, notaryNode.identity.owningKey)
+ if (missingSigs != expected)
+ throw SignatureException("The set of missing signatures is not as expected: ${missingSigs.toStringsShort()} vs ${expected.toStringsShort()}")
val wtx: WireTransaction = it.tx
logger.trace { "Received partially signed transaction: ${it.id}" }
- checkDependencies(it)
+ // Download and check all the things that this transaction depends on and verify it is contract-valid,
+ // even though it is missing signatures.
+ subProtocol(ResolveTransactionsProtocol(wtx, otherSide))
- // This verifies that the transaction is contract-valid, even though it is missing signatures.
- serviceHub.verifyTransaction(wtx.toLedgerTransaction(serviceHub.identityService))
-
- if (wtx.outputs.sumCashBy(myKeyPair.public) != price)
- throw IllegalArgumentException("Transaction is not sending us the right amounnt of cash")
-
- // There are all sorts of funny games a malicious secondary might play here, we should fix them:
- //
- // - This tx may attempt to send some assets we aren't intending to sell to the secondary, if
- // we're reusing keys! So don't reuse keys!
- // - This tx may include output states that impose odd conditions on the movement of the cash,
- // once we implement state pairing.
- //
- // but the goal of this code is not to be fully secure (yet), but rather, just to find good ways to
- // express protocol state machines on top of the messaging layer.
+ if (wtx.outputs.map { it.data }.sumCashBy(myKeyPair.public).withoutIssuer() != price)
+ throw IllegalArgumentException("Transaction is not sending us the right amount of cash")
return it
}
}
-That's pretty straightforward. We generate a session ID to identify what's happening on the seller side, fill out
+Let's break this down. We generate a session ID to identify what's happening on the seller side, fill out
the initial protocol message, and then call ``sendAndReceive``. This function takes a few arguments:
- The topic string that ensures the message is routed to the right bit of code in the other side's node.
- The session IDs that ensure the messages don't get mixed up with other simultaneous trades.
- The thing to send. It'll be serialised and sent automatically.
-- Finally a type argument, which is the kind of object we're expecting to receive from the other side.
+- Finally a type argument, which is the kind of object we're expecting to receive from the other side. If we get
+ back something else an exception is thrown.
-Once ``sendAndReceive`` is called, the call method will be suspended into a continuation. When it gets back we'll do a log
-message. The buyer is supposed to send us a transaction with all the right inputs/outputs/commands in return, with their
-cash put into the transaction and their signature on it authorising the movement of the cash.
+Once ``sendAndReceive`` is called, the call method will be suspended into a continuation and saved to persistent
+storage. If the node crashes or is restarted, the protocol will effectively continue as if nothing had happened. Your
+code may remain blocked inside such a call for seconds, minutes, hours or even days in the case of a protocol that
+needs human interaction!
.. note:: There are a couple of rules you need to bear in mind when writing a class that will be used as a continuation.
The first is that anything on the stack when the function is suspended will be stored into the heap and kept alive by
@@ -331,31 +302,51 @@ cash put into the transaction and their signature on it authorising the movement
The second is that as well as being kept on the heap, objects reachable from the stack will be serialised. The state
of the function call may be resurrected much later! Kryo doesn't require objects be marked as serialisable, but even so,
doing things like creating threads from inside these calls would be a bad idea. They should only contain business
- logic.
+ logic and only do I/O via the methods exposed by the protocol framework.
+
+ It's OK to keep references around to many large internal node services though: these will be serialised using a
+ special token that's recognised by the platform, and wired up to the right instance when the continuation is
+ loaded off disk again.
+
+The buyer is supposed to send us a transaction with all the right inputs/outputs/commands in response to the opening
+message, with their cash put into the transaction and their signature on it authorising the movement of the cash.
You get back a simple wrapper class, ``UntrustworthyData``, which is just a marker class that reminds
us that the data came from a potentially malicious external source and may have been tampered with or be unexpected in
-other ways. It doesn't add any functionality, but acts as a reminder to "scrub" the data before use. Here, our scrubbing
-simply involves checking the signatures on it. Then we go ahead and check all the dependencies of this partial
-transaction for validity. Here's the code to do that:
+other ways. It doesn't add any functionality, but acts as a reminder to "scrub" the data before use.
+
+Our "scrubbing" has three parts:
+
+1. Check that the expected signatures are present and correct. At this point we expect our own signature to be missing,
+ because of course we didn't sign it yet, and also the signature of the notary because that must always come last.
+2. We resolve the transaction, which we will cover below.
+3. We verify that the transaction is paying us the demanded price.
+
+Subprotocols
+------------
+
+Protocols can be composed via nesting. Invoking a sub-protocol looks similar to an ordinary function call:
.. container:: codeset
.. sourcecode:: kotlin
@Suspendable
- private fun checkDependencies(stx: SignedTransaction) {
- // Download and check all the transactions that this transaction depends on, but do not check this
- // transaction itself.
- val dependencyTxIDs = stx.tx.inputs.map { it.txhash }.toSet()
- subProtocol(ResolveTransactionsProtocol(dependencyTxIDs, otherSide))
+ private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.LegallyIdentifiable {
+ progressTracker.currentStep = NOTARY
+ return subProtocol(NotaryProtocol.Client(stx))
}
-This is simple enough: we mark the method as ``@Suspendable`` because we're going to invoke a sub-protocol, extract the
-IDs of the transactions the proposed transaction depends on, and then uses a protocol provided by the system to download
-and check them all. This protocol does a breadth-first search over the dependency graph, bottoming out at issuance
-transactions that don't have any inputs themselves. Once the node has audited the transaction history, all the dependencies
-are committed to the node's local database so they won't be checked again next time.
+In this code snippet we are using the ``NotaryProtocol.Client`` to request notarisation of the transaction.
+We simply create the protocol object via its constructor, and then pass it to the ``subProtocol`` method which
+returns the result of the protocol's execution directly. Behind the scenes all this is doing is wiring up progress
+tracking (discussed more below) and then running the objects ``call`` method. Because this little helper method can
+be on the stack when network IO takes place, we mark it as ``@Suspendable``.
+
+Going back to the previous code snippet, we use a subprotocol called ``ResolveTransactionsProtocol``. This is
+responsible for downloading and checking all the dependencies of a transaction, which in Corda are always retrievable
+from the party that sent you a transaction that uses them. This protocol returns a list of ``LedgerTransaction``
+objects, but we don't need them here so we just ignore the return value.
.. note:: Transaction dependency resolution assumes that the peer you got the transaction from has all of the
dependencies itself. It must do, otherwise it could not have convinced itself that the dependencies were themselves
@@ -375,24 +366,27 @@ Here's the rest of the code:
open fun signWithOurKey(partialTX: SignedTransaction) = myKeyPair.signWithECDSA(partialTX.txBits)
@Suspendable
- open fun sendSignatures(partialTX: SignedTransaction, ourSignature: DigitalSignature.WithKey,
- tsaSig: DigitalSignature.LegallyIdentifiable): SignedTransaction {
- val fullySigned = partialTX + tsaSig + ourSignature
-
+ private fun sendSignatures(partialTX: SignedTransaction, ourSignature: DigitalSignature.WithKey,
+ notarySignature: DigitalSignature.LegallyIdentifiable): SignedTransaction {
+ val fullySigned = partialTX + ourSignature + notarySignature
logger.trace { "Built finished transaction, sending back to secondary!" }
-
- send(TRADE_TOPIC, otherSide, buyerSessionID, SignaturesFromSeller(tsaSig, ourSignature))
+ send(otherSide, buyerSessionID, SignaturesFromSeller(ourSignature, notarySignature))
return fullySigned
}
-It's should be all pretty straightforward: here, ``txBits`` is the raw byte array representing the transaction.
+It's all pretty straightforward from now on. Here ``txBits`` is the raw byte array representing the serialised
+transaction, and we just use our private key to calculate a signature over it. As a reminder, in Corda signatures do
+not cover other signatures: just the core of the transaction data.
-In ``sendSignatures``, we take the two signatures we calculated, then add them to the partial transaction we were sent.
-We provide an overload for the + operator so signatures can be added to a SignedTransaction easily. Finally, we wrap the
+In ``sendSignatures``, we take the two signatures we obtained and add them to the partial transaction we were sent.
+There is an overload for the + operator so signatures can be added to a SignedTransaction easily. Finally, we wrap the
two signatures in a simple wrapper message class and send it back. The send won't block waiting for an acknowledgement,
but the underlying message queue software will retry delivery if the other side has gone away temporarily.
-.. warning:: This code is **not secure**. Other than not checking for all possible invalid constructions, if the
+You can also see that every protocol instance has a logger (using the SLF4J API) which you can use to log progress
+messages.
+
+.. warning:: This sample code is **not secure**. Other than not checking for all possible invalid constructions, if the
seller stops before sending the finalised transaction to the buyer, the seller is left with a valid transaction
but the buyer isn't, so they can't spend the asset they just purchased! This sort of thing will be fixed in a
future version of the code.
@@ -411,24 +405,25 @@ OK, let's do the same for the buyer side:
val tradeRequest = receiveAndValidateTradeRequest()
val (ptx, cashSigningPubKeys) = assembleSharedTX(tradeRequest)
val stx = signWithOurKeys(cashSigningPubKeys, ptx)
+
val signatures = swapSignaturesWithSeller(stx, tradeRequest.sessionID)
- logger.trace { "Got signatures from seller, verifying ... "}
- val fullySigned = stx + signatures.timestampAuthoritySig + signatures.sellerSig
- fullySigned.verify()
+ logger.trace { "Got signatures from seller, verifying ... " }
- logger.trace { "Fully signed transaction was valid. Trade complete! :-)" }
+ val fullySigned = stx + signatures.sellerSig + signatures.notarySig
+ fullySigned.verifySignatures()
+
+ logger.trace { "Signatures received are valid. Trade complete! :-)" }
return fullySigned
}
@Suspendable
- open fun receiveAndValidateTradeRequest(): SellerTradeInfo {
+ private fun receiveAndValidateTradeRequest(): SellerTradeInfo {
// Wait for a trade request to come in on our pre-provided session ID.
- val maybeTradeRequest = receive(TRADE_TOPIC, sessionID)
-
+ val maybeTradeRequest = receive(sessionID)
maybeTradeRequest.validate {
// What is the seller trying to sell us?
- val asset = it.assetForSale.state
+ val asset = it.assetForSale.state.data
val assetTypeName = asset.javaClass.name
logger.trace { "Got trade request for a $assetTypeName: ${it.assetForSale}" }
@@ -448,15 +443,16 @@ OK, let's do the same for the buyer side:
}
@Suspendable
- open fun swapSignaturesWithSeller(stx: SignedTransaction, theirSessionID: Long): SignaturesFromSeller {
+ private fun swapSignaturesWithSeller(stx: SignedTransaction, theirSessionID: Long): SignaturesFromSeller {
+ progressTracker.currentStep = SWAPPING_SIGNATURES
logger.trace { "Sending partially signed transaction to seller" }
// TODO: Protect against the seller terminating here and leaving us in the lurch without the final tx.
- return sendAndReceive(TRADE_TOPIC, otherSide, theirSessionID, sessionID, stx, SignaturesFromSeller::class.java).validate { it }
+ return sendAndReceive(otherSide, theirSessionID, sessionID, stx).validate { it }
}
- open fun signWithOurKeys(cashSigningPubKeys: List, ptx: TransactionBuilder): SignedTransaction {
+ private fun signWithOurKeys(cashSigningPubKeys: List, ptx: TransactionBuilder): SignedTransaction {
// Now sign the transaction with whatever keys we need to move the cash.
for (k in cashSigningPubKeys) {
val priv = serviceHub.keyManagementService.toPrivate(k)
@@ -466,50 +462,45 @@ OK, let's do the same for the buyer side:
return ptx.toSignedTransaction(checkSufficientSignatures = false)
}
- open fun assembleSharedTX(tradeRequest: SellerTradeInfo): Pair> {
- val ptx = TransactionBuilder()
+ private fun assembleSharedTX(tradeRequest: SellerTradeInfo): Pair> {
+ val ptx = TransactionType.General.Builder(notary)
// Add input and output states for the movement of cash, by using the Cash contract to generate the states.
val wallet = serviceHub.walletService.currentWallet
val cashStates = wallet.statesOfType()
val cashSigningPubKeys = Cash().generateSpend(ptx, tradeRequest.price, tradeRequest.sellerOwnerKey, cashStates)
// Add inputs/outputs/a command for the movement of the asset.
- ptx.addInputState(tradeRequest.assetForSale.ref)
+ ptx.addInputState(tradeRequest.assetForSale)
// Just pick some new public key for now. This won't be linked with our identity in any way, which is what
// we want for privacy reasons: the key is here ONLY to manage and control ownership, it is not intended to
// reveal who the owner actually is. The key management service is expected to derive a unique key from some
// initial seed in order to provide privacy protection.
val freshKey = serviceHub.keyManagementService.freshKey()
- val (command, state) = tradeRequest.assetForSale.state.withNewOwner(freshKey.public)
- ptx.addOutputState(state)
- ptx.addCommand(command, tradeRequest.assetForSale.state.owner)
+ val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(freshKey.public)
+ ptx.addOutputState(state, tradeRequest.assetForSale.state.notary)
+ ptx.addCommand(command, tradeRequest.assetForSale.state.data.owner)
// And add a request for timestamping: it may be that none of the contracts need this! But it can't hurt
// to have one.
- ptx.setTime(Instant.now(), timestampingAuthority, 30.seconds)
+ val currentTime = serviceHub.clock.instant()
+ ptx.setTime(currentTime, 30.seconds)
return Pair(ptx, cashSigningPubKeys)
}
-This code is longer but still fairly straightforward. Here are some things to pay attention to:
+This code is longer but no more complicated. Here are some things to pay attention to:
1. We do some sanity checking on the received message to ensure we're being offered what we expected to be offered.
-2. We create a cash spend in the normal way, by using ``Cash().generateSpend``. See the contracts tutorial if this isn't
- clear.
+2. We create a cash spend in the normal way, by using ``Cash().generateSpend``. See the contracts tutorial if this
+ part isn't clear.
3. We access the *service hub* when we need it to access things that are transient and may change or be recreated
- whilst a protocol is suspended, things like the wallet or the timestamping service. Remember that a protocol may
- be suspended when it waits to receive a message across node or computer restarts, so objects representing a service
- or data which may frequently change should be accessed 'just in time'.
+ whilst a protocol is suspended, things like the wallet or the network map.
4. Finally, we send the unfinished, invalid transaction to the seller so they can sign it. They are expected to send
back to us a ``SignaturesFromSeller``, which once we verify it, should be the final outcome of the trade.
As you can see, the protocol logic is straightforward and does not contain any callbacks or network glue code, despite
the fact that it takes minimal resources and can survive node restarts.
-.. warning:: When accessing things via the ``serviceHub`` field, avoid the temptation to stuff a reference into a local variable.
- If you do this then next time your protocol waits to receive an object, the system will try and serialise all your
- local variables and end up trying to serialise, e.g. the timestamping service, which doesn't make any conceptual
- sense. The ``serviceHub`` field is defined by the ``ProtocolStateMachine`` superclass and is marked transient so
- this problem doesn't occur. It's also restored for you when a protocol state machine is restored after a node
- restart.
+.. warning:: In the current version of the platform, exceptions thrown during protocol execution are not propagated
+ back to the sender. A thorough error handling and exceptions framework will be in a future version of the platform.
Progress tracking
-----------------
@@ -662,3 +653,36 @@ directly to the ``a.services.recordTransaction`` method (note that this method d
valid).
And that's it: you can explore the documentation for the `MockNode API `_ here.
+
+Versioning
+----------
+
+Fibers involve persisting object-serialised stack frames to disk. Although we may do some R&D into in-place upgrades
+in future, for now the upgrade process for protocols is simple: you duplicate the code and rename it so it has a
+new set of class names. Old versions of the protocol can then drain out of the system whilst new versions are
+initiated. When enough time has passed that no old versions are still waiting for anything to happen, the previous
+copy of the code can be deleted.
+
+Whilst kind of ugly, this is a very simple approach that should suffice for now.
+
+.. warning:: Protocols are not meant to live for months or years, and by implication they are not meant to implement entire deal
+ lifecycles. For instance, implementing the entire life cycle of an interest rate swap as a single protocol - whilst
+ technically possible - would not be a good idea. The platform provides a job scheduler tool that can invoke
+ protocols for this reason (see ":doc:`event-scheduling`")
+
+Future features
+---------------
+
+The protocol framework is a key part of the platform and will be extended in major ways in future. Here are some of
+the features we have planned:
+
+* Automatic session ID management
+* Identity based addressing
+* Exposing progress trackers to local (inside the firewall) clients using message queues and/or WebSockets
+* Exception propagation and management, with a "protocol hospital" tool to manually provide solutions to unavoidable
+ problems (e.g. the other side doesn't know the trade)
+* Being able to interact with internal apps and tools via HTTP and similar
+* Being able to interact with people, either via some sort of external ticketing system, or email, or a custom UI.
+ For example to implement human transaction authorisations.
+* A standard library of protocols that can be easily sub-classed by local developers in order to integrate internal
+ reporting logic, or anything else that might be required as part of a communications lifecycle.
diff --git a/docs/build/html/_sources/release-process.txt b/docs/build/html/_sources/release-process.txt
index 5cdf4201c9..2fcc8f1e58 100644
--- a/docs/build/html/_sources/release-process.txt
+++ b/docs/build/html/_sources/release-process.txt
@@ -34,11 +34,11 @@ Steps to cut a release
4. Regenerate the docsite if necessary and commit.
5. Create a branch with a name like `release-M0` where 0 is replaced by the number of the milestone.
6. Adjust the version in the root build.gradle file to take out the -SNAPSHOT and commit it on the branch.
-7. Tag the branch with a tag like `release-M0.0`
-8. Push the branch and the tag to git.
-9. Write up a short announcement containing the summary of new features, changes, and API breaks. Send it to the
- r3dlg-awg mailing list.
-10. On master, adjust the version number in the root build.gradle file upwards.
+7. Remove the "is master" warning from the docsite index page on this branch only.
+8. Tag the branch with a tag like `release-M0.0`
+9. Push the branch and the tag to git.
+10. Write up a short announcement containing the summary of new features, changes, and API breaks. Send it to the r3dlg-awg mailing list.
+11. On master, adjust the version number in the root build.gradle file upwards.
If there are serious bugs found in the release, backport the fix to the branch and then tag it with e.g. `release-M0.1`
Minor changes to the branch don't have to be announced unless it'd be critical to get all developers updated.
\ No newline at end of file
diff --git a/docs/build/html/_sources/running-the-demos.txt b/docs/build/html/_sources/running-the-demos.txt
index 5c51afc789..1544f58e13 100644
--- a/docs/build/html/_sources/running-the-demos.txt
+++ b/docs/build/html/_sources/running-the-demos.txt
@@ -13,9 +13,6 @@ so far. We have:
The demos create node data directories in the root of the project. If something goes wrong with them, blow away the
directories and try again.
-.. note:: Corda is developed on MacOS and works best on UNIX systems. Both demos are easily run on Windows but
- you won't get the nice coloured output.
-
Trader demo
-----------
@@ -106,7 +103,7 @@ To install the web demo please follow these steps;
1. Install Node: https://nodejs.org/en/download/ and ensure the npm executable is on your classpath
2. Open a terminal
-3. Run `npm install -g bower` or `sudo npm install -g bower` if on a *nix system.
+3. Run `npm install -g bower` or `sudo npm install -g bower` if on a Unix system.
4. In the terminal navigate to `/src/main/resources/com/r3corda/demos/irswebdemo`
5. Run `bower install`
diff --git a/docs/build/html/_sources/transaction-data-types.txt b/docs/build/html/_sources/transaction-data-types.txt
index b59e3a7792..9b179df7c5 100644
--- a/docs/build/html/_sources/transaction-data-types.txt
+++ b/docs/build/html/_sources/transaction-data-types.txt
@@ -1,63 +1,85 @@
Data types
==========
-There is a large library of data types used in Corda transactions and contract state objects.
+Corda provides a large standard library of data types used in financial transactions and contract state objects.
+These provide a common language for states and contracts.
Amount
------
-The ``Amount`` class is used to represent an amount of some fungible asset. It is a generic class which wraps around
-a type used to define the underlying product, generally represented by an ``Issued`` instance, or this can be a more
-complex type such as an obligation contract issuance definition (which in turn contains a token definition for whatever
-the obligation is to be settled in).
+The `Amount `_ class is used to represent an amount of some
+fungible asset. It is a generic class which wraps around a type used to define the underlying product, called
+the *token*. For instance it can be the standard JDK type ``Currency``, or an ``Issued`` instance, or this can be
+a more complex type such as an obligation contract issuance definition (which in turn contains a token definition
+for whatever the obligation is to be settled in).
.. note:: Fungible is used here to mean that instances of an asset is interchangeable for any other identical instance,
and that they can be split/merged. For example a £5 note can reasonably be exchanged for any other £5 note, and a
£10 note can be exchanged for two £5 notes, or vice-versa.
-Where a contract refers directly to an amount of something, ``Amount`` should wrap ``Issued``, which in
-turn can refer to a ``Currency`` (GBP, USD, CHF, etc.), or any other class. Future work in this area will include
-introducing classes to represent non-currency things (such as commodities) that Issued can wrap. For more
-complex amounts, ``Amount`` can wrap other types, for example to represent a number of Obligation contracts to be
-delivered (themselves referring to a currency), an ``Amount`` such as the following would used:
+Here are some examples:
.. container:: codeset
.. sourcecode:: kotlin
+ // A quantity of some specific currency like pounds, euros, dollars etc.
+ Amount
+ // A quantity of currency that is issued by a specific issuer, for instance central bank vs other bank dollars
+ Amount>
+ // A quantity of obligations to deliver currency of any issuer.
Amount>
+``Amount`` represents quantities as integers. For currencies the quantity represents pennies, cents or whatever
+else the smallest integer amount for that currency is. You cannot use ``Amount`` to represent negative quantities
+or fractional quantities: if you wish to do this then you must use a different type e.g. ``BigDecimal``. ``Amount``
+defines methods to do addition and subtraction and these methods verify that the tokens on both sides of the operator
+are equal (these are operator overloads in Kotlin and can be used as regular methods from Java). There are also
+methods to do multiplication and division by integer amounts.
+
State
-----
A Corda contract is composed of three parts; the executable code, the legal prose, and the state objects that represent
-the details of the contract (see :doc:`data-model` for further detail). States essentially convert the generic template
-(code and legal prose) into a specific instance. In a ``WireTransaction``, outputs are provided as ``TransactionState``
-implementations, while the inputs are references to the outputs of a previous transaction. These references are then
-stored as ``StateRef`` objects, which are converted to ``StateAndRef`` on demand.
+the details of a specific deal or asset (see :doc:`data-model` for further detail). In relational database terms
+a state is like a row in a database. A reference to a state in the ledger (whether it has been consumed or not)
+is represented with a ``StateRef`` object. If the state ref has been looked up from storage, you will have a
+``StateAndRef`` which is simply a ``StateRef`` plus the data.
-The ``TransactionState`` is a container for a ``ContractState`` (the custom data used by a contract program) and additional
-platform-level state information, such as the *notary* pointer (see :doc:`consensus`).
+The ``ContractState`` type is an interface that all states must implement. A ``TransactionState`` is a simple
+container for a ``ContractState`` (the custom data used by a contract program) and additional platform-level state
+information, such as the *notary* pointer (see :doc:`consensus`).
-A number of interfaces then extend ``ContractState``, representing standardised functionality for states:
+A number of interfaces then extend ``ContractState``, representing standardised functionality for common kinds
+of state:
``OwnableState``
- A state which has an owner (represented as a ``PublicKey``, discussed later). Exposes the owner and a function for
- replacing the owner.
+ A state which has an owner (represented as a ``PublicKey``, discussed later). Exposes the owner and a function
+ for replacing the owner e.g. when an asset is sold.
``LinearState``
- A state which links back to its previous state, creating a thread of states over time. Intended to simplify tracking
- state versions.
+ A state which links back to its previous state, creating a thread of states over time. A linear state is
+ useful when modelling an indivisible/non-fungible thing like a specific deal, or an asset that can't be
+ split (like a rare piece of art).
``DealState``
- A state representing an agreement between two or more parties. Intended to simplify implementing generic protocols
- that manipulate many agreement types.
+ A LinearState representing an agreement between two or more parties. Intended to simplify implementing generic
+ protocols that manipulate many agreement types.
``FixableDealState``
A deal state, with further functions exposed to support fixing of interest rates.
-Things (such as attachments) which are identified by their hash should implement the ``NamedByHash`` interface,
-which standardises how the ID is extracted.
+NamedByHash and UniqueIdentifier
+--------------------------------
+
+Things which are identified by their hash, like transactions and attachments, should implement the ``NamedByHash``
+interface which standardises how the ID is extracted. Note that a hash is *not* a globally unique identifier: it
+is always a derivative summary of the contents of the underlying data. Sometimes this isn't what you want:
+two deals that have exactly the same parameters and which are made simultaneously but which are logically different
+can't be identified by hash because their contents would be identical. Instead you would use ``UniqueIdentifier``.
+This is a combination of a (Java) ``UUID`` representing a globally unique 128 bit random number, and an arbitrary
+string which can be paired with it. For instance the string may represent an existing "weak" (not guaranteed unique)
+identifier for convenience purposes.
FungibleAssets and Cash
-----------------------
@@ -77,6 +99,9 @@ in place of the attachments themselves (see also :doc:`data-model`). Once signed
resolving the attachment references to the attachments. Commands with valid signatures are encapsulated in the
``AuthenticatedObject`` type.
+.. note:: A ``LedgerTransaction`` has not necessarily had its contracts be run, and thus could be contract-invalid
+ (but not signature-invalid). You can use the ``verify`` method as shown below to run the contracts.
+
When constructing a new transaction from scratch, you use ``TransactionBuilder``, which is a mutable transaction that
can be signed once modification of the internals is complete. It is typical for contract classes to expose helper
methods that can contribute to a ``TransactionBuilder``.
@@ -105,12 +130,20 @@ write your tests using the :doc:`domain specific language for writing tests `_.
diff --git a/docs/build/html/_sources/tutorial-contract-clauses.txt b/docs/build/html/_sources/tutorial-contract-clauses.txt
index eb1ee343c0..325fcc437c 100644
--- a/docs/build/html/_sources/tutorial-contract-clauses.txt
+++ b/docs/build/html/_sources/tutorial-contract-clauses.txt
@@ -10,14 +10,18 @@ Writing a contract using clauses
This tutorial will take you through restructuring the commercial paper contract to use clauses. You should have
already completed ":doc:`tutorial-contract`".
-Clauses are essentially micro-contracts which contain independent verification logic, and are composed together to form
-a contract. With appropriate design, they can be made to be reusable, for example issuing contract state objects is
-generally the same for all fungible contracts, so a single issuance clause can be shared. This cuts down on scope for
-error, and improves consistency of behaviour.
+Clauses are essentially micro-contracts which contain independent verification logic, and can be logically composed
+together to form a contract. Clauses are designed to enable re-use of common logic, for example issuing state objects
+is generally the same for all fungible contracts, so a common issuance clause can be inherited for each contract's
+issue clause. This cuts down on scope for error, and improves consistency of behaviour. By splitting verification logic
+into smaller chunks, they can also be readily tested in isolation.
-Clauses can be composed of subclauses, either to combine clauses in different ways, or to apply specialised clauses.
-In the case of commercial paper, we have a ``Group`` outermost clause, which will contain the ``Issue``, ``Move`` and
-``Redeem`` clauses. The result is a contract that looks something like this:
+Clauses can be composed of subclauses, for example the ``AllClause`` or ``AnyClause`` clauses take list of clauses
+that they delegate to. Clauses can also change the scope of states and commands being verified, for example grouping
+together fungible state objects and running a clause against each distinct group.
+
+The commercial paper contract has a ``Group`` outermost clause, which contains the ``Issue``, ``Move`` and ``Redeem``
+clauses. The result is a contract that looks something like this:
1. Group input and output states together, and then apply the following clauses on each group:
a. If an ``Issue`` command is present, run appropriate tests and end processing this group.
@@ -27,11 +31,12 @@ In the case of commercial paper, we have a ``Group`` outermost clause, which wil
Commercial paper class
----------------------
-To use the clause verification logic, the contract needs to call the ``verifyClauses()`` function, passing in the transaction,
-a list of clauses to verify, and a collection of commands the clauses are expected to handle all of. This list of
-commands is important because ``verifyClauses()`` checks that none of the commands are left unprocessed at the end, and
-raises an error if they are. The following examples are trimmed to the modified class definition and added elements, for
-brevity:
+To use the clause verification logic, the contract needs to call the ``verifyClause`` function, passing in the
+transaction, a clause to verify, and a collection of commands the clauses are expected to handle all of. This list of
+commands is important because ``verifyClause`` checks that none of the commands are left unprocessed at the end, and
+raises an error if they are. The top level clause would normally be a composite clause (such as ``AnyComposition``,
+``AllComposition``, etc.) which contains further clauses. The following examples are trimmed to the modified class
+definition and added elements, for brevity:
.. container:: codeset
@@ -40,10 +45,7 @@ brevity:
class CommercialPaper : Contract {
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper")
- private fun extractCommands(tx: TransactionForContract): List>
- = tx.commands.select()
-
- override fun verify(tx: TransactionForContract) = verifyClauses(tx, listOf(Clauses.Group()), extractCommands(tx))
+ override fun verify(tx: TransactionForContract) = verifyClause(tx, Clauses.Group(), tx.commands.select())
.. sourcecode:: java
@@ -53,53 +55,40 @@ brevity:
return SecureHash.Companion.sha256("https://en.wikipedia.org/wiki/Commercial_paper");
}
- @Override
- public Collection> extractCommands(@NotNull TransactionForContract tx) {
- return tx.getCommands()
- .stream()
- .filter((AuthenticatedObject command) -> { return command.getValue() instanceof Commands; })
- .collect(Collectors.toList());
- }
-
@Override
public void verify(@NotNull TransactionForContract tx) throws IllegalArgumentException {
- ClauseVerifier.verifyClauses(tx, Collections.singletonList(new Clause.Group()), extractCommands(tx));
+ ClauseVerifier.verifyClause(tx, new Clauses.Group(), extractCommands(tx));
}
Clauses
-------
We'll tackle the inner clauses that contain the bulk of the verification logic, first, and the clause which handles
-grouping of input/output states later. The inner clauses need to implement the ``GroupClause`` interface, which defines
-the verify() function, and properties (``ifMatched``, ``ifNotMatched`` and ``requiredCommands``) defining how the clause
-is processed. These properties specify the command(s) which must be present in order for the clause to be matched,
-and what to do after processing the clause depending on whether it was matched or not.
+grouping of input/output states later. The clauses must implement the ``Clause`` interface, which defines
+the ``verify`` function, and the ``requiredCommands`` property used to determine the conditions under which a clause
+is triggered. Normally clauses would extend ``ConcreteClause`` which provides defaults suitable for a clause which
+verifies transactions, rather than delegating to other clauses.
-The ``verify()`` functions defined in the ``SingleClause`` and ``GroupClause`` interfaces is similar to the conventional
-``Contract`` verification function, although it adds new parameters and returns the set of commands which it has processed.
-Normally this returned set is identical to the commands matched in order to trigger the clause, however in some cases the
-clause may process optional commands which it needs to report that it has handled, or may by designed to only process
-the first (or otherwise) matched command.
+The ``verify`` function defined in the ``Clause`` interface is similar to the conventional ``Contract`` verification
+function, although it adds new parameters and returns the set of commands which it has processed. Normally this returned
+set is identical to the ``requiredCommands`` used to trigger the clause, however in some cases the clause may process
+further optional commands which it needs to report that it has handled.
-The Move clause for the commercial paper contract is relatively simple, so lets start there:
+The ``Move`` clause for the commercial paper contract is relatively simple, so we will start there:
.. container:: codeset
.. sourcecode:: kotlin
- class Move: GroupClause> {
- override val ifNotMatched: MatchBehaviour
- get() = MatchBehaviour.CONTINUE
- override val ifMatched: MatchBehaviour
- get() = MatchBehaviour.END
+ class Move: ConcreteClause>() {
override val requiredCommands: Set>
get() = setOf(Commands.Move::class.java)
override fun verify(tx: TransactionForContract,
inputs: List,
outputs: List,
- commands: Collection>,
- token: Issued): Set {
+ commands: List>,
+ groupingKey: Issued?): Set {
val command = commands.requireSingleCommand()
val input = inputs.single()
requireThat {
@@ -114,140 +103,79 @@ The Move clause for the commercial paper contract is relatively simple, so lets
.. sourcecode:: java
- public class Move implements GroupClause {
- @Override
- public MatchBehaviour getIfNotMatched() {
- return MatchBehaviour.CONTINUE;
- }
-
- @Override
- public MatchBehaviour getIfMatched() {
- return MatchBehaviour.END;
- }
-
+ class Move extends ConcreteClause {
+ @NotNull
@Override
public Set> getRequiredCommands() {
return Collections.singleton(Commands.Move.class);
}
+ @NotNull
@Override
- public Set verify(@NotNull TransactionForContract tx,
+ public Set verify(@NotNull TransactionForContract tx,
@NotNull List extends State> inputs,
@NotNull List extends State> outputs,
- @NotNull Collection extends AuthenticatedObject extends CommandData>> commands,
- @NotNull State token) {
- AuthenticatedObject cmd = requireSingleCommand(tx.getCommands(), JavaCommercialPaper.Commands.Move.class);
+ @NotNull List extends AuthenticatedObject extends Commands>> commands,
+ @NotNull State groupingKey) {
+ AuthenticatedObject cmd = requireSingleCommand(tx.getCommands(), Commands.Move.class);
// There should be only a single input due to aggregation above
State input = single(inputs);
- requireThat(require -> {
- require.by("the transaction is signed by the owner of the CP", cmd.getSigners().contains(input.getOwner()));
- require.by("the state is propagated", outputs.size() == 1);
- return Unit.INSTANCE;
- });
+ if (!cmd.getSigners().contains(input.getOwner()))
+ throw new IllegalStateException("Failed requirement: the transaction is signed by the owner of the CP");
+
+ // Check the output CP state is the same as the input state, ignoring the owner field.
+ if (outputs.size() != 1) {
+ throw new IllegalStateException("the state is propagated");
+ }
// Don't need to check anything else, as if outputs.size == 1 then the output is equal to
// the input ignoring the owner field due to the grouping.
return Collections.singleton(cmd.getValue());
}
}
-The post-processing ``MatchBehaviour`` options are:
- * CONTINUE
- * END
- * ERROR
-
-In this case we process commands against each group, until the first matching clause is found, so we ``END`` on a match
-and ``CONTINUE`` otherwise. ``ERROR`` can be used as a part of a clause which must always/never be matched. By default
-clauses are always matched (``requiredCommands`` is an empty set), execution continues after a clause is matched, and an
-error is raised if a clause is not matched.
-
Group Clause
------------
We need to wrap the move clause (as well as the issue and redeem clauses - see the relevant contract code for their
-full specifications) in an outer clause. For this we extend the standard ``GroupClauseVerifier`` and specify how to
-group input/output states, as well as the clauses to run on each group.
+full specifications) in an outer clause that understands how to group contract states and objects. For this we extend
+the standard ``GroupClauseVerifier`` and specify how to group input/output states, as well as the top-level to run on
+each group. As with the top level clause on a contract, this is normally a composite clause that delegates to subclauses.
.. container:: codeset
.. sourcecode:: kotlin
- class Group : GroupClauseVerifier>() {
- override val ifNotMatched: MatchBehaviour
- get() = MatchBehaviour.ERROR
- override val ifMatched: MatchBehaviour
- get() = MatchBehaviour.END
- override val clauses: List>>
- get() = listOf(
- Clause.Redeem(),
- Clause.Move(),
- Clause.Issue()
- )
-
- override fun extractGroups(tx: TransactionForContract): List>>
+ class Group : GroupClauseVerifier>(
+ AnyComposition(
+ Redeem(),
+ Move(),
+ Issue())) {
+ override fun groupStates(tx: TransactionForContract): List>>
= tx.groupStates> { it.token }
}
.. sourcecode:: java
- public class Group extends GroupClauseVerifier {
- @Override
- public MatchBehaviour getIfMatched() {
- return MatchBehaviour.END;
+ class Group extends GroupClauseVerifier {
+ public Group() {
+ super(new AnyComposition<>(
+ new Clauses.Redeem(),
+ new Clauses.Move(),
+ new Clauses.Issue()
+ ));
}
+ @NotNull
@Override
- public MatchBehaviour getIfNotMatched() {
- return MatchBehaviour.ERROR;
- }
-
- @Override
- public List> getClauses() {
- final List> clauses = new ArrayList<>();
-
- clauses.add(new Clause.Redeem());
- clauses.add(new Clause.Move());
- clauses.add(new Clause.Issue());
-
- return clauses;
- }
-
- @Override
- public List> extractGroups(@NotNull TransactionForContract tx) {
+ public List> groupStates(@NotNull TransactionForContract tx) {
return tx.groupStates(State.class, State::withoutOwner);
}
}
-We then pass this clause into the outer ``ClauseVerifier`` contract by returning it from the ``clauses`` property. We
-also implement the ``extractCommands()`` function, which filters commands on the transaction down to the set the
-contained clauses must handle (any unmatched commands at the end of clause verification results in an exception to be
-thrown).
-
-.. container:: codeset
-
- .. sourcecode:: kotlin
-
- override val clauses: List
- get() = listOf(Clauses.Group())
-
- override fun extractCommands(tx: TransactionForContract): List>
- = tx.commands.select()
-
- .. sourcecode:: java
-
- @Override
- public List getClauses() {
- return Collections.singletonList(new Clause.Group());
- }
-
- @Override
- public Collection> extractCommands(@NotNull TransactionForContract tx) {
- return tx.getCommands()
- .stream()
- .filter((AuthenticatedObject command) -> { return command.getValue() instanceof Commands; })
- .collect(Collectors.toList());
- }
+For the ``CommercialPaper`` contract, this is the top level clause for the contract, and is passed directly into
+``verifyClause`` (see the example code at the top of this tutorial).
Summary
-------
@@ -255,4 +183,12 @@ Summary
In summary the top level contract ``CommercialPaper`` specifies a single grouping clause of type
``CommercialPaper.Clauses.Group`` which in turn specifies ``GroupClause`` implementations for each type of command
(``Redeem``, ``Move`` and ``Issue``). This reflects the flow of verification: In order to verify a ``CommercialPaper``
-we first group states, check which commands are specified, and run command-specific verification logic accordingly.
\ No newline at end of file
+we first group states, check which commands are specified, and run command-specific verification logic accordingly.
+
+Debugging
+---------
+
+Debugging clauses which have been composed together can be complicated due to the difficulty in knowing which clauses
+have been matched, whether specific clauses failed to match or passed verification, etc. There is "trace" level
+logging code in the clause verifier which evaluates which clauses will be matched and logs them, before actually
+performing the validation. To enable this, ensure trace level logging is enabled on the ``Clause`` interface.
diff --git a/docs/build/html/_sources/tutorial-contract.txt b/docs/build/html/_sources/tutorial-contract.txt
index f149a8a433..c3b1b4ca75 100644
--- a/docs/build/html/_sources/tutorial-contract.txt
+++ b/docs/build/html/_sources/tutorial-contract.txt
@@ -9,18 +9,26 @@ Writing a contract
This tutorial will take you through how the commercial paper contract works. This uses a simple contract structure of
everything being in one contract class, while most actual contracts in Corda are broken into clauses (which we'll
-discuss in the next tutorial). You can see the full Kotlin version of this contract in the code as ``CommercialPaperLegacy``.
+discuss in the next tutorial). Clauses help reduce tedious boilerplate, but it's worth understanding how a
+contract is built without them before starting.
-The code in this tutorial is available in both Kotlin and Java. You can quickly switch between them to get a feeling
-for how Kotlin syntax works.
+You can see the full Kotlin version of this contract in the code as ``CommercialPaperLegacy``. The code in this
+tutorial is available in both Kotlin and Java. You can quickly switch between them to get a feeling for how
+Kotlin syntax works.
+
+Where to put your code
+----------------------
+
+A CorDapp is a collection of contracts, state definitions, protocols and other ways to extend the server. To create
+one you would just create a Java-style project as normal, with your choice of build system (Maven, Gradle, etc).
+Then add a dependency on ``com.r3corda:core:0.X`` where X is the milestone number you are depending on. The core
+module defines the base classes used in this tutorial.
Starting the commercial paper class
-----------------------------------
A smart contract is a class that implements the ``Contract`` interface. This can be either implemented directly, or
-via an abstract contract such as ``ClauseVerifier``. For now, contracts have to be a part of the main codebase, as
-dynamic loading of contract code is not yet implemented. Therefore, we start by creating a file named either
-``CommercialPaper.kt`` or ``CommercialPaper.java`` in the ``contracts/src/main`` directory with the following contents:
+by subclassing an abstract contract such as ``OnLedgerAsset``.
.. container:: codeset
@@ -52,13 +60,11 @@ Every contract must have at least a ``getLegalContractReference()`` and a ``veri
a getter without a setter as an immutable property (val). The *legal contract reference* is supposed to be a hash
of a document that describes the legal contract and may take precedence over the code, in case of a dispute.
+.. note:: The way legal contract prose is bound to a smart contract implementation will change in future.
+
The verify method returns nothing. This is intentional: the function either completes correctly, or throws an exception,
in which case the transaction is rejected.
-We also need to define a constant hash that would, in a real system, be the hash of the program bytecode. For now
-we just set it to a dummy value as dynamic loading and sandboxing of bytecode is not implemented. This constant
-isn't shown in the code snippet but is called ``CP_PROGRAM_ID``.
-
So far, so simple. Now we need to define the commercial paper *state*, which represents the fact of ownership of a
piece of issued paper.
@@ -72,101 +78,117 @@ A state is a class that stores data that is checked by the contract.
.. sourcecode:: kotlin
data class State(
- val issuance: InstitutionReference,
- val owner: PublicKey,
- val faceValue: Amount,
- val maturityDate: Instant
- ) : ContractState {
- override val programRef = CP_PROGRAM_ID
+ val issuance: PartyAndReference,
+ override val owner: PublicKey,
+ val faceValue: Amount>,
+ val maturityDate: Instant
+ ) : OwnableState {
+ override val contract = CommercialPaper()
+ override val participants = listOf(owner)
- fun withoutOwner() = copy(owner = NullPublicKey)
+ fun withoutOwner() = copy(owner = NullPublicKey)
+ override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
}
.. sourcecode:: java
- public static class State implements ContractState, SerializeableWithKryo {
- private InstitutionReference issuance;
- private PublicKey owner;
- private Amount faceValue;
- private Instant maturityDate;
+ public static class State implements OwnableState {
+ private PartyAndReference issuance;
+ private PublicKey owner;
+ private Amount> faceValue;
+ private Instant maturityDate;
- public State() {} // For serialization
+ public State() {
+ } // For serialization
- public State(InstitutionReference issuance, PublicKey owner, Amount faceValue, Instant maturityDate) {
- this.issuance = issuance;
- this.owner = owner;
- this.faceValue = faceValue;
- this.maturityDate = maturityDate;
- }
+ public State(PartyAndReference issuance, PublicKey owner, Amount> faceValue,
+ Instant maturityDate) {
+ this.issuance = issuance;
+ this.owner = owner;
+ this.faceValue = faceValue;
+ this.maturityDate = maturityDate;
+ }
- public InstitutionReference getIssuance() {
- return issuance;
- }
+ public State copy() {
+ return new State(this.issuance, this.owner, this.faceValue, this.maturityDate);
+ }
- public PublicKey getOwner() {
- return owner;
- }
+ @NotNull
+ @Override
+ public Pair withNewOwner(@NotNull PublicKey newOwner) {
+ return new Pair<>(new Commands.Move(), new State(this.issuance, newOwner, this.faceValue, this.maturityDate));
+ }
- public Amount getFaceValue() {
- return faceValue;
- }
+ public PartyAndReference getIssuance() {
+ return issuance;
+ }
- public Instant getMaturityDate() {
- return maturityDate;
- }
+ public PublicKey getOwner() {
+ return owner;
+ }
- @NotNull
- @Override
- public SecureHash getProgramRef() {
- return SecureHash.Companion.sha256("java commercial paper (this should be a bytecode hash)");
- }
+ public Amount> getFaceValue() {
+ return faceValue;
+ }
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ public Instant getMaturityDate() {
+ return maturityDate;
+ }
- State state = (State) o;
+ @NotNull
+ @Override
+ public Contract getContract() {
+ return new JavaCommercialPaper();
+ }
- if (issuance != null ? !issuance.equals(state.issuance) : state.issuance != null) return false;
- if (owner != null ? !owner.equals(state.owner) : state.owner != null) return false;
- if (faceValue != null ? !faceValue.equals(state.faceValue) : state.faceValue != null) return false;
- return !(maturityDate != null ? !maturityDate.equals(state.maturityDate) : state.maturityDate != null);
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
- }
+ State state = (State) o;
- @Override
- public int hashCode() {
- int result = issuance != null ? issuance.hashCode() : 0;
- result = 31 * result + (owner != null ? owner.hashCode() : 0);
- result = 31 * result + (faceValue != null ? faceValue.hashCode() : 0);
- result = 31 * result + (maturityDate != null ? maturityDate.hashCode() : 0);
- return result;
- }
+ if (issuance != null ? !issuance.equals(state.issuance) : state.issuance != null) return false;
+ if (owner != null ? !owner.equals(state.owner) : state.owner != null) return false;
+ if (faceValue != null ? !faceValue.equals(state.faceValue) : state.faceValue != null) return false;
+ return !(maturityDate != null ? !maturityDate.equals(state.maturityDate) : state.maturityDate != null);
+ }
- public State withoutOwner() {
- return new State(issuance, NullPublicKey.INSTANCE, faceValue, maturityDate);
- }
+ @Override
+ public int hashCode() {
+ int result = issuance != null ? issuance.hashCode() : 0;
+ result = 31 * result + (owner != null ? owner.hashCode() : 0);
+ result = 31 * result + (faceValue != null ? faceValue.hashCode() : 0);
+ result = 31 * result + (maturityDate != null ? maturityDate.hashCode() : 0);
+ return result;
+ }
+
+ @NotNull
+ @Override
+ public List getParticipants() {
+ return ImmutableList.of(this.owner);
+ }
}
-We define a class that implements the ``ContractState`` and ``SerializableWithKryo`` interfaces. The
-latter is an artifact of how the prototype implements serialization and can be ignored for now: it wouldn't work
-like this in any final product.
-The ``ContractState`` interface requires us to provide a ``getProgramRef`` method that is supposed to return a hash of
-the bytecode of the contract itself. For now this is a dummy value and isn't used: later on, this mechanism will change.
-Beyond that it's a freeform object into which we can put anything which can be serialized.
+We define a class that implements the ``ContractState`` interface.
+
+The ``ContractState`` interface requires us to provide a ``getContract`` method that returns an instance of the
+contract class itself. In future, this will change to support dynamic loading of contracts with versioning
+and signing constraints, but for now this is how it's written.
We have four fields in our state:
-* ``issuance``: a reference to a specific piece of commercial paper at a party
-* ``owner``: the public key of the current owner. This is the same concept as seen in Bitcoin: the public key has no
+* ``issuance``, a reference to a specific piece of commercial paper issued by some party.
+* ``owner``, the public key of the current owner. This is the same concept as seen in Bitcoin: the public key has no
attached identity and is expected to be one-time-use for privacy reasons. However, unlike in Bitcoin, we model
- ownership at the level of individual contracts rather than as a platform-level concept as we envisage many
+ ownership at the level of individual states rather than as a platform-level concept as we envisage many
(possibly most) contracts on the platform will not represent "owner/issuer" relationships, but "party/party"
relationships such as a derivative contract.
-* ``faceValue``: an ``Amount``, which wraps an integer number of pennies and a currency.
-* ``maturityDate``: an `Instant `_, which is a type
+* ``faceValue``, an ``Amount>``, which wraps an integer number of pennies and a currency that is
+ specific to some issuer (e.g. a regular bank, a central bank, etc). You can read more about this very common
+ type in :doc:`transaction-data-types`.
+* ``maturityDate``, an `Instant `_, which is a type
from the Java 8 standard time library. It defines a point on the timeline.
States are immutable, and thus the class is defined as immutable as well. The ``data`` modifier in the Kotlin version
@@ -175,7 +197,7 @@ be used to create variants of the original object. Data classes are similar to c
familiar with that language. The ``withoutOwner`` method uses the auto-generated copy method to return a version of
the state with the owner public key blanked out: this will prove useful later.
-The Java code compiles to the same bytecode as the Kotlin version, but as you can see, is much more verbose.
+The Java code compiles to almost identical bytecode as the Kotlin version, but as you can see, is much more verbose.
Commands
--------
@@ -183,10 +205,11 @@ Commands
The logic for a contract may vary depending on what stage of a lifecycle it is automating. So it can be useful to
pass additional data into the contract code that isn't represented by the states which exist permanently in the ledger.
-For this purpose we have commands. Often, they don't need to contain any data at all, they just need to exist. A command
+For this purpose we have commands. Often they don't need to contain any data at all, they just need to exist. A command
is a piece of data associated with some *signatures*. By the time the contract runs the signatures have already been
checked, so from the contract code's perspective, a command is simply a data structure with a list of attached
-public keys. Each key had a signature proving that the corresponding private key was used to sign.
+public keys. Each key had a signature proving that the corresponding private key was used to sign. Because of this
+approach contracts never actually interact or work with digital signatures directly.
Let's define a few commands now:
@@ -194,10 +217,10 @@ Let's define a few commands now:
.. sourcecode:: kotlin
- interface Commands : Command {
- object Move : Commands
- object Redeem : Commands
- object Issue : Commands
+ interface Commands : CommandData {
+ class Move : TypeOnlyCommandData(), Commands
+ class Redeem : TypeOnlyCommandData(), Commands
+ class Issue : TypeOnlyCommandData(), Commands
}
@@ -226,9 +249,10 @@ Let's define a few commands now:
}
}
-The ``object`` keyword in Kotlin just defines a singleton object. As the commands don't need any additional data in our
-case, they can be empty and we just use their type as the important information. Java has no syntax for declaring
-singletons, so we just define a class that considers any other instance to be equal and that's good enough.
+We define a simple grouping interface or static class, this gives us a type that all our commands have in common,
+then we go ahead and create three commands: ``Move``, ``Redeem``, ``Issue``. ``TypeOnlyCommandData`` is a helpful utility
+for the case when there's no data inside the command; only the existence matters. It defines equals and hashCode
+such that any instances always compare equal and hash to the same value.
The verify function
-------------------
@@ -246,23 +270,28 @@ run two contracts one time each: Cash and CommercialPaper.
.. sourcecode:: kotlin
- override fun verify(tx: TransactionForVerification) {
+ override fun verify(tx: TransactionForContract) {
// Group by everything except owner: any modification to the CP at all is considered changing it fundamentally.
- val groups = tx.groupStates() { it: State -> it.withoutOwner() }
+ val groups = tx.groupStates(State::withoutOwner)
+
+ // There are two possible things that can be done with this CP. The first is trading it. The second is redeeming
+ // it for cash on or after the maturity date.
val command = tx.commands.requireSingleCommand()
.. sourcecode:: java
@Override
- public void verify(@NotNull TransactionForVerification tx) {
+ public void verify(TransactionForContract tx) {
List> groups = tx.groupStates(State.class, State::withoutOwner);
AuthenticatedObject cmd = requireSingleCommand(tx.getCommands(), Commands.class);
We start by using the ``groupStates`` method, which takes a type and a function. State grouping is a way of ensuring
your contract can handle multiple unrelated states of the same type in the same transaction, which is needed for
-splitting/merging of assets, atomic swaps and so on. The second line does what the code suggests: it searches for
-a command object that inherits from the ``CommercialPaper.Commands`` supertype, and either returns it, or throws an
-exception if there's zero or more than one such command.
+splitting/merging of assets, atomic swaps and so on. More on this next.
+
+The second line does what the code suggests: it searches for a command object that inherits from the
+``CommercialPaper.Commands`` supertype, and either returns it, or throws an exception if there's zero or more than one
+such command.
Using state groups
------------------
@@ -297,12 +326,12 @@ inputs e.g. because she received the dollars in two payments. The input and outp
the cash smart contract must consider the pounds and the dollars separately because they are not fungible: they cannot
be merged together. So we have two groups: A and B.
-The ``TransactionForVerification.groupStates`` method handles this logic for us: firstly, it selects only states of the
+The ``TransactionForContract.groupStates`` method handles this logic for us: firstly, it selects only states of the
given type (as the transaction may include other types of state, such as states representing bond ownership, or a
multi-sig state) and then it takes a function that maps a state to a grouping key. All states that share the same key are
grouped together. In the case of the cash example above, the grouping key would be the currency.
-In other kinds of contract, we don't want CP to be fungible: merging and splitting is (in our example) not allowed.
+In this kind of contract we don't want CP to be fungible: merging and splitting is (in our example) not allowed.
So we just use a copy of the state minus the owner field as the grouping key.
Here are some code examples:
@@ -372,51 +401,53 @@ logic.
.. sourcecode:: kotlin
- val time = tx.time
- for (group in groups) {
+ val timestamp: Timestamp? = tx.timestamp
+
+ for ((inputs, outputs, key) in groups) {
when (command.value) {
is Commands.Move -> {
- val input = group.inputs.single()
+ val input = inputs.single()
requireThat {
- "the transaction is signed by the owner of the CP" by (command.signers.contains(input.owner))
+ "the transaction is signed by the owner of the CP" by (input.owner in command.signers)
"the state is propagated" by (group.outputs.size == 1)
+ // Don't need to check anything else, as if outputs.size == 1 then the output is equal to
+ // the input ignoring the owner field due to the grouping.
}
}
is Commands.Redeem -> {
- val input = group.inputs.single()
- val received = tx.outStates.sumCashBy(input.owner)
- if (time == null) throw IllegalArgumentException("Redemption transactions must be timestamped")
+ // Redemption of the paper requires movement of on-ledger cash.
+ val input = inputs.single()
+ val received = tx.outputs.sumCashBy(input.owner)
+ val time = timestamp?.after ?: throw IllegalArgumentException("Redemptions must be timestamped")
requireThat {
- "the paper must have matured" by (time > input.maturityDate)
+ "the paper must have matured" by (time >= input.maturityDate)
"the received amount equals the face value" by (received == input.faceValue)
- "the paper must be destroyed" by group.outputs.isEmpty()
- "the transaction is signed by the owner of the CP" by (command.signers.contains(input.owner))
+ "the paper must be destroyed" by outputs.isEmpty()
+ "the transaction is signed by the owner of the CP" by (input.owner in command.signers)
}
}
is Commands.Issue -> {
- val output = group.outputs.single()
- if (time == null) throw IllegalArgumentException("Issuance transactions must be timestamped")
+ val output = outputs.single()
+ val time = timestamp?.before ?: throw IllegalArgumentException("Issuances must be timestamped")
requireThat {
// Don't allow people to issue commercial paper under other entities identities.
- "the issuance is signed by the claimed issuer of the paper" by
- (command.signers.contains(output.issuance.party.owningKey))
- "the face value is not zero" by (output.faceValue.pennies > 0)
- "the maturity date is not in the past" by (time < output.maturityDate )
+ "output states are issued by a command signer" by (output.issuance.party.owningKey in command.signers)
+ "output values sum to more than the inputs" by (output.faceValue.quantity > 0)
+ "the maturity date is not in the past" by (time < output.maturityDate)
// Don't allow an existing CP state to be replaced by this issuance.
- "there is no input state" by group.inputs.isEmpty()
+ "can't reissue an existing state" by inputs.isEmpty()
}
}
- // TODO: Think about how to evolve contracts over time with new commands.
else -> throw IllegalArgumentException("Unrecognised command")
}
}
.. sourcecode:: java
- Instant time = tx.getTime(); // Can be null/missing.
+ Timestamp time = tx.getTimestamp(); // Can be null/missing.
for (InOutGroup group : groups) {
List inputs = group.getInputs();
List outputs = group.getOutputs();
@@ -424,34 +455,20 @@ logic.
// For now do not allow multiple pieces of CP to trade in a single transaction. Study this more!
State input = single(filterIsInstance(inputs, State.class));
- requireThat(require -> {
- require.by("the transaction is signed by the owner of the CP", cmd.getSigners().contains(input.getOwner()));
- return Unit.INSTANCE;
- });
+ checkState(cmd.getSigners().contains(input.getOwner()), "the transaction is signed by the owner of the CP");
if (cmd.getValue() instanceof JavaCommercialPaper.Commands.Move) {
- requireThat(require -> {
- require.by("the state is propagated", outputs.size() == 1);
- return Unit.INSTANCE;
- });
+ checkState(outputs.size() == 1, "the state is propagated");
// Don't need to check anything else, as if outputs.size == 1 then the output is equal to
// the input ignoring the owner field due to the grouping.
} else if (cmd.getValue() instanceof JavaCommercialPaper.Commands.Redeem) {
- TimestampCommand timestampCommand = tx.getTimestampBy(((Commands.Redeem) cmd.getValue()).notary);
- Instant time = null == timestampCommand
- ? null
- : timestampCommand.getBefore();
+ checkNotNull(timem "must be timestamped");
+ Instant t = time.getBefore();
Amount> received = CashKt.sumCashBy(tx.getOutputs(), input.getOwner());
- requireThat(require -> {
- require.by("must be timestamped", timestampCommand != null);
- require.by("received amount equals the face value: "
- + received + " vs " + input.getFaceValue(), received.equals(input.getFaceValue()));
- require.by("the paper must have matured", time != null && !time.isBefore(input.getMaturityDate()));
- require.by("the received amount equals the face value", input.getFaceValue().equals(received));
- require.by("the paper must be destroyed", outputs.isEmpty());
- return Unit.INSTANCE;
- });
+ checkState(received.equals(input.getFaceValue()), "received amount equals the face value");
+ checkState(t.isBefore(input.getMaturityDate(), "the paper must have matured");
+ checkState(outputs.isEmpty(), "the paper must be destroyed");
} else if (cmd.getValue() instanceof JavaCommercialPaper.Commands.Issue) {
// .. etc .. (see Kotlin for full definition)
}
@@ -462,7 +479,9 @@ 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
may be missing here. We check for it being null later.
-.. note:: In the Kotlin version, as long as we write a comparison with the transaction time first, the compiler will
+.. note:: In future timestamping may be mandatory for all transactions.
+
+.. 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.
@@ -471,19 +490,19 @@ The first line (first three lines in Java) impose a requirement that there be a
this group. We do not allow multiple units of CP to be split or merged even if they are owned by the same owner. The
``single()`` method is a static *extension method* defined by the Kotlin standard library: given a list, it throws an
exception if the list size is not 1, otherwise it returns the single item in that list. In Java, this appears as a
-regular static method of the type familiar from many FooUtils type singleton classes. In Kotlin, it appears as a
-method that can be called on any JDK list. The syntax is slightly different but behind the scenes, the code compiles
-to the same bytecodes.
+regular static method of the type familiar from many FooUtils type singleton classes and we have statically imported it
+here. In Kotlin, it appears as a method that can be called on any JDK list. The syntax is slightly different but
+behind the scenes, the code compiles to the same bytecodes.
Next, we check that the transaction was signed by the public key that's marked as the current owner of the commercial
paper. Because the platform has already verified all the digital signatures before the contract begins execution,
all we have to do is verify that the owner's public key was one of the keys that signed the transaction. The Java code
-is straightforward. The Kotlin version looks a little odd: we have a *requireThat* construct that looks like it's
+is straightforward: we are simply using the ``Preconditions.checkState`` method from Guava. The Kotlin version looks a little odd: we have a *requireThat* construct that looks like it's
built into the language. In fact *requireThat* is an ordinary function provided by the platform's contract API. Kotlin
supports the creation of *domain specific languages* through the intersection of several features of the language, and
we use it here to support the natural listing of requirements. To see what it compiles down to, look at the Java version.
Each ``"string" by (expression)`` statement inside a ``requireThat`` turns into an assertion that the given expression is
-true, with an exception being thrown that contains the string if not. It's just another way to write out a regular
+true, with an ``IllegalStateException`` being thrown that contains the string if not. It's just another way to write out a regular
assertion, but with the English-language requirement being put front and center.
Next, we take one of two paths, depending on what the type of the command object is.
@@ -500,19 +519,19 @@ If the command is a ``Redeem`` command, then the requirements are more complex:
3. The commercial paper must *not* be propagated by this transaction: it must be deleted, by the group having no
output state. This prevents the same CP being considered redeemable multiple times.
-To calculate how much cash is moving, we use the ``sumCashOrNull`` utility method. Again, this is an extension method,
+To calculate how much cash is moving, we use the ``sumCashBy`` utility function. Again, this is an extension function,
so in Kotlin code it appears as if it was a method on the ``List`` type even though JDK provides no such
-method. In Java we see its true nature: it is actually a static method named ``CashKt.sumCashOrNull``. This method simply
-returns an ``Amount`` object containing the sum of all the cash states in the transaction output, or null if there were
-no such states *or* if there were different currencies represented in the outputs! So we can see that this contract
-imposes a limitation on the structure of a redemption transaction: you are not allowed to move currencies in the same
-transaction that the CP does not involve. This limitation could be addressed with better APIs, if it were to be a
-real limitation.
+method. In Java we see its true nature: it is actually a static method named ``CashKt.sumCashBy``. This method simply
+returns an ``Amount`` object containing the sum of all the cash states in the transaction outputs that are owned by
+that given public key, or throws an exception if there were no such states *or* if there were different currencies
+represented in the outputs! So we can see that this contract imposes a limitation on the structure of a redemption
+transaction: you are not allowed to move currencies in the same transaction that the CP does not involve. This
+limitation could be addressed with better APIs, if it were to be a real limitation.
Finally, we support an ``Issue`` command, to create new instances of commercial paper on the ledger. It likewise
enforces various invariants upon the issuance.
-This contract is extremely simple and does not implement all the business logic a real commercial paper lifecycle
+This contract is simple and does not implement all the business logic a real commercial paper lifecycle
management program would. For instance, there is no logic requiring a signature from the issuer for redemption:
it is assumed that any transfer of money that takes place at the same time as redemption is good enough. Perhaps
that is something that should be tightened. Likewise, there is no logic handling what happens if the issuer has gone
@@ -525,172 +544,15 @@ How to test your contract
-------------------------
Of course, it is essential to unit test your new nugget of business logic to ensure that it behaves as you expect.
-Although you can write traditional unit tests in Java, the platform also provides a *domain specific language*
-(DSL) for writing contract unit tests that automates many of the common patterns. This DSL builds on top of JUnit yet
-is a Kotlin DSL, and therefore this section will not show Java equivalent code (for Java unit tests you would not
-benefit from the DSL and would write them by hand).
+As contract code is just a regular Java function you could write out the logic entirely by hand in the usual
+manner. But this would be inconvenient, and then you'd get bored of writing tests and that would be bad: you
+might be tempted to skip a few.
-We start by defining a new test class, with a basic CP state:
+To make contract testing more convenient Corda provides a language-like API for both Kotlin and Java that lets
+you easily construct chains of transactions and verify that they either pass validation, or fail with a particular
+error message.
-.. container:: codeset
-
- .. sourcecode:: kotlin
-
- class CommercialPaperTests {
- val PAPER_1 = CommercialPaper.State(
- issuance = InstitutionReference(MEGA_CORP, OpaqueBytes.of(123)),
- owner = MEGA_CORP_KEY,
- faceValue = 1000.DOLLARS,
- maturityDate = TEST_TX_TIME + 7.days
- )
-
- @Test
- fun key_mismatch_at_issue() {
- transactionGroup {
- transaction {
- output { PAPER_1 }
- arg(DUMMY_PUBKEY_1) { CommercialPaper.Commands.Issue() }
- }
-
- expectFailureOfTx(1, "signed by the claimed issuer")
- }
- }
- }
-
-We start by defining a commercial paper state. It will be owned by a pre-defined unit test party, affectionately
-called ``MEGA_CORP`` (this constant, along with many others, is defined in ``TestUtils.kt``). Due to Kotin's extensive
-type inference, many types are not written out explicitly in this code and it has the feel of a scripting language.
-But the types are there, and you can ask IntelliJ to reveal them by pressing Alt-Enter on a "val" or "var" and selecting
-"Specify type explicitly".
-
-There are a few things that are unusual here:
-
-* We can specify quantities of money by writing 1000.DOLLARS or 1000.POUNDS
-* We can specify quantities of time by writing 7.days
-* We can add quantities of time to the TEST_TX_TIME constant, which merely defines an arbitrary java.time.Instant
-
-If you examine the code in the actual repository, you will also notice that it makes use of method names with spaces
-in them by surrounding the name with backticks, rather than using underscores. We don't show this here as it breaks the
-doc website's syntax highlighting engine.
-
-The ``1000.DOLLARS`` construct is quite simple: Kotlin allows you to define extension functions on primitive types like
-Int or Double. So by writing 7.days, for instance, the compiler will emit a call to a static method that takes an int
-and returns a ``java.time.Duration``.
-
-As this is JUnit, we must remember to annotate each test method with @Test. Let's examine the contents of the first test.
-We are trying to check that it's not possible for just anyone to issue commercial paper in MegaCorp's name. That would
-be bad!
-
-The ``transactionGroup`` function works the same way as the ``requireThat`` construct above.
-
-.. note:: This DSL is an example of what Kotlin calls a type safe builder, which you can read about in `the
- documentation for builders `_. You can mix and match
- ordinary code inside such DSLs so please read the linked page to make sure you fully understand what they are capable
- of.
-
-The code block that follows it is run in the scope of a freshly created ``TransactionGroupForTest`` object, which assists
-you with building little transaction graphs and verifying them as a whole. Here, our "group" only actually has a
-single transaction in it, with a single output, no inputs, and an Issue command signed by ``DUMMY_PUBKEY_1`` which is just
-an arbitrary public key. As the paper claims to be issued by ``MEGA_CORP``, this doesn't match and should cause a
-failure. The ``expectFailureOfTx`` method takes a 1-based index (in this case we expect the first transaction to fail)
-and a string that should appear in the exception message. Then it runs the ``TransactionGroup.verify()`` method to
-invoke all the involved contracts.
-
-It's worth bearing in mind that even though this code may look like a totally different language to normal Kotlin or
-Java, it's actually not, and so you can embed arbitrary code anywhere inside any of these blocks.
-
-Let's set up a full trade and ensure it works:
-
-.. container:: codeset
-
- .. sourcecode:: kotlin
-
- // Generate a trade lifecycle with various parameters.
- private fun trade(redemptionTime: Instant = TEST_TX_TIME + 8.days,
- aliceGetsBack: Amount = 1000.DOLLARS,
- destroyPaperAtRedemption: Boolean = true): TransactionGroupForTest {
- val someProfits = 1200.DOLLARS
- return transactionGroup {
- roots {
- transaction(900.DOLLARS.CASH owned_by ALICE label "alice's $900")
- transaction(someProfits.CASH owned_by MEGA_CORP_KEY label "some profits")
- }
-
- // Some CP is issued onto the ledger by MegaCorp.
- transaction {
- output("paper") { PAPER_1 }
- arg(MEGA_CORP_KEY) { CommercialPaper.Commands.Issue() }
- }
-
- // The CP is sold to alice for her $900, $100 less than the face value. At 10% interest after only 7 days,
- // that sounds a bit too good to be true!
- transaction {
- input("paper")
- input("alice's $900")
- output { 900.DOLLARS.CASH owned_by MEGA_CORP_KEY }
- output("alice's paper") { PAPER_1 owned_by ALICE }
- arg(ALICE) { Cash.Commands.Move }
- arg(MEGA_CORP_KEY) { CommercialPaper.Commands.Move }
- }
-
- // Time passes, and Alice redeem's her CP for $1000, netting a $100 profit. MegaCorp has received $1200
- // as a single payment from somewhere and uses it to pay Alice off, keeping the remaining $200 as change.
- transaction(time = redemptionTime) {
- input("alice's paper")
- input("some profits")
-
- output { aliceGetsBack.CASH owned_by ALICE }
- output { (someProfits - aliceGetsBack).CASH owned_by MEGA_CORP_KEY }
- if (!destroyPaperAtRedemption)
- output { PAPER_1 owned_by ALICE }
-
- arg(MEGA_CORP_KEY) { Cash.Commands.Move }
- arg(ALICE) { CommercialPaper.Commands.Redeem }
- }
- }
- }
-
-In this example we see some new features of the DSL:
-
-* The ``roots`` construct. Sometimes you don't want to write transactions that laboriously issue everything you need
- in a formally correct way. Inside ``roots`` you can create a bunch of states without any contract checking what you're
- doing. As states may not exist outside of transactions, each line inside defines a fake/invalid transaction with the
- given output states, which may be *labelled* with a short string. Those labels can be used later to join transactions
- together.
-* The ``.CASH`` suffix. This is a part of the unit test DSL specific to the cash contract. It takes a monetary amount
- like 1000.DOLLARS and then wraps it in a cash ledger state, with some fake data.
-* The owned_by `infix function `_. This is just
- a normal function that we're allowed to write in a slightly different way, which returns a copy of the cash state
- with the owner field altered to be the given public key. ``ALICE`` is a constant defined by the test utilities that
- is, like ``DUMMY_PUBKEY_1``, just an arbitrary keypair.
-* We are now defining several transactions that chain together. We can optionally label any output we create. Obviously
- then, the ``input`` method requires us to give the label of some other output that it connects to.
-* The ``transaction`` function can also be given a time, to override the default timestamp on a transaction.
-
-The ``trade`` function is not itself a unit test. Instead it builds up a trade/transaction group, with some slight
-differences depending on the parameters provided (Kotlin allows parameters to have default values). Then it returns
-it, unexecuted.
-
-We use it like this:
-
-.. container:: codeset
-
- .. sourcecode:: kotlin
-
- @Test
- fun ok() {
- trade().verify()
- }
-
- @Test
- fun not_matured_at_redemption() {
- trade(redemptionTime = TEST_TX_TIME + 2.days).expectFailureOfTx(3, "must have matured")
- }
-
-That's pretty simple: we just call ``verify`` in order to check all the transactions in the group. If any are invalid,
-an exception will be thrown indicating which transaction failed and why. In the second case, we call ``expectFailureOfTx``
-again to ensure the third transaction fails with a message that contains "must have matured" (it doesn't have to be
-the exact message).
+Testing contracts with this domain specific language is covered in the separate tutorial, :doc:`tutorial-test-dsl`.
Adding a generation API to your contract
@@ -719,16 +581,17 @@ a method to wrap up the issuance process:
.. sourcecode:: kotlin
- fun generateIssue(issuance: InstitutionReference, faceValue: Amount, maturityDate: Instant): TransactionBuilder {
+ fun generateIssue(issuance: PartyAndReference, faceValue: Amount>, maturityDate: Instant,
+ notary: Party): TransactionBuilder {
val state = State(issuance, issuance.party.owningKey, faceValue, maturityDate)
- return TransactionBuilder(state, WireCommand(Commands.Issue, issuance.party.owningKey))
+ return TransactionBuilder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey))
}
We take a reference that points to the issuing party (i.e. the caller) and which can contain any internal
-bookkeeping/reference numbers that we may require. Then the face value of the paper, and the maturity date. It
-returns a ``TransactionBuilder``. A ``TransactionBuilder`` is one of the few mutable classes the platform provides.
-It allows you to add inputs, outputs and commands to it and is designed to be passed around, potentially between
-multiple contracts.
+bookkeeping/reference numbers that we may require. The reference field is an ideal place to put (for example) a
+join key. Then the face value of the paper, and the maturity date. It returns a ``TransactionBuilder``.
+A ``TransactionBuilder`` is one of the few mutable classes the platform provides. It allows you to add inputs,
+outputs and commands to it and is designed to be passed around, potentially between multiple contracts.
.. note:: Generation methods should ideally be written to compose with each other, that is, they should take a
``TransactionBuilder`` as an argument instead of returning one, unless you are sure it doesn't make sense to
@@ -737,17 +600,22 @@ multiple contracts.
an issuer should issue the CP (starting out owned by themselves), and then sell it in a separate transaction.
The function we define creates a ``CommercialPaper.State`` object that mostly just uses the arguments we were given,
-but it fills out the owner field of the state to be the same public key as the issuing party. If the caller wants
-to issue CP onto the ledger that's immediately owned by someone else, they'll have to create the state themselves.
+but it fills out the owner field of the state to be the same public key as the issuing party.
-The returned partial transaction has a ``WireCommand`` object as a parameter. This is a container for any object
-that implements the ``Command`` interface, along with a key that is expected to sign this transaction. In this case,
+The returned partial transaction has a ``Command`` object as a parameter. This is a container for any object
+that implements the ``CommandData`` interface, along with a list of keys that are expected to sign this transaction. In this case,
issuance requires that the issuing party sign, so we put the key of the party there.
-The ``TransactionBuilder`` constructor we used above takes a variable argument list for convenience. You can pass in
-any ``ContractStateRef`` (input), ``ContractState`` (output) or ``Command`` objects and it'll build up the transaction
+The ``TransactionBuilder`` has a convenience ``withItems`` method that takes a variable argument list. You can pass in
+any ``StateAndRef`` (input), ``ContractState`` (output) or ``Command`` objects and it'll build up the transaction
for you.
+There's one final thing to be aware of: we ask the caller to select a *notary* that controls this state and
+prevents it from being double spent. You can learn more about this topic in the :doc:`consensus` article.
+
+.. note:: For now, don't worry about how to pick a notary. More infrastructure will come later to automate this
+ decision for you.
+
What about moving the paper, i.e. reassigning ownership to someone else?
.. container:: codeset
@@ -755,9 +623,9 @@ What about moving the paper, i.e. reassigning ownership to someone else?
.. sourcecode:: kotlin
fun generateMove(tx: TransactionBuilder, paper: StateAndRef, newOwner: PublicKey) {
- tx.addInputState(paper.ref)
- tx.addOutputState(paper.state.copy(owner = newOwner))
- tx.addArg(WireCommand(Commands.Move, paper.state.owner))
+ tx.addInputState(paper)
+ tx.addOutputState(paper.state.data.withOwner(newOwner))
+ tx.addCommand(Command(Commands.Move(), paper.state.data.owner))
}
Here, the method takes a pre-existing ``TransactionBuilder`` and adds to it. This is correct because typically
@@ -769,6 +637,10 @@ The paper is given to us as a ``StateAndRef`` object. Thi
a small object that has a (copy of) a state object, and also the (txhash, index) that indicates the location of this
state on the ledger.
+We add the existing paper state as an input, the same paper state with the owner field adjusted as an output,
+and finally a move command that has the old owner's public key: this is what forces the current owner's signature
+to be present on the transaction, and is what's checked by the contract.
+
Finally, we can do redemption.
.. container:: codeset
@@ -776,77 +648,82 @@ Finally, we can do redemption.
.. sourcecode:: kotlin
@Throws(InsufficientBalanceException::class)
- fun generateRedeem(tx: TransactionBuilder, paper: StateAndRef, wallet: List>) {
+ fun generateRedeem(tx: TransactionBuilder, paper: StateAndRef, wallet: Wallet) {
// Add the cash movement using the states in our wallet.
- Cash().generateSpend(tx, paper.state.faceValue, paper.state.owner, wallet)
- tx.addInputState(paper.ref)
- tx.addArg(WireCommand(CommercialPaper.Commands.Redeem, paper.state.owner))
+ Cash().generateSpend(tx, paper.state.data.faceValue.withoutIssuer(), paper.state.data.owner, wallet.statesOfType())
+ tx.addInputState(paper)
+ tx.addCommand(Command(Commands.Redeem(), paper.state.data.owner))
}
Here we can see an example of composing contracts together. When an owner wishes to redeem the commercial paper, the
issuer (i.e. the caller) must gather cash from its wallet and send the face value to the owner of the paper.
-.. note:: **Exercise for the reader**: In this early, simplified model of CP there is no built in support
- for rollover. Extend the contract code to support rollover as well as redemption (reissuance of the paper with a
- higher face value without any transfer of cash)
+.. note:: This contract has no explicit concept of rollover.
-The *wallet* is a concept that may be familiar from Bitcoin and Ethereum. It is simply a set of cash states that are
+The *wallet* is a concept that may be familiar from Bitcoin and Ethereum. It is simply a set of states (such as cash) that are
owned by the caller. Here, we use the wallet to update the partial transaction we are handed with a movement of cash
from the issuer of the commercial paper to the current owner. If we don't have enough quantity of cash in our wallet,
-an exception is thrown. And then we add the paper itself as an input, but, not an output (as we wish to delete it
-from the ledger permanently). Finally, we add a Redeem command that should be signed by the owner of the commercial
-paper.
+an exception is thrown. Then we add the paper itself as an input, but, not an output (as we wish to remove it
+from the ledger). Finally, we add a Redeem command that should be signed by the owner of the commercial paper.
+
+.. warning:: The amount we pass to the ``generateSpend`` function has to be treated first with ``withoutIssuer``.
+ This reflects the fact that the way we handle issuer constraints is still evolving; the commercial paper
+ contract requires payment in the form of a currency issued by a specific party (e.g. the central bank,
+ or the issuers own bank perhaps). But the wallet wants to assemble spend transactions using cash states from
+ any issuer, thus we must strip it here. This represents a design mismatch that we will resolve in future
+ versions with a more complete way to express issuer constraints.
A ``TransactionBuilder`` is not by itself ready to be used anywhere, so first, we must convert it to something that
is recognised by the network. The most important next step is for the participating entities to sign it using the
``signWith()`` method. This takes a keypair, serialises the transaction, signs the serialised form and then stores the
signature inside the ``TransactionBuilder``. Once all parties have signed, you can call ``TransactionBuilder.toSignedTransaction()``
-to get a ``SignedTransaction`` object. This is an immutable form of the transaction that's ready for *timestamping*,
-which can be done using a ``TimestamperClient``. To learn more about that, please refer to the
-:doc:`protocol-state-machines` document.
+to get a ``SignedTransaction`` object.
You can see how transactions flow through the different stages of construction by examining the commercial paper
unit tests.
-Non-asset-oriented based smart contracts
-----------------------------------------
+How multi-party transactions are constructed and transmitted
+------------------------------------------------------------
-It is important to distinguish between the idea of a legal contract vs a code contract. In this document we use the
-term *contract* as a shorthand for code contract: a small module of widely shared, simultaneously executed business
-logic that uses standardised APIs and runs in a sandbox.
+OK, so now we know how to define the rules of the ledger, and we know how to construct transactions that satisfy
+those rules ... and if all we were doing was maintaining our own data that might be enough. But we aren't: Corda
+is about keeping many different parties all in sync with each other.
+
+In a classical blockchain system all data is transmitted to everyone and if you want to do something fancy, like
+a multi-party transaction, you're on your own. In Corda data is transmitted only to parties that need it and
+multi-party transactions are a way of life, so we provide lots of support for managing them.
+
+You can learn how transactions are moved between peers and taken through the build-sign-notarise-broadcast
+process in a separate tutorial, :doc:`protocol-state-machines`.
+
+Non-asset-oriented smart contracts
+----------------------------------
Although this tutorial covers how to implement an owned asset, there is no requirement that states and code contracts
*must* be concerned with ownership of an asset. It is better to think of states as representing useful facts about the
-world, and (code) contracts as imposing logical relations on how facts combine to produce new facts.
+world, and (code) contracts as imposing logical relations on how facts combine to produce new facts. Alternatively
+you can imagine that states are like rows in a relational database and contracts are like stored procedures and
+relational constraints.
-For example, in the case that the transfer of an asset cannot be performed entirely on-ledger, one possible usage of
-the model is to implement a delivery-vs-payment lifecycle in which there is a state representing an intention to trade
-and two other states that can be interpreted by off-ledger platforms as firm instructions to move the respective asset
-or cash - and a final state in which the exchange is marked as complete. The key point here is that the two off-platform
-instructions form pa rt of the same Transaction and so either both are signed (and can be processed by the off-ledger
-systems) or neither are.
+When writing a contract that handles deal-like entities rather than asset-like entities, you may wish to refer
+to ":doc:`contract-irs`" and the accompanying source code. Whilst all the concepts are the same, deals are
+typically not splittable or mergeable and thus you don't have to worry much about grouping of states.
-As another example, consider multi-signature transactions, a feature which is commonly used in Bitcoin to implement
-various kinds of useful protocols. This technique allows you to lock an asset to ownership of a group, in which a
-threshold of signers (e.g. 3 out of 4) must all sign simultaneously to enable the asset to move. It is initially
-tempting to simply add this as another feature to each existing contract which someone might want to treat in this way.
-But that could lead to unnecessary duplication of work.
+Making things happen at a particular time
+-----------------------------------------
-A better approach is to model the fact of joint ownership as a new contract with its own state. In this approach, to
-lock up your commercial paper under multi-signature ownership you would make a transaction that looks like this:
-
-* **Input**: the CP state
-* **Output**: a multi-sig state that contains the list of keys and the signing threshold desired (e.g. 3 of 4). The state has a hash of H.
-* **Output**: the same CP state, with a marker that says a state with hash H must exist in any transaction that spends it.
-
-The CP contract then needs to be extended only to verify that a state with the required hash is present as an input.
-The logic that implements measurement of the threshold, different signing combinations that may be allowed etc can then
-be implemented once in a separate contract, with the controlling data being held in the named state.
-
-Future versions of the prototype will explore these concepts in more depth.
+It would be nice if you could program your node to automatically redeem your commercial paper as soon as it matures.
+Corda provides a way for states to advertise scheduled events that should occur in future. Whilst this information
+is by default ignored, if the corresponding *Cordapp* is installed and active in your node, and if the state is
+considered relevant by your wallet (e.g. because you own it), then the node can automatically begin the process
+of creating a transaction and taking it through the life cycle. You can learn more about this in the article
+":doc:`event-scheduling`".
Clauses
-------
-Instead of structuring contracts as a single entity, they can be broken down into reusable chunks known as clauses.
-This idea is addressed in the next tutorial, ":doc:`tutorial-contract-clauses`".
\ No newline at end of file
+It is typical for slightly different contracts to have lots of common logic that can be shared. For example, the
+concept of being issued, being exited and being upgraded are all usually required in any contract. Corda calls these
+frequently needed chunks of logic "clauses", and they can simplify development considerably.
+
+Clauses and how to use them are addressed in the next tutorial, ":doc:`tutorial-contract-clauses`".
\ No newline at end of file
diff --git a/docs/build/html/_static/corda-introductory-whitepaper.pdf b/docs/build/html/_static/corda-introductory-whitepaper.pdf
index 7bbf2a1a08..a43f57e18b 100644
Binary files a/docs/build/html/_static/corda-introductory-whitepaper.pdf and b/docs/build/html/_static/corda-introductory-whitepaper.pdf differ
diff --git a/docs/build/html/api/alltypes/index.html b/docs/build/html/api/alltypes/index.html
index 17f1dd3c5b..b6864614ea 100644
--- a/docs/build/html/api/alltypes/index.html
+++ b/docs/build/html/api/alltypes/index.html
@@ -117,6 +117,13 @@ for ensuring code runs on the right thread, and also for unit testing.
+com.r3corda.core.contracts.clauses.AllComposition |
+
+ Compose a number of clauses, such that all of the clauses must run for verification to pass.
+ |
+
+
+
com.r3corda.core.messaging.AllPossibleRecipients |
A special base class for the set of all possible recipients, without having to identify who they all are.
@@ -139,6 +146,13 @@ amount used in whatever underlying thing the amount represents.
|
+com.r3corda.core.contracts.clauses.AnyComposition |
+
+ Compose a number of clauses, such that any number of the clauses can run.
+ |
+
+
+
com.r3corda.core.protocols.AppContext |
This is just some way to track what attachments need to be in the class loader, but may later include some app
@@ -240,6 +254,14 @@ Bilateral states are used in close-out netting.
|
+com.r3corda.protocols.BroadcastTransactionProtocol |
+
+ Notify all involved parties about a transaction, including storing a copy. Normally this would be called via
+FinalityProtocol.
+ |
+
+
+
com.r3corda.core.contracts.BusinessCalendar |
A business calendar performs date calculations that take into account national holidays and weekends. This is a
@@ -300,7 +322,6 @@ the same transaction.
|
com.r3corda.core.contracts.clauses.Clause |
- A clause that can be matched as part of execution of a contract.
|
@@ -318,7 +339,7 @@ the same transaction.
-com.r3corda.node.services.monitor.ClientToServiceCommand |
+com.r3corda.core.contracts.ClientToServiceCommand
A command from the monitoring client, to the node.
|
@@ -371,6 +392,8 @@ the same transaction.
com.r3corda.core.contracts.Commodity |
+ Class representing a commodity, as an equivalent to the Currency class. This exists purely to enable the
+CommodityContract contract, and is likely to change in future.
|
@@ -386,6 +409,21 @@ internal accounting by the issuer (it might be, for example, a warehouse and/or
+com.r3corda.core.contracts.clauses.CompositeClause |
+
+ Abstract supertype for clauses which compose other clauses together in some logical manner.
+ |
+
+
+
+com.r3corda.core.contracts.clauses.ConcreteClause |
+
+ Abstract supertype for clauses which provide their own verification logic, rather than delegating to subclauses.
+By default these clauses are always matched (they have no required commands).
+ |
+
+
+
com.r3corda.node.servlets.Config |
Primary purpose is to install Kotlin extensions for Jackson ObjectMapper so data classes work
@@ -727,6 +765,20 @@ attachments are saved to local storage automatically.
|
+com.r3corda.protocols.FinalityProtocol |
+
+ Finalise a transaction by notarising it, then recording it locally, and then sending it to all involved parties.
+ |
+
+
+
+com.r3corda.core.contracts.clauses.FirstComposition |
+
+ Compose a number of clauses, such that the first match is run, and it errors if none is run.
+ |
+
+
+
com.r3corda.core.contracts.Fix |
A Fix represents a named interest rate, on a given day, for a given duration. It can be embedded in a tx.
@@ -812,24 +864,12 @@ container), shares of the same class in a specific company are fungible and coun
|
-com.r3corda.core.contracts.clauses.GroupClause |
-
- |
-
-
-
com.r3corda.core.contracts.clauses.GroupClauseVerifier |
|
-com.r3corda.core.contracts.clauses.GroupVerify |
-
- |
-
-
-
com.r3corda.demos.IRSDemoNode |
|
@@ -950,14 +990,6 @@ states relevant to us into a database and once such a wallet is implemented, thi
-com.r3corda.core.contracts.clauses.InterceptorClause |
-
- A clause which intercepts calls to a wrapped clause, and passes them through verification
-only from a pre-clause. This is similar to an inceptor in aspect orientated programming.
- |
-
-
-
com.r3corda.contracts.InterestRateSwap |
The Interest Rate Swap class. For a quick overview of what an IRS is, see here - http://www.pimco.co.uk/EN/Education/Pages/InterestRateSwapsBasics1-08.aspx (no endorsement).
@@ -1135,12 +1167,6 @@ with the commands from the wire, and verified/looked up.
|
-com.r3corda.core.contracts.clauses.MatchBehaviour |
-
- |
-
-
-
com.r3corda.core.messaging.Message |
A message is defined, at this level, to be a (topic, timestamp, byte arrays) triple, where the topic is a string in
@@ -2024,14 +2050,6 @@ contained within.
|
-com.r3corda.core.contracts.clauses.SingleClause |
-
- A single verifiable clause. By default always matches, continues to the next clause when matched and errors
-if not matched.
- |
-
-
-
com.r3corda.core.messaging.SingleMessageRecipient |
A base class for the case of point-to-point messages
@@ -2039,12 +2057,6 @@ if not matched.
|
-com.r3corda.core.contracts.clauses.SingleVerify |
-
- |
-
-
-
com.r3corda.core.serialization.SingletonSerializationToken |
A class representing a SerializationToken for some object that is not serializable but can be looked up
@@ -2084,7 +2096,7 @@ Points at which polynomial pieces connect are known as knots.
|
com.r3corda.node.services.statemachine.StateMachineManager |
- A StateMachineManager is responsible for coordination and persistence of multiple ProtocolStateMachine objects.
+ A StateMachineManager is responsible for coordination and persistence of multiple ProtocolStateMachine objects.
Each such object represents an instantiation of a (two-party) protocol that has reached a particular point.
|
diff --git a/docs/build/html/api/com.r3corda.contracts.asset/-cash/-clauses/-conserve-amount/index.html b/docs/build/html/api/com.r3corda.contracts.asset/-cash/-clauses/-conserve-amount/index.html
index 245d4d3bcd..c635939f00 100644
--- a/docs/build/html/api/com.r3corda.contracts.asset/-cash/-clauses/-conserve-amount/index.html
+++ b/docs/build/html/api/com.r3corda.contracts.asset/-cash/-clauses/-conserve-amount/index.html
@@ -7,7 +7,7 @@
com.r3corda.contracts.asset / Cash / Clauses / ConserveAmount
ConserveAmount
-class ConserveAmount : AbstractConserveAmount<State, Currency>
+class ConserveAmount : AbstractConserveAmount<State, Commands, Currency>
Constructors
@@ -21,29 +21,14 @@
-Inherited Properties
+Inherited Functions
diff --git a/docs/build/html/api/com.r3corda.contracts.asset/-cash/-clauses/-group/index.html b/docs/build/html/api/com.r3corda.contracts.asset/-cash/-clauses/-group/index.html
index cdc1e69e52..2d44a03d70 100644
--- a/docs/build/html/api/com.r3corda.contracts.asset/-cash/-clauses/-group/index.html
+++ b/docs/build/html/api/com.r3corda.contracts.asset/-cash/-clauses/-group/index.html
@@ -7,7 +7,7 @@
com.r3corda.contracts.asset / Cash / Clauses / Group
Group
-class Group : GroupClauseVerifier<State, Issued<Currency>>
+class Group : GroupClauseVerifier<State, Commands, Issued<Currency>>
Constructors
@@ -21,40 +21,14 @@
-Properties
-
Inherited Properties
@@ -69,19 +43,5 @@
-Inherited Functions
-
-
-
-
-verify |
-
-open fun verify(tx: TransactionForContract, commands: Collection<AuthenticatedObject<CommandData>>): Set<CommandData> Verify the transaction matches the conditions from this clause. For example, a "no zero amount output" clause
-would check each of the output states that it applies to, looking for a zero amount, and throw IllegalStateException
-if any matched.
- |
-
-
-