Release 0.82.0

Corda upgrade

The CSL CorDapp has been upgraded to Corda 4.9.

Bounded events cache in CordaLedger

com.deondigital.cordapp.client.CordaLedger no longer stores materialized events in the contract cache. Instead, a separate shared event cache has been added to store the events across all contracts whose size can be controlled in addition to the size of the contract cache. The change now ensures that the contract and event caches can be bounded in size and the memory usage of the CordaLedger object and the process instantiating it can be tightly controlled. The event cache also supports speculative loading of all events of a contract when a contract cache entry is accessed or updated to improve cache hit rates. This speculative behavior is a configuration parameter that can be enabled or disabled when the cache configuration is specified. The segregated cache architecture of bounded contract and event cache is now unified across the Corda and DbLedger backends.

Simple report cache in CordaLedger

com.deondigital.cordapp.client.CordaLedger now caches report results, using the arguments as the cache key. This will ensure that repeated report invocations with identical arguments return instantly, providing a large speed up in many common access patterns. The cache uses a simple invalidation strategy where any event application causes a cache invalidation.

Queries.pastEvents() exposes a lazy Sequence instead of an eager List

To take advantage of a separate event cache, the com.deondigital.sic.Queries.pastEvents method now returns a Sequence that is materialized lazily by the consumer instead of an eager pre-materialized List. This change ensures that the cache is only invoked for elements that are accessed in the sequence thus reducing unnecessary cache evictions. Note that this change means that a materialized list of all events in a contract is never stored in the contract cache as before. Instead, the required events for a contract are always materialized from the event cache upon access from the application code.

com.deondigital.cordapp.client.ContractHandler returns CachedCSLContractStateHandle instead of CachedCSLContractState

To facilitate the cache-related reorganization, a wrapper object CachedCSLContractStateHandle has been introduced that is returned by the methods addContracts, execute, getCurrentState in com.deondigital.cordapp.client.ContractHandler instead of CachedCSLContractState. CachedCSLContractStateHandle has a property data which consists of the CachedCSLContractState object. CachedCSLContractState no longer consists of properties to access events in a contract since they are not materialized and stored in the contract cache anymore. Instead, they are exposed as functions in CachedCSLContractHandle to signify their materialization upon access.

Removal of recursive types

CSL no longer supports the definition of recursive datatypes. In practice, the only recursive type that was actually used was List, and the presence of recursive types complicated many aspects of the language.

Breaking changes to the sic ABI

A CSL declaration compiled with the newest version sic is not binary compatible with the output of older versions of sic. Although the differences are small, because Corda identifies attachments uniquely by their hash, the same CSL compiled with 0.82.0 sic will get a different attachment ID compared to when compiling with previous versions of sic. A consequence is that contracts instantiated on Corda from a previously-compiled CSL declaration are invisible if querying the ledger with a ContractHandler that is scoped to the attachment id of a CSL declaration compiled with 0.82.0 sic.

To mitigate this, you should run the CSL Platform 0.82.0 Upgrade Script after you have deployed the 0.82.0 CSL cordapp to your Corda node(s).

See the migration guide for more details.

Properties and Unit Tests in CSL

CSL has been extended with support for properties, which are either Boolean values that the contract developer expects to be true, or Boolean functions that are expected to be true for all possible arguments. Properties can be checked from the JVM APIs using a variety of testing strategies, and integrate with JUnit and with Clojure testing tools.

Additionally, CSL has been extended with support for unit testing. Please refer to the documentation for the Features for Testing CSL module in the standard library for details.

A new built-in type NonEmptyList and several utility functions added to the CSL standard library

The CSL Standard Library has been extended with a new data type for non-empty lists, and with several utility functions. Please refer to the documentation for the NonEmptyList module for description of the new type, and examples of usage.

Queries.pastEvents() supports configuration parameter to customize sequence order

Previously the lazy event sequence generated by Queries.pastEvents() was always ordered from oldest to newest i.e., the event application order. Now a configuration parameter has been exposed to customize the sequence order either from oldest to newest or from newest to oldest. If the parameter is not provided, the generated sequence is ordered from oldest to newest (as before). This parameter can be specified when the event consumption pattern is known by the application to optimize the materialization of the underlying event sequence.

CSL declaration compatibility on Corda

CordaLedger can now be configured to scope over all CSL declaration attachments on the ledger that are “compatible” with a specific CSL attachment.

Two CSL Corda attachments are compatible if and only if all types defined in both attachments are identical. Conversely, if there is any type such that:

  • it is defined in both declarations, and

  • its definition in one of the declarations is different from the other

Then there is a mismatch and the two declarations are not compatible.

Concretely, declaration compatibility can be enabled by constructing a CordaLedger with an implementation of the DeclarationScope interface. In this example, we use DeclarationScope.AllCompatible, which is one of two implementations provided. The following example shows a full CordaLedger construction as a parameter to GenericContractHandler.

// "ops" refers to a connected Corda RPC client of type CordaRPCOps.
// "ContractService" refers to the ContractService generated by the gradle sic plugin.
GenericContractHandler(
    ops.notaryIdentities().first(),
    CordaLedger(
      ops,
      DefaultCSLFlows,
      DeclarationScope.AllCompatible(
        ClasspathCslCordaAttachment(ContractService::class.java).uploadIfNotExists(ops)
      ),
      CordaLedger.DEFAULT_CACHE_CONFIG
    )
)

The consequence is that all compatible attachments are considered when running reports with CordaLedger.report. Similarly getAllContractIds will include contracts from all compatible attachments. CordaLedger.onContractUpdated will also emit events when any contract is instantiated or changed on compatible attachments.

Note that the set of compatible attachments is computed when CordaLedger is instantiated. Therefore, after adding a new compatible CSL attachment to the Corda ledger, you must re-instantiate CordaLedger.

DeclarationScope is an interface and as such you can implement your own scoping rules if needed. DeclarationScope.AllCompatible is also designed to be extensible and can be subclassed. You can override DeclarationScope.includeAttachment(attachment: CSLAttachment) to filter out attachments from being considered for compatibility.

Syntax for polymorphic external declarations has changed

The syntax for external declarations predates that of explicit variable binders. Consequently, it used a different scheme where type variables were inferred from usage and constrained in a postfixed where clause.

To streamline the syntax of CSL, the syntax for polymorphic external declarations now resembles that of polymorphic value declarations.

See the migration guide for instructions on migrating your CSL files.

Direct pattern matching on expressions

Previously, if you had a value x of some type with constructors C1 and C2, to pattern match on it you’d have to create a pattern matching lambda and immediately apply it to x ala:

(\ C1 -> ...
 | C2 -> ...
) x

In complex definitions, this could quickly become unwieldy and obscure the intent of the code. To avoid this situation, there is new syntax to directly pattern match on expressions:

match x with {
  | C1 -> ...
  | C2 -> ...
}

This new form works in both contract and value definitions.