*** UPDATE as of 10/9/19: Please see the New Quickstart Guide***

An update from Enigma on how our protocol can be used to implement “secret voting”, complete with a video walkthrough!

Why Secret Voting

Blockchain-based voting is used for governance of decentralized systems, and crypto-economic mechanisms like Token-Curated Registries (we’ve written about these before). However, current voting implementations expose user information and allow anyone to trace how users have voted, leaving polls vulnerable to bribery and manipulation. While designs that protect votes during the polling process exist such as partial-lock commit reveal (PLCR), they present limited privacy because votes are public after the vote is concluded. Furthermore, the two-step process of commit-reveal has drawbacks for user experience.

The Enigma Protocol is a general decentralized off-chain layer for private computation. In this article, we present a private voting scheme that encrypts underlying votes and protects user privacy.

What’s New

This post is an update of our previous code walkthrough for Secret Voting. It features:

A redesigned UI Code for the web3 portion of the dApp Significant refactoring

Video Demo

Workflow Description

Purchase voting tokens: Voting tokens are OpenZeppelin mintable tokens created using a token factory with an arbitrary exchange rate of 10 voting tokens for 1 ETH. Stake voting tokens: Staked tokens refers to a portion of the voting tokens the user has purchased that the user is willing to commit to the voting contract. We’ve implemented a token-weighted voting system for this demonstration, and as such, a voter is allowed to place a maximum weight that doesn’t exceed the number of votes he/she has staked. Create a poll: Specify quorum percentage (“yea” votes divided by total number of votes needs to exceed this threshold in order for the vote to pass), description/poll question, and duration (s). Vote in poll: Enter a vote weight and click thumbs up (yea) or down (nay). This will cast the vote in an encrypted bytes representation (we do not store yea or nay, 1 or 0) alongside the weight. Tally poll: This will be a passive action from the user’s standpoint. Once the poll expires, the poll will automatically trigger the callable function. This noteworthy function runs the secret computation inside secure SGX enclaves on the Enigma network, decrypts the votes, tallies the results, and updates the contract state on Ethereum with the result of the poll. Withdraw tokens: A user can withdraw staked tokens from the contract that are not currently in use in an active poll.

Writing a Voting Secret Contract

The contract code for this section can be found in the enigma-template-dapp/contracts/ folder (voting_demo branch).

Before we jump into the secret contract good stuff, I want to briefly mention two regular, plain-old smart contracts:

VotingToken.sol — This is an OpenZeppelin mintable token that can be created out of thin air.

— This is an OpenZeppelin mintable token that can be created out of thin air. TokenFactory.sol — This file employs a contract factory design pattern to handle a user’s ETH in the payable function by defining an exchange rate of 10 voting tokens for 1 ETH and minting them accordingly.

Cool, now that that’s out of the way, we can talk about the secret contract, Voting.sol, that you came here for!

As a refresher, secret contracts are smart contracts that execute on the decentralized network without revealing input data, ensuring data privacy. Secret contracts are written in a programming language called Solidity, and are implemented very similarly to the Ethereum-based smart contracts you know and love. We recently published a walkthrough exploring a secret voting contract on Enigma.

This contract is certainly on the longer, more complicated side, but I’ll paste it in its entirety here and go through it afterwards:

As mentioned above, the design principles and syntax (state variables, structs, constructors, functions, events, modifiers, etc.) are basically the same as smart contracts for Ethereum.

The code above is pretty heavily comment-annotated, but to make things a little clearer…with the exception of the final two functions ( countVotes and updatePollStatus ), the contract is broken down into three sections:

Poll operations : Create new polls or get a poll’s status, expiration date, history, and voter-specific info [Lines 81–150].

: Create new polls or get a poll’s status, expiration date, history, and voter-specific info [Lines 81–150]. Vote operations : Cast an encrypted vote and weight or check if a user has voted [Lines 152–185].

: Cast an encrypted vote and weight or check if a user has voted [Lines 152–185]. Token operations: Stake and withdraw tokens or get the locked/staked token amount in the contract [Lines 187–231].

What about those final two functions. Well, these are the major keys to note in this code, and we call them the callable and callback functions.

Callable [Lines 233–250]: This is a public function that runs the secret computation inside the SGX enclave. Note that it’s a pure function because it does not actually read from nor write to contract state — it computes only off of the arguments that are passed into it. We’ll go over this further later, but what’s really cool is that although you pass in encrypted values via the front end, the decryption automatically occurs within this function. The original types are inferred based on the argument types specified in the function signature. In the case of this countVotes callable example, we pass in a pollID , encrypted argument votes , and weights , and specify types uint , uint[] , and uint[] , respectively. Although only the votes are encrypted as bytes, this argument receives a uint[] type just as the other arguments do. The votes argument is decrypted automatically according to this function signature. We can now loop through the votes that have been cast, increment the positive or negative votes depending on the corresponding weight, and return a tuple containing the poll ID, yay votes, and nay votes. This output will serve as the input to the callback function.

[Lines 233–250]: This is a public function that runs the secret computation inside the SGX enclave. Note that it’s a function because it does not actually read from nor write to contract state — it computes only off of the arguments that are passed into it. We’ll go over this further later, but what’s really cool is that although you pass in encrypted values via the front end, the decryption automatically occurs within this function. The original types are inferred based on the argument types specified in the function signature. In the case of this callable example, we pass in a , encrypted argument , and , and specify types , , and , respectively. Although only the are encrypted as bytes, this argument receives a type just as the other arguments do. The argument is decrypted automatically according to this function signature. We can now loop through the votes that have been cast, increment the positive or negative votes depending on the corresponding weight, and return a tuple containing the poll ID, yay votes, and nay votes. This output will serve as the input to the callback function. Callback [Lines 252–274]: This is a public function automatically called by the worker (note the onlyEnigma() modifier) after the callable function is completed. It is responsible for committing the results and altering the contract state. Be sure that the input arguments for the callback directly match to the output values you’ve returned in the callable function. In this example, we input the _pollID , _yeaVotes , and _nayVotes we’ve obtained from the callable, check that the tally satisfies the quorum percentage for the poll under question, and update the contract state poll’s value for pollStatus . Furthermore, we emit the PollStatusEvent event. This event emission is critical here, because you can set up an event watcher in your front end to react appropriately upon completion of the secure computation task.

At this point, you’ve gone through all the Solidity code you need to. I recommend you look through the migration script called 2_deploy_voting_contracts.js that can be found under the enigma-template-dapp/contracts/ folder (voting_demo branch) to look at the nuances of deploying Enigma secret contracts.

As an additional reminder, any time you create new contracts like you have in this case, or modify existing ones, make sure you redeploy to the network with the following command: darq-truffle migrate --reset --network development .

Amazing— you’ve covered writing some important terminology for decentralized voting applications and how to construct a secret voting contract with callable and callback functions. Now that that’s complete, let’s look at how we run secret computations and retrieve their results from the front-end!

Interacting with Your Secret Contract from the Front-End

The contract code for this section can be found in the enigma-template-dapp/client/src folder (voting_demo branch).

App.js — This is essentially the root page of our application and handles the initialization of necessary Enigma components/libraries and pulls in our custom contracts.

— This is essentially the root page of our application and handles the initialization of necessary Enigma components/libraries and pulls in our custom contracts. VotingWrapper.js — This component is one level down from the root page, and handles changes to the current test network account, account balance, staked token balance. Moreover, it renders the user’s voting token dashboard and poll interface.

— This component is one level down from the root page, and handles changes to the current test network account, account balance, staked token balance. Moreover, it renders the user’s voting token dashboard and poll interface. Token.js — This component provides the interface for the user to purchase, stake, and withdraw voting tokens from the contract.

— This component provides the interface for the user to purchase, stake, and withdraw voting tokens from the contract. Polls.js — This is the main business-logic component; I recommend you study since it offers the user the ability to create a new poll, view active and expired polls, and cast their encrypted, weighted vote. Not only does it accept the user’s vote, but also, upon timer end (Timer.js), will trigger the secure computation ( callable ), update the contract state ( callback ), and render the poll’s status accordingly.

Once again, I suggest you look at these files in their entirety, but seeing as much of it is general React-, dapp-based programming concepts, I’d like to mainly focus on a snippet of code from Polls.js:

The callable and callback function signatures are specified near the top (for later use) as strings and with no spaces.

and function signatures are specified near the top (for later use) as strings and with no spaces. The steps for the secret computation are wrapped in the above comment-annotated function enigmaTask . The first step is to construct a list of encrypted votes and weights [Lines 12–44]. The next critical step is to use the Enigma library to create a task with the callable function, an array of the arguments needed for it, and the callback function, among a couple other arguments as demonstrated [Lines 47–60]. Finally, the computation needs to run [Lines 65–68].

This enigmaTask function is called when a poll’s active window has ended by hitting its expiration date (triggered by this handleTimerEnd function). Then, an event watcher can be set up in your front-end to react to the callback once it completes like so:

Recall that the PollStatusUpdate event was emitted at the end of the callback function in the secret contract we wrote above.