Expressing these sorts of model-constraints in DAML is always interesting. In order to allow a DAML system to scale, the DAML ledger model imposes a couple of strict limitations (system-constraints) on how you express constraints. In my experience, while this can be frustrating, thinking through and accommodating these within your design generally improves both your understanding of your business problem, and more importantly, makes your final design less brittle and more adaptable to future requirement changes.
-
The only thing a DAML transaction knows about is what it can read from on the ledger, and the only way it can know of something on the ledger is to be explicitly told about it. This means that with exactly one exception, all constraints in DAML must be expressed existentially. The only universally quantified constraint in DAML is the key-uniqueness constraint—and even that required introducing the whole
maintainer
semantic and key maintenance protocol. -
With the single exception of key-uniqueness (again), all constraints on the creation of a new contract must be evaluated within the static-local scope of the contract itself.
Both of these ensure that a creation/exercise of a contract/choice can be evaluated and validated with reference to a bounded set of contracts
In order to permit DAML to scale to large numbers of parties, all constraints in DAML must be expressed existentially A constraint on a contracts, and that therefore the submitter can never be surprised at the result of a transaction by being informed of the presence of a contract about which they were not aware*. This is important to allow DAML to scale effectively as a distributed system.
So, to think about your problem:
-
templates don’t have signatories, only contracts do. So for the rest of this answer I’m going to assume you want to ensure “a contract with a signatory amongst …”.
-
Recall system-constraint 1. The only thing that DAML knows about is what it can read from the ledger. That means that this idea of “parties from which the signatories must be drawn” must exist on the ledger somehow. Note that DAML is a symmetric multi-party system. Unlike traditional architectures there is no privileged perspective, no super-user, no a priori centre (pulling on this thread ends up with us buried in post-modern deconstructionism, so I’m going to stop there).
The result is that a DAML model (ie. template) can’t express the idea that you are privileged in anyway, that any model that can express the constraint Jean restricts contracts to “Jean, Pierre, or Paul” can also express the constraint Andrae restricts contracts to “John, Peter, or Paula”. Because to do otherwise would be to express a universal property of the ledger, and we are restricted to existential predication.
So we need to existentially model the context of our constraint. Specifically, the perspective of the predicating speaker (Jean or Andrae) must be explicitly modelled. As in all modelling this can be achieved in at least two ways, intensionally or extensionally. In my experience DAML is at its best when used intensionally so I’ll cover that first.
The intensional model is focused on behaviour and implication. We don’t model state as data, but rather as the set of behaviours (choices) and constraints on that behaviour implied by previous acts. This approach sees DAML as a means of achieving coherency of praxis in a symmetric distributed system while denying a central authority (and pulling on this thread leads into semiotic pragmatics, so again I’ll shelve it).
What this means is that if there is a contract on the ledger then there was at some point a consensus on potential acts one of which has caused the creation of this contract.
Ie. If there is a contract:
-- (Note the explicit modelling of the `predicator`, there is no implicit authority you can appeal to, there is nothing that is not on the ledger).
template OneOfN
with
predicator: Party
signer: Party
where
signatory predicator, signer
Then there must have been the right to create this contract…
template RightToCreateOneOfN
with
predicator: Party
potentialSigners: Set Party
where
signatory predicator
nonconsuming choice CreateOneOfN: ContractId OneOfN
with
signer: Party
controller signer
do
create OneOfN with predicator; signer
…which allows you to now utter the predicate: Jean
requires that all OneOfN
contracts within their authority must be signed by one of the specified signers. Note that the constraint itself is not modelled as data on the ledger, but as a choice, a right to act that has been authorised by whatever prior choice/right-to-act led to the creation of that contract.
Yes, intensionally it’s turtles all the way down until you get to self-authorising unilateral speech acts that therefore do not require or represent consensus.
The alternative is to model this extensionally, This represents the state of the system as data, and constraints on the system as constraints on the permitted values of that data. So naturally this has to comply with the system-constraint 2. One way of rewording constraint 1 is “All intensional constraints must only be existentially predicated”; similarly constraint 2 can be expressed: “All extensional constraints must be contract local”.
So if we want to constrain the signatory of a contract to a set of parties, that predicate must be expressed exclusively in terms of data in that contract. So:
template OneOfNLocal
with
signer: Party
potentialSigners: Party
where
signatory signer
ensure signatory elem potentialSigners
Now, you will immediately observe that while this constraint does encode the letter of the requirement, it falls well short of the spirit. As mentioned above, DAML prefers to be an intensional modelling language. The root of this difficulty is the lack of a central authority. So as a single-signatory template, this represents not consensus on predicated state, but rather a unilateral speech act by signer
, and here we see Eco’s observation that any system that can express a truth can by necessity express a lie.
It is for this reason that ensure
clauses are rare in DAML modelling; however, I didn’t want to ignore them in this discussion as there are times when this sort of constraint is appropriate — generally to guarantee preconditions on the contract’s choices (ie. a list/set is non-empty, a number is positive or non-zero; etc).
Summary: DAML is symmetric and un-centred, as such modelling must be explicit about authority, existentially predicated, and invariably intensional. This is a significant departure from traditional modelling which invariably has an implicit central authority, admits a range of universally quantified predicates, and is oriented around manipulation of extensional data — but this departure is a good thing and essential for scalable, distributed application of the resulting models.