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 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

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.

115 changes: 115 additions & 0 deletions contracts/validator-manager/ACP99ValidatorManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// SPDX-License-Identifier: Ecosystem

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 {
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 {
_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(
ConversionData calldata conversionData,
uint32 messageIndex
) external {
_initializeValidatorSet(conversionData, messageIndex);
}

function initializeValidatorRegistration(
ValidatorRegistrationInput calldata input,
uint64 weight,
bytes calldata args
) external returns (bytes32){
bytes32 validationID = _initializeValidatorRegistration(input, weight);
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, _msgSender(), weight, args);
return validationID;
}

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

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

function completeEndValidation(uint32 messageIndex) external{
bytes32 validationID = _completeEndValidation(messageIndex);
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);
_getACP99ValidatorManagerStorage().securityModule.handleInitializeValidatorWeightChange(validationID, weight, nonce, args);
}

function completeValidatorWeightChange(bytes32 validationID, bytes calldata args) external{
_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));
}
}
34 changes: 4 additions & 30 deletions contracts/validator-manager/ERC20TokenStakingManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,22 @@ pragma solidity 0.8.25;

import {PoSValidatorManager} from "./PoSValidatorManager.sol";
import {PoSValidatorManagerSettings} from "./interfaces/IPoSValidatorManager.sol";
import {ValidatorRegistrationInput} from "./interfaces/IValidatorManager.sol";
import {IERC20TokenStakingManager} from "./interfaces/IERC20TokenStakingManager.sol";
import {IERC20Mintable} from "./interfaces/IERC20Mintable.sol";
import {ICMInitializable} from "@utilities/ICMInitializable.sol";
import {SafeERC20TransferFrom} from "@utilities/SafeERC20TransferFrom.sol";
import {Initializable} from
"@openzeppelin/[email protected]/proxy/utils/Initializable.sol";
import {SafeERC20} from "@openzeppelin/[email protected]/token/ERC20/utils/SafeERC20.sol";


/**
* @dev Implementation of the {IERC20TokenStakingManager} interface.
*
* @custom:security-contact https://github.com/ava-labs/teleporter/blob/main/SECURITY.md
*/
contract ERC20TokenStakingManager is
Initializable,
PoSValidatorManager,
IERC20TokenStakingManager
PoSValidatorManager
{
using SafeERC20 for IERC20Mintable;
using SafeERC20TransferFrom for IERC20Mintable;
Expand Down Expand Up @@ -95,30 +93,6 @@ contract ERC20TokenStakingManager is
$._token = token;
}

/**
* @notice See {IERC20TokenStakingManager-initializeValidatorRegistration}
*/
function initializeValidatorRegistration(
ValidatorRegistrationInput calldata registrationInput,
uint16 delegationFeeBips,
uint64 minStakeDuration,
uint256 stakeAmount
) external nonReentrant returns (bytes32 validationID) {
return _initializeValidatorRegistration(
registrationInput, delegationFeeBips, minStakeDuration, stakeAmount
);
}

/**
* @notice See {IERC20TokenStakingManager-initializeDelegatorRegistration}
*/
function initializeDelegatorRegistration(
bytes32 validationID,
uint256 delegationAmount
) external nonReentrant returns (bytes32) {
return _initializeDelegatorRegistration(validationID, _msgSender(), delegationAmount);
}

/**
* @notice Returns the ERC20 token being staked
*/
Expand All @@ -130,8 +104,8 @@ contract ERC20TokenStakingManager is
* @notice See {PoSValidatorManager-_lock}
* Note: Must be guarded with reentrancy guard for safe transfer from.
*/
function _lock(uint256 value) internal virtual override returns (uint256) {
return _getERC20StakingManagerStorage()._token.safeTransferFrom(value);
function _lock(address sender, uint256 value) internal virtual override returns (uint256) {
return _getERC20StakingManagerStorage()._token.safeTransferFrom(sender, value);
}

/**
Expand Down
32 changes: 2 additions & 30 deletions contracts/validator-manager/NativeTokenStakingManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ pragma solidity 0.8.25;

import {PoSValidatorManager} from "./PoSValidatorManager.sol";
import {PoSValidatorManagerSettings} from "./interfaces/IPoSValidatorManager.sol";
import {ValidatorRegistrationInput} from "./interfaces/IValidatorManager.sol";
import {INativeTokenStakingManager} from "./interfaces/INativeTokenStakingManager.sol";
import {INativeMinter} from
"@avalabs/[email protected]/contracts/interfaces/INativeMinter.sol";
import {ICMInitializable} from "@utilities/ICMInitializable.sol";
Expand All @@ -23,8 +21,7 @@ import {Initializable} from
*/
contract NativeTokenStakingManager is
Initializable,
PoSValidatorManager,
INativeTokenStakingManager
PoSValidatorManager
{
using Address for address payable;

Expand Down Expand Up @@ -58,35 +55,10 @@ contract NativeTokenStakingManager is
// solhint-disable-next-line func-name-mixedcase, no-empty-blocks
function __NativeTokenStakingManager_init_unchained() internal onlyInitializing {}

/**
* @notice See {INativeTokenStakingManager-initializeValidatorRegistration}.
*/
function initializeValidatorRegistration(
ValidatorRegistrationInput calldata registrationInput,
uint16 delegationFeeBips,
uint64 minStakeDuration
) external payable nonReentrant returns (bytes32) {
return _initializeValidatorRegistration(
registrationInput, delegationFeeBips, minStakeDuration, msg.value
);
}

/**
* @notice See {INativeTokenStakingManager-initializeDelegatorRegistration}.
*/
function initializeDelegatorRegistration(bytes32 validationID)
external
payable
nonReentrant
returns (bytes32)
{
return _initializeDelegatorRegistration(validationID, _msgSender(), msg.value);
}

/**
* @notice See {PoSValidatorManager-_lock}
*/
function _lock(uint256 value) internal virtual override returns (uint256) {
function _lock(address, uint256 value) internal virtual override returns (uint256) {
return value;
}

Expand Down
Loading