mirror of
https://github.com/corda/corda.git
synced 2025-04-07 19:34:41 +00:00
Merged in rnicoll-clause-default (pull request #290)
Add default values for ifMatched/ifNotMatched/requiredCommands
This commit is contained in:
commit
88da5ba942
@ -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
|
||||
|
@ -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>>(),
|
||||
|
@ -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> {
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
}
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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}]"
|
||||
}
|
@ -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()
|
||||
}
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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
|
||||
------------
|
||||
|
Loading…
x
Reference in New Issue
Block a user