Voting

Voting

As we’ve mentioned previously, MarketTokens are analogous to company shares. Just as company shareholders can vote on some company decisions, MarketToken stakeholders are allowed to vote on some data market governance questions. In fact, all major decisions in the data market are made by token holder vote. These decisions include which new listing candidates (a “listing candidate” is a chunk of data which has not yet been confirmed as a listing in the market) should be confirmed as listings, which challenged listings should be removed from the market (we’ll say more about challenges later), what changes should be made to the Parameterizer parameters (which govern the market’s behavior; more on this later), and who the datatrust operator should be. In this chapter, we’ll introduce you to the fundamentals of the voting system and describe the basics of on-chain Computable governance.

Candidates, Candidates, Candidates

All voting is done on “candidates.” Think of a candidate as governance question brought up for referendum. There are a number of different types of candidates:

Listing candidates

A proposal to add a new listing to the data market is a “listing candidate.” A maker (a data contributor) who has gathered some interesting data, creates a listing candidate to propose that their data should be added to the data market. If this candidate is accepted, the maker is rewarded with listing_reward, a tranche of newly minted MarketToken.

Here, the vote serves as a gating mechanism to prevent fraudulent data from entering the market. It also allows for some quality control checks on data to be performed by any interested parties.

Challenge candidate

A proposal to remove an existing listing from the data market. Sometimes, the listing process will allow a bad listing (with poor data perhaps) to slip through. In this case, we need a mechanism to allow for clean-up and removal of this data. The challenge mechanism allows for this sort of cleanup. An interested party can challenge a listing they believe to be fraudulent. If the vote succeeds, this listing is removed from the market.

To prevent nuisance challenges, creating a challenge requires locking up some MarketToken. The amount to be locked up is governed by the stake parameter set in the Parameterizer. If the challenge is rejected (judged by stakeholders to be frivolous), then the listing owner is rewarded for the trouble they faced. For this reason, challenges will likely be relatively uncommon since there’s a risk of losing funds.

As a second complication, you might ask, what happens to the MarketToken that was minted in the case of a successful challenge that removes the associated listing? The answer is absolutely nothing. If you succeeded in pulling a fast one on the data market, you are allowed to walk with the funds. This choice was made to keep the contract complexity to manageable levels, since having “lockups” to prevent this situation would add quite a bit of extra code.

Reparameterization candidate

A proposal to change the parameters of the data market. We’ll say more about the parameters that can be altered in a future chapter.

Datatrust candidate

A proposal to change the datatrust used for the market. If a datatrust operator is misbehaving, this mechanism allows for the datatrust to be replaced if necessary. However, it’s worth nothing that the datatrust has considerable power in this version of the Computable protocol, so this facility is something of a “nuclear option” only to be used if other measures aren’t panning out.

The voting code

On-chain, a candidate is represented by a Candidate struct:

struct Candidate:
  kind: uint256 # one of [1,2,3,4] representing an
application, challenge, reparam or registration
respectively
  owner: address
  stake: wei_value
  vote_by: timestamp
  yea: uint256
  nay: uint256

Let’s quickly review the fields of this struct. The owner is the Ethereum address of the candidate proposer. As a quick note, the Computable protocol doesn’t know anything about humans. The only entities that it knows about are Ethereum addresses. What is an address you might ask? It’s simply a 64 character hex string that uniquely identifies some entity on Ethereum. This entity could be a human, a smart contract, a consortium or anything. All the participants in the Computable protocol we’ve talked about (makers, stakeholders, datatrust operators) are all Ethereum addresses. Which means that participants in the Computable protocol aren’t necessarily humans, although the could be of course.

Returning to the discussion, the stake is the amount of MarketToken that must be placed as stake to vote for/against this candidate. (We’ll say more about this shortly). vote_by is how long the the voting poll will be open for this candidate. And yea and nay count the number of votes in favor and opposing this candidate.

Voting

The voting system is quite simple. To place a vote, a stakeholder simply locks up Candidate.stake of MarketToken.

@public
def vote(hash: bytes32, option: uint256):
  """
  @notice Cast a vote for a given candidate
  @dev User mush have approved market token to spend on
their behalf
  @param hash The candidate identifier
  @param option Yea (1) or Nay (!1)
  """
  assert self.candidates[hash].owner != ZERO_ADDRESS
  assert self.candidates[hash].vote_by >
block.timestamp
  stake: wei_value = self.candidates[hash].stake
  self.market_token.transferFrom(msg.sender, self,
stake)
  self.stakes[msg.sender][hash] += stake
  if option == 1:
    self.candidates[hash].yea += 1
  else:
    self.candidates[hash].nay += 1
  log.Voted(hash, msg.sender)

A stakeholder may vote as many times as they wish, at the cost of locking up more stake. Note that this makes a data market an explicit plutocracy: larger stakeholders in the data market explicitly have greater voting power. While the downsides of plutocratic systems are well known, it’s worth remembering that each data market is a relatively local system. It’s not unfair that large stakeholders have more say in how its run (just as owners of a business have more say in its operations than third parties).

The vote passes if the proportion of yea votes is greater than the plurality parameter.

@public
@constant
def didPass(hash: bytes32, plurality: uint256) -> bool:
  """
  @notice Return a bool indicating whether a given
candidate recieved enough votes to exceed the plurality
  @dev The poll must be closed. Also we cover the
corner case that no one voted.
  @return bool
  """
  assert self.candidates[hash].owner != ZERO_ADDRESS
  assert self.candidates[hash].vote_by <
block.timestamp
  yea: uint256 = self.candidates[hash].yea
  total: uint256 = yea + self.candidates[hash].nay
  # edge case that no one voted
  if total == 0:
    # theoretically a market could have a 0 plurality
    return plurality == 0
  else:
    return ((yea * 100) / total) >= plurality

This simple voting system has the advantage of being easy to understand. However, the trade-off is that this simplicity allows for some sophisticated vote manipulation techniques to be feasible. We discuss such attacks in greater detail later.

Last Thoughts

Now that you understand how voting works, let’s dive into the most important thing to be governed by votes, the listings themselves. You’ll learn more in the next chapter.

Next Chapter