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 = "1220554fc51a396bb4e66a69f381f1a2b7737e3ba2cb5571226e8227cc2ef62d4c60"

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 = "1220554fc51a396bb4e66a69f381f1a2b7737e3ba2cb5571226e8227cc2ef62d4c60",
    name = "CantonExamples"
  ),
  DarDescription(
    hash = "12205055a57d60c08b16bd9f1368ecc982d486e6364ac727405336da641260bf45af",
    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 = "1220554fc51a396bb4e66a69f381f1a2b7737e3ba2cb5571226e8227cc2ef62d4c60",
    name = "CantonExamples"
  )
)
@ val hash = dars.head.hash
hash : String = "1220554fc51a396bb4e66a69f381f1a2b7737e3ba2cb5571226e8227cc2ef62d4c60"

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 = "49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646",
  packages = Vector(
    "49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646",
    "94f7c83923aa273fb90b907f57385ae951ca1195fb35806275cbd03e9e4faab6",
    "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 = "86828b9843465f419db1ef8a8ee741d1eef645df02375ebf509cdc8c3ddd16cb",
    sourceDescription = "CantonExamples"
  ),
  PackageDescription(
    packageId = "cc348d369011362a5190fe96dd1f0dfbc697fdfd10e382b9e9666f0da05961b7",
    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 PACKAGE_NO_VETTED_BY_RECIPIENTS.

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 = 2023-02-08T12:47:40.111708Z,
      validUntil = None,
      operation = Add,
      serialized = <ByteString@20c9081b size=2384 contents="\n\315\022\n\373\017\n\366\017\n\363\017\022 9n2qaVYoAXOQ8C8pTQFgHHZPl6igFwMCJ...">,
..

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 removed. 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(
  "86828b9843465f419db1ef8a8ee741d1eef645df02375ebf509cdc8c3ddd16cb",
  "cc348d369011362a5190fe96dd1f0dfbc697fdfd10e382b9e9666f0da05961b7",
  "6839a6d3d430c569b2425e9391717b44ca324b88ba621d597778811b2d05031d",
  "99a2705ed38c1c26cbb8fe7acf36bbf626668e167a33335de932599219e0a235",
  "e22bce619ae24ca3b8e6519281cb5a33b64b3190cc763248b4c3f9ad5087a92c",
  "d58cf9939847921b2aab78eaa7b427dc4c649d25e6bee3c749ace4c3f52f5c97",
  "df5c44a8f35d5964dd3912c24579a7ae308b8a376e7302ab383fe9fe1a9d4e30",
  "c1f1f00558799eec139fb4f4c76f95fb52fa1837a5dd29600baa1c8ed1bdccfd",
  "733e38d36a2759688a4b2c4cec69d48e7b55ecc8dedc8067b815926c917a182a",
..
@ val darHash = participant1.dars.upload("dars/CantonExamples.dar")
darHash : String = "1220554fc51a396bb4e66a69f381f1a2b7737e3ba2cb5571226e8227cc2ef62d4c60"

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 = "1220554fc51a396bb4e66a69f381f1a2b7737e3ba2cb5571226e8227cc2ef62d4c60"

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",
  "f20de1e4e37b92280264c08bf15eca0be0bc5babd7a7b5e574997f154c00cb78",
  "8a7806365bbd98d88b4c13832ebfa305f6abaeaf32cfa2b7dd25c4fa489b79fb",
  "bfcd37bd6b84768e86e432f5f6c33e25d9e7724a9d42e33875ff74f6348e733f",
..

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(
  "86828b9843465f419db1ef8a8ee741d1eef645df02375ebf509cdc8c3ddd16cb",
  "cc348d369011362a5190fe96dd1f0dfbc697fdfd10e382b9e9666f0da05961b7",
  "6839a6d3d430c569b2425e9391717b44ca324b88ba621d597778811b2d05031d",
  "99a2705ed38c1c26cbb8fe7acf36bbf626668e167a33335de932599219e0a235",
  "e22bce619ae24ca3b8e6519281cb5a33b64b3190cc763248b4c3f9ad5087a92c",
  "d58cf9939847921b2aab78eaa7b427dc4c649d25e6bee3c749ace4c3f52f5c97",
  "df5c44a8f35d5964dd3912c24579a7ae308b8a376e7302ab383fe9fe1a9d4e30",
  "c1f1f00558799eec139fb4f4c76f95fb52fa1837a5dd29600baa1c8ed1bdccfd",
  "733e38d36a2759688a4b2c4cec69d48e7b55ecc8dedc8067b815926c917a182a",
..
@ 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 = "1220554fc51a396bb4e66a69f381f1a2b7737e3ba2cb5571226e8227cc2ef62d4c60"
@ val packageId = participant1.packages.find("Iou").head.packageId
packageId : String = "49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646"
@ 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 = "122015705d49f9a68f6a26b6df64c9e43a8ecbef722593be317a8982060b167d0bbe",
  commandId = "8a3e2ef2-92f3-4040-b589-b70d2421bf9d",
  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,5cc45345): The DAR DarDescriptor(SHA-256:554fc51a396b...,CantonExamples) cannot be removed because its main package 49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646 is in-use by contract ContractId(001c16aae3b528d1eab493cc2c19b6f6d1e448fc69a028d89a9b3e1944112f2d08ca011220fc36949d11eca7d0a85d18078c56bda68d365739843f21acdab380e9dc05cf1b)
on domain mydomain::1220c7e6f3b9....
  Request: RemoveDar(1220554fc51a396bb4e66a69f381f1a2b7737e3ba2cb5571226e8227cc2ef62d4c60)
  CorrelationId: 5cc4534500ed578719c566580fdc8c46
  Context: Map(participant -> participant1, test -> PackageDarManagementDocumentationIntegrationTest, pkg -> 49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646)
  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 == "Iou.Iou")
iou : com.digitalasset.canton.admin.api.client.commands.LedgerApiTypeWrappers.WrappedCreatedEvent = WrappedCreatedEvent(
  event = CreatedEvent(
    eventId = "#122015705d49f9a68f6a26b6df64c9e43a8ecbef722593be317a8982060b167d0bbe:0",
    contractId = "001c16aae3b528d1eab493cc2c19b6f6d1e448fc69a028d89a9b3e1944112f2d08ca011220fc36949d11eca7d0a85d18078c56bda68d365739843f21acdab380e9dc05cf1b",
    templateId = Some(
      value = Identifier(
        packageId = "49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646",
        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 = "122096df751472a4238a7c975ddf9ca8c6965ffc82b373be3d6356492acf12d287a4",
  commandId = "8bf2b002-a2ee-44c0-a505-38549315e5c5",
  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 = "1220554fc51a396bb4e66a69f381f1a2b7737e3ba2cb5571226e8227cc2ef62d4c60"
@ 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@5369779a size=2186 contents="\n\207\021\n\265\016\n\260\016\n\255\016\022 sqoWWYWId2Y1TCQxUMqIuUrikef01fV1J...">

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,abc93045): An error was encountered whilst trying to unvet the DAR DarDescriptor(SHA-256:554fc51a396b...,CantonExamples) with main package 49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646 for DAR removal. Details: IdentityManagerParentError(Mapping(VettedPackages(
  participant = participant1::122066a9c22a...,
  packages = Seq(
    49db2a1ba6eb...,
    94f7c83923aa...,
    cb0552debf21...,
    3f4deaf145a1...,
    86828b984346...,
    f20de1e4e37b...,
    76bf0fd12bd9...,
    38e6274601b2...,
    d58cf...
  Request: RemoveDar(1220554fc51a396bb4e66a69f381f1a2b7737e3ba2cb5571226e8227cc2ef62d4c60)
  CorrelationId: abc93045183aee905d5996504f71b360
  Context: Map(participant -> participant1, 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@3fb54468 size=2188 contents="\n\211\021\n\267\016\n\262\016\n\257\016\b\001\022 sqoWWYWId2Y1TCQxUMqIuUrikef01fV...">
@ 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 = "1220554fc51a396bb4e66a69f381f1a2b7737e3ba2cb5571226e8227cc2ef62d4c60"
@ val packageId = participant1.packages.find("Iou").head.packageId
packageId : String = "49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646"

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,f48d8217): Package 49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646 is currently vetted and available to use.
  Request: RemovePackage(49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646,false)
  CorrelationId: f48d8217c6255b41bae883a2df09606e
  Context: Map(participant -> participant1, 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(
  "49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646",
  "94f7c83923aa273fb90b907f57385ae951ca1195fb35806275cbd03e9e4faab6",
..
@ participant1.topology.vetted_packages.authorize(TopologyChangeOp.Remove, participant1.id, packageIds, force = true)
res35: com.google.protobuf.ByteString = <ByteString@596b4245 size=2188 contents="\n\211\021\n\267\016\n\262\016\n\257\016\b\001\022 NNx2BD1T4GsSsra9v7cX0hEvCUS1wff...">

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 = "1220554fc51a396bb4e66a69f381f1a2b7737e3ba2cb5571226e8227cc2ef62d4c60"

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 = "1220416db1675f4c756244cc4d5738011ca570202025d3647ee062231ede97d48c8e",
  commandId = "1e7fbd11-95bf-4266-b735-c6b60b025cf4",
  workflowId = "",
  effectiveAt = Some(
    value = Timestamp(
      seconds = 1675860473L,
      nanos = 464517000,
      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,fddd9577): Package 49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646 is currently in-use by contract ContractId(00b28086ab1037cb4e9cb5d1565806b341f583d5d736a759a51fe80e99f39bb206ca011220fd3b0717340400ca8120a5b1e3f7219c260e9ea6ec3f22095000f869c0c8d598) on domain mydomain::1220c7e6f3b9.... It may also be in-use by other contracts.
  Request: RemovePackage(49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646,false)
  CorrelationId: fddd9577fe1a183004870826379c3020
  Context: HashMap(participant -> participant1, test -> PackageDarManagementDocumentationIntegrationTest, domain -> mydomain::1220c7e6f3b9..., pkg -> 49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646, contract -> ContractId(00b28086ab1037cb4e9cb5d1565806b341f583d5d736a759a51fe80e99f39bb206ca011220fd3b0717340400ca8120a5b1e3f7219c260e9ea6ec3f22095000f869c0c8d598))
  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 == "Iou.Iou")
iou : com.digitalasset.canton.admin.api.client.commands.LedgerApiTypeWrappers.WrappedCreatedEvent = WrappedCreatedEvent(
  event = CreatedEvent(
    eventId = "#1220416db1675f4c756244cc4d5738011ca570202025d3647ee062231ede97d48c8e:0",
    contractId = "00b28086ab1037cb4e9cb5d1565806b341f583d5d736a759a51fe80e99f39bb206ca011220fd3b0717340400ca8120a5b1e3f7219c260e9ea6ec3f22095000f869c0c8d598",
    templateId = Some(
      value = Identifier(
        packageId = "49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646",
        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 = "49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646",
          moduleName = "Iou",
          entityName = "Iou"
        )
      ),
..
@ participant1.ledger_api.commands.submit(Seq(participant1.adminParty), Seq(archiveIouCmd))
res42: com.daml.ledger.api.v1.transaction.TransactionTree = TransactionTree(
  transactionId = "12203974c758f91cd9a9766e04956bf5a45adddc52b7eefb75e0710af5c7edcd029e",
  commandId = "854a51aa-ff7e-4c8e-946c-b9a8d28eb396",
  workflowId = "",
  effectiveAt = Some(
    value = Timestamp(
      seconds = 1675860473L,
      nanos = 951103000,
      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(
  "49db2a1ba6eb4bdd6b9d297518337bc35f4c9df266d6b6395b874a0c30f6e646",
  "94f7c83923aa273fb90b907f57385ae951ca1195fb35806275cbd03e9e4faab6",
..
@ participant1.topology.vetted_packages.authorize(TopologyChangeOp.Remove, participant1.id, packageIds, force = true)
res44: com.google.protobuf.ByteString = <ByteString@795a2289 size=2188 contents="\n\211\021\n\267\016\n\262\016\n\257\016\b\001\022 bcW4qw8aMAgNEcEyn8DAoLEh1vK6iav...">

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 = "1220554fc51a396bb4e66a69f381f1a2b7737e3ba2cb5571226e8227cc2ef62d4c60"

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.