-
Notifications
You must be signed in to change notification settings - Fork 0
Contract Command Verification
Matthew Layton edited this page Jun 22, 2021
·
1 revision
In Corda, a smart contract is defined as a set of commands that govern the evolution of state transitions on the ledger. For as long as I can remember, I've seen R3 implement contracts, something like this:
class ExampleContract : Contract {
override fun verify(tx: LedgerTransaction) {
val command = requireSingleCommand<ExampleCommand>()
when (command.value) {
is ExampleCommand.Issue -> verifyIssue(tx, command.signers)
is ExampleCommand.Spend -> verifySpend(tx, command.signers)
else -> throw IllegalArgumentException("Unrecognised command")
}
}
interface ExampleCommand : CommandData {
class Issue : ExampleCommand
class Spend : ExampleCommand
}
private fun verifyIssue(tx: LedgerTransaction, signers: List<PublicKey>) {
}
private fun verifySpend(tx: LedgerTransaction, signers: List<PublicKey>) {
}
}
There's a couple of things in this design that I've never really liked (but in rare cases, necessary).
-
Issue
andSpend
are marker classes, and that's not a good idea. - It violates the Single Responsibility Principle.
The VerifiableCommandData
interface solves these problems as it implements the verification logic per command class, rather than all in the contract class; no more empty marker classes and each command responsible for its own verification logic. There's even an extension function to get rid of all the boilerplate logic in the contract's verify
function.
The following example demonstrates a VerifiableCommandData
implementation.
class ExampleContract : Contract {
override fun verify(tx: LedgerTransaction) = tx.allowCommands(
Issue::class.java,
Spend::class.java
)
interface ExampleCommand : VerifiedCommandData
class Issue : VerifiedCommandData {
override fun verify(transaction: LedgerTransaction, signers: Set<PublicKey>) {
}
}
class Spend : VerifiedCommandData {
override fun verify(transaction: LedgerTransaction, signers: Set<PublicKey>) {
}
}
}