From a712cece856d4e103a8e52bd5b442c8dcec5fbe6 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Tue, 26 Jun 2018 10:19:04 -0400 Subject: [PATCH] Poll contracts --- config/contracts.json | 12 +- ...Manager.sol => MultiOptionPollManager.sol} | 4 +- contracts/polls/LowLevelStringManipulator.sol | 115 +++++ contracts/polls/PollManager.sol | 205 +++++++++ contracts/polls/RLP.sol | 416 ++++++++++++++++++ contracts/polls/SingleChoice.sol | 88 ++++ contracts/polls/SingleChoiceFactory.sol | 15 + 7 files changed, 852 insertions(+), 3 deletions(-) rename contracts/democracy/{PollManager.sol => MultiOptionPollManager.sol} (98%) create mode 100644 contracts/polls/LowLevelStringManipulator.sol create mode 100644 contracts/polls/PollManager.sol create mode 100644 contracts/polls/RLP.sol create mode 100644 contracts/polls/SingleChoice.sol create mode 100644 contracts/polls/SingleChoiceFactory.sol diff --git a/config/contracts.json b/config/contracts.json index b29e8de..ee18462 100644 --- a/config/contracts.json +++ b/config/contracts.json @@ -48,6 +48,10 @@ "TestToken": { "deploy": true }, + "MultiOptionPollManager": { + "deploy": false + }, + "SNT": { "instanceOf": "MiniMeToken", "deploy": true, @@ -79,7 +83,13 @@ ], "gasLimit": 5000000 }, - "PollManager": { "deploy": false } + "PollManager": { + "deploy": true, + "args": ["$MiniMeTokenFactory", "$SNT"] + }, + "SingleChoice": { + "deploy": false + } } } } diff --git a/contracts/democracy/PollManager.sol b/contracts/democracy/MultiOptionPollManager.sol similarity index 98% rename from contracts/democracy/PollManager.sol rename to contracts/democracy/MultiOptionPollManager.sol index 8b065c6..349d694 100644 --- a/contracts/democracy/PollManager.sol +++ b/contracts/democracy/MultiOptionPollManager.sol @@ -8,7 +8,7 @@ import "../token/MiniMeTokenFactory.sol"; * @title PollManager * @author Richard Ramos (Status Research & Development GmbH) */ -contract PollManager is Controlled { +contract MultiOptionPollManager is Controlled { event PollCreated(uint256 pollId, uint8 numOptions); event PollCanceled(uint256 pollId); event Voted(address voter, uint8[] votes); @@ -131,7 +131,7 @@ contract PollManager is Controlled { 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); diff --git a/contracts/polls/LowLevelStringManipulator.sol b/contracts/polls/LowLevelStringManipulator.sol new file mode 100644 index 0000000..4b44fe4 --- /dev/null +++ b/contracts/polls/LowLevelStringManipulator.sol @@ -0,0 +1,115 @@ +pragma solidity ^0.4.11; + +contract LowLevelStringManipulator { + +//////////////////// +// Internal helper functions to manipulate strings +///////////////// + + function strConcat(string _a, string _b, string _c, string _d, string _e) internal returns (string){ + bytes memory _ba = bytes(_a); + bytes memory _bb = bytes(_b); + bytes memory _bc = bytes(_c); + bytes memory _bd = bytes(_d); + bytes memory _be = bytes(_e); + string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length); + bytes memory babcde = bytes(abcde); + uint k = 0; + for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i]; + for (i = 0; i < _bb.length; i++) babcde[k++] = _bb[i]; + for (i = 0; i < _bc.length; i++) babcde[k++] = _bc[i]; + for (i = 0; i < _bd.length; i++) babcde[k++] = _bd[i]; + for (i = 0; i < _be.length; i++) babcde[k++] = _be[i]; + return string(babcde); + } + + function strConcat(string _a, string _b, string _c, string _d) internal returns (string) { + return strConcat(_a, _b, _c, _d, ""); + } + + function strConcat(string _a, string _b, string _c) internal returns (string) { + return strConcat(_a, _b, _c, "", ""); + } + + function strConcat(string _a, string _b) internal returns (string) { + return strConcat(_a, _b, "", "", ""); + } + + function uint2str(uint a) internal returns (string) { + return bytes32ToString(uintToBytes(a)); + } + + function uintToBytes(uint v) internal constant returns (bytes32 ret) { + if (v == 0) { + ret = '0'; + } + else { + while (v > 0) { + ret = bytes32(uint(ret) / (2 ** 8)); + ret |= bytes32(((v % 10) + 48) * 2 ** (8 * 31)); + v /= 10; + } + } + return ret; + } + + function bytes32ToString (bytes32 data) internal constant returns (string) { + bytes memory bytesString = new bytes(32); + for (uint j=0; j<32; j++) { + byte char = byte(bytes32(uint(data) * 2 ** (8 * j))); + if (char != 0) { + bytesString[j] = char; + } + } + return string(bytesString); + } + + function getTokenNameSymbol(address tokenAddr) internal returns (string name, string symbol) { + return (getString(tokenAddr, bytes4(sha3("name()"))),getString(tokenAddr, bytes4(sha3("symbol()")))); + } + + function getString(address _dst, bytes4 sig) internal returns(string) { + string memory s; + bool success1; + bool success2; + assembly { + let x := mload(0x40) //Find empty storage location using "free memory pointer" + mstore(x,sig) //Place signature at begining of empty storage + + success1 := call( //This is the critical change (Pop the top stack value) + 5000, //5k gas + _dst, //To addr + 0, //No value + x, //Inputs are stored at location x + 0x04, //Inputs are 36 byes long + x, //Store output over input (saves space) + 0x80) //Outputs are 32 bytes long + + let strL := mload(add(x, 0x20)) // Load the length of the sring + + jumpi(ask_more, gt(strL, 64)) + + mstore(0x40,add(x,add(strL, 0x40))) + + s := add(x,0x20) + ask_more: + mstore(x,sig) //Place signature at begining of empty storage + + success2 := call( //This is the critical change (Pop the top stack value) + 5000, //5k gas + _dst, //To addr + 0, //No value + x, //Inputs are stored at location x + 0x04, //Inputs are 36 byes long + x, //Store output over input (saves space) + add(0x40, strL)) //Outputs are 32 bytes long + + mstore(0x40,add(x,add(strL, 0x40))) + s := add(x,0x20) + + } + if ((!success1)||(!success2)) throw; + return s; + } + +} diff --git a/contracts/polls/PollManager.sol b/contracts/polls/PollManager.sol new file mode 100644 index 0000000..4fa2bf0 --- /dev/null +++ b/contracts/polls/PollManager.sol @@ -0,0 +1,205 @@ +pragma solidity ^0.4.11; + +import "../common/Controlled.sol"; +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); +} + +contract IPollFactory { + function create(bytes _description) returns(address); +} + +contract PollManager is LowLevelStringManipulator, Controlled { + + struct VoteLog { + bytes32 ballot; + uint amount; + } + + struct Poll { + uint startBlock; + uint endBlock; + address token; + address pollContract; + bool canceled; + mapping(address => VoteLog) votes; + } + + Poll[] _polls; + + MiniMeTokenFactory public tokenFactory; + MiniMeToken public token; + + function PollManager(address _tokenFactory, address _token) + public { + tokenFactory = MiniMeTokenFactory(_tokenFactory); + token = MiniMeToken(_token); + } + + modifier onlySNTHolder { + // TODO: require min number of tokens? + require(token.balanceOf(msg.sender) > 0); + _; + } + + function addPoll( + uint _startBlock, + uint _endBlock, + address _pollFactory, + bytes _description) + onlySNTHolder + returns (uint _idPoll) + { + if (_endBlock <= _startBlock) throw; + if (_endBlock <= getBlockNumber()) throw; + _idPoll = _polls.length; + _polls.length ++; + Poll p = _polls[ _idPoll ]; + p.startBlock = _startBlock; + p.endBlock = _endBlock; + + + var (name,symbol) = getTokenNameSymbol(address(token)); + string memory proposalName = strConcat(name , "_", uint2str(_idPoll)); + string memory proposalSymbol = strConcat(symbol, "_", uint2str(_idPoll)); + + p.token = tokenFactory.createCloneToken( + address(token), + _startBlock - 1, + proposalName, + token.decimals(), + proposalSymbol, + true); + + + p.pollContract = IPollFactory(_pollFactory).create(_description); + + if (p.pollContract == 0) throw; + } + + function cancelPoll(uint _idPoll) onlyController { + if (_idPoll >= _polls.length) throw; + Poll p = _polls[_idPoll]; + if (getBlockNumber() >= p.endBlock) throw; + p.canceled = true; + PollCanceled(_idPoll); + } + + 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; + + 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; + + p.votes[msg.sender].ballot = _ballot; + p.votes[msg.sender].amount = amount; + + if (!IPollContract(p.pollContract).deltaVote(int(amount), _ballot)) throw; + + 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; + + 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; + + + p.votes[msg.sender].ballot = 0; + p.votes[msg.sender].amount = 0; + +// enableTransfers = true; + if (!MiniMeToken(p.token).transferFrom(address(this), msg.sender, amount)) throw; +// enableTransfers = false; + + Unvote(_idPoll, msg.sender, ballot, amount); + } + +// Constant Helper Function + + function nPolls() constant returns(uint) { + return _polls.length; + } + + function poll(uint _idPoll) constant returns( + uint _startBlock, + uint _endBlock, + address _token, + address _pollContract, + bool _canceled, + bytes32 _pollType, + string _question, + bool _finalized, + uint _totalCensus + ) { + if (_idPoll >= _polls.length) throw; + Poll 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); + _totalCensus = MiniMeToken(p.token).totalSupply(); + } + + function getVote(uint _idPoll, address _voter) constant returns (bytes32 _ballot, uint _amount) { + if (_idPoll >= _polls.length) throw; + Poll p = _polls[_idPoll]; + + _ballot = p.votes[_voter].ballot; + _amount = p.votes[_voter].amount; + } + + function proxyPayment(address ) payable returns(bool) { + return false; + } + + + function onTransfer(address , address , uint ) returns(bool) { + return true; + } + + function onApprove(address , address , uint ) 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); + + + +} diff --git a/contracts/polls/RLP.sol b/contracts/polls/RLP.sol new file mode 100644 index 0000000..5bb27bb --- /dev/null +++ b/contracts/polls/RLP.sol @@ -0,0 +1,416 @@ +pragma solidity ^0.4.6; + +/** +* @title RLPReader +* +* RLPReader is used to read and parse RLP encoded data in memory. +* +* @author Andreas Olofsson (androlo1980@gmail.com) +*/ +library RLP { + + uint constant DATA_SHORT_START = 0x80; + uint constant DATA_LONG_START = 0xB8; + uint constant LIST_SHORT_START = 0xC0; + uint constant LIST_LONG_START = 0xF8; + + uint constant DATA_LONG_OFFSET = 0xB7; + uint constant LIST_LONG_OFFSET = 0xF7; + + + struct RLPItem { + uint _unsafe_memPtr; // Pointer to the RLP-encoded bytes. + uint _unsafe_length; // Number of bytes. This is the full length of the string. + } + + struct Iterator { + RLPItem _unsafe_item; // Item that's being iterated over. + uint _unsafe_nextPtr; // Position of the next item in the list. + } + + /* Iterator */ + + function next(Iterator memory self) internal constant returns (RLPItem memory subItem) { + if(hasNext(self)) { + var ptr = self._unsafe_nextPtr; + var itemLength = _itemLength(ptr); + subItem._unsafe_memPtr = ptr; + subItem._unsafe_length = itemLength; + self._unsafe_nextPtr = ptr + itemLength; + } + else + throw; + } + + function next(Iterator memory self, bool strict) internal constant returns (RLPItem memory subItem) { + subItem = next(self); + if(strict && !_validate(subItem)) + throw; + return; + } + + function hasNext(Iterator memory self) internal constant returns (bool) { + var item = self._unsafe_item; + return self._unsafe_nextPtr < item._unsafe_memPtr + item._unsafe_length; + } + + /* RLPItem */ + + /// @dev Creates an RLPItem from an array of RLP encoded bytes. + /// @param self The RLP encoded bytes. + /// @return An RLPItem + function toRLPItem(bytes memory self) internal constant returns (RLPItem memory) { + uint len = self.length; + if (len == 0) { + return RLPItem(0, 0); + } + uint memPtr; + assembly { + memPtr := add(self, 0x20) + } + return RLPItem(memPtr, len); + } + + /// @dev Creates an RLPItem from an array of RLP encoded bytes. + /// @param self The RLP encoded bytes. + /// @param strict Will throw if the data is not RLP encoded. + /// @return An RLPItem + function toRLPItem(bytes memory self, bool strict) internal constant returns (RLPItem memory) { + var item = toRLPItem(self); + if(strict) { + uint len = self.length; + if(_payloadOffset(item) > len) + throw; + if(_itemLength(item._unsafe_memPtr) != len) + throw; + if(!_validate(item)) + throw; + } + return item; + } + + /// @dev Check if the RLP item is null. + /// @param self The RLP item. + /// @return 'true' if the item is null. + function isNull(RLPItem memory self) internal constant returns (bool ret) { + return self._unsafe_length == 0; + } + + /// @dev Check if the RLP item is a list. + /// @param self The RLP item. + /// @return 'true' if the item is a list. + function isList(RLPItem memory self) internal constant returns (bool ret) { + if (self._unsafe_length == 0) + return false; + uint memPtr = self._unsafe_memPtr; + assembly { + ret := iszero(lt(byte(0, mload(memPtr)), 0xC0)) + } + } + + /// @dev Check if the RLP item is data. + /// @param self The RLP item. + /// @return 'true' if the item is data. + function isData(RLPItem memory self) internal constant returns (bool ret) { + if (self._unsafe_length == 0) + return false; + uint memPtr = self._unsafe_memPtr; + assembly { + ret := lt(byte(0, mload(memPtr)), 0xC0) + } + } + + /// @dev Check if the RLP item is empty (string or list). + /// @param self The RLP item. + /// @return 'true' if the item is null. + function isEmpty(RLPItem memory self) internal constant returns (bool ret) { + if(isNull(self)) + return false; + uint b0; + uint memPtr = self._unsafe_memPtr; + assembly { + b0 := byte(0, mload(memPtr)) + } + return (b0 == DATA_SHORT_START || b0 == LIST_SHORT_START); + } + + /// @dev Get the number of items in an RLP encoded list. + /// @param self The RLP item. + /// @return The number of items. + function items(RLPItem memory self) internal constant returns (uint) { + if (!isList(self)) + return 0; + uint b0; + uint memPtr = self._unsafe_memPtr; + assembly { + b0 := byte(0, mload(memPtr)) + } + uint pos = memPtr + _payloadOffset(self); + uint last = memPtr + self._unsafe_length - 1; + uint itms; + while(pos <= last) { + pos += _itemLength(pos); + itms++; + } + return itms; + } + + /// @dev Create an iterator. + /// @param self The RLP item. + /// @return An 'Iterator' over the item. + function iterator(RLPItem memory self) internal constant returns (Iterator memory it) { + if (!isList(self)) + throw; + uint ptr = self._unsafe_memPtr + _payloadOffset(self); + it._unsafe_item = self; + it._unsafe_nextPtr = ptr; + } + + /// @dev Return the RLP encoded bytes. + /// @param self The RLPItem. + /// @return The bytes. + function toBytes(RLPItem memory self) internal constant returns (bytes memory bts) { + var len = self._unsafe_length; + if (len == 0) + return; + bts = new bytes(len); + _copyToBytes(self._unsafe_memPtr, bts, len); + } + + /// @dev Decode an RLPItem into bytes. This will not work if the + /// RLPItem is a list. + /// @param self The RLPItem. + /// @return The decoded string. + function toData(RLPItem memory self) internal constant returns (bytes memory bts) { + if(!isData(self)) + throw; + var (rStartPos, len) = _decode(self); + bts = new bytes(len); + _copyToBytes(rStartPos, bts, len); + } + + /// @dev Get the list of sub-items from an RLP encoded list. + /// Warning: This is inefficient, as it requires that the list is read twice. + /// @param self The RLP item. + /// @return Array of RLPItems. + function toList(RLPItem memory self) internal constant returns (RLPItem[] memory list) { + if(!isList(self)) + throw; + var numItems = items(self); + list = new RLPItem[](numItems); + var it = iterator(self); + uint idx; + while(hasNext(it)) { + list[idx] = next(it); + idx++; + } + } + + /// @dev Decode an RLPItem into an ascii string. This will not work if the + /// RLPItem is a list. + /// @param self The RLPItem. + /// @return The decoded string. + function toAscii(RLPItem memory self) internal constant returns (string memory str) { + if(!isData(self)) + throw; + var (rStartPos, len) = _decode(self); + bytes memory bts = new bytes(len); + _copyToBytes(rStartPos, bts, len); + str = string(bts); + } + + /// @dev Decode an RLPItem into a uint. This will not work if the + /// RLPItem is a list. + /// @param self The RLPItem. + /// @return The decoded string. + function toUint(RLPItem memory self) internal constant returns (uint data) { + if(!isData(self)) + throw; + var (rStartPos, len) = _decode(self); + if (len > 32 || len == 0) + throw; + assembly { + data := div(mload(rStartPos), exp(256, sub(32, len))) + } + } + + /// @dev Decode an RLPItem into a boolean. This will not work if the + /// RLPItem is a list. + /// @param self The RLPItem. + /// @return The decoded string. + function toBool(RLPItem memory self) internal constant returns (bool data) { + if(!isData(self)) + throw; + var (rStartPos, len) = _decode(self); + if (len != 1) + throw; + uint temp; + assembly { + temp := byte(0, mload(rStartPos)) + } + if (temp > 1) + throw; + return temp == 1 ? true : false; + } + + /// @dev Decode an RLPItem into a byte. This will not work if the + /// RLPItem is a list. + /// @param self The RLPItem. + /// @return The decoded string. + function toByte(RLPItem memory self) internal constant returns (byte data) { + if(!isData(self)) + throw; + var (rStartPos, len) = _decode(self); + if (len != 1) + throw; + uint temp; + assembly { + temp := byte(0, mload(rStartPos)) + } + return byte(temp); + } + + /// @dev Decode an RLPItem into an int. This will not work if the + /// RLPItem is a list. + /// @param self The RLPItem. + /// @return The decoded string. + function toInt(RLPItem memory self) internal constant returns (int data) { + return int(toUint(self)); + } + + /// @dev Decode an RLPItem into a bytes32. This will not work if the + /// RLPItem is a list. + /// @param self The RLPItem. + /// @return The decoded string. + function toBytes32(RLPItem memory self) internal constant returns (bytes32 data) { + return bytes32(toUint(self)); + } + + /// @dev Decode an RLPItem into an address. This will not work if the + /// RLPItem is a list. + /// @param self The RLPItem. + /// @return The decoded string. + function toAddress(RLPItem memory self) internal constant returns (address data) { + if(!isData(self)) + throw; + var (rStartPos, len) = _decode(self); + if (len != 20) + throw; + assembly { + data := div(mload(rStartPos), exp(256, 12)) + } + } + + // Get the payload offset. + function _payloadOffset(RLPItem memory self) private constant returns (uint) { + if(self._unsafe_length == 0) + return 0; + uint b0; + uint memPtr = self._unsafe_memPtr; + assembly { + b0 := byte(0, mload(memPtr)) + } + if(b0 < DATA_SHORT_START) + return 0; + if(b0 < DATA_LONG_START || (b0 >= LIST_SHORT_START && b0 < LIST_LONG_START)) + return 1; + if(b0 < LIST_SHORT_START) + return b0 - DATA_LONG_OFFSET + 1; + return b0 - LIST_LONG_OFFSET + 1; + } + + // Get the full length of an RLP item. + function _itemLength(uint memPtr) private constant returns (uint len) { + uint b0; + assembly { + b0 := byte(0, mload(memPtr)) + } + if (b0 < DATA_SHORT_START) + len = 1; + else if (b0 < DATA_LONG_START) + len = b0 - DATA_SHORT_START + 1; + else if (b0 < LIST_SHORT_START) { + assembly { + let bLen := sub(b0, 0xB7) // bytes length (DATA_LONG_OFFSET) + let dLen := div(mload(add(memPtr, 1)), exp(256, sub(32, bLen))) // data length + len := add(1, add(bLen, dLen)) // total length + } + } + else if (b0 < LIST_LONG_START) + len = b0 - LIST_SHORT_START + 1; + else { + assembly { + let bLen := sub(b0, 0xF7) // bytes length (LIST_LONG_OFFSET) + let dLen := div(mload(add(memPtr, 1)), exp(256, sub(32, bLen))) // data length + len := add(1, add(bLen, dLen)) // total length + } + } + } + + // Get start position and length of the data. + function _decode(RLPItem memory self) private constant returns (uint memPtr, uint len) { + if(!isData(self)) + throw; + uint b0; + uint start = self._unsafe_memPtr; + assembly { + b0 := byte(0, mload(start)) + } + if (b0 < DATA_SHORT_START) { + memPtr = start; + len = 1; + return; + } + if (b0 < DATA_LONG_START) { + len = self._unsafe_length - 1; + memPtr = start + 1; + } else { + uint bLen; + assembly { + bLen := sub(b0, 0xB7) // DATA_LONG_OFFSET + } + len = self._unsafe_length - 1 - bLen; + memPtr = start + bLen + 1; + } + return; + } + + // Assumes that enough memory has been allocated to store in target. + function _copyToBytes(uint btsPtr, bytes memory tgt, uint btsLen) private constant { + // Exploiting the fact that 'tgt' was the last thing to be allocated, + // we can write entire words, and just overwrite any excess. + assembly { + { + let i := 0 // Start at arr + 0x20 + let words := div(add(btsLen, 31), 32) + let rOffset := btsPtr + let wOffset := add(tgt, 0x20) + tag_loop: + jumpi(end, eq(i, words)) + { + let offset := mul(i, 0x20) + mstore(add(wOffset, offset), mload(add(rOffset, offset))) + i := add(i, 1) + } + jump(tag_loop) + end: + mstore(add(tgt, add(0x20, mload(tgt))), 0) + } + } + } + + // Check that an RLP item is valid. + function _validate(RLPItem memory self) private constant returns (bool ret) { + // Check that RLP is well-formed. + uint b0; + uint b1; + uint memPtr = self._unsafe_memPtr; + assembly { + b0 := byte(0, mload(memPtr)) + b1 := byte(1, mload(memPtr)) + } + if(b0 == DATA_SHORT_START + 1 && b1 < DATA_SHORT_START) + return false; + return true; + } +} diff --git a/contracts/polls/SingleChoice.sol b/contracts/polls/SingleChoice.sol new file mode 100644 index 0000000..aac346a --- /dev/null +++ b/contracts/polls/SingleChoice.sol @@ -0,0 +1,88 @@ +pragma solidity ^0.4.6; + +/* + Copyright 2016, Jordi Baylina + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +import "../common/Controlled.sol"; +import "./RLP.sol"; + +contract SingleChoice is Controlled { + using RLP for RLP.RLPItem; + using RLP for RLP.Iterator; + using RLP for bytes; + + string public question; + string[] public options; + int[] public result; + bytes32 uid; + + function SingleChoice(address _controller, bytes _rlpDefinition, uint salt) { + + uid = sha3(block.blockhash(block.number-1), salt); + controller = _controller; + + var itmPoll = _rlpDefinition.toRLPItem(true); + + if (!itmPoll.isList()) throw; + + var itrPoll = itmPoll.iterator(); + + question = itrPoll.next().toAscii(); + + var itmOptions = itrPoll.next(); + + if (!itmOptions.isList()) throw; + + var itrOptions = itmOptions.iterator(); + + while(itrOptions.hasNext()) { + options.length++; + options[options.length-1] = itrOptions.next().toAscii(); + } + + result.length = options.length; + } + + function pollType() constant returns (bytes32) { + return bytes32("SINGLE_CHOICE"); + } + + function isValid(bytes32 _ballot) constant returns(bool) { + uint v = uint(_ballot) / (2**248); + if (v>=options.length) return false; + if (getBallot(v) != _ballot) return false; + return true; + } + + function deltaVote(int _amount, bytes32 _ballot) onlyController returns (bool _succes) { + if (!isValid(_ballot)) return false; + uint v = uint(_ballot) / (2**248); + result[v] += _amount; + return true; + } + + function nOptions() constant returns(uint) { + return options.length; + } + + function getBallot(uint _option) constant returns(bytes32) { + return bytes32((_option * (2**248)) + (uint(sha3(uid, _option)) & (2**248 -1))); + } +} + + + diff --git a/contracts/polls/SingleChoiceFactory.sol b/contracts/polls/SingleChoiceFactory.sol new file mode 100644 index 0000000..31d0799 --- /dev/null +++ b/contracts/polls/SingleChoiceFactory.sol @@ -0,0 +1,15 @@ +pragma solidity ^0.4.6; + +import "./SingleChoice.sol"; + +contract SingleChoiceFactory { + uint salt; + function create(bytes _description) returns(address) { + salt++; + SingleChoice sc = new SingleChoice(msg.sender, _description, salt); + SingleChoiceCreated(address(sc)); + return address(sc); + } + + event SingleChoiceCreated(address indexed addr); +}