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.
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).
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.
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.
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:
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.
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'sEvmScriptExecutor
contract. In case the DAO needs to make some operation with this role on the side of theVoting
contract, this role may be taken back, temporarily swapping theeasyTrack
address in theEvmScriptExecutor
contract and executing EVM script, which callsACL.removePermissionManager()
orACL.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
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.
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.
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
:
IACL acl
- address of the Lido's instance of the Aragon's ACL
contractINodeOperatorsRegistry registry
- address of the instance of the Simple DVT NodeOperatorsRegistry
address _creator
- the creator of the EVM Scriptbytes memory _evmScriptCallData
- an ABI encoded tuple (uint256 nodeOperatorsCount, AddNodeOperatorInput[] nodeOperators)
external
view
(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 theNodeOperatorsRegistry
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 theNodeOperatorsRegistry
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:
_nodeOperatorsCount
nodeOperatorsRegistry.MAX_NODE_OPERATORS_COUNT()
nodeOperatorsRegistry.MAX_NODE_OPERATOR_NAME_LENGTH()
bytes memory _evmScriptCallData
- an ABI encoded tuple (uint256, ActivateNodeOperatorInput[])
external
pure
(uint256, ActivateNodeOperatorInput[] memory)
Decodes _evmScriptCallData
into tuple (uint256 nodeOperatorsCount, AddNodeOperatorInput[] nodeOperators)
.
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 theNodeOperatorRegistry._onlyNodeOperatorManager()
).
The input data for each node operator is organized in the following struct:
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.
IACL acl
- address of the Lido's instance of the Aragon's ACL
contractINodeOperatorsRegistry registry
- address of the instance of the Simple DVT NodeOperatorsRegistry
address _creator
- the creator of the EVM Scriptbytes memory _evmScriptCallData
- an ABI encoded tuple (DectivateNodeOperatorInput[])
external
view
(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:
DeactivateNodeOperatorInput
MUST be sorted in strictly ascending order by the nodeOperatorId
key (duplicates are not allowed)MANAGE_SIGNING_KEYS
permission for the corresponding node operatorbytes memory _evmScriptCallData
- an ABI encoded tuple (DeactivateNodeOperatorInput[])
external
pure
(DeactivateNodeOperatorInput[] memory)
Decodes _evmScriptCallData
into tuple (DeactivateNodeOperatorInput[])
.
Allows creation of EVM script to activate and set manager address for the set of previously deactivated node operators.
The input data for each activated node operator is organized in the following struct:
The
managerAddress
is required because the current implementation of the DeactivateNodeOperators EVM Script factory revokes this permission from the manager address.
IACL immutable acl
- address of the Lido's instance of the Aragon's ACL
contractINodeOperatorsRegistry immutable registry
- address of the instance of the Simple DVT NodeOperatorsRegistry
address _creator
- the creator of the EVM Scriptbytes memory _evmScriptCallData
- an ABI encoded tuple (ActivateNodeOperatorInput[])
external
view
(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:
ActivateNodeOperatorInput
MUST be sorted in strictly ascending order by the nodeOperatorId
key (duplicates are not allowed)MANAGE_SIGNING_KEYS
permissionbytes memory _evmScriptCallData
- an ABI encoded tuple (ActivateNodeOperatorInput[])
external
pure
(ActivateNodeOperatorInput[] memory)
Decodes _evmScriptCallData
into tuple (ActivateNodeOperatorInput[])
.
Allows creation of EVM script to set the name for the set of the node operators.
The input data for each change of the node operator's name is organized in the following struct:
INodeOperatorsRegistry immutable registry
- address of the instance of the Simple DVT NodeOperatorsRegistry
address _creator
- the creator of the EVM Scriptbytes memory _evmScriptCallData
- an ABI encoded tuple (SetNameInput[])
external
view
(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:
SetNameInput
MUST be sorted in strictly ascending order by the nodeOperatorId
key (duplicates are not allowed)NodeOperatorsRegistry.MAX_NODE_OPERATOR_NAME_LENGTH()
bytes memory _evmScriptCallData
- an ABI encoded tuple (SetNameInput[])
external
pure
(SetNameInput[] memory)
Decodes _evmScriptCallData
into tuple (SetNameInput[])
.
Allows creation of EVM script to set the reward addresses for the set of the node operators.
The input data for each reward address change is organized in the following struct:
INodeOperatorsRegistry immutable registry
- address of the instance of the Simple DVT NodeOperatorsRegistry
address _creator
- the creator of the EVM Scriptbytes memory _evmScriptCallData
- an ABI encoded tuple (SetRewardAddressInput[])
external
view
(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:
SetRewardAddressInput
MUST be sorted in strictly ascending order by the nodeOperatorId
key (duplicates are not allowed)bytes memory _evmScriptCallData
- an ABI encoded tuple (SetRewardAddressInput[])
external
pure
(SetRewardAddressInput[] memory)
Decodes _evmScriptCallData
into tuple (SetRewardAddressInput[])
.
Allows creation of EVM script to update the target validators limits for the set of node operators.
The input data for each target validators limit is organized in the following struct:
INodeOperatorsRegistry immutable registry
- address of the instance of the Simple DVT NodeOperatorsRegistry
address _creator
- the creator of the EVM Scriptbytes memory _evmScriptCallData
- an ABI encoded tuple (TargetValidatorsLimit[])
external
view
(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:
TargetValidatorsLimit
MUST be sorted in strictly ascending order by the nodeOperatorId
key (duplicates are not allowed)UINT64_MAX
valuebytes memory _evmScriptCallData
- an ABI encoded tuple (TargetValidatorsLimit[])
external
pure
(TargetValidatorsLimit[] memory)
Decodes _evmScriptCallData
into tuple (TargetValidatorsLimit[])
.
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.
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.
The input data for each manager changer is organized in the following struct:
IACL immutable acl
- address of the Lido's instance of the Aragon's ACL
contractINodeOperatorsRegistry immutable registry
- address of the instance of the Simple DVT NodeOperatorsRegistry
address _creator
- the creator of the EVM Scriptbytes memory _evmScriptCallData
- an ABI encoded tuple (PermissionInput[])
external
view
(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:
PermissionInput
MUST be sorted in strictly ascending order by the nodeOperatorId
key (duplicates are not allowed)oldManagerAddress
and newManagerAddress
MUST NOT be equal to zero addressoldManagerAddress
MUST have the parametrized MANAGE_SIGNING_KEYS
permission grantednewManagerAddress
MUST NOT have the parametrized MANAGE_SIGNING_KEYS
permission grantedbytes memory _evmScriptCallData
- an ABI encoded tuple (PermissionInput[])
external
pure
(PermissionInput[] memory)
Decodes _evmScriptCallData
into tuple (PermissionInput[])
.
Allows creation of EVM script to set the vetted validators limits for the set of node operators.
The input data for each vetted validators limit is organized in the following struct:
INodeOperatorsRegistry immutable registry
- address of the instance of the Simple DVT NodeOperatorsRegistry
address _creator
- the creator of the EVM Scriptbytes memory _evmScriptCallData
- an ABI encoded tuple (VettedValidatorsLimit[])
external
view
(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:
VettedValidatorsLimit
MUST be sorted in strictly ascending order by the nodeOperatorId
key (duplicates are not allowed)bytes memory _evmScriptCallData
- an ABI encoded tuple (VettedValidatorsLimit[])
external
pure
(VettedValidatorsLimit[] memory)
Decodes _evmScriptCallData
into tuple (VettedValidatorsLimit[])
.
Allows creation of EVMScript to increase the vetted validators limit for node operator with the given id.
IACL immutable acl
- address of the Lido's instance of the Aragon's ACL
contractINodeOperatorsRegistry immutable registry
- address of the instance of the Simple DVT NodeOperatorsRegistry
address _creator
- the creator of the EVM Scriptbytes memory _evmScriptCallData
- an ABI encoded tuple (uint256 _nodeOperatorId, uint256 _stakingLimit)
, where
_nodeOperatorId
- id of node operator to increase the limit for,_stakingLimit
- new staking limitexternal
view
(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:
_creator
or _creator
MUST be set as manager of the node operatorbytes memory _evmScriptCallData
- an ABI encoded tuple (uint256 _nodeOperatorId, uint256 _stakingLimit)
external
pure
(VettedValidatorsLimit[] memory)
Decodes _evmScriptCallData
into tuple (uint256 _nodeOperatorId, uint256 _stakingLimit)
.