Aventus Protocol (Part 1): Deep Dive on Voting

Aventus Network
Oct 31, 2017 · 9 min read

We are excited to have a look at the first component of the Aventus Protocol: An AVT stake weighted voting module, allowing Aventus network participants to have a part in shaping the future of the protocol.

Before we jump into the details we would like to thank Colony, Aragon and Zeppelin amongst many others for their previous work around voting, gas optimising design, upgradability patterns etc.

Voting Mechanics

Phase 0: Setup

Setting up a vote involves submitting the description (describing what the vote is about), adding up to 4 options that can be voted for and finalising the vote by specifying the start of the lobbying phase and an interval for how long the voting phase should be (minimum 1 week).

We have decided to create no financial incentive for voting on the direction of the Aventus protocol (this does not apply to later components such as voting on event and application legitimacy). As an AVT holder you should have an incentive to participate in votes, since they influence the direction of the protocol. Initially these are advisory and non binding, but once the protocol has been fully developed, deployed and a sufficient degree of adoption can be observed, binding votes will be introduced. Thus it should be obvious that any AVT token holder acting in their own self interest, should participate in votes to steer decisions in the direction they believe maximises the utility of the network thereby driving underlying value of AVT, without any further incentive.

Phase 1: Lobbying

Phase 2: Voting

The vote is AVT weighted to prevent sybil attacks (e.g. you could create and vote from many Ethereum addresses to skew results if stake did not matter) and to ensure that those with the most exposure get a proportional say in any major vote.

All data on a blockchain is publicly visible which presents a problem when voting since you should have no knowledge of the likelihood of any option winning before you cast your vote. To get around this, participants submit an obscured vote (i.e. the hash of the signature corresponding to the voters public key of the option voted on).

Phase 3: Revealing

Revealing a vote is done by submitting the option voted on and the signature thereof. The smart contract hashes the signature and checks that it is the same as that submitted during the voting phase and that is is from the appropriate public key. Once everything checks out, the amount of AVT in the lock contract is added to the tally for the specified option and the lock on the AVT is released so the voter can withdraw their funds again if desired (however if the voter intends to repeatedly vote they may as well leave it be until needed).

Phase 4: Result

Implementation

All smart contacts inherit from interfaces, so that they can reference each other without incurring an additional gas overhead. When a smart contract references another its compiled code contains the compiled code of all other referenced contracts. Interfaces means only function signatures are included not their implementation drastically reducing deployment costs.

We use libraries (deployed as singletons) for most business logic. This means common functionality that will be repeatedly used only needs to be deployed once and other smart contracts referencing it will be much cheaper to deploy.

Storage.sol

This design has a slight gas overhead for reading or mutating state on chain, however this means all other protocol contracts created do not need to allocate storage space for their state directly and can thus be upgraded without copying any state. This is much more gas efficient since storage opcodes are very costly.

Owned.sol

LLock.sol

LVote.sol

A doubly linked list, ordered by reveal start time is maintained for each user as shown in the figure from Colony below. Each node contains another doubly linked list ordered by vote id.

Doubly linked list of reveal end times, each containing a doubly linked list of vote ids

We do not want to have to traverse the whole data structure and incur the gas overhead of doing so when inserting a vote. Voters will be able to calculate in memory (i.e. off chain, locally) what position their vote should be inserted in the two lists and supply this as a parameter when voting, taking the computational complexity from linear to constant (i.e. way more gas efficient). Before inserting the entry the smart contract validates that the previous and next entry are as expected (i.e. the user is not trying to cheat) and then inserts it. We need the vote id doubly liked list within each of the nodes in the top list for votes with identical reveal periods.

AventusVote.sol

contract AventusVote is Owned {
using LLock for IStorage;
using LVote for IStorage;
IStorage s;/**
* @dev Constructor
* @param s_ Persistent storage contract
*/
function AventusVote(IStorage s_) {
s = s_;
}
/**
* @dev Withdraw locked, staked AVT not used in an active vote
* @param amount Amount to withdraw from lock
*/
function withdraw(uint amount) {
s.withdraw(amount);
}
/**
* @dev Deposit & lock AVT for stake weighted votes
* @param amount Amount to withdraw from lock
*/
function deposit(uint amount) {
s.deposit(amount);
}
// @dev Toggle the ability to lock funds for staking (For security)
function toggleLockFreeze()
onlyOwner
{
s.toggleLockFreeze();
}
/**
* @dev Set up safety controls for initial release of voting
* @param restricted True if we are in restricted mode
* @param amount Maximum amount of AVT any account can lock up at a time
* @param balance Maximum amount of AVT that can be locked up in total
*/
function setThresholds(bool restricted, uint amount, uint balance)
onlyOwner
{
s.setThresholds(restricted, amount, balance);
}
/**
* @dev Create a proposal to be voted on
* @param desc Either just a title or a pointer to IPFS details
* @return uint ID of newly created proposal
*/
function createVote(string desc) {
s.createVote(desc);
}
/**
* @dev Add an option to a proposal that voters can choose
* @param id Proposal ID
* @param option Description of option
*/
function addVoteOption(uint id, string option) {
s.addVoteOption(id, option);
}
/**
* @dev Finish setting up votes with time intervals & start
* @param id Proposal ID
* @param start The start date of the cooldown period, after which vote starts
* @param interval The amount of time the vote and reveal periods last for
*/
function finaliseVote(uint id, uint start, uint interval) {
s.finaliseVote(id, start, interval);
}
/**
* @dev Cast a vote on one of a given proposal's options
* @param id Proposal ID
* @param secret The secret vote: Sha3(signed Sha3(option ID))
* @param prevTime The previous revealStart time that locked the user's funds
* @param prevId The previous proposal ID at the current revealStart time
*/
function castVote(uint id, bytes32 secret, uint prevTime, uint prevId) {
s.castVote(id, secret, prevTime, prevId);
}
/**
* @dev Reveal a vote on a proposal
* @param id Proposal ID
* @param optId ID of option that was voted on
* @param v User's ECDSA signature(sha3(optID)) v value
* @param r User's ECDSA signature(sha3(optID)) r value
* @param s_ User's ECDSA signature(sha3(optID)) s value
*/
function revealVote(uint id, uint optId, uint8 v, bytes32 r, bytes32 s_) {
s.revealVote(id, optId, v, r, s_);
}
}

Limitations and Future Work

Our current design is primarily lacking when it comes to upgrading either of the libraries: Lock or Vote. If we were to upgrade these currently we would have to redeploy the Aventus Vote smart contract also. Whilst that is not a huge overhead since it is rather efficient (gas wise), it would be better if we could decouple the dependance between the Aventus Vote smart contract and the libraries themselves using a proxy pattern as suggested by Zeppelin illustrated below although this does still have its own issues.

We are also yet to finalise and release our unit and integration testing suite for the code and have an audit of the system once more of the protocol is completed.

This module discussed here will be deployed on the Rinkeby test net (as well as a snapshot of AVT balances to make the test net environment is as close to the main net as possible) with the corresponding DApp UI this week. We intend to test it on there and have the bug bounty running in parallel until we feel as comfortable as possible with the security aspects before deploying on the main Ethereum network.

For more information please read our blog posts, follow us on Twitter, join the discussion on Discord or get registered on our mailing list on our website.

Aventus

Aventus is a layer-2 blockchain protocol that brings scalability, lower costs and speed to Ethereum.

Aventus

Aventus Network is a layer-2 scaling solution that enables digital asset issuance, management and ownership, bringing the scale of permissioned blockchain and the security and interoperability of public blockchains. All without the shortcomings of either world.

Aventus Network

Written by

Aventus Network is a layer-2 blockchain protocol that brings scalability, lower costs and speed to Ethereum transactions.

Aventus

Aventus Network is a layer-2 scaling solution that enables digital asset issuance, management and ownership, bringing the scale of permissioned blockchain and the security and interoperability of public blockchains. All without the shortcomings of either world.