2016-07-12 09:24:11 +00:00
|
|
|
.. highlight:: kotlin
|
|
|
|
.. raw:: html
|
|
|
|
|
|
|
|
<script type="text/javascript" src="_static/jquery.js"></script>
|
|
|
|
<script type="text/javascript" src="_static/codesets.js"></script>
|
|
|
|
|
|
|
|
Writing a contract using clauses
|
|
|
|
================================
|
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
In this tutorial, we will restructure the commercial paper contract to use clauses. You should have
|
2016-07-12 09:24:11 +00:00
|
|
|
already completed ":doc:`tutorial-contract`".
|
2016-11-29 11:53:54 +00:00
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
As before, this example is focused on a basic implementation of commercial paper (CP), which is essentially a simpler version of a corporate
|
|
|
|
bond. A company issues commercial paper with a particular face value, say $100, but sells it for less, say $90. The paper can be redeemed
|
|
|
|
for cash at a given future date. In our example, the commercial paper has a 10% interest rate, with a single repayment.
|
|
|
|
The full Kotlin code can be found in ``CommercialPaper.kt``.
|
|
|
|
|
|
|
|
What are clauses and why use them?
|
|
|
|
----------------------------------
|
2016-07-12 09:24:11 +00:00
|
|
|
|
2016-08-10 16:51:13 +00:00
|
|
|
Clauses are essentially micro-contracts which contain independent verification logic, and can be logically composed
|
2017-01-09 16:09:32 +00:00
|
|
|
to form a complete contract. Clauses are designed to enable re-use of common verification parts. For example, issuing state objects
|
|
|
|
is generally the same for all fungible contracts, so a common issuance clause can be used for each contract's
|
2016-08-10 16:51:13 +00:00
|
|
|
issue clause. This cuts down on scope for error, and improves consistency of behaviour. By splitting verification logic
|
2017-01-09 16:09:32 +00:00
|
|
|
into smaller chunks, these can also be readily tested in isolation.
|
|
|
|
|
|
|
|
How do clauses work?
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
There are different types of clauses. The most basic are those that define the verification logic for a single command
|
|
|
|
(e.g. ``Move``, ``Issue`` and ``Redeem``, in the case of commercial paper), or even run without any commands at all (e.g. ``Timestamp``).
|
|
|
|
|
|
|
|
These basic clauses can then be combined using a ``CompositeClause``. The goal of composite clauses is to determine
|
|
|
|
which individual clauses need to be matched and verified for a given transaction
|
|
|
|
to be considered valid. We refer to a clause as being "matched" when the transaction has the required commands present for the clause
|
|
|
|
in question to trigger. Meanwhile, we talk about a clause "verifying" when its ``verify()`` function returns ``True``.
|
|
|
|
|
|
|
|
As an example, let's say we want a transaction to be valid only when every single one of its clauses matches and verifies. We implement this
|
2017-01-13 14:10:54 +00:00
|
|
|
by wrapping the individual clauses into an ``AllOf`` composite clause, which ensures that a transaction is
|
2017-01-09 16:09:32 +00:00
|
|
|
only considered valid if all of its clauses are both matched and verify.
|
2016-07-12 09:24:11 +00:00
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
There are two other basic composite clauses that you should be aware of:
|
2016-11-29 11:53:54 +00:00
|
|
|
|
2017-01-13 14:10:54 +00:00
|
|
|
* ``AnyOf``, whereby 1 or more clauses may match, and every matched clause must verify
|
|
|
|
* ``FirstOf``, whereby at least one clause must match, and the first such clause must verify
|
2016-11-29 11:53:54 +00:00
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
In turn, composite clauses are themselves ``Clause`` s, and can, for example, be wrapped in the special ``GroupClauseVerifier`` grouping clause.
|
|
|
|
For ``CommercialPaper``, this would look as follows:
|
2016-08-10 16:51:13 +00:00
|
|
|
|
2016-11-29 11:53:54 +00:00
|
|
|
.. image:: resources/commPaperClauses.png
|
2016-07-12 09:24:11 +00:00
|
|
|
|
2017-01-13 14:10:54 +00:00
|
|
|
For this tutorial, we will be using ``GroupClauseVerifier`` and ``AnyOf``. Since it's important to understand how these work,
|
2017-01-09 16:09:32 +00:00
|
|
|
charts showing their execution and other details can be found in :doc:`clauses`.
|
2016-11-29 11:53:54 +00:00
|
|
|
|
|
|
|
.. _verify_ref:
|
2016-07-12 09:24:11 +00:00
|
|
|
|
|
|
|
Commercial paper class
|
|
|
|
----------------------
|
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
We start by defining the ``CommercialPaper`` class. As in the previous tutorial, we need some elementary parts: a ``Commands`` interface,
|
|
|
|
``generateMove``, ``generateIssue``, ``generateRedeem``. So far, so good - these stay the same. The new part is verification and the
|
|
|
|
``Clauses`` interface (which we will see later in code). Let's start from the basic structure:
|
2016-07-12 09:24:11 +00:00
|
|
|
|
|
|
|
.. container:: codeset
|
|
|
|
|
|
|
|
.. sourcecode:: kotlin
|
|
|
|
|
2016-11-29 11:53:54 +00:00
|
|
|
class CommercialPaper : Contract {
|
2017-07-19 12:06:43 +00:00
|
|
|
override fun verify(tx: LedgerTransaction) = verifyClause(tx, Clauses.Group(), tx.commands.select<Commands>())
|
2016-07-12 09:24:11 +00:00
|
|
|
|
2016-11-29 11:53:54 +00:00
|
|
|
interface Commands : CommandData {
|
|
|
|
data class Move(override val contractHash: SecureHash? = null) : FungibleAsset.Commands.Move, Commands
|
|
|
|
class Redeem : TypeOnlyCommandData(), Commands
|
|
|
|
data class Issue(override val nonce: Long = random63BitValue()) : IssueCommand, Commands
|
|
|
|
}
|
2016-08-04 14:23:58 +00:00
|
|
|
|
2016-07-12 09:24:11 +00:00
|
|
|
.. sourcecode:: java
|
|
|
|
|
|
|
|
public class CommercialPaper implements Contract {
|
2016-08-04 14:23:58 +00:00
|
|
|
@Override
|
2017-07-19 12:06:43 +00:00
|
|
|
public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
|
2016-08-10 16:51:13 +00:00
|
|
|
ClauseVerifier.verifyClause(tx, new Clauses.Group(), extractCommands(tx));
|
2016-08-04 14:23:58 +00:00
|
|
|
}
|
|
|
|
|
2016-11-29 11:53:54 +00:00
|
|
|
public interface Commands extends CommandData {
|
|
|
|
class Move implements Commands {
|
|
|
|
@Override
|
|
|
|
public boolean equals(Object obj) { return obj instanceof Move; }
|
|
|
|
}
|
2016-07-12 09:24:11 +00:00
|
|
|
|
2016-11-29 11:53:54 +00:00
|
|
|
class Redeem implements Commands {
|
|
|
|
@Override
|
|
|
|
public boolean equals(Object obj) { return obj instanceof Redeem; }
|
|
|
|
}
|
2016-07-12 09:24:11 +00:00
|
|
|
|
2016-11-29 11:53:54 +00:00
|
|
|
class Issue implements Commands {
|
|
|
|
@Override
|
|
|
|
public boolean equals(Object obj) { return obj instanceof Issue; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
As you can see, we used ``verifyClause`` function with ``Clauses.Group()`` in place of our previous verification logic.
|
2016-11-29 11:53:54 +00:00
|
|
|
It's an entry point to running clause logic. ``verifyClause`` takes the transaction, a clause (usually a composite one)
|
2017-01-09 16:09:32 +00:00
|
|
|
to verify, and all of the commands the clause is expected to handle. This list of commands is important because
|
|
|
|
``verifyClause`` checks that none of the commands are left unprocessed at the end, raising an error if they are.
|
2016-07-12 09:24:11 +00:00
|
|
|
|
2016-11-29 11:53:54 +00:00
|
|
|
Simple Clauses
|
|
|
|
--------------
|
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
Let's move to constructing contract logic in terms of clauses. The commercial paper contract has three commands and
|
2016-11-29 11:53:54 +00:00
|
|
|
three corresponding behaviours: ``Issue``, ``Move`` and ``Redeem``. Each of them has a specific set of requirements that must be satisfied -
|
2017-01-09 16:09:32 +00:00
|
|
|
perfect material for defining clauses. For brevity, we will only show the ``Move`` clause. The rest is constructed in similar manner,
|
|
|
|
and is included in the ``CommercialPaper.kt`` code.
|
2016-07-12 09:24:11 +00:00
|
|
|
|
|
|
|
.. container:: codeset
|
|
|
|
|
|
|
|
.. sourcecode:: kotlin
|
|
|
|
|
2016-11-29 11:53:54 +00:00
|
|
|
interface Clauses {
|
|
|
|
class Move: Clause<State, Commands, Issued<Terms>>() {
|
|
|
|
override val requiredCommands: Set<Class<out CommandData>>
|
|
|
|
get() = setOf(Commands.Move::class.java)
|
2016-07-12 09:24:11 +00:00
|
|
|
|
2017-07-19 12:06:43 +00:00
|
|
|
override fun verify(tx: LedgerTransaction,
|
2016-07-12 09:24:11 +00:00
|
|
|
inputs: List<State>,
|
|
|
|
outputs: List<State>,
|
2017-09-01 13:10:35 +00:00
|
|
|
commands: List<CommandWithParties<Commands>>,
|
2016-08-10 16:51:13 +00:00
|
|
|
groupingKey: Issued<Terms>?): Set<Commands> {
|
2016-11-29 11:53:54 +00:00
|
|
|
val command = commands.requireSingleCommand<Commands.Move>()
|
|
|
|
val input = inputs.single()
|
|
|
|
requireThat {
|
2017-06-08 10:53:45 +00:00
|
|
|
"the transaction is signed by the owner of the CP" using (input.owner.owningKey in command.signers)
|
2017-04-05 07:32:54 +00:00
|
|
|
"the state is propagated" using (outputs.size == 1)
|
2016-11-29 11:53:54 +00:00
|
|
|
// 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 setOf(command.value)
|
2016-07-12 09:24:11 +00:00
|
|
|
}
|
|
|
|
}
|
2016-11-29 11:53:54 +00:00
|
|
|
...
|
2016-07-12 09:24:11 +00:00
|
|
|
|
|
|
|
.. sourcecode:: java
|
|
|
|
|
2016-11-29 11:53:54 +00:00
|
|
|
public interface Clauses {
|
|
|
|
class Move extends Clause<State, Commands, State> {
|
|
|
|
@NotNull
|
|
|
|
@Override
|
|
|
|
public Set<Class<? extends CommandData>> getRequiredCommands() {
|
|
|
|
return Collections.singleton(Commands.Move.class);
|
|
|
|
}
|
2016-07-12 09:24:11 +00:00
|
|
|
|
2016-11-29 11:53:54 +00:00
|
|
|
@NotNull
|
|
|
|
@Override
|
2017-07-19 12:06:43 +00:00
|
|
|
public Set<Commands> verify(@NotNull LedgerTransaction tx,
|
2016-11-29 11:53:54 +00:00
|
|
|
@NotNull List<? extends State> inputs,
|
|
|
|
@NotNull List<? extends State> outputs,
|
2017-09-01 13:10:35 +00:00
|
|
|
@NotNull List<? extends CommandWithParties<? extends Commands>> commands,
|
2016-11-29 11:53:54 +00:00
|
|
|
@NotNull State groupingKey) {
|
2017-09-01 13:10:35 +00:00
|
|
|
CommandWithParties<Commands.Move> cmd = requireSingleCommand(tx.getCommands(), Commands.Move.class);
|
2016-11-29 11:53:54 +00:00
|
|
|
// There should be only a single input due to aggregation above
|
|
|
|
State input = single(inputs);
|
|
|
|
|
2017-06-08 10:53:45 +00:00
|
|
|
if (!cmd.getSigners().contains(input.getOwner().getOwningKey()))
|
2016-11-29 11:53:54 +00:00
|
|
|
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());
|
2016-08-10 16:51:13 +00:00
|
|
|
}
|
2016-07-12 09:24:11 +00:00
|
|
|
}
|
2016-11-29 11:53:54 +00:00
|
|
|
...
|
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
We took part of the code for ``Command.Move`` verification from the previous tutorial and put it into the verify function
|
2016-11-29 11:53:54 +00:00
|
|
|
of ``Move`` class. Notice that this class must extend the ``Clause`` abstract class, which defines
|
2017-01-09 16:09:32 +00:00
|
|
|
the ``verify`` function and the ``requiredCommands`` property used to determine the conditions under which a clause
|
|
|
|
is triggered. In the above example, this means that the clause will run its verification logic when ``Commands.Move`` is present in a transaction.
|
2016-11-29 11:53:54 +00:00
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
.. note:: Notice that commands refer to all input and output states in a transaction. For a clause to be executed, the transaction has
|
|
|
|
to include all commands from the ``requiredCommands`` set.
|
2016-11-29 11:53:54 +00:00
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
A few important changes:
|
2016-11-29 11:53:54 +00:00
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
- The ``verify`` function returns the set of commands which it has processed. Normally this set is identical to the
|
|
|
|
``requiredCommands`` used to trigger the clause. However, in some cases, the clause may process further optional commands
|
2016-11-29 11:53:54 +00:00
|
|
|
which it needs to report that it has handled.
|
|
|
|
|
|
|
|
- Verification takes new parameters. Usually inputs and outputs are some subset of the original transaction entries
|
|
|
|
passed to the clause by outer composite or grouping clause. ``groupingKey`` is a key used to group original states.
|
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
As a simple example, imagine the following input states:
|
2016-11-29 11:53:54 +00:00
|
|
|
|
|
|
|
1. 1000 GBP issued by Bank of England
|
|
|
|
2. 500 GBP issued by Bank of England
|
|
|
|
3. 1000 GBP issued by Bank of Scotland
|
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
We will group states by Issuer, meaning that we have inputs 1 and 2 in one group, and input 3 in another group. The grouping keys are
|
2016-11-29 11:53:54 +00:00
|
|
|
'GBP issued by Bank of England' and 'GBP issued by Bank of Scotland'.
|
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
How are the states grouped and passed in this form to the ``Move`` clause? Answering that question leads us to the concept of
|
|
|
|
``GroupClauseVerifier``.
|
2016-07-12 09:24:11 +00:00
|
|
|
|
2016-11-25 14:29:13 +00:00
|
|
|
Group clause
|
2016-07-12 09:24:11 +00:00
|
|
|
------------
|
|
|
|
|
2016-11-29 11:53:54 +00:00
|
|
|
We may have a transaction with similar but unrelated state evolutions which need to be validated independently. It
|
2017-01-09 16:09:32 +00:00
|
|
|
makes sense to check the ``Move`` command on groups of related inputs and outputs (see example above). Thus, we need to collect
|
2016-11-29 11:53:54 +00:00
|
|
|
relevant states together.
|
2017-01-09 16:09:32 +00:00
|
|
|
For this, we extend the standard ``GroupClauseVerifier`` and specify how to group input/output states, as well as the top-level
|
|
|
|
clause to run on each group. In our example, the top level is a composite clause - ``AnyCompostion`` - that delegates verification to
|
|
|
|
its subclauses (wrapped move, issue, redeem). "Any" in this case means that it will take 0 or more clauses that match the transaction commands.
|
2016-07-12 09:24:11 +00:00
|
|
|
|
|
|
|
.. container:: codeset
|
|
|
|
|
|
|
|
.. sourcecode:: kotlin
|
|
|
|
|
2016-08-10 16:51:13 +00:00
|
|
|
class Group : GroupClauseVerifier<State, Commands, Issued<Terms>>(
|
2017-01-13 14:10:54 +00:00
|
|
|
AnyOf(
|
2016-08-10 16:51:13 +00:00
|
|
|
Redeem(),
|
|
|
|
Move(),
|
|
|
|
Issue())) {
|
2017-07-19 12:06:43 +00:00
|
|
|
override fun groupStates(tx: LedgerTransaction): List<LedgerTransaction.InOutGroup<State, Issued<Terms>>>
|
2016-07-12 09:24:11 +00:00
|
|
|
= tx.groupStates<State, Issued<Terms>> { it.token }
|
|
|
|
}
|
|
|
|
|
|
|
|
.. sourcecode:: java
|
|
|
|
|
2016-08-10 16:51:13 +00:00
|
|
|
class Group extends GroupClauseVerifier<State, Commands, State> {
|
|
|
|
public Group() {
|
2017-01-13 14:10:54 +00:00
|
|
|
super(new AnyOf<>(
|
2016-08-10 16:51:13 +00:00
|
|
|
new Clauses.Redeem(),
|
|
|
|
new Clauses.Move(),
|
|
|
|
new Clauses.Issue()
|
|
|
|
));
|
2016-07-12 09:24:11 +00:00
|
|
|
}
|
|
|
|
|
2016-08-10 16:51:13 +00:00
|
|
|
@NotNull
|
2016-07-12 09:24:11 +00:00
|
|
|
@Override
|
2017-07-19 12:06:43 +00:00
|
|
|
public List<InOutGroup<State, State>> groupStates(@NotNull LedgerTransaction tx) {
|
2016-07-12 09:24:11 +00:00
|
|
|
return tx.groupStates(State.class, State::withoutOwner);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-29 11:53:54 +00:00
|
|
|
For the ``CommercialPaper`` contract, ``Group`` is the main clause for the contract, and is passed directly into
|
2017-01-09 16:09:32 +00:00
|
|
|
``verifyClause`` (see the example code at the top of this tutorial). We also used ``groupStates`` function here - it
|
|
|
|
may be worth reminding yourself how it works here: :ref:`state_ref`.
|
2016-07-12 09:24:11 +00:00
|
|
|
|
|
|
|
Summary
|
|
|
|
-------
|
|
|
|
|
2017-01-09 16:09:32 +00:00
|
|
|
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 verification flow: in order to verify ``CommercialPaper``,
|
|
|
|
we first group states, then we check which commands are specified, and finally we run command-specific verification logic accordingly.
|
2016-08-10 16:51:13 +00:00
|
|
|
|
2016-11-29 11:53:54 +00:00
|
|
|
.. image:: resources/commPaperExecution.png
|
|
|
|
|
2016-08-10 16:51:13 +00:00
|
|
|
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.
|