Skip to content

Commit

Permalink
Update Governor.sol with new refresh proposal and fix tests (#341)
Browse files Browse the repository at this point in the history
  • Loading branch information
drewstone authored Jul 6, 2023
1 parent a248224 commit c3c08fa
Show file tree
Hide file tree
Showing 5 changed files with 307 additions and 268 deletions.
2 changes: 1 addition & 1 deletion packages/contracts/contracts/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@webb-tools/protocol-solidity",
"description": "Solidity contracts for protocol-solidity",
"version": "1.0.1",
"version": "1.1.0",
"files": [
"**/*.sol"
],
Expand Down
165 changes: 102 additions & 63 deletions packages/contracts/contracts/test/Governable.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { StdCheats } from "forge-std/StdCheats.sol";
import { Governable, Vote } from "../utils/Governable.sol";
import { MerkleTreeMock as MerkleTree } from "../mocks/MerkleTreeMock.sol";
import { KeccakHasher } from "../hashers/KeccakHasher.sol";
import { ProposalHelpers } from "./ProposalHelpers.t.sol";
import { ProposalHelpers, RefreshProposal } from "./ProposalHelpers.t.sol";

contract GovernableTest is PRBTest, StdCheats, ProposalHelpers {
Governable governableContract;
Expand Down Expand Up @@ -60,65 +60,97 @@ contract GovernableTest is PRBTest, StdCheats, ProposalHelpers {

function test_transferOwnershipWithSignature() public {
string
memory pubkeyString = "989f3c75d99033df6074a25c7255629da79c57fd5379bcb6e2438d2bf339e1511a71de858155b44efcbe1f7621693fae07e57a531799a38c4d00e2904389e938";
bytes memory pubkey = fromHex(pubkeyString);
memory pubKeyString = "989f3c75d99033df6074a25c7255629da79c57fd5379bcb6e2438d2bf339e1511a71de858155b44efcbe1f7621693fae07e57a531799a38c4d00e2904389e938";
bytes memory pubKey = fromHex(pubKeyString);
address newGovernor = 0xe725B59239D1b324CF0e0a93473009A155167733;
bytes memory sig = signWithGoverner(abi.encodePacked(refreshNonce + 1, pubkey));
(RefreshProposal memory proposal, bytes memory encodedProposal) = buildRefreshProposal(
bytes32(0),
1000 * 3600,
1,
refreshNonce + 1,
pubKey
);
bytes memory sig = signWithGoverner(encodedProposal);
vm.prank(governor);
governableContract.transferOwnershipWithSignaturePubKey(pubkey, refreshNonce + 1, sig);
governableContract.transferOwnershipWithSignature(
proposal.voterMerkleRoot,
proposal.averageSessionLengthInMillisecs,
proposal.voterCount,
proposal.nonce,
proposal.publicKey,
sig
);
assertEq(governableContract.governor(), newGovernor);
assertEq(governableContract.refreshNonce(), refreshNonce + 1);
}

function test_transferOwnershipWithSignatureShouldFailWithInvalidNonce() public {
string
memory pubkeyString = "989f3c75d99033df6074a25c7255629da79c57fd5379bcb6e2438d2bf339e1511a71de858155b44efcbe1f7621693fae07e57a531799a38c4d00e2904389e938";
bytes memory pubkey = fromHex(pubkeyString);
memory pubKeyString = "989f3c75d99033df6074a25c7255629da79c57fd5379bcb6e2438d2bf339e1511a71de858155b44efcbe1f7621693fae07e57a531799a38c4d00e2904389e938";
bytes memory pubKey = fromHex(pubKeyString);
address newGovernor = 0xe725B59239D1b324CF0e0a93473009A155167733;
bytes memory sig = signWithGoverner(abi.encodePacked(newGovernor, refreshNonce + 2));
(RefreshProposal memory proposal, bytes memory encodedProposal) = buildRefreshProposal(
bytes32(0),
1000 * 3600,
1,
refreshNonce + 2,
pubKey
);
bytes memory sig = signWithGoverner(encodedProposal);
vm.prank(governor);
vm.expectRevert(bytes("Governable: Nonce must increment by 1"));
governableContract.transferOwnershipWithSignaturePubKey(pubkey, refreshNonce + 2, sig);
governableContract.transferOwnershipWithSignature(
proposal.voterMerkleRoot,
proposal.averageSessionLengthInMillisecs,
proposal.voterCount,
proposal.nonce,
proposal.publicKey,
sig
);
}

function test_proposerSetUpdate(address[4] memory proposers) public {
function test_voterMerkleRootUpdate(address[4] memory proposers) public {
for (uint i = 0; i < proposers.length; i++) {
tree.insert(uint256(keccak256(abi.encodePacked(proposers[i]))));
}

bytes32 merkleRoot = bytes32(tree.getLastRoot());
bytes32 voterMerkleRoot = bytes32(tree.getLastRoot());
uint64 averageSessionLengthInMillisecs = 50000;
uint32 numOfProposers = 4;
uint32 proposerSetUpdateNonce = 1;
bytes memory proposerSetUpdate = buildProposerSetUpdateProposal(
merkleRoot,
uint32 voterCount = 4;

string
memory pubKeyString = "989f3c75d99033df6074a25c7255629da79c57fd5379bcb6e2438d2bf339e1511a71de858155b44efcbe1f7621693fae07e57a531799a38c4d00e2904389e938";
bytes memory pubKey = fromHex(pubKeyString);
address newGovernor = 0xe725B59239D1b324CF0e0a93473009A155167733;
(RefreshProposal memory proposal, bytes memory encodedProposal) = buildRefreshProposal(
voterMerkleRoot,
averageSessionLengthInMillisecs,
numOfProposers,
proposerSetUpdateNonce
voterCount,
refreshNonce + 1,
pubKey
);

bytes memory sig = signWithGoverner(proposerSetUpdate);
bytes memory sig = signWithGoverner(encodedProposal);

vm.prank(governor);
governableContract.updateProposerSetData(
merkleRoot,
averageSessionLengthInMillisecs,
numOfProposers,
proposerSetUpdateNonce,
governableContract.transferOwnershipWithSignature(
proposal.voterMerkleRoot,
proposal.averageSessionLengthInMillisecs,
proposal.voterCount,
proposal.nonce,
proposal.publicKey,
sig
);

assertEq(governableContract.proposerSetUpdateNonce(), proposerSetUpdateNonce);
assertEq(governableContract.proposerSetRoot(), merkleRoot);
assertEq(governableContract.voterMerkleRoot(), voterMerkleRoot);
assertEq(
governableContract.averageSessionLengthInMillisecs(),
averageSessionLengthInMillisecs
);
assertEq(governableContract.numOfProposers(), numOfProposers);
assertEq(governableContract.voterCount(), voterCount);
assertEq(governableContract.currentVotingPeriod(), 1);
}

function test_proposerSetUpdateForceChangeGovernor() public {
function test_forceChangeGovernorWithVotes() public {
address[] memory proposers = new address[](4);

for (uint256 i = 0; i < 4; i++) {
Expand All @@ -131,25 +163,29 @@ contract GovernableTest is PRBTest, StdCheats, ProposalHelpers {
tree.insert(uint256(keccak256(abi.encodePacked(proposers[i]))));
}

bytes32 merkleRoot = bytes32(tree.getLastRoot());
bytes32 voterMerkleRoot = bytes32(tree.getLastRoot());
uint64 averageSessionLengthInMillisecs = 50000;
uint32 numOfProposers = 4;
uint32 proposerSetUpdateNonce = 1;
bytes memory proposerSetUpdate = buildProposerSetUpdateProposal(
merkleRoot,
uint32 voterCount = 4;
string
memory pubKeyString = "989f3c75d99033df6074a25c7255629da79c57fd5379bcb6e2438d2bf339e1511a71de858155b44efcbe1f7621693fae07e57a531799a38c4d00e2904389e938";
bytes memory pubKey = fromHex(pubKeyString);
address newGovernor = 0xe725B59239D1b324CF0e0a93473009A155167733;
(RefreshProposal memory proposal, bytes memory encodedProposal) = buildRefreshProposal(
voterMerkleRoot,
averageSessionLengthInMillisecs,
numOfProposers,
proposerSetUpdateNonce
voterCount,
refreshNonce + 1,
pubKey
);

bytes memory sig = signWithGoverner(proposerSetUpdate);
bytes memory sig = signWithGoverner(encodedProposal);

vm.prank(governor);
governableContract.updateProposerSetData(
merkleRoot,
averageSessionLengthInMillisecs,
numOfProposers,
proposerSetUpdateNonce,
governableContract.transferOwnershipWithSignature(
proposal.voterMerkleRoot,
proposal.averageSessionLengthInMillisecs,
proposal.voterCount,
proposal.nonce,
proposal.publicKey,
sig
);

Expand All @@ -159,10 +195,10 @@ contract GovernableTest is PRBTest, StdCheats, ProposalHelpers {
averageSessionLengthInMillisecs *
governableContract.sessionLengthMultiplier()
);
address newGovernor = vm.addr(2);
newGovernor = vm.addr(2);
// We only need a majority of votes so we can skip the last proposer
for (uint i = 0; i < proposers.length - 1; i++) {
bytes32[] memory path = getPathForLeaf(proposers, i, merkleRoot);
bytes32[] memory path = getPathForLeaf(proposers, i, voterMerkleRoot);
Vote memory vote = governableContract.createVote(uint32(i), newGovernor, path);
vm.prank(proposers[i]);
governableContract.voteInFavorForceSetGovernor(vote);
Expand All @@ -172,7 +208,7 @@ contract GovernableTest is PRBTest, StdCheats, ProposalHelpers {
assertEq(governableContract.currentVotingPeriod(), 2);
}

function test_proposerSetUpdateForceChangeGovernorWithSig() public {
function test_forceChangeGovernorWithVoteSigs() public {
address[] memory proposers = new address[](4);

for (uint256 i = 0; i < 4; i++) {
Expand All @@ -185,25 +221,29 @@ contract GovernableTest is PRBTest, StdCheats, ProposalHelpers {
tree.insert(uint256(keccak256(abi.encodePacked(proposers[i]))));
}

bytes32 merkleRoot = bytes32(tree.getLastRoot());
bytes32 voterMerkleRoot = bytes32(tree.getLastRoot());
uint64 averageSessionLengthInMillisecs = 50000;
uint32 numOfProposers = 4;
uint32 proposerSetUpdateNonce = 1;
bytes memory proposerSetUpdate = buildProposerSetUpdateProposal(
merkleRoot,
uint32 voterCount = 4;
string
memory pubKeyString = "989f3c75d99033df6074a25c7255629da79c57fd5379bcb6e2438d2bf339e1511a71de858155b44efcbe1f7621693fae07e57a531799a38c4d00e2904389e938";
bytes memory pubKey = fromHex(pubKeyString);
address newGovernor = 0xe725B59239D1b324CF0e0a93473009A155167733;
(RefreshProposal memory proposal, bytes memory encodedProposal) = buildRefreshProposal(
voterMerkleRoot,
averageSessionLengthInMillisecs,
numOfProposers,
proposerSetUpdateNonce
voterCount,
refreshNonce + 1,
pubKey
);

bytes memory sig = signWithGoverner(proposerSetUpdate);
bytes memory sig = signWithGoverner(encodedProposal);

vm.prank(governor);
governableContract.updateProposerSetData(
merkleRoot,
averageSessionLengthInMillisecs,
numOfProposers,
proposerSetUpdateNonce,
governableContract.transferOwnershipWithSignature(
proposal.voterMerkleRoot,
proposal.averageSessionLengthInMillisecs,
proposal.voterCount,
proposal.nonce,
proposal.publicKey,
sig
);

Expand All @@ -213,21 +253,20 @@ contract GovernableTest is PRBTest, StdCheats, ProposalHelpers {
averageSessionLengthInMillisecs *
governableContract.sessionLengthMultiplier()
);
address newGovernor = vm.addr(2);
newGovernor = vm.addr(2);
// We only need a majority of votes so we can skip the last proposer
Vote[] memory votes = new Vote[](proposers.length - 1);
bytes[] memory sigs = new bytes[](proposers.length - 1);
for (uint i = 0; i < proposers.length - 1; i++) {
bytes32[] memory path = getPathForLeaf(proposers, i, merkleRoot);
bytes32[] memory path = getPathForLeaf(proposers, i, voterMerkleRoot);
Vote memory vote = governableContract.createVote(uint32(i), newGovernor, path);
// Sign the vote with the proposer's key
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
uint256(keccak256(abi.encodePacked(i))),
keccak256(abi.encode(vote))
);
bytes memory sig = abi.encodePacked(r, s, v);
votes[i] = vote;
sigs[i] = sig;
sigs[i] = abi.encodePacked(r, s, v);
}
// Submit the votes and signatures
governableContract.voteInFavorForceSetGovernorWithSig(votes, sigs);
Expand Down
45 changes: 34 additions & 11 deletions packages/contracts/contracts/test/ProposalHelpers.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ pragma solidity >=0.8.19 <0.9.0;

import "../utils/ChainIdWithType.sol";

/// @param voterMerkleRoot the Merkle root of the voter set Merkle tree
/// @param averageSessionLengthInMillisecs the average session length in milliseconds
/// @param voterCount the number of voters
/// @param nonce the nonce of the proposal
/// @param publicKey the public key of the new governor
struct RefreshProposal {
bytes32 voterMerkleRoot;
uint64 averageSessionLengthInMillisecs;
uint32 voterCount;
uint32 nonce;
bytes publicKey;
}

contract ProposalHelpers is ChainIdWithType {
function buildTypedChainId(uint16 chainType, uint32 chainId) public pure returns (bytes6) {
// Return a 6 bytes value of the chainType and chainId concatenated
Expand Down Expand Up @@ -90,18 +103,28 @@ contract ProposalHelpers is ChainIdWithType {
return buildProposal(proposalHeader, proposalData);
}

function buildProposerSetUpdateProposal(
bytes32 _proposerSetRoot,
function buildRefreshProposal(
bytes32 _voterMerkleRoot,
uint64 _averageSessionLengthInMillisecs,
uint32 _numOfProposers,
uint32 _proposerSetUpdateNonce
) public pure returns (bytes memory) {
return
uint32 _voterCount,
uint32 _refreshNonce,
bytes memory _publicKey
) public pure returns (RefreshProposal memory, bytes memory) {
return (
RefreshProposal(
_voterMerkleRoot,
_averageSessionLengthInMillisecs,
_voterCount,
_refreshNonce,
_publicKey
),
abi.encodePacked(
_proposerSetRoot,
bytes8(_averageSessionLengthInMillisecs),
bytes4(_numOfProposers),
bytes4(_proposerSetUpdateNonce)
);
_voterMerkleRoot,
_averageSessionLengthInMillisecs,
_voterCount,
_refreshNonce,
_publicKey
)
);
}
}
Loading

0 comments on commit c3c08fa

Please sign in to comment.