Skip to content

Commit

Permalink
feat(contracts): implement swap vote on Consenus.sol (#761)
Browse files Browse the repository at this point in the history
* Add swap vote

* Check emits

* Unvote requires vote

* Update abi

* Update crypto

* Fix e2e tests
  • Loading branch information
sebastijankuzner authored Nov 12, 2024
1 parent fab9a1c commit c4f07e4
Show file tree
Hide file tree
Showing 15 changed files with 754 additions and 665 deletions.
19 changes: 15 additions & 4 deletions contracts/src/consensus/Consensus.sol
Original file line number Diff line number Diff line change
Expand Up @@ -353,11 +353,17 @@ contract Consensus {

function vote(address addr) external preventOwner {
require(isValidatorRegistered(addr), "Must vote for validator");
require(_voters[msg.sender].validator == address(0), "Already voted");

ValidatorData storage validatorData = _registeredValidatorData[addr];
require(!validatorData.isResigned, "Must vote for unresigned validator");

Vote storage voter = _voters[msg.sender];
require(voter.validator != addr, "Already voted for this validator");

if (voter.validator != address(0)) {
_unvote();
}

_voters[msg.sender] = Vote({validator: addr, balance: msg.sender.balance, prev: address(0), next: address(0)});

if (_votersHead == address(0)) {
Expand All @@ -370,16 +376,19 @@ contract Consensus {
}
_votersCount++;

// TODO: safe math
validatorData.voteBalance += msg.sender.balance;
validatorData.votersCount += 1;

emit Voted(msg.sender, addr);
}

function unvote() external {
emit Unvoted(msg.sender, _unvote());
}

function _unvote() internal returns (address) {
Vote storage voter = _voters[msg.sender];
require(voter.validator != address(0), "TODO: not voted");
require(voter.validator != address(0), "Must vote for validator before unvote");

if (_votersHead == _votersTail) {
_votersHead = address(0);
Expand All @@ -395,7 +404,7 @@ contract Consensus {
_voters[voter.next].prev = voter.prev;
}

emit Unvoted(msg.sender, voter.validator);
address validatorAddr = voter.validator;

ValidatorData storage validatorData = _registeredValidatorData[voter.validator];

Expand All @@ -405,6 +414,8 @@ contract Consensus {
delete _voters[msg.sender];

_votersCount--;

return validatorAddr;
}

function updateVoters(address[] calldata voters) external onlyOwner {
Expand Down
82 changes: 80 additions & 2 deletions contracts/test/consensus/Consensus-Vote.sol
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ contract ConsensusTest is Test {
consensus.vote(address(1));
}

function test_unvote_revert_if_did_not_vote() public {
vm.expectRevert("Must vote for validator before unvote");
consensus.unvote();
}

function test_get_voters_revert_if_caller_is_not_owner() public {
vm.startPrank(address(1));

Expand Down Expand Up @@ -162,7 +167,7 @@ contract ConsensusTest is Test {
assertEq(allVoters[0].validator, voterAddr);
}

function test_vote_prevent_double_vote() public {
function test_vote_prevent_double_vote_same_voter() public {
// Register validator
address addr = address(1);
registerValidator(addr);
Expand All @@ -175,7 +180,7 @@ contract ConsensusTest is Test {
emit Voted(voterAddr, addr);
consensus.vote(addr);

vm.expectRevert("Already voted");
vm.expectRevert("Already voted for this validator");
consensus.vote(addr);
}

Expand All @@ -200,6 +205,79 @@ contract ConsensusTest is Test {
consensus.vote(addr);
}

function test_swap_vote() public {
// Assert voters
assertEq(consensus.getVotesCount(), 0);
VoteResult[] memory allVoters = consensus.getVotes(address(0), 10);
assertEq(allVoters.length, 0);

// Register validator
address validatorAddr1 = address(1);
registerValidator(validatorAddr1);

address validatorAddr2 = address(2);
registerValidator(validatorAddr2);

// Vote
address voterAddr = address(3);
vm.deal(voterAddr, 100 ether);
vm.startPrank(voterAddr);
vm.expectEmit(address(consensus));
emit Voted(voterAddr, validatorAddr1);
consensus.vote(validatorAddr1);
vm.stopPrank();

// Assert validator 1
Validator memory validator1 = consensus.getValidator(validatorAddr1);
assertEq(validator1.addr, validatorAddr1);
assertEq(validator1.data.voteBalance, 100 ether);
assertEq(validator1.data.votersCount, 1);
// Assert validator 2
Validator memory validator2 = consensus.getValidator(validatorAddr2);
assertEq(validator2.addr, validatorAddr2);
assertEq(validator2.data.voteBalance, 0 ether);
assertEq(validator2.data.votersCount, 0);

// Assert voter balance
assertEq(voterAddr.balance, 100 ether);
// Assert voters
assertEq(consensus.getVotesCount(), 1);
allVoters = consensus.getVotes(address(0), 10);
assertEq(allVoters.length, 1);
assertEq(allVoters[0].voter, voterAddr);
assertEq(allVoters[0].validator, validatorAddr1);

// Let say voter has 90 eth after some tx
vm.deal(voterAddr, 90 ether);

// Swap Vote
vm.startPrank(voterAddr);
vm.expectEmit(address(consensus));
emit Voted(voterAddr, validatorAddr2);
consensus.vote(validatorAddr2);
vm.stopPrank();

// Assert validator 1
validator1 = consensus.getValidator(validatorAddr1);
assertEq(validator1.addr, validatorAddr1);
assertEq(validator1.data.voteBalance, 0 ether);
assertEq(validator1.data.votersCount, 0);
// Assert validator 2
validator2 = consensus.getValidator(validatorAddr2);
assertEq(validator2.addr, validatorAddr2);
assertEq(validator2.data.voteBalance, 90 ether);
assertEq(validator2.data.votersCount, 1);

// Assert voter balance
assertEq(voterAddr.balance, 90 ether);
// Assert voters
assertEq(consensus.getVotesCount(), 1);
allVoters = consensus.getVotes(address(0), 10);
assertEq(allVoters.length, 1);
assertEq(allVoters[0].voter, voterAddr);
assertEq(allVoters[0].validator, validatorAddr2);
}

function test_unvote_and_vote_in_same_block() public {
// Assert voters
assertEq(consensus.getVotesCount(), 0);
Expand Down
Loading

0 comments on commit c4f07e4

Please sign in to comment.