@daostack/infra-experimental
Version:
Base layer DAO's components
1,149 lines (962 loc) • 89.3 kB
JavaScript
const helpers = require('./helpers');
const GenesisProtocol = artifacts.require("./GenesisProtocol.sol");
const ERC827TokenMock = artifacts.require('./test/ERC827TokenMock.sol');
const GenesisProtocolCallbacks = artifacts.require("./GenesisProtocolCallbacksMock.sol");
var ethereumjs = require('ethereumjs-abi');
const Reputation = artifacts.require("./Reputation.sol");
const BigNumber = require('bignumber.js');
class GenesisProtocolParams {
constructor() {
}
}
const setupGenesisProtocolParams = async function(
testSetup,
voteOnBehalf = helpers.NULL_ADDRESS,
_queuedVoteRequiredPercentage=50,
_queuedVotePeriodLimit=60,
_boostedVotePeriodLimit=60,
_preBoostedVotePeriodLimit =0,
_thresholdConst=2000,
_quietEndingPeriod=0,
_proposingRepReward=60,
_votersReputationLossRatio=10,
_minimumDaoBounty=15,
_daoBountyConst=1000,
_activationTime=0,
) {
var genesisProtocolParams = new GenesisProtocolParams();
await testSetup.genesisProtocolCallbacks.setParameters([_queuedVoteRequiredPercentage,
_queuedVotePeriodLimit,
_boostedVotePeriodLimit,
_preBoostedVotePeriodLimit,
_thresholdConst,
_quietEndingPeriod,
_proposingRepReward,
_votersReputationLossRatio,
_minimumDaoBounty,
_daoBountyConst,
_activationTime],
voteOnBehalf);
genesisProtocolParams.paramsHash = await testSetup.genesisProtocol.getParametersHash([_queuedVoteRequiredPercentage,
_queuedVotePeriodLimit,
_boostedVotePeriodLimit,
_preBoostedVotePeriodLimit,
_thresholdConst,
_quietEndingPeriod,
_proposingRepReward,
_votersReputationLossRatio,
_minimumDaoBounty,
_daoBountyConst,
_activationTime],
voteOnBehalf);
return genesisProtocolParams;
};
var YES,NO;
const setup = async function (accounts,
_voteOnBehalf = helpers.NULL_ADDRESS,
_queuedVoteRequiredPercentage=50,
_queuedVotePeriodLimit=60,
_boostedVotePeriodLimit=60,
_preBoostedVotePeriodLimit =0,
_thresholdConst=2000,
_quietEndingPeriod=0,
_proposingRepReward=60,
_votersReputationLossRatio=10,
_minimumDaoBounty=15,
_daoBountyConst=1000,
_activationTime=0) {
var testSetup = new helpers.TestSetup();
testSetup.stakingToken = await ERC827TokenMock.new(accounts[0],web3.utils.toWei(((new BigNumber(2)).pow(200)).toString(10)));
testSetup.genesisProtocol = await GenesisProtocol.new(testSetup.stakingToken.address);
testSetup.reputationArray = [200, 100, 700 ];
testSetup.org = {};
//let reputationMinimeTokenFactory = await ReputationMinimeTokenFactory.new();
testSetup.org.reputation = await Reputation.new();
await testSetup.org.reputation.initialize(accounts[0]) ;
await testSetup.org.reputation.mint(accounts[0],testSetup.reputationArray[0]);
await testSetup.org.reputation.mint(accounts[1],testSetup.reputationArray[1]);
await testSetup.org.reputation.mint(accounts[2],testSetup.reputationArray[2]);
await testSetup.stakingToken.transfer(accounts[1],web3.utils.toWei('3'));
await testSetup.stakingToken.transfer(accounts[2],1000);
testSetup.genesisProtocolCallbacks = await GenesisProtocolCallbacks.new();
await testSetup.genesisProtocolCallbacks.initialize(
testSetup.org.reputation.address,
testSetup.stakingToken.address,
testSetup.genesisProtocol.address
);
await testSetup.org.reputation.transferOwnership(testSetup.genesisProtocolCallbacks.address);
testSetup.genesisProtocolParams= await setupGenesisProtocolParams(testSetup,
_voteOnBehalf,
_queuedVoteRequiredPercentage,
_queuedVotePeriodLimit,
_boostedVotePeriodLimit,
_preBoostedVotePeriodLimit,
_thresholdConst,
_quietEndingPeriod,
_proposingRepReward,
_votersReputationLossRatio,
_minimumDaoBounty,
_daoBountyConst,
_activationTime);
YES = await testSetup.genesisProtocol.YES();
YES = YES.toNumber();
NO = await testSetup.genesisProtocol.NO();
NO = NO.toNumber();
testSetup.proposer = accounts[0];
return testSetup;
};
const proposalStateIndex = 2;
const boostedState = 5;
const preBoostedState = 4;
const proposalTotalStakesIndex = 9;
const numberOfChoices = 2;
const checkProposalInfo = async function(proposalId, _proposalInfo,_times,genesisProtocol) {
let proposalInfo = await genesisProtocol.proposals(proposalId);
// proposalInfo has the following structure
// bytes32 organizationId;
assert.equal(proposalInfo[0], _proposalInfo[0]);
// address callbacks;
assert.equal(proposalInfo[1], _proposalInfo[1]);
//ProposalState state
assert.equal(proposalInfo[2], _proposalInfo[2]);
// uint winningVote
assert.equal(proposalInfo[3], _proposalInfo[3]);
//address proposer
assert.equal(proposalInfo[4], _proposalInfo[4]);
//uint currentBoostedVotePeriodLimit
assert.equal(proposalInfo[5], _proposalInfo[5]);
//bytes32 paramsHash
assert.equal(proposalInfo[6], _proposalInfo[6]);
//uint daoBountyRemain
assert.equal(proposalInfo[7], _proposalInfo[7]);
//uint daoBounty
assert.equal(proposalInfo[8], _proposalInfo[8]);
//int totalStakes
assert.equal(proposalInfo[9], _proposalInfo[9]);
//int threshold
assert.equal(proposalInfo[10], _proposalInfo[10]);
//uint secondsFromTimeOutTillExecuteBoosted
assert.equal(proposalInfo[11], _proposalInfo[11]);
// - the mapping and array are simply not returned at all in the array
checkProposalTimes(proposalId,_times,genesisProtocol);
};
const checkProposalTimes = async function(proposalId,_times,genesisProtocol) {
//times[0] - submittedTime
//times[1] - boostedPhaseTime
//times[2] -preBoostedPhaseTime;
let times = await genesisProtocol.getProposalTimes(proposalId);
assert.equal(times[0], _times[0]);
assert.equal(times[1], _times[1]);
assert.equal(times[2], _times[2]);
};
const checkVotesStatus = async function(proposalId, _votesStatus,genesisProtocol){
return helpers.checkVotesStatus(proposalId, _votesStatus,genesisProtocol);
};
const checkIsVotable = async function(proposalId, _votable,genesisProtocol){
let votable;
votable = await genesisProtocol.isVotable(proposalId);
assert.equal(votable, _votable);
};
const checkVoteInfo = async function(proposalId, voterAddress, _voteInfo, genesisProtocol) {
let voteInfo;
voteInfo = await genesisProtocol.voteInfo(proposalId, voterAddress);
// voteInfo has the following structure
// int vote;
assert.equal(voteInfo[0], _voteInfo[0]);
// uint reputation;
assert.equal(voteInfo[1], _voteInfo[1]);
};
const propose = async function(_testSetup,_proposer = 0) {
if (_proposer === 0) {
_proposer = _testSetup.proposer;
}
let tx = await _testSetup.genesisProtocolCallbacks.propose(numberOfChoices,
_testSetup.genesisProtocolParams.paramsHash,
_testSetup.genesisProtocolCallbacks.address,
_proposer,
helpers.NULL_ADDRESS);
const proposalId = await helpers.getValueFromLogs(tx, '_proposalId');
assert.equal(tx.logs.length, 2);
assert.equal(tx.logs[0].event, "NewProposal");
assert.equal(tx.logs[0].args._proposalId, proposalId);
assert.equal(tx.logs[0].args._proposer, _proposer);
assert.equal(tx.logs[0].args._paramsHash, _testSetup.genesisProtocolParams.paramsHash);
assert.equal(proposalId,await helpers.getProposalId(tx,_testSetup.genesisProtocol,"NewProposal"));
assert.isOk(proposalId);
return proposalId;
};
const REAL_FBITS = 40;
const threshold = async function(_testSetup) {
const organizationId = await web3.utils.soliditySha3(_testSetup.genesisProtocolCallbacks.address,helpers.NULL_ADDRESS);
var t = await _testSetup.genesisProtocol.threshold(_testSetup.genesisProtocolParams.paramsHash,organizationId);
return (t.shrn(REAL_FBITS).toNumber() + (t.maskn(REAL_FBITS)/Math.pow(2,REAL_FBITS))).toFixed(2);
};
const score = async function(_testSetup,proposalId) {
var s = await _testSetup.genesisProtocol.score(proposalId);
return (s.shrn(REAL_FBITS).toNumber() + (s.maskn(REAL_FBITS)/Math.pow(2,REAL_FBITS))).toFixed(2);
};
const signatureType = 1;
function fixSignature (signature) {
// in geth its always 27/28, in ganache its 0/1. Change to 27/28 to prevent
// signature malleability if version is 0/1
// see https://github.com/ethereum/go-ethereum/blob/v1.8.23/internal/ethapi/api.go#L465
let v = parseInt(signature.slice(130, 132), 16);
if (v < 27) {
v += 27;
}
const vHex = v.toString(16);
return signature.slice(0, 130) + vHex;
}
// signs message in node (ganache auto-applies "Ethereum Signed Message" prefix)
async function signMessage (signer, messageHex = '0x') {
return fixSignature(await web3.eth.sign(messageHex, signer));
}
const stake = async function(_testSetup,_proposalId,_vote,_amount,_staker,eventName = 'Stake') {
var nonce = (await _testSetup.genesisProtocol.stakesNonce(_staker)).toString();
var textMsg = "0x"+ethereumjs.soliditySHA3(
["address","bytes32","uint", "uint","uint"],
[_testSetup.genesisProtocol.address, _proposalId,_vote,_amount, nonce]
).toString("hex");
//https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethsign
let signature = await signMessage(_staker,textMsg);
const encodeABI = await new web3.eth.Contract(_testSetup.genesisProtocol.abi).methods.stakeWithSignature(_proposalId,_vote,_amount,nonce,signatureType,signature).encodeABI();
var transaction;
try {
transaction = await _testSetup.stakingToken.approveAndCall(
_testSetup.genesisProtocol.address, _amount, encodeABI ,{from : _staker}
);
} catch (ex) {
return "revert";
}
var stakeLog;
await _testSetup.genesisProtocol.getPastEvents(eventName,
{_proposalId: _proposalId},
{fromBlock: transaction.blockNumber}
)
.then(function(events){
stakeLog = events;
});
return stakeLog;
};
//use this method to approve and call stake with GEN token
//GEN token use old version of ERC827 which implemnt approve with data abi by
//overloading standardToken approve function approve(address _spender,uint256 _amount,bytes data);
// const stakeGENToken = async function(_testSetup,_proposalId,_vote,_amount,_staker) {
// var textMsg = "0x"+ethereumjs.soliditySHA3(
// ["address","bytes32","uint", "uint","uint"],
// [_testSetup.genesisProtocol.address, _proposalId,_vote,_amount, nonce]
// ).toString("hex");
// const signature = web3.eth.sign(_staker, textMsg);
// var ethjsABI = require('ethjs-abi');
// const extraData = await _testSetup.genesisProtocol.stake.request(_proposalId,_vote,_amount,nonce,signature);
//
// const abiMethodString = '{ "constant": false,' +
// '"inputs":[ { "name": "_spender", "type": "address" },' +
// '{ "name": "_value", "type": "uint256" },' +
// '{ "name": "_data", "type": "bytes" } ],' +
// '"name": "approve",' +
// '"outputs": [ { "name": "", "type": "bool"} ],'+
// '"payable": false,' +
// '"stateMutability": "nonpayable",' +
// '"type": "function" }';
// var abiMethod = JSON.parse(abiMethodString);
//
// const approveData = ethjsABI.encodeMethod(abiMethod,
// [_testSetup.genesisProtocol.address, _amount, extraData.params[0].data]
// );
// const transaction = await _testSetup.stakingToken.sendTransaction(
// { from : _staker,data: approveData }
// );
// const stakeLog = await new Promise((resolve) => {
// _testSetup.genesisProtocol.Stake({_proposalId: _proposalId}, {fromBlock: transaction.blockNumber})
// .get((err,events) => {
// resolve(events);
// });
// });
// return stakeLog;
// };
contract('GenesisProtocol', accounts => {
it("staking token address", async() => {
var testSetup = await setup(accounts);
assert.equal(await testSetup.genesisProtocol.stakingToken(),testSetup.stakingToken.address);
});
it("Sanity checks", async function () {
var testSetup = await setup(accounts);
let winningVote = 2;
let state = 3; //Qued
//propose a vote
const proposalId = await propose(testSetup);
const organizationId = await web3.utils.soliditySha3(testSetup.genesisProtocolCallbacks.address,helpers.NULL_ADDRESS);
var submittedTime = (await web3.eth.getBlock("latest")).timestamp;
var currentBoostedVotePeriodLimit = 60;
var daoBountyRemain = 15;
await checkProposalInfo(proposalId, [
organizationId,
testSetup.genesisProtocolCallbacks.address,
state,
winningVote,
helpers.NULL_ADDRESS,
currentBoostedVotePeriodLimit,
testSetup.genesisProtocolParams.paramsHash,
daoBountyRemain,
0, //daoBounty
0, //totalStake
0,
0,
],
[submittedTime,0,0],
testSetup.genesisProtocol);
await checkVotesStatus(proposalId, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],testSetup.genesisProtocol);
await checkIsVotable(proposalId, true,testSetup.genesisProtocol);
// now lets vote Option 2 with a minority reputation
await testSetup.genesisProtocol.vote(proposalId, 1,0,helpers.NULL_ADDRESS);
//cancel vote do nothing
await testSetup.genesisProtocol.cancelVote(proposalId);
assert.equal(await testSetup.genesisProtocol.getNumberOfChoices(proposalId),2);
assert.equal(await testSetup.genesisProtocol.voteStake(proposalId,1),0);
assert.equal(await testSetup.genesisProtocol.isAbstainAllow(),false);
winningVote = 1;
var proposalStatus = await testSetup.genesisProtocol.proposalStatus(proposalId);
assert.equal(testSetup.reputationArray[0],proposalStatus[0]);
assert.equal(0,proposalStatus[1]);
await checkProposalInfo(proposalId, [
organizationId,
testSetup.genesisProtocolCallbacks.address,
state,
winningVote,
helpers.NULL_ADDRESS,
currentBoostedVotePeriodLimit,
testSetup.genesisProtocolParams.paramsHash,
daoBountyRemain,
0, //daoBounty
0, //totalStake
0,
0
],
[submittedTime,0,0],
testSetup.genesisProtocol);
await checkVoteInfo(proposalId, accounts[0], [1, testSetup.reputationArray[0]],testSetup.genesisProtocol);
await checkVotesStatus(proposalId, [0, testSetup.reputationArray[0],0],testSetup.genesisProtocol);
await checkIsVotable(proposalId, true,testSetup.genesisProtocol);
// another minority reputation (Option 0):
await testSetup.genesisProtocol.vote(proposalId, 2,0,helpers.NULL_ADDRESS,{ from: accounts[1] });
await checkVoteInfo(proposalId, accounts[1], [2, testSetup.reputationArray[1]],testSetup.genesisProtocol);
proposalStatus = await testSetup.genesisProtocol.proposalStatus(proposalId);
assert.equal(testSetup.reputationArray[0],proposalStatus[0]);
assert.equal(testSetup.reputationArray[1],proposalStatus[1]);
await checkProposalInfo(proposalId,[
organizationId,
testSetup.genesisProtocolCallbacks.address,
state,
winningVote,
helpers.NULL_ADDRESS,
currentBoostedVotePeriodLimit,
testSetup.genesisProtocolParams.paramsHash,
daoBountyRemain,
0, //daoBounty
0, //totalStake
0,
0
],
[submittedTime,0,0],
testSetup.genesisProtocol);
await checkVotesStatus(proposalId, [0,testSetup.reputationArray[0], testSetup.reputationArray[1]],testSetup.genesisProtocol);
await checkIsVotable(proposalId, true,testSetup.genesisProtocol);
});
it("check organization params validity", async function() {
var queuedVoteRequiredPercentage = 0;
var votersReputationLossRatio = 1;
var thresholdConst = 2000;
try {
await setup(accounts,
helpers.NULL_ADDRESS,
queuedVoteRequiredPercentage,
60,//_queuedVotePeriodLimit
60,//_boostedVotePeriodLimit
0,//_preBoostedVotePeriodLimit
thresholdConst,//_thresholdConst
20,//_quietEndingPeriod
60,//_proposingRepReward
0,//_votersReputationLossRatio
1,//_minimumDaoBounty
1//_daoBountyConst
);
assert(false, " 50 <= queuedVoteRequiredPercentage <=100 ");
} catch(error) {
helpers.assertVMException(error);
}
queuedVoteRequiredPercentage = 101;
try {
await setup(accounts,helpers.NULL_ADDRESS,
queuedVoteRequiredPercentage,
60,//_queuedVotePeriodLimit
60,//_boostedVotePeriodLimit
0,//_preBoostedVotePeriodLimit
thresholdConst,//_thresholdConst
20,//_quietEndingPeriod
60,//_proposingRepReward
0,//_votersReputationLossRatio
1,//_minimumDaoBounty
1//_daoBountyConst
);
assert(false, " 50 <= queuedVoteRequiredPercentage <=100 ");
} catch(error) {
helpers.assertVMException(error);
}
queuedVoteRequiredPercentage = 100;
votersReputationLossRatio = 100;
thresholdConst = 0;
try {
await setup(accounts,helpers.NULL_ADDRESS,
queuedVoteRequiredPercentage,
60,//_queuedVotePeriodLimit
60,//_boostedVotePeriodLimit
0,//_preBoostedVotePeriodLimit
thresholdConst,//_thresholdConst
20,//_quietEndingPeriod
60,//_proposingRepReward
votersReputationLossRatio,//_votersReputationLossRatio
1,//_minimumDaoBounty
1//_daoBountyConst
);
assert(false, " thresholdConst > 0 ");
} catch(error) {
helpers.assertVMException(error);
}
thresholdConst = 2000;
try {
await setup(accounts,helpers.NULL_ADDRESS,
queuedVoteRequiredPercentage,
60,//_queuedVotePeriodLimit
60,//_boostedVotePeriodLimit
0,//_preBoostedVotePeriodLimit
thresholdConst,//_thresholdConst
20,//_quietEndingPeriod
60,//_proposingRepReward
votersReputationLossRatio,//_votersReputationLossRatio
1,//_minimumDaoBounty
0//_daoBountyConst
);
assert(false, " _daoBountyConst > 0 ");
} catch(error) {
helpers.assertVMException(error);
}
try {
await setup(accounts,helpers.NULL_ADDRESS,
queuedVoteRequiredPercentage,
60,//_queuedVotePeriodLimit
60,//_boostedVotePeriodLimit
0,//_preBoostedVotePeriodLimit
thresholdConst,//_thresholdConst
20,//_quietEndingPeriod
60,//_proposingRepReward
votersReputationLossRatio,//_votersReputationLossRatio
0,//_minimumDaoBounty
1//_daoBountyConst
);
assert(false, " _minimumDaoBounty > 0 ");
} catch(error) {
helpers.assertVMException(error);
}
});
it("log the VoteProposal event on voting ", async function() {
var testSetup = await setup(accounts);
const proposalId = await propose(testSetup);
let voteTX = await testSetup.genesisProtocol.vote(proposalId, 1,0,helpers.NULL_ADDRESS);
assert.equal(voteTX.logs.length, 1);
assert.equal(voteTX.logs[0].event, "VoteProposal");
assert.equal(voteTX.logs[0].args._proposalId, proposalId);
assert.equal(voteTX.logs[0].args._voter, accounts[0]);
assert.equal(voteTX.logs[0].args._vote, 1);
assert.equal(voteTX.logs[0].args._reputation, testSetup.reputationArray[0]);
});
it("should log the ExecuteProposal event", async function() {
var testSetup = await setup(accounts);
const proposalId = await propose(testSetup);
// now lets vote with a minority reputation
await testSetup.genesisProtocol.vote(proposalId, 1,0,helpers.NULL_ADDRESS);
//test that reputation change does not effect the snapshot
var account2Rep =await testSetup.org.reputation.balanceOf(accounts[2]);
assert.equal(account2Rep,testSetup.reputationArray[2]);
await testSetup.genesisProtocolCallbacks.burnReputationTest(account2Rep,accounts[2],helpers.NULL_HASH);
account2Rep =await testSetup.org.reputation.balanceOf(accounts[2]);
assert.equal(account2Rep,0);
// // the decisive vote is cast now and the proposal will be executed
var tx = await testSetup.genesisProtocol.vote(proposalId, 2,0,helpers.NULL_ADDRESS, { from: accounts[2] });
assert.equal(tx.logs.length, 4);
assert.equal(tx.logs[1].event, "ExecuteProposal");
assert.equal(tx.logs[1].args._proposalId, proposalId);
assert.equal(tx.logs[1].args._decision, 2);
assert.equal(tx.logs[2].event, "GPExecuteProposal");
assert.equal(tx.logs[2].args._executionState, 1); //QueBarCrossed
assert.equal(tx.logs[3].event, "StateChange");
assert.equal(tx.logs[3].args._proposalState, 2);
});
it("should log the ExecuteProposal event after time pass for preBoostedVotePeriodLimit (decision == 2 )", async function() {
var testSetup = await setup(accounts,helpers.NULL_ADDRESS,50,2);
const proposalId = await propose(testSetup);
// now lets vote with a minority reputation
await testSetup.genesisProtocol.vote(proposalId, 1,0,helpers.NULL_ADDRESS);
await helpers.increaseTime(3);
// the decisive vote is cast now and the proposal will be executed
var tx = await testSetup.genesisProtocol.vote(proposalId, 1,0,helpers.NULL_ADDRESS, { from: accounts[2] });
assert.equal(tx.logs.length, 3);
assert.equal(tx.logs[0].event, "ExecuteProposal");
assert.equal(tx.logs[0].args._proposalId, proposalId);
assert.equal(tx.logs[0].args._decision, 2);
assert.equal(tx.logs[1].event, "GPExecuteProposal");
assert.equal(tx.logs[1].args._executionState, 2);//QueTimeOut
await testSetup.genesisProtocolCallbacks.getPastEvents('LogBytes32',
{fromBlock: tx.blockNumber}
)
.then(function(events){
assert.equal(events[0].args._msg,proposalId);
});
});
it("All options can be voted (1-2)", async function() {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
// Option 1
await testSetup.genesisProtocol.vote(proposalId, 1,0,helpers.NULL_ADDRESS);
await checkVoteInfo(proposalId, accounts[0], [1, testSetup.reputationArray[0]],testSetup.genesisProtocol);
await checkVotesStatus(proposalId, [0,testSetup.reputationArray[0], 0],testSetup.genesisProtocol);
await checkIsVotable(proposalId,true,testSetup.genesisProtocol);
testSetup = await setup(accounts);
var tx = await testSetup.genesisProtocolCallbacks.propose(2, testSetup.genesisProtocolParams.paramsHash,helpers.NULL_ADDRESS,accounts[0],helpers.NULL_ADDRESS);
proposalId = await helpers.getValueFromLogs(tx, '_proposalId');
// Option 2
await testSetup.genesisProtocol.vote(proposalId, 2,0,helpers.NULL_ADDRESS);
await checkVoteInfo(proposalId, accounts[0], [2, testSetup.reputationArray[0]],testSetup.genesisProtocol);
await checkVotesStatus(proposalId, [0,0, testSetup.reputationArray[0]],testSetup.genesisProtocol);
await checkIsVotable(proposalId,true,testSetup.genesisProtocol);
});
it("cannot vote with invalid options", async function() {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
try {
await testSetup.genesisProtocol.vote(proposalId, 3,0,helpers.NULL_ADDRESS);
assert(false, "vote was supposed to throw because invalid option was selected");
} catch(error) {
helpers.assertVMException(error);
}
try {
await testSetup.genesisProtocol.vote(proposalId, 0,0,helpers.NULL_ADDRESS);
assert(false, "vote was supposed to throw because invalid option was selected");
} catch(error) {
helpers.assertVMException(error);
}
});
it("cannot re vote", async function() {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
await testSetup.genesisProtocol.vote(proposalId, 2,0,helpers.NULL_ADDRESS);
await checkVoteInfo(proposalId, accounts[0], [2, testSetup.reputationArray[0]],testSetup.genesisProtocol);
await checkVotesStatus(proposalId, [0,0,testSetup.reputationArray[0]],testSetup.genesisProtocol);
await checkIsVotable(proposalId,true,testSetup.genesisProtocol);
await testSetup.genesisProtocol.vote(proposalId, 1,0,helpers.NULL_ADDRESS);
await checkVoteInfo(proposalId, accounts[0], [2, testSetup.reputationArray[0]],testSetup.genesisProtocol);
await checkVotesStatus(proposalId, [0,0,testSetup.reputationArray[0]],testSetup.genesisProtocol);
await checkIsVotable(proposalId,true,testSetup.genesisProtocol);
});
it("Non-existent parameters hash shouldn't work - propose with wrong organization", async function() {
var testSetup = await setup(accounts);
await testSetup.genesisProtocolCallbacks.setParameters([50,
60,
60,
0,
2000,
0,
60,
10,
15,
1000,
0],
helpers.NULL_ADDRESS);
await testSetup.genesisProtocolCallbacks.propose(2, testSetup.genesisProtocolParams.paramsHash,helpers.NULL_ADDRESS,accounts[0],helpers.NULL_ADDRESS);
try {
await testSetup.genesisProtocolCallbacks.propose(2, helpers.NULL_HASH, helpers.NULL_ADDRESS,accounts[0],helpers.NULL_ADDRESS);
assert(false, "propose was supposed to throw because wrong organization address was sent");
} catch(error) {
helpers.assertVMException(error);
}
});
it("Invalid percentage required( < 0 || > 100) shouldn't work", async function() {
try {
await setup(accounts,helpers.NULL_ADDRESS,150);
assert(false, "setParameters was supposed to throw but didn't.");
} catch(error) {
helpers.assertVMException(error);
}
try {
await setup(accounts,helpers.NULL_ADDRESS,-50);
assert(false, "setParameters was supposed to throw but didn't.");
} catch(error) {
helpers.assertVMException(error);
}
});
it("Boosted period limit must be larger than or equal to quit ending period", async function() {
try {
await setup(accounts, helpers.NULL_ADDRESS, 50, 60, 60, 0, 2000, 61);
assert(false, "setParameters was supposed to throw but didn't.");
} catch(error) {
helpers.assertVMException(error);
}
});
it("Proposal voting shouldn't be able after proposal has been executed", async function () {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
// After this voting the proposal should be executed
await testSetup.genesisProtocol.vote(proposalId, 2,0,helpers.NULL_ADDRESS, {from: accounts[2]});
// Should not be able to vote because the proposal has been executed
try {
await testSetup.genesisProtocol.vote(proposalId, 1,0,helpers.NULL_ADDRESS, { from: accounts[1] });
assert(false, "vote was supposed to throw but didn't.");
} catch (error) {
helpers.assertVMException(error);
}
});
it("cannot vote without reputation", async function () {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
const organizationId = await web3.utils.soliditySha3(testSetup.genesisProtocolCallbacks.address,helpers.NULL_ADDRESS);
// no one has voted yet at this point
var submittedTime = (await web3.eth.getBlock("latest")).timestamp;
var state = 3;
var winningVote = 2;
var currentBoostedVotePeriodLimit = 60;
var daoBountyRemain = 15;
await checkProposalInfo(proposalId, [ organizationId,
testSetup.genesisProtocolCallbacks.address,
state,
winningVote,
helpers.NULL_ADDRESS,
currentBoostedVotePeriodLimit,
testSetup.genesisProtocolParams.paramsHash,
daoBountyRemain,
0, //daoBounty
0, //totalStake
0,
0
],
[submittedTime,0,0],
testSetup.genesisProtocol);
// lets try to vote by the owner on the behalf of non-existent voters(they do exist but they aren't registered to the reputation system).
try {
await testSetup.genesisProtocol.vote(proposalId, 1,0,helpers.NULL_ADDRESS ,{ from: accounts[3] });
assert(false, 'cannot vote without reputation');
} catch (ex) {
helpers.assertVMException(ex);
}
// everything should be 0
await checkVotesStatus(proposalId, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],testSetup.genesisProtocol);
});
it('Proposal with wrong num of options', async function () {
var testSetup = await setup(accounts);
// 3 options - max is 2 - exception should be raised
try {
await testSetup.genesisProtocolCallbacks.propose(3, helpers.NULL_HASH, testSetup.genesisProtocolCallbacks.address,accounts[0],helpers.NULL_ADDRESS);
assert(false, 'Tried to create a proposal with 3 options - max is 2');
} catch (ex) {
helpers.assertVMException(ex);
}
// -5 options - exception should be raised
try {
await testSetup.genesisProtocolCallbacks.propose(-5, helpers.NULL_HASH, testSetup.genesisProtocolCallbacks.address,accounts[0],helpers.NULL_ADDRESS);
assert(false, 'Tried to create an absolute vote with negative number of options');
} catch (ex) {
helpers.assertVMException(ex);
}
// 0 options - exception should be raised
try {
await testSetup.genesisProtocolCallbacks.propose(0, helpers.NULL_HASH ,testSetup.genesisProtocolCallbacks.address,accounts[0],helpers.NULL_ADDRESS);
assert(false, 'Tried to create an absolute vote with 0 number of options');
} catch (ex) {
helpers.assertVMException(ex);
}
});
it('Test voteWithSpecifiedAmounts - More reputation than I own, negative reputation, etc..', async function () {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
// Vote with the reputation the I own - should work
let tx = await testSetup.genesisProtocol.vote(proposalId, 1, testSetup.reputationArray[0] / 10, helpers.NULL_ADDRESS);
var repVoted = await helpers.getValueFromLogs(tx, "_reputation");
assert.equal(repVoted, testSetup.reputationArray[0] / 10, 'Should vote with specified amount');
// Vote with more reputation that i own - exception should be raised
try {
await testSetup.genesisProtocol.vote(proposalId, 1, (testSetup.reputationArray[1] + 1), helpers.NULL_ADDRESS,{from:accounts[1]});
assert(false, 'Not enough reputation - voting shouldn\'t work');
} catch (ex) {
helpers.assertVMException(ex);
}
// Vote with a very big number - exception should be raised
let bigNum = ((new BigNumber(2)).toPower(254)).toString(10);
try {
await testSetup.genesisProtocol.vote(proposalId, 1, bigNum, helpers.NULL_ADDRESS);
assert(false, 'Voting shouldn\'t work');
} catch (ex) {
helpers.assertVMException(ex);
}
});
it("Internal functions can not be called externally", async () => {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
// Lets try to call internalVote function
try {
await testSetup.genesisProtocol.internalVote(proposalId, accounts[0], 1, testSetup.reputationArray[0]);
assert(false, 'Can\'t call internalVote');
} catch (ex) {
helpers.assertInternalFunctionException(ex);
}
});
it("Try to send wrong proposal id to the voting/cancel functions", async () => {
var testSetup = await setup(accounts);
await propose(testSetup);
// Lets try to call vote with invalid proposal id
try {
await testSetup.genesisProtocol.vote(helpers.NULL_HASH, 1,0,helpers.NULL_ADDRESS, {from: accounts[0]});
assert(false, 'Invalid proposal ID has been delivered');
} catch (ex) {
helpers.assertVMException(ex);
}
// Lets try to call voteWithSpecifiedAmounts with invalid proposal id
try {
await testSetup.genesisProtocol.vote(helpers.NULL_HASH, 1, 1,helpers.NULL_ADDRESS);
assert(false, 'Invalid proposal ID has been delivered');
} catch (ex) {
helpers.assertVMException(ex);
}
// Lets try to call execute with invalid proposal id
try {
await testSetup.genesisProtocol.execute(helpers.NULL_HASH);
assert(false, 'Invalid proposal ID has been delivered');
} catch (ex) {
helpers.assertVMException(ex);
}
});
it("stake with approveAndCall log", async () => {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
var tx = await stake(testSetup,proposalId,1,10,accounts[0]);
assert.equal(tx.length, 1);
assert.equal(tx[0].event, "Stake");
assert.equal(tx[0].args._staker, accounts[0]);
assert.equal(tx[0].args._vote, 1);
assert.equal(tx[0].args._amount, 10);
});
it("stake more than allowed.", async () => {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
let maxTotalStakeAllowed = ((new BigNumber(2)).toPower(128)).sub(15);
assert.equal(await stake(testSetup,proposalId,1,maxTotalStakeAllowed.add(1).toString(10),accounts[0]),"revert");
var tx = await stake(testSetup,proposalId,1,maxTotalStakeAllowed.toString(10),accounts[0]);
assert.equal(tx.length, 1);
assert.equal(tx[0].event, "Stake");
assert.equal(tx[0].args._staker, accounts[0]);
assert.equal(tx[0].args._vote, 1);
assert.equal(tx[0].args._amount, maxTotalStakeAllowed.toString(10));
});
it("stake log", async () => {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
await testSetup.stakingToken.approve(testSetup.genesisProtocol.address,10);
var tx = await testSetup.genesisProtocol.stake(proposalId,1,10);
assert.equal(tx.logs.length, 1);
assert.equal(tx.logs[0].event, "Stake");
assert.equal(tx.logs[0].args._staker, accounts[0]);
assert.equal(tx.logs[0].args._vote, 1);
assert.equal(tx.logs[0].args._amount, 10);
});
it("check nonce ", async () => {
var testSetup = await setup(accounts,helpers.NULL_ADDRESS,50,60,60,0,2000);
var proposalId = await propose(testSetup);
let staker = await testSetup.genesisProtocol.getStaker(proposalId,accounts[0]);
assert.equal(staker[0],0);
assert.equal(staker[1],0);
var tx = await stake(testSetup,proposalId,1,10,accounts[0]);
assert.equal(tx.length, 1);
assert.equal(tx[0].event, "Stake");
assert.equal(tx[0].args._staker, accounts[0]);
assert.equal(tx[0].args._vote, 1);
assert.equal(tx[0].args._amount, 10);
staker = await testSetup.genesisProtocol.getStaker(proposalId,accounts[0]);
assert.equal(staker[0],1);
assert.equal(staker[1],10);
var nonce = await testSetup.genesisProtocol.stakesNonce(accounts[0]);
nonce--;
var textMsg = "0x"+ethereumjs.soliditySHA3(
["address","bytes32","uint", "uint","uint"],
[testSetup.genesisProtocol.address, proposalId,1,10, nonce.toString()]
).toString("hex");
const signature = await signMessage(accounts[0],textMsg);
const encodeABI = await new web3.eth.Contract(testSetup.genesisProtocol.abi).methods.stakeWithSignature(proposalId,1,10,nonce.toString(),signatureType,signature).encodeABI();
try {
await testSetup.stakingToken.approveAndCall(
testSetup.genesisProtocol.address, 10, encodeABI ,{from : accounts[0]}
);
assert(false, 'stake should fail with the same nonce');
} catch (ex) {
//helpers.assertVMException(ex);
}
});
it("check stake with wrong signature ", async () => {
var testSetup = await setup(accounts,helpers.NULL_ADDRESS,50,60,60,0,2000);
var proposalId = await propose(testSetup);
let staker = await testSetup.genesisProtocol.getStaker(proposalId,accounts[0]);
assert.equal(staker[0],0);
assert.equal(staker[1],0);
var nonce = (await testSetup.genesisProtocol.stakesNonce(accounts[0])).toString();
var textMsg = "0x"+ethereumjs.soliditySHA3(
["address","bytes32","uint", "uint","uint"],
[testSetup.genesisProtocol.address, proposalId,1,10, nonce]
).toString("hex");
const signature = await web3.eth.sign(textMsg , accounts[0]);
proposalId = "0x1234"; //change proposalId
const encodeABI = await new web3.eth.Contract(testSetup.genesisProtocol.abi).methods.stakeWithSignature(proposalId,1,10,nonce,signatureType,signature).encodeABI();
try {
await testSetup.stakingToken.approveAndCall(
testSetup.genesisProtocol.address, 10, encodeABI ,{from : accounts[0]}
);
assert(false, 'stake should fail due to wrong signature');
} catch (ex) {
//helpers.assertVMException(ex);
}
});
it("multiple stakes ", async () => {
var testSetup = await setup(accounts,helpers.NULL_ADDRESS,50,60,60,0,2000);
var proposalId = await propose(testSetup);
let staker = await testSetup.genesisProtocol.getStaker(proposalId,accounts[0]);
assert.equal(staker[0],0);
assert.equal(staker[1],0);
var tx = await stake(testSetup,proposalId,YES,10,accounts[0]);
assert.equal(tx[0].event, "Stake");
assert.equal(tx[0].args._staker, accounts[0]);
assert.equal(tx[0].args._vote, 1);
assert.equal(tx[0].args._amount, 10);
staker = await testSetup.genesisProtocol.getStaker(proposalId,accounts[0]);
assert.equal(staker[0],1);
assert.equal(staker[1],10);
//add more stake on the same vote
tx = await stake(testSetup,proposalId,YES,10,accounts[0]);
assert.equal(await testSetup.genesisProtocol.voteStake(proposalId,YES),20);
assert.equal(tx[0].event, "Stake");
assert.equal(tx[0].args._staker, accounts[0]);
assert.equal(tx[0].args._vote, 1);
assert.equal(tx[0].args._amount, 10);
staker = await testSetup.genesisProtocol.getStaker(proposalId,accounts[0]);
assert.equal(staker[0],1);
assert.equal(staker[1],20);
//try to stake with different vote as before
await stake(testSetup,proposalId,2,10,accounts[0]);
staker = await testSetup.genesisProtocol.getStaker(proposalId,accounts[0]);
assert.equal(staker[0],1);
assert.equal(staker[1],20);
let proposalInfo = await testSetup.genesisProtocol.proposals(proposalId);
assert.equal(proposalInfo[9],20); //totalStakes + dao downstake
});
it("stake without approval - fail", async () => {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
try {
await testSetup.genesisProtocol.stake(proposalId,2,10);
assert(false, 'stake without approval should revert');
} catch (ex) {
//helpers.assertVMException(ex);
}
});
it("stake with zero amount will fail", async () => {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
assert.equal(await stake(testSetup,proposalId,1,0,accounts[0]),"revert");
});
it("stake with invalid vote will fail", async () => {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
assert.equal(await stake(testSetup,proposalId,3,10,accounts[0]),"revert");
assert.equal(await stake(testSetup,proposalId,0,10,accounts[0]),"revert");
});
it("stake on boosted proposal is not allowed", async () => {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
//shift proposal to boosted phase
var proposalInfo = await testSetup.genesisProtocol.proposals(proposalId);
assert.equal(proposalInfo[proposalStateIndex],3);
await testSetup.genesisProtocol.vote(proposalId,YES,0,helpers.NULL_ADDRESS);
assert.equal(await testSetup.genesisProtocol.shouldBoost(proposalId),false);
await stake(testSetup,proposalId,YES,100,accounts[0]);
proposalInfo = await testSetup.genesisProtocol.proposals(proposalId);
assert.equal(proposalInfo[proposalTotalStakesIndex],100); //totalStakes
assert.equal(proposalInfo[proposalStateIndex],boostedState); //state boosted
//S = (S+) /(S-)
var proposalStatus = await testSetup.genesisProtocol.proposalStatus(proposalId);
var score = proposalStatus[2]/proposalStatus[3];
assert.equal(score,100/15);
//try to stake on boosted proposal should fail
var tx = await stake(testSetup,proposalId,YES,10,accounts[0]);
assert.equal(tx.length, 0);
});
it("boost proposal", async () => {
var thresholdConst = 1700; //1.7
var testSetup = await setup(accounts,helpers.NULL_ADDRESS,50,60,60,0,thresholdConst,0,60,10,2);
var proposalId = await propose(testSetup);
//shift proposal to boosted phase
var proposalInfo = await testSetup.genesisProtocol.proposals(proposalId);
assert.equal(proposalInfo[proposalStateIndex],3);
await testSetup.genesisProtocol.vote(proposalId,YES,0,helpers.NULL_ADDRESS);
assert.equal(await testSetup.genesisProtocol.shouldBoost(proposalId),false);
await stake(testSetup,proposalId,YES,30,accounts[0]);
proposalInfo = await testSetup.genesisProtocol.proposals(proposalId);
assert.equal(proposalInfo[proposalTotalStakesIndex],30); //totalStakes
assert.equal(proposalInfo[proposalStateIndex],boostedState); //state boosted
//S = (S+) /(S-)
var proposalStatus = await testSetup.genesisProtocol.proposalStatus(proposalId);
var score = proposalStatus[2]/proposalStatus[3];
assert.equal(score,30/2);
//try to boost another proposal - score(confidence) should be higher than 1.7
proposalId = await propose(testSetup);
proposalStatus = await testSetup.genesisProtocol.proposalStatus(proposalId);
assert.equal(proposalStatus[3].toNumber(),20);
await stake(testSetup,proposalId,YES,34,accounts[0]);
proposalInfo = await testSetup.genesisProtocol.proposals(proposalId);
//not boosted yet because 34/20 = 1.7
assert.equal(proposalInfo[proposalStateIndex],3);
// upstake with 1 should be enough to boost
await stake(testSetup,proposalId,YES,1,accounts[0]);
proposalInfo = await testSetup.genesisProtocol.proposals(proposalId);
proposalStatus = await testSetup.genesisProtocol.proposalStatus(proposalId);
assert.equal(proposalStatus[3].toNumber(),20);
assert.equal(proposalStatus[2].toNumber(),35);
assert.equal(proposalInfo[proposalStateIndex],boostedState);
});
it("stake on boosted dual proposal is not allowed", async () => {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
//shift proposal to boosted phase
var proposalInfo = await testSetup.genesisProtocol.proposals(proposalId);
assert.equal(proposalInfo[proposalStateIndex],3);
await testSetup.genesisProtocol.vote(proposalId,YES,0,helpers.NULL_ADDRESS);
assert.equal(await testSetup.genesisProtocol.shouldBoost(proposalId),false);
await stake(testSetup,proposalId,YES,100,accounts[0]);
proposalInfo = await testSetup.genesisProtocol.proposals(proposalId);
assert.equal(proposalInfo[proposalTotalStakesIndex],100); //totalStakes
assert.equal(proposalInfo[proposalStateIndex],boostedState); //state boosted
//S* POW(R/totalR)
var proposalStatus = await testSetup.genesisProtocol.proposalStatus(proposalId);
var score = proposalStatus[2]/proposalStatus[3];
assert.equal(score,100/15);
//try to stake on boosted proposal should fail
var tx = await stake(testSetup,proposalId,YES,10,accounts[0]);
assert.equal(tx.length, 0);
});
it("shouldBoost ", async () => {
var testSetup = await setup(accounts);
var proposalId = await propose(testSetup);
var proposalInfo = await testSetup.genesisProtocol.proposals(proposalId);
await testSetup.genesisProtocol.vote(proposalId,YES,0,helpers.NULL_ADDRESS);
assert.equal(await testSetup.genesisProtocol.shouldBoost(proposalId),false);
var proposalStatus = await testSetup.genesisProtocol.proposalStatus(proposalId);
var score = proposalStatus[2]/proposalStatus[3];
assert.equal(score,0);
await stake(testSetup,proposalId,YES,100,accounts[0]);
proposalInfo = await testSetup.genesisProtocol.proposals(proposalId);
assert.equal(proposalInfo[proposalTotalStakesIndex],100); //totalStakes
assert.equal(proposalInfo[proposalStateIndex],boostedState);