mirror of
https://github.com/corda/corda.git
synced 2025-06-01 15:10:54 +00:00
Merged in rnicoll-clause-cleanup (pull request #266)
Clean up how clauses are implemented
This commit is contained in:
commit
84c6989b8b
@ -22,7 +22,7 @@ import static kotlin.collections.CollectionsKt.*;
|
|||||||
* This is a Java version of the CommercialPaper contract (chosen because it's simple). This demonstrates how the
|
* This is a Java version of the CommercialPaper contract (chosen because it's simple). This demonstrates how the
|
||||||
* use of Kotlin for implementation of the framework does not impose the same language choice on contract developers.
|
* use of Kotlin for implementation of the framework does not impose the same language choice on contract developers.
|
||||||
*/
|
*/
|
||||||
public class JavaCommercialPaper extends ClauseVerifier {
|
public class JavaCommercialPaper implements Contract {
|
||||||
//public static SecureHash JCP_PROGRAM_ID = SecureHash.sha256("java commercial paper (this should be a bytecode hash)");
|
//public static SecureHash JCP_PROGRAM_ID = SecureHash.sha256("java commercial paper (this should be a bytecode hash)");
|
||||||
private static final Contract JCP_PROGRAM_ID = new JavaCommercialPaper();
|
private static final Contract JCP_PROGRAM_ID = new JavaCommercialPaper();
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ public class JavaCommercialPaper extends ClauseVerifier {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public List<InOutGroup<State, State>> extractGroups(@NotNull TransactionForContract tx) {
|
public List<InOutGroup<State, State>> groupStates(@NotNull TransactionForContract tx) {
|
||||||
return tx.groupStates(State.class, State::withoutOwner);
|
return tx.groupStates(State.class, State::withoutOwner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -316,20 +316,18 @@ public class JavaCommercialPaper extends ClauseVerifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
private Collection<AuthenticatedObject<CommandData>> extractCommands(@NotNull TransactionForContract tx) {
|
||||||
public List<SingleClause> getClauses() {
|
|
||||||
return Collections.singletonList(new Clause.Group());
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Collection<AuthenticatedObject<CommandData>> extractCommands(@NotNull TransactionForContract tx) {
|
|
||||||
return tx.getCommands()
|
return tx.getCommands()
|
||||||
.stream()
|
.stream()
|
||||||
.filter((AuthenticatedObject<CommandData> command) -> command.getValue() instanceof Commands)
|
.filter((AuthenticatedObject<CommandData> command) -> command.getValue() instanceof Commands)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verify(@NotNull TransactionForContract tx) throws IllegalArgumentException {
|
||||||
|
ClauseVerifier.verifyClauses(tx, Collections.singletonList(new Clause.Group()), extractCommands(tx));
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public SecureHash getLegalContractReference() {
|
public SecureHash getLegalContractReference() {
|
||||||
|
@ -40,7 +40,7 @@ import java.util.*
|
|||||||
val CP_PROGRAM_ID = CommercialPaper()
|
val CP_PROGRAM_ID = CommercialPaper()
|
||||||
|
|
||||||
// TODO: Generalise the notion of an owned instrument into a superclass/supercontract. Consider composition vs inheritance.
|
// TODO: Generalise the notion of an owned instrument into a superclass/supercontract. Consider composition vs inheritance.
|
||||||
class CommercialPaper : ClauseVerifier() {
|
class CommercialPaper : Contract {
|
||||||
// TODO: should reference the content of the legal agreement, not its URI
|
// TODO: should reference the content of the legal agreement, not its URI
|
||||||
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper")
|
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper")
|
||||||
|
|
||||||
@ -49,11 +49,11 @@ class CommercialPaper : ClauseVerifier() {
|
|||||||
val maturityDate: Instant
|
val maturityDate: Instant
|
||||||
)
|
)
|
||||||
|
|
||||||
override val clauses = listOf(Clauses.Group())
|
private fun extractCommands(tx: TransactionForContract): List<AuthenticatedObject<CommandData>>
|
||||||
|
|
||||||
override fun extractCommands(tx: TransactionForContract): List<AuthenticatedObject<CommandData>>
|
|
||||||
= tx.commands.select<Commands>()
|
= tx.commands.select<Commands>()
|
||||||
|
|
||||||
|
override fun verify(tx: TransactionForContract) = verifyClauses(tx, listOf(Clauses.Group()), extractCommands(tx))
|
||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
val issuance: PartyAndReference,
|
val issuance: PartyAndReference,
|
||||||
override val owner: PublicKey,
|
override val owner: PublicKey,
|
||||||
@ -88,7 +88,7 @@ class CommercialPaper : ClauseVerifier() {
|
|||||||
Issue()
|
Issue()
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun extractGroups(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, Issued<Terms>>>
|
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, Issued<Terms>>>
|
||||||
= tx.groupStates<State, Issued<Terms>> { it.token }
|
= tx.groupStates<State, Issued<Terms>> { it.token }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ class FloatingRatePaymentEvent(date: LocalDate,
|
|||||||
* Currently, we are not interested (excuse pun) in valuing the swap, calculating the PVs, DFs and all that good stuff (soon though).
|
* Currently, we are not interested (excuse pun) in valuing the swap, calculating the PVs, DFs and all that good stuff (soon though).
|
||||||
* This is just a representation of a vanilla Fixed vs Floating (same currency) IRS in the R3 prototype model.
|
* This is just a representation of a vanilla Fixed vs Floating (same currency) IRS in the R3 prototype model.
|
||||||
*/
|
*/
|
||||||
class InterestRateSwap() : ClauseVerifier() {
|
class InterestRateSwap() : Contract {
|
||||||
override val legalContractReference = SecureHash.sha256("is_this_the_text_of_the_contract ? TBD")
|
override val legalContractReference = SecureHash.sha256("is_this_the_text_of_the_contract ? TBD")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -447,10 +447,11 @@ class InterestRateSwap() : ClauseVerifier() {
|
|||||||
fixingCalendar, index, indexSource, indexTenor)
|
fixingCalendar, index, indexSource, indexTenor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val clauses: List<SingleClause> = listOf(Clause.Timestamped(), Clause.Group())
|
private fun extractCommands(tx: TransactionForContract): Collection<AuthenticatedObject<CommandData>>
|
||||||
override fun extractCommands(tx: TransactionForContract): Collection<AuthenticatedObject<CommandData>>
|
|
||||||
= tx.commands.select<Commands>() + tx.commands.select<TimestampCommand>()
|
= tx.commands.select<Commands>() + tx.commands.select<TimestampCommand>()
|
||||||
|
|
||||||
|
override fun verify(tx: TransactionForContract) = verifyClauses(tx, listOf(Clause.Timestamped(), Clause.Group()), extractCommands(tx))
|
||||||
|
|
||||||
interface Clause {
|
interface Clause {
|
||||||
/**
|
/**
|
||||||
* Common superclass for IRS contract clauses, which defines behaviour on match/no-match, and provides
|
* Common superclass for IRS contract clauses, which defines behaviour on match/no-match, and provides
|
||||||
@ -505,7 +506,7 @@ class InterestRateSwap() : ClauseVerifier() {
|
|||||||
override val ifMatched = MatchBehaviour.END
|
override val ifMatched = MatchBehaviour.END
|
||||||
override val ifNotMatched = MatchBehaviour.ERROR
|
override val ifNotMatched = MatchBehaviour.ERROR
|
||||||
|
|
||||||
override fun extractGroups(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, String>>
|
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, String>>
|
||||||
// Group by Trade ID for in / out states
|
// Group by Trade ID for in / out states
|
||||||
= tx.groupStates() { state -> state.common.tradeID }
|
= tx.groupStates() { state -> state.common.tradeID }
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class Cash : OnLedgerAsset<Currency, Cash.State>() {
|
|||||||
Issue(),
|
Issue(),
|
||||||
ConserveAmount())
|
ConserveAmount())
|
||||||
|
|
||||||
override fun extractGroups(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, Issued<Currency>>>
|
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, Issued<Currency>>>
|
||||||
= tx.groupStates<State, Issued<Currency>> { it.issuanceDef }
|
= tx.groupStates<State, Issued<Currency>> { it.issuanceDef }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.State>() {
|
|||||||
/**
|
/**
|
||||||
* Group commodity states by issuance definition (issuer and underlying commodity).
|
* Group commodity states by issuance definition (issuer and underlying commodity).
|
||||||
*/
|
*/
|
||||||
override fun extractGroups(tx: TransactionForContract)
|
override fun groupStates(tx: TransactionForContract)
|
||||||
= tx.groupStates<State, Issued<Commodity>> { it.issuanceDef }
|
= tx.groupStates<State, Issued<Commodity>> { it.issuanceDef }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ val OBLIGATION_PROGRAM_ID = Obligation<Currency>()
|
|||||||
*
|
*
|
||||||
* @param P the product the obligation is for payment of.
|
* @param P the product the obligation is for payment of.
|
||||||
*/
|
*/
|
||||||
class Obligation<P> : ClauseVerifier() {
|
class Obligation<P> : Contract {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO:
|
* TODO:
|
||||||
@ -43,7 +43,7 @@ class Obligation<P> : ClauseVerifier() {
|
|||||||
* that is inconsistent with the legal contract.
|
* that is inconsistent with the legal contract.
|
||||||
*/
|
*/
|
||||||
override val legalContractReference: SecureHash = SecureHash.sha256("https://www.big-book-of-banking-law.example.gov/cash-settlement.html")
|
override val legalContractReference: SecureHash = SecureHash.sha256("https://www.big-book-of-banking-law.example.gov/cash-settlement.html")
|
||||||
override val clauses = listOf(InterceptorClause(Clauses.VerifyLifecycle<P>(), Clauses.Net<P>()),
|
private val clauses = listOf(InterceptorClause(Clauses.VerifyLifecycle<P>(), Clauses.Net<P>()),
|
||||||
Clauses.Group<P>())
|
Clauses.Group<P>())
|
||||||
|
|
||||||
interface Clauses {
|
interface Clauses {
|
||||||
@ -62,7 +62,7 @@ class Obligation<P> : ClauseVerifier() {
|
|||||||
ConserveAmount()
|
ConserveAmount()
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun extractGroups(tx: TransactionForContract): List<TransactionForContract.InOutGroup<Obligation.State<P>, Issued<Terms<P>>>>
|
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<Obligation.State<P>, Issued<Terms<P>>>>
|
||||||
= tx.groupStates<Obligation.State<P>, Issued<Terms<P>>> { it.issuanceDef }
|
= tx.groupStates<Obligation.State<P>, Issued<Terms<P>>> { it.issuanceDef }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,8 +377,9 @@ class Obligation<P> : ClauseVerifier() {
|
|||||||
data class Exit<P>(override val amount: Amount<Issued<Terms<P>>>) : Commands, FungibleAsset.Commands.Exit<Terms<P>>
|
data class Exit<P>(override val amount: Amount<Issued<Terms<P>>>) : Commands, FungibleAsset.Commands.Exit<Terms<P>>
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun extractCommands(tx: TransactionForContract): List<AuthenticatedObject<FungibleAsset.Commands>>
|
private fun extractCommands(tx: TransactionForContract): List<AuthenticatedObject<FungibleAsset.Commands>>
|
||||||
= tx.commands.select<Obligation.Commands>()
|
= tx.commands.select<Obligation.Commands>()
|
||||||
|
override fun verify(tx: TransactionForContract) = verifyClauses(tx, clauses, extractCommands(tx))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A default command mutates inputs and produces identical outputs, except that the lifecycle changes.
|
* A default command mutates inputs and produces identical outputs, except that the lifecycle changes.
|
||||||
|
@ -2,7 +2,8 @@ package com.r3corda.contracts.asset
|
|||||||
|
|
||||||
import com.r3corda.contracts.clause.AbstractConserveAmount
|
import com.r3corda.contracts.clause.AbstractConserveAmount
|
||||||
import com.r3corda.core.contracts.*
|
import com.r3corda.core.contracts.*
|
||||||
import com.r3corda.core.contracts.clauses.ClauseVerifier
|
import com.r3corda.core.contracts.clauses.SingleClause
|
||||||
|
import com.r3corda.core.contracts.clauses.verifyClauses
|
||||||
import com.r3corda.core.crypto.Party
|
import com.r3corda.core.crypto.Party
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
@ -24,9 +25,13 @@ import java.security.PublicKey
|
|||||||
* At the same time, other contracts that just want assets and don't care much who is currently holding it can ignore
|
* At the same time, other contracts that just want assets and don't care much who is currently holding it can ignore
|
||||||
* the issuer/depositRefs and just examine the amount fields.
|
* the issuer/depositRefs and just examine the amount fields.
|
||||||
*/
|
*/
|
||||||
abstract class OnLedgerAsset<T : Any, S : FungibleAsset<T>> : ClauseVerifier() {
|
abstract class OnLedgerAsset<T : Any, S : FungibleAsset<T>> : Contract {
|
||||||
|
abstract val clauses: List<SingleClause>
|
||||||
|
abstract fun extractCommands(tx: TransactionForContract): Collection<AuthenticatedObject<CommandData>>
|
||||||
abstract val conserveClause: AbstractConserveAmount<S, T>
|
abstract val conserveClause: AbstractConserveAmount<S, T>
|
||||||
|
|
||||||
|
override fun verify(tx: TransactionForContract) = verifyClauses(tx, clauses, extractCommands(tx))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate an transaction exiting assets from the ledger.
|
* Generate an transaction exiting assets from the ledger.
|
||||||
*
|
*
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package com.r3corda.core.contracts.clauses
|
||||||
|
|
||||||
|
import com.r3corda.core.contracts.AuthenticatedObject
|
||||||
|
import com.r3corda.core.contracts.CommandData
|
||||||
|
import com.r3corda.core.contracts.TransactionForContract
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A clause that can be matched as part of execution of a contract.
|
||||||
|
*/
|
||||||
|
// TODO: ifNotMatched/ifMatched should be dropped, and replaced by logic in the calling code that understands
|
||||||
|
// "or", "and", "single" etc. composition of sets of clauses.
|
||||||
|
interface Clause {
|
||||||
|
/** Classes for commands which must ALL be present in transaction for this clause to be triggered */
|
||||||
|
val requiredCommands: Set<Class<out CommandData>>
|
||||||
|
/** Behaviour if this clause is matched */
|
||||||
|
val ifNotMatched: MatchBehaviour
|
||||||
|
/** Behaviour if this clause is not matches */
|
||||||
|
val ifMatched: MatchBehaviour
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class MatchBehaviour {
|
||||||
|
CONTINUE,
|
||||||
|
END,
|
||||||
|
ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SingleVerify {
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @return the set of commands that are consumed IF this clause is matched, and cannot be used to match a
|
||||||
|
* later clause. This would normally be all commands matching "requiredCommands" for this clause, but some
|
||||||
|
* verify() functions may do further filtering on possible matches, and return a subset. This may also include
|
||||||
|
* commands that were not required (for example the Exit command for fungible assets is optional).
|
||||||
|
*/
|
||||||
|
@Throws(IllegalStateException::class)
|
||||||
|
fun verify(tx: TransactionForContract,
|
||||||
|
commands: Collection<AuthenticatedObject<CommandData>>): Set<CommandData>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SingleClause : Clause, SingleVerify
|
@ -1,64 +1,10 @@
|
|||||||
|
@file:JvmName("ClauseVerifier")
|
||||||
package com.r3corda.core.contracts.clauses
|
package com.r3corda.core.contracts.clauses
|
||||||
|
|
||||||
import com.r3corda.core.contracts.*
|
import com.r3corda.core.contracts.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
interface Clause {
|
// Wrapper object for exposing a JVM friend version of the clause verifier
|
||||||
/** Classes for commands which must ALL be present in transaction for this clause to be triggered */
|
|
||||||
val requiredCommands: Set<Class<out CommandData>>
|
|
||||||
/** Behaviour if this clause is matched */
|
|
||||||
val ifNotMatched: MatchBehaviour
|
|
||||||
/** Behaviour if this clause is not matches */
|
|
||||||
val ifMatched: MatchBehaviour
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class MatchBehaviour {
|
|
||||||
CONTINUE,
|
|
||||||
END,
|
|
||||||
ERROR
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SingleVerify {
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @return the set of commands that are consumed IF this clause is matched, and cannot be used to match a
|
|
||||||
* later clause. This would normally be all commands matching "requiredCommands" for this clause, but some
|
|
||||||
* verify() functions may do further filtering on possible matches, and return a subset. This may also include
|
|
||||||
* commands that were not required (for example the Exit command for fungible assets is optional).
|
|
||||||
*/
|
|
||||||
@Throws(IllegalStateException::class)
|
|
||||||
fun verify(tx: TransactionForContract,
|
|
||||||
commands: Collection<AuthenticatedObject<CommandData>>): Set<CommandData>
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
interface SingleClause : Clause, SingleVerify
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract superclass for clause-based contracts to extend, which provides a verify() function
|
|
||||||
* that delegates to the supplied list of clauses.
|
|
||||||
*/
|
|
||||||
abstract class ClauseVerifier : Contract {
|
|
||||||
abstract val clauses: List<SingleClause>
|
|
||||||
abstract fun extractCommands(tx: TransactionForContract): Collection<AuthenticatedObject<CommandData>>
|
|
||||||
override fun verify(tx: TransactionForContract) = verifyClauses(tx, clauses, extractCommands(tx))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify a transaction against the given list of clauses.
|
|
||||||
*
|
|
||||||
* @param tx transaction to be verified.
|
|
||||||
* @param clauses the clauses to verify.
|
|
||||||
* @param T common supertype of commands to extract from the transaction, which are of relevance to these clauses.
|
|
||||||
*/
|
|
||||||
inline fun <reified T : CommandData> verifyClauses(tx: TransactionForContract,
|
|
||||||
clauses: List<SingleClause>)
|
|
||||||
= verifyClauses(tx, clauses, tx.commands.select<T>())
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify a transaction against the given list of clauses.
|
* Verify a transaction against the given list of clauses.
|
||||||
*
|
*
|
||||||
@ -89,4 +35,5 @@ fun verifyClauses(tx: TransactionForContract,
|
|||||||
}
|
}
|
||||||
|
|
||||||
require(unmatchedCommands.isEmpty()) { "All commands must be matched at end of execution." }
|
require(unmatchedCommands.isEmpty()) { "All commands must be matched at end of execution." }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,10 +26,10 @@ abstract class GroupClauseVerifier<S : ContractState, T : Any> : SingleClause {
|
|||||||
override val requiredCommands: Set<Class<out CommandData>>
|
override val requiredCommands: Set<Class<out CommandData>>
|
||||||
get() = emptySet()
|
get() = emptySet()
|
||||||
|
|
||||||
abstract fun extractGroups(tx: TransactionForContract): List<TransactionForContract.InOutGroup<S, T>>
|
abstract fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<S, T>>
|
||||||
|
|
||||||
override fun verify(tx: TransactionForContract, commands: Collection<AuthenticatedObject<CommandData>>): Set<CommandData> {
|
override fun verify(tx: TransactionForContract, commands: Collection<AuthenticatedObject<CommandData>>): Set<CommandData> {
|
||||||
val groups = extractGroups(tx)
|
val groups = groupStates(tx)
|
||||||
val matchedCommands = HashSet<CommandData>()
|
val matchedCommands = HashSet<CommandData>()
|
||||||
val unmatchedCommands = ArrayList(commands.map { it.value })
|
val unmatchedCommands = ArrayList(commands.map { it.value })
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ Writing a contract using clauses
|
|||||||
This tutorial will take you through restructuring the commercial paper contract to use clauses. You should have
|
This tutorial will take you through restructuring the commercial paper contract to use clauses. You should have
|
||||||
already completed ":doc:`tutorial-contract`".
|
already completed ":doc:`tutorial-contract`".
|
||||||
|
|
||||||
Clauses are essentially "mini-contracts" which contain verification logic, and are composed together to form
|
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
|
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
|
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.
|
error, and improves consistency of behaviour.
|
||||||
@ -27,25 +27,24 @@ In the case of commercial paper, we have a "Grouping" outermost clause, which wi
|
|||||||
Commercial paper class
|
Commercial paper class
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
First we need to change the class from implementing ``Contract``, to extend ``ClauseVerifier``. This is an abstract
|
To use the clause verification logic, the contract needs to call the ``verifyClauses()`` function, passing in the transaction,
|
||||||
class which provides a verify() function for us, and requires we provide a property (``clauses``) for the clauses to test,
|
a list of clauses to verify, and a collection of commands the clauses are expected to handle all of. This list of
|
||||||
and a function (``extractCommands``) to extract the applicable commands from the transaction. This is important because
|
commands is important because ``verifyClauses()`` checks that none of the commands are left unprocessed at the end, and
|
||||||
``ClauseVerifier`` checks that no commands applicable to the contract are left unprocessed at the end. The following
|
raises an error if they are. The following examples are trimmed to the modified class definition and added elements, for
|
||||||
examples are trimmed to the modified class definition and added elements, for brevity:
|
brevity:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
.. sourcecode:: kotlin
|
.. sourcecode:: kotlin
|
||||||
|
|
||||||
class CommercialPaper : ClauseVerifier {
|
class CommercialPaper : Contract {
|
||||||
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper");
|
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper")
|
||||||
|
|
||||||
override val clauses: List<SingleClause>
|
private fun extractCommands(tx: TransactionForContract): List<AuthenticatedObject<CommandData>>
|
||||||
get() = throw UnsupportedOperationException("not implemented")
|
|
||||||
|
|
||||||
override fun extractCommands(tx: TransactionForContract): List<AuthenticatedObject<CommandData>>
|
|
||||||
= tx.commands.select<Commands>()
|
= tx.commands.select<Commands>()
|
||||||
|
|
||||||
|
override fun verify(tx: TransactionForContract) = verifyClauses(tx, listOf(Clauses.Group()), extractCommands(tx))
|
||||||
|
|
||||||
.. sourcecode:: java
|
.. sourcecode:: java
|
||||||
|
|
||||||
public class CommercialPaper implements Contract {
|
public class CommercialPaper implements Contract {
|
||||||
@ -54,11 +53,6 @@ examples are trimmed to the modified class definition and added elements, for br
|
|||||||
return SecureHash.Companion.sha256("https://en.wikipedia.org/wiki/Commercial_paper");
|
return SecureHash.Companion.sha256("https://en.wikipedia.org/wiki/Commercial_paper");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<SingleClause> getClauses() {
|
|
||||||
throw UnsupportedOperationException("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<AuthenticatedObject<CommandData>> extractCommands(@NotNull TransactionForContract tx) {
|
public Collection<AuthenticatedObject<CommandData>> extractCommands(@NotNull TransactionForContract tx) {
|
||||||
return tx.getCommands()
|
return tx.getCommands()
|
||||||
@ -67,6 +61,11 @@ examples are trimmed to the modified class definition and added elements, for br
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verify(@NotNull TransactionForContract tx) throws IllegalArgumentException {
|
||||||
|
ClauseVerifier.verifyClauses(tx, Collections.singletonList(new Clause.Group()), extractCommands(tx));
|
||||||
|
}
|
||||||
|
|
||||||
Clauses
|
Clauses
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user