In the previous post we started to look at how to implement a doodle using smart contracts written in DAML. The last code snippet we looked at was the IssueInvites choice, which is used to send out invites to each of the voters. There’s one more detail about the code in that choice that I’d like to point out.

Divulged Contracts

Remember we mentioned that is a choice. Exercising it will archive the current version of the contract before executing the choice code. Let’s see why this is important.

If the choice were not , the version of the Doodle contract on which you exercise the choice would still exist when the contracts are created.

As a result of that, as shown on the screenshot on the left, every invite contract is going to be to every voter.

Divulging the invites is not necessarily something we want to do, as “” has no business getting their hands on an invite meant for “.

In order to fix this problem we make the choice “”, and the result can be seen in the screenshot on the left.

This is what is happening: the current version of the contract is archived before the choice is executed, and that means that there are no voters around to witness the creation of the invite contracts, and as such, the invite contracts are not divulged to unwanted parties. Each voter only sees their own invite because they are an observer thereof.

The Doodle Voting Invite

Now that we’ve seen how the doodle organizer issues voting invites, let’s have a look at what those invites look like:

The DoodleInvite template holds the doodle name, the organizer and the recipient voter.

Invite contracts are owned by the because they decide who can vote in the doodle. This way, the organizer get the opportunity to withdraw an invite by archiving the invite contract.

The key of the invite contract is a triplet, made of the , the and the . The organizer will therefore not be able to invite a voter more than once on the same doodle.

The is an observer on the invite contract. This will allow the voter party will be notified about the contract being created and, most importantly, act as a controller when exercising the “ choice.

Let’s look into more detail at the “ choice:

The choice takes just one parameter, and that’s the option we want to cast a vote for, and returns a Doodle updated with the vote.

You’ll also notice that the choice is “, which means that the invite contract will not be archived after exercising this choice. That’s because a voter is allowed to use the invite to vote for more than one option, and so we need the invite contract around after casting the first vote.

First thing, the voter needs to verify that the doodle actually exists, so in line 6 we are looking for a Doodle contract signed by the and having the name specified on the invite. This will return an optional wrapping a contract ID.

In line 7 we attempt to extract the contract ID from the optional using the fromSome function and use it to fetch the contract payload. If the contract we are looking for does not exist, this is where it will fail.

On line 8, we are checking that the organizer on the invite matches the organizer on the doodle contract. This way a voter will not be able to impersonate the organizer on a doodle by issuing fake invites.

Ultimately, on line 9, we exercise the choice on by specifying the , the the voter is voting for and the ID of the contract itself. Passing the invite contract ID is necessary so that the doodle’s choice can ensure that the voter casting the vote was actually issued an invite to do so.

The Doodle’s CastVote Choice

To quickly recap: in order for a voter to effectively vote, they need to receive a contract and exercise the choice on it. The choice will then look up he contract by name and organizer, and exercise on it.

So let’s see what this choice looks like.

CastVote Parameters

takes three parameters:

  • the voter
  • the option the voter wants to cast a vote for
  • the ID of the the voter has received

It is obvious why we need to know who the voter is and what he intends to vote for in order to cast the vote. The invite contract ID however, is a little trick meant to prevent fraud.

Making sure the vote is legitimate

In line 8 we are fetching the invite contract. If we succeed, it will guarantee that the invite provided by the user actually exists. Once we get hold of the invite payload, we can go on and assert a certain number of conditions to make sure the vote is legitimate:

  • in line 9 we ensure that the invite the voter has received was issued for the doodle the voter is trying to cast a vote on
  • in line 10, we check that the party exercising the choice is the one who received the invite. This check is defending against malicious parties hijacking the and voting on behalf of somebody they are not. Only if the controller party is able to produce an invite in their name is the vote allowed to go through.
  • in line 11 we make sure the invite provided by the voter was issued by the Doodle’s organizer. This defends against a situation where a voter with a valid invite for doodle A would try to use that to vote on a with the same name, but created by a different organizer.
  • in line 12 we check that the is open for voting
  • and finally in lines 13 and 14, we check that the controller is actually a registered voter and that the option they’re trying to cast a vote for is a valid one for this .

If all checks go trough it means we are confident enough that the vote is legitimate and the Doodle is ready to receive it.

Using TextMap to keep count of the votes

Let’s zoom into the code that’s actually casting the vote.

There are a few syntax elements here that we did not see before. First, in line 4 we start a “” block. A “ block lets you define values (variables) and bind them in the scope of the following code. All expressions in the let block are evaluated locally on the participant node.

So let’s have a closer look at what’s going on in there.

In line 5 we retrieve the current state of votes for the option we are about to cast a vote for.

The goal here is to retrieve the VotingSlot that holds the votes for the specified option. To do so, we call on the votes TextMap, as you can see on the rightmost side of the expression.

, however, returns an Optional of type VotingSdlot To extract the actual VotingSlot from it, we use the DA.Optional.fromOptional function.

fromOptional takes a fallback value and an optional. The fallback value will be returned if the optional — in our case, returned by — is empty. This can be the case if the map does not contain the option in its key set and that happens if nobody has voted for that option yet. The fallback value is a VotingSlot with a votes count of zero and an empty voters array — because if the fallback value is returned, it means that nobody has voted for that option yet.

we update the votes TextMap with a new VotingSlot that reflects the new vote:

Here we use DA.TextMap.insert to insert an updated VotingSlot into the votes TextMap.

DA.TextMap.insert takes three parameters: the key, the value associated with the key, the map in which the key / value pair will be inserted.

If the TextMap does not already contain an entry for the specified key, one will be created. If one exists, the value previously associated with the key will be replaced with the new one.

The value we insert is the updated VotingSlot:

The new VotingSlot is created with an incremented and an updated list.

we create an updated version of the Doodle with a new map, but not before we check that the user has not already voted for the same option:

This should be read as: “create a new contract, identical to this one except for the votes argument, which should be replaced with updatedVotes”. Since the choice is not marked as “”, the contract will archive itself.

This wraps up the actual smart contract functionality for our Doodle. In the next iteration, we’ll be looking at how to properly and exhaustively test it.

: Peter Schubert

Lead Architect at LiquidShare, building a cloud native, blockchain enabled, financial services SaaS platform.