The ethereum.org democracy tutorial: token holders propose a transaction, vote on it for a fixed period, and the contract sends it if it passes.
Historical Significance
This is the pattern The DAO was built on, reduced to something one person could deploy: votes weighted by token balance, a fixed debating period, and a passing proposal that executes as a raw call from the treasury. The tutorial also shows the defence that mattered, committing to a hash of the transaction when the proposal opens so that voters and executor cannot be shown different code. Weighing votes at execution time rather than at voting time is the flaw it kept: a voter can sell their shares and their vote still counts.
Context
The democracy tutorial sat on ethereum.org beside the token and crowdsale tutorials and was revised repeatedly through 2016. Copies of it were deployed both before and after The DAO was drained in June 2016, which suggests it was read as a worked example of shareholder voting rather than as a warning.
Key Facts
Description
The democracy contract from the ethereum.org tutorial: an association whose members are whoever holds a balance in a separate token, and whose decisions are transactions the association itself sends.
Any holder may open a proposal naming a recipient, an amount in whole ether, a text description and the bytecode of the transaction to send. What is stored is a hash of the recipient, the amount and that bytecode, so the exact transaction being voted on is committed in advance and cannot be swapped afterwards. Holders then vote for or against until the debating period expires, and each address may vote once.
After the deadline anyone may execute the proposal, supplying the bytecode again for the contract to check against the stored hash. Votes are weighed by the token balance each voter holds at that moment, not at the time they voted, and the tally has to clear a minimum quorum measured in shares before it counts. If more weight is in favour than against, the association makes the call with the proposed value attached.
This one takes its shares from the token at 0x4d5a3fc0c3526d2aa30b55091c351e525c119d5d, requires 500 shares of weight for a vote to count, and gives each proposal 10 minutes of debate.
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
Frontier Era
The initial release of Ethereum. A bare-bones implementation for technical users.
Bytecode Overview
Verified Source Available
This contract has verified source code on Etherscan.
Show source code (Solidity)
// Submitted by EthereumHistory (ethereumhistory.com)
contract token { mapping (address => uint) public balanceOf; }
contract Association {
uint public minimumQuorum;
uint public debatingPeriodInMinutes;
Proposal[] public proposals;
uint public numProposals;
token public sharesTokenAddress;
event ProposalAdded(uint proposalID, address recipient, uint amount, string description);
event Voted(uint proposalID, bool position, address voter);
event ProposalTallied(uint proposalID, int result, uint quorum, bool active);
struct Proposal {
address recipient;
uint amount;
string description;
uint votingDeadline;
bool openToVote;
bool proposalPassed;
uint numberOfVotes;
bytes32 proposalHash;
Vote[] votes;
mapping (address => bool) voted;
}
struct Vote {
bool inSupport;
address voter;
}
modifier onlyShareholders {
if (sharesTokenAddress.balanceOf(msg.sender) == 0) throw;
_
}
function Association(address sharesAddress, uint minimumSharesForVoting, uint minutesForDebate) {
sharesTokenAddress = token(sharesAddress);
minimumQuorum = minimumSharesForVoting;
debatingPeriodInMinutes = minutesForDebate;
}
function newProposal(address beneficiary, uint etherAmount, string JobDescription, bytes transactionBytecode) onlyShareholders returns (uint proposalID) {
proposalID = proposals.length++;
Proposal p = proposals[proposalID];
p.recipient = beneficiary;
p.amount = etherAmount;
p.description = JobDescription;
p.proposalHash = sha3(beneficiary, etherAmount, transactionBytecode);
p.votingDeadline = now + debatingPeriodInMinutes * 1 minutes;
p.openToVote = true;
p.proposalPassed = false;
p.numberOfVotes = 0;
ProposalAdded(proposalID, beneficiary, etherAmount, JobDescription);
numProposals = proposalID+1;
}
function checkProposalCode(uint proposalNumber, address beneficiary, uint etherAmount, bytes transactionBytecode) constant returns (bool codeChecksOut) {
Proposal p = proposals[proposalNumber];
return p.proposalHash == sha3(beneficiary, etherAmount, transactionBytecode);
}
function vote(uint proposalNumber, bool supportsProposal) onlyShareholders returns (uint voteID) {
Proposal p = proposals[proposalNumber];
if (p.voted[msg.sender] == true) throw;
voteID = p.votes.length++;
p.votes[voteID] = Vote({inSupport: supportsProposal, voter: msg.sender});
p.voted[msg.sender] = true;
p.numberOfVotes = voteID + 1;
Voted(proposalNumber, supportsProposal, msg.sender);
}
function executeProposal(uint proposalNumber, bytes transactionBytecode) returns (int result) {
Proposal p = proposals[proposalNumber];
if (now < p.votingDeadline
|| !p.openToVote
|| p.proposalHash != sha3(p.recipient, p.amount, transactionBytecode)) throw;
uint quorum = 0;
uint yea = 0;
uint nay = 0;
for (uint i = 0; i < p.votes.length; ++i) {
Vote v = p.votes[i];
uint voteWeight = sharesTokenAddress.balanceOf(v.voter);
quorum += voteWeight;
if (v.inSupport) {
yea += voteWeight;
} else {
nay += voteWeight;
}
}
if (quorum > minimumQuorum && yea > nay) {
p.recipient.call.value(p.amount * 1000000000000000000)(transactionBytecode);
p.openToVote = false;
p.proposalPassed = true;
} else if (quorum > minimumQuorum && nay > yea) {
p.openToVote = false;
p.proposalPassed = false;
}
ProposalTallied(proposalNumber, result, quorum, p.openToVote);
}
}
External Links
Related contracts
Contract 0xfd4a39...980dfe
Same deployerByte-identical deployment of: Standard ethereum.org tutorial token (Homestead, Mar 2016): name/symbol/decimals metadata, a public balanceOf mapping and an overf
0xfd4a39...980dfeFebruary 17, 2016Greeter
Same eraA HelloWorld greeter deployed at block 51,909 on 8 August 2015, one of a run of contracts from the same account that week.
0x412fda...7267edAugust 8, 2015Greeter
Same eraA Greeter with the message 'Ethereum will change the world smarter' - deployed Aug 8, 2015 by a developer connected to the Tokyo Smart Contract meetup.
0xda0c1c...7a68ebAugust 8, 2015MessageStore
Same eraA 6-line contract storing a single public string, deployed Day 9 of Ethereum by a prolific early experimenter who shipped 18 contracts in one week.
0xd2eccd...ff3d6bAugust 8, 2015Greeter
Same eraThe go-ethereum Contract Tutorial greeter, deployed with the greeting: Hello World!
0x506f4b...466983August 8, 2015Contract 0xd4eae4...a2c77a
Same eraThe ethereum.org crowdsale tutorial, deployed on the ninth day of the public chain, with the funding goal, deadline and price fixed at construction.
0xd4eae4...a2c77aAugust 8, 2015