mirror of
https://github.com/corda/corda.git
synced 2025-05-02 16:53:22 +00:00
Details of First/AnyComposition. Expands on clause workings. Typos.
This commit is contained in:
parent
af42bb389b
commit
e55833d147
@ -7,50 +7,59 @@
|
|||||||
Writing a contract using clauses
|
Writing a contract using clauses
|
||||||
================================
|
================================
|
||||||
|
|
||||||
This tutorial will take you through restructuring the commercial paper contract to use clauses. You should have
|
In this tutorial, we will restructure the commercial paper contract to use clauses. You should have
|
||||||
already completed ":doc:`tutorial-contract`".
|
already completed ":doc:`tutorial-contract`".
|
||||||
As before, the example is focused on basic implementation of commercial paper, which is essentially a simpler version of a corporate
|
|
||||||
bond. A company issues CP with a particular face value, say $100, but sells it for less, say $90. The paper can be redeemed
|
|
||||||
for cash at a given date in the future. Thus this example would have a 10% interest rate with a single repayment.
|
|
||||||
Whole Kotlin code can be found in ``CommercialPaper.kt``.
|
|
||||||
|
|
||||||
What are clauses and why to use them?
|
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?
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
Clauses are essentially micro-contracts which contain independent verification logic, and can be logically composed
|
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 verification parts, for example issuing state objects
|
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 inherited for each contract's
|
is generally the same for all fungible contracts, so a common issuance clause can be used for each contract's
|
||||||
issue clause. This cuts down on scope for error, and improves consistency of behaviour. By splitting verification logic
|
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.
|
into smaller chunks, these can also be readily tested in isolation.
|
||||||
|
|
||||||
How clauses work?
|
How do clauses work?
|
||||||
-----------------
|
--------------------
|
||||||
|
|
||||||
We have different types of clauses, the most basic are the ones that define verification logic for particular command set.
|
There are different types of clauses. The most basic are those that define the verification logic for a single command
|
||||||
We will see them later as elementary building blocks that commercial paper consist of - ``Move``, ``Issue`` and ``Redeem``.
|
(e.g. ``Move``, ``Issue`` and ``Redeem``, in the case of commercial paper), or even run without any commands at all (e.g. ``Timestamp``).
|
||||||
As a developer you need to identify reusable parts of your contract and decide how they should be combined. It is where
|
|
||||||
composite clauses become useful. They gather many clause subcomponents and resolve how and which of them should be checked.
|
|
||||||
|
|
||||||
For example, assume that we want to verify a transaction using all constraints defined in separate clauses. We need to
|
These basic clauses can then be combined using a ``CompositeClause``. The goal of composite clauses is to determine
|
||||||
wrap classes that define them into ``AllComposition`` composite clause. It assures that all clauses from that combination
|
which individual clauses need to be matched and verified for a given transaction
|
||||||
match with commands in a transaction - only then verification logic can be executed.
|
to be considered valid. We refer to a clause as being "matched" when the transaction has the required commands present for the clause
|
||||||
It may be a little confusing, but composite clause is also a clause and you can even wrap it in the special grouping clause.
|
in question to trigger. Meanwhile, we talk about a clause "verifying" when its ``verify()`` function returns ``True``.
|
||||||
In ``CommercialPaper`` it looks like that:
|
|
||||||
|
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
|
||||||
|
by wrapping the individual clauses into an ``AllComposition`` composite clause, which ensures that a transaction is
|
||||||
|
only considered valid if all of its clauses are both matched and verify.
|
||||||
|
|
||||||
|
There are two other basic composite clauses that you should be aware of:
|
||||||
|
|
||||||
|
* ``AnyComposition``, whereby any number of clauses (0+) may match, but each matched clause must verify
|
||||||
|
* ``FirstComposition``, whereby at least one clause must match, and the first such clause must verify
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
.. image:: resources/commPaperClauses.png
|
.. image:: resources/commPaperClauses.png
|
||||||
|
|
||||||
The most basic types of composite clauses are ``AllComposition``, ``AnyComposition`` and ``FirstComposition``.
|
For this tutorial, we will be using ``GroupClauseVerifier`` and ``AnyComposition``. Since it's important to understand how these work,
|
||||||
In this tutorial we will use ``GroupClauseVerifier`` and ``AnyComposition``. It's important to understand how they work.
|
charts showing their execution and other details can be found in :doc:`clauses`.
|
||||||
Charts showing execution and more detailed information can be found in :doc:`clauses`.
|
|
||||||
|
|
||||||
.. _verify_ref:
|
.. _verify_ref:
|
||||||
|
|
||||||
Commercial paper class
|
Commercial paper class
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
We start from defining ``CommercialPaper`` class. As in previous tutorial we need some elementary parts: ``Commands`` interface,
|
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 that stays the same. The new part is verification and
|
``generateMove``, ``generateIssue``, ``generateRedeem``. So far, so good - these stay the same. The new part is verification and the
|
||||||
``Clauses`` interface (you will see them later in code). Let's start from the basic structure:
|
``Clauses`` interface (which we will see later in code). Let's start from the basic structure:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -97,18 +106,18 @@ We start from defining ``CommercialPaper`` class. As in previous tutorial we nee
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
As you can see we used ``verifyClause`` function with ``Clauses.Group()`` in place of previous verification.
|
As you can see, we used ``verifyClause`` function with ``Clauses.Group()`` in place of our previous verification logic.
|
||||||
It's an entry point to running clause logic. ``verifyClause`` takes the transaction, a clause (usually a composite one)
|
It's an entry point to running clause logic. ``verifyClause`` takes the transaction, a clause (usually a composite one)
|
||||||
to verify, and a collection of commands the clause is expected to handle all of. This list of commands is important because
|
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, and raises an error if they are.
|
``verifyClause`` checks that none of the commands are left unprocessed at the end, raising an error if they are.
|
||||||
|
|
||||||
Simple Clauses
|
Simple Clauses
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Let's move to constructing contract logic in terms of clauses language. Commercial paper contract has three commands and
|
Let's move to constructing contract logic in terms of clauses. The commercial paper contract has three commands and
|
||||||
three corresponding behaviours: ``Issue``, ``Move`` and ``Redeem``. Each of them has a specific set of requirements that must be satisfied -
|
three corresponding behaviours: ``Issue``, ``Move`` and ``Redeem``. Each of them has a specific set of requirements that must be satisfied -
|
||||||
perfect material for defining clauses. For brevity we will show only ``Move`` clause, rest is constructed in similar manner
|
perfect material for defining clauses. For brevity, we will only show the ``Move`` clause. The rest is constructed in similar manner,
|
||||||
and included in the ``CommercialPaper.kt`` code.
|
and is included in the ``CommercialPaper.kt`` code.
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -172,43 +181,44 @@ and included in the ``CommercialPaper.kt`` code.
|
|||||||
}
|
}
|
||||||
...
|
...
|
||||||
|
|
||||||
We took part of code for ``Command.Move`` verification from previous tutorial and put it into the verify function
|
We took part of the code for ``Command.Move`` verification from the previous tutorial and put it into the verify function
|
||||||
of ``Move`` class. Notice that this class must extend the ``Clause`` abstract class, which defines
|
of ``Move`` class. Notice that this class must extend the ``Clause`` abstract class, which defines
|
||||||
the ``verify`` function, and the ``requiredCommands`` property used to determine the conditions under which a clause
|
the ``verify`` function and the ``requiredCommands`` property used to determine the conditions under which a clause
|
||||||
is triggered. In the above example it means that the clause will run verification when the ``Commands.Move`` is present in a transaction.
|
is triggered. In the above example, this means that the clause will run its verification logic when ``Commands.Move`` is present in a transaction.
|
||||||
|
|
||||||
.. note:: Notice that commands refer to all input and output states in a transaction. For clause to be executed, transaction has
|
.. 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 ``requiredCommands`` set.
|
to include all commands from the ``requiredCommands`` set.
|
||||||
|
|
||||||
Few important changes:
|
A few important changes:
|
||||||
|
|
||||||
- ``verify`` function returns the set of commands which it has processed. Normally this returned set is identical to the
|
- 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
|
``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.
|
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
|
- 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.
|
passed to the clause by outer composite or grouping clause. ``groupingKey`` is a key used to group original states.
|
||||||
|
|
||||||
As a simple example imagine input states:
|
As a simple example, imagine the following input states:
|
||||||
|
|
||||||
1. 1000 GBP issued by Bank of England
|
1. 1000 GBP issued by Bank of England
|
||||||
2. 500 GBP issued by Bank of England
|
2. 500 GBP issued by Bank of England
|
||||||
3. 1000 GBP issued by Bank of Scotland
|
3. 1000 GBP issued by Bank of Scotland
|
||||||
|
|
||||||
We will group states by Issuer so in the first group we have inputs 1 and 2, in second group input number 3. Grouping keys are:
|
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
|
||||||
'GBP issued by Bank of England' and 'GBP issued by Bank of Scotland'.
|
'GBP issued by Bank of England' and 'GBP issued by Bank of Scotland'.
|
||||||
|
|
||||||
How the states can be grouped and passed in that form to the ``Move`` clause? That leads us to the concept of ``GroupClauseVerifier``.
|
How are the states grouped and passed in this form to the ``Move`` clause? Answering that question leads us to the concept of
|
||||||
|
``GroupClauseVerifier``.
|
||||||
|
|
||||||
Group clause
|
Group clause
|
||||||
------------
|
------------
|
||||||
|
|
||||||
We may have a transaction with similar but unrelated state evolutions which need to be validated independently. It
|
We may have a transaction with similar but unrelated state evolutions which need to be validated independently. It
|
||||||
makes sense to check ``Move`` command on groups of related inputs and outputs (see example above). Thus, we need to collect
|
makes sense to check the ``Move`` command on groups of related inputs and outputs (see example above). Thus, we need to collect
|
||||||
relevant states together.
|
relevant states together.
|
||||||
For this we extend the standard ``GroupClauseVerifier`` and specify how to group input/output states, as well as the top-level
|
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 a top-level is a composite clause - ``AnyCompostion`` that delegates verification to
|
clause to run on each group. In our example, the top level is a composite clause - ``AnyCompostion`` - that delegates verification to
|
||||||
it's subclasses (wrapped move, issue, redeem). Any in this case means that it will take 0 or more clauses that match transaction commands.
|
its subclauses (wrapped move, issue, redeem). "Any" in this case means that it will take 0 or more clauses that match the transaction commands.
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -242,16 +252,16 @@ it's subclasses (wrapped move, issue, redeem). Any in this case means that it wi
|
|||||||
}
|
}
|
||||||
|
|
||||||
For the ``CommercialPaper`` contract, ``Group`` is the main clause for the contract, and is passed directly into
|
For the ``CommercialPaper`` contract, ``Group`` is the main clause for the contract, and is passed directly into
|
||||||
``verifyClause`` (see the example code at the top of this tutorial). We used ``groupStates`` function here, it's worth reminding
|
``verifyClause`` (see the example code at the top of this tutorial). We also used ``groupStates`` function here - it
|
||||||
how it works: :ref:`state_ref`.
|
may be worth reminding yourself how it works here: :ref:`state_ref`.
|
||||||
|
|
||||||
Summary
|
Summary
|
||||||
-------
|
-------
|
||||||
|
|
||||||
In summary the top level contract ``CommercialPaper`` specifies a single grouping clause of type
|
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
|
``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``
|
(``Redeem``, ``Move`` and ``Issue``). This reflects the verification flow: in order to verify ``CommercialPaper``,
|
||||||
we first group states, check which commands are specified, and run command-specific verification logic accordingly.
|
we first group states, then we check which commands are specified, and finally we run command-specific verification logic accordingly.
|
||||||
|
|
||||||
.. image:: resources/commPaperExecution.png
|
.. image:: resources/commPaperExecution.png
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user