Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Validator registration in L2 without BLS key #11181

Merged
merged 5 commits into from
Aug 19, 2024
Merged
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
33 changes: 30 additions & 3 deletions packages/protocol/contracts/governance/Validators.sol
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ contract Validators is
* @return True upon success.
* @dev Fails if the account is already a validator or validator group.
* @dev Fails if the account does not have sufficient Locked Gold.
* @dev Fails after L2 activation, but see registerValidator(bytes) below.
*/
function registerValidator(
bytes calldata ecdsaPublicKey,
Expand Down Expand Up @@ -271,6 +272,34 @@ contract Validators is
return true;
}

/**
* @notice Registers a validator unaffiliated with any validator group.
* @param ecdsaPublicKey The ECDSA public key that the validator is using for consensus, should
* match the validator signer. 64 bytes.
* @return True upon success.
* @dev Fails if the account is already a validator or validator group.
* @dev Fails if the account does not have sufficient Locked Gold.
*/
function registerValidator(
bytes calldata ecdsaPublicKey
) external nonReentrant onlyL2 returns (bool) {
address account = getAccounts().validatorSignerToAccount(msg.sender);
_isRegistrationAllowed(account);
require(!isValidator(account) && !isValidatorGroup(account), "Already registered");
uint256 lockedGoldBalance = getLockedGold().getAccountTotalLockedGold(account);
require(lockedGoldBalance >= validatorLockedGoldRequirements.value, "Deposit too small");
Validator storage validator = validators[account];
address signer = getAccounts().getValidatorSigner(account);
require(
_updateEcdsaPublicKey(validator, account, signer, ecdsaPublicKey),
"Error updating ECDSA public key"
);
registeredValidators.push(account);
updateMembershipHistory(account, address(0));
emit ValidatorRegistered(account);
return true;
}

/**
* @notice De-registers a validator.
* @param index The index of this validator in the list of all registered validators.
Expand Down Expand Up @@ -374,7 +403,6 @@ contract Validators is
address signer,
bytes calldata ecdsaPublicKey
) external onlyRegisteredContract(ACCOUNTS_REGISTRY_ID) returns (bool) {
allowOnlyL1();
require(isValidator(account), "Not a validator");
Validator storage validator = validators[account];
require(
Expand Down Expand Up @@ -451,7 +479,6 @@ contract Validators is
* @dev Fails if the account does not have sufficient weight.
*/
function registerValidatorGroup(uint256 commission) external nonReentrant returns (bool) {
allowOnlyL1();
require(commission <= FixidityLib.fixed1().unwrap(), "Commission can't be greater than 100%");
address account = getAccounts().validatorSignerToAccount(msg.sender);
_isRegistrationAllowed(account);
Expand Down Expand Up @@ -1120,7 +1147,7 @@ contract Validators is
* @return Whether a particular address is a registered validator.
*/
function isValidator(address account) public view returns (bool) {
return validators[account].publicKeys.bls.length > 0;
return validators[account].publicKeys.ecdsa.length > 0;
martinvol marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ contract MockElection is IsL2Check {
return electedValidators;
}

function setAllowedToVoteOverMaxNumberOfGroups(address account, bool flag) public onlyL1 {
function setAllowedToVoteOverMaxNumberOfGroups(address account, bool flag) public {
allowedToVoteOverMaxNumberOfGroups[account] = flag;
}
}
215 changes: 213 additions & 2 deletions packages/protocol/test-sol/unit/governance/validators/Validators.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,23 @@ contract ValidatorsTest is Test, TestConstants, Utils, ECDSAHelper {
return _ecdsaPubKey;
}

function _registerValidatorWithSignerHelper_noBls() internal returns (bytes memory) {
lockedGold.setAccountTotalLockedGold(validator, originalValidatorLockedGoldRequirements.value);

(bytes memory _ecdsaPubKey, uint8 v, bytes32 r, bytes32 s) = _generateEcdsaPubKeyWithSigner(
validator,
signerPk
);

vm.prank(validator);
accounts.authorizeValidatorSigner(signer, v, r, s);

vm.prank(validator);
validators.registerValidator(_ecdsaPubKey);
validatorRegistrationEpochNumber = validators.getEpochNumber();
return _ecdsaPubKey;
}

function _generateEcdsaPubKey(
address _account,
uint256 _accountPk
Expand Down Expand Up @@ -804,6 +821,191 @@ contract ValidatorsTest_RegisterValidator is ValidatorsTest {
);
}
}
contract ValidatorsTest_RegisterValidator_NoBls is ValidatorsTest {
function setUp() public {
super.setUp();

lockedGold.setAccountTotalLockedGold(validator, originalValidatorLockedGoldRequirements.value);
}

function test_Reverts_WhenVoteOverMaxNumberOfGroupsSetToTrue() public {
_whenL2();
vm.prank(validator);
election.setAllowedToVoteOverMaxNumberOfGroups(validator, true);

(uint8 v, bytes32 r, bytes32 s) = getParsedSignatureOfAddress(validator, signerPk);

vm.prank(validator);
accounts.authorizeValidatorSigner(signer, v, r, s);
bytes memory pubKey = addressToPublicKey("random msg", v, r, s);

vm.expectRevert("Cannot vote for more than max number of groups");
vm.prank(validator);
validators.registerValidator(pubKey);
}

function test_Reverts_WhenDelagatingCELO() public {
_whenL2();
lockedGold.setAccountTotalDelegatedAmountInPercents(validator, 10);
(uint8 v, bytes32 r, bytes32 s) = getParsedSignatureOfAddress(validator, signerPk);
vm.prank(validator);
accounts.authorizeValidatorSigner(signer, v, r, s);
bytes memory pubKey = addressToPublicKey("random msg", v, r, s);

vm.expectRevert("Cannot delegate governance power");
vm.prank(validator);
validators.registerValidator(pubKey);
}

function test_ShouldMarkAccountAsValidator_WhenAccountHasAuthorizedValidatorSigner() public {
_whenL2();
_registerValidatorWithSignerHelper_noBls();

assertTrue(validators.isValidator(validator));
}

function test_ShouldRevert_WhenInL1_WhenAccountHasAuthorizedValidatorSigner() public {
lockedGold.setAccountTotalLockedGold(validator, originalValidatorLockedGoldRequirements.value);

(bytes memory _ecdsaPubKey, uint8 v, bytes32 r, bytes32 s) = _generateEcdsaPubKeyWithSigner(
validator,
signerPk
);

ph.mockSuccess(ph.PROOF_OF_POSSESSION(), abi.encodePacked(validator, blsPublicKey, blsPop));

vm.prank(validator);
accounts.authorizeValidatorSigner(signer, v, r, s);

vm.expectRevert("This method is not supported in L1.");
vm.prank(validator);
validators.registerValidator(_ecdsaPubKey);
validatorRegistrationEpochNumber = validators.getEpochNumber();
}

function test_ShouldAddAccountToValidatorList_WhenAccountHasAuthorizedValidatorSigner() public {
_whenL2();
address[] memory ExpectedRegisteredValidators = new address[](1);
ExpectedRegisteredValidators[0] = validator;
_registerValidatorWithSignerHelper_noBls();
assertEq(validators.getRegisteredValidators().length, ExpectedRegisteredValidators.length);
assertEq(validators.getRegisteredValidators()[0], ExpectedRegisteredValidators[0]);
}

function test_ShouldSetValidatorEcdsaPublicKey_WhenAccountHasAuthorizedValidatorSigner() public {
_whenL2();
bytes memory _registeredEcdsaPubKey = _registerValidatorWithSignerHelper_noBls();
(bytes memory actualEcdsaPubKey, , , , ) = validators.getValidator(validator);

assertEq(actualEcdsaPubKey, _registeredEcdsaPubKey);
}

function test_ShouldNotSetValidatorBlsPublicKey_WhenAccountHasAuthorizedValidatorSigner() public {
_whenL2();
_registerValidatorWithSignerHelper_noBls();
(, bytes memory actualBlsPubKey, , , ) = validators.getValidator(validator);

assertEq(actualBlsPubKey, "");
}

function test_ShouldSetValidatorSigner_WhenAccountHasAuthorizedValidatorSigner() public {
_whenL2();
_registerValidatorWithSignerHelper_noBls();
(, , , , address ActualSigner) = validators.getValidator(validator);

assertEq(ActualSigner, signer);
}

function test_ShouldSetLockGoldRequirements_WhenAccountHasAuthorizedValidatorSigner() public {
_whenL2();
_registerValidatorWithSignerHelper_noBls();
uint256 _lockedGoldReq = validators.getAccountLockedGoldRequirement(validator);

assertEq(_lockedGoldReq, originalValidatorLockedGoldRequirements.value);
}

function test_ShouldSetValidatorMembershipHistory_WhenAccountHasAuthorizedValidatorSigner()
public
{
_whenL2();
_registerValidatorWithSignerHelper_noBls();
(uint256[] memory _epoch, address[] memory _membershipGroups, , ) = validators
.getMembershipHistory(validator);

uint256[] memory validatorRegistrationEpochNumberList = new uint256[](1);
validatorRegistrationEpochNumberList[0] = validatorRegistrationEpochNumber;
address[] memory expectedMembershipGroups = new address[](1);
expectedMembershipGroups[0] = address(0);

assertEq(_epoch, validatorRegistrationEpochNumberList);
assertEq(_membershipGroups, expectedMembershipGroups);
}

function testFail_DoesNotEmit_ValidatorBlsPublicKeyUpdatedEvent() public {
_whenL2();
(bytes memory _ecdsaPubKey, uint8 v, bytes32 r, bytes32 s) = _generateEcdsaPubKeyWithSigner(
validator,
signerPk
);

vm.prank(validator);
accounts.authorizeValidatorSigner(signer, v, r, s);

vm.expectEmit(true, true, true, true);
emit ValidatorBlsPublicKeyUpdated(validator, blsPublicKey);

vm.prank(validator);
validators.registerValidator(_ecdsaPubKey);
}

function test_Emits_ValidatorRegisteredEvent() public {
_whenL2();
(bytes memory _ecdsaPubKey, uint8 v, bytes32 r, bytes32 s) = _generateEcdsaPubKeyWithSigner(
validator,
signerPk
);

vm.prank(validator);
accounts.authorizeValidatorSigner(signer, v, r, s);

vm.expectEmit(true, true, true, true);
emit ValidatorRegistered(validator);

vm.prank(validator);
validators.registerValidator(_ecdsaPubKey);
}

function test_Reverts_WhenAccountAlreadyRegisteredAsValidator() public {
_whenL2();
bytes memory _registeredEcdsaPubKey = _registerValidatorWithSignerHelper_noBls();
vm.expectRevert("Already registered");
vm.prank(validator);
validators.registerValidator(_registeredEcdsaPubKey);
}

function test_Reverts_WhenAccountAlreadyRegisteredAsValidatorGroup() public {
_whenL2();
_registerValidatorGroupHelper(validator, 1);
vm.expectRevert("Already registered");
vm.prank(validator);
validators.registerValidator(
abi.encodePacked(bytes32(0x0101010101010101010101010101010101010101010101010101010101010101))
soloseng marked this conversation as resolved.
Show resolved Hide resolved
);
}

function test_Reverts_WhenAccountDoesNotMeetLockedGoldRequirements() public {
_whenL2();
lockedGold.setAccountTotalLockedGold(
validator,
originalValidatorLockedGoldRequirements.value.sub(11)
);
vm.expectRevert("Deposit too small");
vm.prank(validator);
validators.registerValidator(
abi.encodePacked(bytes32(0x0101010101010101010101010101010101010101010101010101010101010101))
);
}
}

contract ValidatorsTest_DeregisterValidator_WhenAccountHasNeverBeenMemberOfValidatorGroup is
ValidatorsTest
Expand Down Expand Up @@ -1313,7 +1515,7 @@ contract ValidatorsTest_UpdateEcdsaPublicKey is ValidatorsTest {
assertEq(actualEcdsaPubKey, _newEcdsaPubKey);
}

function test_Reverts_SetValidatorEcdsaPubKey_WhenCalledByRegisteredAccountsContract_WhenL2()
function test_ShouldSetValidatorEcdsaPubKey_WhenCalledByRegisteredAccountsContract_WhenL2()
public
{
_whenL2();
Expand All @@ -1322,8 +1524,11 @@ contract ValidatorsTest_UpdateEcdsaPublicKey is ValidatorsTest {
signerPk
);
vm.prank(address(accounts));
vm.expectRevert("This method is no longer supported in L2.");
validators.updateEcdsaPublicKey(validator, signer, _newEcdsaPubKey);

(bytes memory actualEcdsaPubKey, , , , ) = validators.getValidator(validator);

assertEq(actualEcdsaPubKey, _newEcdsaPubKey);
}

function test_Emits_ValidatorEcdsaPublicKeyUpdatedEvent_WhenCalledByRegisteredAccountsContract()
Expand Down Expand Up @@ -1608,6 +1813,12 @@ contract ValidatorsTest_RegisterValidatorGroup is ValidatorsTest {
assertTrue(validators.isValidatorGroup(group));
}

function test_WhenInL2_ShouldMarkAccountAsValidatorGroup() public {
_whenL2();
_registerValidatorGroupHelper(group, 1);
assertTrue(validators.isValidatorGroup(group));
}

function test_ShouldAddAccountToListOfValidatorGroup() public {
address[] memory ExpectedRegisteredValidatorGroups = new address[](1);
ExpectedRegisteredValidatorGroups[0] = group;
Expand Down
Loading