From 9eb347e32c6ed0a1bfdf36d30e5ffe1c73b222d0 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Tue, 26 Jun 2018 14:44:45 -0400 Subject: [PATCH 1/3] Linting and removing warnings --- .../democracy/MultiOptionPollManager.sol | 145 ---------------- contracts/polls/LowLevelStringManipulator.sol | 2 +- contracts/polls/PollManager.sol | 158 ++++++++++-------- contracts/polls/SingleChoice.sol | 6 +- contracts/polls/SingleChoiceFactory.sol | 4 +- 5 files changed, 98 insertions(+), 217 deletions(-) delete mode 100644 contracts/democracy/MultiOptionPollManager.sol diff --git a/contracts/democracy/MultiOptionPollManager.sol b/contracts/democracy/MultiOptionPollManager.sol deleted file mode 100644 index 349d694..0000000 --- a/contracts/democracy/MultiOptionPollManager.sol +++ /dev/null @@ -1,145 +0,0 @@ -pragma solidity ^0.4.21; - -import "../common/Controlled.sol"; -import "../token/MiniMeTokenInterface.sol"; -import "../token/MiniMeTokenFactory.sol"; - -/** - * @title PollManager - * @author Richard Ramos (Status Research & Development GmbH) - */ -contract MultiOptionPollManager is Controlled { - event PollCreated(uint256 pollId, uint8 numOptions); - event PollCanceled(uint256 pollId); - event Voted(address voter, uint8[] votes); - - MiniMeTokenFactory public tokenFactory; - MiniMeTokenInterface public token; - - Poll[] public polls; - - struct Vote { - mapping(uint8 => uint8) distribution; - bool voted; - } - - struct Poll { - uint start; - uint end; - uint8 numOptions; - address[] voters; - mapping(address => Vote) voteMap; - uint256[] results; - address token; - bool cancelled; - } - - constructor(address _tokenFactory, address _token) - public - { - tokenFactory = MiniMeTokenFactory(_tokenFactory); - token = MiniMeTokenInterface(_token); - } - - function createPoll( - uint _blocksUntilVotingStart, - uint _voteDuration, - uint8 _numOptions - ) - public - onlyController - returns (uint pollId) - { - pollId = polls.length++; - - Poll storage p = polls[pollId]; - - p.cancelled = false; - p.start = block.number + _blocksUntilVotingStart; - p.end = p.start + _voteDuration; - p.numOptions = _numOptions; - p.token = tokenFactory.createCloneToken( - token, - p.start - 1, - "VotingToken", - MiniMeToken(token).decimals(), - "VTN", - true); - - - emit PollCreated(pollId, _numOptions); - } - - function vote(uint _pollId, uint8[] _vote) - public - { - Poll storage poll = polls[_pollId]; - - require(block.number >= poll.start); - require(block.number <= poll.end); - require(_vote.length == poll.numOptions); - require(!poll.voteMap[msg.sender].voted); - require(!poll.cancelled); - - uint8 percentage = 0; - - for(uint8 i = 0; i < poll.numOptions; i++){ - poll.results[i] = (MiniMeTokenInterface(poll.token).balanceOf(msg.sender) * _vote[i]) / 100; - percentage += _vote[i]; - poll.voteMap[msg.sender].distribution[i] = _vote[i]; - } - - require(percentage == 100); - - poll.voteMap[msg.sender].voted = true; - poll.voters.push(msg.sender); - - emit Voted(msg.sender, _vote); - } - - function getPollCount() - public - view - returns (uint256) - { - return polls.length; - } - - function exists(uint _pollId) - public - view - returns (bool) { - return polls.length != 0 && polls[_pollId].start != 0; - } - - function getPollResults(uint _pollId) - external - view - returns (uint256[]){ - return polls[_pollId].results; - } - - function hasVotesRecorded(uint256 _pollId) - external - view - returns (bool) - { - return polls[_pollId].voters.length > 0; - } - - function isVotingAvailable(uint _pollId) public view returns (bool){ - Poll memory p = polls[_pollId]; - return p.end > block.number; - } - - function cancel(uint _pollId) onlyController { - require(polls[_pollId].start > 0); - require(polls[_pollId].end < block.number); - - Poll storage p = polls[_pollId]; - p.cancelled = true; - - emit PollCanceled(_pollId); - } - -} \ No newline at end of file diff --git a/contracts/polls/LowLevelStringManipulator.sol b/contracts/polls/LowLevelStringManipulator.sol index 4b44fe4..2de012c 100644 --- a/contracts/polls/LowLevelStringManipulator.sol +++ b/contracts/polls/LowLevelStringManipulator.sol @@ -65,7 +65,7 @@ contract LowLevelStringManipulator { } function getTokenNameSymbol(address tokenAddr) internal returns (string name, string symbol) { - return (getString(tokenAddr, bytes4(sha3("name()"))),getString(tokenAddr, bytes4(sha3("symbol()")))); + return (getString(tokenAddr, bytes4(keccak256("name()"))),getString(tokenAddr, bytes4(keccak256("symbol()")))); } function getString(address _dst, bytes4 sig) internal returns(string) { diff --git a/contracts/polls/PollManager.sol b/contracts/polls/PollManager.sol index 3595f0d..b5ee77d 100644 --- a/contracts/polls/PollManager.sol +++ b/contracts/polls/PollManager.sol @@ -5,13 +5,13 @@ import "./LowLevelStringManipulator.sol"; import "../token/MiniMeToken.sol"; contract IPollContract { - function deltaVote(int _amount, bytes32 _ballot) returns (bool _succes); - function pollType() constant returns (bytes32); - function question() constant returns (string); + function deltaVote(int _amount, bytes32 _ballot) public returns (bool _succes); + function pollType() public constant returns (bytes32); + function question() public constant returns (string); } contract IPollFactory { - function create(bytes _description) returns(address); + function create(bytes _description) public returns(address); } contract PollManager is LowLevelStringManipulator, Controlled { @@ -37,7 +37,7 @@ contract PollManager is LowLevelStringManipulator, Controlled { MiniMeTokenFactory public tokenFactory; MiniMeToken public token; - function PollManager(address _tokenFactory, address _token) + constructor(address _tokenFactory, address _token) public { tokenFactory = MiniMeTokenFactory(_tokenFactory); token = MiniMeToken(_token); @@ -54,19 +54,23 @@ contract PollManager is LowLevelStringManipulator, Controlled { uint _endBlock, address _pollFactory, bytes _description) + public onlySNTHolder returns (uint _idPoll) { - if (_endBlock <= _startBlock) throw; - if (_endBlock <= getBlockNumber()) throw; + require(_endBlock > _startBlock && _endBlock > block.number); + _idPoll = _polls.length; _polls.length ++; - Poll p = _polls[ _idPoll ]; + Poll storage p = _polls[ _idPoll ]; p.startBlock = _startBlock; p.endBlock = _endBlock; p.voters = 0; - var (name, symbol) = getTokenNameSymbol(address(token)); + string memory name; + string memory symbol; + (name, symbol) = getTokenNameSymbol(address(token)); + string memory proposalName = strConcat(name, "_", uint2str(_idPoll)); string memory proposalSymbol = strConcat(symbol, "_", uint2str(_idPoll)); @@ -78,23 +82,32 @@ contract PollManager is LowLevelStringManipulator, Controlled { proposalSymbol, true); - p.pollContract = IPollFactory(_pollFactory).create(_description); - if (p.pollContract == 0) throw; + require(p.pollContract != 0); emit PollCreated(_idPoll); } - function cancelPoll(uint _idPoll) onlyController { - if (_idPoll >= _polls.length) throw; - Poll p = _polls[_idPoll]; - if (getBlockNumber() >= p.endBlock) throw; + function cancelPoll(uint _idPoll) + onlyController + public + { + require(_idPoll < _polls.length); + + Poll storage p = _polls[_idPoll]; + + require(p.endBlock < block.number); + p.canceled = true; - PollCanceled(_idPoll); + emit PollCanceled(_idPoll); } - function canVote(uint _idPoll) public view returns(bool) { + function canVote(uint _idPoll) + public + view + returns(bool) + { if(_idPoll >= _polls.length) return false; Poll storage p = _polls[_idPoll]; @@ -106,23 +119,19 @@ contract PollManager is LowLevelStringManipulator, Controlled { balance != 0; } - function vote(uint _idPoll, bytes32 _ballot) { - if (_idPoll >= _polls.length) throw; - Poll p = _polls[_idPoll]; - if (getBlockNumber() < p.startBlock) throw; - if (getBlockNumber() >= p.endBlock) throw; - if (p.canceled) throw; + function vote(uint _idPoll, bytes32 _ballot) public { + require(_idPoll < _polls.length); + + Poll storage p = _polls[_idPoll]; + + require(block.number >= p.startBlock && block.number < p.endBlock && !p.canceled); unvote(_idPoll); uint amount = MiniMeToken(p.token).balanceOf(msg.sender); - if (amount == 0) throw; - - -// enableTransfers = true; - if (!MiniMeToken(p.token).transferFrom(msg.sender, address(this), amount)) throw; -// enableTransfers = false; + require(amount != 0); + require(MiniMeToken(p.token).transferFrom(msg.sender, address(this), amount)); p.votes[msg.sender].ballot = _ballot; p.votes[msg.sender].amount = amount; @@ -131,44 +140,48 @@ contract PollManager is LowLevelStringManipulator, Controlled { p.votersPerBallot[_ballot]++; - if (!IPollContract(p.pollContract).deltaVote(int(amount), _ballot)) throw; + require(IPollContract(p.pollContract).deltaVote(int(amount), _ballot)); - Vote(_idPoll, msg.sender, _ballot, amount); + emit Vote(_idPoll, msg.sender, _ballot, amount); } - function unvote(uint _idPoll) { - if (_idPoll >= _polls.length) throw; - Poll p = _polls[_idPoll]; - if (getBlockNumber() < p.startBlock) throw; - if (getBlockNumber() >= p.endBlock) throw; - if (p.canceled) throw; + function unvote(uint _idPoll) public { + require(_idPoll < _polls.length); + Poll storage p = _polls[_idPoll]; + + require(block.number >= p.startBlock && block.number < p.endBlock && !p.canceled); uint amount = p.votes[msg.sender].amount; bytes32 ballot = p.votes[msg.sender].ballot; if (amount == 0) return; - if (!IPollContract(p.pollContract).deltaVote(-int(amount), ballot)) throw; + require(IPollContract(p.pollContract).deltaVote(-int(amount), ballot)); - p.votes[msg.sender].ballot = 0; + p.votes[msg.sender].ballot = 0x00; p.votes[msg.sender].amount = 0; p.votersPerBallot[ballot]--; p.voters--; -// enableTransfers = true; - if (!MiniMeToken(p.token).transferFrom(address(this), msg.sender, amount)) throw; -// enableTransfers = false; + require(MiniMeToken(p.token).transferFrom(address(this), msg.sender, amount)); - Unvote(_idPoll, msg.sender, ballot, amount); + emit Unvote(_idPoll, msg.sender, ballot, amount); } // Constant Helper Function - function nPolls() constant returns(uint) { + function nPolls() + public + view + returns(uint) + { return _polls.length; } - function poll(uint _idPoll) constant returns( + function poll(uint _idPoll) + public + view + returns( uint _startBlock, uint _endBlock, address _token, @@ -179,32 +192,44 @@ contract PollManager is LowLevelStringManipulator, Controlled { bool _finalized, uint _totalCensus, uint _voters - ) { - if (_idPoll >= _polls.length) throw; - Poll p = _polls[_idPoll]; + ) + { + require(_idPoll < _polls.length); + + Poll storage p = _polls[_idPoll]; + _startBlock = p.startBlock; _endBlock = p.endBlock; _token = p.token; _pollContract = p.pollContract; _canceled = p.canceled; _pollType = IPollContract(p.pollContract).pollType(); - _question = getString(p.pollContract, bytes4(sha3("question()"))); - _finalized = (!p.canceled) && (getBlockNumber() >= _endBlock); + _question = getString(p.pollContract, bytes4(keccak256("question()"))); + _finalized = (!p.canceled) && (block.number >= _endBlock); _totalCensus = MiniMeToken(p.token).totalSupply(); _voters = p.voters; } - function getVote(uint _idPoll, address _voter) constant returns (bytes32 _ballot, uint _amount) { - if (_idPoll >= _polls.length) throw; - Poll p = _polls[_idPoll]; + function getVote(uint _idPoll, address _voter) + public + view + returns (bytes32 _ballot, uint _amount) + { + require(_idPoll < _polls.length); + + Poll storage p = _polls[_idPoll]; _ballot = p.votes[_voter].ballot; _amount = p.votes[_voter].amount; } function getVotesByBallot(uint _idPoll, bytes32 _ballot) - public view returns(uint voters, uint votes) { - if (_idPoll >= _polls.length) throw; + public + view + returns(uint voters, uint votes) + { + require(_idPoll < _polls.length); + Poll storage p = _polls[_idPoll]; voters = p.votersPerBallot[_ballot]; @@ -212,29 +237,30 @@ contract PollManager is LowLevelStringManipulator, Controlled { } - function proxyPayment(address ) payable returns(bool) { + function proxyPayment(address ) + payable + returns(bool) { return false; } - function onTransfer(address , address , uint ) returns(bool) { + function onTransfer(address , address , uint ) + public + pure + returns(bool) + { return true; } - function onApprove(address , address , uint ) returns(bool) { + function onApprove(address , address , uint ) + public + pure + returns(bool) { return true; } - - function getBlockNumber() internal constant returns (uint) { - return block.number; - } - event Vote(uint indexed idPoll, address indexed _voter, bytes32 ballot, uint amount); event Unvote(uint indexed idPoll, address indexed _voter, bytes32 ballot, uint amount); event PollCanceled(uint indexed idPoll); event PollCreated(uint indexed idPoll); - - - } diff --git a/contracts/polls/SingleChoice.sol b/contracts/polls/SingleChoice.sol index 2cbcb57..ab5b250 100644 --- a/contracts/polls/SingleChoice.sol +++ b/contracts/polls/SingleChoice.sol @@ -32,7 +32,7 @@ contract SingleChoice is Controlled { function SingleChoice(address _controller, bytes _rlpDefinition, uint salt) { - uid = sha3(block.blockhash(block.number-1), salt); + uid = keccak256(block.blockhash(block.number-1), salt); controller = _controller; var itmPoll = _rlpDefinition.toRLPItem(true); @@ -57,7 +57,7 @@ contract SingleChoice is Controlled { result.length = choices.length; } - function pollType() constant returns (bytes32) { + function pollType() public constant returns (bytes32) { return bytes32("SINGLE_CHOICE"); } @@ -80,7 +80,7 @@ contract SingleChoice is Controlled { } function getBallot(uint _option) constant returns(bytes32) { - return bytes32((_option * (2**248)) + (uint(sha3(uid, _option)) & (2**248 -1))); + return bytes32((_option * (2**248)) + (uint(keccak256(uid, _option)) & (2**248 -1))); } } diff --git a/contracts/polls/SingleChoiceFactory.sol b/contracts/polls/SingleChoiceFactory.sol index 864e4d8..c38f321 100644 --- a/contracts/polls/SingleChoiceFactory.sol +++ b/contracts/polls/SingleChoiceFactory.sol @@ -5,10 +5,10 @@ import "./PollManager.sol"; contract SingleChoiceFactory is IPollFactory { uint salt; - function create(bytes _description) returns(address) { + function create(bytes _description) public returns(address) { salt++; SingleChoice sc = new SingleChoice(msg.sender, _description, salt); - SingleChoiceCreated(address(sc)); + emit SingleChoiceCreated(address(sc)); return address(sc); } From 2979fbcc2e53749c0bbd223c1a8b4dba525bdc81 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Tue, 26 Jun 2018 15:41:28 -0400 Subject: [PATCH 2/3] Changed constructor --- config/contracts.json | 15 +++++++-------- contracts/polls/IPollFactory.sol | 5 +++++ contracts/polls/PollManager.sol | 12 +++++++----- contracts/polls/SingleChoiceFactory.sol | 3 ++- test/votingdapp.js | 8 +++----- 5 files changed, 24 insertions(+), 19 deletions(-) create mode 100644 contracts/polls/IPollFactory.sol diff --git a/config/contracts.json b/config/contracts.json index ee18462..589928d 100644 --- a/config/contracts.json +++ b/config/contracts.json @@ -48,10 +48,6 @@ "TestToken": { "deploy": true }, - "MultiOptionPollManager": { - "deploy": false - }, - "SNT": { "instanceOf": "MiniMeToken", "deploy": true, @@ -83,10 +79,13 @@ ], "gasLimit": 5000000 }, - "PollManager": { - "deploy": true, - "args": ["$MiniMeTokenFactory", "$SNT"] - }, + "SingleChoiceFactory": { + "deploy": false + }, + "PollManager": { + "deploy": true, + "args": ["$MiniMeTokenFactory", "$SNT"] + }, "SingleChoice": { "deploy": false } diff --git a/contracts/polls/IPollFactory.sol b/contracts/polls/IPollFactory.sol new file mode 100644 index 0000000..e4679bd --- /dev/null +++ b/contracts/polls/IPollFactory.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.4.23; + +contract IPollFactory { + function create(bytes _description) public returns(address); +} diff --git a/contracts/polls/PollManager.sol b/contracts/polls/PollManager.sol index b5ee77d..2c1bf60 100644 --- a/contracts/polls/PollManager.sol +++ b/contracts/polls/PollManager.sol @@ -3,6 +3,10 @@ pragma solidity ^0.4.23; import "../common/Controlled.sol"; import "./LowLevelStringManipulator.sol"; import "../token/MiniMeToken.sol"; +import "./IPollFactory.sol"; +import "./SingleChoiceFactory.sol"; + + contract IPollContract { function deltaVote(int _amount, bytes32 _ballot) public returns (bool _succes); @@ -10,9 +14,6 @@ contract IPollContract { function question() public constant returns (string); } -contract IPollFactory { - function create(bytes _description) public returns(address); -} contract PollManager is LowLevelStringManipulator, Controlled { @@ -33,6 +34,7 @@ contract PollManager is LowLevelStringManipulator, Controlled { } Poll[] _polls; + IPollFactory pollFactory; MiniMeTokenFactory public tokenFactory; MiniMeToken public token; @@ -41,6 +43,7 @@ contract PollManager is LowLevelStringManipulator, Controlled { public { tokenFactory = MiniMeTokenFactory(_tokenFactory); token = MiniMeToken(_token); + pollFactory = IPollFactory(new SingleChoiceFactory()); } modifier onlySNTHolder { @@ -52,7 +55,6 @@ contract PollManager is LowLevelStringManipulator, Controlled { function addPoll( uint _startBlock, uint _endBlock, - address _pollFactory, bytes _description) public onlySNTHolder @@ -82,7 +84,7 @@ contract PollManager is LowLevelStringManipulator, Controlled { proposalSymbol, true); - p.pollContract = IPollFactory(_pollFactory).create(_description); + p.pollContract = pollFactory.create(_description); require(p.pollContract != 0); diff --git a/contracts/polls/SingleChoiceFactory.sol b/contracts/polls/SingleChoiceFactory.sol index c38f321..34e19a6 100644 --- a/contracts/polls/SingleChoiceFactory.sol +++ b/contracts/polls/SingleChoiceFactory.sol @@ -1,7 +1,8 @@ pragma solidity ^0.4.6; import "./SingleChoice.sol"; -import "./PollManager.sol"; +import "./IPollFactory.sol"; + contract SingleChoiceFactory is IPollFactory { uint salt; diff --git a/test/votingdapp.js b/test/votingdapp.js index da17144..04e79b7 100644 --- a/test/votingdapp.js +++ b/test/votingdapp.js @@ -26,12 +26,12 @@ config({ ], "gasLimit": 4000000 }, + "SingleChoiceFactory": { + "deploy": false + }, "PollManager": { "deploy": true, "args": ["$MiniMeTokenFactory", "$SNT"] - }, - "SingleChoiceFactory": { - "deploy": true } } }); @@ -84,7 +84,6 @@ describe("VotingDapp", function () { receipt = await PollManager.methods.addPoll( blockNumber, blockNumber + 10, - SingleChoiceFactory.options.address, question) .send({from: accounts[8]}); assert.fail('should have reverted before'); @@ -99,7 +98,6 @@ describe("VotingDapp", function () { receipt = await PollManager.methods.addPoll( blockNumber, blockNumber + 10, - SingleChoiceFactory.options.address, question) .send({from: accounts[0]}); From f7985fb57c15f469f1be5115fd3112267cdbc4f3 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Tue, 26 Jun 2018 16:34:28 -0400 Subject: [PATCH 3/3] Added quadratic voting --- contracts/polls/SingleChoice.sol | 24 ++++++++++++++++++++++++ test/votingdapp.js | 25 ++++++++++++++++--------- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/contracts/polls/SingleChoice.sol b/contracts/polls/SingleChoice.sol index ab5b250..3ba2230 100644 --- a/contracts/polls/SingleChoice.sol +++ b/contracts/polls/SingleChoice.sol @@ -27,7 +27,10 @@ contract SingleChoice is Controlled { string public question; string[] public choices; + int[] public result; + int[] public qvResult; + bytes32 uid; function SingleChoice(address _controller, bytes _rlpDefinition, uint salt) { @@ -55,6 +58,7 @@ contract SingleChoice is Controlled { } result.length = choices.length; + qvResult.length = choices.length; } function pollType() public constant returns (bytes32) { @@ -71,7 +75,18 @@ contract SingleChoice is Controlled { function deltaVote(int _amount, bytes32 _ballot) onlyController returns (bool _succes) { if (!isValid(_ballot)) return false; uint v = uint(_ballot) / (2**248); + result[v] += _amount; + + int qv; + if (_amount < 0) { + qv = -sqrt(-_amount); + } else { + qv = sqrt(_amount); + } + + qvResult[v] += qv; + return true; } @@ -82,6 +97,15 @@ contract SingleChoice is Controlled { function getBallot(uint _option) constant returns(bytes32) { return bytes32((_option * (2**248)) + (uint(keccak256(uid, _option)) & (2**248 -1))); } + + function sqrt(int256 x) public pure returns (int256 y) { + int256 z = (x + 1) / 2; + y = x; + while (z < y) { + y = z; + z = (x / z + z) / 2; + } + } } diff --git a/test/votingdapp.js b/test/votingdapp.js index 04e79b7..e9105e3 100644 --- a/test/votingdapp.js +++ b/test/votingdapp.js @@ -59,13 +59,13 @@ describe("VotingDapp", function () { web3.eth.getAccounts().then((acc) => { accounts = acc; - return SNT.methods.generateTokens(accounts[0], 123456).send() + return SNT.methods.generateTokens(accounts[0], 6).send() }).then((receipt) => { - return SNT.methods.generateTokens(accounts[1], 789012).send() + return SNT.methods.generateTokens(accounts[1], 12).send() }).then((receipt) => { - return SNT.methods.generateTokens(accounts[2], 345678).send() + return SNT.methods.generateTokens(accounts[2], 10).send() }).then((receipt) => { - return SNT.methods.generateTokens(accounts[3], 901234).send() + return SNT.methods.generateTokens(accounts[3], 7).send() }).then((receipt) => { done(); }); @@ -148,18 +148,25 @@ describe("VotingDapp", function () { poll = await PollManager.methods.poll(pollId).call(); let votersByBallotYES = await PollManager.methods.getVotesByBallot(pollId, Yes).call(); let tokenVotesByBallotYES = await pollContract.methods.result(0).call(); // 0 == Yes (because it is the initial option ) - - // console.dir(poll); // Will contain state of the poll - // console.log(tokenVotesByBallotYES); // Contains how many votes has a ballot - // console.log(votersByBallotYES); // Contains how many voters voted for that option + let quadraticVotesByBallotYES = await pollContract.methods.qvResult(0).call(); // 0 == Yes (because it is the initial option ) + // Will contain state of the poll + // console.dir(poll); + + // Contains how many votes has a ballot + // console.log(tokenVotesByBallotYES); + + // Contains how many votes has a ballot using quadratic voting + //console.log(quadraticVotesByBallotYES); + + // Contains how many voters voted for that option + // console.log(votersByBallotYES); // =================================================== // Unvote receipt = await PollManager.methods.unvote(pollId).send({from: accounts[0]}); assert.equal(!!receipt.events.Unvote, true, "Unvote not triggered"); - }); });