Skip to content

Commit

Permalink
test(provideStep): added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
fforbeck committed Mar 29, 2022
1 parent 055d8cf commit ec4ddba
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 27 deletions.
55 changes: 32 additions & 23 deletions contracts/adapters/voting/OffchainVoting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -391,8 +391,8 @@ contract OffchainVotingContract is
}

/**
* @notice Allows DAO members to reveal specific leaf nodes of the voting tree.
* @notice A step represents a vote of a DAO member, and the index indicates which index that member is stored in the DAO.
* @notice Allows DAO members to reveal specific leaf nodes of the voting tree (steps) if they are not found off-chain for any reason
* @notice A step represents a vote of a DAO member, and the index indicates which index that member is stored in the DAO
* @param dao The DAO address
* @param proposalId The proposalId associated with the step
* @param index The index of the step in the voting tree
Expand Down Expand Up @@ -426,8 +426,36 @@ contract OffchainVotingContract is
vote.gracePeriodStartingTime = uint64(block.timestamp);
}

/**
* @notice Submits an step that was requested or not.
* @notice The step is verified to make sure it is part of the voting tree.
* @notice Bad steps might be submitted, but they can be challenged as well.
* @notice Providing an step indicates that the step/vote is revealed, so it can be verified.
* @param dao The DAO Address
* @param adapterAddress The address of the adapter that was used as actionId to build the vote step proof.
* @param node The vote/node/step to be revealed.
*/
// slither-disable-next-line reentrancy-benign
function provideStep(
DaoRegistry dao,
address adapterAddress,
OffchainVotingHashContract.VoteResultNode memory node
) external reimbursable(dao) {
Voting storage vote = votes[address(dao)][node.proposalId];
// slither-disable-next-line timestamp
require(
node.index > 0 && vote.stepRequested == node.index,
"wrong step provided"
);

_verifyNode(dao, adapterAddress, node, vote.resultRoot);

vote.stepRequested = 0;
vote.gracePeriodStartingTime = uint64(block.timestamp);
}

/*
* @notice This function marks the proposal as challenged if a step requested by a member never came.
* @notice This function marks the proposal as challenged if a step requested by a member never came (was not provided/revealed).
* @notice The rule is, if a step has been requested and we are after the grace period, then challenge it
* @param dao The DAO address
* @param proposalId The proposal id associated with the missing step
Expand All @@ -450,25 +478,6 @@ contract OffchainVotingContract is
_challengeResult(dao, proposalId);
}

// slither-disable-next-line reentrancy-benign
function provideStep(
DaoRegistry dao,
address adapterAddress,
OffchainVotingHashContract.VoteResultNode memory node
) external reimbursable(dao) {
Voting storage vote = votes[address(dao)][node.proposalId];
// slither-disable-next-line timestamp
require(
node.index > 0 && vote.stepRequested == node.index,
"wrong step provided"
);

_verifyNode(dao, adapterAddress, node, vote.resultRoot);

vote.stepRequested = 0;
vote.gracePeriodStartingTime = uint64(block.timestamp);
}

// slither-disable-next-line reentrancy-benign
function startNewVotingForProposal(
DaoRegistry dao,
Expand Down Expand Up @@ -701,7 +710,7 @@ contract OffchainVotingContract is
root,
ovHash.nodeHash(dao, adapterAddress, node)
),
"invalid vote proof"
"invalid step proof"
);
}

Expand Down
132 changes: 132 additions & 0 deletions test/adapters/offchain.voting.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1976,6 +1976,138 @@ describe("Adapter - Offchain Voting", () => {
});
});

describe("Provide Vote Step", async () => {
it("should be possible to provide a valid vote step", async () => {
const dao = this.dao;
const bank = this.extensions.bankExt;
const voting = this.adapters.voting;
const configuration = this.adapters.configuration;
const submitter = members[0];

const result = await submitValidVoteResult(
dao,
bank,
configuration,
voting,
submitter,
false
);

// Request the first step (index 1)
await voting.requestStep(dao.address, result.proposalId, 1);
let storedVote = await voting.getVote(dao.address, result.proposalId);
expect(storedVote.stepRequested).to.be.equal("1");

const initialGracePeriod = parseInt(storedVote.gracePeriodStartingTime);

const voteResults = result.resultTree.votesResults;

const voteStep1 = voteResults[0];

// Provide/reveal the vote step one that was requested
await voting.provideStep(dao.address, configuration.address, voteStep1);

// After the valid vote step was provided, the grace period should be restarted
// and the stepRequested should be set to 0, because it was revealed
storedVote = await voting.getVote(dao.address, result.proposalId);
const newGracePeriod = parseInt(storedVote.gracePeriodStartingTime);
expect(newGracePeriod).to.be.greaterThan(initialGracePeriod);
expect(storedVote.stepRequested).to.be.equal("0");
expect(storedVote.isChallenged).to.be.false;
});

it("should not be possible to provide a vote step with index zero", async () => {
const dao = this.dao;
const bank = this.extensions.bankExt;
const voting = this.adapters.voting;
const configuration = this.adapters.configuration;
const submitter = members[0];

const result = await submitValidVoteResult(
dao,
bank,
configuration,
voting,
submitter,
false
);

// Request the first step (index 1)
await voting.requestStep(dao.address, result.proposalId, 1);
let storedVote = await voting.getVote(dao.address, result.proposalId);
expect(storedVote.stepRequested).to.be.equal("1");

const voteResults = result.resultTree.votesResults;
const voteStep1 = voteResults[0];

// Invalid index, because it must start from 1
voteStep1.index = 0;

await expect(
voting.provideStep(dao.address, configuration.address, voteStep1)
).to.be.revertedWith("wrong step provided");
});

it("should not be possible to provide a vote step if it was not requested", async () => {
const dao = this.dao;
const bank = this.extensions.bankExt;
const voting = this.adapters.voting;
const configuration = this.adapters.configuration;
const submitter = members[0];

const result = await submitValidVoteResult(
dao,
bank,
configuration,
voting,
submitter,
false
);

let storedVote = await voting.getVote(dao.address, result.proposalId);
// There is no step requested
expect(storedVote.stepRequested).to.be.equal("0");

const voteResults = result.resultTree.votesResults;
const voteStep1 = voteResults[0];

await expect(
voting.provideStep(dao.address, configuration.address, voteStep1)
).to.be.revertedWith("wrong step provided");
});

it("should not be possible to provide a vote step with an invalid proof", async () => {
const dao = this.dao;
const bank = this.extensions.bankExt;
const voting = this.adapters.voting;
const configuration = this.adapters.configuration;
const submitter = members[0];

const result = await submitValidVoteResult(
dao,
bank,
configuration,
voting,
submitter,
false
);

await voting.requestStep(dao.address, result.proposalId, 1);
let storedVote = await voting.getVote(dao.address, result.proposalId);
expect(storedVote.stepRequested).to.be.equal("1");

const voteResults = result.resultTree.votesResults;
const voteStep1 = voteResults[0];
// Set an invalid proof to mimic the behavior of a invalid step
voteStep1.proof[0] =
"0x1d2c3a91bdb8c7ccbd7cf5ea1df6c9408f9678deef9bfc27639e8ea9429a3572";

await expect(
voting.provideStep(dao.address, configuration.address, voteStep1)
).to.be.revertedWith("invalid step proof");
});
});

describe("Challenge Missing Vote Step", async () => {
it("should be possible to challenge a missing step", async () => {
const dao = this.dao;
Expand Down
12 changes: 8 additions & 4 deletions utils/offchain-voting-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -456,13 +456,17 @@ async function prepareVoteResult(votes, dao, actionId, chainId) {
votes.forEach((vote, idx) => {
const stepIndex = idx + 1;
vote.choice = vote.choice || vote.payload.choice;
vote.nbYes = vote.choice === 1 ? vote.payload.weight : toBNWeb3(0);
vote.nbNo = vote.choice !== 1 ? vote.payload.weight : toBNWeb3(0);
vote.nbYes = vote.choice === 1 ? vote.payload.weight.toString() : "0";
vote.nbNo = vote.choice !== 1 ? vote.payload.weight.toString() : "0";
vote.proposalId = vote.payload.proposalId;
if (stepIndex > 1) {
const previousVote = votes[stepIndex - 1];
vote.nbYes = vote.nbYes.add(toBNWeb3(previousVote.nbYes)).toString();
vote.nbNo = vote.nbNo.add(toBNWeb3(previousVote.nbNo)).toString();
vote.nbYes = toBNWeb3(vote.nbYes)
.add(toBNWeb3(previousVote.nbYes))
.toString();
vote.nbNo = toBNWeb3(vote.nbNo)
.add(toBNWeb3(previousVote.nbNo))
.toString();
}

vote.index = stepIndex;
Expand Down

0 comments on commit ec4ddba

Please sign in to comment.