Release 2.0.0

The 2.0.0 release of the CSL platform is a major version bump, but should not require much work to adapt to. The main reason for the major version bump is that the ABI of compiled CSL declarations has changed. Version 2.0.0 is backwards compatible though, (but note that 1.0.0 is not forward compatible), so upgrading should be painless. However, since the Corda attachment hashes are computed based on the compiled CSL format, the hashes of identical CSL declarations compiled under 2.0.0 and 1.0.0 respectively have changed. This means that you should either

Prefix guards in CSL

Event prefix contracts can now contain a “prefix guard”, allowing you to specify that a list of patterns must match before accepting the event. This allows expressing more advanced predicates than simple “where clauses”, and furthermore means that variables bound in the patterns are available in the continuation contract.

For example, consider following definitions:

type Receipt {
  gross: Float,
  tax: Float,
  receiver: Agent,
  payee: Agent,
  store: String
}
type Payment : Event {
  amount: Float,
  receipt: Maybe Receipt,
  receiver: Agent,
  store: Maybe String
}

If one wants to write a contract that

  • accepts a payment event only if there is a receipt where - the receiver matches the receiver specified on the event - the payee is the agent that submitted the event - the gross plus the tax matches the total amount on the event - the store that got the payment is also the store on the receipt

  • then allows the receipt to be cleared by the bookkeeper

one would previously have to write:

contract acceptPaymentFrom = \(payee: Agent) ->
  <payee> e: Payment where
    e.agent = payee &&
    match e.receipt with {
      | (Some receipt : Maybe Receipt) ->
        receipt.gross + receipt.tax = e.amount &&
        receipt.payee = payee &&
        receipt.receiver = e.receiver && (
          match e.store with {
            | Some store -> receipt.store = store
            | None -> False
          }
        )
      | None -> False
    }
  then match e.receipt with {
    | Some receipt -> clearReceipt receipt
    | None -> failure
  }

Note that one must write a pattern matching expression in the where clause, which continues with a boolean expression in the Some branch. Because store is also a Maybe type, a nested pattern match is required. In the continuation, one must pattern match once again to reveal the receipt, even though it is obvious that this point could only have been reached if there indeed was a receipt.

With the new prefix guards, the same contract can be specified far simpler:

contract acceptPaymentFrom2 = \(payee: Agent) ->
  <payee> e: Payment matching {
    Some receipt <- e.receipt,
    Some store <- e.store
  } where
    e.agent = payee &&
    receipt.gross + receipt.tax = e.amount &&
    receipt.payee = payee &&
    receipt.receiver = e.receiver &&
    receipt.store = store
  then clearReceipt receipt

The matching keyword introduces the prefix guard, which is a comma-separated list of PATTERN <- EXPRESSION qualifiers, wrapped in braces. The event is only accepted if all the qualifers match. In the following where clause, all names bound in the guard are available, as are they in the continuation. Therefore, it is no longer necessary to match on the receipt again.

Trailing commas in CSL

CSL files now support trailing commas in all comma-separated lists, records, and tuples. This allows more consistency when formatting some CSL constructs, e.g.

val strings = [
  "hello",
  "with",
  "trailing",
  "commas",
]