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
migrate your existing Corda states using the mass migration tool, or
configure your
CordaLedger
to use declaration compatibility.
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",
]