Add default values for ifMatched/ifNotMatched/requiredCommands

This commit is contained in:
Ross Nicoll 2016-08-19 16:26:28 +01:00
parent 668fecfea7
commit 03e120d04b
11 changed files with 39 additions and 58 deletions

View File

@ -517,11 +517,7 @@ class InterestRateSwap() : Contract {
override val clauses = listOf(Agree(), Fix(), Pay(), Mature())
}
class Timestamped : SingleClause {
override val ifMatched = MatchBehaviour.CONTINUE
override val ifNotMatched = MatchBehaviour.ERROR
override val requiredCommands = emptySet<Class<out CommandData>>()
class Timestamped : SingleClause() {
override fun verify(tx: TransactionForContract, commands: Collection<AuthenticatedObject<CommandData>>): Set<CommandData> {
require(tx.timestamp?.midpoint != null) { "must be timestamped" }
// We return an empty set because we don't process any commands

View File

@ -197,11 +197,7 @@ class Obligation<P> : Contract {
* any lifecycle change clause, which is the only clause that involve
* non-standard lifecycle states on input/output.
*/
class VerifyLifecycle<P> : SingleClause, GroupClause<State<P>, Issued<Terms<P>>> {
override val requiredCommands: Set<Class<out CommandData>> = emptySet()
override val ifMatched: MatchBehaviour = MatchBehaviour.CONTINUE
override val ifNotMatched: MatchBehaviour = MatchBehaviour.ERROR
class VerifyLifecycle<P> : SingleClause(), GroupClause<State<P>, Issued<Terms<P>>> {
override fun verify(tx: TransactionForContract, commands: Collection<AuthenticatedObject<CommandData>>): Set<CommandData>
= verify(
tx.inputs.filterIsInstance<State<P>>(),

View File

@ -43,13 +43,10 @@ data class MultilateralNetState<P>(
* Clause for netting contract states. Currently only supports obligation contract.
*/
// TODO: Make this usable for any nettable contract states
open class NetClause<P> : SingleClause {
override val ifNotMatched: MatchBehaviour
get() = MatchBehaviour.CONTINUE
override val ifMatched: MatchBehaviour
get() = MatchBehaviour.END
override val requiredCommands: Set<Class<out CommandData>>
get() = setOf(Obligation.Commands.Net::class.java)
open class NetClause<P> : SingleClause() {
override val ifMatched: MatchBehaviour = MatchBehaviour.END
override val ifNotMatched: MatchBehaviour = MatchBehaviour.CONTINUE
override val requiredCommands: Set<Class<out CommandData>> = setOf(Obligation.Commands.Net::class.java)
@Suppress("ConvertLambdaToReference")
override fun verify(tx: TransactionForContract, commands: Collection<AuthenticatedObject<CommandData>>): Set<CommandData> {

View File

@ -218,11 +218,7 @@ interface LinearState: ContractState {
/**
* Standard clause to verify the LinearState safety properties.
*/
class ClauseVerifier<S: LinearState>(val stateClass: Class<S>) : SingleClause {
override val ifMatched = MatchBehaviour.CONTINUE
override val ifNotMatched = MatchBehaviour.ERROR
override val requiredCommands = emptySet<Class<out CommandData>>()
class ClauseVerifier<S: LinearState>(val stateClass: Class<S>) : SingleClause() {
override fun verify(tx: TransactionForContract, commands: Collection<AuthenticatedObject<CommandData>>): Set<CommandData> {
val inputs = tx.inputs.filterIsInstance(stateClass)
val inputIds = inputs.map { it.linearId }.distinct()

View File

@ -41,4 +41,12 @@ interface SingleVerify {
}
interface SingleClause : Clause, SingleVerify
/**
* A single verifiable clause. By default always matches, continues to the next clause when matched and errors
* if not matched.
*/
abstract class SingleClause : Clause, SingleVerify {
override val ifMatched: MatchBehaviour = MatchBehaviour.CONTINUE
override val ifNotMatched: MatchBehaviour = MatchBehaviour.ERROR
override val requiredCommands: Set<Class<out CommandData>> = emptySet()
}

View File

@ -27,7 +27,7 @@ fun verifyClauses(tx: TransactionForContract,
}
when (matchBehaviour) {
MatchBehaviour.ERROR -> throw IllegalStateException()
MatchBehaviour.ERROR -> throw IllegalStateException("Error due to matching/not matching ${clause}")
MatchBehaviour.CONTINUE -> {
}
MatchBehaviour.END -> break@verify

View File

@ -21,7 +21,7 @@ interface GroupVerify<in S, in T : Any> {
interface GroupClause<in S : ContractState, in T : Any> : Clause, GroupVerify<S, T>
abstract class GroupClauseVerifier<S : ContractState, T : Any> : SingleClause {
abstract class GroupClauseVerifier<S : ContractState, T : Any> : SingleClause() {
abstract val clauses: List<GroupClause<S, T>>
override val requiredCommands: Set<Class<out CommandData>>
get() = emptySet()

View File

@ -9,10 +9,10 @@ import java.util.*
* 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.
*/
data class InterceptorClause(
class InterceptorClause(
val preclause: SingleVerify,
val clause: SingleClause
) : SingleClause {
) : SingleClause() {
override val ifNotMatched: MatchBehaviour
get() = clause.ifNotMatched
override val ifMatched: MatchBehaviour
@ -25,4 +25,6 @@ data class InterceptorClause(
consumed.addAll(clause.verify(tx, commands))
return consumed
}
override fun toString(): String = "Interceptor clause [${clause}]"
}

View File

@ -19,11 +19,7 @@ class VerifyClausesTests {
/** Very simple check that the function doesn't error when given any clause */
@Test
fun minimal() {
val clause = object : SingleClause {
override val requiredCommands: Set<Class<out CommandData>>
get() = emptySet()
override val ifMatched: MatchBehaviour
get() = MatchBehaviour.CONTINUE
val clause = object : SingleClause() {
override val ifNotMatched: MatchBehaviour
get() = MatchBehaviour.CONTINUE
@ -36,14 +32,7 @@ class VerifyClausesTests {
/** Check that when there are no required commands, a clause always matches */
@Test
fun emptyAlwaysMatches() {
val clause = object : SingleClause {
override val requiredCommands: Set<Class<out CommandData>>
get() = emptySet()
override val ifMatched: MatchBehaviour
get() = MatchBehaviour.CONTINUE
override val ifNotMatched: MatchBehaviour
get() = MatchBehaviour.ERROR
val clause = object : SingleClause() {
override fun verify(tx: TransactionForContract, commands: Collection<AuthenticatedObject<CommandData>>): Set<CommandData> = emptySet()
}
val tx = TransactionForContract(emptyList(), emptyList(), emptyList(), emptyList(), SecureHash.randomSHA256())
@ -53,9 +42,7 @@ class VerifyClausesTests {
@Test
fun errorSuperfluousCommands() {
val clause = object : SingleClause {
override val requiredCommands: Set<Class<out CommandData>>
get() = emptySet()
val clause = object : SingleClause() {
override val ifMatched: MatchBehaviour
get() = MatchBehaviour.ERROR
override val ifNotMatched: MatchBehaviour
@ -73,7 +60,7 @@ class VerifyClausesTests {
/** Check triggering of error if matched */
@Test
fun errorMatched() {
val clause = object : SingleClause {
val clause = object : SingleClause() {
override val requiredCommands: Set<Class<out CommandData>>
get() = setOf(DummyContract.Commands.Create::class.java)
override val ifMatched: MatchBehaviour
@ -98,13 +85,9 @@ class VerifyClausesTests {
/** Check triggering of error if unmatched */
@Test
fun errorUnmatched() {
val clause = object : SingleClause {
val clause = object : SingleClause() {
override val requiredCommands: Set<Class<out CommandData>>
get() = setOf(DummyContract.Commands.Create::class.java)
override val ifMatched: MatchBehaviour
get() = MatchBehaviour.CONTINUE
override val ifNotMatched: MatchBehaviour
get() = MatchBehaviour.ERROR
override fun verify(tx: TransactionForContract, commands: Collection<AuthenticatedObject<CommandData>>): Set<CommandData> = emptySet()
}

View File

@ -54,6 +54,7 @@ API changes:
* The ``arg`` method in the test DSL is now called ``command`` to be consistent with the rest of the data model.
* The messaging APIs have changed somewhat to now use a new ``TopicSession`` object. These APIs will continue to change
in the upcoming releases.
* Clauses now have default values provided for ``ifMatched``, ``ifNotMatched`` and ``requiredCommands``.
New documentation:

View File

@ -16,13 +16,13 @@ generally the same for all fungible contracts, so a single issuance clause can b
error, and improves consistency of behaviour.
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 "Grouping" outermost clause, which will contain the "Issue", "Move" and
"Redeem" clauses. The result is a contract that looks something like this:
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:
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.
b. If a Move command is present, run appropriate tests and end processing this group.
c. If a Redeem command is present, run appropriate tests and end processing this group.
a. If an ``Issue`` command is present, run appropriate tests and end processing this group.
b. If a ``Move`` command is present, run appropriate tests and end processing this group.
c. If a ``Redeem`` command is present, run appropriate tests and end processing this group.
Commercial paper class
----------------------
@ -71,9 +71,9 @@ 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 for key information on 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.
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.
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.
@ -157,7 +157,9 @@ The post-processing ``MatchBehaviour`` options are:
* 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.
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
------------