Thanks @entzik … I would add to what you wrote the need to have parties a hierarchical nature . That would allow you, together with your role definitions, to correctly express a signatory or observer (for example, all traders within the fx desk of the London Branch of Bank X could approve a trade) while the trace of the action would still be the actual party that performed the action. That would be great to have that …
@entzik, your real world analogy works very well here. Your proposal is to assign roles outside the ledger via more fine grained user management via attributes on parties. Let’s think about a few scenarios that we’d run into:
- What happens if Alice has role broker, creates a contract with the ensure clause, and then loses the broker role. Does the contract become void? Does it stay around because the
ensureclause is only enforced at the point of creation? Can the role not be taken away until there are no active contracts of that type?
- Who administers these attributes in a distributed setting? Who decides that a notary is a notary or a broker is a broker?
- How do you make sure the ensure clause can be validated in a deterministic way? Suppose I set an attribute for Alice and she immediately fires off a transaction that needs that attribute, how can we ensure there is consensus about whether the attribute setting happened before or after the transaction?
A lot of these questions can be resolved quite easily by making roles an on-ledger concept. Ie put your attribute in a contract:
template PartyAttribute with giver : Party holder : Party name : Text value : Text where signatory [giver, holder] key (holder, name) : (Party, Text) maintainer : hey._1
We have now completely cleared up items 2. and 3. above. You may argue that we have merely shifted the problem from restricting who can act as a notary or broker to who can sign attributes. That’s true, but because most roles are hierarchical, you get to a point where there is just one special party, or a small number of special parties very quickly, which are effectively your roots of trust when it comes to attesting party roles. You can distribute that trust explicitly by requiring multiple signatures on Attributes before accepting them (ie
givers : [Party] instead of
giver : Party).
We are still left with issue 1. Currently we don’t allow fetching contracts in an
ensure clause, which means you can’t write
template BrokerContract with broker : Party brokerAttestator : Party ... where signatory broker ensure do attribute <- fetchByKey @PartyAttribute (broker, "role") return ((attribute.giver = brokerAttestator) && (attribute.name = "broker"))
The reason this isn’t possible is in part because the semantics are quite unclear. It would be easy to check this at the time of creation. But then what? We run into exactly the questions in item 1. What happens if the attributes change? Suddenly we have a contract with an
ensure clause that would no longer execute successfully.
ensure is meant to be an invariant that always holds.
In part, this problem arises because we have tried to distribute the information that the party is a broker. There is now a contract
Attribute that makes a claim on this, and a contract
BrokerContract. Item 1 really boils down to keeping these in sync. But here my question: why spread this information out in the first place? The
Attribute contract seems like a pretty sound source of truth on the matter.
Instead of stopping parties from signing contracts, we can stop them from doing anything useful with them unless they have matching roots of trust.
template BrokerContract with broker : Party brokerAttestator : Party where signatory broker let checkRole = do attribute <- fetchByKey @PartyAttribute (broker, "role") return ((attribute.giver = brokerAttestator) && (attribute.name = "broker")) controller broker can Broke : () do checkRole ...
You can potentially even save yourself from needing to thread through the roots of trust (
brokerAttestator in this case) by just checking that contracts have matching roots of trust when they are used together. I’d describe this pattern as an “access token” pattern. The
Attribute here is really just an access token to the choices on
Now there are other solutions I’ve seen discussed for cases where the semantics of the
ensure clause would be clear and or we can weaken our general trust assumptions. Do you know how you’d answer the questions in 1.? If so, maybe I can dig out some of the proposed solutions just to see whether they would have fitted your needs.
None of the examples are really properties of the parties themselves, they are just who you decide to trust. You trust the bar to decide who can be a lawyer; you trust the system that designates notaries, etc.
I’m not saying this is always the most convenient approach in the short term, but the approach taken by DAML is to make these kinds of trust decisions explicit as contracts, rather than some ambient, global property of the ledger.
This can be cumbersome, but it can also have advantages: what happens when you expand to a new country, and suddenly you have two separate bars, and not everyone wants to trust both? And not every lawyer will be qualified to operate in both countries. And most disputes will have to be resolved in either one or the other, but perhaps some are cross-cutting?
What if there was an optional
onCreation field that one could specify, that only performed the
Update on instantiation. This would let you distribute the enforcement of the invariant to the creation and choice exercise as needed.
While I largely agree with your and @Gary_Verhaegen’s point that the hard coding of roles and trust within a distribute setting is difficult, it would be helpful for us to find a middle ground for DAML users who might use a private DLT.
The example of expanding to a new country does not require a distributed system.
Think of it as avoiding global variables. You’re always better off not building in the assumption that there is only one thing, because eventually you need a second one, and a way to say which one you’re talking about. That’s just as true for the bar that certifies lawyers as for the database connection of your application.
How do you verify that? Imagine this template:
template T with bernhard : Party leonid : Party where signatory bernhard onCreation do lookupByKey @TCreationAuth (leonid, bernhard) key bernhard : Party maintainer key
Now suppose I later fetch this contract in a context with
gary, but without you:
template TFetcher with bernhard : Party gary : Party where signatory bernhard observer gary controller bernhard can FetchT : T do fetchByKey @T bernhard
I’m the only signatory on the
T. How does
gary know the
onCreate was ever even run? He somehow has to get verification from you that you validated the
onCreate way back when. The two ways of doing that I can see are
A) Ask you every time the contract is fetched, which means you have to remember this contract forever since you don’t get to find out when it gets archived
B) Put your signature on the contract. We have a mechanism for that
I’m not trying to be contrary or obtuse here. I understand both the use-case and the sort of feature you are after, and we have put some cycles into thinking about solutions without coming up with something truly convincing that is equally as safe as the rest of DAML.
Why does he need verification from me?
bernhard is the only signatory on
T isn’t general, but specific’
If you are not going to verify the
onCreate block, why have it? Then you can achieve the same thing with a “smart constructor” function:
createT (t : T) = do fetchByKey @TCreationAuth (t.leonid, t.bernhard) create t
But in order to know that, one would have to look at the transactions leading to a given
T, (ie find the fetch node ?) as opposed to looking at the code for
template T with player: Party sponsor: Party limit: Int observers: [Party] where signatory player, sponsor onCreation do (_, funds) <- fetchByKey @Funds sponsor assert $ funds.amount > limit
That’s definitely not any concern of the language or runtime. The language and runtime should only provide a means to assign and remove roles to / from parties. What happens if a role is retracted? that’s regulation. Business logic. Up to the application to deal with.
A couple of weeks back they discuvered somebody in France who was illegally practicing medicine after illegally obtaining a Medical Doctor license. In reaction, the “Ordre des Medecins” has retracted the license (retracted the role) and they are working with every patient currently ongoing treatment to check if everything is fine and compensate. And it’s similar in every other domain. That’s how real world works.
Who administers parties today ? external systems who get access to the admin API. If those external systems have the authority to define parties in the first place, maybe they could also do that in a more precise way, by adding and removing roles.
I do not know DAML internals well enough to answer that. It’s obviously some work. I do not pretend to have the answer, I merely dare suggest a feature that would make DAML easier to use in certain use cases and more secure.
Fully understand and appreciate your suggestion. My questions were purely rhetoric in order to explain where we see challenges with offering externally administered role-based auth. It’s a topic that does come up regularly and one I take very seriously. Really my whole post was just meant to lay out where my current thinking on the matter is.
The problem I see with this kind of approach - is that anyone, not just the giver, should be able to assert the party’s attributes (roles), and that’s literally everyone, every party on the system. And that’s beyond cumbersome. It could work if the contract could be made public (everyone can be an observer) but to my knowledge, that’s not possible.
Ah, but that’s a gap we are hoping to fill some time soon. See here for a long thread on the matter: Query and Create Exercise as multiple parties
Those attributes indeed fall into the realm of distributed network governance and should be administered by contracts. This is especially important as, hopefully, networks will be interconnected.
looking forward to that
If “Ordre des Médecins” is a party on the ledger, does that not let you do what you want? Practicing doctors could have a license they receive from it (i.e. a contract signed by it) and insurance companies/pharmacies/other caregivers that depend on a doctor’s authority would check that signature.
well yes in theory it works, in practice, as I said, it’s beyond cumbersome. think about it, every pharmacy, every insurance company any other doctor, any patient, basically anyone who will ever want to make sure another party is a doctor, will have to be an observer of every doctor contract in the world. And as you add new parties to the system in the future, you’ll have to add them as obsevers as well.
That’s not just a huge waste of storage and compute, it also make the the entire code base more complex and error prone.
If the role is an attribute of the party, all this goes away.
Also being able to create public contracts (where everyone is an observer by default) would an acceptable solution because it eliminates the complexity of having to store the entire world as an observer on each “role” contract, and apparently that’s on its way.
A post was split to a new topic: Problems with Edit Feature