Roles in Daml - Introducing Multi-party submissions
What is a Daml party? It’s a great question to which there is a precise technical answer, of course, but that answer would help us as little in designing a multi-party application as a precise technical understanding of database users would help us in designing a web application.
In our examples, we often use party names like “Alice” and “Bob”, or organisations like “Bank”, suggesting that parties represent individuals or business entities. This is an oversimplification for examples. Looking top down, it’s not so much a bank that acts in a transaction, but a legal entity within that bank. And maybe a particular desk within that legal entity. Ultimately paper gets signed by an individual within the responsible team. Similarly, looking at the situation bottom up, an individual rarely acts purely as an individual. We act as members of teams, using authority bestowed by the surrounding organization. In some businesses and cultures this concept of acting on behalf of an organization still has a physical manifestation in the form of a stamp.
In short, agents, be they human or machine, take on roles. The role can be that of an individual, or as a member or a team or organisation. It’s the mantle of the role that we take on that gives us access and authority. And access and authority are exactly what Daml parties are about via observers, signatories and controllers. Thus, a good way of thinking about Parties in Daml is to think of them as roles. Parties Alice and Bob represent individuals acting simply on behalf of themselves. A party Bank represents something or someone acting on behalf of a Bank.
Now in reality, we rarely act in only a single role. Just in your day-to-day professional activities you are probably acting as an individual, a team member, and a member of an organisation all at the same time. Multi-party submissions now make it possible to do just that in Daml, allowing you to translate business roles into Daml applications much more easily, and improving development and performance of several important use cases.
Use case 1: reference data
Let’s assume that we want to create a digital market where parties exchange items at a fixed price given by the market operator. The operator can update the price at any time, and we want the parties to automatically use the current price in their agreements.
The current price could be stored on the ledger in a simple contract where the price for a given item can be looked up by key.
But how do the market participants look up the current price? The CurrentPrice contract as defined above is only visible to the operator.
We could make every party observer on all CurrentPrice contracts by storing a list of observers in the CurrentPrice contract. But maintaining a list of n parties on m price contracts gets expensive and cumbersome fast. Adding a single new party requires a transaction of size O(m * n). There are some possible optimizations, but ultimately this approach doesn’t match the obvious mental model for read access. We want to express the role of being able to read the prices. In line with the party/role equivalence, that means we add a new party and give it the right to read the prices:
Now with multi-party submissions, all that’s needed is for agents that should be able to read and use that price data to get the authorization to read as “reader”. For example, here’s the payload of an access token for the DAML sandbox that allows Alice to act on behalf of herself, while also reading all contracts of the “reader” party.
Full code snippet for the described use case can be found here.
Use case 2: role-based access control
In this section, we will have a look at how to implement role-based access control in Daml. We’ll do so quite generically by showing how multi-party submissions can be used to model groups of parties (think of them as individuals) and give those groups some special access.
First, we define a template that captures the fact that a given party is a member of a group.
where org is the Daml party representing the entire organisation (the root of trust for current group hierarchy), group is the Daml party representing the group membership role, and member is a Daml party representing an individual person. Group membership can be checked through a fetch by key operation:
which will fail if the given party is not a member of the given group. Now let’s give a group special permissions by modeling one group being able to administer another.
Here adminGroup is the Daml party representing the group admin role. Note that the AddMember choice uses flexible controllers and can be exercised by anyone. The assertMember call is used to make sure the party exercising the choice is indeed a member of the adminGroup role. In an organisation where Alice is the administrator of the legal team, she could use the following Daml Script to add Bob to the legal team:
Note how Alice has read-only access to the legalTeamAdmins contracts, which allows her to access the GroupAdmin contract. Similar to the first use case, the read delegation was partially moved to the access token provider - in the above example, all administrators of the legal team would need an access token that allows them to read on behalf of the legalTeamAdmins party (e.g., "readAs": ["legalTeamAdmins"]). However, administrative actions are still validated in the Daml model, and the ledger remains the single source of truth for who can administer what group, and which individual triggered changes.
Use case 3: Ledger Initialization
A more administrative use-case is that of ledger initialization. For example, imagine you are implementing a new workflow in Daml, where the core of your workflow are simple IOU contracts:
The Iou contract has multiple signatories. Previously, if you wanted to test your template using Daml Script, you had to implement a propose-accept workflow and submit multiple commands in order to create a single Iou contract. With multi-party submissions, you can instead send a single create command with both the issuer and the owner as act_as parties.
Not only is this shorter to write, but you can also start testing your code before fully implementing all propose-accept workflows.
Summary of API changes
To let you jump right in and try out these new features yourself, here is a brief summary of the API changes that you need to use muli-party submissions.
Previously, submitted commands had a single party field, which was the party on whose behalf the command should be executed - ie the single role the submitting agent was acting in. This field has been deprecated and replaced by the following two fields:
- actAs: The set of parties on whose behalf the command should be executed. All of these parties jointly authorize the command for the purpose of the ledger model authorization rules.
- readAs: The set of parties on whose behalf (in addition to all actAs parties) contracts can be retrieved. These parties do not authorize any changes to the ledger. They affect Daml operations such as fetch, fetchByKey, lookupByKey, exercise, and exerciseByKey, which only “see” contracts visible to the submitting parties.
The commands object contains new actAs and readAs fields as described above. The change is backwards compatible, any party specified in the party field is merged into the actAs parties.
There is a new submitMulti command in DAML Script.
Note that most production ledgers will secure their ledger API access and users will have to add access tokens to their ledger API requests. If an application wants to submit a multi-party command, it needs an access token which authorizes it to act on behalf of all readAs parties and to read on behalf of all readAs parties.
For the DAML sandbox, the access token payload contains actAs and readAs fields, which need to contain at least all of the parties mentioned in the corresponding command fields.