Try   HackMD

EVM Script Factories For Simple DVT Module

The current doc describes the implementation details of EasyTrack EVM factories to manage an instance of the Simple DVT NodeOperatorsRegistry. The rights to manage node operators (clusters) in the Simple DVT will be granted to the Simple DVT committee represented by the gnosis multisig. The high-view overview of the management flow is presented in the below diagram.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Before reading the current document, please check out the high-level Simple DVT module management flow.

For consistency, throughout the document, the term node operator(s) is used, but by this, we also mean cluster(s).

Validator Keys Management

The management of the validator keys in the instance of the NodeOperatorsRegistry used as a curated staking module happens from the reward address. To add or remove the validator's keys, the node operator sends the transaction from the reward address.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Unfortunately, such an approach doesn't apply to the Simple DVT instance because node operators use a dedicated splitter contract as a reward address, which distributes rewards among the cluster participants and doesn't contain logic to manage keys in the NodeOperatorsRegistry.

Besides the reward address, rights to add and remove keys might be granted using special MANAGE_SIGNING_KEYS permission.
This permission may be parametrized with the ID of the node operator, which may be used to implement unique access to certain node operators from the specific manager address.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Aragon's ACL contract provides ample opportunities to implement custom checks using the permissions params, but non-trivial logic may overcomplicate the final solution. To preserve the checks simple, the following constraints apply for manager addresses:

  • Each node operator MUST have a unique manager
  • The same manager address CAN NOT be used for different node operators

With an introduction of the above invariants, the granular rights might be implemented using only one additional parameter:

  • Param(0, uint8(Op.EQ), managedNodeOperatorId)

During the check of MANAGE_SIGNING_KEYS permission with the above permission, the ACL contract validates that the id of the node operator passed via arguments is equal to managedNodeOperatorId, set on the permission granting step.

Rights & Permissions

To operate properly, the introducing EVM script factories require the following ACL permissions granted to EasyTrack's EvmScriptExecutor contract on the instance of the NodeOperatorsRegistry for Simple DVT:

Permission EVM Script Factory Method
STAKING_ROUTER_ROLE UpdateTargetValidatorLimits updateTargetValidatorsLimits()
MANAGE_NODE_OPERATOR_ROLE AddNodeOperators addNodeOperator()
MANAGE_NODE_OPERATOR_ROLE ActivateNodeOperators activateNodeOperator()
MANAGE_NODE_OPERATOR_ROLE DeactivateNodeOperators deactivateNodeOperator()
MANAGE_NODE_OPERATOR_ROLE SetNodeOperatorNames setNodeOperatorName()
MANAGE_NODE_OPERATOR_ROLE SetNodeOperatorRewardAddresses setNodeOperatorRewardAddress()
SET_NODE_OPERATOR_LIMIT_ROLE SetVettedValidatorsLimit setNodeOperatorStakingLimit()
SET_NODE_OPERATOR_LIMIT_ROLE IncreaseVettedValidatorsLimit setNodeOperatorStakingLimit()

Besides the granted permissions, the EvmScriptExecutor contract MUST BE assigned as a unique manager of the MANAGE_SIGNING_KEYS permission on the Simple DVT NodeOperatorsRegistry instance. It's necessary to allow granting and revoking a parametrized version of the MANAGE_SIGNING_KEYS permissions to the node operator's manager when adding, activating, and deactivating the node operator.

The ACL contract allows setting only one address as the manager for the permission. It means that manager rights of granting and revoking the MANAGE_SIGNING_KEYS permission will be exclusively delegated to EasyTrack's EvmScriptExecutor contract. In case the DAO needs to make some operation with this role on the side of the Voting contract, this role may be taken back, temporarily swapping the easyTrack address in the EvmScriptExecutor contract and executing EVM script, which calls ACL.removePermissionManager() or ACL.setPermissionManager() method. An example of such voting script creation might be found here: https://github.com/lidofinance/easy-track/blob/98afb4a12755d39a1856c5d111accef36c0688d0/tests/scenario/test_dvt_signing_keys_role.py#L149

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

AddNodeOperators

Allows creation of EVM script to add a set of node operators to the Simple DVT's instance of the NodeOperatorsRegistry and grant parametrized MANAGE_SIGNING_KEYS permission to a node operator's manager address.

Restrictions

The current implementation DOES NOT allow to addition of multiple batches of node operators in parallel motions. If two AddNodeOperators motions are started simultaneously, only the first executed will be applied successfully, and the second one will fail on the execution attempt.

Data Types

To simplify the process of preparing data for motion creation, the input data for each newly added node operator is organized in the struct AddNodeOperatorInput:

struct AddNodeOperatorInput {
    // the name of the newly added node operator
    string name;

    // the reward address of the newly added node operator
    address rewardAddress;
    
    // address to assign a parametrized version of the MANAGE_SIGNING_KEYS permission for the newly added node operator
    address managerAddress;
}

Immutable Variables

  • IACL acl - address of the Lido's instance of the Aragon's ACL contract
  • INodeOperatorsRegistry registry - address of the instance of the Simple DVT NodeOperatorsRegistry

Methods

createEVMScript(address,bytes)

  • Arguments:
    • address _creator - the creator of the EVM Script
    • bytes memory _evmScriptCallData - an ABI encoded tuple (uint256 nodeOperatorsCount, AddNodeOperatorInput[] nodeOperators)
  • Visibility: external
  • Mutability: view
  • Modifiers: onlyTrustedCaller(_creator)
  • Returns: (bytes memory)

Creates an EVM script to add a list of node operators to the instance of NodeOperatorsRegistry, and for every added node operator, grants parametrized MANAGE_SIGNING_KEYS permission to the provided manager address.

Additionally to the array of AddNodeOperatorInput instances, the encoded calldata includes the total number of node operators in the registry (nodeOperatorsCount). This field is required to check that the manager's rights will be granted to the correct pair of the manager and node operator.

To grant the MANAGE_SIGNING_KEYS permission to the manager address, the id of the added node operator must be known at the moment of the creation of the motion. The current implementation of the NodeOperatorsRegistry allows prediction of the id of the new node operator (https://github.com/lidofinance/lido-dao/blob/cadffa46a2b8ed6cfa1127fca2468bae1a82d6bf/contracts/0.4.24/nos/NodeOperatorsRegistry.sol#L292), but as each motion executed with a delay, there is a chance that after the creation of the motion, but before its execution, will be added another node operator and the precalculated ids of the node operators used on motion creation become invalid. To prevent the enactment of such motions, the _nodeOperatorsCount argument is added to the calldata of the factory. This parameter is then used to validate that the onchain state of the NodeOperatorsRegistry hasn't changed from the moment of the creation of the motion.

To successfully create and enact motion, the following onchain requirements must be met:

  • The current number of node operators in the registry MUST be equal to the _nodeOperatorsCount
  • The total number of node operators in the registry, after adding the new ones, MUST NOT exceed nodeOperatorsRegistry.MAX_NODE_OPERATORS_COUNT()
  • Manager addresses MUST NOT have duplicates
  • Manager addresses MUST NOT be used as managers for previously added node operators
  • Reward addresses of newly added node operators MUST NOT contain the address of the stETH token
  • Reward addresses of newly added node operators MUST NOT contain zero addresses
  • The names of newly added node operators MUST NOT be an empty string
  • The name lengths of each newly added node operator MUST NOT exceed the nodeOperatorsRegistry.MAX_NODE_OPERATOR_NAME_LENGTH()

decodeEVMScriptCallData(bytes)

  • Arguments:
    • bytes memory _evmScriptCallData - an ABI encoded tuple (uint256, ActivateNodeOperatorInput[])
  • Visibility: external
  • Mutability: pure
  • Returns: (uint256, ActivateNodeOperatorInput[] memory)

Decodes _evmScriptCallData into tuple (uint256 nodeOperatorsCount, AddNodeOperatorInput[] nodeOperators).

DeactivateNodeOperators

Allows creation of EVM script to deactivate and revoke the MANAGE_SIGNING_KEYS permission from the manager address for the set of node operators.

The MANANGE_SIGNING_KEYS permission revoking is needed to prevent uploading and removing validator keys by the managers of the deactivated node operators (See the implementation of the NodeOperatorRegistry._onlyNodeOperatorManager()).

Data Types

The input data for each node operator is organized in the following struct:

struct DectivateNodeOperatorInput {
    // id of the node operator to deactivate
    uint256 nodeOperatorId;

    // address of the node operator manager with granted parameterized version of MANAGE_SIGNING_KEYS permission
    address managerAddress;
}

The current implementation of the ACL contract doesn't allow receiving all holders of the particular permission onchain, so the current address of the manager of the node operator is passed explicitly.

Immutable Variables

  • IACL acl - address of the Lido's instance of the Aragon's ACL contract
  • INodeOperatorsRegistry registry - address of the instance of the Simple DVT NodeOperatorsRegistry

Methods

createEVMScript(address,bytes)

  • Arguments:
    • address _creator - the creator of the EVM Script
    • bytes memory _evmScriptCallData - an ABI encoded tuple (DectivateNodeOperatorInput[])
  • Visibility: external
  • Mutability: view
  • Modifiers: onlyTrustedCaller(_creator)
  • Returns: (bytes memory)

Creates an EVM script to deactivate and revoke a parameterized version of the MANAGER_SIGNING_KEYS permission from the manager address for the set of node operators.

To successfully create and enact motion, the following onchain requirements must be met:

  • The list of DeactivateNodeOperatorInput MUST be sorted in strictly ascending order by the nodeOperatorId key (duplicates are not allowed)
  • Each node operator in the set MUST be registered in the registry
  • Each node operator in the set MUST be in the activated state
  • Each manager address in the set MUST be granted with a parametrized version of MANAGE_SIGNING_KEYS permission for the corresponding node operator

decodeEVMScriptCallData(bytes)

  • Arguments:
    • bytes memory _evmScriptCallData - an ABI encoded tuple (DeactivateNodeOperatorInput[])
  • Visibility: external
  • Mutability: pure
  • Returns: (DeactivateNodeOperatorInput[] memory)

Decodes _evmScriptCallData into tuple (DeactivateNodeOperatorInput[]).

ActivateNodeOperators

Allows creation of EVM script to activate and set manager address for the set of previously deactivated node operators.

Data Types

The input data for each activated node operator is organized in the following struct:

struct ActivateNodeOperatorInput {
    // id of the node operator to activate
    uint256 nodeOperatorId;

    // address to assign a parametrized version of the MANAGE_SIGNING_KEYS permission for the activated node operator
    address managerAddress;
}

The managerAddress is required because the current implementation of the DeactivateNodeOperators EVM Script factory revokes this permission from the manager address.

Immutable Variables

  • IACL immutable acl - address of the Lido's instance of the Aragon's ACL contract
  • INodeOperatorsRegistry immutable registry - address of the instance of the Simple DVT NodeOperatorsRegistry

Methods

createEVMScript(address,bytes)

  • Arguments:
    • address _creator - the creator of the EVM Script
    • bytes memory _evmScriptCallData - an ABI encoded tuple (ActivateNodeOperatorInput[])
  • Visibility: external
  • Mutability: view
  • Modifiers: onlyTrustedCaller(_creator)
  • Returns: (bytes memory)

Creates an EVM script to activate and grant a parameterized version of the MANAGER_SIGNING_KEYS permission to the manager address for the set of node operators.

To successfully create and enact motion, the following onchain requirements must be met:

  • The list of ActivateNodeOperatorInput MUST be sorted in strictly ascending order by the nodeOperatorId key (duplicates are not allowed)
  • Each node operator in the set MUST be registered in the registry
  • Each node operator in the set MUST be in the deactivated state
  • Each manager address in the set MUST NOT be granted with MANAGE_SIGNING_KEYS permission

decodeEVMScriptCallData(bytes)

  • Arguments:
    • bytes memory _evmScriptCallData - an ABI encoded tuple (ActivateNodeOperatorInput[])
  • Visibility: external
  • Mutability: pure
  • Returns: (ActivateNodeOperatorInput[] memory)

Decodes _evmScriptCallData into tuple (ActivateNodeOperatorInput[]).

SetNodeOperatorNames

Allows creation of EVM script to set the name for the set of the node operators.

Data Types

The input data for each change of the node operator's name is organized in the following struct:

struct SetNameInput {
    // id of the node operator to set new name
    uint256 nodeOperatorId;
    
    // new name of the node operator
    string name;
}

Immutable Variables

  • INodeOperatorsRegistry immutable registry - address of the instance of the Simple DVT NodeOperatorsRegistry

Methods

createEVMScript(address,bytes)

  • Arguments:
    • address _creator - the creator of the EVM Script
    • bytes memory _evmScriptCallData - an ABI encoded tuple (SetNameInput[])
  • Visibility: external
  • Mutability: view
  • Modifiers: onlyTrustedCaller(_creator)
  • Returns: (bytes memory)

Returns EVM script to set the name of the node operators with the given IDs.

To successfully create and enact motion, the following onchain requirements must be met:

  • The list of SetNameInput MUST be sorted in strictly ascending order by the nodeOperatorId key (duplicates are not allowed)
  • Each node operator MUST be registered in the registry contract
  • The new names of the node operators MUST differ from the current names
  • The new names of the node operators MUST NOT contain empty strings
  • The lengths of the new names of the node operators don't exceed the NodeOperatorsRegistry.MAX_NODE_OPERATOR_NAME_LENGTH()

decodeEVMScriptCallData(bytes)

  • Arguments:
    • bytes memory _evmScriptCallData - an ABI encoded tuple (SetNameInput[])
  • Visibility: external
  • Mutability: pure
  • Returns: (SetNameInput[] memory)

Decodes _evmScriptCallData into tuple (SetNameInput[]).

SetNodeOperatorRewardAddresses

Allows creation of EVM script to set the reward addresses for the set of the node operators.

Data Types

The input data for each reward address change is organized in the following struct:

struct SetRewardAddressInput {
    // id of the node operator to set new reward address
    uint256 nodeOperatorId;
    
    // new reward address of the node operator 
    address rewardAddress;
}

Immutable Variables

  • INodeOperatorsRegistry immutable registry - address of the instance of the Simple DVT NodeOperatorsRegistry

Methods

createEVMScript(address,bytes)

  • Arguments:
    • address _creator - the creator of the EVM Script
    • bytes memory _evmScriptCallData - an ABI encoded tuple (SetRewardAddressInput[])
  • Visibility: external
  • Mutability: view
  • Modifiers: onlyTrustedCaller(_creator)
  • Returns: (bytes memory)

Returns EVM script to set the reward address of the node operators with the given ids.

To successfully create and enact motion, the following onchain requirements must be met:

  • The list of SetRewardAddressInput MUST be sorted in strictly ascending order by the nodeOperatorId key (duplicates are not allowed)
  • Each node operator in the set MUST be registered in the registry contract
  • Each new reward address MUST NOT be equal to the current reward address of the node operator
  • Each new reward address MUST NOT be equal to zero address
  • Each new reward address MUST NOT be equal to the address of the stETH token

decodeEVMScriptCallData(bytes)

  • Arguments:
    • bytes memory _evmScriptCallData - an ABI encoded tuple (SetRewardAddressInput[])
  • Visibility: external
  • Mutability: pure
  • Returns: (SetRewardAddressInput[] memory)

Decodes _evmScriptCallData into tuple (SetRewardAddressInput[]).

UpdateTargetValidatorLimits

Allows creation of EVM script to update the target validators limits for the set of node operators.

Data Types

The input data for each target validators limit is organized in the following struct:

struct TargetValidatorsLimitInput {
    // id of the node operator to set target validators limit
    uint256 nodeOperatorId;
    
    // whether the target limit is active for the node operator or not
    bool isTargetLimitActive;
    
    // the value of the target limit to set
    uint256 targetLimit;
}

Immutable Variables

  • INodeOperatorsRegistry immutable registry - address of the instance of the Simple DVT NodeOperatorsRegistry

Methods

createEVMScript(address,bytes)

  • Arguments:
    • address _creator - the creator of the EVM Script
    • bytes memory _evmScriptCallData - an ABI encoded tuple (TargetValidatorsLimit[])
  • Visibility: external
  • Mutability: view
  • Modifiers: onlyTrustedCaller(_creator)
  • Returns: (bytes memory)

Returns EVM script to set the target validator limits for the set of node operators.

To successfully create and enact motion the next onchain requirements must be met:

  • The list of TargetValidatorsLimit MUST be sorted in strictly ascending order by the nodeOperatorId key (duplicates are not allowed)
  • Each node operator in the set MUST be registered in the registry contract
  • The new value of the validator's target limit MUST NOT exceed the UINT64_MAX value

decodeEVMScriptCallData(bytes)

  • Arguments:
    • bytes memory _evmScriptCallData - an ABI encoded tuple (TargetValidatorsLimit[])
  • Visibility: external
  • Mutability: pure
  • Returns: (TargetValidatorsLimit[] memory)

Decodes _evmScriptCallData into tuple (TargetValidatorsLimit[]).

ChangeNodeOperatorManagers

Allows creation of EVM Script to revoke the parametrized version of the MANAGE_SIGNING_KEYS permission from the current manager address and grant it to the new one for the set of node operators.

Restrictions

The current design of the permissions system forbids using the same manager address for different node operators. In other words, to successfully transfer the manager permission to another account via the SetNodeOperatorManager motion, the new address of the manager MUST NOT be used as a manager in other node operators of the registry.

Data Types

The input data for each manager changer is organized in the following struct:

struct PermissionInput {
    // id of the node operator to change the manager address for
    uint256 nodeOperatorId;
    
    // address of the current manager of the node operator
    address oldManagerAddress;
    
    // address of the new manager of the node operator
    address newManagerAddress;
}

Immutable Variables

  • IACL immutable acl - address of the Lido's instance of the Aragon's ACL contract
  • INodeOperatorsRegistry immutable registry - address of the instance of the Simple DVT NodeOperatorsRegistry

Methods

createEVMScript(address,bytes)

  • Arguments:
    • address _creator - the creator of the EVM Script
    • bytes memory _evmScriptCallData - an ABI encoded tuple (PermissionInput[])
  • Visibility: external
  • Mutability: view
  • Modifiers: onlyTrustedCaller(_creator)
  • Returns: (bytes memory)

Creates EVM script to update the manager of the node operators with the given ids. The resulting EVM script revokes MANAGE_SIGNING_KEYS permission from the oldManagerAddress and grants it to the newManagerAddress.

To successfully create and enact motion, the following onchain requirements must be met:

  • The list of PermissionInput MUST be sorted in strictly ascending order by the nodeOperatorId key (duplicates are not allowed)
  • Each node operator in the set MUST be registered in the registry contract
  • Manager addresses MUST NOT have duplicates
  • oldManagerAddress and newManagerAddress MUST NOT be equal to zero address
  • oldManagerAddress MUST have the parametrized MANAGE_SIGNING_KEYS permission granted
  • newManagerAddress MUST NOT have the parametrized MANAGE_SIGNING_KEYS permission granted

decodeEVMScriptCallData(bytes)

  • Arguments:
    • bytes memory _evmScriptCallData - an ABI encoded tuple (PermissionInput[])
  • Visibility: external
  • Mutability: pure
  • Returns: (PermissionInput[] memory)

Decodes _evmScriptCallData into tuple (PermissionInput[]).

SetVettedValidatorsLimits

Allows creation of EVM script to set the vetted validators limits for the set of node operators.

Data Types

The input data for each vetted validators limit is organized in the following struct:

struct VettedValidatorsLimit {
    // id of the node operator to set the limit of the vetted validators
    uint256 nodeOperatorId;
    
    // the new limit of the vetted validators for the node operator
    uint256 vettedValidatorsLimit;
}

Immutable Variables

  • INodeOperatorsRegistry immutable registry - address of the instance of the Simple DVT NodeOperatorsRegistry

Methods

createEVMScript(address,bytes)

  • Arguments:
    • address _creator - the creator of the EVM Script
    • bytes memory _evmScriptCallData - an ABI encoded tuple (VettedValidatorsLimit[])
  • Visibility: external
  • Mutability: view
  • Modifiers: onlyTrustedCaller(_creator)
  • Returns: (bytes memory)

Returns EVM script to set the vetted validators limits for the set of node operators.

To successfully create EVMScript, the following requirements must be met:

  • The list of VettedValidatorsLimit MUST be sorted in strictly ascending order by the nodeOperatorId key (duplicates are not allowed)
  • Each Node Operator MUST be registered in the registry.
  • Each value of the vetted validators limit MUST be less or equal to the number of the total validators of the node operator.
  • Each Node Operator MUST be in an active state.

decodeEVMScriptCallData(bytes)

  • Arguments:
    • bytes memory _evmScriptCallData - an ABI encoded tuple (VettedValidatorsLimit[])
  • Visibility: external
  • Mutability: pure
  • Returns: (VettedValidatorsLimit[] memory)

Decodes _evmScriptCallData into tuple (VettedValidatorsLimit[]).

IncreaseVettedValidatorsLimit

Allows creation of EVMScript to increase the vetted validators limit for node operator with the given id.

Immutable Variables

  • IACL immutable acl - address of the Lido's instance of the Aragon's ACL contract
  • INodeOperatorsRegistry immutable registry - address of the instance of the Simple DVT NodeOperatorsRegistry

Methods

createEVMScript(address,bytes)

  • Arguments:
    • address _creator - the creator of the EVM Script
    • bytes memory _evmScriptCallData - an ABI encoded tuple (uint256 _nodeOperatorId, uint256 _stakingLimit), where
      • _nodeOperatorId - id of node operator to increase the limit for,
      • _stakingLimit - new staking limit
  • Visibility: external
  • Mutability: view
  • Modifiers: onlyTrustedCaller(_creator)
  • Returns: (bytes memory)

Returns EVMScript to increase the vetted validators limit of the node operator with the given ID.

To successfully create an EVM script following requirements must be met:

  • Node operator with the given id MUST be registered in the registry contract
  • Reward address of the node operator MUST be equal to the address of the _creator or _creator MUST be set as manager of the node operator
  • Node Operator MUST be activated.
  • The new staking limit MUST be greater than the current staking limit
  • The new staking limit MUST be less or equal to the total number of signing keys

decodeEVMScriptCallData(bytes)

  • Arguments:
    • bytes memory _evmScriptCallData - an ABI encoded tuple (uint256 _nodeOperatorId, uint256 _stakingLimit)
  • Visibility: external
  • Mutability: pure
  • Returns: (VettedValidatorsLimit[] memory)

Decodes _evmScriptCallData into tuple (uint256 _nodeOperatorId, uint256 _stakingLimit).