Documentation Index
Fetch the complete documentation index at: https://docs.canton.network/llms.txt
Use this file to discover all available pages before exploring further.
Smart Contract Upgrade
Overview
What is Smart Contract Upgrade (SCU)?
Smart Contract Upgrade (SCU) allows Daml models (packages in DAR files) to be updated on Canton transparently, provided some guidelines in making the changes are followed. For example, you can fix an application bug by uploading the DAR of the fixed package. Smart Contract Upgrade (SCU) is a feature for Daml packages which enable authors to publish new versions of their templates while maintaining compatibility with prior versions, without any downtime for package users and existing contracts. This feature is well-suited for developing and rolling out incremental template changes. There are guidelines to ensure upgrade compatibility between DAR files. The compatibility is checked at compile time, DAR upload time, and runtime. This is to ensure data backwards upgrade compatibility and forwards compatibility (subject to the guidelines being followed) so that DARs can be safely upgraded to new versions. It also prevents unexpected data loss at clients if a runtime downgrade occurs (e.g., a client is using template version 1.0.0 while the participant node has the newer version 1.1.0). A general guideline for SCU is that additive application changes are allowed but items cannot be removed. A summary of the allowed changes in templates are:- A template can add new
Optionalfields at the end of the list of fields; - A
recorddatatype can add new optional fields at the end of the list of fields, and avariantorenumdatatype can add new constructors at the end; - The
ensurepredicate can be changed and it is reevaluated at interpretation; - A choice signature can be changed by adding
Optionalparameters at the end; - The controller of a choice can be changed;
- The observer of a choice can be changed
- The body of a choice can be changed;
- A new choice can be added to a template;
- The implementation of an interface instance can be changed;
| Daml Model Change | Scope | Covered by SCU? | Comments |
|---|---|---|---|
| Rename of Module | Template Definition | No | |
| Rename of Template | Template Definition | No | |
| Change of Field Type | Template Definition | No | |
| Change of Field Ordering | Template Definition | No | |
| Change of Field Name | Template Definition | No | |
| Change of Signatories | Template Definition | No | A new template version needs to return the same signatory result for a persisted contract, otherwise the contracts cannot be used by an upgraded package (this fails at runtime). |
| Change of | Template | No | The key’s type cannot be changed, like adding a new field in the record it contains. |
| Change of Observers | Template Definition | No | Same as template signatories |
| Change of Key Maintainers | Template Definition | No | Same as template signatories |
| Change of Ensure Predicate | Template Definition | Yes | Note that the Ensure is recalculated just before usage so any changes need to ensure that a V1 contract is still valid as a V2 contract. |
| Addition of New Definition Fields, Enum, and Variant Constructors | Template Definition | Yes, but they they must be optional | |
| Deletion of Fields from Template | Template Definition | No | |
| Change of Choice Implementation | Template Definition | Yes | |
| Change in Choice Controller | Choice | Yes | |
| Addition of Choice Parameters | Choice | Yes, but they must be optional | |
| Deletion of Choice Parameters | Choice | No | |
| Change of Consuming Choice to Non-Consuming Choice, or Vice-versa | Choice | No | |
| Change of Choice Body | Choice | Yes | |
| Change of Choice Return Type | Choice | Yes, except for Tuple or scalar types | You can change a user data type (Record, Variant, Enum) contained in the return type. In this case you can add a field to the record or a new case to the Variant/Enum. |
| Change of Interface Definition | Interface Definition | No | Interfaces are static and cannot be changed. Separate interface definition and keep the implementation in the template code. |
| Adding Interfaces to Templates | Template Definition | Yes | |
| Removing Interfaces from Templates | Template Definition | No | Though the implementation can be changed. |
| Change or Deletion of Exception | Exception Definition | No | Keep the exception definition separate. Exceptions cannot be upgraded. |
- the package ID
- the package name.
- When defining a Daml package, the
namefield of the package’sdaml.yamlis now used to specify the SCU package name.
daml.yaml) instead of package-id. When specifying a template/interface/choice name, simply substitute any package-id with the package-name (eg. now register:DA.Register:Token) instead of the old deadbeefpackageidhex:DA.Register:Token format. This applies to template filters and SQL queries (eg. via the active() function). These functions will always return all versions of a given identifier. Qualified name can be:
- fully qualified, e.g.
<package-name>:<module-path>:<template-name> - partially qualified, e.g.
<module-path>:<template-name>
Smart Contract Upgrade Basics
To upgrade a package the package author modifies their existing package to add new functionality, such as new fields and choices. When the new package is uploaded to a participant with the old version, the participant ensures that every modification to the model in the new version is a valid upgrade of the previous version. To be able to automatically upgrade a contract or datatype, SCU restricts the kinds of changes that a new package version can introduce over its prior version. For example, the simplest kind of data transformation that SCU supports is adding a field to a template. Given the following first version of a template:None. This optional field requirement extends to all records in your package. Conversely, newer contracts with this field set to None can be automatically downgraded to previous versions of the template in workflows that have not yet been updated.
Automatic Data Upgrades and Downgrades
When extending data in a Daml model, SCU requires the old model to be representable in the new model. For extending a record, we can only add nullable (Optional) fields, so that old data can be represented by setting these fields to None. Similar constraints hold for Variants and Enums, which only allow adding constructors, with some other restrictions covered in Continuing to Write Your Upgrades. This approach is inspired by Protobuf and Typescript’s ability to ignore excess fields via as.
Automatic data upgrades occur in the following places:
Submissions to the Ledger API
When you submit a command, and provide only a package-name instead of a package-id, Canton will automatically upgrade (or downgrade) the payloads you give to the most recent version of the package that is uploaded on the participant. It will also use the most recent implementation of any choices you exercise directly through the Ledger API, by automatically upgrading/downgrading the choice argument. Choice result upgrading/downgrading is handled by Consuming Clients, as discussed later in this section. This behavior can be influenced by the package preference.
Updates in a choice body
When Fetching a contract, the contract payload will be automatically upgraded/downgraded to match the version expected by the calling choice body, as compiled into the DAR. When Exercising a choice on a contract, the contract payload will be upgraded/downgraded to match the version of the choice expected by the calling choice body. This means that in a choice body, an exercised choice argument or return type is never upgraded/downgraded.
Consuming Clients (such as Daml Script, TypeScript/Java codegen)
When clients query the Ledger API for contracts, the returned event payload format matches the template originally used for generating the event (creating a contract/exercising a choice). It is the responsibility of these clients to upgrade/downgrade the payloads they receive to match what is expected downstream. The same applies to choice results. Daml Script, as well as TypeScript/Java codegen, does this for you to match the Ledger API response to the package versions they were run/built from.
Upgrading Across the Stack
These are all the components that interact with SCU, and how you as a user should be aware that they interacts.Canton
When considering the Canton ledger nodes, only the Canton participant node is aware of smart contract upgrading. Below, we provide a brief overview of the interactions with the participant node that have been adapted for supporting the smart contract upgrading feature:- DAR upload requests go through an additional validation stage to check the contained new packages for upgrade-compatibility with other packages previously uploaded on the participant.
- Ledger API command submissions can be automatically or explicitly up/downgraded if multiple packages exist for the same package-name.
- Ledger API streaming queries are adapted to support fetching events more generically, by package-name.
Code Generation
The Java and TypeScript CodeGen have been updated to perform upgrades on retrieved contracts, and now use package-names over package-ids for commands to the participant.JSON Ledger API
To match the changes to the gRPC Ledger API, the JSON Ledger API similarly supports package-name queries and command submission.PQS & Daml Shell
PQS only supports querying contracts via package-name, dropping support for direct package-id queries. See the PQS section for more information and a work-around. Daml Shell builds on top of PQS, so inherits this behavior.Daml Script
Support for SCU is available in the opt-in LTS version of Daml Script. This version acts as a drop-in replacement for the previous Daml Script, and enables support for upgrades on all queries and command submissions. We also expose functions for more advanced interactions with upgrades, as well as to revert to the previous submission behavior.Daml Compiler
The Daml compiler supports theupgrades: configuration field - every time dpm build is invoked, it validates the current package for upgrade compatibility against the package specified in the upgrades: field.
Validation emits custom error messages for common upgrading mistakes, and warns the package author when upgrading a package in a potentially unsafe way. Note however that this validation cannot be complete, as upgrade validity depends on a participant’s package store. The participant’s DAR upload checks have the final say on upgrade validity.
Limitations
To allow SCU to minimize downtime, and multiple versions of a package to be active at once, we limit the types of transformations that can be performed on live data. Following are some data transformations that cannot be made using SCU upgrades:- Renaming, removal, or rearrangement of fields in a template
- Conversion of records to variants and vice versa
- Moving templates/datatypes to other modules
- Upgrading interface and exception definitions
- Removing an interface instance from a template
- Daml Script does not support SCU or LF1.17, you must use Daml Script LTS.
- Once a version of a package is uploaded to the ledger, it cannot be replaced or removed. If a package is uploaded by mistake it can only be overriden by uploading an even newer version.
- As a consequence of the above, if a record is extended by mistake with an optional field, that field must be part of all future versions.
- Package versions cannot be deleted, they can only be unvetted. As a good rule of thumb the highest version for each package name should be vetted.
- If, for whatever reason, a lower version of a package is vetted and preferred, that package version needs to be explicitly mentioned in the command submissions in the specific
package_id_selection_preferencefield of the grpcCommandsmessage. - Participants that confirm and observe a certain transaction must have vetted the package version that is preferred by the submitting participant. If that is not the case, the transaction is rejected.
- The contract create events sent as part of the active contracts and transaction streams are always represented according to the template definition in the package version used at time of their creation.
The Programming Model by Example
Writing Your First Smart Contract Upgrade
Setup
We continue with the example introduced in Smart Contract Upgrade Basics. Begin by defining the first (old) version of our package:dpm version should print a line showing that 3.3.0 or higher is the “project SDK version from daml.yaml”.
Add daml-script to the list of dependencies in v1/my-pkg/daml.yaml, as well as --target=2.1 to the build-options:
daml-script in any package that is uploaded to the ledger - more on this here.
Then create v1/my-pkg/daml/Main.daml:
v1/my-pkg/.daml/dist/my-pkg-1.0.0.dar:
daml.yaml to update the package version, and add the upgrades: field pointing to v1:
Adding a New Field
Begin by adding a newcurrency field to v2/my-pkg/daml/Main.daml:
dpm build. An error is emitted:
None.
Fix the currency field to be optional, and re-run dpm build:
Seeing Upgraded Fields in Contracts
Using the Daml Sandbox, we can see our old contracts automatically upgrade. Add a script to make and get IOUs tov1/my-pkg/daml/Main.daml:
v2/my-pkg/daml/Main.daml and add scripts to make IOUs with and without a currency field, and a script to get any IOU:
dpm sandbox to start a simple ledger in which to test upgrades.
v1/my-pkg, upload and run the mkIOU script and place the resulting party for Alice into an output file alice-v1:
v2/my-pkg, upload and run the getIOU script, passing in the alice-v1 file as the script’s input:
currency which is set to null. When running the getIOU script from v1, this field does not appear.
Downgrading Contracts
New contracts cannot be downgraded if they have a value in their Optional fields. Create a new v2 IOU contract from thev2/my-pkg directory, with USD as currency:
v1/my-pkg directory:
None. For example, create an IOU with the currency field set to None using mkIOUWithoutCurrency:
None.
Adding a Choice
SCU also allows package authors to add new choices - add the example choiceDouble to v2/my-pkg/daml/Main.daml, which archives the current contract and produces a new one with twice the value.
None, but this requires a different script function -replace the use of exerciseCmd with exerciseExactCmd in the body of doubleIOU in v1, and restart your sandbox.
mkIOUWithoutCurrency, then run doubleIOU on it from V1:
Deploying Your First Upgrade
Configuring Canton to Support Smart Upgrading
When using the feature one must be using Protocol Version 7.Using Smart Contract Upgrading Enabled Packages
Once you have finished development of your smart contract app, use the mentioned upgrade-enabled options in daml.yaml to compile and generate the related DAR. This can be uploaded using the existing gRPC endpoints without modifications and is immediately available for use.Validate the DAR Against a Running Participant Node
You can validate your DAR against the current participant node state without uploading it to the participant via thePackageManagementService.validateDar Ledger API endpoint. This allows participant node operators to first check the DAR before uploading it.
This operation is also available via the Canton Admin API and Console:
Upgrading and Package Vetting
Upgradable packages are also subject to package vetting restrictions: in to be able to use a package in Daml transactions with smart contract upgrading, it must be vetted by all participants informed about the transaction. This applies to both the packages used for creating the contracts and the target packages. Note: Package vetting is enabled by default on DAR upload operations.Continuing to Write Your Upgrades
SCU allows package authors to change many more aspects of their packages - fields can be extended in templates, choices, and data type definitions. Choice bodies can be changed, and other expressions such as key definitions and signatory lists can be changed with caveats.Setup
Continue the package defined in the Writing Your First Upgrade section above, but overwrite the v1 and v2 IOU modules. The v1 IOU module should be overwritten as follows:Changing Choices
Add the following choice,Duplicate, to both v1 and v2 versions of IOU:
dpm build should succeed without errors.
Duplicate choice by adding an optional field amount, and changing the behavior of the choice to default to a multiple of 3. Also upgrade the DuplicateResult data type to include the old value. Replace the definitions of the DuplicateResult data type and of the Duplicate choice in v2 only:
duplicateIOU in V1:
duplicateIOU should be added in V2, supplying an amount field:
duplicateIOU script with exerciseExactCmd always runs the v1 implementation for the Duplicate choice, and likewise for v2.
Modifying Signatory Definitions
Other definitions can be changed, but warnings are emitted to remind the developer that the changes can be unsafe and need to be made with care to preserve necessary invariants. Signatories and observers are one expression that can be changed. Importantly, SCU assumes that the new definition does not alter the computed values of the signatories. The computed value of the observers is allowed to change in one specific way: observers that are also signatories can be removed. Any other change to the computed value of the observers (losing a non-signatory observer, adding an observer) is not allowed. For example, add a new field of “outside observers” to the v2 IOU template, and add them to the observer definition.None for the outsideObservers field, so all existing contracts have the same observer list as before: the single owner.
In the case where a contract’s signatories or observers change in during an upgrade/downgrade in a way that doesn’t meet the constraints above, the upgrade, and thus full transaction, fails at runtime.
Upgrading Enums
Variants and enums can be extended using SCU, either by adding fields to an existing constructor, or by adding a new constructor to the end of the list. Redefine the IOU package, overwriting the v1 and v2 sections similarly to the previous section. Overwrite the IOU package in both V1 and V2 with the following:Text for the currency field, here we use an enum data-type Currency with two constructors: USD and GBP.
Running dpm build should succeed with no errors:
Currency enum.
currency field are a subset of those in a v2 contract.
For example, create an IOU with USD via v1’s mkIOU script, and query it via v2’s getIOU script:
IOU, any CHF fails to downgrade, so any v2 contracts with a CHF currency cannot be used in v1 workflows.
For example, create a contract with CHF as its currency field via v2’s mkIOU script. Attempting to query it via v1’s getIOU script fails with a lookup error for the CHF variant.
Upgrading Variants
Variants, also known as algebraic data types, are very similar to enums except that they also contain structured data. For example, the following variant has two constructors, each with unique fields. Overwrite both v1 and v2 modules with the following source:Line constructor with a len field:
sideLen field to the Polygon constructor, to specify the lengths of the sides of the polygon.
sideLen field is non-optional.
sideLen field optional fixes the error:
Limitations in Upgrading Variants
Upgrading variants has some limitations - because theCircle constructor has been defined without a field in curly braces, it cannot be upgraded with new fields.
Line constructor were as follows, it would also not be able to upgrade:
Circle constructor would be as follows:
Nested Datatypes
If a data type, choice, or template has a field which refers to another data type, the larger data type can be upgraded if the field’s data type is upgradeable. For example, given the data typeA with a field referring to data type B,
B are valid for SCU, then A is also valid.
Dependencies
Package authors may upgrade the dependencies of a package as well as the package itself. A new version of a package may add new dependencies, and must have all the (non-utility-package) dependencies of the old version. If these dependencies are used in ways that are checked for upgrades, each existing dependency must be either unchanged from the old DAR or an upgrade of its previous version. For example, given a dependencies folder containing v1, v2, and v3 of a dependency packagedep:
1.0.0 of a package main that depends on a datatype from version 2.0.0 of dep:
2.0.0 of the main package may keep its dependencies the same, or it may upgrade dep to 3.0.0:
main may not downgrade dep to version 1.0.0. The following daml.yaml would be invalid:
Upgrading Interface Instances
SCU also supports changing Interface instances. First, create a new package directorymy-iface, with my-iface/daml.yaml and module my-iface/daml/MyIface.daml:
v1/my-pkg/daml.yaml and v2/my-pkg/daml.yaml:
v1/my-pkg/daml/Main.daml and v2/my-pkg/daml/Main.daml with the following:
getValue in the HasValue instance.
Add a quantity field to the v2 IOU package, and amend the definition of getValue to use it:
HasValue. Comment out the interface instance for HasValue from v2/my-pkg/daml/Main.daml completely, then restart the sandbox and try to reupload the two versions:
HasValue interface from v2/my-pkg/daml/Main.daml instead. Then restart the sandbox and try to reupload the two versions.
exerciseExactCmd.
Upgrading Interfaces
Interface instances may be upgraded, but interface definitions cannot be upgraded. If an interface definition is present in v1 of a package, it must be removed from all subsequent versions of that package. Because interfaces definitions may not be defined in subsequent versions, any package that uses an interface definition from a dependency package can never upgrade that dependency to a new version. For this reason, it is strongly recommended that interfaces always be defined in their own packages separately from templates.Developer Workflow
This section contains suggestions on how to set up your development environment for SCU to help you iterate more quickly on your projects.Multi-Package Builds
Following the best practices outlined below and the testing recommendations leads to a proliferation of packages in your project. Use Multi-Package builds to reliably rebuild these packages as you iterate on your project. Multi-package builds also enable cross-package navigation in Daml Studio. The Multi-package builds for upgrades section goes into more detail on how to set up multi-package builds for SCU and how it can help with testing.Working with a Running Canton Instance
With SCU, it is no longer possible to iterate on a package by uploading it to a running participant after each rebuild. A participant rejects two packages with the same name and version whose content differs. There are two ways to work around this:- restart the participant after each rebuild
- change the version name of the package before each rebuild
daml.yaml file of each of your packages, append an environment variable to the name of the package:
my-package in the daml.yaml files that depend on it:
dpm build --all, increment the UNIQUE_BUILD_ID environment variable. This ensures that the build produces unique DAR files that can be uploaded to the participant without conflict.
Working with the Daml Sandbox
For the same reason, the Daml sandbox does not support hot-swapping packages. There are two ways to work around this:- restart the sandbox after each rebuild
- change the version name of your packages after each rebuild, as outlined in the previous section
The Upgrade Model in Depth - Reference
You can find the in-depth upgrading model, which can be used as a reference for valid upgrades, here.Package Selection in the Ledger API
Until the introduction of SCU, template IDs in requests to the Ledger API were all of the form<package-id>:<module-name>:<template-name>. For disambiguation, going forward, we refer to this format as by-package-id template IDs.
With SCU, we introduce a more generic template reference of the format #<package-name>:<module-name>:<template-name> (by-package-name template ID) that scopes templates by package-name, allowing version-agnostic addressing of templates on the Ledger API. It serves as a reference to all template IDs with the qualified name <module-name>:<template-name> from all packages with the name package-name known to the Ledger API server.
By-package-name template ID addressing is supported for all packages and is possible on both the write path (command submission) and read path (Ledger API queries).
The by-package-name template ID is an API-level concept of the Ledger API. Internally, Canton uses by-package-id template IDs for all operations. Therefore, when the new format is used in requests to the Ledger API, a dynamic resolution is performed as described in the following sections.
Package Preference
On processing a command submission, there are scenarios where the Ledger API server needs to resolveby-package-name template IDs to by-package-id template IDs (see dynamic-package-resolution-in-command-submission). For this purpose, the package preference concept is introduced as the mapping from package-name to package ID for all known package-names on the participant.
The package preference is needed at each command submission time and is assembled from two sources:
- The default package preference of the Ledger API server to which the command is submitted. By default, for each package name the Ledger API picks a package version known by all participants involved in the transaction, if it exists.
-
The package-id selection preference list specified in the submitted command’s package_id_selection_preference in a command submission. This is package-id resolution list explicitly provided by the client to override the default package preference mentioned above.
- See here for how to provide this in Daml-Script
- Note: The package_id_selection_preference must not lead to ambiguous resolutions for package-names, meaning that it must not contain two package-ids pointing to packages with the same package-name, as otherwise the submission will fail with an
INVALID_ARGUMENTerror.
Dynamic Package Resolution in Command Submission
Dynamic package resolution can happen in two cases during command submission:- For command submissions that use a
by-package-name template IDin the command’s templateId field (e.g. in a create command here) - For command submissions whose Daml interpretation requires the execution of interface choices or fetch-by-interface actions.
Dynamic Package Resolution in Ledger API Queries
When subscribing for transaction or active contract streams, users can now use theby-package-name template ID format in the template-id request filter field. to specify that they’re interested in fetching events for all templates pertaining to the specified package-name. This template selection set is dynamic and it widens with each uploaded template/package.
Example
Given the following packages existing on the participant node:- Package AppV1
- package-name:
app1 - package-version:
1.0.0 - template-ids:
pkgId1:mod:T
- package-name:
- Package AppV2
- package-name:
app1 - package-version:
1.1.0 - template-ids:
pkgId2:mod:T
- package-name:
#app1:mod:T, then the events stream will include events from both template-ids: pkgId1:mod:T and pkgId2:mod:T
Best Practices
To ensure that future upgrades and DAR lifecycling go smoothly, we recommend the following practices:Separate Interfaces/Exceptions from Templates
Interface and exception definitions are not upgradable. As such, if you attempt to redefine an interface or exception in version 2 of a package, even if it is unchanged, the package does not type check. Removing an interface from the version 2 package also causes issues, especially if the interface has choices. This means that template definitions that exist in the same package as interfaces and exception definitions are not upgradeable. To avoid this issue, move interface and exception definitions into a separate package such that subsequent versions of your template package all depend on the same version of the package with interfaces/exceptions. For example, a single packagemain defined as follows would not be able to upgrade, leaving the template T non-upgradeable.
-Wupgrade-interfaces flag, or ignore this error entirely with the -Wno-upgrade-interfaces flag.
The recommended way to fix this is to split the main package by redefining it as two packages, helper and main:
Avoid Contract Metadata Changes
The signatories, stakeholders, contract key, and ensure clauses of a contract should be fixed at runtime for a given contract. Changing their definitions in your Daml code is discouraged and triggers a warning from the SCU typechecker. We strongly recommend against altering the type of a key. If changing the type of a key cannot be avoided, consider using an off-ledger migration instead of SCU.Breaking Changes via Explicit Package Version
To make a breaking change to your package that is not upgrade compatible, you can change the name of your package to indicate a breaking version bump. To enable this, we recommend that your package name contains a version marker for when a breaking change occurs. For example, for your first iteration of a package, you would name itmain-v1, starting with package version 1.0.0. In this case, the v1 is part of the package name, not the package version. You could publish upgrade-compatible versions by changing the version: field from 1.0.0 to 2.0.0 to 3.0.0. These versions would all be upgrade-compatible with one another:
v1 in all three packages remains stable - this means the package name has not changed, and ensures that these three packages and their datatypes are considered by the runtime and the type checker to be upgradeable.
To make a breaking change, publish a new package version with package name main-v2. Because this package has a different package name from those with main-v1, it is not typechecked against those packages and its datatypes are not automatically converted. You would need to manually migrate values from main-v1 packages to main-v2.
Do not depend on Daml Script Packages
We recommend only depending on Daml Script packages such asdaml-script in dedicated packages for running tests written in Daml Script. These packages should not be part of your model and should not be uploaded to the ledger.
Daml Script packages are not guaranteed to be upgradeable across SDK versions. If you depend on a Daml Script datatype in a serializable position (e.g. the field of a template), your package may rely on a Daml Script package in a way that can neither be removed nor upgraded to the next SDK version. Your package and any of its SCU upgrades would be stuck on that SDK version.
Operational Design Guideline for Upgrading Daml Apps
When considering upgrading, we regard each Daml application as composed of:- On-ledger components: The Daml code running the on-ledger logic (i.e. the DARs uploaded to all participant nodes interacting with the app)
- Off-ledger components interacting with the ledger via the Ledger API or other supported Canton Ledger API clients (JSON Ledger API or PQS). These are typically Java or TypeScript services implementing off-ledger business logic.
Zero-Downtime Upgrades
Upgrading an application without operational downtime can be achieved by designing the above-mentioned components to allow:- Asynchronous rollout: Operators deploy updated software at their own pace, similar to established CI/CD practices in micro-service environments.
- Synchronous switch-over: All components switch to using updated Daml code at the same time.
-
use package names in Ledger API requests: App components interacting with the Ledger API should use
by-package-name template IDsinstead ofby-package-id template IDsin all their command submissions and queries. This allows:- Ledger API server-side version selection of the package preference for command submissions.
- reading all versions of the templates in queries, even newer versions than the one the component was developed against.
- handle missing optional fields: App components reading from the Ledger API or Ledger API clients must be prepared to handle missing optional fields in records, including those in the initial package. The TypeScript and Java codegens for reading Daml values do so by default.
-
use exhaustive package preference: on each command submission, the
package_id_selection_preferenceis set ensuring that:- The package ID of every package used in the command and of every package used by all possible interface instances is included in the package preference.
- Within an application, all submissions use the same package preference.
- Upload the upgraded DARs to the participant nodes
- Roll-out the updated off-ledger components
- A switch-over time is decided and communicated to all app clients in advance, along with the updated package preference pertaining to the application’s upgraded DARs. For example, you may encode the switch-over time in a config value set on all components; or publish it at a well-known HTTP endpoint from which all components read it.
- After the switch-over time, all Ledger API clients update their package preference and use it for subsequent command submissions.
Rolling out backwards-incompatible changes
Some changes to a Daml workflow cannot be made backwards-compatible, such as changing the definition of a template in a breaking way (e.g., extending the observer set). To handle such changes, you can replace the existing contract with an upgraded one of the target template as follows:- Introduce the target template as a new template following the Breaking changes via Explicit Package Version
- Add a consuming
OriginalTemplate_UpgradeToNewTemplatechoice to the existing template by rolling out a new version in a backwards-compatible fashion. - Where required, provide reference data for the
upgradechoice via additional choice arguments. - Implement backend automation to migrate all old contracts to the new ones by exercising the
upgradechoice on the existing contracts.
Testing
Standalone Upgradeability Checks
upgrade-check tool to perform a standalone check that all of the DARs typecheck against one another correctly as further validation of your upgraded packages.helper that does not change, and two packages main and dep.
main-2.0.0.dar for main and a new DAR dep-2.0.0.dar for dep. We would then recommend running the upgrade-check tool as follows:
Dry Run Uploading to a Test Environment
If you have a test environment with DARs that are not available to you, you may not be able to supply a complete list of DARs for your previous model to the standalonedpm upgrade-check tool.
For workflows involving multiple DARs, we recommend more robust testing by running a Canton sandbox with the same version and environment as your in-production participant and uploading all the old and new packages that constitute your Daml app.
If you want to test the validity of your package against a running ledger, but without uploading the dar, you can hit the validateDar gRPC endpoint
Daml Script Testing
Daml Script has been used for demonstrative purposes in this document, however usually the complexities of live upgrades comes with your workflows, not the data transformations themselves. You can use Daml Script (with Canton) to test some isolated simple cases, but for thorough tests of you system using SCU, you should prefer full Workflow Testing. We recommend placing your Daml Script tests in a separate package which depends on all versions of your business logic when testing your upgrades with Daml Script. This testing package should not be uploaded to the ledger if possible, as it depends on thedaml-script package. This package emits a warning on the participant when uploaded, as it serves no purpose on a participant, cannot be fully removed (as with any package), and may not be uploadable to the ledger in future versions (Daml 3). More information about this limitation here.
Depending on multiple versions of the same package does however face ambiguity issues with imports. You can resolve these issues using module prefixes:
- Run your existing test suite for V1 but updated to call V2 choices. This can be done with a rewrite, or by passing down a package preference and calling the test with both the V1 and V2 package ID.
-
Assuming your data change affects a template payload, write separate setup code for V1 and V2, populating new fields
setupV1 : Script V1TestDatasetupV2 : Script V2TestDataThese new data types should mostly just hold Contract IDs -
Update your existing test suite from V1 to take a package preference, allowing the V2 implementation without additional fields to choices to be called.
testV1 : [PackageId] -> V1TestData -> Script () - Run the above test suite against V1 data, passing a V1 preference, then a V2 preference. This ensures your changes haven’t broken any existing workflows.
-
Next write tests for your new/modified workflows, using the V2 choice implementations. This does not need a package preference.
testV2 : V2TestData -> Script () -
Run these tests against both the V1 setup and the V2 setup, to ensure your new workflows support existing/old templates.
In order to do this, you’ll need some way to upcast your
V1TestData, i.e.upcastTestData : V1TestData -> V2TestDataThis function should mostly just callcoerceContractIdon any contract IDs, and fill in anyNonevalues if needed. - Finally, you can cover any workflows that require the contract data to already be upgraded (new fields populated), these are written entirely in V2 without any special considerations.
Multi-package builds for upgrades
upgrades-example template.
- A package
upgraded-iou-interfaces, which defines an interfaceAssetand a viewtypeAsset.View. - The first version (1.0.0) of a package
upgraded-iou-main, which defines a templateIOUwith instance ofupgraded-iou-interfaces:Main.Asset. - The second version (2.0.0) of
upgraded-iou-mainwhich upgrades the first. It adds a newdescriptionfield toIOU, and uses it (when the field is defined) in an upgraded implementation ofAsset. - A testing package
upgraded-iou-test, which depends on bothupgraded-iou-main-1.0.0andupgraded-iou-main-2.0.0. It defines a script which exercises v1.0.0 and v2.0.0IOUs via theirAssetinterface. - A script
run-test.sh, which runs the main test inupgraded-iou-test. - A
multi-package.yamlfile which lists our four packages.
main-v2 in an incompatible way but did not recompile it, the test package would still compile successfully against the previous DAR for main-v2.
./run-test.sh script automatically rebuilds all DARs in the package that need to be rebuilt:
dpm build --all always recompile stale dependencies and DARs in order. This ensures a fully up-to-date package environment before running ./run-test.sh.
Workflow Testing
While testing your workflows is application-specific, we still recommend at least one test for your core workflows that follows this pattern:- Start your app using version 2.0 of your DAR, but only upload version 1.0.
- Initialize the app and start one instance of every core workflow.
- Upload version 2.0 of your DAR.
- Switch your backends to start using version 2.0, ideally this should be a flag.
- Validate that the core workflows are in the same state and advance them to check that they are not stuck.
SCU Support in Daml Tooling
Codegen
Generated code uses package names in place of package IDs in template IDs. Retrieved data from the ledger is subject to the upgrade transformations described in previous sections. Concretely, this is implemented as follows:Java
The classes that are generated for each template and interface contain aTEMPLATE_ID field, which, for upgradable packages, now use a package name rather than a package ID. To help you determine the package ID of these packages, we have added a new PACKAGE_ID field to all such classes. Upgradable packages also have PACKAGE_NAME and PACKAGE_VERSION fields.
If you need to identify a template by the specific package ID of the DAR from which the code was generated, you can use the TEMPLATE_ID_WITH_PACKAGE_ID field, which is on all generated classes and their companion objects. When submitting commands you may also use the packageIdSelectionPreference to explicitly specify which package ID(s) you want the ledger to use to interpret the commands. Note that using these options prevents your application from seamlessly supporting upgrades.
TypeScript
ThetemplateId field on generated template classes has been updated to use the package name as the package qualifier for upgrade-compatible packages. This is used for command submission and queries. However, note that queries return the package qualifier with the package ID rather than the package name. Generated modules now also give the package “reference”, which is the package name for upgrade-compatible packages; for other packages it is the package ID.
To perform package ID-qualified commands/queries in an upgrade compatible package, create a copy of the template object using the following:
JSON Ledger API
Packages can be addressed both by package ID and by package name in the interaction with the JSON Ledger API. Note: template IDs in query results always use a package ID. This allows you to distinguish the source of a particular contract. This means that if you use a template with a package name in the request, you can no longer expect the template IDs in the result to exactly match the input template ID. Package ID selection preference: preferences apply to JSON Ledger API where you can specify your preferred selection of package versions.PQS
To match the package-name changes to the Ledger API, PQS has changed how packages are selected for queries. All queries that take a Daml identity in the form<package-id>:<module-name>:<template-name> now take a package name in place of package ID. Note that this differs from the Ledger API in that the \# prefix is not required for PQS, as PQS has dropped direct package ID queries. Queries for package names return all versions of a given contract, alongside the package version and package ID for each contract.
SELECT \* FROM active('my_package:My.App:MyTemplate') WHERE package_id = 'my_package_id'Daml Shell
Daml Shell builds on PQS by providing a shell interface to inspect the ledger using package name to create an integrated view of all versions of contracts.Daml-Script
All commands and queries in this version of Daml Script now use upgrades/downgrades automatically to ensure that they exercise the correct versions of choices and return correct payloads. The following additional functionality is available for more advanced uses of SCU. Exact commands Each of the four submission commands now has an “exact” variant:createExactCmd, exerciseExactCmd, exerciseByKeyExactCmd, and createAndExerciseExactCmd. These commands force the participant to use the exact version of the package that your script uses, so you can be certain of the choice code you are calling. Note that exact and non-exact commands can be mixed in the same submission.
submitWithOptions : SubmitOptions -> Commands a -> Script a. You can build SubmitOptions by combining the actAs and packagePreference functions with <>.
The full list of builders for SubmitOptions is as follows:
PackageId can be hard-coded in your script, in which case it must be updated whenever the package changes. Otherwise, it can be provided using the --input-file flag of the dpm script command line tool.
The following example demonstrates reading the package ID from a DAR and passing it to a script: