Daml Engineering

Parties and users in Daml 2.0

Written by Moritz Kiefer | Mar 16, 2022

With the introduction of Canton in Daml 2.0, party ids look and behave differently in both the local Sandbox during development and in production ledgers like the Daml Driver for PostgreSQL 2.0. This post provides an overview of the changes, as well as practical guidance on how to handle the new party in your own applications.

Parties in Daml 1.18

First, let's recap how parties worked in SDK 1.18 in Sandbox, the Daml Driver for PostgreSQL 1.0, and the VMware Blockchain. Parties can be fully user controlled via the party id hint, which can be specified on party allocation, e.g., via Daml Script’s allocatePartyWithHint. The call allocatePartyWithHint “Alice” (PartyIdHint “alice”) allocates a party with display name Alice and the party id alice. If a party with the id alice already exists, the allocation fails. This has a few implications:

  • You can allocate the same party id even if you restart your ledger.

  • Party ids are human-readable (provided you choose human-readable party id hints). For example, following the user Bob in create-daml-app is easy, because their party id is also Bob.

In addition, Sandbox (but not the Daml Driver for SQL 1.x) implicitly allocates parties. This means that if you create a contract with Bob as an observer or signatory, a party with id Bob will be created implicitly even if it has not been allocated before.

Parties in Daml 2.0

With the introduction of Canton in Daml 2.0 and a Canton-based Sandbox, parties have changed significantly. When you try to allocate a party with a given hint Alice, you will now get back a party id like Alice::1220f2fe29866fd6a0009ecc8a64ccdc09f1958bd0f801166baaee469d1251b2eb72. The prefix before the double colon corresponds to the hint specified on party allocation. If the hint is not specified, it defaults to party-${randomUUID}. The suffix is the fingerprint of the public key that can authorize topology transactions for this party. Keys are generated randomly, so the suffix will look different locally and every time you restart Sandbox, you will get a different party id. This has a few implications:

  • You can no longer allocate a party with a fixed party id. While you have some control over the prefix, we recommend not to rely on that (more on this below).

  • Party ids are no longer easily understandable by humans. This means you might want to display something else in user interfaces.

  • Discovering the party id of other users might get tricky. For example, to follow the user Bob, I cannot assume that their party id is Bob.

The role of party id hints and display names

Party id hints and display names which existed in SDK 1.18.0 are still available in SDK 2.0.0. We recommend against relying on display names for new applications, but if you are migrating your existing application, they function exactly as before.

Party id hints, on the other hand, still serve a purpose. While we recommend against parsing party ids and extracting the hint, for debugging and during development it can be helpful to see  the party id hint at the beginning. Bear in mind that different parties can be allocated to different participants with the same party id hint. The full party ids will be different due to the suffix, but the party id hint would be the same.

The second use for party id hints is to avoid duplicated party allocation. Consider sending a party allocation request that fails due to a network error. The client now has no way of knowing whether the party has been allocated. Because a party allocation will be rejected if a party with the given hint already exists, the client can safely send the same request with the same hint, which will either allocate a party if the previous request failed or fail itself. Note that while this works for Canton, including Sandbox as well as the VMWare blockchain, it is not part of the ledger API specifications, so other ledgers might behave differently.

Authorization and user management

Daml 2.0 also introduced user management. User management allows you to create users on a participant that are associated with a primary party and a dynamic set of actAs and readAs claims. Crucially, the user id can be fully controlled when creating a user – unlike party ids – and are unique on a single participant. You can also use the user id in authorization tokens instead of party tokens that have specific parties in actAs and readAs fields. This means your IAM, which can sometimes be limited in configurability, only has to work with fixed user ids.

However, users are purely local to a given participant. You cannot refer to users or parties associated with a given user on another participant via their user id. You also need admin claims to interact with the user management endpoint for users other than your own. This means that while you can have a user id in place of the primary party of your own user, you cannot generally replace party ids with user ids.

Working with parties

So how do you handle these unwieldy party ids? The primary rule is to treat them as opaque identifiers. In particular, don’t parse them, don’t make assumptions about their format, and don’t try to turn arbitrary strings into party ids. Instead, the only way to get a new party id is as the result of a party allocation. Applications should never hardcode specific parties. Instead either accept them as inputs or read them from contract or choice arguments.

To illustrate this, we’ll go over the tools in the SDK and how this affects them.

Daml script

In Daml script, allocateParty returns the party id that has been allocated. This party can then be used later, for example, in command submissions. When your script should refer to parties that have been allocated outside of the current script, accept those parties as arguments and pass them in via --input-file. Similarly, if your script allocates parties and you want to refer to them outside of the script, either in a later script or somewhere else, you can store them via --output-file. You can also query the party management and user management endpoints and get access to parties that way. Keep in mind though, this requires admin rights on a participant and there are no uniqueness guarantees for display names. That usually makes querying party and user management endpoints usually only an option for development, and we recommend passing parties as arguments where possible instead.

Daml triggers

To start a trigger via the trigger service, you still have to supply the party ids for the actAs and readAs claims for your trigger. This could, for example, come from a party allocation in a Daml script that you wrote to a file via Daml Script’s --output-file. Within your trigger, you get access to those parties via getActAs and getReadAs. To refer to other parties, for example when creating a contract, reference them from an existing contract. If there is no contract, consider creating a special configuration template that lists the parties your trigger should interact with outside of your trigger, and query for that template in your trigger to get access to the parties.

Navigator

Navigator presents you with the list of user ids on the participant as login options. Once logged in, you will interact with the ledger as the primary party of that user. Any field that expects a party provides autocompletion, so if you know the prefix (by having chosen the hint), you don’t have to remember the suffix. In addition, party ids have been shortened in the Navigator UI so that not all of the id is shown. Clicking on a party identifier will copy the full identifier to the system clipboard, making it easier to use elsewhere.

Java bindings

When writing an application using the Java bindings, we recommend that you pass parties as arguments. This can either be CLI arguments or JVM properties as used in the quickstart-java example.

create-daml-app and UIs

Create-daml-app and UIs in general are a bit more complex. First, they often need to interact with an IAM during the login. Second, it is often important to have human-readable names in a UI — to go back to an earlier example, a user wants to follow Bob without typing a very long party id.

Logging in is going to depend on your specific IAM, but there are a few common patterns. In create-daml-app, you log in by typing your user id directly and then interacting with the primary party of that user. In an authorized setup, users might use their email address and a password, and as a result, the IAM will provide them with a token for their user id. The approach to discovering party ids corresponding to human-readable uses can also vary depending on privacy requirements and other constraints. Create-daml-app addresses this by writing alias contracts on the ledger with associate human-readable names with the party id. These alias contracts are shared with everyone via a public party.

Conclusion

While the new party ids might seem daunting at first, the Daml SDK tooling is well set up to handle it. Sandbox now behaves more like your production environment, making it easier to move your applications from development to production, while the introduction of user management makes integration with IAMs easier than before. If you still encounter issues handling these party ids, don’t hesitate to reach out to us on the Daml forum.