From 1f08d07a3a261a5d7c6fb348d3c67dd88d767ff5 Mon Sep 17 00:00:00 2001 From: Felipe Forbeck Date: Fri, 25 Mar 2022 16:03:46 -0300 Subject: [PATCH] test bad vote signatures --- test/adapters/offchain.voting.test.js | 278 ++++++++++++++++++++++++++ utils/offchain-voting-util.js | 50 +++-- 2 files changed, 308 insertions(+), 20 deletions(-) diff --git a/test/adapters/offchain.voting.test.js b/test/adapters/offchain.voting.test.js index d265b8841..c12341ef9 100644 --- a/test/adapters/offchain.voting.test.js +++ b/test/adapters/offchain.voting.test.js @@ -1180,6 +1180,284 @@ describe("Adapter - Offchain Voting", () => { expect(BadNodeError[parseInt(errorCode)]).equal("INDEX_OUT_OF_BOUND"); }); + it("should not be possible to submit a vote result with an invalid vote choice", async () => { + const daoOwnerSubmitter = members[0]; + const dao = this.dao; + const bank = this.extensions.bankExt; + const configuration = this.adapters.configuration; + const proposalId = getProposalCounter(); + const actionId = configuration.address; + const voting = this.adapters.voting; + + const blockNumber = await getCurrentBlockNumber(); + const { proposalData } = await buildProposal( + dao, + actionId, + daoOwnerSubmitter, + blockNumber, + chainId + ); + + const configs = [ + { + key: sha3("config1"), + numericValue: "10", + addressValue: ZERO_ADDRESS, + configType: 0, + }, + ]; + const data = prepareVoteProposalData(proposalData, web3); + await configuration.submitProposal( + dao.address, + proposalId, + configs, + data, + { + from: daoOwner, + gasPrice: toBN("0"), + } + ); + + // Only the submitter votes Yes, and attempts to submit the vote result + const votes = await buildVotes( + dao, + bank, + proposalId, + daoOwnerSubmitter, + blockNumber, + chainId, + actionId, + VotingStrategies.AllVotesYes, + null, + 100 //invalid vote choice + ); + + const { lastVoteResult, rootSig, resultHash, membersCount } = + await buildVoteTree( + dao, + bank, + proposalId, + daoOwnerSubmitter, + blockNumber, + chainId, + actionId, + VotingStrategies.AllVotesYes, + votes + ); + + await expect( + voting.submitVoteResult( + dao.address, + proposalId, + resultHash, + daoOwnerSubmitter.address, + lastVoteResult, + rootSig + ) + ).to.be.revertedWith("bad node"); + + const getBadNodeErrorResponse = await voting.getBadNodeError( + dao.address, + proposalId, + // `bool submitNewVote` + true, + resultHash, + blockNumber, + // `gracePeriodStartingTime` should be `0` as `submitNewVote` is `true` + 0, + membersCount, + lastVoteResult + ); + + const errorCode = getBadNodeErrorResponse.toString(); + expect(BadNodeError[parseInt(errorCode)]).equal("INVALID_CHOICE"); + }); + + it("should not be possible to submit a vote result with the wrong proposal id", async () => { + const daoOwnerSubmitter = members[0]; + const dao = this.dao; + const bank = this.extensions.bankExt; + const configuration = this.adapters.configuration; + const proposalId = getProposalCounter(); + const actionId = configuration.address; + const voting = this.adapters.voting; + + const blockNumber = await getCurrentBlockNumber(); + const { proposalData } = await buildProposal( + dao, + actionId, + daoOwnerSubmitter, + blockNumber, + chainId + ); + + const configs = [ + { + key: sha3("config1"), + numericValue: "10", + addressValue: ZERO_ADDRESS, + configType: 0, + }, + ]; + const data = prepareVoteProposalData(proposalData, web3); + await configuration.submitProposal( + dao.address, + proposalId, + configs, + data, + { + from: daoOwner, + gasPrice: toBN("0"), + } + ); + + const wrongProposalId = await getProposalCounter(); + const votes = await buildVotes( + dao, + bank, + wrongProposalId, + daoOwnerSubmitter, + blockNumber, + chainId, + actionId, + VotingStrategies.AllVotesYes + ); + + const { lastVoteResult, rootSig, resultHash, membersCount } = + await buildVoteTree( + dao, + bank, + wrongProposalId, + daoOwnerSubmitter, + blockNumber, + chainId, + actionId, + VotingStrategies.AllVotesYes, + votes + ); + + await expect( + voting.submitVoteResult( + dao.address, + proposalId, // submits the correct one, but the vote uses the wrong one + resultHash, + daoOwnerSubmitter.address, + lastVoteResult, + rootSig + ) + ).to.be.revertedWith("bad node"); + + const getBadNodeErrorResponse = await voting.getBadNodeError( + dao.address, + proposalId, + // `bool submitNewVote` + true, + resultHash, + blockNumber, + // `gracePeriodStartingTime` should be `0` as `submitNewVote` is `true` + 0, + membersCount, + lastVoteResult + ); + + const errorCode = getBadNodeErrorResponse.toString(); + expect(BadNodeError[parseInt(errorCode)]).equal("WRONG_PROPOSAL_ID"); + }); + + it("should not be possible to submit a vote result with a bad signature", async () => { + const daoOwnerSubmitter = members[0]; + const dao = this.dao; + const bank = this.extensions.bankExt; + const configuration = this.adapters.configuration; + const proposalId = getProposalCounter(); + const actionId = configuration.address; + const voting = this.adapters.voting; + + const blockNumber = await getCurrentBlockNumber(); + const { proposalData } = await buildProposal( + dao, + actionId, + daoOwnerSubmitter, + blockNumber, + chainId + ); + + const configs = [ + { + key: sha3("config1"), + numericValue: "10", + addressValue: ZERO_ADDRESS, + configType: 0, + }, + ]; + const data = prepareVoteProposalData(proposalData, web3); + await configuration.submitProposal( + dao.address, + proposalId, + configs, + data, + { + from: daoOwner, + gasPrice: toBN("0"), + } + ); + + const votes = await buildVotes( + dao, + bank, + proposalId, + daoOwnerSubmitter, + blockNumber, + chainId, + actionId, + VotingStrategies.AllVotesYes, + null, // votingWeight + null, // voteChoice + // invalid signature + "0xbe90f2a9a51a554ef37a3bc3f47a1fc8dc29279ff320fa45754f6df165cc692f7cf7ed059edded2c87e95320229420c8e359a498fd89e55a1086a4adf03f73901c" + ); + + const { lastVoteResult, rootSig, resultHash, membersCount } = + await buildVoteTree( + dao, + bank, + proposalId, + daoOwnerSubmitter, + blockNumber, + chainId, + actionId, + VotingStrategies.AllVotesYes, + votes + ); + + await expect( + voting.submitVoteResult( + dao.address, + proposalId, // submits the correct one, but the vote uses the wrong one + resultHash, + daoOwnerSubmitter.address, + lastVoteResult, + rootSig + ) + ).to.be.revertedWith("bad node"); + + const getBadNodeErrorResponse = await voting.getBadNodeError( + dao.address, + proposalId, + // `bool submitNewVote` + true, + resultHash, + blockNumber, + // `gracePeriodStartingTime` should be `0` as `submitNewVote` is `true` + 0, + membersCount, + lastVoteResult + ); + + const errorCode = getBadNodeErrorResponse.toString(); + expect(BadNodeError[parseInt(errorCode)]).equal("BAD_SIGNATURE"); + }); + it("should not be possible to submit a vote result with an invalid result signature", async () => { const daoOwnerSubmitter = members[0]; const dao = this.dao; diff --git a/utils/offchain-voting-util.js b/utils/offchain-voting-util.js index 73a27d263..fa85b44dd 100644 --- a/utils/offchain-voting-util.js +++ b/utils/offchain-voting-util.js @@ -113,7 +113,8 @@ const vote = async ( choice, actionId, chainId, - votingWeight = undefined + votingWeight = undefined, + signature = undefined ) => { const voteSigner = SigUtilSigner(member.privateKey); const weight = await bank.balanceOf(member.address, UNITS); @@ -122,13 +123,15 @@ const vote = async ( votingWeight ? toBN(votingWeight) : weight, choice ); - voteEntry.sig = voteSigner(voteEntry, dao.address, actionId, chainId); + voteEntry.sig = signature + ? signature + : voteSigner(voteEntry, dao.address, actionId, chainId); return voteEntry; }; -const emptyVote = async (proposalId) => { - const voteEntry = await createVote(proposalId, toBN("0"), false); - voteEntry.sig = "0x"; +const emptyVote = async (proposalId, choice = 2 /*no*/, signature = "0x") => { + const voteEntry = await createVote(proposalId, toBN("0"), choice); + voteEntry.sig = signature; return voteEntry; }; @@ -141,7 +144,9 @@ const buildVotes = async ( chainId, actionId, voteStrategy, - votingWeight = undefined + votingWeight = undefined, + voteChoice = undefined, + signature = undefined ) => { const membersCount = await bank.getPriorAmount( TOTAL, @@ -156,7 +161,7 @@ const buildVotes = async ( let voteEntry; if (!member) { - voteEntry = await emptyVote(proposalId); + voteEntry = await emptyVote(proposalId, 2, signature); voteEntries.push(voteEntry); continue; } @@ -169,10 +174,11 @@ const buildVotes = async ( bank, proposalId, member, - true, + voteChoice ? voteChoice : 1, actionId, chainId, - votingWeight + votingWeight, + signature ); break; case VotingStrategies.SingleNoVote && memberAddress === submitter.address: @@ -181,10 +187,11 @@ const buildVotes = async ( bank, proposalId, member, - false, + voteChoice ? voteChoice : 2, actionId, chainId, - votingWeight + votingWeight, + signature ); break; case VotingStrategies.AllVotesYes: @@ -193,10 +200,11 @@ const buildVotes = async ( bank, proposalId, member, - true, + voteChoice ? voteChoice : 1, actionId, chainId, - votingWeight + votingWeight, + signature ); break; case VotingStrategies.AllVotesNo: @@ -205,10 +213,11 @@ const buildVotes = async ( bank, proposalId, member, - false, + voteChoice ? voteChoice : 2, actionId, chainId, - votingWeight + votingWeight, + signature ); break; case VotingStrategies.TieVote: @@ -217,15 +226,16 @@ const buildVotes = async ( bank, proposalId, member, - i < parseInt(totalVotes / 2), + i < parseInt(totalVotes / 2) ? 1 : 2, actionId, chainId, - votingWeight + votingWeight, + signature ); break; case VotingStrategies.NoBodyVotes: default: - voteEntry = await emptyVote(proposalId); + voteEntry = await emptyVote(proposalId, 2, signature); break; } voteEntries.push(voteEntry); @@ -660,9 +670,9 @@ function toStepNode(step, verifyingContract, actionId, chainId, merkleTree) { }; } -async function createVote(proposalId, weight, voteYes) { +async function createVote(proposalId, weight, choice) { const payload = { - choice: voteYes ? 1 : 2, + choice, proposalId, weight: toBNWeb3(weight), };