Here is an example below, where a selected signatory (I call it host) invites a new one. It does not allow concurrent invitations, but I wanted to keep it very simple.
You’ll probably find useful the following blogposts. The first show a slightly different approach, the second gives you some context on the flexible controller I used.
module Multi where
import Daml.Script
template ContractWithMultiSignatories
with
signatories: [Party]
where
signatory signatories
choice Invite : ContractId Invitation with
host : Party
invitee : Party
controller host
do
-- One could add constraints like: assert (host `elem` signatories)
create Invitation with
signatories
host
invitee
template Invitation
with
signatories: [Party]
host : Party
invitee : Party
where
signatory signatories
controller invitee can
Accept : ContractId ContractWithMultiSignatories
do
create ContractWithMultiSignatories with signatories = invitee::signatories
invite_two_more_signatories = do
user1 <- allocateParty "user1"
user2 <- allocateParty "user2"
user3 <- allocateParty "user3"
first <- submit user1 do
createCmd ContractWithMultiSignatories with
signatories = [user1]
invitationForUser2 <- submit user1 do
exerciseCmd first Invite with
host = user1
invitee = user2
second <- submit user2 do
exerciseCmd invitationForUser2 Accept
invitationForUser3 <- submit user1 do
exerciseCmd second Invite with
host = user1
invitee = user3
submit user3 do
exerciseCmd invitationForUser3 Accept
The syntax controller contract.signatories can means “all signatories together can do this thing”, ie you need all of their authority. What you want is for any of them to be able to sign. To do that, you need to use the advanced choice syntax @Richard_Kapolnai used in his example as well:
template ContractWithMultiSignatories
with
signatories: [Party]
where
signatory signatories
template ContracProposal
with
contract: ContractWithMultiSignatories
signed: [Party]
where
signatory signed
observer contract.signatories
choice Accept : ContractId ContractWithMultiSignatories
with
accepter : Party
controller accepter
do
assertMsg "accepter needs to be a signatory" (accepter `elem` contract.signatories)
assertMsg "Somebody hasn't signed" (dedupSort contract.signatories == dedupSort signed)
create contract
choice Sign : ContractId ContracProposal
with
signer : Party
controller signer
do
assertMsg "signer needs to be a signatory" (signer `elem` contract.signatories)
create this with
signed = dedupSort (signer :: signed)
contract_with_multi_signatories_test = do
user1 <- allocateParty "user1"
user2 <- allocateParty "user2"
user3 <- allocateParty "user3"
proposalId <- submit user1 do
createCmd ContracProposal with
signed = [user1]
contract = ContractWithMultiSignatories with
signatories = [user1, user2, user3]
proposalId <- submit user2 do
exerciseCmd proposalId Sign with
signer = user2
proposalId <- submit user3 do
exerciseCmd proposalId Sign with
signer = user3
contractId <- submit user3 do
exerciseCmd proposalId Accept with
accepter = user3
pure()