1

I'm new to DAML and have been scratching my head over this for two solid days. In this voting system specified in "template Voting", an actor has the ability to add a voter party to a list of voters specified as "choice Add". This voters list (I presume a list) is defined as "voters : Set Party", (of which I can't seem to find the definition), and some conditions are defined in "Choice Vote" and "Choice Decide".

I'm trying to remove the "Choice Add" ability and instead pre-define all voters in a list defined in a separate template. If I understand correctly these predefined voters must be declared as a list: [Party] I created a template called "Creation" containing some variables in conjunction with a custom type called "CreationRights" containing the to be voters list which must be referenced from the voting template. However, I can't seem to refer to this list in any way. Changing the voters data type to [Party] also gives an error for "voters" in Choice Decide and Choice Vote. rendering the conditions specified unusable:

Couldn't match expected type 'Set a1' with actual type '[Party]'

How can I reference to the predefined voters (votingRight) list while still applying the condition set? Any tips are very welcome!

Code:

data CreationRights = CreationRights
  with 
    votingRight : [Party]
  deriving (Eq, Show)

template Creation
  with 
    actor       : Party
    creationId  : Text
    title       : Text
    votingRight : CreationRights
  where
    signatory actor

template Voting
  with
    actor : Party
    claim : Claim
    voters : Set Party
    voted : Set Party
    votes : [Bool]

  where
    signatory actor, voted
    observer voters

    key (actor, claim) : VotingKey
    maintainer key._1

    ---choice Add : ()
      ---with voter : Party
      ---controller actor
      ---do
        ---create this with voters = S.insert voter voters
        ---pure ()

    choice Decide : ContractId Decision
      controller actor
      do
        assertMsg "At least 60% must have voted" $ ((size voters / 100) * 60) <= length votes
        let approvals = length $ L.filter (\v -> v) votes
        let disapprovals = length $ L.filter (\v -> not v) votes
        let accept = approvals > disapprovals
        create Decision with ..

    choice Vote : ()
      with
        voter : Party
        accept : Bool
      controller voter
      do
        assertMsg "Voter not added" $ member voter voters
        assertMsg "Voter already voted" $ not $ member voter voted
        create this with voted = S.insert voter voted; votes = accept :: votes
        pure ()

template Decision
  with
    actor : Party
    claim : Claim
    voters : Set Party
    accept : Bool
  where
    signatory actor, voters
Sjoerd1234
  • 148
  • 1
  • 2
  • 11

1 Answers1

2

You don't have to do any of this to create a Voting instance without using Add. Voting defines its signatories to be actor, voted, so if voted is either empty or a singleton containing the same party, then the initial Voting instance only has one signatory. That means it can be created directly by actor without further interactions. I have included a simplified demonstration of this at the end of this answer.

Given you have created an instance of the Creation, you will have a copy of your list of voters on the ledger. At that point, if you want to use that list, you will have to either exercise a choice on that contract or read that contract off the ledger. You can do the latter with fetch, fetchByKey, lookup, and lookupByKey within a choice on another contract (although you will need to provide either the key or contractId). However as this template instance represents an authority to create Voting instances, representing this authority directly as a choice on Creation is recommended.

Finally, if you change the datatype of voters from a Set to a List then you will also have to change the corresponding functions that use that parameter. So, for instance, S.insert voter voted would have to become voter :: voted or similar.

daml 1.2
module Voting where

import DA.Next.Set as S
import DA.List as L

type Claim = Text

type VotingKey = (Party, Claim)

template Voting
  with
    actor : Party
    claim : Claim
    voters : Set Party
    voted : Set Party
    votes : [Bool]

  where
    signatory actor, voted
    observer voters

    key (actor, claim) : VotingKey
    maintainer key._1

    choice Decide : ContractId Decision
      controller actor
      do
        assertMsg "At least 60% must have voted" $ ((size voters / 100) * 60) <= length votes
        let approvals = length $ L.filter identity votes
        let disapprovals = length $ L.filter not votes
        let accept = approvals > disapprovals
        create Decision with actor; claim; voters; accept

    choice Vote : ContractId Voting
      with
        voter : Party
        accept : Bool
      controller voter
      do
        assertMsg "Voter not added" $ member voter voters
        assertMsg "Voter already voted" . not $ member voter voted
        create this with voted = S.insert voter voted; votes = accept :: votes

template Decision
  with
    actor : Party
    claim : Claim
    voters : Set Party
    accept : Bool
  where
    signatory actor, voters

test = scenario do
    alice <- getParty "Alice"
    bob <- getParty "Bob"
    charlie <- getParty "Charlie"

    alice `submit` create
        Voting with
            actor = alice
            claim = "Pizza"
            voters = fromList [ alice, bob, charlie ]
            voted = empty
            votes = []
Recurse
  • 3,557
  • 1
  • 23
  • 36
  • Thanks, really helpful! I have some questions though if you don't mind. Now in my situation, I will need to have that template "Creation" (and also template "Actor"). I'm trying to create it so that when you create a voting, you provide a claim where you specify an instance of Actor and an instance of Creation. Voters (among other data types) are defined in the Creation instance and only the voters are derived and inputted in the Voting template. Am I correct to assume that this can be achieved by your explanation in the second paragraph (using fetch, fetchByKey, lookup, and lookupByKey)? – Sjoerd1234 Mar 04 '20 at 12:32
  • So, specifying the Actor and Creation in the claim would be done by providing only the Actor contractId and Creation contractId. Is it possible to define Claim as a template containing the two ContractId datatypes: actor and creation. Based on the two contractIds given retrieve the corresponding Actor and Creation from the ledger (which would be done by fetch, fetchByKey, lookup, lookupByKey defined in a Choice under Claim). Then from the retrieved Creation, select the voters defined in the Creation voters list and input those in the voters list in the voting. Would this be the DAML way? – Sjoerd1234 Mar 04 '20 at 13:14
  • If you have an `Actor` template then you have discovered the DAML "Role Contract" pattern. If being an actor allows you to define a `Claim`, then make a choice on `Actor`: `MakeClaim`. This should probably lookup `Creation` by key, and then create the `Claim` instance using that. The DAML way is to treat all data received as an argument to a choice as an attestation. Data is not "believed" until it is evidenced by reading it from the ledger. Even then, it is only "believed" by its signatories. So your actor trusts data it provides, data it signs, and data signed by someone it trusts. – Recurse Mar 05 '20 at 01:08