How does template vetting work in practise?

Hi everyone,
Reading the documentation and checking the DAML material I cannot get my head around template vetting and the whole process of introducing new templates to participants.

From my initial impression in order to use Daml templates on a participant, the Dar must first be uploaded and then vetted by the participant. Vetting will ensure that other participants can check whether they can actually send a transaction referring to a particular Daml package and participant.

How does the vetting process works from this point onward? Is the domain node (sequencer + mediator) involved in any way? Would another participant node with a specific party on it deal with the “signing” logic?

Sorry for the broad and thank you in advance for the help - loving DAML so far!

7 Likes

Hi @francescobenintende , delighted to hear you are enjoying Daml, and welcome to the forums!

Package vetting is really a simple propose-accept pattern that can be done out of band, or in case of Canton, over the ledger. Let’s say Alice has developed a new application she wants to share with Bob.

First, she deploys the application to her participant node via daml ledger upload-dar (or alternatively via dars.upload of the administrative shell.

Now she shares her application code - her DAR file - with Bob. The DAR file contains the source code of the main package, the compiled binaries, and meta-information. These can be inspected using daml damlc inspect-dar, or the DAR file can be unzipped. Daml compilation is deterministic within an SDK version so Bob can also recompile the source to verify that it matches the binary. Now Bob uploads the DAR file to his participant the same way.

To do the sharing over the ledger, the participant administrative shell has commands to propose and accept DAR sharing requests, as well as a whitelist function to allow other participants to share packages automatically and unilaterally.

5 Likes

Hi @bernhard, when sharing over the ledger - is the domain involved? if so, what is their involvement?

2 Likes

Generally, you don’t have to do anything manually with respect to vetting. If you upload a Dar to a Canton participant node, the node will automatically create the vetting transactions. These transactions are “topology transactions” that get automatically added to the topology state.

During transaction processing, the submitting and validating participants will check for each part of the Daml transaction that all involved participants have vetted the respective transactions. If not, then the transaction is rejected as invalid:

https://www.canton.io/docs/dev/user-manual/usermanual/error_codes.html#package-no-vetted-by-recipients

Generally, a submitting participant will notice during transaction assembly that some participants have not vetted the tx and fail the submission.

If the validating participants reject it, it means that the submitter was maliciously trying to build a transaction with participants that do not accept the package.

1 Like

Same involvement as for any Daml transaction. The sharing workflows are implemented in Daml. You can get the source code of the sharing model to vet it (so on topic!) as follows:

  1. Download Canton
  2. Unzip the JAR file in the lib directory
  3. Unzip AdminWorkflows.dar inside the dar directory inside the JAR.
  4. Look at DarDistribution.daml inside that.
3 Likes

@Ratko_Veprek I think we are talking about different things with respect to vetting.

I’m talking about the DAR sharing process and human node operators vetting packages to decide whether to allow them to be run on their nodes.

I think you are talking about a process under the hood that happens when a node operator activates (uploads or accepts) a package, and registers the fact that the package is vetted with the topology manager.

1 Like

Thank you guys this is incredibly helpful.

What I am interested in is this process (:arrow_up:), i.e. how do I create new templates, share them and ensure that the relevant participants have the correct templates to use.

Regarding the operation side, in a real world setting would you have a management participant node which role is to issue DAR sharing proposal to other participants?
For example, my application pushing an update would issue a proposal to the relevant participants. They then would inspect and accept the new templates and upload them to their participant nodes?

In this setting anyone in that domain could do the same right? Is there a way of exclusively permissioning a specific party in a domain to be able to issue DAR sharing requests?

No, you can’t prevent participants from sending out requests. But you can ignore or reject them. So a valid setup may be that all participants whitelist one node that is agreed on as the package issuer. Sharing requests from that node are then auto-accepted. All other requests can be ignored.

2 Likes

Perfect thank you for all the details - much clearer!

Hi @bernhard,

I was looking for information in regards to this but couldn’t find any… I tried to use the dar sharing template(DarDistribution:ShareDar@65921e553a353588e950cbc87e98a127730e63295f7ad8d3adae952ef0133b3e) via Navigator but it doesn’t see to do much. Did I miss anything or is there any doco I can follow?

To do the sharing over the ledger, the participant administrative shell has commands to propose and accept DAR sharing requests , as well as a whitelist function to allow other participants to share packages automatically and unilaterally.

I just found the followings in the receiver’s canton console. Looks like it cannot accept the dar shared. The Canton and daml pacakge version are 2.6.3

@ ERROR c.d.c.p.l.a.c.LedgerConnection$$anon$1$$anon$2:participant=participant2/admin-dar-distribution - Fatally failed to handle transaction
java.lang.IllegalArgumentException: Illegal base64 character 20
	at java.base/java.util.Base64$Decoder.decode0(Base64.java:788)
	at java.base/java.util.Base64$Decoder.decode(Base64.java:564)
	at java.base/java.util.Base64$Decoder.decode(Base64.java:587)
	at com.digitalasset.canton.participant.admin.DarDistributionService.decode(DarDistributionService.scala:343)
	at com.digitalasset.canton.participant.admin.DarDistributionService.processShareCreated(DarDistributionService.scala:189)
	at com.digitalasset.canton.participant.admin.DarDistributionService.$anonfun$processTransaction$1(DarDistributionService.scala:167)
	at scala.collection.immutable.Vector1.map(Vector.scala:1911)
	at scala.collection.immutable.Vector1.map(Vector.scala:377)
	at com.digitalasset.canton.participant.admin.DarDistributionService.processTransaction(DarDistributionService.scala:167)
	at com.digitalasset.canton.participant.admin.AdminWorkflowServices.$anonfun$createService$3(AdminWorkflowServices.scala:254)
	at com.digitalasset.canton.participant.admin.AdminWorkflowServices.$anonfun$createService$3$adapted(AdminWorkflowServices.scala:253)
	at com.digitalasset.canton.tracing.Spanning.withSpan(Spanning.scala:62)
	at com.digitalasset.canton.tracing.Spanning.withSpan$(Spanning.scala:29)
	at com.digitalasset.canton.participant.admin.AdminWorkflowServices.withSpan(AdminWorkflowServices.scala:52)
	at com.digitalasset.canton.participant.admin.AdminWorkflowServices.$anonfun$createService$1(AdminWorkflowServices.scala:253)
	at com.digitalasset.canton.participant.admin.AdminWorkflowServices.$anonfun$createService$1$adapted(AdminWorkflowServices.scala:252)
	at akka.stream.impl.fusing.Map$$anon$1.onPush(Ops.scala:52)
	at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:542)
	at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:423)
	at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:650)
	at akka.stream.impl.fusing.ActorGraphInterpreter$SimpleBoundaryEvent.execute(ActorGraphInterpreter.scala:61)
	at akka.stream.impl.fusing.ActorGraphInterpreter$SimpleBoundaryEvent.execute$(ActorGraphInterpreter.scala:57)
	at akka.stream.impl.fusing.ActorGraphInterpreter$BatchingActorInputBoundary$OnNext.execute(ActorGraphInterpreter.scala:104)
	at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:625)
	at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:800)
	at akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:818)
	at akka.actor.Actor.aroundReceive(Actor.scala:537)
	at akka.actor.Actor.aroundReceive$(Actor.scala:535)
	at akka.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:716)
	at akka.actor.ActorCell.receiveMessage(ActorCell.scala:580)
	at akka.actor.ActorCell.invoke(ActorCell.scala:548)
	at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:270)
	at akka.dispatch.Mailbox.run(Mailbox.scala:231)
	at com.daml.executors.QueueAwareExecutorService$TrackingRunnable.run(QueueAwareExecutorService.scala:98)
	at com.daml.metrics.InstrumentedExecutorServiceMetrics$InstrumentedExecutorService$InstrumentedRunnable.run(InstrumentedExecutorServiceMetrics.scala:202)
	at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1425)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1016)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1665)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1598)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
WARN  c.d.c.p.a.AdminWorkflowServices:participant=participant2 - ledger subscription for admin service [com.digitalasset.canton.participant.admin.DarDistributionService@70f776a8] has completed with error
java.lang.IllegalArgumentException: Illegal base64 character 20
	at java.base/java.util.Base64$Decoder.decode0(Base64.java:788)
	at java.base/java.util.Base64$Decoder.decode(Base64.java:564)
	at java.base/java.util.Base64$Decoder.decode(Base64.java:587)
	at com.digitalasset.canton.participant.admin.DarDistributionService.decode(DarDistributionService.scala:343)
	at com.digitalasset.canton.participant.admin.DarDistributionService.processShareCreated(DarDistributionService.scala:189)
	at com.digitalasset.canton.participant.admin.DarDistributionService.$anonfun$processTransaction$1(DarDistributionService.scala:167)
	at scala.collection.immutable.Vector1.map(Vector.scala:1911)
	at scala.collection.immutable.Vector1.map(Vector.scala:377)
	at com.digitalasset.canton.participant.admin.DarDistributionService.processTransaction(DarDistributionService.scala:167)
	at com.digitalasset.canton.participant.admin.AdminWorkflowServices.$anonfun$createService$3(AdminWorkflowServices.scala:254)
	at com.digitalasset.canton.participant.admin.AdminWorkflowServices.$anonfun$createService$3$adapted(AdminWorkflowServices.scala:253)
	at com.digitalasset.canton.tracing.Spanning.withSpan(Spanning.scala:62)
	at com.digitalasset.canton.tracing.Spanning.withSpan$(Spanning.scala:29)
	at com.digitalasset.canton.participant.admin.AdminWorkflowServices.withSpan(AdminWorkflowServices.scala:52)
	at com.digitalasset.canton.participant.admin.AdminWorkflowServices.$anonfun$createService$1(AdminWorkflowServices.scala:253)
	at com.digitalasset.canton.participant.admin.AdminWorkflowServices.$anonfun$createService$1$adapted(AdminWorkflowServices.scala:252)
	at akka.stream.impl.fusing.Map$$anon$1.onPush(Ops.scala:52)
	at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:542)
	at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:423)
	at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:650)
	at akka.stream.impl.fusing.ActorGraphInterpreter$SimpleBoundaryEvent.execute(ActorGraphInterpreter.scala:61)
	at akka.stream.impl.fusing.ActorGraphInterpreter$SimpleBoundaryEvent.execute$(ActorGraphInterpreter.scala:57)
	at akka.stream.impl.fusing.ActorGraphInterpreter$BatchingActorInputBoundary$OnNext.execute(ActorGraphInterpreter.scala:104)
	at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:625)
	at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:800)
	at akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:818)
	at akka.actor.Actor.aroundReceive(Actor.scala:537)
	at akka.actor.Actor.aroundReceive$(Actor.scala:535)
	at akka.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:716)
	at akka.actor.ActorCell.receiveMessage(ActorCell.scala:580)
	at akka.actor.ActorCell.invoke(ActorCell.scala:548)
	at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:270)
	at akka.dispatch.Mailbox.run(Mailbox.scala:231)
	at com.daml.executors.QueueAwareExecutorService$TrackingRunnable.run(QueueAwareExecutorService.scala:98)
	at com.daml.metrics.InstrumentedExecutorServiceMetrics$InstrumentedExecutorService$InstrumentedRunnable.run(InstrumentedExecutorServiceMetrics.scala:202)
	at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1425)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1016)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1665)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1598)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)