Skip to content

Contract Command Verification

Matthew Layton edited this page Jun 22, 2021 · 1 revision

Introduction

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).

VerifiableCommandData

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.

Example

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>) {
    }
  }
}