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 = "1220ede89a6886830f2a95bca895e341f93cb8fb18eebdec3793197a014c006ff9e7"

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 = "1220ede89a6886830f2a95bca895e341f93cb8fb18eebdec3793197a014c006ff9e7",
    name = "CantonExamples"
  ),
  DarDescription(
    hash = "122030db65331447efdbde79f3108049286b72de2da07f820777b190830fd0f80597",
    name = "AdminWorkflowsWithVacuuming"
  )
)

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 = "1220ede89a6886830f2a95bca895e341f93cb8fb18eebdec3793197a014c006ff9e7",
    name = "CantonExamples"
  )
)
@ val hash = dars.head.hash
hash : String = "1220ede89a6886830f2a95bca895e341f93cb8fb18eebdec3793197a014c006ff9e7"

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 = "6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b",
  packages = Vector(
    "6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b",
    "a566728bb2d4ad0103eb11ff8140296f4cea4fc94f1f95ddc6c3e4f983d107f1",
    "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 = "489fbbf60f7b06d24bf8a9334294d009d04a6c392e18d71c29e7282d1bb59b41",
    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 are 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. Therefore, for two participants to synchronise on a transaction that uses packages contained in a certain DAR, we need both participant operators to have uploaded the same DAR before the transaction was submitted.

If one of the involved participants doesn’t know about a certain DAR, then the transaction will bounce with an error NO_DOMAIN_FOR_SUBMISSION (with additional metadata listing which package has not been vetted on which participant).

This error goes back to the fact that both participants not only upload the DAR, but also publicly declare towards their peers that they are ready to receive transactions referring to certain packages. This declaration happens automatically when you upload a DAR. The package vettings can be inspected using (preview):

@ participant2.topology.vetted_packages.list()
res8: Seq[ListVettedPackagesResult] = Vector(
  ListVettedPackagesResult(
    context = BaseResult(
      domain = "Authorized",
      validFrom = 2024-01-24T09:14:37.059334Z,
      validUntil = None,
      operation = Add,
      serialized = <ByteString@4b53953e size=2582 contents="\n\223\024\n\301\021\n\274\021\n\271\021\022 eXShZXjUkyBXOUWMSt2QqCDaANju5AOqJ...">,
..

Vetting is necessary, as otherwise, a malicious participant might send a transaction referring to package a receiver does not have, which would make it impossible for the receiver to process the transaction, leading to a ledger fork. As transactions are valid only if all involved participants have vetted the used packages, this attack cannot happen.

Removing Packages and DARs

Note

Note that package and DAR removal is under active development. The behaviour described in this documentation may change in the future. Package and DAR removal is a preview feature and should not be used in production.

Canton supports removal of both packages and DARs that are no longer in use. Removing unused packages and DARs has the following advantages:

  • Freeing up storage
  • Preventing accidental use of the old package / DAR
  • Reducing the number of packages / DARs that are trusted and may potentially have to be audited

Certain conditions must to be met in order to remove packages or DARs. These conditions are designed to prevent removal of packages or DARs that are currently in use. The rest of this page describes the requirements.

Removing DARs

The following checks are performed before a DAR can be removed:

  • The main package of the DAR must be unused – there should be no active contract from this package
  • All package dependencies of the DAR should either be unused or contained in another of the participant node’s uploaded DARs. Canton uses this restriction to ensure that the package dependencies of the DAR don’t become “stranded” if they’re in use.
  • The main package of the dar should not be vetted. If it is vetted, Canton will try to automatically revoke the vetting for the main package of the DAR, but this automatic vetting revocation will only succeed if the main package vetting originates from a standard dars.upload. Even if the automatic revocation fails, you can always manually revoke the package vetting.

The following tutorial shows how to remove a DAR with the Canton console. The first step is to upload a DAR so that we have one to remove. Additionally, store the packages that are present before the DAR is uploaded, as these can be used to double-check that DAR removal reverts to a clean state.

@ val packagesBefore = participant1.packages.list().map(_.packageId).toSet
packagesBefore : Set[String] = HashSet(
  "a447ef961654eb5130b1aa6905ff49bc02065108e2962c93d771bc5ec02511ac",
  "5921708ce82f4255deb1b26d2c05358b548720938a5a325718dc69f381ba47ff",
  "cc348d369011362a5190fe96dd1f0dfbc697fdfd10e382b9e9666f0da05961b7",
  "6839a6d3d430c569b2425e9391717b44ca324b88ba621d597778811b2d05031d",
  "99a2705ed38c1c26cbb8fe7acf36bbf626668e167a33335de932599219e0a235",
  "e22bce619ae24ca3b8e6519281cb5a33b64b3190cc763248b4c3f9ad5087a92c",
  "d58cf9939847921b2aab78eaa7b427dc4c649d25e6bee3c749ace4c3f52f5c97",
  "6c2c0667393c5f92f1885163068cd31800d2264eb088eb6fc740e11241b2bf06",
  "489fbbf60f7b06d24bf8a9334294d009d04a6c392e18d71c29e7282d1bb59b41",
..
@ val darHash = participant1.dars.upload("dars/CantonExamples.dar")
darHash : String = "1220ede89a6886830f2a95bca895e341f93cb8fb18eebdec3793197a014c006ff9e7"

If the DAR hash is unknown, it can be found using dars.list:

@ val darHash_ = participant1.dars.list().filter(_.name == "CantonExamples").head.hash
darHash_ : String = "1220ede89a6886830f2a95bca895e341f93cb8fb18eebdec3793197a014c006ff9e7"

The DAR can then be removed with the following command:

@ participant1.dars.remove(darHash)

Note that, right now, DAR removal will only remove the main packages associated with the DAR:

@ val packageIds = participant1.packages.list().filter(_.sourceDescription == "CantonExamples").map(_.packageId)
packageIds : Seq[String] = Vector(
  "86828b9843465f419db1ef8a8ee741d1eef645df02375ebf509cdc8c3ddd16cb",
  "cc348d369011362a5190fe96dd1f0dfbc697fdfd10e382b9e9666f0da05961b7",
  "e491352788e56ca4603acc411ffe1a49fefd76ed8b163af86cf5ee5f4c38645b",
  "cb0552debf219cc909f51cbb5c3b41e9981d39f8f645b1f35e2ef5be2e0b858a",
  "38e6274601b21d7202bb995bc5ec147decda5a01b68d57dda422425038772af7",
  "99a2705ed38c1c26cbb8fe7acf36bbf626668e167a33335de932599219e0a235",
  "a566728bb2d4ad0103eb11ff8140296f4cea4fc94f1f95ddc6c3e4f983d107f1",
  "f20de1e4e37b92280264c08bf15eca0be0bc5babd7a7b5e574997f154c00cb78",
  "8a7806365bbd98d88b4c13832ebfa305f6abaeaf32cfa2b7dd25c4fa489b79fb",
..

It’s possible to remove each of these manually, using package removal. There is a complication here that packages needed for admin workflows (e.g. the Ping command) cannot be removed, so these are skipped.

@ packageIds.filter(id => ! packagesBefore.contains(id)).foreach(id => participant1.packages.remove(id))

The following command verifies that all the packages have been removed.

@ val packages = participant1.packages.list().map(_.packageId).toSet
packages : Set[String] = HashSet(
  "a447ef961654eb5130b1aa6905ff49bc02065108e2962c93d771bc5ec02511ac",
  "5921708ce82f4255deb1b26d2c05358b548720938a5a325718dc69f381ba47ff",
  "cc348d369011362a5190fe96dd1f0dfbc697fdfd10e382b9e9666f0da05961b7",
  "6839a6d3d430c569b2425e9391717b44ca324b88ba621d597778811b2d05031d",
  "99a2705ed38c1c26cbb8fe7acf36bbf626668e167a33335de932599219e0a235",
  "e22bce619ae24ca3b8e6519281cb5a33b64b3190cc763248b4c3f9ad5087a92c",
  "d58cf9939847921b2aab78eaa7b427dc4c649d25e6bee3c749ace4c3f52f5c97",
  "6c2c0667393c5f92f1885163068cd31800d2264eb088eb6fc740e11241b2bf06",
  "489fbbf60f7b06d24bf8a9334294d009d04a6c392e18d71c29e7282d1bb59b41",
..
@ assert(packages == packagesBefore)

The following sections explain what happens when the DAR removal operation goes wrong, for various reasons.

Main package of the DAR is in use

The first step to illustrate this is to upload a DAR and create a contract using the main package of the DAR:

@ val darHash = participant1.dars.upload("dars/CantonExamples.dar")
darHash : String = "1220ede89a6886830f2a95bca895e341f93cb8fb18eebdec3793197a014c006ff9e7"
@ val packageId = participant1.packages.find("Iou").head.packageId
packageId : String = "6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b"
@ participant1.domains.connect_local(mydomain)
@ val createIouCmd = ledger_api_utils.create(packageId,"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))
res21: com.daml.ledger.api.v1.transaction.TransactionTree = TransactionTree(
  transactionId = "1220d8e4d451253593c827a5dd65b7bb6c8afa581ea985efaee0c2facafa76ee510c",
  commandId = "84a71ad8-3205-45cc-8d6d-17e6aa051129",
  workflowId = "",
  effectiveAt = Some(
..

Now that a contract exists using the main package of the DAR, a subsequent DAR removal operation will fail:

@ participant1.dars.remove(darHash)
ERROR com.digitalasset.canton.integration.EnterpriseEnvironmentDefinition$$anon$3 - Request failed for participant1.
  GrpcRequestRefusedByServer: FAILED_PRECONDITION/PACKAGE_OR_DAR_REMOVAL_ERROR(9,4439e1ed): The DAR DarDescriptor(SHA-256:ede89a688683...,CantonExamples) cannot be removed because its main package 6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b is in-use by contract ContractId(0016e05e4a8ab7c172daec069c4d58558f5419978921c116e1cce6c781f164b3f2ca0212207cf2f124c166bfc52a876568d9c656e273217b852ad94dec23a8b235f82c68ce)
on domain mydomain::122062941b19....
  Request: RemoveDar(1220ede89a6886830f2a95bca895e341f93cb8fb18eebdec3793197a014c006ff9e7)
  CorrelationId: 4439e1ed8e1efc6870a8e297b26a0408
  Context: HashMap(participant -> participant1, test -> PackageDarManagementDocumentationIntegrationTest, pkg -> 6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b, tid -> 4439e1ed8e1efc6870a8e297b26a0408)
  Command ParticipantAdministration$dars$.remove invoked from cmd10000056.sc:1

In order to remove the DAR, we must archive this contract. Note that the contract ID for this contract can also be found in the error message above.

@ val iou = participant1.ledger_api.acs.find_generic(participant1.adminParty, _.templateId.isModuleEntity("Iou", "Iou"))
iou : com.digitalasset.canton.admin.api.client.commands.LedgerApiTypeWrappers.WrappedCreatedEvent = WrappedCreatedEvent(
  event = CreatedEvent(
    eventId = "#1220d8e4d451253593c827a5dd65b7bb6c8afa581ea985efaee0c2facafa76ee510c:0",
    contractId = "0016e05e4a8ab7c172daec069c4d58558f5419978921c116e1cce6c781f164b3f2ca0212207cf2f124c166bfc52a876568d9c656e273217b852ad94dec23a8b235f82c68ce",
    templateId = Some(
      value = Identifier(
        packageId = "6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b",
        moduleName = "Iou",
        entityName = "Iou"
      )
..
@ val archiveIouCmd = ledger_api_utils.exercise("Archive", Map.empty, iou.event)
..
@ participant1.ledger_api.commands.submit(Seq(participant1.adminParty), Seq(archiveIouCmd))
res24: com.daml.ledger.api.v1.transaction.TransactionTree = TransactionTree(
  transactionId = "1220563e642033dfae734cce135696e7777c31c1f4084b3395ab9fabd2385211ca1c",
  commandId = "6df995fe-0f2a-439d-9b78-acd62d348cc1",
  workflowId = "",
  effectiveAt = Some(
..

The DAR removal operation will now succeed.

@ participant1.dars.remove(darHash)

Main package of the DAR can’t be automatically removed

Similarly, DAR removal may fail because the DAR can’t be automatically removed. To illustrate this, upload the DAR without automatic vetting and subsequently vet all the packages manually.

@ val darHash = participant1.dars.upload("dars/CantonExamples.dar", vetAllPackages = false)
darHash : String = "1220ede89a6886830f2a95bca895e341f93cb8fb18eebdec3793197a014c006ff9e7"
@ import com.daml.lf.data.Ref.IdString.PackageId
@ val packageIds = participant1.packages.list().filter(_.sourceDescription == "CantonExamples").map(_.packageId).map(PackageId.assertFromString)
packageIds : Seq[PackageId] = Vector(
  "86828b9843465f419db1ef8a8ee741d1eef645df02375ebf509cdc8c3ddd16cb",
  "cc348d369011362a5190fe96dd1f0dfbc697fdfd10e382b9e9666f0da05961b7",
..
@ participant1.topology.vetted_packages.authorize(TopologyChangeOp.Add, participant1.id, packageIds)
res29: com.google.protobuf.ByteString = <ByteString@50a746b5 size=2384 contents="\n\315\022\n\373\017\n\366\017\n\363\017\022 P3brn6mEct6984YAk0xGQOYyiZzSxOP2J...">

The DAR removal operation will now fail:

@ participant1.dars.remove(darHash)
ERROR com.digitalasset.canton.integration.EnterpriseEnvironmentDefinition$$anon$3 - Request failed for participant1.
  GrpcRequestRefusedByServer: FAILED_PRECONDITION/PACKAGE_OR_DAR_REMOVAL_ERROR(9,f734010d): An error was encountered whilst trying to unvet the DAR DarDescriptor(SHA-256:ede89a688683...,CantonExamples) with main package 6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b for DAR removal. Details: IdentityManagerParentError(Mapping(VettedPackages(
  participant = participant1::1220bb70b384...,
  packages = Seq(
    6c727740f131...,
    a566728bb2d4...,
    cb0552debf21...,
    3f4deaf145a1...,
    86828b984346...,
    f20de1e4e37b...,
    76bf0fd12bd9...,
    38e6274601b2...,
    d58cf...
  Request: RemoveDar(1220ede89a6886830f2a95bca895e341f93cb8fb18eebdec3793197a014c006ff9e7)
  CorrelationId: f734010d922e648ccd55c9b9fcd4f672
  Context: Map(participant -> participant1, tid -> f734010d922e648ccd55c9b9fcd4f672, test -> PackageDarManagementDocumentationIntegrationTest)
  Command ParticipantAdministration$dars$.remove invoked from cmd10000076.sc:1

The DAR can be successfully removed after manually revoking the vetting for the main package:

@ participant1.topology.vetted_packages.authorize(TopologyChangeOp.Remove, participant1.id, packageIds, force = true)
res30: com.google.protobuf.ByteString = <ByteString@1b27ccd0 size=2386 contents="\n\317\022\n\375\017\n\370\017\n\365\017\b\001\022 P3brn6mEct6984YAk0xGQOYyiZzSxOP...">
@ participant1.dars.remove(darHash)

Note that a force flag is needed used to revoke the package vetting; throughout this tutorial force will be used whenever a package vetting is being removed. See topology.vetted_packages.authorize for more detail.

Removing Packages

Canton also supports removing individual packages, giving the user more fine-grained control over the system. Packages can be removed if the package satisfies the following two requirements:

  • The package must be unused. This means that there shouldn’t be an active contract corresponding to the package.
  • The package must not be vetted. This means there shouldn’t be an active vetting transaction corresponding to the package.

The following tutorial shows how to remove a package using the Canton console. The first step is to upload and identify the package ID for the package to be removed.

@ val darHash = participant1.dars.upload("dars/CantonExamples.dar")
darHash : String = "1220ede89a6886830f2a95bca895e341f93cb8fb18eebdec3793197a014c006ff9e7"
@ val packageId = participant1.packages.find("Iou").head.packageId
packageId : String = "6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b"

Package removal will initially fail as, by default, uploading the DAR will add a vetting transaction for the package:

@ participant1.packages.remove(packageId)
ERROR com.digitalasset.canton.integration.EnterpriseEnvironmentDefinition$$anon$3 - Request failed for participant1.
  GrpcRequestRefusedByServer: FAILED_PRECONDITION/PACKAGE_OR_DAR_REMOVAL_ERROR(9,2aa29e3f): Package 6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b is currently vetted and available to use.
  Request: RemovePackage(6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b,false)
  CorrelationId: 2aa29e3fdfea14c7a0674d465366b07d
  Context: Map(participant -> participant1, tid -> 2aa29e3fdfea14c7a0674d465366b07d, test -> PackageDarManagementDocumentationIntegrationTest)
  Command ParticipantAdministration$packages$.remove invoked from cmd10000087.sc:1

The vetting transaction must be manually revoked:

@ val packageIds = participant1.topology.vetted_packages.list().map(_.item.packageIds).filter(_.contains(packageId)).head
packageIds : Seq[com.digitalasset.canton.package.LfPackageId] = Vector(
  "6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b",
  "a566728bb2d4ad0103eb11ff8140296f4cea4fc94f1f95ddc6c3e4f983d107f1",
..
@ participant1.topology.vetted_packages.authorize(TopologyChangeOp.Remove, participant1.id, packageIds, force = true)
res35: com.google.protobuf.ByteString = <ByteString@3726f38a size=2386 contents="\n\317\022\n\375\017\n\370\017\n\365\017\b\001\022 sjeGWZcBYu8uNpzwTlZpCO6Fmi2Ldtp...">

And then the package can be removed:

@ participant1.packages.remove(packageId)

Package is in use

The operations above will fail if the package is in use. To illustrate this, first re-upload the package (uploading the associated DAR will work):

@ val darHash = participant1.dars.upload("dars/CantonExamples.dar")
darHash : String = "1220ede89a6886830f2a95bca895e341f93cb8fb18eebdec3793197a014c006ff9e7"

Then create a contract using the package:

@ val createIouCmd = ledger_api_utils.create(packageId,"Iou","Iou",Map("payer" -> participant1.adminParty,"owner" -> participant1.adminParty,"amount" -> Map("value" -> 100.0, "currency" -> "EUR"),"viewers" -> List()))
createIouCmd : com.daml.ledger.api.v1.commands.Command = Command(
  command = Create(
    value = CreateCommand(
      templateId = Some(
        value = Identifier(
..
@ participant1.ledger_api.commands.submit(Seq(participant1.adminParty), Seq(createIouCmd))
res39: com.daml.ledger.api.v1.transaction.TransactionTree = TransactionTree(
  transactionId = "12202ae90f4e527270de9f973d08b5be3faa65110c450e4fcf523603860509239a1f",
  commandId = "fbedd27c-26b9-4e30-b6a3-02ffe271a847",
  workflowId = "",
  effectiveAt = Some(
    value = Timestamp(
      seconds = 1706087690L,
      nanos = 298449000,
      unknownFields = UnknownFieldSet(fields = Map())
    )
..

In this situation, the package cannot be removed:

@ participant1.packages.remove(packageId)
ERROR com.digitalasset.canton.integration.EnterpriseEnvironmentDefinition$$anon$3 - Request failed for participant1.
  GrpcRequestRefusedByServer: FAILED_PRECONDITION/PACKAGE_OR_DAR_REMOVAL_ERROR(9,f7770f19): Package 6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b is currently in-use by contract ContractId(0094750c676d33cc659c7ed855567d05728019f2db87b106ce619f70d50258709cca021220016a5d02719757d435411860bed362ca080f67772cf3cc647bc6bbe795b5c71c) on domain mydomain::122062941b19.... It may also be in-use by other contracts.
  Request: RemovePackage(6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b,false)
  CorrelationId: f7770f197cc1c91dc7321f4c4221c9ba
  Context: HashMap(participant -> participant1, test -> PackageDarManagementDocumentationIntegrationTest, domain -> mydomain::122062941b19..., pkg -> 6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b, tid -> f7770f197cc1c91dc7321f4c4221c9ba, contract -> ContractId(0094750c676d33cc659c7ed855567d05728019f2db87b106ce619f70d50258709cca021220016a5d02719757d435411860bed362ca080f67772cf3cc647bc6bbe795b5c71c))
  Command ParticipantAdministration$packages$.remove invoked from cmd10000103.sc:1

To remove the package, first archive the contract:

@ val iou = participant1.ledger_api.acs.find_generic(participant1.adminParty, _.templateId.isModuleEntity("Iou", "Iou"))
iou : com.digitalasset.canton.admin.api.client.commands.LedgerApiTypeWrappers.WrappedCreatedEvent = WrappedCreatedEvent(
  event = CreatedEvent(
    eventId = "#12202ae90f4e527270de9f973d08b5be3faa65110c450e4fcf523603860509239a1f:0",
    contractId = "0094750c676d33cc659c7ed855567d05728019f2db87b106ce619f70d50258709cca021220016a5d02719757d435411860bed362ca080f67772cf3cc647bc6bbe795b5c71c",
    templateId = Some(
      value = Identifier(
        packageId = "6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b",
        moduleName = "Iou",
        entityName = "Iou"
      )
..
@ val archiveIouCmd = ledger_api_utils.exercise("Archive", Map.empty, iou.event)
archiveIouCmd : com.daml.ledger.api.v1.commands.Command = Command(
  command = Exercise(
    value = ExerciseCommand(
      templateId = Some(
        value = Identifier(
          packageId = "6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b",
          moduleName = "Iou",
          entityName = "Iou"
        )
      ),
..
@ participant1.ledger_api.commands.submit(Seq(participant1.adminParty), Seq(archiveIouCmd))
res42: com.daml.ledger.api.v1.transaction.TransactionTree = TransactionTree(
  transactionId = "12207b60c14d88b2fe93aa7dcacb98f977ef1c07ac7966a8e097f2d2a94163b8eea9",
  commandId = "cc047186-b379-41d5-b55d-dc91819ff85e",
  workflowId = "",
  effectiveAt = Some(
    value = Timestamp(
      seconds = 1706087690L,
      nanos = 689022000,
      unknownFields = UnknownFieldSet(fields = Map())
    )
  ),
  offset = "00000000000000000c",
..

Then revoke the package vetting transaction:

@ val packageIds = participant1.topology.vetted_packages.list().map(_.item.packageIds).filter(_.contains(packageId)).head
packageIds : Seq[com.digitalasset.canton.package.LfPackageId] = Vector(
  "6c727740f131655be00b7b047bca14685a8b10a060b465b8ec2835a89ed80c6b",
  "a566728bb2d4ad0103eb11ff8140296f4cea4fc94f1f95ddc6c3e4f983d107f1",
..
@ participant1.topology.vetted_packages.authorize(TopologyChangeOp.Remove, participant1.id, packageIds, force = true)
res44: com.google.protobuf.ByteString = <ByteString@7de00b96 size=2386 contents="\n\317\022\n\375\017\n\370\017\n\365\017\b\001\022 VCDchjMgKNfrP643extH2TJlW07EdWf...">

The package removal operation should now succeed.

@ participant1.packages.remove(packageId)

Force-removing packages

Packages can also be forcibly removed, even if the conditions above are not satisfied. This is done by setting the force flag to true.

To experiment with this, first re-upload the DAR so the package becomes available again:

@ participant1.dars.upload("dars/CantonExamples.dar")
res46: String = "1220ede89a6886830f2a95bca895e341f93cb8fb18eebdec3793197a014c006ff9e7"

Then force-remove the package:

@ participant1.packages.remove(packageId, force = true)

Please note, this is a dangerous operation. Forced removal of packages should be avoided whenever possible.