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

ACP 99 reference implementation #700

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open

Conversation

cam-schultz
Copy link
Contributor

@cam-schultz cam-schultz commented Jan 10, 2025

Why this should be merged

Implements ACP-99 as described in avalanche-foundation/ACPs#170

How this works

Adds ACP99Manager to the class hierarchy,. ValidatorManager implements ACP99Manager.

How this was tested

CI, fixed breaking tests

How is this documented

Updates README to point to ACP-99

Future work

The following items are candidates to be included in this PR. They may also be deferred to later changes.

  • Remove IValidatorManager altogether, as ACP99Manager does the same job of defining the core validator manager functionality
  • Rename all initialize* methods to initiate* to match ACP-99. initialize is a misnomer, since nothing is initialized as part of these function calls. Rather, they initiate the process of registering/modifying a validator.

Copy link
Contributor

@richardpringle richardpringle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just leaving as a comment review. All looks pretty good

contracts/validator-manager/ACP99Manager.sol Show resolved Hide resolved
contracts/validator-manager/ACP99Manager.sol Outdated Show resolved Hide resolved
contracts/validator-manager/ACP99Manager.sol Show resolved Hide resolved
contracts/validator-manager/PoAValidatorManager.sol Outdated Show resolved Hide resolved
Comment on lines 326 to 327
_initiateValidatorRemoval(validationID);
Validator memory validator = getValidator(validationID);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order of these calls matters, since _initiateValidatorRemoval modifies the Validator. I removed the Validator return value from the ACP99Manager interface since it was not strictly necessary, and can otherwise be accessed like it is here. If the internal ACP99Manager functions do return a Validator, it increases the ERC20TokenStakingManager contract size by ~900 bytes, surpassing the size limit.

I settled on adding a @dev comment to the ValidatorManager implementation of these methods to warn developers that this function mutates accessible state.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should probably add a little blurb inline here.

// order matters for the next two lines, see...

Then reference an explanation somewhere? Ideally in a github permalink or something else that is guaranteed not to change. That is unless you can make the comment succinct, haha (I can't).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that a blurb should be added. I think it would be difficult for someone to discover the ordering constraint here without one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment above the call to getValidator here. I also added a @dev natspec comment to all of the methods in ValidatorManager that touch the internal validator state.

@cam-schultz cam-schultz marked this pull request as ready for review January 15, 2025 19:05
@cam-schultz cam-schultz requested a review from a team as a code owner January 15, 2025 19:05
Comment on lines 326 to 327
_initiateValidatorRemoval(validationID);
Validator memory validator = getValidator(validationID);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should probably add a little blurb inline here.

// order matters for the next two lines, see...

Then reference an explanation somewhere? Ideally in a github permalink or something else that is guaranteed not to change. That is unless you can make the comment succinct, haha (I can't).

contracts/validator-manager/PoSValidatorManager.sol Outdated Show resolved Hide resolved
(bytes32 messageValidationID, uint64 nonce) =
completeValidatorWeightUpdate(messageIndex);
if (validationID != messageValidationID) {
revert InvalidValidationID(delegator.validationID);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should probably output both the validation ID of the message, as well as the validation ID of the delegator here. (I realize this is the old behaviour, so feel free to punt this)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a new error type UnexpectedValidationID for cases when the provided ID does not match the expected one. The unary InvalidaValidationID is still used in the resend methods for cases where the provided validation ID doesn't correspond to a registered validation.

Copy link
Contributor

@bernard-avalabs bernard-avalabs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally makes sense to me.

Comment on lines 326 to 327
_initiateValidatorRemoval(validationID);
Validator memory validator = getValidator(validationID);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that a blurb should be added. I think it would be difficult for someone to discover the ordering constraint here without one.

Copy link
Contributor

@richardpringle richardpringle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

_initiateValidatorRemoval(validationID);

// The validator must be fetched after the removal has been initiated, since the above call modifies
// the validator's state.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@cam-schultz
Copy link
Contributor Author

See here for context on the nodeID format in events, as implemented in 0f7039f

Comment on lines +659 to +666
function _fixedNodeID(bytes memory nodeID) private pure returns (bytes20) {
bytes20 fixedID;
// solhint-disable-next-line no-inline-assembly
assembly {
fixedID := mload(add(nodeID, 32))
}
return fixedID;
}

Check warning

Code scanning / Slither

Assembly usage Warning

@@ -171,25 +181,27 @@
if ($._registeredValidators[initialValidator.nodeID] != bytes32(0)) {
revert NodeAlreadyRegistered(initialValidator.nodeID);
}
if (initialValidator.nodeID.length != NODE_ID_LENGTH) {

Check notice

Code scanning / Semgrep OSS

Semgrep Finding: solidity.performance.array-length-outside-loop.array-length-outside-loop Note

Caching the array length outside a loop saves reading it on each iteration, as long as the array's length is not changed during the loop.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Backlog 🗄️
Development

Successfully merging this pull request may close these issues.

4 participants