Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 7 additions & 17 deletions contracts/src/consensus/ConsensusV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,15 @@ contract ConsensusV1 is UUPSUpgradeable, OwnableUpgradeable {

// External functions
function setFee(uint128 registrationFee) external onlyOwner {
if (_fee == registrationFee) {
return;
}

_fee = registrationFee;
emit FeeUpdated(registrationFee);
}

// TODO: import validator without bls public key
function addValidator(address addr, bytes calldata blsPublicKey, bool isResigned) external onlyOwner {
function addValidator(address addr, bool isResigned) external onlyOwner {
if (_rounds.length > 0) {
revert ImportIsNotAllowed();
}
Expand All @@ -144,17 +147,8 @@ contract ConsensusV1 is UUPSUpgradeable, OwnableUpgradeable {
revert ValidatorAlreadyRegistered();
}

if (_blsPublicKeys[keccak256(blsPublicKey)]) {
revert BlsKeyAlreadyRegistered();
}

// Allow empty blsPublicKey for imports
if (blsPublicKey.length != 0) {
_verifyAndRegisterBlsPublicKey(blsPublicKey);
}

ValidatorData memory validator =
ValidatorData({votersCount: 0, voteBalance: 0, fee: 0, isResigned: isResigned, blsPublicKey: blsPublicKey});
ValidatorData({votersCount: 0, voteBalance: 0, fee: 0, isResigned: isResigned, blsPublicKey: ""});

_hasValidator[addr] = true;
_validatorsData[addr] = validator;
Expand All @@ -164,11 +158,7 @@ contract ConsensusV1 is UUPSUpgradeable, OwnableUpgradeable {
_resignedValidatorsCount++;
}

if (_canBecomeActiveValidator(addr)) {
_addActiveValidator(addr);
}

emit ValidatorRegistered(addr, blsPublicKey);
emit ValidatorRegistered(addr, "");
}

function addVotes(address[] calldata voters, address[] calldata validators) external onlyOwner {
Expand Down
6 changes: 3 additions & 3 deletions contracts/test/consensus/Consensus-CalculateTop.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ contract ConsensusTest is Base {
}

function test_should_revert_with_only_resigned_validators() public {
consensus.addValidator(address(2), prepareBLSKey(address(2)), true);
consensus.addValidator(address(2), true);

vm.expectRevert(ConsensusV1.NoActiveValidators.selector);
consensus.calculateRoundValidators(1);
}

function test_should_revert_with_only_validators_without_public_key() public {
consensus.addValidator(address(1), new bytes(0), false);
consensus.addValidator(address(1), false);

vm.expectRevert(ConsensusV1.NoActiveValidators.selector);
consensus.calculateRoundValidators(1);
Expand Down Expand Up @@ -79,7 +79,7 @@ contract ConsensusTest is Base {
address addr = address(1);

registerValidator(addr);
consensus.addValidator(address(2), new bytes(0), false);
consensus.addValidator(address(2), false);

// address(2) has no BLS key so it is not eligible; only one eligible validator remains.
vm.expectRevert(abi.encodeWithSelector(ConsensusV1.InsufficientActiveValidators.selector, 1, 2));
Expand Down
49 changes: 49 additions & 0 deletions contracts/test/consensus/Consensus-Fee.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.8.13;

import {ConsensusV1} from "@contracts/consensus/ConsensusV1.sol";
import {Base} from "./Base.sol";
import {Vm} from "@forge-std/Vm.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

Expand Down Expand Up @@ -30,4 +31,52 @@ contract ConsensusTest is Base {

assertEq(consensus.fee(), newFee);
}

function test_set_fee_same_value_is_noop() public {
uint128 newFee = 1000;

// Establish a non-zero fee.
vm.expectEmit(address(consensus));
emit ConsensusV1.FeeUpdated(newFee);
consensus.setFee(newFee);
assertEq(consensus.fee(), newFee);

// Setting the same value again changes nothing and emits no event.
vm.recordLogs();
consensus.setFee(newFee);
Vm.Log[] memory logs = vm.getRecordedLogs();

assertEq(logs.length, 0);
assertEq(consensus.fee(), newFee);
}

function test_set_fee_same_as_default_is_noop() public {
// Default fee is 0; setting it to 0 again is a no-op (no event, no change).
assertEq(consensus.fee(), 0);

vm.recordLogs();
consensus.setFee(0);
Vm.Log[] memory logs = vm.getRecordedLogs();

assertEq(logs.length, 0);
assertEq(consensus.fee(), 0);
}

function test_set_fee_still_updates_after_noop() public {
uint128 firstFee = 1000;
consensus.setFee(firstFee);

// A repeated value is skipped...
vm.recordLogs();
consensus.setFee(firstFee);
assertEq(vm.getRecordedLogs().length, 0);

// ...but a different value still updates and emits (guard is strictly equality).
uint128 secondFee = 2000;
vm.expectEmit(address(consensus));
emit ConsensusV1.FeeUpdated(secondFee);
consensus.setFee(secondFee);

assertEq(consensus.fee(), secondFee);
}
}
2 changes: 1 addition & 1 deletion contracts/test/consensus/Consensus-ValidatoUpdate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ contract ConsensusTest is Base {
bytes memory pop = createValidPop();

address addr = address(1);
consensus.addValidator(addr, "", false);
consensus.addValidator(addr, false);
assertEq(consensus.validatorsCount(), 1);
assertEq(consensus.activeValidatorsCount(), 0);

Expand Down
85 changes: 34 additions & 51 deletions contracts/test/consensus/Consensus-ValidatorAdd.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pragma solidity ^0.8.13;

import {ConsensusV1} from "@contracts/consensus/ConsensusV1.sol";
import {Base} from "./Base.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract ConsensusTest is Base {
Expand All @@ -15,17 +14,19 @@ contract ConsensusTest is Base {

// Act
vm.expectEmit(address(consensus));
emit ConsensusV1.ValidatorRegistered(addr, prepareBLSKey(addr));
consensus.addValidator(addr, prepareBLSKey(addr), false);
emit ConsensusV1.ValidatorRegistered(addr, new bytes(0));
consensus.addValidator(addr, false);

// Assert
assertEq(consensus.validatorsCount(), 1);
assertEq(consensus.activeValidatorsCount(), 1);
// Imported validators are dormant: registered but never active, because they
// carry no BLS public key. They only become active via updateValidator().
assertEq(consensus.activeValidatorsCount(), 0);
assertEq(consensus.resignedValidatorsCount(), 0);

ConsensusV1.Validator memory validator = consensus.getValidator(addr);
assertEq(validator.addr, addr);
assertEq(validator.data.blsPublicKey, prepareBLSKey(addr));
assertEq(validator.data.blsPublicKey, new bytes(0));
assertEq(validator.data.voteBalance, 0);
assertEq(validator.data.votersCount, 0);
assertEq(validator.data.fee, 0);
Expand All @@ -40,90 +41,72 @@ contract ConsensusTest is Base {

// Act
vm.expectEmit(address(consensus));
emit ConsensusV1.ValidatorRegistered(addr, prepareBLSKey(addr));
consensus.addValidator(addr, prepareBLSKey(addr), true);
emit ConsensusV1.ValidatorRegistered(addr, new bytes(0));
consensus.addValidator(addr, true);

// Assert
assertEq(consensus.validatorsCount(), 1);
assertEq(consensus.activeValidatorsCount(), 0);
assertEq(consensus.resignedValidatorsCount(), 1);
ConsensusV1.Validator memory validator = consensus.getValidator(addr);
assertEq(validator.addr, addr);
assertEq(validator.data.blsPublicKey, prepareBLSKey(addr));
assertEq(validator.data.blsPublicKey, new bytes(0));
assertEq(validator.data.voteBalance, 0);
assertEq(validator.data.votersCount, 0);
assertEq(validator.data.fee, 0);
assertEq(validator.data.isResigned, true);
}

function test_validator_add_pass_if_ble_key_is_zero() public {
assertEq(consensus.validatorsCount(), 0);
assertEq(consensus.activeValidatorsCount(), 0);
assertEq(consensus.resignedValidatorsCount(), 0);
address addr = address(1);
function test_validator_add_keeps_validators_dormant() public {
// No matter how many validators are imported, none enter the active set:
// the active set is only reachable via the PoP-verified registerValidator /
// updateValidator paths.
consensus.addValidator(address(1), false);
consensus.addValidator(address(2), true);
consensus.addValidator(address(3), false);

// Act
vm.expectEmit(address(consensus));
emit ConsensusV1.ValidatorRegistered(addr, new bytes(0));
consensus.addValidator(addr, new bytes(0), false);

// Assert
assertEq(consensus.validatorsCount(), 1);
assertEq(consensus.validatorsCount(), 3);
assertEq(consensus.activeValidatorsCount(), 0);
assertEq(consensus.resignedValidatorsCount(), 0);
assertEq(consensus.resignedValidatorsCount(), 1);

ConsensusV1.Validator memory validator = consensus.getValidator(addr);
assertEq(validator.addr, addr);
assertEq(validator.data.blsPublicKey, new bytes(0));
assertEq(validator.data.voteBalance, 0);
assertEq(validator.data.votersCount, 0);
assertEq(validator.data.fee, 0);
assertEq(validator.data.isResigned, false);
assertEq(consensus.getValidator(address(1)).data.blsPublicKey, new bytes(0));
assertEq(consensus.getValidator(address(2)).data.blsPublicKey, new bytes(0));
assertEq(consensus.getValidator(address(3)).data.blsPublicKey, new bytes(0));
}

function test_validator_add_revert_if_caller_is_not_owner() public {
address addr = address(1);
vm.startPrank(addr);
vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, addr));
consensus.addValidator(addr, prepareBLSKey(addr), false);
consensus.addValidator(addr, false);
vm.stopPrank();
}

function test_validator_add_revert_if_round_already_calculated() public {
address addr = address(1);
consensus.addValidator(addr, prepareBLSKey(addr), false);
// An active validator is required for the round calculation to succeed, since
// imported (dormant) validators never enter the active set.
registerValidator(address(1));

consensus.calculateRoundValidators(1);

vm.expectRevert(ConsensusV1.ImportIsNotAllowed.selector);
consensus.addValidator(addr, prepareBLSKey(addr), false);
consensus.addValidator(address(2), false);
}

function test_validator_add_revert_if_validator_is_already_registered() public {
address addr = address(1);
consensus.addValidator(addr, prepareBLSKey(addr), false);
consensus.addValidator(addr, false);

vm.expectRevert(ConsensusV1.ValidatorAlreadyRegistered.selector);
consensus.addValidator(addr, prepareBLSKey(addr), false);
consensus.addValidator(addr, false);
}

function test_validator_registration_revert_if_bls_key_is_already_registered() public {
function test_validator_add_revert_if_validator_is_already_active() public {
// A validator that registered through the live path cannot be shadow-imported.
address addr = address(1);
consensus.addValidator(addr, prepareBLSKey(addr), false);

vm.expectRevert(ConsensusV1.BlsKeyAlreadyRegistered.selector);
consensus.addValidator(address(2), prepareBLSKey(addr), false);
}
registerValidator(addr);

function test_validator_registration_revert_if_bls_key_length_is_invalid() public {
address addr = address(1);

vm.expectRevert(ConsensusV1.BlsKeyIsInvalid.selector);
consensus.addValidator(addr, prepareBLSKey(addr, 46), false);
vm.expectRevert(ConsensusV1.BlsKeyIsInvalid.selector);
consensus.addValidator(addr, prepareBLSKey(addr, 47), false);
vm.expectRevert(ConsensusV1.BlsKeyIsInvalid.selector);
consensus.addValidator(addr, prepareBLSKey(addr, 49), false);
vm.expectRevert(ConsensusV1.BlsKeyIsInvalid.selector);
consensus.addValidator(addr, prepareBLSKey(addr, 50), false);
vm.expectRevert(ConsensusV1.ValidatorAlreadyRegistered.selector);
consensus.addValidator(addr, false);
}
}
7 changes: 4 additions & 3 deletions contracts/test/consensus/Consensus-ValidatorResignation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,12 @@ contract ConsensusTest is Base {

assertEq(consensus.validatorsCount(), 3);

// Act - value higher than the active validator count must revert (no duplicate-slot padding).
// _minValidators is left unchanged because the whole call reverts.
// Act - higher value than the active validator count is rejected: a round can no longer
// be padded by duplicating validators, so the minimum cannot exceed the active set.
vm.expectRevert(abi.encodeWithSelector(ConsensusV1.InsufficientActiveValidators.selector, 3, 5));
consensus.calculateRoundValidators(5);

// Act - same value as the active count sets _minValidators = 3
// Act - same value as the active count: min == active, cannot resign below the minimum
consensus.calculateRoundValidators(3);

// Test
Expand All @@ -234,5 +234,6 @@ contract ConsensusTest is Base {
vm.startPrank(address(2));
vm.expectRevert(ConsensusV1.BellowMinValidators.selector);
consensus.resignValidator();
vm.stopPrank();
}
}
2 changes: 1 addition & 1 deletion contracts/test/consensus/Consensus-Vote.sol
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ contract ConsensusTest is Base {

function test_vote_prevent_for_validator_without_bls_key() public {
address addr = address(1);
consensus.addValidator(addr, new bytes(0), false);
consensus.addValidator(addr, false);

vm.startPrank(addr);
vm.expectRevert(ConsensusV1.VoteValidatorWithoutBlsPublicKey.selector);
Expand Down
6 changes: 3 additions & 3 deletions contracts/test/consensus/Consensus-VoteAdd.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ contract ConsensusTest is Base {

// Register validator
address addr = address(1);
consensus.addValidator(addr, prepareBLSKey(addr), true);
consensus.addValidator(addr, true);

// Prepare voter
address voterAddr = address(2);
Expand Down Expand Up @@ -95,7 +95,7 @@ contract ConsensusTest is Base {

// Register validator
address addr = address(1);
consensus.addValidator(addr, new bytes(0), false);
consensus.addValidator(addr, false);

// Prepare voter
address voterAddr = address(2);
Expand Down Expand Up @@ -145,7 +145,7 @@ contract ConsensusTest is Base {
address[] memory validators = new address[](1);
validators[0] = addr;

consensus.addValidator(addr, prepareBLSKey(addr), false);
registerValidator(addr);

consensus.calculateRoundValidators(1);

Expand Down
2 changes: 1 addition & 1 deletion packages/api-http/test/fixtures/node_configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"tolerance": 100
},
"roundValidators": 0,
"validatorRegistrationFee": "250000000000000000000"
"validatorRegistrationFee": "0"
},
"core": {
"version": "0.0.1"
Expand Down
Loading
Loading