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

[Proposed] ACP-99: Implement using composition - ValidatorManager as single entry point #668

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
wip
  • Loading branch information
cam-schultz committed Dec 5, 2024
commit 05d07ba50fb5fdcc754e41c52a48c3a0f55a2ff6

Large diffs are not rendered by default.

Large diffs are not rendered by default.

49 changes: 49 additions & 0 deletions abi-bindings/go/validator-manager/ArgumentsExternal/packing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package argumentsexternal

import "github.com/ava-labs/subnet-evm/accounts/abi"

var (
initializeValidatorRegistrationArgsType abi.Type
initializeEndValidationArgsType abi.Type
)

func init() {
var err error
initializeValidatorRegistrationArgsType, err = abi.NewType("tuple", "struct Overloader.F", []abi.ArgumentMarshaling{
{Name: "delegationFeeBips", Type: "uint16"},
{Name: "minStakeDuration", Type: "uint64"},
})
if err != nil {
panic("failed to create InitializeValidatorRegistrationArgs ABI type")
}

initializeEndValidationArgsType, err = abi.NewType("tuple", "struct Overloader.F", []abi.ArgumentMarshaling{
{Name: "force", Type: "bool"},
{Name: "includeUptimeProof", Type: "bool"},
{Name: "messageIndex", Type: "uint32"},
{Name: "rewardRecipient", Type: "address"},
})
if err != nil {
panic("failed to create InitializeEndValidationArgs ABI type")
}
}

func (a *InitializeValidatorRegistrationArgs) Pack() ([]byte, error) {
args := abi.Arguments{
{
Name: "initializeValidatorRegistrationArgs",
Type: initializeValidatorRegistrationArgsType,
},
}
return args.Pack(a)
}

func (a *InitializeEndValidationArgs) Pack() ([]byte, error) {
args := abi.Arguments{
{
Name: "initializeEndValidationArgs",
Type: initializeEndValidationArgsType,
},
}
return args.Pack(a)
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

1,534 changes: 123 additions & 1,411 deletions abi-bindings/go/validator-manager/PoAValidatorManager/PoAValidatorManager.go

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

73 changes: 64 additions & 9 deletions contracts/validator-manager/ACP99ValidatorManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,57 @@
import {IACP99SecurityModule} from "./interfaces/IACP99SecurityModule.sol";
import {ValidatorManager} from "./ValidatorManager.sol";
import {IACP99ValidatorManager, ConversionData, ValidatorRegistrationInput} from "./interfaces/IACP99ValidatorManager.sol";
import {ICMInitializable} from "@utilities/ICMInitializable.sol";
import {ValidatorManagerSettings} from "./interfaces/IValidatorManager.sol";

pragma solidity 0.8.25;

contract ACP99ValidatorManager is IACP99ValidatorManager, ValidatorManager {
IACP99SecurityModule public securityModule;
struct ACP99ValidatorManagerStorage {
IACP99SecurityModule securityModule;
}
// keccak256(abi.encode(uint256(keccak256("avalanche-icm.storage.ACP99ValidatorManager")) - 1)) & ~bytes32(uint256(0xff));
bytes32 public constant ACP_99_VALIDATOR_MANAGER_STORAGE_LOCATION =
0x6d7896c90f86967e463241c430aa4c1ef638639857cb8d4f18c905fa5443d600;

function _getACP99ValidatorManagerStorage()
private
pure
returns (ACP99ValidatorManagerStorage storage $)
{
// solhint-disable-next-line no-inline-assembly
assembly {
$.slot := ACP_99_VALIDATOR_MANAGER_STORAGE_LOCATION
}
}

constructor(ICMInitializable init) {
if (init == ICMInitializable.Disallowed) {
_disableInitializers();
}
}

function initialize(ValidatorManagerSettings calldata settings, IACP99SecurityModule securityModule) external initializer {
__ACP99ValidatorManager_init(settings, securityModule);
}

// TODO: calling this should be restricted to...who?
Copy link
Contributor

Choose a reason for hiding this comment

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

😜

Suggested change
// TODO: calling this should be restricted to...who?
// TODO: calling this should be restricted to...whom?

function setSecurityModule(IACP99SecurityModule _securityModule) external {
securityModule = _securityModule;
function setSecurityModule(IACP99SecurityModule securityModule) external {
_getACP99ValidatorManagerStorage().securityModule = securityModule;
}

function getSecurityModule() external view returns (IACP99SecurityModule) {
return _getACP99ValidatorManagerStorage().securityModule;
}

function __ACP99ValidatorManager_init(ValidatorManagerSettings calldata settings, IACP99SecurityModule securityModule) internal onlyInitializing {
__ValidatorManager_init(settings);
__ACP99ValidatorManager_init_unchained(securityModule);
}

function __ACP99ValidatorManager_init_unchained(IACP99SecurityModule securityModule) internal onlyInitializing {
ACP99ValidatorManagerStorage storage $ = _getACP99ValidatorManagerStorage();
$.securityModule = securityModule;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Should the security module itself control its own upgrades? If not, should there be a SecurityUpdateModule?


function initializeValidatorSet(
Expand All @@ -30,31 +72,44 @@ contract ACP99ValidatorManager is IACP99ValidatorManager, ValidatorManager {
bytes calldata args
) external returns (bytes32){
bytes32 validationID = _initializeValidatorRegistration(input, weight);
securityModule.handleInitializeValidatorRegistration(validationID, weight, args);
ACP99ValidatorManagerStorage storage $ = _getACP99ValidatorManagerStorage();
// address securityModule = address($.securityModule);

// // Delegate call so that the sender can approval ERC20 transfers by the validator manager
// (bool success, bytes memory data) = securityModule.delegatecall(abi.encodeWithSelector($.securityModule.handleInitializeValidatorRegistration.selector, validationID, weight, args));
// require(success, string(data));

$.securityModule.handleInitializeValidatorRegistration(validationID, weight, args);
return validationID;
}

function completeValidatorRegistration(uint32 messageIndex) external{
bytes32 validationID = _completeValidatorRegistration(messageIndex);
securityModule.handleCompleteValidatorRegistration(validationID);
_getACP99ValidatorManagerStorage().securityModule.handleCompleteValidatorRegistration(validationID);
}

function initializeEndValidation(bytes32 validationID, bytes calldata args) external{
_initializeEndValidation(validationID);
securityModule.handleInitializeEndValidation(validationID, args);
_getACP99ValidatorManagerStorage().securityModule.handleInitializeEndValidation(validationID, args);
}

function completeEndValidation(uint32 messageIndex) external{
bytes32 validationID = _completeEndValidation(messageIndex);
securityModule.handleCompleteEndValidation(validationID);
ACP99ValidatorManagerStorage storage $ = _getACP99ValidatorManagerStorage();
// address securityModule = address($.securityModule);

// (bool success, bytes memory data) = securityModule.delegatecall(abi.encodeWithSelector($.securityModule.handleCompleteEndValidation.selector, validationID));
// require(success, string(data));

$.securityModule.handleCompleteEndValidation(validationID);
}

function initializeValidatorWeightChange(bytes32 validationID, uint64 weight, bytes calldata args) external{
(uint64 nonce, ) = _setValidatorWeight(validationID, weight);
securityModule.handleInitializeValidatorWeightChange(validationID, weight, nonce, args);
_getACP99ValidatorManagerStorage().securityModule.handleInitializeValidatorWeightChange(validationID, weight, nonce, args);
}

function completeValidatorWeightChange(bytes32 validationID, bytes calldata args) external{
securityModule.handleCompleteValidatorWeightChange(validationID, args);
_getACP99ValidatorManagerStorage().securityModule.handleCompleteValidatorWeightChange(validationID, args);
}
}
28 changes: 28 additions & 0 deletions contracts/validator-manager/Arguments.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// SPDX-License-Identifier: Ecosystem

pragma solidity 0.8.25;

struct InitializeValidatorRegistrationArgs {
uint16 delegationFeeBips;
uint64 minStakeDuration;
}

struct InitializeEndValidationArgs {
bool force;
bool includeUptimeProof;
uint32 messageIndex;
address rewardRecipient;
}

library Arguments {
function decodeInitializeValidatorRegistrationArgs(bytes calldata args) internal pure returns (InitializeValidatorRegistrationArgs memory) {
return abi.decode(args, (InitializeValidatorRegistrationArgs));
}

function decodeInitializeEndValidationArgs(bytes calldata args) internal pure returns (InitializeEndValidationArgs memory) {
return abi.decode(args, (InitializeEndValidationArgs));
}
}
18 changes: 18 additions & 0 deletions contracts/validator-manager/ArgumentsExternal.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// SPDX-License-Identifier: Ecosystem

pragma solidity 0.8.25;

import {InitializeEndValidationArgs, InitializeValidatorRegistrationArgs} from "./Arguments.sol";

contract ArgumentsExternal {
function decodeInitializeEndValidationArgs(bytes calldata args) external pure returns (InitializeEndValidationArgs memory) {
return abi.decode(args, (InitializeEndValidationArgs));
}

function decodeInitializeValidatorRegistrationArgs(bytes calldata args) external pure returns (InitializeValidatorRegistrationArgs memory) {
return abi.decode(args, (InitializeValidatorRegistrationArgs));
}
}
7 changes: 1 addition & 6 deletions contracts/validator-manager/PoAValidatorManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@

pragma solidity 0.8.25;

import {
ValidatorManagerSettings
} from "./interfaces/IValidatorManager.sol";
import {ICMInitializable} from "@utilities/ICMInitializable.sol";
import {OwnableUpgradeable} from
"@openzeppelin/contracts-upgradeable@5.0.2/access/OwnableUpgradeable.sol";
Expand All @@ -27,15 +24,13 @@ contract PoAValidatorManager is IACP99SecurityModule, OwnableUpgradeable {
}

function initialize(
ValidatorManagerSettings calldata settings,
address initialOwner
) external initializer {
__PoAValidatorManager_init(settings, initialOwner);
__PoAValidatorManager_init(initialOwner);
}

// solhint-disable func-name-mixedcase, ordering
function __PoAValidatorManager_init(
ValidatorManagerSettings calldata,
address initialOwner
) internal onlyInitializing {
__Ownable_init(initialOwner);
Expand Down
16 changes: 10 additions & 6 deletions contracts/validator-manager/PoSValidatorManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {IACP99SecurityModule} from "./interfaces/IACP99SecurityModule.sol";
import {IValidatorManager} from "./interfaces/IValidatorManager.sol";
import {ContextUpgradeable} from
"@openzeppelin/contracts-upgradeable@5.0.2/utils/ContextUpgradeable.sol";
import {InitializeValidatorRegistrationArgs, InitializeEndValidationArgs, Arguments} from "./Arguments.sol";

/**
* @dev Implementation of the {IPoSValidatorManager} interface.
Expand Down Expand Up @@ -135,6 +136,7 @@ abstract contract PoSValidatorManager is
{
__ReentrancyGuard_init();
__POS_Validator_Manager_init_unchained({
validatorManager: settings.validatorManager,
minimumStakeAmount: settings.minimumStakeAmount,
maximumStakeAmount: settings.maximumStakeAmount,
minimumStakeDuration: settings.minimumStakeDuration,
Expand All @@ -148,6 +150,7 @@ abstract contract PoSValidatorManager is

// solhint-disable-next-line func-name-mixedcase
function __POS_Validator_Manager_init_unchained(
IValidatorManager validatorManager,
uint256 minimumStakeAmount,
uint256 maximumStakeAmount,
uint64 minimumStakeDuration,
Expand All @@ -170,7 +173,7 @@ abstract contract PoSValidatorManager is
revert InvalidStakeMultiplier(maximumStakeMultiplier);
}
// Minimum stake duration should be at least one churn period in order to prevent churn tracker abuse.
if (minimumStakeDuration < $.validatorManager.getChurnPeriodSeconds()) {
if (minimumStakeDuration < validatorManager.getChurnPeriodSeconds()) {
revert InvalidMinStakeDuration(minimumStakeDuration);
}
if (weightToValueFactor == 0) {
Expand All @@ -180,6 +183,7 @@ abstract contract PoSValidatorManager is
revert InvalidUptimeBlockchainID(uptimeBlockchainID);
}

$.validatorManager = validatorManager;
$._minimumStakeAmount = minimumStakeAmount;
$._maximumStakeAmount = maximumStakeAmount;
$._minimumStakeDuration = minimumStakeDuration;
Expand All @@ -192,21 +196,21 @@ abstract contract PoSValidatorManager is

function handleInitializeValidatorRegistration(bytes32 validationID, uint64 weight, bytes calldata args) external {
uint256 stakeAmount = weightToValue(weight);
(uint16 delegationFeeBips, uint64 minStakeDuration) = abi.decode(args, (uint16, uint64));
_initializeValidatorRegistration(validationID, stakeAmount, delegationFeeBips, minStakeDuration);
InitializeValidatorRegistrationArgs memory decoded = abi.decode(args, (InitializeValidatorRegistrationArgs));
_initializeValidatorRegistration(validationID, stakeAmount, decoded.delegationFeeBips, decoded.minStakeDuration);
}

function handleCompleteValidatorRegistration(bytes32 validationID) external {
// No-op
}

function handleInitializeEndValidation(bytes32 validationID, bytes calldata args) external {
(bool force, bool includeUptimeProof, uint32 messageIndex, address rewardRecipient) = abi.decode(args, (bool, bool, uint32, address));
InitializeEndValidationArgs memory decoded = Arguments.decodeInitializeEndValidationArgs(args);

bool success = _initializeEndPoSValidation(
validationID, includeUptimeProof, messageIndex, rewardRecipient
validationID, decoded.includeUptimeProof, decoded.messageIndex, decoded.rewardRecipient
);
if (force) {
if (decoded.force) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

pragma solidity 0.8.25;

import {IValidatorManager, ValidatorManagerSettings} from "./IValidatorManager.sol";
import {IValidatorManager} from "./IValidatorManager.sol";
import {IRewardCalculator} from "./IRewardCalculator.sol";

/**
Expand All @@ -20,7 +20,6 @@ enum DelegatorStatus {

/**
* @notice PoS Validator Manager settings, used to initialize the PoS Validator Manager
* @notice baseSettings specified the base settings for the Validator Manager. See {IValidatorManager-ValidatorManagerSettings}
* @notice minimumStakeAmount is the minimum amount of stake required to stake to a validator
* @notice maximumStakeAmount is the maximum amount of stake that can be staked to a validator
* @notice minimumStakeDuration is the minimum duration that validators must stake for
Expand All @@ -33,7 +32,7 @@ enum DelegatorStatus {
* This must be a blockchain validated by the subnetID that this contract manages.
*/
struct PoSValidatorManagerSettings {
ValidatorManagerSettings baseSettings;
IValidatorManager validatorManager;
uint256 minimumStakeAmount;
uint256 maximumStakeAmount;
uint64 minimumStakeDuration;
Expand Down
2 changes: 1 addition & 1 deletion scripts/abi_bindings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ echo "ARCH set to $ARCH"

DEFAULT_CONTRACT_LIST="TeleporterMessenger TeleporterRegistry ExampleERC20 ExampleRewardCalculator TestMessenger ValidatorSetSig NativeTokenStakingManager ERC20TokenStakingManager PoAValidatorManager
TokenHome TokenRemote ERC20TokenHome ERC20TokenHomeUpgradeable ERC20TokenRemote ERC20TokenRemoteUpgradeable NativeTokenHome NativeTokenHomeUpgradeable NativeTokenRemote NativeTokenRemoteUpgradeable
WrappedNativeToken MockERC20SendAndCallReceiver MockNativeSendAndCallReceiver ExampleERC20Decimals IValidatorManager IPoSValidatorManager"
WrappedNativeToken MockERC20SendAndCallReceiver MockNativeSendAndCallReceiver ExampleERC20Decimals IValidatorManager IPoSValidatorManager IACP99ValidatorManager ACP99ValidatorManager IACP99SecurityModule ArgumentsExternal"
PROXY_LIST="TransparentUpgradeableProxy ProxyAdmin"

SUBNET_EVM_LIST="INativeMinter"
Expand Down
Loading