Manage DARs and Packages¶
A package is a unit of compiled Daml code corresponding to one Daml project. A DAR is a collection of packages including a main package and all other packages from the dependencies of this Daml project.
Uploading DARs¶
To use a Daml application on a participant, you need to upload it to your participant node. The application always comes packaged as one or more DARs that need to be uploaded in the order of their dependency. There are two ways to upload DARs to a Canton node: either via the Ledger Api, or through Canton console command:
@ participant2.dars.upload("dars/CantonExamples.dar")
res1: String = "12202102be83f3489f0f3df0d42d54280e6769eb173a8fee8eb02b20f82e1ef5d709"
Inspecting DARs and Packages¶
You can get a list of uploaded DARs using:
@ participant2.dars.list()
res2: Seq[com.digitalasset.canton.participant.admin.v0.DarDescription] = Vector(
DarDescription(
hash = "12207f38b04c40dd9cf5bc1daa97625427665af959801fc77024dafd5898cb01f01b",
name = "AdminWorkflowsWithVacuuming"
),
DarDescription(
hash = "12202102be83f3489f0f3df0d42d54280e6769eb173a8fee8eb02b20f82e1ef5d709",
name = "CantonExamples"
)
)
Please note that the package “AdminWorkflows” is a package that ships with Canton. It contains the Daml templates
used by the participant.health.ping
command.
In order to inspect the contents of the DAR, you need to grab the hash identifying it:
@ val dars = participant2.dars.list(filterName = "CantonExamples")
dars : Seq[com.digitalasset.canton.participant.admin.v0.DarDescription] = Vector(
DarDescription(
hash = "12202102be83f3489f0f3df0d42d54280e6769eb173a8fee8eb02b20f82e1ef5d709",
name = "CantonExamples"
)
)
@ val hash = dars.head.hash
hash : String = "12202102be83f3489f0f3df0d42d54280e6769eb173a8fee8eb02b20f82e1ef5d709"
Using that hash, you can inspect the contents of the DAR using:
@ val darContent = participant2.dars.list_contents(hash)
darContent : DarMetadata = DarMetadata(
name = "CantonExamples",
main = "7e0aa3d819f2dfd20c1ba08ac133130e1f135172e5c0f4cf0c8a70d79d7b5a84",
packages = Vector(
"7e0aa3d819f2dfd20c1ba08ac133130e1f135172e5c0f4cf0c8a70d79d7b5a84",
"6851f194e144b693e63e9034b956c76cef6b5088dd8c66a657ab652a204dba2b",
"cb0552debf219cc909f51cbb5c3b41e9981d39f8f645b1f35e2ef5be2e0b858a",
"3f4deaf145a15cdcfa762c058005e2edb9baa75bb7f95a4f8f6f937378e86415",
..
You can also directly look at the packages, using:
@ participant2.packages.list()
res6: Seq[com.digitalasset.canton.participant.admin.v0.PackageDescription] = Vector(
PackageDescription(
packageId = "88cf65277201722eda8564ac04494bed23ac79673722af482c6f3fb37aec83ed",
sourceDescription = "AdminWorkflowsWithVacuuming"
),
PackageDescription(
packageId = "86828b9843465f419db1ef8a8ee741d1eef645df02375ebf509cdc8c3ddd16cb",
sourceDescription = "CantonExamples"
..
Please note that a DAR can include packages that are already included in other DARs. In particular the Daml standard library
is shipped with every DAR. Therefore, the sourceDescription
will always contain only one textual reference to a DAR.
You can also inspect the content of a package, using:
@ participant2.packages.list_contents(darContent.main)
res7: Seq[com.digitalasset.canton.participant.admin.v0.ModuleDescription] = Vector(
ModuleDescription(name = "CantonExamples"),
ModuleDescription(name = "ContractKeys"),
ModuleDescription(name = "SafePaint"),
ModuleDescription(name = "LockIou"),
ModuleDescription(name = "Iou"),
ModuleDescription(name = "Divulgence"),
ModuleDescription(name = "Paint"),
..
Understanding Package Vetting¶
Every participant operator uploads DARs individually to their participant node. There is no global DAR repository anywhere and participants do not have access to each others DAR repositories. Instead, participants publicly declare towards their peers that they are ready to receive transactions referring to certain packages. This is done by Canton package state topology transactions which can be seen by all the participants connected to a specific sync domain. Therefore, for two participants to synchronize a transaction that uses packages contained in a certain DAR, both participant operators must upload and vet the same DAR and its contained packages before the transaction is submitted.
If the DAR has not been uploaded to one of the involved participants or it has not been vetted, then transaction submissions are rejected with a
NO_DOMAIN_FOR_SUBMISSION
error (with additional metadata listing which package has not been vetted on which participant).
Transactions are valid only if all involved participants have vetted the used packages. This helps prohibiting attacks from malicious participants, who could send a transaction referring to a package the receiver does not have. Such a transaction would be impossible for the receiver to process, leading to a ledger fork.
DAR vetting lifecycle¶
As mentioned above, a participant can start accepting transactions that reference packages in a DAR after it has been uploaded and vetted.
Vetting happens automatically when you upload a DAR, although it can be disabled by a request flag.
@ val darHash = participant1.dars.upload("dars/CantonExamples.dar", vetAllPackages = false)
darHash : String = "12202102be83f3489f0f3df0d42d54280e6769eb173a8fee8eb02b20f82e1ef5d709"
The packages contained in a DAR can also be vetted explicitly when the DAR upload operation was performed without vetting
@ participant1.dars.vetting.enable(darHash)
After uploading and vetting, you can create a contract referencing a template from the main package of the DAR:
@ val mainPackageId = participant1.packages.find("Iou").head.packageId
mainPackageId : String = "7e0aa3d819f2dfd20c1ba08ac133130e1f135172e5c0f4cf0c8a70d79d7b5a84"
@ participant1.domains.connect_local(mydomain)
@ val createIouCmd = ledger_api_utils.create(mainPackageId,"Iou","Iou",Map("payer" -> participant1.adminParty,"owner" -> participant1.adminParty,"amount" -> Map("value" -> 100.0, "currency" -> "EUR"),"viewers" -> List()))
..
@ participant1.ledger_api.commands.submit(Seq(participant1.adminParty ), Seq(createIouCmd))
res13: com.daml.ledger.api.v1.transaction.TransactionTree = TransactionTree(
transactionId = "1220659388dfd31751452e0af913535d8c26e9176e0caf23185fd7b7f825276bb1e6",
commandId = "bb2732c7-64d4-4fec-8696-d491b1574206",
workflowId = "",
effectiveAt = Some(
..
You can unvet a DAR, effectively preventing its use in Daml transactions.
@ participant1.dars.vetting.disable(darHash)
Note
Unvetting a DAR is a supported and safe operation on participants running protocol version 7 and above. Usage of this operation in production environments is not advised on previous protocol versions.
Any subsequent commands attempting to create or exercise choices on contracts for the referenced package IDs are rejected.
@ participant1.ledger_api.commands.submit(Seq(participant1.adminParty), Seq(createIouCmd))
ERROR com.digitalasset.canton.integration.EnterpriseEnvironmentDefinition$$anon$3 - Request failed for participant1.
GrpcRequestRefusedByServer: FAILED_PRECONDITION/NO_DOMAIN_FOR_SUBMISSION(9,67d333be): No valid domain for submission found.
Request: SubmitAndWaitTransactionTree(
actAs = participant1::1220c1317037...,
readAs = Seq(),
commandId = '',
workflowId = '',
submissionId = '',
deduplicationPeriod = None(),
applicationId = 'CantonConsole',
commands = ...
)
CorrelationId: 67d333bed94be4929c64661a19003178
Context: HashMap(participant -> participant1, test -> PackageDarManagementDocumentationIntegrationTest, domainsNotUsed -> Map(mydomain::12202c2a92d4... -> The topology state for some packages does not meet the requirements on domain mydomain::12202c2a92d4...: [(Participant PAR::participant1::1220c1317037... has not vetted 7e0aa3d819f2...)]), tid -> 67d333bed94be4929c64661a19003178, definite_answer -> false)
Command BaseLedgerApiAdministration$ledger_api$commands$.submit invoked from cmd10000036.sc:1
If the decision to support the DAR changes, it can be re-vetted:
@ participant1.dars.vetting.enable(darHash)
@ participant1.ledger_api.commands.submit(Seq(participant1.adminParty), Seq(createIouCmd))
res16: com.daml.ledger.api.v1.transaction.TransactionTree = TransactionTree(
transactionId = "1220f808522edffc3410e69d2aa48eb299f3a345995b0951a1a522251bc205efec6a",
commandId = "ff9298e6-b78c-4ba6-8f18-c65779a58457",
workflowId = "",
effectiveAt = Some(
..
What if a package is vetted multiple times?¶
You can’t unvet a DAR whose main package is referenced from another vetted DAR. For example, if you upload a DAR that depends on the “CantonExamples” DAR and try to unvet the latter, the operation fails
@ val examplesDependencyDarHash = participant1.dars.upload("dars/CantonExamplesDependency.dar")
examplesDependencyDarHash : String = "1220ee0bfbe921a7f88b376b9b35c74d9ff18a4aab9ceacf877b8e1880281518ef7a"
@ participant1.dars.vetting.disable(darHash)
ERROR com.digitalasset.canton.integration.EnterpriseEnvironmentDefinition$$anon$3 - Request failed for participant1.
GrpcRequestRefusedByServer: FAILED_PRECONDITION/MAIN_DAR_PACKAGE_REFERENCED_EXTERNALLY(9,e170807f): Operation DAR disabling for DAR 'CantonExamples' failed due to its main package with packageId=7e0aa3d819f2dfd20c1ba08ac133130e1f135172e5c0f4cf0c8a70d79d7b5a84 being vetted multiple times
Request: UnvetDar(12202102be83f3489f0f3df0d42d54280e6769eb173a8fee8eb02b20f82e1ef5d709,true)
CorrelationId: e170807f92749b94aa7a5c8a552a4804
Context: HashMap(participant -> participant1, test -> PackageDarManagementDocumentationIntegrationTest, darDescription -> CantonExamples, operationName -> DAR disabling, mainPackageId -> 7e0aa3d819f2dfd20c1ba08ac133130e1f135172e5c0f4cf0c8a70d79d7b5a84, tid -> e170807f92749b94aa7a5c8a552a4804)
Command ParticipantAdministration$dars$vetting$.disable invoked from cmd10000044.sc:1
Instead, you should first unvet the “CantonExamplesDependency” DAR, which contains as package dependencies the main package of the “CantonExamples”,
@ participant1.dars.vetting.disable(examplesDependencyDarHash)
then you can safely unvet the “CantonExamples” DAR as well.
@ participant1.dars.vetting.disable(darHash)
Advanced vetting concepts¶
Note
This section concentrates on lower-level details of package topology state and commands. For most use cases, the high-level vetting APIs mentioned above are sufficient. Using lower-level topology APIs can lead to inconsistencies in the participant’s topology state; it should be used only by experts.
Package topology states¶
With respect to a participant, a package can be in one of the following states:
- Not found: The package does not exist in the local participant stores and cannot be referenced in any request to the participant node.
- Unknown: The package may exist in the local participant stores, but it has no associated topology transaction issued by the participant node (i.e. it is unknown topology-wise). A package pertaining to a DAR uploaded with the vetting flag off is unknown.
- Check-only: The package appears in a CheckOnlyPackages topology transaction and it allows a participant to announce that a collection of Daml packages is known, but it can only be used to validate preexisting contracts on the ledger, not for executing new Daml transactions (e.g. contract creates or exercises using Daml code defined in the package). This concept has been introduced in protocol version 7 to support Smart contract upgrades.
- Vetted: A package in this state appears at least in a VettedPackages topology transaction and allows the participant to accept new transactions that reference it in Daml action nodes. This state is unchanged from the previous protocol versions.
For a DAR that is unknown (topology-wise), the vetting operations (participant.dars.vetting.enable(darHash)
or PackageService.vetDar
) results in a VettedPackages topology transaction referencing all the packages in the DAR.
Note
The unit of Daml code for which a Canton participant can issue topology transactions is the Daml package.
However, in practice, to ensure that transactions can be successfully submitted using any package of a specific DAR,
the Canton vetting APIs act at the DAR level (i.e. for all of a DAR’s packages, transactionally).
The APIs in question are the console commands: participant.dars.vetting.enable
and participant.dars.vetting.disable
as well
as the Admin API calls: PackageService.vetDar
and PackageService.unvetDar
.
To illustrate the point, please vet the example DAR again:
@ participant1.dars.vetting.enable(darHash)
Now, check that the DAR’s main package-id appears in a VettedPackages topology transaction:
@ participant1.topology.vetted_packages.list().exists(_.item.packageIds.contains(mainPackageId))
res21: Boolean = true
Note
When a DAR is vetted, if it exists, the CheckOnlyPackages topology transaction is eventually removed, as a VettedPackages topology transaction already implies that the referenced packages can be used for validating the preexisting ledger contracts. This operation is asynchronous and does not block the vetting API call.
Now, unvet the DAR:
@ participant1.dars.vetting.disable(darHash)
And you can observe that the package ID appears only in a CheckOnlyPackages topology transaction:
@ participant1.topology.vetted_packages.list().exists(_.item.packageIds.contains(mainPackageId))
res23: Boolean = false
@ participant1.topology.check_only_packages.list().exists(_.item.packageIds.contains(mainPackageId))
res24: Boolean = true
Commands toggling between the two vetting states effectively issue two topology operations:
- On vetting enable: A VettedPackages topology transaction addition and the removal of the corresponding CheckOnlyPackages topology transaction
- On vetting disable: A CheckOnlyPackages topology transaction addition and the removal of the VettedPackages topology transaction
Forcefully unvetting a package¶
In some cases, you might want to circumvent the high-level vetting APIs and directly issue or revoke package topology transactions. One such example is when a package is referenced in multiple topology transactions (e.g. the package is contained in multiple vetted DARs uploaded on the participant) and it cannot be automatically unvetted (as exemplified in the What if a package is vetted multiple times?).
To build the example, re-enable vetting for both the CantonExamples and the CantonExamplesDependency DARs.
@ participant1.dars.vetting.enable(examplesDependencyDarHash)
@ participant1.dars.vetting.enable(darHash)
To mark the main package of the CantonExamples DAR as check-only, remove it from all the VettedPackages topology transactions it appears in using the low-level topology management API.
First, identify the topology transactions containing the package to remove:
@ val txsContainingMainPackage = participant1.topology.vetted_packages.list(filterStore = "Authorized", filterParticipant = participant1.id.filterString).filter(_.item.packageIds.contains(mainPackageId))
txsContainingMainPackage : Seq[ListVettedPackagesResult] = Vector(
ListVettedPackagesResult(
context = BaseResult(
domain = "Authorized",
validFrom = 2025-01-09T12:49:35.660209Z,
validUntil = None,
operation = Add,
serialized = <ByteString@74635c96 size=2450 contents="\n\217\023\n\275\020\n\270\020\n\265\020\022 pq2G2mKLZMB05jYDi6GljSDXTmuQ8FUfJ...">,
signedBy = 1220c1317037...
),
item = VettedPackages(
participant = participant1::1220c1317037...,
packages = Seq(
28cf6c194d92...,
7e0aa3d819f2...,
6851f194e144...,
..
Then, replace the VettedPackages transactions with ones that do not refer to the main package.
@ import com.digitalasset.canton.LfPackageId
@ txsContainingMainPackage.foreach { tx => participant1.topology.vetted_packages.authorize(TopologyChangeOp.Remove,participant1.id,tx.item.packageIds,force = true); participant1.topology.vetted_packages.authorize(TopologyChangeOp.Add,participant1.id,tx.item.packageIds.filterNot(_ == mainPackageId),force = true)}
Ensure that the package becomes check-only by issuing a dedicated CheckOnlyPackages topology transaction.
@ participant1.topology.check_only_packages.authorize(TopologyChangeOp.Add, participant1.id, Seq(LfPackageId.assertFromString(mainPackageId)), force = true)
..