UNPKG

@daostack/infra-experimental

Version:

Base layer DAO's components

1,149 lines (962 loc) 89.3 kB
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);