mirror of
https://github.com/status-im/snt-voting.git
synced 2025-02-23 07:38:07 +00:00
Simplified contract to only gather votes without requiring options
This commit is contained in:
parent
06a6cceb46
commit
bfad0ae88e
@ -5,28 +5,12 @@ import CardContent from '@material-ui/core/CardContent';
|
||||
import PollManager from 'Embark/contracts/PollManager';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import { map } from 'lodash';
|
||||
import rlp from 'rlp';
|
||||
import LinearProgress from '@material-ui/core/LinearProgress';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import { withFormik } from 'formik';
|
||||
|
||||
const oneDayinBlocks = 5760;
|
||||
|
||||
const singleChoiceDef = (question, options) => {
|
||||
const d = [
|
||||
new Buffer(question),
|
||||
map(options, function(o) {
|
||||
return new Buffer(o);
|
||||
})
|
||||
];
|
||||
|
||||
const b = rlp.encode(d);
|
||||
const rlpDefinition = '0x' + b.toString('hex');
|
||||
|
||||
return rlpDefinition;
|
||||
}
|
||||
|
||||
const styles = theme => ({
|
||||
button: {
|
||||
margin: theme.spacing.unit,
|
||||
@ -108,7 +92,7 @@ const AddPoll = withFormik({
|
||||
const { addPoll } = PollManager.methods;
|
||||
const currentBlock = await getBlockNumber();
|
||||
const endTime = currentBlock + (oneDayinBlocks * 90);
|
||||
const toSend = addPoll(endTime, singleChoiceDef(description, ['YES']));
|
||||
const toSend = addPoll(endTime, description);
|
||||
|
||||
setSubmitting(true);
|
||||
|
||||
|
10
app/dapp.js
10
app/dapp.js
@ -2,7 +2,6 @@ import React, { Fragment } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import EmbarkJS from 'Embark/EmbarkJS';
|
||||
import PollManager from 'Embark/contracts/PollManager';
|
||||
import SingleChoice from 'Embark/contracts/SingleChoice';
|
||||
import AdminView from './components/AdminView';
|
||||
import Voting from './components/Voting';
|
||||
import SNT from 'Embark/contracts/SNT';
|
||||
@ -41,18 +40,9 @@ class App extends React.Component {
|
||||
const { nPolls, poll } = PollManager.methods;
|
||||
const polls = await nPolls.call();
|
||||
const total = await polls.call();
|
||||
if (total) this._setVotingOptions();
|
||||
getPolls(total, poll).then(rawPolls => { this.setState({ rawPolls })});
|
||||
}
|
||||
|
||||
async _setVotingOptions(){
|
||||
const poll = await PollManager.methods.poll(0).call();
|
||||
SingleChoice.options.address = poll._pollContract;
|
||||
const yes = await SingleChoice.methods.getBallot(0).call();
|
||||
const no = await SingleChoice.methods.getBallot(1).call();
|
||||
this.setState({ votingOptions: { yes, no } })
|
||||
}
|
||||
|
||||
_renderStatus(title, available) {
|
||||
let className = available ? 'pull-right status-online' : 'pull-right status-offline';
|
||||
return <Fragment>
|
||||
|
@ -1,5 +0,0 @@
|
||||
pragma solidity ^0.4.23;
|
||||
|
||||
contract IPollFactory {
|
||||
function create(bytes _description) public returns(address);
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
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(keccak256("name()"))),getString(tokenAddr, bytes4(keccak256("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;
|
||||
}
|
||||
|
||||
}
|
@ -1,40 +1,25 @@
|
||||
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);
|
||||
function pollType() public constant returns (bytes32);
|
||||
function question() public constant returns (string);
|
||||
}
|
||||
|
||||
|
||||
contract PollManager is LowLevelStringManipulator, Controlled {
|
||||
|
||||
struct VoteLog {
|
||||
bytes32 ballot;
|
||||
uint amount;
|
||||
}
|
||||
contract PollManager is Controlled {
|
||||
|
||||
struct Poll {
|
||||
uint startBlock;
|
||||
uint endBlock;
|
||||
address token;
|
||||
address pollContract;
|
||||
bool canceled;
|
||||
uint voters;
|
||||
mapping(bytes32 => uint) votersPerBallot;
|
||||
mapping(address => VoteLog) votes;
|
||||
string description;
|
||||
mapping(address => uint) votes;
|
||||
uint results;
|
||||
uint qvResults;
|
||||
}
|
||||
|
||||
Poll[] _polls;
|
||||
IPollFactory pollFactory;
|
||||
|
||||
MiniMeTokenFactory public tokenFactory;
|
||||
MiniMeToken public token;
|
||||
@ -43,7 +28,6 @@ contract PollManager is LowLevelStringManipulator, Controlled {
|
||||
public {
|
||||
tokenFactory = MiniMeTokenFactory(_tokenFactory);
|
||||
token = MiniMeToken(_token);
|
||||
pollFactory = IPollFactory(new SingleChoiceFactory());
|
||||
}
|
||||
|
||||
modifier onlySNTHolder {
|
||||
@ -54,7 +38,7 @@ contract PollManager is LowLevelStringManipulator, Controlled {
|
||||
|
||||
function addPoll(
|
||||
uint _endBlock,
|
||||
bytes _description)
|
||||
string _description)
|
||||
public
|
||||
onlySNTHolder
|
||||
returns (uint _idPoll)
|
||||
@ -67,26 +51,16 @@ contract PollManager is LowLevelStringManipulator, Controlled {
|
||||
p.startBlock = block.number;
|
||||
p.endBlock = _endBlock;
|
||||
p.voters = 0;
|
||||
|
||||
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));
|
||||
p.description = _description;
|
||||
|
||||
p.token = tokenFactory.createCloneToken(
|
||||
address(token),
|
||||
block.number - 1,
|
||||
proposalName,
|
||||
"SNT Voting Token",
|
||||
token.decimals(),
|
||||
proposalSymbol,
|
||||
"SVT",
|
||||
true);
|
||||
|
||||
p.pollContract = pollFactory.create(_description);
|
||||
|
||||
require(p.pollContract != 0);
|
||||
|
||||
emit PollCreated(_idPoll);
|
||||
}
|
||||
|
||||
@ -120,7 +94,16 @@ contract PollManager is LowLevelStringManipulator, Controlled {
|
||||
balance != 0;
|
||||
}
|
||||
|
||||
function vote(uint _idPoll, bytes32 _ballot) public {
|
||||
function sqrt(uint256 x) public pure returns (uint256 y) {
|
||||
uint256 z = (x + 1) / 2;
|
||||
y = x;
|
||||
while (z < y) {
|
||||
y = z;
|
||||
z = (x / z + z) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
function vote(uint _idPoll) public {
|
||||
require(_idPoll < _polls.length);
|
||||
|
||||
Poll storage p = _polls[_idPoll];
|
||||
@ -134,19 +117,16 @@ contract PollManager is LowLevelStringManipulator, Controlled {
|
||||
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;
|
||||
|
||||
p.votes[msg.sender] = amount;
|
||||
p.voters++;
|
||||
|
||||
p.results += amount;
|
||||
p.qvResults += sqrt(amount);
|
||||
|
||||
p.votersPerBallot[_ballot]++;
|
||||
|
||||
require(IPollContract(p.pollContract).deltaVote(int(amount), _ballot));
|
||||
|
||||
emit Vote(_idPoll, msg.sender, _ballot, amount);
|
||||
emit Vote(_idPoll, msg.sender, amount);
|
||||
}
|
||||
|
||||
function customVote(uint _idPoll, bytes32 _ballot, uint _amount) public {
|
||||
function customVote(uint _idPoll, uint _amount) public {
|
||||
require(_idPoll < _polls.length);
|
||||
|
||||
Poll storage p = _polls[_idPoll];
|
||||
@ -160,16 +140,13 @@ contract PollManager is LowLevelStringManipulator, Controlled {
|
||||
require(balance != 0 && balance >= _amount);
|
||||
require(MiniMeToken(p.token).transferFrom(msg.sender, address(this), _amount));
|
||||
|
||||
p.votes[msg.sender].ballot = _ballot;
|
||||
p.votes[msg.sender].amount = _amount;
|
||||
|
||||
p.votes[msg.sender] = _amount;
|
||||
p.voters++;
|
||||
|
||||
p.votersPerBallot[_ballot]++;
|
||||
p.results += _amount;
|
||||
p.qvResults += sqrt(_amount);
|
||||
|
||||
require(IPollContract(p.pollContract).deltaVote(int(_amount), _ballot));
|
||||
|
||||
emit Vote(_idPoll, msg.sender, _ballot, _amount);
|
||||
emit Vote(_idPoll, msg.sender, _amount);
|
||||
}
|
||||
|
||||
function unvote(uint _idPoll) public {
|
||||
@ -178,21 +155,18 @@ contract PollManager is LowLevelStringManipulator, Controlled {
|
||||
|
||||
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;
|
||||
uint amount = p.votes[msg.sender];
|
||||
if (amount == 0) return;
|
||||
|
||||
require(IPollContract(p.pollContract).deltaVote(-int(amount), ballot));
|
||||
|
||||
p.votes[msg.sender].ballot = 0x00;
|
||||
p.votes[msg.sender].amount = 0;
|
||||
p.votersPerBallot[ballot]--;
|
||||
p.votes[msg.sender] = 0;
|
||||
|
||||
p.voters--;
|
||||
p.results -= amount;
|
||||
p.qvResults -= sqrt(amount);
|
||||
|
||||
require(MiniMeToken(p.token).transferFrom(address(this), msg.sender, amount));
|
||||
|
||||
emit Unvote(_idPoll, msg.sender, ballot, amount);
|
||||
emit Unvote(_idPoll, msg.sender, amount);
|
||||
}
|
||||
|
||||
// Constant Helper Function
|
||||
@ -212,13 +186,13 @@ contract PollManager is LowLevelStringManipulator, Controlled {
|
||||
uint _startBlock,
|
||||
uint _endBlock,
|
||||
address _token,
|
||||
address _pollContract,
|
||||
bool _canceled,
|
||||
bytes32 _pollType,
|
||||
string _question,
|
||||
string _description,
|
||||
bool _finalized,
|
||||
uint _totalCensus,
|
||||
uint _voters
|
||||
uint _voters,
|
||||
uint _results,
|
||||
uint _qvResults
|
||||
)
|
||||
{
|
||||
require(_idPoll < _polls.length);
|
||||
@ -228,40 +202,24 @@ contract PollManager is LowLevelStringManipulator, Controlled {
|
||||
_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(keccak256("question()")));
|
||||
_description = p.description;
|
||||
_finalized = (!p.canceled) && (block.number >= _endBlock);
|
||||
_totalCensus = MiniMeToken(p.token).totalSupply();
|
||||
_voters = p.voters;
|
||||
_results = p.results;
|
||||
_qvResults = p.qvResults;
|
||||
}
|
||||
|
||||
function getVote(uint _idPoll, address _voter)
|
||||
public
|
||||
view
|
||||
returns (bytes32 _ballot, uint _amount)
|
||||
returns (uint)
|
||||
{
|
||||
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)
|
||||
{
|
||||
require(_idPoll < _polls.length);
|
||||
|
||||
Poll storage p = _polls[_idPoll];
|
||||
|
||||
voters = p.votersPerBallot[_ballot];
|
||||
votes = p.votersPerBallot[_ballot];
|
||||
|
||||
return p.votes[_voter];
|
||||
}
|
||||
|
||||
function proxyPayment(address )
|
||||
@ -286,8 +244,8 @@ contract PollManager is LowLevelStringManipulator, Controlled {
|
||||
return true;
|
||||
}
|
||||
|
||||
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 Vote(uint indexed idPoll, address indexed _voter, uint amount);
|
||||
event Unvote(uint indexed idPoll, address indexed _voter, uint amount);
|
||||
event PollCanceled(uint indexed idPoll);
|
||||
event PollCreated(uint indexed idPoll);
|
||||
}
|
||||
|
@ -1,416 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 choices;
|
||||
|
||||
int[] public result;
|
||||
int[] public qvResult;
|
||||
|
||||
bytes32 uid;
|
||||
|
||||
function SingleChoice(address _controller, bytes _rlpDefinition, uint salt) {
|
||||
|
||||
uid = keccak256(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()) {
|
||||
choices.length++;
|
||||
choices[choices.length-1] = itrOptions.next().toAscii();
|
||||
}
|
||||
|
||||
result.length = choices.length;
|
||||
qvResult.length = choices.length;
|
||||
}
|
||||
|
||||
function pollType() public constant returns (bytes32) {
|
||||
return bytes32("SINGLE_CHOICE");
|
||||
}
|
||||
|
||||
function isValid(bytes32 _ballot) constant returns(bool) {
|
||||
uint v = uint(_ballot) / (2**248);
|
||||
if (v>=choices.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;
|
||||
|
||||
int qv;
|
||||
if (_amount < 0) {
|
||||
qv = -sqrt(-_amount);
|
||||
} else {
|
||||
qv = sqrt(_amount);
|
||||
}
|
||||
|
||||
qvResult[v] += qv;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function nOptions() constant returns(uint) {
|
||||
return choices.length;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
pragma solidity ^0.4.6;
|
||||
|
||||
import "./SingleChoice.sol";
|
||||
import "./IPollFactory.sol";
|
||||
|
||||
|
||||
contract SingleChoiceFactory is IPollFactory {
|
||||
uint salt;
|
||||
function create(bytes _description) public returns(address) {
|
||||
salt++;
|
||||
SingleChoice sc = new SingleChoice(msg.sender, _description, salt);
|
||||
emit SingleChoiceCreated(address(sc));
|
||||
return address(sc);
|
||||
}
|
||||
|
||||
event SingleChoiceCreated(address indexed addr);
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
const utils = require('../utils/testUtils')
|
||||
const assert = require('assert');
|
||||
const SingleChoice = require('Embark/contracts/SingleChoice');
|
||||
const BN = web3.utils.BN;
|
||||
var _ = require('lodash');
|
||||
var rlp = require('rlp');
|
||||
@ -26,9 +25,6 @@ config({
|
||||
],
|
||||
"gasLimit": 4000000
|
||||
},
|
||||
"SingleChoiceFactory": {
|
||||
"deploy": false
|
||||
},
|
||||
"PollManager": {
|
||||
"deploy": true,
|
||||
"args": ["$MiniMeTokenFactory", "$SNT"]
|
||||
@ -36,20 +32,6 @@ config({
|
||||
}
|
||||
});
|
||||
|
||||
singleChoiceDef = (question, options) => {
|
||||
var d = [
|
||||
new Buffer(question),
|
||||
_.map(options, function(o) {
|
||||
return new Buffer(o);
|
||||
})
|
||||
];
|
||||
|
||||
var b= rlp.encode(d);
|
||||
var rlpDefinition = '0x' + b.toString('hex');
|
||||
|
||||
return rlpDefinition;
|
||||
}
|
||||
|
||||
describe("VotingDapp", function () {
|
||||
this.timeout(0);
|
||||
|
||||
@ -74,7 +56,7 @@ describe("VotingDapp", function () {
|
||||
it("Test", async () => {
|
||||
|
||||
const blockNumber = await web3.eth.getBlockNumber();
|
||||
const question = singleChoiceDef("Move from Slack to Status Desktop", [ "Yes", "No" ]);
|
||||
const question = "Move from Slack to Status Desktop";
|
||||
let receipt;
|
||||
|
||||
|
||||
@ -103,14 +85,7 @@ describe("VotingDapp", function () {
|
||||
|
||||
const pollId = receipt.events.PollCreated.returnValues.idPoll;
|
||||
let poll = await PollManager.methods.poll(pollId).call();
|
||||
|
||||
SingleChoice.options.address = poll._pollContract;
|
||||
const pollContract = SingleChoice;
|
||||
|
||||
// Options are represented as a hex value
|
||||
const Yes = await SingleChoice.methods.getBallot(0).call();
|
||||
const No = await SingleChoice.methods.getBallot(1).call();
|
||||
|
||||
|
||||
|
||||
// ===================================================
|
||||
// Determining if I can vote por a proposal
|
||||
@ -120,23 +95,24 @@ describe("VotingDapp", function () {
|
||||
|
||||
// ===================================================
|
||||
// Voting
|
||||
receipt = await PollManager.methods.vote(pollId, Yes).send({from: accounts[0]});
|
||||
receipt = await PollManager.methods.vote(pollId).send({from: accounts[0]});
|
||||
assert.equal(!!receipt.events.Vote, true, "Vote not triggered");
|
||||
|
||||
receipt = await PollManager.methods.customVote(pollId, Yes, 12).send({from: accounts[1]});
|
||||
receipt = await PollManager.methods.customVote(pollId, 12).send({from: accounts[1]});
|
||||
assert.equal(!!receipt.events.Vote, true, "Vote not triggered");
|
||||
|
||||
|
||||
// ===================================================
|
||||
// Getting what option the voter selected
|
||||
let myVote = await PollManager.methods.getVote(pollId, accounts[0]).call();
|
||||
assert.equal(myVote._ballot, Yes, "Vote is different from selected");
|
||||
const balance = await SNT.methods.balanceOf(accounts[0]).call();
|
||||
assert.equal(myVote, balance, "Vote is different from selected");
|
||||
|
||||
|
||||
// ===================================================
|
||||
// Voting when you're not a SNT holder SHOULD FAIL!
|
||||
try {
|
||||
receipt = await PollManager.methods.vote(pollId, Yes)
|
||||
receipt = await PollManager.methods.vote(pollId)
|
||||
.send({from: accounts[8]});
|
||||
assert.fail('should have reverted before');
|
||||
} catch(error) {
|
||||
@ -147,21 +123,21 @@ describe("VotingDapp", function () {
|
||||
// ===================================================
|
||||
// Getting proposal information
|
||||
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 )
|
||||
let quadraticVotesByBallotYES = await pollContract.methods.qvResult(0).call(); // 0 == Yes (because it is the initial option )
|
||||
let tokenVotes = poll._results;
|
||||
let quadraticVotes = poll._qvResults;
|
||||
let voters = poll._voters;
|
||||
|
||||
// Will contain state of the poll
|
||||
// console.dir(poll);
|
||||
|
||||
// Contains how many votes has a ballot
|
||||
//console.log(tokenVotesByBallotYES);
|
||||
// Contains how many voters
|
||||
// console.log(voters);
|
||||
|
||||
// Contains how many votes has a ballot using quadratic voting
|
||||
//console.log(quadraticVotesByBallotYES);
|
||||
// Contains how many votes using quadratic voting
|
||||
// console.log(quadraticVotes);
|
||||
|
||||
// Contains how many voters voted for that option
|
||||
// console.log(votersByBallotYES);
|
||||
// Contains how many votes
|
||||
// console.log(tokenVotes);
|
||||
|
||||
// ===================================================
|
||||
// Unvote
|
||||
|
Loading…
x
Reference in New Issue
Block a user