From b5334916b447821be45796a1280432e4666d3d77 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 30 Jan 2024 20:37:32 +0100 Subject: [PATCH 01/68] fix: tests with empty simple-dvt --- configs/config_mainnet.py | 16 ++++++++++++++ scripts/vote_simple_dvt.py | 0 tests/acceptance/test_staking_router.py | 28 +++++++++++++++++++++++-- tests/regression/test_permissions.py | 16 ++++++++++++-- utils/config.py | 6 +++++- 5 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 scripts/vote_simple_dvt.py diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index 87b62663..51028add 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -23,6 +23,7 @@ LIDO = "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84" LEGACY_ORACLE = "0x442af784A788A5bd6F42A01Ebe9F287a871243fb" NODE_OPERATORS_REGISTRY = "0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5" +SIMPLE_DVT = "0xaE7B191A31f627b4eB1d4DaC64eaB9976995b433" LIDO_IMPL_V1 = "0x47EbaB13B806773ec2A2d16873e2dF770D130b50" LEGACY_ORACLE_IMPL_V1 = "0x1430194905301504e8830ce4B0b0df7187E84AbD" @@ -127,6 +128,21 @@ CURATED_STAKING_MODULE_OPERATORS_COUNT = 39 CURATED_STAKING_MODULE_OPERATORS_ACTIVE_COUNT = 37 +# NodeOperatorsRegistry clone aka SimpleDVT +SIMPLE_DVT_IMPL = "0x8538930c385C0438A357d2c25CB3eAD95Ab6D8ed" +## see SimpleDVT's proxy appId() +SIMPLE_DVT_ARAGON_APP_ID = "0xe1635b63b5f7b5e545f2a637558a4029dea7905361a2f0fc28c66e9136cf86a4" +SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY = 432000 +SIMPLE_DVT_MODULE_TARGET_SHARE_BP = 50 +SIMPLE_DVT_MODULE_MODULE_FEE_BP = 800 +SIMPLE_DVT_MODULE_TREASURY_FEE_BP = 200 +SIMPLE_DVT_MODULE_ID = 2 +SIMPLE_DVT_MODULE_NAME = "SimpleDVT" +SIMPLE_DVT_MODULE_TYPE = ( + # bytes32("curated-onchain-v1") + "0x637572617465642d6f6e636861696e2d76310000000000000000000000000000" +) + # OracleDaemonConfig ORACLE_DAEMON_CONFIG = "0xbf05A929c3D7885a6aeAd833a992dA6E5ac23b09" NORMALIZED_CL_REWARD_PER_EPOCH = 64 diff --git a/scripts/vote_simple_dvt.py b/scripts/vote_simple_dvt.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/acceptance/test_staking_router.py b/tests/acceptance/test_staking_router.py index 0b521a15..72825727 100644 --- a/tests/acceptance/test_staking_router.py +++ b/tests/acceptance/test_staking_router.py @@ -18,6 +18,11 @@ CURATED_STAKING_MODULE_MODULE_FEE_BP, CURATED_STAKING_MODULE_TREASURY_FEE_BP, WITHDRAWAL_CREDENTIALS, + SIMPLE_DVT_MODULE_ID, + SIMPLE_DVT_MODULE_MODULE_FEE_BP, + SIMPLE_DVT_MODULE_NAME, + SIMPLE_DVT_MODULE_TARGET_SHARE_BP, + SIMPLE_DVT_MODULE_TREASURY_FEE_BP, ) from utils.evm_script import encode_error @@ -71,15 +76,21 @@ def test_constants(contract): def test_staking_modules(contract): - assert contract.getStakingModulesCount() == 1 + assert contract.getStakingModulesCount() == 2 - assert contract.getStakingModuleIds() == [CURATED_STAKING_MODULE_ID] + assert contract.getStakingModuleIds() == [CURATED_STAKING_MODULE_ID, SIMPLE_DVT_MODULE_ID] assert contract.getStakingModuleIsActive(1) == True assert contract.getStakingModuleIsStopped(1) == False assert contract.getStakingModuleIsDepositsPaused(1) == False assert contract.getStakingModuleNonce(1) >= 7260 assert contract.getStakingModuleStatus(1) == 0 + assert contract.getStakingModuleIsActive(2) == True + assert contract.getStakingModuleIsStopped(2) == False + assert contract.getStakingModuleIsDepositsPaused(2) == False + assert contract.getStakingModuleNonce(2) >= 0 + assert contract.getStakingModuleStatus(2) == 0 + curated_module = contract.getStakingModule(1) assert curated_module["id"] == CURATED_STAKING_MODULE_ID assert curated_module["stakingModuleAddress"] == contracts.node_operators_registry @@ -92,6 +103,19 @@ def test_staking_modules(contract): assert curated_module["lastDepositBlock"] >= 8705383 assert curated_module["exitedValidatorsCount"] >= 145 + simple_dvt_module = contract.getStakingModule(2) + assert simple_dvt_module["id"] == SIMPLE_DVT_MODULE_ID + assert simple_dvt_module["stakingModuleAddress"] == contracts.simple_dvt + assert simple_dvt_module["stakingModuleFee"] == SIMPLE_DVT_MODULE_MODULE_FEE_BP + assert simple_dvt_module["treasuryFee"] == SIMPLE_DVT_MODULE_TREASURY_FEE_BP + assert simple_dvt_module["targetShare"] == SIMPLE_DVT_MODULE_TARGET_SHARE_BP + assert simple_dvt_module["status"] == 0 + assert simple_dvt_module["name"] == SIMPLE_DVT_MODULE_NAME + # TODO: fix values after adding new keys + # assert simple_dvt_module["lastDepositAt"] >= 0 + # assert simple_dvt_module["lastDepositBlock"] >= 0 + # assert simple_dvt_module["exitedValidatorsCount"] >= 0 + fee_aggregate_distribution = contract.getStakingFeeAggregateDistribution() assert fee_aggregate_distribution["modulesFee"] == SR_MODULES_FEE_E20 assert fee_aggregate_distribution["treasuryFee"] == SR_TREASURY_FEE_E20 diff --git a/tests/regression/test_permissions.py b/tests/regression/test_permissions.py index 1d74cf56..b8f2c265 100644 --- a/tests/regression/test_permissions.py +++ b/tests/regression/test_permissions.py @@ -40,6 +40,7 @@ BURNER, LIDO_LOCATOR, LEGACY_ORACLE, + SIMPLE_DVT, ) @@ -60,7 +61,7 @@ def protocol_permissions(): "roles": { "DEFAULT_ADMIN_ROLE": [contracts.agent], "REQUEST_BURN_MY_STETH_ROLE": [contracts.agent], - "REQUEST_BURN_SHARES_ROLE": [contracts.lido, contracts.node_operators_registry], + "REQUEST_BURN_SHARES_ROLE": [contracts.lido, contracts.node_operators_registry, contracts.simple_dvt], }, }, STAKING_ROUTER: { @@ -280,6 +281,17 @@ def protocol_permissions(): "STAKING_ROUTER_ROLE": [STAKING_ROUTER], }, }, + SIMPLE_DVT: { + "contract_name": "NodeOperatorsRegistry", + "contract": contracts.simple_dvt, + "type": "AragonApp", + "roles": { + "MANAGE_NODE_OPERATOR_ROLE": [EASYTRACK_EVMSCRIPT_EXECUTOR], + "MANAGE_SIGNING_KEYS": [EASYTRACK_EVMSCRIPT_EXECUTOR], + "SET_NODE_OPERATOR_LIMIT_ROLE": [EASYTRACK_EVMSCRIPT_EXECUTOR], + "STAKING_ROUTER_ROLE": [STAKING_ROUTER, EASYTRACK_EVMSCRIPT_EXECUTOR], + }, + }, LEGACY_ORACLE: { "contract_name": "LegacyOracle", "contract": contracts.legacy_oracle, @@ -299,7 +311,7 @@ def test_protocol_permissions(protocol_permissions): method for method in permissions_config["contract"].signatures.keys() if method.endswith("_ROLE") ] - if contract_address == NODE_OPERATORS_REGISTRY: + if contract_address in [NODE_OPERATORS_REGISTRY, SIMPLE_DVT]: abi_roles_list.append("MANAGE_SIGNING_KEYS") roles = permissions_config["roles"] diff --git a/utils/config.py b/utils/config.py index 4a7c3593..270fbf11 100644 --- a/utils/config.py +++ b/utils/config.py @@ -132,6 +132,10 @@ def agent(self) -> interface.Agent: def node_operators_registry(self) -> interface.NodeOperatorsRegistry: return interface.NodeOperatorsRegistry(NODE_OPERATORS_REGISTRY) + @property + def simple_dvt(self) -> interface.NodeOperatorsRegistry: + return interface.NodeOperatorsRegistry(SIMPLE_DVT) + @property def legacy_oracle(self) -> interface.LegacyOracle: return interface.LegacyOracle(LEGACY_ORACLE) @@ -227,7 +231,7 @@ def dai_token(self) -> interface.ERC20: @property def usdt_token(self) -> interface.ERC20: return interface.ERC20(USDT_TOKEN) - + @property def usdc_token(self) -> interface.ERC20: return interface.ERC20(USDC_TOKEN) From 2aacab5a3c24b8aee8215ee971008438cc0a83c3 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 31 Jan 2024 16:22:24 +0100 Subject: [PATCH 02/68] fix: ET for simple dvt + helpers --- configs/config_mainnet.py | 9 + interfaces/ActivateNodeOperators.json | 1 + interfaces/AddNodeOperators.json | 1 + interfaces/ChangeNodeOperatorManagers.json | 1 + interfaces/DeactivateNodeOperators.json | 1 + interfaces/SetNodeOperatorNames.json | 1 + .../SetNodeOperatorRewardAddresses.json | 1 + interfaces/SetVettedValidatorsLimits.json | 1 + interfaces/SimpleDVT.json | 1 + interfaces/UpdateTargetValidatorLimits.json | 1 + tests/acceptance/conftest.py | 8 +- tests/conftest.py | 1 + tests/regression/conftest.py | 9 +- tests/regression/test_easy_track.py | 101 +++++++++++- utils/test/simple_dvt_helpers.py | 154 ++++++++++++++++++ 15 files changed, 283 insertions(+), 8 deletions(-) create mode 100644 interfaces/ActivateNodeOperators.json create mode 100644 interfaces/AddNodeOperators.json create mode 100644 interfaces/ChangeNodeOperatorManagers.json create mode 100644 interfaces/DeactivateNodeOperators.json create mode 100644 interfaces/SetNodeOperatorNames.json create mode 100644 interfaces/SetNodeOperatorRewardAddresses.json create mode 100644 interfaces/SetVettedValidatorsLimits.json create mode 100644 interfaces/SimpleDVT.json create mode 100644 interfaces/UpdateTargetValidatorLimits.json create mode 100644 utils/test/simple_dvt_helpers.py diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index 51028add..efbddf2c 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -57,6 +57,15 @@ EASYTRACK_EVMSCRIPT_EXECUTOR = "0xFE5986E06210aC1eCC1aDCafc0cc7f8D63B3F977" EASYTRACK_INCREASE_NOP_STAKING_LIMIT_FACTORY = "0xFeBd8FAC16De88206d4b18764e826AF38546AfE0" +EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER = "0x08637515E85A4633E23dfc7861e2A9f53af640f7" +EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY = "0xcAa3AF7460E83E665EEFeC73a7a542E5005C9639" +EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY = "0xCBb418F6f9BFd3525CE6aADe8F74ECFEfe2DB5C8" +EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY = "0x8B82C1546D47330335a48406cc3a50Da732672E7" +EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY = "0xD75778b855886Fc5e1eA7D6bFADA9EB68b35C19D" +EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_NAMES_FACTORY = "0x7d509BFF310d9460b1F613e4e40d342201a83Ae4" +EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY = "0x589e298964b9181D9938B84bB034C3BB9024E2C0" +EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY = "0x41CF3DbDc939c5115823Fba1432c4EC5E7bD226C" +EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY = "0xE31A0599A6772BCf9b2bFc9e25cf941e793c9a7D" # Multisigs FINANCE_MULTISIG = "0x48F300bD3C52c7dA6aAbDE4B683dEB27d38B9ABb" diff --git a/interfaces/ActivateNodeOperators.json b/interfaces/ActivateNodeOperators.json new file mode 100644 index 00000000..2aab8904 --- /dev/null +++ b/interfaces/ActivateNodeOperators.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_trustedCaller","type":"address"},{"internalType":"address","name":"_nodeOperatorsRegistry","type":"address"},{"internalType":"address","name":"_acl","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"acl","outputs":[{"internalType":"contract IACL","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_creator","type":"address"},{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"createEVMScript","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"decodeEVMScriptCallData","outputs":[{"components":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"address","name":"managerAddress","type":"address"}],"internalType":"struct ActivateNodeOperators.ActivateNodeOperatorInput[]","name":"","type":"tuple[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"nodeOperatorsRegistry","outputs":[{"internalType":"contract INodeOperatorsRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trustedCaller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] diff --git a/interfaces/AddNodeOperators.json b/interfaces/AddNodeOperators.json new file mode 100644 index 00000000..1b9274dc --- /dev/null +++ b/interfaces/AddNodeOperators.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_trustedCaller","type":"address"},{"internalType":"address","name":"_nodeOperatorsRegistry","type":"address"},{"internalType":"address","name":"_acl","type":"address"},{"internalType":"address","name":"_lido","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"acl","outputs":[{"internalType":"contract IACL","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_creator","type":"address"},{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"createEVMScript","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"decodeEVMScriptCallData","outputs":[{"internalType":"uint256","name":"nodeOperatorsCount","type":"uint256"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"rewardAddress","type":"address"},{"internalType":"address","name":"managerAddress","type":"address"}],"internalType":"struct AddNodeOperators.AddNodeOperatorInput[]","name":"nodeOperators","type":"tuple[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"lido","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nodeOperatorsRegistry","outputs":[{"internalType":"contract INodeOperatorsRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trustedCaller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] diff --git a/interfaces/ChangeNodeOperatorManagers.json b/interfaces/ChangeNodeOperatorManagers.json new file mode 100644 index 00000000..838bf063 --- /dev/null +++ b/interfaces/ChangeNodeOperatorManagers.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_trustedCaller","type":"address"},{"internalType":"address","name":"_nodeOperatorsRegistry","type":"address"},{"internalType":"address","name":"_acl","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"acl","outputs":[{"internalType":"contract IACL","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_creator","type":"address"},{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"createEVMScript","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"decodeEVMScriptCallData","outputs":[{"components":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"address","name":"oldManagerAddress","type":"address"},{"internalType":"address","name":"newManagerAddress","type":"address"}],"internalType":"struct ChangeNodeOperatorManagers.ChangeNodeOperatorManagersInput[]","name":"","type":"tuple[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"nodeOperatorsRegistry","outputs":[{"internalType":"contract INodeOperatorsRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trustedCaller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] diff --git a/interfaces/DeactivateNodeOperators.json b/interfaces/DeactivateNodeOperators.json new file mode 100644 index 00000000..518a2e7f --- /dev/null +++ b/interfaces/DeactivateNodeOperators.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_trustedCaller","type":"address"},{"internalType":"address","name":"_nodeOperatorsRegistry","type":"address"},{"internalType":"address","name":"_acl","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"acl","outputs":[{"internalType":"contract IACL","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_creator","type":"address"},{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"createEVMScript","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"decodeEVMScriptCallData","outputs":[{"components":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"address","name":"managerAddress","type":"address"}],"internalType":"struct DeactivateNodeOperators.DeactivateNodeOperatorInput[]","name":"","type":"tuple[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"nodeOperatorsRegistry","outputs":[{"internalType":"contract INodeOperatorsRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trustedCaller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] diff --git a/interfaces/SetNodeOperatorNames.json b/interfaces/SetNodeOperatorNames.json new file mode 100644 index 00000000..9e299d48 --- /dev/null +++ b/interfaces/SetNodeOperatorNames.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_trustedCaller","type":"address"},{"internalType":"address","name":"_nodeOperatorsRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"_creator","type":"address"},{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"createEVMScript","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"decodeEVMScriptCallData","outputs":[{"components":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"string","name":"name","type":"string"}],"internalType":"struct SetNodeOperatorNames.SetNameInput[]","name":"","type":"tuple[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"nodeOperatorsRegistry","outputs":[{"internalType":"contract INodeOperatorsRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trustedCaller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] diff --git a/interfaces/SetNodeOperatorRewardAddresses.json b/interfaces/SetNodeOperatorRewardAddresses.json new file mode 100644 index 00000000..62d1c0fe --- /dev/null +++ b/interfaces/SetNodeOperatorRewardAddresses.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_trustedCaller","type":"address"},{"internalType":"address","name":"_nodeOperatorsRegistry","type":"address"},{"internalType":"address","name":"_lido","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"_creator","type":"address"},{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"createEVMScript","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"decodeEVMScriptCallData","outputs":[{"components":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"address","name":"rewardAddress","type":"address"}],"internalType":"struct SetNodeOperatorRewardAddresses.SetRewardAddressInput[]","name":"","type":"tuple[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"lido","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nodeOperatorsRegistry","outputs":[{"internalType":"contract INodeOperatorsRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trustedCaller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] diff --git a/interfaces/SetVettedValidatorsLimits.json b/interfaces/SetVettedValidatorsLimits.json new file mode 100644 index 00000000..1b839938 --- /dev/null +++ b/interfaces/SetVettedValidatorsLimits.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_trustedCaller","type":"address"},{"internalType":"address","name":"_nodeOperatorsRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"_creator","type":"address"},{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"createEVMScript","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"decodeEVMScriptCallData","outputs":[{"components":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"uint256","name":"stakingLimit","type":"uint256"}],"internalType":"struct SetVettedValidatorsLimits.VettedValidatorsLimitInput[]","name":"","type":"tuple[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"nodeOperatorsRegistry","outputs":[{"internalType":"contract INodeOperatorsRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trustedCaller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] diff --git a/interfaces/SimpleDVT.json b/interfaces/SimpleDVT.json new file mode 100644 index 00000000..63223133 --- /dev/null +++ b/interfaces/SimpleDVT.json @@ -0,0 +1 @@ +[{"constant":true,"inputs":[],"name":"hasInitialized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_keysCount","type":"uint256"},{"name":"_publicKeys","type":"bytes"},{"name":"_signatures","type":"bytes"}],"name":"addSigningKeys","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getType","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_script","type":"bytes"}],"name":"getEVMScriptExecutor","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"clearNodeOperatorPenalty","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getRecoveryVault","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_offset","type":"uint256"},{"name":"_limit","type":"uint256"}],"name":"getNodeOperatorIds","outputs":[{"name":"nodeOperatorIds","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_offset","type":"uint256"},{"name":"_limit","type":"uint256"}],"name":"getSigningKeys","outputs":[{"name":"pubkeys","type":"bytes"},{"name":"signatures","type":"bytes"},{"name":"used","type":"bool[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_fromIndex","type":"uint256"},{"name":"_keysCount","type":"uint256"}],"name":"removeSigningKeysOperatorBH","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"getNodeOperatorIsActive","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_name","type":"string"}],"name":"setNodeOperatorName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_totalRewardShares","type":"uint256"}],"name":"getRewardsDistribution","outputs":[{"name":"recipients","type":"address[]"},{"name":"shares","type":"uint256[]"},{"name":"penalized","type":"bool[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_indexFrom","type":"uint256"},{"name":"_indexTo","type":"uint256"}],"name":"invalidateReadyToDepositKeysRange","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_locator","type":"address"},{"name":"_type","type":"bytes32"},{"name":"_stuckPenaltyDelay","type":"uint256"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_delay","type":"uint256"}],"name":"setStuckPenaltyDelay","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getStuckPenaltyDelay","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_index","type":"uint256"}],"name":"removeSigningKey","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_fromIndex","type":"uint256"},{"name":"_keysCount","type":"uint256"}],"name":"removeSigningKeys","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"isOperatorPenalized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"deactivateNodeOperator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"token","type":"address"}],"name":"allowRecoverability","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"STAKING_ROUTER_ROLE","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_keysCount","type":"uint256"},{"name":"_publicKeys","type":"bytes"},{"name":"_signatures","type":"bytes"}],"name":"addSigningKeysOperatorBH","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"appId","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getActiveNodeOperatorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_rewardAddress","type":"address"}],"name":"addNodeOperator","outputs":[{"name":"id","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getContractVersion","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getInitializationBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"getUnusedSigningKeyCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"onRewardsMinted","outputs":[],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MANAGE_NODE_OPERATOR_ROLE","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"onWithdrawalCredentialsChanged","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"activateNodeOperator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_rewardAddress","type":"address"}],"name":"setNodeOperatorRewardAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_fullInfo","type":"bool"}],"name":"getNodeOperator","outputs":[{"name":"active","type":"bool"},{"name":"name","type":"string"},{"name":"rewardAddress","type":"address"},{"name":"totalVettedValidators","type":"uint64"},{"name":"totalExitedValidators","type":"uint64"},{"name":"totalAddedValidators","type":"uint64"},{"name":"totalDepositedValidators","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_locator","type":"address"},{"name":"_type","type":"bytes32"},{"name":"_stuckPenaltyDelay","type":"uint256"}],"name":"finalizeUpgrade_v2","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getStakingModuleSummary","outputs":[{"name":"totalExitedValidators","type":"uint256"},{"name":"totalDepositedValidators","type":"uint256"},{"name":"depositableValidatorsCount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorIds","type":"bytes"},{"name":"_exitedValidatorsCounts","type":"bytes"}],"name":"updateExitedValidatorsCount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorIds","type":"bytes"},{"name":"_stuckValidatorsCounts","type":"bytes"}],"name":"updateStuckValidatorsCount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"}],"name":"transferToVault","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_sender","type":"address"},{"name":"_role","type":"bytes32"},{"name":"_params","type":"uint256[]"}],"name":"canPerform","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_refundedValidatorsCount","type":"uint256"}],"name":"updateRefundedValidatorsCount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getEVMScriptRegistry","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getNodeOperatorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_isTargetLimitActive","type":"bool"},{"name":"_targetLimit","type":"uint256"}],"name":"updateTargetValidatorsLimits","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_vettedSigningKeysCount","type":"uint64"}],"name":"setNodeOperatorStakingLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"getNodeOperatorSummary","outputs":[{"name":"isTargetLimitActive","type":"bool"},{"name":"targetValidatorsCount","type":"uint256"},{"name":"stuckValidatorsCount","type":"uint256"},{"name":"refundedValidatorsCount","type":"uint256"},{"name":"stuckPenaltyEndTimestamp","type":"uint256"},{"name":"totalExitedValidators","type":"uint256"},{"name":"totalDepositedValidators","type":"uint256"},{"name":"depositableValidatorsCount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_index","type":"uint256"}],"name":"getSigningKey","outputs":[{"name":"key","type":"bytes"},{"name":"depositSignature","type":"bytes"},{"name":"used","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MAX_NODE_OPERATOR_NAME_LENGTH","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_depositsCount","type":"uint256"},{"name":"","type":"bytes"}],"name":"obtainDepositData","outputs":[{"name":"publicKeys","type":"bytes"},{"name":"signatures","type":"bytes"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getKeysOpIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getNonce","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"kernel","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getLocator","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"SET_NODE_OPERATOR_LIMIT_ROLE","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"getTotalSigningKeyCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isPetrified","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MAX_STUCK_PENALTY_DELAY","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"onExitedAndStuckValidatorsCountsUpdated","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MAX_NODE_OPERATORS_COUNT","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_index","type":"uint256"}],"name":"removeSigningKeyOperatorBH","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_exitedValidatorsCount","type":"uint256"},{"name":"_stuckValidatorsCount","type":"uint256"}],"name":"unsafeUpdateValidatorsCount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MANAGE_SIGNING_KEYS","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"isOperatorPenaltyCleared","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"rewardAddress","type":"address"},{"indexed":false,"name":"stakingLimit","type":"uint64"}],"name":"NodeOperatorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"active","type":"bool"}],"name":"NodeOperatorActiveSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"name","type":"string"}],"name":"NodeOperatorNameSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"rewardAddress","type":"address"}],"name":"NodeOperatorRewardAddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"totalKeysTrimmed","type":"uint64"}],"name":"NodeOperatorTotalKeysTrimmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"keysOpIndex","type":"uint256"}],"name":"KeysOpIndexSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"moduleType","type":"bytes32"}],"name":"StakingModuleTypeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"rewardAddress","type":"address"},{"indexed":false,"name":"sharesAmount","type":"uint256"}],"name":"RewardsDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"locatorAddress","type":"address"}],"name":"LocatorContractSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"approvedValidatorsCount","type":"uint256"}],"name":"VettedSigningKeysCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"depositedValidatorsCount","type":"uint256"}],"name":"DepositedSigningKeysCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"exitedValidatorsCount","type":"uint256"}],"name":"ExitedSigningKeysCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"totalValidatorsCount","type":"uint256"}],"name":"TotalSigningKeysCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"nonce","type":"uint256"}],"name":"NonceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"stuckPenaltyDelay","type":"uint256"}],"name":"StuckPenaltyDelayChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"stuckValidatorsCount","type":"uint256"},{"indexed":false,"name":"refundedValidatorsCount","type":"uint256"},{"indexed":false,"name":"stuckPenaltyEndTimestamp","type":"uint256"}],"name":"StuckPenaltyStateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"targetValidatorsCount","type":"uint256"}],"name":"TargetValidatorsCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"recipientAddress","type":"address"},{"indexed":false,"name":"sharesPenalizedAmount","type":"uint256"}],"name":"NodeOperatorPenalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"version","type":"uint256"}],"name":"ContractVersionSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"executor","type":"address"},{"indexed":false,"name":"script","type":"bytes"},{"indexed":false,"name":"input","type":"bytes"},{"indexed":false,"name":"returnData","type":"bytes"}],"name":"ScriptResult","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"vault","type":"address"},{"indexed":true,"name":"token","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"RecoverToVault","type":"event"}] \ No newline at end of file diff --git a/interfaces/UpdateTargetValidatorLimits.json b/interfaces/UpdateTargetValidatorLimits.json new file mode 100644 index 00000000..7260ce4e --- /dev/null +++ b/interfaces/UpdateTargetValidatorLimits.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_trustedCaller","type":"address"},{"internalType":"address","name":"_nodeOperatorsRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"_creator","type":"address"},{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"createEVMScript","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_evmScriptCallData","type":"bytes"}],"name":"decodeEVMScriptCallData","outputs":[{"components":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"bool","name":"isTargetLimitActive","type":"bool"},{"internalType":"uint256","name":"targetLimit","type":"uint256"}],"internalType":"struct UpdateTargetValidatorLimits.TargetValidatorsLimit[]","name":"","type":"tuple[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"nodeOperatorsRegistry","outputs":[{"internalType":"contract INodeOperatorsRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trustedCaller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] diff --git a/tests/acceptance/conftest.py b/tests/acceptance/conftest.py index 4e7f1405..4e8d9c43 100644 --- a/tests/acceptance/conftest.py +++ b/tests/acceptance/conftest.py @@ -6,16 +6,22 @@ from utils.test.helpers import ETH from utils.test.oracle_report_helpers import oracle_report +from utils.test.simple_dvt_helpers import fill_simple_dvt_ops_vetted_keys from brownie import chain ENV_REPORT_AFTER_VOTE = "REPORT_AFTER_VOTE" +ENV_FILL_SIMPLE_DVT = "FILL_SIMPLE_DVT" + @pytest.fixture(scope="function", autouse=is_there_any_vote_scripts() or is_there_any_upgrade_scripts()) -def autoexecute_vote(helpers, vote_ids_from_env, accounts): +def autoexecute_vote(helpers, vote_ids_from_env, accounts, stranger): if vote_ids_from_env: helpers.execute_votes(accounts, vote_ids_from_env, contracts.voting, topup="0.5 ether") else: start_and_execute_votes(contracts.voting, helpers) + if os.getenv(ENV_FILL_SIMPLE_DVT): + print(f"Prefilling SimpleDVT...") + fill_simple_dvt_ops_vetted_keys(stranger) if os.getenv(ENV_REPORT_AFTER_VOTE): oracle_report(cl_diff=ETH(523), exclude_vaults_balances=False) diff --git a/tests/conftest.py b/tests/conftest.py index fe73d266..6aea0c93 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -213,6 +213,7 @@ def parse_events_from_local_abi(): "LidoExecutionLayerRewardsVault": [EXECUTION_LAYER_REWARDS_VAULT], "Kernel": [ARAGON_KERNEL_IMPL], "NodeOperatorsRegistry": [NODE_OPERATORS_REGISTRY, NODE_OPERATORS_REGISTRY_IMPL], + "SimpleDVT": [SIMPLE_DVT, SIMPLE_DVT_IMPL], "OracleDaemonConfig": [ORACLE_DAEMON_CONFIG], "OracleReportSanityChecker": [ORACLE_REPORT_SANITY_CHECKER], "Repo": [ARAGON_COMMON_REPO_IMPL], diff --git a/tests/regression/conftest.py b/tests/regression/conftest.py index 4e7f1405..dbaac6d4 100644 --- a/tests/regression/conftest.py +++ b/tests/regression/conftest.py @@ -6,16 +6,23 @@ from utils.test.helpers import ETH from utils.test.oracle_report_helpers import oracle_report +from utils.test.simple_dvt_helpers import fill_simple_dvt_ops_vetted_keys from brownie import chain ENV_REPORT_AFTER_VOTE = "REPORT_AFTER_VOTE" +ENV_FILL_SIMPLE_DVT = "FILL_SIMPLE_DVT" + @pytest.fixture(scope="function", autouse=is_there_any_vote_scripts() or is_there_any_upgrade_scripts()) -def autoexecute_vote(helpers, vote_ids_from_env, accounts): +def autoexecute_vote(helpers, vote_ids_from_env, accounts, stranger): if vote_ids_from_env: helpers.execute_votes(accounts, vote_ids_from_env, contracts.voting, topup="0.5 ether") else: start_and_execute_votes(contracts.voting, helpers) + if os.getenv(ENV_FILL_SIMPLE_DVT): + print(f"Prefilling SimpleDVT...") + fill_simple_dvt_ops_vetted_keys(stranger) + if os.getenv(ENV_REPORT_AFTER_VOTE): oracle_report(cl_diff=ETH(523), exclude_vaults_balances=False) diff --git a/tests/regression/test_easy_track.py b/tests/regression/test_easy_track.py index e7439efc..88d13836 100644 --- a/tests/regression/test_easy_track.py +++ b/tests/regression/test_easy_track.py @@ -1,14 +1,18 @@ from eth_abi.abi import encode_single -from brownie import accounts, chain, interface # type: ignore -from utils.config import contracts, EASYTRACK_INCREASE_NOP_STAKING_LIMIT_FACTORY +from brownie import accounts, chain, interface +from utils.config import ( + contracts, + EASYTRACK_INCREASE_NOP_STAKING_LIMIT_FACTORY, + EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, + EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY, + EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY, +) +from utils.test.simple_dvt_helpers import simple_dvt_add_keys, simple_dvt_add_node_operators +from utils.test.easy_track_helpers import _encode_calldata NODE_OPERATOR_ID = 0 -def _encode_calldata(signature, values): - return "0x" + encode_single(signature, values).hex() - - def test_increase_nop_staking_limit( stranger, ): @@ -48,3 +52,88 @@ def test_increase_nop_staking_limit( updated_node_operator = contracts.node_operators_registry.getNodeOperator(NODE_OPERATOR_ID, False) assert updated_node_operator["totalVettedValidators"] == new_staking_limit + + +def test_simple_dvt_add_node_operators( + stranger, +): + NEW_OPERATOR_NAMES = [ + "New Name 1", + "New Name 2", + ] + + NEW_REWARD_ADDRESSES = [ + "0x1110000000000000000000000000000000001111", + "0x1110000000000000000000000000000000002222", + ] + + NEW_MANAGERS = [ + "0x1110000000000000000000000000000011111111", + "0x1110000000000000000000000000000022222222", + ] + + input_params = [ + (NEW_OPERATOR_NAMES[0], NEW_REWARD_ADDRESSES[0], NEW_MANAGERS[0]), + (NEW_OPERATOR_NAMES[1], NEW_REWARD_ADDRESSES[1], NEW_MANAGERS[1]), + ] + (node_operators_count_before, node_operator_count_after) = simple_dvt_add_node_operators( + contracts.simple_dvt, stranger, input_params + ) + + assert node_operator_count_after == node_operators_count_before + len(input_params) + + +def test_simple_dvt_set_vetted_validators_limits( + stranger, +): + NEW_OPERATOR_NAMES = [ + "New Name 1", + ] + + NEW_REWARD_ADDRESSES = [ + "0x1110000000000000000000000000000000001111", + ] + + NEW_MANAGERS = [ + "0x1110000000000000000000000000000011111111", + ] + + input_params = [ + (NEW_OPERATOR_NAMES[0], NEW_REWARD_ADDRESSES[0], NEW_MANAGERS[0]), + ] + + (_, node_operator_count_after) = simple_dvt_add_node_operators(contracts.simple_dvt, stranger, input_params) + + no_id = node_operator_count_after - 1 + + factory = interface.SetVettedValidatorsLimits(EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY) + trusted_caller = accounts.at(EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, force=True) + + node_operator = contracts.simple_dvt.getNodeOperator(no_id, False) + new_staking_limit = node_operator["totalVettedValidators"] + 1 + + if node_operator["totalAddedValidators"] < new_staking_limit: + simple_dvt_add_keys(contracts.simple_dvt, no_id, 1) + + calldata = _encode_calldata("((uint256,uint256)[])", [[(no_id, new_staking_limit)]]) + + motions_before = contracts.easy_track.getMotions() + + tx = contracts.easy_track.createMotion(factory, calldata, {"from": trusted_caller}) + + assert len(contracts.easy_track.getMotions()) == len(motions_before) + 1 + + chain.sleep(60 * 60 * 24 * 3) + chain.mine() + + motions = contracts.easy_track.getMotions() + + contracts.easy_track.enactMotion( + motions[-1][0], + tx.events["MotionCreated"]["_evmScriptCallData"], + {"from": stranger}, + ) + + updated_node_operator = contracts.simple_dvt.getNodeOperator(no_id, False) + + assert updated_node_operator["totalVettedValidators"] == new_staking_limit diff --git a/utils/test/simple_dvt_helpers.py b/utils/test/simple_dvt_helpers.py new file mode 100644 index 00000000..8d4a2b18 --- /dev/null +++ b/utils/test/simple_dvt_helpers.py @@ -0,0 +1,154 @@ +from brownie import chain, accounts, interface +from tests.regression.test_node_operators_flow import random_pubkeys_batch, random_signatures_batch # type: ignore +from utils.config import ( + contracts, + EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, + EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY, + EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY, +) +from utils.test.easy_track_helpers import _encode_calldata + + +MIN_OP_KEYS_CNT = 10 +MIN_OPS_CNT = 3 + +OPERATOR_NAMES = [ + "Name 1", + "Name 2", + "Name 3", +] + +REWARD_ADDRESSES = [ + "0x0000000000000000000000000000000000001111", + "0x0000000000000000000000000000000000002222", + "0x0000000000000000000000000000000000003333", +] + +MANAGERS = [ + "0x0000000000000000000000000000000011111111", + "0x0000000000000000000000000000000022222222", + "0x0000000000000000000000000000000033333333", +] + + +def fill_simple_dvt_ops(stranger, min_ops_cnt=MIN_OPS_CNT): + node_operators_count_before = contracts.simple_dvt.getNodeOperatorsCount() + cnt = 0 + input_params = [] + while node_operators_count_before + cnt < min_ops_cnt: + input_params.append((OPERATOR_NAMES[cnt], REWARD_ADDRESSES[cnt], MANAGERS[cnt])) + cnt += 1 + + (node_operators_count_before, node_operator_count_after) = simple_dvt_add_node_operators( + contracts.simple_dvt, stranger, input_params + ) + assert node_operator_count_after == node_operators_count_before + cnt + assert contracts.simple_dvt.getNodeOperatorsCount() >= min_ops_cnt + + +def fill_simple_dvt_ops_keys(stranger, min_ops_cnt=MIN_OPS_CNT, min_keys_cnt=MIN_OP_KEYS_CNT): + fill_simple_dvt_ops(stranger, min_ops_cnt) + for no_id in range(0, min_ops_cnt): + unused_keys_count = contracts.simple_dvt.getUnusedSigningKeyCount(no_id) + + if unused_keys_count < min_keys_cnt: + simple_dvt_add_keys(contracts.simple_dvt, no_id, min_keys_cnt - unused_keys_count) + + assert contracts.simple_dvt.getUnusedSigningKeyCount(no_id) >= min_keys_cnt + + +def fill_simple_dvt_ops_vetted_keys(stranger, min_ops_cnt=MIN_OPS_CNT, min_keys_cnt=MIN_OP_KEYS_CNT): + fill_simple_dvt_ops_keys(stranger, min_ops_cnt, min_keys_cnt) + factory = interface.SetVettedValidatorsLimits(EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY) + trusted_caller = accounts.at(EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, force=True) + + input_params = [] + for no_id in range(0, min_ops_cnt): + no = contracts.simple_dvt.getNodeOperator(no_id, False) + + if no["totalVettedValidators"] < no["totalAddedValidators"]: + input_params.append((no_id, no["totalAddedValidators"])) + + if len(input_params) > 0: + calldata = _encode_calldata("((uint256,uint256)[])", [input_params]) + + motions_before = contracts.easy_track.getMotions() + + tx = contracts.easy_track.createMotion(factory, calldata, {"from": trusted_caller}) + motions = contracts.easy_track.getMotions() + + assert len(motions) == len(motions_before) + 1 + + chain.sleep(60 * 60 * 24 * 3) + chain.mine() + + contracts.easy_track.enactMotion( + motions[-1][0], + tx.events["MotionCreated"]["_evmScriptCallData"], + {"from": stranger}, + ) + + for no_id in range(0, min_ops_cnt): + no = contracts.simple_dvt.getNodeOperator(no_id, False) + + assert no["totalVettedValidators"] == no["totalAddedValidators"] + + +def simple_dvt_add_node_operators(simple_dvt, stranger, input_params=[]): + factory = interface.AddNodeOperators(EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY) + trusted_caller = accounts.at(EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, force=True) + + node_operators_count_before = simple_dvt.getNodeOperatorsCount() + + # input_params = [ + # (OPERATOR_NAMES[0], REWARD_ADDRESSES[0], MANAGERS[0]), + # (OPERATOR_NAMES[1], REWARD_ADDRESSES[1], MANAGERS[1]), + # ] + if len(input_params) > 0: + calldata = _encode_calldata( + "(uint256,(string,address,address)[])", + [ + node_operators_count_before, + input_params, + ], + ) + motions_before = contracts.easy_track.getMotions() + + tx = contracts.easy_track.createMotion(factory, calldata, {"from": trusted_caller}) + + motions = contracts.easy_track.getMotions() + assert len(motions) == len(motions_before) + 1 + + chain.sleep(60 * 60 * 24 * 3) + chain.mine() + + contracts.easy_track.enactMotion( + motions[-1][0], + tx.events["MotionCreated"]["_evmScriptCallData"], + {"from": stranger}, + ) + + return (node_operators_count_before, simple_dvt.getNodeOperatorsCount()) + + +def simple_dvt_add_keys(simple_dvt, node_operator_id, keys_count=1): + pubkeys_batch = random_pubkeys_batch(keys_count) + signatures_batch = random_signatures_batch(keys_count) + + total_signing_keys_count_before = simple_dvt.getTotalSigningKeyCount(node_operator_id) + unused_signing_keys_count_before = simple_dvt.getUnusedSigningKeyCount(node_operator_id) + node_operator_before = simple_dvt.getNodeOperator(node_operator_id, True) + + tx = simple_dvt.addSigningKeys( + node_operator_id, + keys_count, + pubkeys_batch, + signatures_batch, + {"from": node_operator_before["rewardAddress"]}, + ) + + total_signing_keys_count_after = simple_dvt.getTotalSigningKeyCount(node_operator_id) + unused_signing_keys_count_after = simple_dvt.getUnusedSigningKeyCount(node_operator_id) + + assert total_signing_keys_count_after == total_signing_keys_count_before + keys_count + assert unused_signing_keys_count_after == unused_signing_keys_count_before + keys_count From f234ba383f7ad34633c3d035ab54fd9af87d62a9 Mon Sep 17 00:00:00 2001 From: George Avsetsin Date: Thu, 1 Feb 2024 13:21:31 +0300 Subject: [PATCH 03/68] simple dvt rewards distribution tests --- configs/config_mainnet.py | 7 + interfaces/0xsplits/SplitWallet.json | 1 + interfaces/obol_split/ObolLidoSplit.json | 1 + .../obol_split/ObolLidoSplitFactory.json | 1 + .../test_sdvt_rewards_happy_path.py | 149 ++++++++++++++++++ ...test_sdvt_splitter_rewards_distribution.py | 138 ++++++++++++++++ utils/config.py | 8 + utils/test/reward_wrapper_helpers.py | 58 +++++++ utils/test/simple_dvt_helpers.py | 32 ++++ utils/test/split_helpers.py | 114 ++++++++++++++ utils/test/staking_router_helpers.py | 17 ++ 11 files changed, 526 insertions(+) create mode 100644 interfaces/0xsplits/SplitWallet.json create mode 100644 interfaces/obol_split/ObolLidoSplit.json create mode 100644 interfaces/obol_split/ObolLidoSplitFactory.json create mode 100644 tests/regression/test_sdvt_rewards_happy_path.py create mode 100644 tests/regression/test_sdvt_splitter_rewards_distribution.py create mode 100644 utils/test/reward_wrapper_helpers.py create mode 100644 utils/test/split_helpers.py create mode 100644 utils/test/staking_router_helpers.py diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index efbddf2c..6e7d5749 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -269,3 +269,10 @@ # Anchor ANCHOR_VAULT_PROXY = "0xA2F987A546D4CD1c607Ee8141276876C26b72Bdf" + +# 0xSplits +SPLIT_MAIN = "0x2ed6c4B5dA6378c7897AC67Ba9e43102Feb694EE" + +# Rewards Wrapper (aka ObolLidoSplit) +OBOL_LIDO_SPLIT_FACTORY = "0xA9d94139A310150Ca1163b5E23f3E1dbb7D9E2A6" +OBOL_LIDO_SPLIT_IMPL = "0x2fB59065F049e0D0E3180C6312FA0FeB5Bbf0FE3" diff --git a/interfaces/0xsplits/SplitWallet.json b/interfaces/0xsplits/SplitWallet.json new file mode 100644 index 00000000..26a54110 --- /dev/null +++ b/interfaces/0xsplits/SplitWallet.json @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"split","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReceiveETH","type":"event"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sendERC20ToMain","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sendETHToMain","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"splitMain","outputs":[{"internalType":"contract ISplitMain","name":"","type":"address"}],"stateMutability":"view","type":"function"}] diff --git a/interfaces/obol_split/ObolLidoSplit.json b/interfaces/obol_split/ObolLidoSplit.json new file mode 100644 index 00000000..f28b0e7a --- /dev/null +++ b/interfaces/obol_split/ObolLidoSplit.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_feeRecipient","type":"address"},{"internalType":"uint256","name":"_feeShare","type":"uint256"},{"internalType":"contract ERC20","name":"_stETH","type":"address"},{"internalType":"contract ERC20","name":"_wstETH","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Invalid_Address","type":"error"},{"inputs":[],"name":"Invalid_FeeRecipient","type":"error"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"Invalid_FeeShare","type":"error"},{"inputs":[],"name":"distribute","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"rescueFunds","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"splitWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"stETH","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wstETH","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"}] diff --git a/interfaces/obol_split/ObolLidoSplitFactory.json b/interfaces/obol_split/ObolLidoSplitFactory.json new file mode 100644 index 00000000..c2f423bd --- /dev/null +++ b/interfaces/obol_split/ObolLidoSplitFactory.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_feeRecipient","type":"address"},{"internalType":"uint256","name":"_feeShare","type":"uint256"},{"internalType":"contract ERC20","name":"_stETH","type":"address"},{"internalType":"contract ERC20","name":"_wstETH","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Invalid_Wallet","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"split","type":"address"}],"name":"CreateObolLidoSplit","type":"event"},{"inputs":[{"internalType":"address","name":"splitWallet","type":"address"}],"name":"createSplit","outputs":[{"internalType":"address","name":"lidoSplit","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lidoSplitImpl","outputs":[{"internalType":"contract ObolLidoSplit","name":"","type":"address"}],"stateMutability":"view","type":"function"}] diff --git a/tests/regression/test_sdvt_rewards_happy_path.py b/tests/regression/test_sdvt_rewards_happy_path.py new file mode 100644 index 00000000..6abc1197 --- /dev/null +++ b/tests/regression/test_sdvt_rewards_happy_path.py @@ -0,0 +1,149 @@ +import pytest + +from brownie import accounts, ZERO_ADDRESS +from utils.test.helpers import ETH +from utils.config import contracts + +from utils.test.reward_wrapper_helpers import deploy_reward_wrapper, wrap_and_split_rewards +from utils.test.split_helpers import ( + deploy_split_wallet, + get_split_percent_allocation, + get_split_percentage_scale, + split_and_withdraw_wsteth_rewards, +) +from utils.test.simple_dvt_helpers import simple_dvt_add_keys, simple_dvt_vet_keys, simple_dvt_add_node_operators +from utils.test.staking_router_helpers import pause_staking_module +from utils.test.oracle_report_helpers import oracle_report + + +WEI_TOLERANCE = 5 # wei tolerance to avoid rounding issue + + +# fixtures + + +@pytest.fixture(scope="module") +def cluster_participants(accounts): + CLUSTER_PARTICIPANTS = 5 + + return sorted(map(lambda participant: participant.address, accounts[0:CLUSTER_PARTICIPANTS])) + + +@pytest.fixture(scope="module") +def split_wallet(cluster_participants): + percentage_scale = get_split_percentage_scale() + percent_allocation = get_split_percent_allocation(len(cluster_participants), percentage_scale) + (deployed_contract, _) = deploy_split_wallet(cluster_participants, percent_allocation, cluster_participants[0]) + + return deployed_contract + + +@pytest.fixture(scope="module") +def reward_wrapper(split_wallet, cluster_participants): + (deployed_contract, _) = deploy_reward_wrapper(split_wallet, cluster_participants[0]) + return deployed_contract + + +@pytest.fixture(scope="function") +def simple_dvt_module_id(): + modules = contracts.staking_router.getStakingModules() + return next(filter(lambda module: module[1] == contracts.simple_dvt.address, modules))[0] + + +# staking router <> simple dvt tests + + +def test_sdvt_module_connected_to_router(): + """ + Test that simple dvt module is connected to staking router + """ + modules = contracts.staking_router.getStakingModules() + assert any(map(lambda module: module[1] == contracts.simple_dvt.address, modules)) + + +# full happy path test + + +def test_rewards_distribution_happy_path(simple_dvt_module_id, cluster_participants, reward_wrapper): + """ + Test happy path of rewards distribution + Test adding new cluster to simple dvt module, depositing to simple dvt module, distributing and claiming rewards + """ + simple_dvt, staking_router = contracts.simple_dvt, contracts.staking_router + lido, deposit_security_module = contracts.lido, contracts.deposit_security_module + withdrawal_queue = contracts.withdrawal_queue + + stranger = cluster_participants[0] + + new_cluster_name = "new cluster" + new_manager_address = "0x1110000000000000000000000000000011111111" + new_reward_address = reward_wrapper.address + + # add operator to simple dvt module + input_params = [(new_cluster_name, new_reward_address, new_manager_address)] + (node_operators_count_before, node_operator_count_after) = simple_dvt_add_node_operators( + simple_dvt, stranger, input_params + ) + operator_id = node_operator_count_after - 1 + assert node_operator_count_after == node_operators_count_before + len(input_params) + + # add keys to the operator + simple_dvt_add_keys(simple_dvt, operator_id, 10) + + # vet operator keys + simple_dvt_vet_keys(operator_id, stranger) + + # pause deposit to all modules except simple dvt + # to be sure that all deposits go to simple dvt + modules = staking_router.getStakingModules() + for module in modules: + if module[0] != simple_dvt_module_id: + pause_staking_module(module[0]) + + # fill the deposit buffer + deposits_count = 10 + deposit_size = ETH(32) + + buffered_ether_before_submit = lido.getBufferedEther() + withdrawal_unfinalized_steth = withdrawal_queue.unfinalizedStETH() + + required_buffer = max(0, withdrawal_unfinalized_steth - buffered_ether_before_submit) + required_buffer += deposits_count * deposit_size + WEI_TOLERANCE + + eth_whale = accounts.at(staking_router.DEPOSIT_CONTRACT(), force=True) + lido.submit(ZERO_ADDRESS, {"from": eth_whale, "value": required_buffer}) + + assert lido.getDepositableEther() >= required_buffer + + # deposit to simple dvt + module_summary_before = staking_router.getStakingModuleSummary(simple_dvt_module_id) + lido.deposit(deposits_count, simple_dvt_module_id, "0x", {"from": deposit_security_module}) + module_summary_after = staking_router.getStakingModuleSummary(simple_dvt_module_id) + + assert ( + module_summary_after["totalDepositedValidators"] + == module_summary_before["totalDepositedValidators"] + deposits_count + ) + + # check that there is no steth on the cluster reward address + cluster_rewards_before_report = lido.balanceOf(new_reward_address) + assert cluster_rewards_before_report == 0 + + # oracle report + oracle_report(cl_diff=ETH(100)) + cluster_rewards_after_report = lido.balanceOf(new_reward_address) + + # check that cluster reward address balance increased + assert cluster_rewards_after_report > 0 + + # wrap rewards and split between dvt provider and split wallet + wrap_and_split_rewards(reward_wrapper, stranger) + + # split wsteth rewards between participants and withdraw + split_and_withdraw_wsteth_rewards( + reward_wrapper.splitWallet(), + cluster_participants, + get_split_percent_allocation(len(cluster_participants), get_split_percentage_scale()), + get_split_percentage_scale(), + stranger, + ) diff --git a/tests/regression/test_sdvt_splitter_rewards_distribution.py b/tests/regression/test_sdvt_splitter_rewards_distribution.py new file mode 100644 index 00000000..d06eb133 --- /dev/null +++ b/tests/regression/test_sdvt_splitter_rewards_distribution.py @@ -0,0 +1,138 @@ +import pytest + +from brownie import ZERO_ADDRESS +from utils.config import contracts + +from utils.test.reward_wrapper_helpers import deploy_reward_wrapper, wrap_and_split_rewards +from utils.test.split_helpers import ( + deploy_split_wallet, + get_split_percent_allocation, + get_split_percentage_scale, + split_and_withdraw_wsteth_rewards, +) + +WEI_TOLERANCE = 5 # wei tolerance to avoid rounding issue + + +# fixtures + + +@pytest.fixture(scope="module") +def cluster_participants(accounts): + CLUSTER_PARTICIPANTS = 5 + + return sorted(map(lambda participant: participant.address, accounts[0:CLUSTER_PARTICIPANTS])) + + +@pytest.fixture(scope="module") +def split_percentage_scale(): + return get_split_percentage_scale() + + +@pytest.fixture(scope="module") +def split_percent_allocation(cluster_participants, split_percentage_scale): + return get_split_percent_allocation(len(cluster_participants), split_percentage_scale) + + +@pytest.fixture(scope="module") +def split_wallet(cluster_participants, split_percent_allocation): + (deployed_contract, _) = deploy_split_wallet( + cluster_participants, split_percent_allocation, cluster_participants[0] + ) + + return deployed_contract + + +@pytest.fixture(scope="module") +def reward_wrapper(split_wallet, cluster_participants): + (deployed_contract, _) = deploy_reward_wrapper(split_wallet, cluster_participants[0]) + + return deployed_contract + + +def test_reward_wrapper_deploy(reward_wrapper, split_wallet): + """ + Test reward wrapper contract deployment + """ + connected_split_wallet = reward_wrapper.splitWallet() + assert connected_split_wallet == split_wallet.address + + steth = reward_wrapper.stETH() + assert steth == contracts.lido.address + + wsteth = reward_wrapper.wstETH() + assert wsteth == contracts.wsteth.address + + fee_share = reward_wrapper.feeShare() + fee_recipient = reward_wrapper.feeRecipient() + + with_fee = fee_share > 0 and fee_recipient != ZERO_ADDRESS + without_fee = fee_share == 0 and fee_recipient == ZERO_ADDRESS + + assert with_fee or without_fee + + +def test_split_wallet_deploy(split_wallet): + """ + Test split wallet contract deployment + """ + assert split_wallet.splitMain() == contracts.split_main.address + + +# rewards wrapping tests + + +def test_wrap_rewards(accounts, reward_wrapper): + """ + Test rewards wrapping logic + Should wrap steth rewards to wsteth and split between dvt provider and split wallet + """ + steth = contracts.lido + steth_to_distribute = 1 * 10 ** contracts.lido.decimals() + stranger = accounts[0] + + # get steth to distribute + eth_to_submit = steth_to_distribute + WEI_TOLERANCE + steth.submit(ZERO_ADDRESS, {"from": stranger, "value": eth_to_submit}) + assert steth.balanceOf(stranger) >= steth_to_distribute + + # transfer steth to wrapper contract + assert steth.balanceOf(reward_wrapper.address) == 0 + steth.transfer(reward_wrapper.address, steth_to_distribute, {"from": stranger}) + assert steth.balanceOf(reward_wrapper.address) >= steth_to_distribute - WEI_TOLERANCE + + # wrap rewards and split between dvt provider and split wallet + wrap_and_split_rewards(reward_wrapper, stranger) + + +def test_split_rewards(accounts, split_wallet, cluster_participants, split_percent_allocation, split_percentage_scale): + """ + Test separate split wallet (instance of 0xSplit protocol) contract distribution logic + Should distribute wsteth rewards between participants according to split wallet shares + """ + wsteth = contracts.wsteth + stranger = accounts[0] + + wsteth_to_distribute = 1 * 10 ** contracts.wsteth.decimals() + + # check split wallet balance initial state + split_wallet_balance_before = wsteth.balanceOf(split_wallet) + assert split_wallet_balance_before == 0 + + # get required wsteth + eth_to_submit = wsteth.getStETHByWstETH(wsteth_to_distribute) + WEI_TOLERANCE + stranger.transfer(wsteth.address, eth_to_submit) + assert wsteth.balanceOf(stranger) >= wsteth_to_distribute + + # transfer wsteth to split wallet contract + wsteth.transfer(split_wallet.address, wsteth_to_distribute, {"from": stranger}) + assert wsteth.balanceOf(split_wallet.address) == wsteth_to_distribute + + # split wsteth rewards between participants and withdraw + split_and_withdraw_wsteth_rewards( + split_wallet.address, + cluster_participants, + split_percent_allocation, + split_percentage_scale, + stranger, + ) diff --git a/utils/config.py b/utils/config.py index 270fbf11..259fc658 100644 --- a/utils/config.py +++ b/utils/config.py @@ -268,6 +268,14 @@ def anchor_vault(self) -> interface.InsuranceFund: def anchor_vault_proxy(self) -> interface.InsuranceFund: return interface.AnchorVaultProxy(ANCHOR_VAULT_PROXY) + @property + def obol_lido_split_factory(self) -> interface.ObolLidoSplitFactory: + return interface.ObolLidoSplitFactory(OBOL_LIDO_SPLIT_FACTORY) + + @property + def split_main(self) -> interface.SplitMain: + return interface.SplitMain(SPLIT_MAIN) + def __getattr__(name: str) -> Any: if name == "contracts": diff --git a/utils/test/reward_wrapper_helpers.py b/utils/test/reward_wrapper_helpers.py new file mode 100644 index 00000000..0a6345b8 --- /dev/null +++ b/utils/test/reward_wrapper_helpers.py @@ -0,0 +1,58 @@ +from utils.config import contracts +from brownie import interface, ZERO_ADDRESS + +WEI_TOLERANCE = 5 # wei tolerance to avoid rounding issue + + +def deploy_reward_wrapper(split_wallet, deployer): + factory = contracts.obol_lido_split_factory + deploy_tx = factory.createSplit(split_wallet, {"from": deployer}) + + deployed_instance_address = deploy_tx.events["CreateObolLidoSplit"]["split"] + deployed_contract = interface.ObolLidoSplit(deployed_instance_address) + + return (deployed_contract, deploy_tx) + + +def wrap_and_split_rewards(reward_wrapper, stranger): + WRAPPER_FEE_PERCENTAGE_SCALE = 10**5 # dvt provider fee percentage scale + + steth, wsteth = contracts.lido, contracts.wsteth + split_wallet = reward_wrapper.splitWallet() + + # dvt provider fee variables + dvt_provider_fee = reward_wrapper.feeShare() + dvt_provider_fee_recipient = reward_wrapper.feeRecipient() + + # check initial contract balance + reward_wrapper_balance_before = steth.balanceOf(reward_wrapper) + + steth_to_distribute = reward_wrapper_balance_before + wsteth_to_distribute = wsteth.getWstETHByStETH(steth_to_distribute) + + assert steth_to_distribute > WEI_TOLERANCE, "no steth to distribute" + assert wsteth_to_distribute > WEI_TOLERANCE, "no wsteth to distribute" + + # get split wallet balance before distribution + split_wallet_wsteth_balance_before = wsteth.balanceOf(split_wallet) + + # distribute wrapped rewards and fee + dvt_provider_wsteth_balance_before = wsteth.balanceOf(dvt_provider_fee_recipient) + + reward_wrapper.distribute({"from": stranger}) + split_wallet_wsteth_balance_after = wsteth.balanceOf(split_wallet) + dvt_provider_wsteth_balance_after = wsteth.balanceOf(dvt_provider_fee_recipient) + + # check wrapper balance after distribution + assert steth.balanceOf(reward_wrapper.address) < WEI_TOLERANCE + + # check fee charged to dvt provider + expected_fee_charged = wsteth_to_distribute * dvt_provider_fee // WRAPPER_FEE_PERCENTAGE_SCALE + dvt_provider_expected_wsteth_balance = dvt_provider_wsteth_balance_before + expected_fee_charged + assert dvt_provider_wsteth_balance_after - dvt_provider_expected_wsteth_balance <= WEI_TOLERANCE + + # check split wallet balance after distribution + split_wallet_expected_wsteth_balance = ( + split_wallet_wsteth_balance_before + wsteth_to_distribute - expected_fee_charged + ) + assert split_wallet_wsteth_balance_after - split_wallet_expected_wsteth_balance <= WEI_TOLERANCE diff --git a/utils/test/simple_dvt_helpers.py b/utils/test/simple_dvt_helpers.py index 8d4a2b18..222de7b9 100644 --- a/utils/test/simple_dvt_helpers.py +++ b/utils/test/simple_dvt_helpers.py @@ -94,6 +94,38 @@ def fill_simple_dvt_ops_vetted_keys(stranger, min_ops_cnt=MIN_OPS_CNT, min_keys_ assert no["totalVettedValidators"] == no["totalAddedValidators"] +def simple_dvt_vet_keys(operator_id, stranger): + factory = interface.SetVettedValidatorsLimits(EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY) + trusted_caller = accounts.at(EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, force=True) + + simple_dvt, easy_track = contracts.simple_dvt, contracts.easy_track + + operator = simple_dvt.getNodeOperator(operator_id, False) + + if operator["totalVettedValidators"] == operator["totalAddedValidators"]: + return + + calldata = _encode_calldata("((uint256,uint256)[])", [[(operator_id, operator["totalAddedValidators"])]]) + motions_before = easy_track.getMotions() + + tx = easy_track.createMotion(factory, calldata, {"from": trusted_caller}) + motions = easy_track.getMotions() + + assert len(motions) == len(motions_before) + 1 + + chain.sleep(60 * 60 * 24 * 3) + chain.mine() + + easy_track.enactMotion( + motions[-1][0], + tx.events["MotionCreated"]["_evmScriptCallData"], + {"from": stranger}, + ) + + operator = simple_dvt.getNodeOperator(operator_id, False) + assert operator["totalVettedValidators"] == operator["totalAddedValidators"] + + def simple_dvt_add_node_operators(simple_dvt, stranger, input_params=[]): factory = interface.AddNodeOperators(EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY) trusted_caller = accounts.at(EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, force=True) diff --git a/utils/test/split_helpers.py b/utils/test/split_helpers.py new file mode 100644 index 00000000..9609cd5f --- /dev/null +++ b/utils/test/split_helpers.py @@ -0,0 +1,114 @@ +from utils.config import contracts +from brownie import interface, ZERO_ADDRESS + +SPLIT_DISTRIBUTOR_FEE = 0 +SPLIT_CONTROLLER = ZERO_ADDRESS + +WEI_TOLERANCE = 5 # wei tolerance to avoid rounding issue + + +def deploy_split_wallet(members, percent_allocation, deployer): + factory = contracts.split_main + + deploy_tx = factory.createSplit( + members, + percent_allocation, + SPLIT_DISTRIBUTOR_FEE, + SPLIT_CONTROLLER, + {"from": deployer or members[0]}, + ) + + deployed_instance_address = deploy_tx.events["CreateSplit"]["split"] + deployed_contract = interface.SplitWallet(deployed_instance_address) + + return (deployed_contract, deploy_tx) + + +def get_split_percentage_scale(): + return contracts.split_main.PERCENTAGE_SCALE() + + +def get_split_percent_allocation(total_members, percentage_scale): + # distribute shares evenly between participants + + shares = [percentage_scale // total_members] * total_members + remainder = percentage_scale % total_members + + for i in range(remainder): + shares[i] += 1 + + return shares + + +def get_balances_on_split_main(participants, token): + split_main = contracts.split_main + + balances = [] + for participant in participants: + balance = split_main.getERC20Balance(participant, token) + balances.append(balance) + + return balances + + +def split_and_withdraw_wsteth_rewards(split_wallet, participants, percent_allocation, percentage_scale, stranger): + split_main = contracts.split_main + wsteth = contracts.wsteth + + # check split wallet balance initial state + split_wallet_balance_before = wsteth.balanceOf(split_wallet) + wsteth_to_distribute = split_wallet_balance_before + assert wsteth_to_distribute > WEI_TOLERANCE, "no wsteth to distribute" + + # collect participants balances on split main contract before distribution + participant_balances_on_split_main_before = get_balances_on_split_main(participants, wsteth) + + # distribute rewards + distribute_tx = split_main.distributeERC20( + split_wallet, + wsteth.address, + participants, + percent_allocation, + 0, + ZERO_ADDRESS, + {"from": stranger}, + ) + distribute_event = distribute_tx.events["DistributeERC20"] + assert distribute_event["split"] == split_wallet + assert distribute_event["token"] == wsteth.address + assert wsteth_to_distribute - distribute_event["amount"] <= WEI_TOLERANCE + assert distribute_event["distributorAddress"] == ZERO_ADDRESS + + # check participants balances on split main contract after distribution + participant_balances_on_split_main_after = get_balances_on_split_main(participants, wsteth) + for index, balance_on_split_main_after in enumerate(participant_balances_on_split_main_after): + balance_on_split_main_before = participant_balances_on_split_main_before[index] + participant_income = balance_on_split_main_after - balance_on_split_main_before + + expected_participant_income = wsteth_to_distribute * percent_allocation[index] // percentage_scale + + assert participant_income > 0 + assert expected_participant_income - participant_income <= WEI_TOLERANCE + + # check that all wsteth was distributed on split main contract + total_wsteth_distributed = sum(participant_balances_on_split_main_after) + assert wsteth_to_distribute - total_wsteth_distributed <= len(participants) * WEI_TOLERANCE + + # check that a participant can withdraw wsteth from split main + participant = participants[0] + participant_balance_on_split_main = participant_balances_on_split_main_after[0] + + participant_wsteth_balance_before = wsteth.balanceOf(participant) + + withdraw_eth = 0 # withdraw only erc20 + withdraw_tx = split_main.withdraw(participant, withdraw_eth, [wsteth.address], {"from": participant}) + + participant_wsteth_balance_after = wsteth.balanceOf(participant) + withdrawn_wsteth = participant_wsteth_balance_after - participant_wsteth_balance_before + + withdraw_event = withdraw_tx.events["Withdrawal"] + assert len(withdraw_event["tokens"]) == 1 + assert withdraw_event["tokens"][0] == wsteth.address + assert withdraw_event["tokenAmounts"][0] == withdrawn_wsteth + + assert participant_balance_on_split_main - withdrawn_wsteth <= WEI_TOLERANCE diff --git a/utils/test/staking_router_helpers.py b/utils/test/staking_router_helpers.py new file mode 100644 index 00000000..cfd421f2 --- /dev/null +++ b/utils/test/staking_router_helpers.py @@ -0,0 +1,17 @@ +from utils.config import contracts +from enum import Enum + + +class ModuleStatus(Enum): + ACTIVE = 0 + PAUSED = 1 + DISABLED = 2 + + +def pause_staking_module(module_id): + staking_router, deposit_security_module = contracts.staking_router, contracts.deposit_security_module + + pause_tx = staking_router.pauseStakingModule(module_id, {"from": deposit_security_module}) + pause_event = pause_tx.events["StakingModuleStatusSet"] + assert pause_event["stakingModuleId"] == module_id + assert pause_event["status"] == ModuleStatus.PAUSED.value From 85abfbd064b01885e1b4ef7be5adf09c4320fd6f Mon Sep 17 00:00:00 2001 From: KRogLA Date: Fri, 2 Feb 2024 04:30:50 +0100 Subject: [PATCH 04/68] fix: migrate simpledvt deploy vote --- configs/config_mainnet.py | 2 + interfaces/APMRegistry.json | 1 + scripts/vote_simple_dvt.py | 273 ++++++++++++++++++++++++++++++++++++ tests/test_simple_dvt.py | 213 ++++++++++++++++++++++++++++ utils/config.py | 8 +- utils/repo.py | 9 ++ 6 files changed, 504 insertions(+), 2 deletions(-) create mode 100644 interfaces/APMRegistry.json create mode 100644 tests/test_simple_dvt.py diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index efbddf2c..e6f44003 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -30,6 +30,7 @@ NODE_OPERATORS_REGISTRY_IMPL_V1 = "0x5d39ABaa161e622B99D45616afC8B837E9F19a25" # Aragon APM Repos +APM_REGISTRY = "0x0cb113890b04B49455DfE06554e2D784598A29C9" VOTING_REPO = "0x4Ee3118E3858E8D7164A634825BfE0F73d99C792" LIDO_REPO = "0xF5Dc67E54FC96F993CD06073f71ca732C1E654B1" NODE_OPERATORS_REGISTRY_REPO = "0x0D97E876ad14DB2b183CFeEB8aa1A5C788eB1831" @@ -140,6 +141,7 @@ # NodeOperatorsRegistry clone aka SimpleDVT SIMPLE_DVT_IMPL = "0x8538930c385C0438A357d2c25CB3eAD95Ab6D8ed" ## see SimpleDVT's proxy appId() +# SIMPLE_DVT_ARAGON_APP_NAME = "simple-dvt" SIMPLE_DVT_ARAGON_APP_ID = "0xe1635b63b5f7b5e545f2a637558a4029dea7905361a2f0fc28c66e9136cf86a4" SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY = 432000 SIMPLE_DVT_MODULE_TARGET_SHARE_BP = 50 diff --git a/interfaces/APMRegistry.json b/interfaces/APMRegistry.json new file mode 100644 index 00000000..598aa760 --- /dev/null +++ b/interfaces/APMRegistry.json @@ -0,0 +1 @@ +[{"constant":true,"inputs":[],"name":"hasInitialized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_script","type":"bytes"}],"name":"getEVMScriptExecutor","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"registrar","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_dev","type":"address"},{"name":"_initialSemanticVersion","type":"uint16[3]"},{"name":"_contractAddress","type":"address"},{"name":"_contentURI","type":"bytes"}],"name":"newRepoWithVersion","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getRecoveryVault","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"CREATE_REPO_ROLE","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"token","type":"address"}],"name":"allowRecoverability","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"appId","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getInitializationBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"}],"name":"transferToVault","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_sender","type":"address"},{"name":"_role","type":"bytes32"},{"name":"_params","type":"uint256[]"}],"name":"canPerform","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getEVMScriptRegistry","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_dev","type":"address"}],"name":"newRepo","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_registrar","type":"address"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_kernel","type":"address"},{"name":"_appId","type":"bytes32"},{"name":"_initializePayload","type":"bytes"}],"name":"newAppProxyPinned","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"kernel","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isPetrified","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_kernel","type":"address"},{"name":"_appId","type":"bytes32"}],"name":"newAppProxy","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_kernel","type":"address"},{"name":"_appId","type":"bytes32"},{"name":"_initializePayload","type":"bytes"}],"name":"newAppProxy","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_kernel","type":"address"},{"name":"_appId","type":"bytes32"}],"name":"newAppProxyPinned","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"bytes32"},{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"repo","type":"address"}],"name":"NewRepo","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proxy","type":"address"},{"indexed":false,"name":"isUpgradeable","type":"bool"},{"indexed":false,"name":"appId","type":"bytes32"}],"name":"NewAppProxy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"executor","type":"address"},{"indexed":false,"name":"script","type":"bytes"},{"indexed":false,"name":"input","type":"bytes"},{"indexed":false,"name":"returnData","type":"bytes"}],"name":"ScriptResult","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"vault","type":"address"},{"indexed":true,"name":"token","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"RecoverToVault","type":"event"}] diff --git a/scripts/vote_simple_dvt.py b/scripts/vote_simple_dvt.py index e69de29b..6692a59c 100644 --- a/scripts/vote_simple_dvt.py +++ b/scripts/vote_simple_dvt.py @@ -0,0 +1,273 @@ +""" +Voting SimpleDVT +""" + +import time + +from typing import Dict, List, NamedTuple +from brownie.network.transaction import TransactionReceipt +from utils.agent import agent_execute, agent_forward +from utils.kernel import update_app_implementation +from utils.repo import create_new_app_repo +from utils.voting import bake_vote_items, confirm_vote_script, create_vote +from utils.ipfs import upload_vote_ipfs_description, calculate_vote_ipfs_description +from utils.config import ( + contracts, + get_deployer_account, + get_is_live, + get_priority_fee, + SIMPLE_DVT_IMPL, + SIMPLE_DVT_ARAGON_APP_ID, + SIMPLE_DVT_MODULE_NAME, + SIMPLE_DVT_MODULE_TYPE, + SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY, + SIMPLE_DVT_MODULE_TARGET_SHARE_BP, + SIMPLE_DVT_MODULE_MODULE_FEE_BP, + SIMPLE_DVT_MODULE_TREASURY_FEE_BP, + EASYTRACK_EVMSCRIPT_EXECUTOR, + EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY, + EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY, + EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY, + EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY, + EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_NAMES_FACTORY, + EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY, + EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY, + EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY, +) +from utils.permissions import ( + encode_permission_create, + encode_permission_grant, + encode_permission_revoke, + encode_permission_grant_p, + encode_oz_grant_role, +) +from utils.permission_parameters import Param, SpecialArgumentID, encode_argument_value_if, ArgumentValue, Op +from utils.easy_track import ( + add_evmscript_factory, + create_permissions, +) + +create_simple_dvt_app = { + "name": "simple-dvt", + "new_address": SIMPLE_DVT_IMPL, + "content_uri": "0x697066733a516d615353756a484347636e4675657441504777565735426567614d42766e355343736769334c5366767261536f", + "id": SIMPLE_DVT_ARAGON_APP_ID, + "version": (1, 0, 0), + "module_type": SIMPLE_DVT_MODULE_TYPE, + "penalty_delay": SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY, +} + + +description = """ +Deploy SimpleDVT +""" + + +def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | TransactionReceipt | None]: + """Prepare and run voting.""" + + vote_desc_items, call_script_items = zip( + # + # I. Setup SimpleDVT module as new Aragon app + # + ( + "1) Create new Repo for SimpleDVT app", + create_new_app_repo( + name=create_simple_dvt_app["name"], + manager=contracts.voting, + version=create_simple_dvt_app["version"], + address=create_simple_dvt_app["new_address"], + content_uri=create_simple_dvt_app["content_uri"], + ), + ), + ( + "2) Link SimpleDVT app to 0x8538930c385C0438A357d2c25CB3eAD95Ab6D8ed implementation", + update_app_implementation(create_simple_dvt_app["id"], create_simple_dvt_app["new_address"]), + ), + ( + "3) Initialize SimpleDVT module", + ( + contracts.simple_dvt.address, + contracts.simple_dvt.initialize.encode_input( + contracts.lido_locator, + create_simple_dvt_app["module_type"], + create_simple_dvt_app["penalty_delay"], + ), + ), + ), + # + # II. Set permissions + # + ( + "4) Create and grant permission STAKING_ROUTER_ROLE on SimpleDVT module for StakingRouter", + encode_permission_create( + entity=contracts.staking_router, + target_app=contracts.simple_dvt, + permission_name="STAKING_ROUTER_ROLE", + manager=contracts.voting, + ), + ), + ( + "5) Grant STAKING_ROUTER_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor", + # 9. Grant permission for STAKING_ROUTER_ROLE of SimpleDVT app + # assigning it to StakingRouter + encode_permission_grant( + target_app=contracts.simple_dvt, + permission_name="STAKING_ROUTER_ROLE", + grant_to=EASYTRACK_EVMSCRIPT_EXECUTOR, + ), + ), + ( + "6) Create and grant permission MANAGE_NODE_OPERATOR_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor", + encode_permission_create( + entity=EASYTRACK_EVMSCRIPT_EXECUTOR, + target_app=contracts.simple_dvt, + permission_name="MANAGE_NODE_OPERATOR_ROLE", + manager=contracts.voting, + ), + ), + ( + "7) Create and grant permission SET_NODE_OPERATOR_LIMIT_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor", + encode_permission_create( + entity=EASYTRACK_EVMSCRIPT_EXECUTOR, + target_app=contracts.simple_dvt, + permission_name="SET_NODE_OPERATOR_LIMIT_ROLE", + manager=contracts.voting, + ), + ), + ( + "8) Create and grant permission MANAGE_SIGNING_KEYS on SimpleDVT module for EasyTrackEVMScriptExecutor", + encode_permission_create( + entity=EASYTRACK_EVMSCRIPT_EXECUTOR, + target_app=contracts.simple_dvt, + permission_name="MANAGE_SIGNING_KEYS", + manager=EASYTRACK_EVMSCRIPT_EXECUTOR, + ), + ), + # + # III. Add EasyTrack factories for SimpleDVT module + # + ( + "9) Add AddNodeOperators EVM script factory", + add_evmscript_factory( + factory=EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY, + permissions=( + create_permissions(contracts.simple_dvt, "addNodeOperator") + + create_permissions(contracts.acl, "grantPermissionP")[2:] + ), + ), + ), + ( + "10) Add ActivateNodeOperators EVM script factory", + add_evmscript_factory( + factory=EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY, + permissions=( + create_permissions(contracts.simple_dvt, "activateNodeOperator") + + create_permissions(contracts.acl, "grantPermissionP")[2:] + ), + ), + ), + ( + "11) Add DeactivateNodeOperators EVM script factory", + add_evmscript_factory( + factory=EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY, + permissions=( + create_permissions(contracts.simple_dvt, "deactivateNodeOperator") + + create_permissions(contracts.acl, "revokePermission")[2:] + ), + ), + ), + ( + "12) Add SetVettedValidatorsLimits EVM script factory", + add_evmscript_factory( + factory=EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY, + permissions=(create_permissions(contracts.simple_dvt, "setNodeOperatorStakingLimit")), + ), + ), + ( + "13) Add UpdateTargetValidatorLimits EVM script factory", + add_evmscript_factory( + factory=EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY, + permissions=(create_permissions(contracts.simple_dvt, "updateTargetValidatorsLimits")), + ), + ), + ( + "14) Add SetNodeOperatorNames EVM script factory", + add_evmscript_factory( + factory=EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_NAMES_FACTORY, + permissions=(create_permissions(contracts.simple_dvt, "setNodeOperatorName")), + ), + ), + ( + "15) Add SetNodeOperatorRewardAddresses EVM script factory", + add_evmscript_factory( + factory=EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY, + permissions=(create_permissions(contracts.simple_dvt, "setNodeOperatorRewardAddress")), + ), + ), + ( + "16) Add ChangeNodeOperatorManagers EVM script factory", + add_evmscript_factory( + factory=EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY, + permissions=( + create_permissions(contracts.acl, "revokePermission") + + create_permissions(contracts.acl, "grantPermissionP")[2:] + ), + ), + ), + # + # IV. Finish SimpleDVT module setup + # + ( + "17) Grant REQUEST_BURN_SHARES_ROLE on Burner for SimpleDVT module", + agent_forward( + [ + encode_oz_grant_role( + contract=contracts.burner, + role_name="REQUEST_BURN_SHARES_ROLE", + grant_to=contracts.simple_dvt, + ) + ] + ), + ), + ( + "18) Add SimpleDVT module to StakingRouter", + agent_forward( + [ + ( + contracts.staking_router.address, + contracts.staking_router.addStakingModule.encode_input( + SIMPLE_DVT_MODULE_NAME, + contracts.simple_dvt, + SIMPLE_DVT_MODULE_TARGET_SHARE_BP, + SIMPLE_DVT_MODULE_MODULE_FEE_BP, + SIMPLE_DVT_MODULE_TREASURY_FEE_BP, + ), + ), + ] + ), + ), + ) + + vote_items = bake_vote_items(list(vote_desc_items), list(call_script_items)) + + if silent: + desc_ipfs = calculate_vote_ipfs_description(description) + else: + desc_ipfs = upload_vote_ipfs_description(description) + + return confirm_vote_script(vote_items, silent, desc_ipfs) and list( + create_vote(vote_items, tx_params, desc_ipfs=desc_ipfs) + ) + + +def main(): + tx_params = {"from": get_deployer_account()} + if get_is_live(): + tx_params["priority_fee"] = get_priority_fee() + + vote_id, _ = start_vote(tx_params=tx_params, silent=False) + + vote_id >= 0 and print(f"Vote created: {vote_id}.") + + time.sleep(5) # hack for waiting thread #2. diff --git a/tests/test_simple_dvt.py b/tests/test_simple_dvt.py new file mode 100644 index 00000000..60027f7b --- /dev/null +++ b/tests/test_simple_dvt.py @@ -0,0 +1,213 @@ +""" +Tests for voting 23/01/2023 + +""" + +from typing import List +from scripts.vote_simple_dvt import start_vote +from brownie import interface, ZERO_ADDRESS, reverts, web3, accounts, convert, network +from utils.test.tx_tracing_helpers import * +from utils.test.event_validators.permission import Permission +from utils.config import contracts, LDO_HOLDER_ADDRESS_FOR_TESTS, network_name +from utils.test.helpers import almostEqWithDiff +from configs.config_mainnet import ( + SIMPLE_DVT_IMPL, + SIMPLE_DVT_ARAGON_APP_ID, + SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY, + SIMPLE_DVT_MODULE_TARGET_SHARE_BP, + SIMPLE_DVT_MODULE_MODULE_FEE_BP, + SIMPLE_DVT_MODULE_TREASURY_FEE_BP, + SIMPLE_DVT_MODULE_ID, + SIMPLE_DVT_MODULE_NAME, + SIMPLE_DVT_MODULE_TYPE, + EASYTRACK_EVMSCRIPT_EXECUTOR, + EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY, + EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY, + EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY, + EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY, + EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_NAMES_FACTORY, + EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY, + EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY, + EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY, +) +from utils.test.easy_track_helpers import create_and_enact_payment_motion, check_add_and_remove_recipient_with_voting +from utils.test.event_validators.permission import ( + Permission, + validate_grant_role_event, + validate_permission_revoke_event, + validate_permission_grantp_event, +) +from utils.test.event_validators.hash_consensus import ( + validate_hash_consensus_member_removed, + validate_hash_consensus_member_added, +) +from utils.test.event_validators.node_operators_registry import ( + validate_node_operator_deactivated, + validate_node_operator_name_set_event, + NodeOperatorNameSetItem, +) +from utils.test.event_validators.easy_track import ( + validate_evmscript_factory_added_event, + EVMScriptFactoryAdded, + validate_evmscript_factory_removed_event, +) +from utils.test.event_validators.allowed_recipients_registry import ( + validate_set_limit_parameter_event, + validate_update_spent_amount_event, +) +from utils.easy_track import create_permissions +from utils.voting import find_metadata_by_vote_id +from utils.ipfs import get_lido_vote_cid_from_str + + +REQUEST_BURN_SHARES_ROLE = "0x4be29e0e4eb91f98f709d98803cba271592782e293b84a625e025cbb40197ba8" +CREATE_VERSION_ROLE = "0x1f56cfecd3595a2e6cc1a7e6cb0b20df84cdbd92eff2fee554e70e4e45a9a7d8" +STAKING_ROUTER_ROLE = "0xbb75b874360e0bfd87f964eadd8276d8efb7c942134fc329b513032d0803e0c6" +MANAGE_NODE_OPERATOR_ROLE = "0x78523850fdd761612f46e844cf5a16bda6b3151d6ae961fd7e8e7b92bfbca7f8" +SET_NODE_OPERATOR_LIMIT_ROLE = "0x07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754" +MANAGE_SIGNING_KEYS = "0x75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee" + + +simple_dvt_repo_ens = "simple-dvt.lidopm.eth" +simple_dvt_content_uri = ( + "0x697066733a516d615353756a484347636e4675657441504777565735426567614d42766e355343736769334c5366767261536f" +) + + +def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_decoding, ldo_holder): + simple_dvt = contracts.simple_dvt + kernel = contracts.kernel + burner = contracts.burner + voting = contracts.voting + acl = contracts.acl + easy_track = contracts.easy_track + staking_router = contracts.staking_router + + assert staking_router.getStakingModulesCount() == 1 + assert kernel.getApp(kernel.APP_BASES_NAMESPACE(), SIMPLE_DVT_ARAGON_APP_ID) == ZERO_ADDRESS + assert not burner.hasRole(kernel.APP_BASES_NAMESPACE(), simple_dvt.address) + + assert not network.web3.ens.resolve(simple_dvt_repo_ens) + + # TODO: check absence of repo ? + + evm_script_factories_before = easy_track.getEVMScriptFactories() + + add_node_operators_evm_script_factory = EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY + activate_node_operators_evm_script_factory = EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY + deactivate_node_operators_evm_script_factory = EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY + set_vetted_validators_limits_evm_script_factory = EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY + set_node_operator_names_evm_script_factory = EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_NAMES_FACTORY + set_node_operator_reward_addresses_evm_script_factory = ( + EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY + ) + update_target_validator_limits_evm_script_factory = EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY + change_node_operator_managers_evm_script_factory = EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY + + assert add_node_operators_evm_script_factory not in evm_script_factories_before + assert activate_node_operators_evm_script_factory not in evm_script_factories_before + assert deactivate_node_operators_evm_script_factory not in evm_script_factories_before + assert set_vetted_validators_limits_evm_script_factory not in evm_script_factories_before + assert set_node_operator_names_evm_script_factory not in evm_script_factories_before + assert set_node_operator_reward_addresses_evm_script_factory not in evm_script_factories_before + assert update_target_validator_limits_evm_script_factory not in evm_script_factories_before + assert change_node_operator_managers_evm_script_factory not in evm_script_factories_before + + # START VOTE + if len(vote_ids_from_env) > 0: + (vote_id,) = vote_ids_from_env + else: + tx_params = {"from": LDO_HOLDER_ADDRESS_FOR_TESTS} + vote_id, _ = start_vote(tx_params, silent=True) + + vote_tx = helpers.execute_vote(accounts, vote_id, contracts.voting) + + print(f"voteId = {vote_id}, gasUsed = {vote_tx.gas_used}") + + # I. Setup SimpleDVT module as new Aragon app + assert kernel.getApp(kernel.APP_BASES_NAMESPACE(), SIMPLE_DVT_ARAGON_APP_ID) == SIMPLE_DVT_IMPL + + simple_dvt_repo = interface.Repo(network.web3.ens.resolve(simple_dvt_repo_ens)) + assert simple_dvt_repo + + # Voting has permission to update repo + assert simple_dvt_repo.canPerform(voting.address, CREATE_VERSION_ROLE, []) + + # Latest version in repo is 1st and only one + latest_ver = simple_dvt_repo.getLatest() + assert latest_ver["semanticVersion"] == (1, 0, 0) + assert latest_ver["contractAddress"] == SIMPLE_DVT_IMPL + assert latest_ver["contentURI"] == simple_dvt_content_uri + + # StakingRouter params + assert staking_router.getStakingModulesCount() == 2 + assert staking_router.hasStakingModule(SIMPLE_DVT_MODULE_ID) + + module = staking_router.getStakingModule(SIMPLE_DVT_MODULE_ID) + assert module["id"] == SIMPLE_DVT_MODULE_ID + assert module["stakingModuleAddress"] == simple_dvt.address + assert module["stakingModuleFee"] == SIMPLE_DVT_MODULE_MODULE_FEE_BP + assert module["treasuryFee"] == SIMPLE_DVT_MODULE_TREASURY_FEE_BP + assert module["targetShare"] == SIMPLE_DVT_MODULE_TARGET_SHARE_BP + # assert simple_dvt_module["status"] == simple_dvt.address + assert module["name"] == SIMPLE_DVT_MODULE_NAME + # assert simple_dvt_module["lastDepositBlock"] == vote_tx.block_number + assert module["exitedValidatorsCount"] == 0 + + # SimpleDVT app papams + assert simple_dvt.appId() == SIMPLE_DVT_ARAGON_APP_ID + assert simple_dvt.kernel() == kernel.address + assert simple_dvt.hasInitialized() + assert simple_dvt.getLocator() == contracts.lido_locator.address + assert simple_dvt.getType() == SIMPLE_DVT_MODULE_TYPE + assert simple_dvt.getStuckPenaltyDelay() == SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY + assert simple_dvt.getNodeOperatorsCount() == 0 + assert simple_dvt.getActiveNodeOperatorsCount() == 0 + assert simple_dvt.getNonce() == 0 + + module_summary = simple_dvt.getStakingModuleSummary() + assert module_summary["totalExitedValidators"] == 0 + assert module_summary["totalDepositedValidators"] == 0 + assert module_summary["depositableValidatorsCount"] == 0 + + # II. Permissions + assert acl.getPermissionManager(simple_dvt.address, STAKING_ROUTER_ROLE) == voting.address + assert simple_dvt.canPerform(staking_router.address, STAKING_ROUTER_ROLE, []) + assert simple_dvt.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, STAKING_ROUTER_ROLE, []) + + assert acl.getPermissionManager(simple_dvt.address, MANAGE_NODE_OPERATOR_ROLE) == voting.address + assert simple_dvt.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, MANAGE_NODE_OPERATOR_ROLE, []) + + assert acl.getPermissionManager(simple_dvt.address, SET_NODE_OPERATOR_LIMIT_ROLE) == voting.address + assert simple_dvt.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, SET_NODE_OPERATOR_LIMIT_ROLE, []) + + assert acl.getPermissionManager(simple_dvt.address, MANAGE_SIGNING_KEYS) == EASYTRACK_EVMSCRIPT_EXECUTOR + assert simple_dvt.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, MANAGE_SIGNING_KEYS, []) + + # III. EasyTrack factories + evm_script_factories = easy_track.getEVMScriptFactories() + + assert add_node_operators_evm_script_factory in evm_script_factories + assert activate_node_operators_evm_script_factory in evm_script_factories + assert deactivate_node_operators_evm_script_factory in evm_script_factories + assert set_vetted_validators_limits_evm_script_factory in evm_script_factories + assert set_node_operator_names_evm_script_factory in evm_script_factories + assert set_node_operator_reward_addresses_evm_script_factory in evm_script_factories + assert update_target_validator_limits_evm_script_factory in evm_script_factories + assert change_node_operator_managers_evm_script_factory in evm_script_factories + + # validate vote events + # assert count_vote_items_by_events(vote_tx, contracts.voting) == 65, "Incorrect voting items count" + + # display_voting_events(vote_tx) + + # if bypass_events_decoding or network_name() in ("goerli", "goerli-fork"): + # return + + # evs = group_voting_events(vote_tx) + + +def has_permission(permission: Permission, how: List[int]) -> bool: + return contracts.acl.hasPermission["address,address,bytes32,uint[]"]( + permission.entity, permission.app, permission.role, how + ) diff --git a/utils/config.py b/utils/config.py index 270fbf11..eaa875f1 100644 --- a/utils/config.py +++ b/utils/config.py @@ -133,8 +133,8 @@ def node_operators_registry(self) -> interface.NodeOperatorsRegistry: return interface.NodeOperatorsRegistry(NODE_OPERATORS_REGISTRY) @property - def simple_dvt(self) -> interface.NodeOperatorsRegistry: - return interface.NodeOperatorsRegistry(SIMPLE_DVT) + def simple_dvt(self) -> interface.SimpleDVT: + return interface.SimpleDVT(SIMPLE_DVT) @property def legacy_oracle(self) -> interface.LegacyOracle: @@ -200,6 +200,10 @@ def staking_router(self) -> interface.StakingRouter: def kernel(self) -> interface.Kernel: return interface.Kernel(ARAGON_KERNEL) + @property + def apm_registry(self) -> interface.APMRegistry: + return interface.APMRegistry(APM_REGISTRY) + @property def lido_app_repo(self) -> interface.Repo: return interface.Repo(LIDO_REPO) diff --git a/utils/repo.py b/utils/repo.py index ee3588f6..2ffd0d06 100644 --- a/utils/repo.py +++ b/utils/repo.py @@ -19,3 +19,12 @@ def add_implementation_to_voting_app_repo(version, address, content_uri): def add_implementation_to_oracle_app_repo(version, address, content_uri): return _add_implementation_to_repo(contracts.oracle_app_repo, version, address, content_uri) + + +def create_new_app_repo(name, manager, version, address, content_uri): + apm_registry = contracts.apm_registry + + return ( + apm_registry.address, + apm_registry.newRepoWithVersion.encode_input(name, manager, version, address, content_uri), + ) From 787fa3801ebeaec101d3865c1f5c76ab3e15fcb7 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Fri, 2 Feb 2024 04:31:31 +0100 Subject: [PATCH 05/68] fix: ET tests for simpledvt --- tests/regression/test_easy_track.py | 320 +++++++++++++++++++++++++--- 1 file changed, 286 insertions(+), 34 deletions(-) diff --git a/tests/regression/test_easy_track.py b/tests/regression/test_easy_track.py index 88d13836..9d3398ab 100644 --- a/tests/regression/test_easy_track.py +++ b/tests/regression/test_easy_track.py @@ -1,16 +1,38 @@ from eth_abi.abi import encode_single -from brownie import accounts, chain, interface +from brownie import accounts, chain, interface, convert from utils.config import ( contracts, EASYTRACK_INCREASE_NOP_STAKING_LIMIT_FACTORY, EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY, EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY, + EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY, + EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY, + EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_NAMES_FACTORY, + EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY, + EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY, + EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY, ) from utils.test.simple_dvt_helpers import simple_dvt_add_keys, simple_dvt_add_node_operators from utils.test.easy_track_helpers import _encode_calldata NODE_OPERATOR_ID = 0 +NEW_OPERATOR_NAMES = [ + "New Name 1", + "New Name 2", +] + +NEW_REWARD_ADDRESSES = [ + "0x1110000000000000000000000000000000001111", + "0x1110000000000000000000000000000000002222", +] + +NEW_MANAGERS = [ + "0x1110000000000000000000000000000011111111", + "0x1110000000000000000000000000000022222222", +] + +MANAGE_SIGNING_KEYS = "0x75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee" def test_increase_nop_staking_limit( @@ -36,13 +58,12 @@ def test_increase_nop_staking_limit( tx = contracts.easy_track.createMotion(factory, calldata, {"from": trusted_caller}) - assert len(contracts.easy_track.getMotions()) == len(motions_before) + 1 + motions = contracts.easy_track.getMotions() + assert len(motions) == len(motions_before) + 1 chain.sleep(60 * 60 * 24 * 3) chain.mine() - motions = contracts.easy_track.getMotions() - contracts.easy_track.enactMotion( motions[-1][0], tx.events["MotionCreated"]["_evmScriptCallData"], @@ -57,25 +78,11 @@ def test_increase_nop_staking_limit( def test_simple_dvt_add_node_operators( stranger, ): - NEW_OPERATOR_NAMES = [ - "New Name 1", - "New Name 2", - ] - - NEW_REWARD_ADDRESSES = [ - "0x1110000000000000000000000000000000001111", - "0x1110000000000000000000000000000000002222", - ] - - NEW_MANAGERS = [ - "0x1110000000000000000000000000000011111111", - "0x1110000000000000000000000000000022222222", - ] - input_params = [ (NEW_OPERATOR_NAMES[0], NEW_REWARD_ADDRESSES[0], NEW_MANAGERS[0]), (NEW_OPERATOR_NAMES[1], NEW_REWARD_ADDRESSES[1], NEW_MANAGERS[1]), ] + (node_operators_count_before, node_operator_count_after) = simple_dvt_add_node_operators( contracts.simple_dvt, stranger, input_params ) @@ -86,18 +93,6 @@ def test_simple_dvt_add_node_operators( def test_simple_dvt_set_vetted_validators_limits( stranger, ): - NEW_OPERATOR_NAMES = [ - "New Name 1", - ] - - NEW_REWARD_ADDRESSES = [ - "0x1110000000000000000000000000000000001111", - ] - - NEW_MANAGERS = [ - "0x1110000000000000000000000000000011111111", - ] - input_params = [ (NEW_OPERATOR_NAMES[0], NEW_REWARD_ADDRESSES[0], NEW_MANAGERS[0]), ] @@ -121,13 +116,12 @@ def test_simple_dvt_set_vetted_validators_limits( tx = contracts.easy_track.createMotion(factory, calldata, {"from": trusted_caller}) - assert len(contracts.easy_track.getMotions()) == len(motions_before) + 1 + motions = contracts.easy_track.getMotions() + assert len(motions) == len(motions_before) + 1 chain.sleep(60 * 60 * 24 * 3) chain.mine() - motions = contracts.easy_track.getMotions() - contracts.easy_track.enactMotion( motions[-1][0], tx.events["MotionCreated"]["_evmScriptCallData"], @@ -137,3 +131,261 @@ def test_simple_dvt_set_vetted_validators_limits( updated_node_operator = contracts.simple_dvt.getNodeOperator(no_id, False) assert updated_node_operator["totalVettedValidators"] == new_staking_limit + + +def test_simple_dvt_activate_deactivate_operators( + stranger, +): + op_name = NEW_OPERATOR_NAMES[0] + op_addr = NEW_REWARD_ADDRESSES[0] + op_manager = NEW_MANAGERS[0] + + (_, node_operator_count_after) = simple_dvt_add_node_operators( + contracts.simple_dvt, + stranger, + [ + (op_name, op_addr, op_manager), + ], + ) + + no_id = node_operator_count_after - 1 + + factory_activate = interface.ActivateNodeOperators(EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY) + factory_deactivate = interface.DeactivateNodeOperators(EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY) + trusted_caller = accounts.at(EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, force=True) + + is_active = contracts.simple_dvt.getNodeOperatorIsActive(no_id) + is_manager = contracts.simple_dvt.canPerform( + op_manager, + MANAGE_SIGNING_KEYS, + [convert.to_uint((0 << 248) + (1 << 240) + no_id, "uint256")], + ) + assert is_active == True + assert is_manager == True + + # deactivating + calldata = _encode_calldata("((uint256,address)[])", [[(no_id, op_manager)]]) + + motions_before = contracts.easy_track.getMotions() + + tx = contracts.easy_track.createMotion(factory_deactivate, calldata, {"from": trusted_caller}) + + motions = contracts.easy_track.getMotions() + assert len(motions) == len(motions_before) + 1 + + chain.sleep(60 * 60 * 24 * 3) + chain.mine() + + contracts.easy_track.enactMotion( + motions[-1][0], + tx.events["MotionCreated"]["_evmScriptCallData"], + {"from": stranger}, + ) + + is_active = contracts.simple_dvt.getNodeOperatorIsActive(no_id) + is_manager = contracts.simple_dvt.canPerform( + op_manager, + MANAGE_SIGNING_KEYS, + [convert.to_uint((0 << 248) + (1 << 240) + no_id, "uint256")], + ) + assert is_active == False + assert is_manager == False + + # activating + # calldata = _encode_calldata("((uint256,address)[])", [[(no_id, op_manager)]]) + + motions_before = contracts.easy_track.getMotions() + + tx = contracts.easy_track.createMotion(factory_activate, calldata, {"from": trusted_caller}) + + motions = contracts.easy_track.getMotions() + assert len(motions) == len(motions_before) + 1 + + chain.sleep(60 * 60 * 24 * 3) + chain.mine() + + contracts.easy_track.enactMotion( + motions[-1][0], + tx.events["MotionCreated"]["_evmScriptCallData"], + {"from": stranger}, + ) + + is_active = contracts.simple_dvt.getNodeOperatorIsActive(no_id) + is_manager = contracts.simple_dvt.canPerform( + op_manager, + MANAGE_SIGNING_KEYS, + [convert.to_uint((0 << 248) + (1 << 240) + no_id, "uint256")], + ) + assert is_active == True + assert is_manager == True + + +def test_simple_dvt_set_operator_name_reward_address( + stranger, +): + op_name = NEW_OPERATOR_NAMES[0] + op_addr = NEW_REWARD_ADDRESSES[0] + op_manager = NEW_MANAGERS[0] + op_name_upd = NEW_OPERATOR_NAMES[1] + op_addr_upd = NEW_REWARD_ADDRESSES[1] + + (_, node_operator_count_after) = simple_dvt_add_node_operators( + contracts.simple_dvt, + stranger, + [ + (op_name, op_addr, op_manager), + ], + ) + + no_id = node_operator_count_after - 1 + + factory_name = interface.SetNodeOperatorNames(EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_NAMES_FACTORY) + factory_addr = interface.SetNodeOperatorRewardAddresses( + EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY + ) + trusted_caller = accounts.at(EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, force=True) + + node_operator = contracts.simple_dvt.getNodeOperator(no_id, True) + + assert node_operator["name"] == op_name and node_operator["name"] != op_name_upd + assert node_operator["rewardAddress"] == op_addr and node_operator["rewardAddress"] != op_addr_upd + + calldata_name = _encode_calldata("((uint256,string)[])", [[(no_id, op_name_upd)]]) + calldata_addr = _encode_calldata("((uint256,address)[])", [[(no_id, op_addr_upd)]]) + + motions_before = contracts.easy_track.getMotions() + + tx1 = contracts.easy_track.createMotion(factory_name, calldata_name, {"from": trusted_caller}) + tx2 = contracts.easy_track.createMotion(factory_addr, calldata_addr, {"from": trusted_caller}) + + motions = contracts.easy_track.getMotions() + assert len(motions) == len(motions_before) + 2 + + chain.sleep(60 * 60 * 24 * 3) + chain.mine() + + contracts.easy_track.enactMotion( + motions[-2][0], + tx1.events["MotionCreated"]["_evmScriptCallData"], + {"from": stranger}, + ) + contracts.easy_track.enactMotion( + motions[-1][0], + tx2.events["MotionCreated"]["_evmScriptCallData"], + {"from": stranger}, + ) + + node_operator = contracts.simple_dvt.getNodeOperator(no_id, True) + + assert node_operator["name"] == op_name_upd and node_operator["name"] != op_name + assert node_operator["rewardAddress"] == op_addr_upd and node_operator["rewardAddress"] != op_addr + + +def test_simple_dvt_set_operator_target_limit( + stranger, +): + op_name = NEW_OPERATOR_NAMES[0] + op_addr = NEW_REWARD_ADDRESSES[0] + op_manager = NEW_MANAGERS[0] + + target_limit = 2 + + (_, node_operator_count_after) = simple_dvt_add_node_operators( + contracts.simple_dvt, + stranger, + [ + (op_name, op_addr, op_manager), + ], + ) + + no_id = node_operator_count_after - 1 + + factory = interface.UpdateTargetValidatorLimits(EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY) + trusted_caller = accounts.at(EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, force=True) + + no_summary = contracts.simple_dvt.getNodeOperatorSummary(no_id) + + assert no_summary["isTargetLimitActive"] == False + assert no_summary["targetValidatorsCount"] == 0 + + calldata = _encode_calldata("((uint256,bool,uint256)[])", [[(no_id, True, target_limit)]]) + + motions_before = contracts.easy_track.getMotions() + + tx = contracts.easy_track.createMotion(factory, calldata, {"from": trusted_caller}) + + motions = contracts.easy_track.getMotions() + assert len(motions) == len(motions_before) + 1 + + chain.sleep(60 * 60 * 24 * 3) + chain.mine() + + contracts.easy_track.enactMotion( + motions[-1][0], + tx.events["MotionCreated"]["_evmScriptCallData"], + {"from": stranger}, + ) + + no_summary = contracts.simple_dvt.getNodeOperatorSummary(no_id) + + assert no_summary["isTargetLimitActive"] == True + assert no_summary["targetValidatorsCount"] == target_limit + + +def test_simple_dvt_change_operator_manager( + stranger, +): + op_name = NEW_OPERATOR_NAMES[0] + op_addr = NEW_REWARD_ADDRESSES[0] + op_manager = NEW_MANAGERS[0] + op_manager_upd = NEW_MANAGERS[1] + + (_, node_operator_count_after) = simple_dvt_add_node_operators( + contracts.simple_dvt, + stranger, + [ + (op_name, op_addr, op_manager), + ], + ) + + no_id = node_operator_count_after - 1 + + factory = interface.ChangeNodeOperatorManagers(EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY) + trusted_caller = accounts.at(EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, force=True) + + perm_param = [convert.to_uint((0 << 248) + (1 << 240) + no_id, "uint256")] + is_manager = contracts.simple_dvt.canPerform( + op_manager, + MANAGE_SIGNING_KEYS, + perm_param, + ) + is_manager_upd = contracts.simple_dvt.canPerform(op_manager_upd, MANAGE_SIGNING_KEYS, perm_param) + + assert is_manager == True and is_manager_upd == False + + calldata = _encode_calldata("((uint256,address,address)[])", [[(no_id, op_manager, op_manager_upd)]]) + + motions_before = contracts.easy_track.getMotions() + + tx = contracts.easy_track.createMotion(factory, calldata, {"from": trusted_caller}) + + motions = contracts.easy_track.getMotions() + assert len(motions) == len(motions_before) + 1 + + chain.sleep(60 * 60 * 24 * 3) + chain.mine() + + contracts.easy_track.enactMotion( + motions[-1][0], + tx.events["MotionCreated"]["_evmScriptCallData"], + {"from": stranger}, + ) + + is_manager = contracts.simple_dvt.canPerform( + op_manager, + MANAGE_SIGNING_KEYS, + perm_param, + ) + is_manager_upd = contracts.simple_dvt.canPerform(op_manager_upd, MANAGE_SIGNING_KEYS, perm_param) + + assert is_manager == False and is_manager_upd == True From 4c509c9f0104e84e11852e2b9e5d9d211e1c344f Mon Sep 17 00:00:00 2001 From: KRogLA Date: Fri, 2 Feb 2024 14:46:18 +0100 Subject: [PATCH 06/68] fix: add simple dvt to round happy path --- tests/regression/test_all_round_happy_path.py | 98 ++++++++++++++----- 1 file changed, 75 insertions(+), 23 deletions(-) diff --git a/tests/regression/test_all_round_happy_path.py b/tests/regression/test_all_round_happy_path.py index b90b5cd2..0b473c00 100644 --- a/tests/regression/test_all_round_happy_path.py +++ b/tests/regression/test_all_round_happy_path.py @@ -11,6 +11,7 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): amount = ETH(100) max_deposit = 150 curated_module_id = 1 + simple_dvt_module_id = 2 """ report """ while contracts.withdrawal_queue.getLastRequestId() != contracts.withdrawal_queue.getLastFinalizedRequestId(): @@ -91,34 +92,59 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): assert contracts.lido.getDepositableEther() == buffered_ether_after_submit - withdrawal_unfinalized_steth - deposit_tx = contracts.lido.deposit(max_deposit, curated_module_id, "0x0", {"from": dsm}) + deposit_tx_nor = contracts.lido.deposit(max_deposit, curated_module_id, "0x0", {"from": dsm}) + deposit_tx_sdvt = contracts.lido.deposit(max_deposit, simple_dvt_module_id, "0x0", {"from": dsm}) + buffered_ether_after_deposit = contracts.lido.getBufferedEther() - unbuffered_event = deposit_tx.events["Unbuffered"] - deposit_validators_changed_event = deposit_tx.events["DepositedValidatorsChanged"] + unbuffered_event_nor = deposit_tx_nor.events["Unbuffered"] + deposit_validators_changed_event_nor = deposit_tx_nor.events["DepositedValidatorsChanged"] + + unbuffered_event_sdvt = deposit_tx_sdvt.events["Unbuffered"] + deposit_validators_changed_event_sdvt = deposit_tx_sdvt.events["DepositedValidatorsChanged"] - deposits_count = math.floor(unbuffered_event["amount"] / ETH(32)) + deposits_count = math.floor(unbuffered_event_nor["amount"] / ETH(32)) + math.floor( + unbuffered_event_sdvt["amount"] / ETH(32) + ) + + assert ( + buffered_ether_after_deposit + == buffered_ether_after_submit - unbuffered_event_nor["amount"] - unbuffered_event_sdvt["amount"] + ) - assert buffered_ether_after_deposit == buffered_ether_after_submit - unbuffered_event["amount"] + # get total deposited validators count from the last deposit even assert ( - deposit_validators_changed_event["depositedValidators"] == deposited_validators_before_deposit + deposits_count + deposit_validators_changed_event_sdvt["depositedValidators"] + == deposited_validators_before_deposit + deposits_count ) # Rebasing (Increasing balance) treasury = contracts.lido_locator.treasury() nor = contracts.node_operators_registry.address + sdvt = contracts.simple_dvt.address nor_operators_count = contracts.node_operators_registry.getNodeOperatorsCount() + sdvt_operators_count = contracts.simple_dvt.getNodeOperatorsCount() - penalized_node_operator_ids = [] + penalized_node_operator_ids_nor = [] for i in range(nor_operators_count): no = contracts.node_operators_registry.getNodeOperator(i, True) is_node_operator_penalized = contracts.node_operators_registry.isOperatorPenalized(i) if is_node_operator_penalized: - penalized_node_operator_ids.append(i) + penalized_node_operator_ids_nor.append(i) if not no["totalDepositedValidators"] or no["totalDepositedValidators"] == no["totalExitedValidators"]: nor_operators_count = nor_operators_count - 1 + + penalized_node_operator_ids_sdvt = [] + for i in range(sdvt_operators_count): + no = contracts.simple_dvt.getNodeOperator(i, True) + is_node_operator_penalized = contracts.simple_dvt.isOperatorPenalized(i) + if is_node_operator_penalized: + penalized_node_operator_ids_sdvt.append(i) + if not no["totalDepositedValidators"] or no["totalDepositedValidators"] == no["totalExitedValidators"]: + sdvt_operators_count = sdvt_operators_count - 1 + treasury_balance_before_rebase = contracts.lido.sharesOf(treasury) report_tx, extra_tx = oracle_report(cl_diff=ETH(100)) @@ -129,19 +155,30 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): token_rebased_event = report_tx.events["TokenRebased"] transfer_event = report_tx.events["Transfer"] - expected_transfers_count = ( - nor_operators_count if len(penalized_node_operator_ids) == 0 else nor_operators_count + 1 + # if no penalized ops: transfers count = number of active validators + # otherwise: transfers count = number of active validators + 1 transfer to burner + expected_transfers_count_nor = ( + nor_operators_count if len(penalized_node_operator_ids_nor) == 0 else nor_operators_count + 1 ) - if len(penalized_node_operator_ids) > 0: - burner_transfers = 0 - for e in extra_tx.events["Transfer"]: - if e["to"] == contracts.burner: - burner_transfers += 1 - assert burner_transfers == 1 + expected_transfers_count_sdvt = ( + sdvt_operators_count if len(penalized_node_operator_ids_sdvt) == 0 else sdvt_operators_count + 1 + ) + + burner_transfers = 0 + expected_burner_transfers = 0 + if len(penalized_node_operator_ids_nor) > 0: + expected_burner_transfers += 1 + if len(penalized_node_operator_ids_sdvt) > 0: + expected_burner_transfers += 1 + + for e in extra_tx.events["Transfer"]: + if e["to"] == contracts.burner: + burner_transfers += 1 + assert burner_transfers == expected_burner_transfers assert ( - extra_tx.events.count("Transfer") == expected_transfers_count - ), "extra_tx.events should have Transfer to all active operators, check activity condition above" + extra_tx.events.count("Transfer") == expected_transfers_count_nor + expected_transfers_count_sdvt + ), "extra_tx.events should have Transfer to all active operators (+1 optional to Burner), check activity condition above" assert report_tx.events.count("TokenRebased") == 1 assert report_tx.events.count("WithdrawalsFinalized") == 1 assert report_tx.events.count("StETHBurnt") == 1 @@ -152,19 +189,23 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): + token_rebased_event["sharesMintedAsFees"] - report_tx.events["StETHBurnt"]["amountOfShares"] ) - assert transfer_event[0]["from"] == contracts.withdrawal_queue assert transfer_event[0]["to"] == contracts.burner + # curated module assert transfer_event[1]["from"] == ZERO_ADDRESS assert transfer_event[1]["to"] == nor + # simple dvt module assert transfer_event[2]["from"] == ZERO_ADDRESS - assert transfer_event[2]["to"] == treasury + assert transfer_event[2]["to"] == sdvt + + assert transfer_event[3]["from"] == ZERO_ADDRESS + assert transfer_event[3]["to"] == treasury assert almostEqEth( treasury_balance_after_rebase, - treasury_balance_before_rebase + contracts.lido.getSharesByPooledEth(transfer_event[2]["value"]), + treasury_balance_before_rebase + contracts.lido.getSharesByPooledEth(transfer_event[3]["value"]), ) assert treasury_balance_after_rebase > treasury_balance_before_rebase @@ -187,6 +228,9 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): last_request_id_before = contracts.withdrawal_queue.getLastRequestId() + # get accidentally unaccounted stETH shares on WQ contract + uncounted_steth_shares = contracts.lido.sharesOf(contracts.withdrawal_queue) + withdrawal_request_tx = contracts.withdrawal_queue.requestWithdrawals( [amount_with_rewards], stranger, {"from": stranger} ) @@ -209,6 +253,7 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): assert withdrawal_request_event["amountOfStETH"] == amount_with_rewards steth_balance_after_withdrawal_request = contracts.lido.balanceOf(stranger) + [(_, _, _, _, finalized, _)] = contracts.withdrawal_queue.getWithdrawalStatus(request_ids) assert almostEqEth(steth_balance_after_withdrawal_request, steth_balance_after_rebase - amount_with_rewards) @@ -218,7 +263,11 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): # Rebasing (Withdrawal finalization) - assert almostEqEth(contracts.lido.balanceOf(contracts.withdrawal_queue), amount_with_rewards) + # calc uncounted steth balance value + uncounted_steth_balance = contracts.lido.getPooledEthByShares(uncounted_steth_shares) + assert almostEqEth( + contracts.lido.balanceOf(contracts.withdrawal_queue), amount_with_rewards + uncounted_steth_balance + ) locked_ether_amount_before_finalization = contracts.withdrawal_queue.getLockedEtherAmount() report_tx, _ = oracle_report(cl_diff=ETH(100)) @@ -226,7 +275,10 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): locked_ether_amount_after_finalization = contracts.withdrawal_queue.getLockedEtherAmount() withdrawal_finalized_event = report_tx.events["WithdrawalsFinalized"] - assert contracts.lido.balanceOf(contracts.withdrawal_queue) == 0 + # recalc uncounted steth balance new value + uncounted_steth_balance = contracts.lido.getPooledEthByShares(uncounted_steth_shares) + assert contracts.lido.balanceOf(contracts.withdrawal_queue) == uncounted_steth_balance + assert withdrawal_finalized_event["amountOfETHLocked"] == amount_with_rewards assert withdrawal_finalized_event["from"] == request_ids[0] assert withdrawal_finalized_event["to"] == request_ids[0] From c5b0303b7ef5b69df246494c1f57d01a80401ab7 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Fri, 2 Feb 2024 14:53:18 +0100 Subject: [PATCH 07/68] fix: withdrawal test --- tests/regression/test_all_round_happy_path.py | 10 ++++++---- tests/regression/test_withdrawal_happy_path.py | 5 ++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/regression/test_all_round_happy_path.py b/tests/regression/test_all_round_happy_path.py index 0b473c00..ae0fa5ba 100644 --- a/tests/regression/test_all_round_happy_path.py +++ b/tests/regression/test_all_round_happy_path.py @@ -20,6 +20,9 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): # stake new ether to increase buffer contracts.lido.submit(ZERO_ADDRESS, {"from": eth_whale.address, "value": ETH(10000)}) + # get accidentally unaccounted stETH shares on WQ contract + uncounted_steth_shares = contracts.lido.sharesOf(contracts.withdrawal_queue) + contracts.lido.approve(contracts.withdrawal_queue.address, 1000, {"from": steth_holder}) contracts.withdrawal_queue.requestWithdrawals([1000], steth_holder, {"from": steth_holder}) @@ -98,9 +101,11 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): buffered_ether_after_deposit = contracts.lido.getBufferedEther() unbuffered_event_nor = deposit_tx_nor.events["Unbuffered"] - deposit_validators_changed_event_nor = deposit_tx_nor.events["DepositedValidatorsChanged"] + # deposit_validators_changed_event_nor = deposit_tx_nor.events["DepositedValidatorsChanged"] unbuffered_event_sdvt = deposit_tx_sdvt.events["Unbuffered"] + + # we need just last one event deposit_validators_changed_event_sdvt = deposit_tx_sdvt.events["DepositedValidatorsChanged"] deposits_count = math.floor(unbuffered_event_nor["amount"] / ETH(32)) + math.floor( @@ -228,9 +233,6 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): last_request_id_before = contracts.withdrawal_queue.getLastRequestId() - # get accidentally unaccounted stETH shares on WQ contract - uncounted_steth_shares = contracts.lido.sharesOf(contracts.withdrawal_queue) - withdrawal_request_tx = contracts.withdrawal_queue.requestWithdrawals( [amount_with_rewards], stranger, {"from": stranger} ) diff --git a/tests/regression/test_withdrawal_happy_path.py b/tests/regression/test_withdrawal_happy_path.py index 26e18464..6a29dc98 100644 --- a/tests/regression/test_withdrawal_happy_path.py +++ b/tests/regression/test_withdrawal_happy_path.py @@ -24,6 +24,9 @@ def test_withdraw(steth_holder, eth_whale): # stake new ether to increase buffer contracts.lido.submit(ZERO_ADDRESS, {"from": eth_whale.address, "value": ETH(10000)}) + # get accidentally unaccounted stETH shares on WQ contract + uncounted_steth_shares = contracts.lido.sharesOf(contracts.withdrawal_queue) + """ pre request """ no_requests = contracts.withdrawal_queue.getWithdrawalRequests(steth_holder, {"from": steth_holder}) assert len(no_requests) == 0 @@ -43,7 +46,7 @@ def test_withdraw(steth_holder, eth_whale): ) steth_balance_after = steth_balance(steth_holder) - shares_to_burn = contracts.lido.sharesOf(contracts.withdrawal_queue) + shares_to_burn = contracts.lido.sharesOf(contracts.withdrawal_queue) - uncounted_steth_shares # post request checks assert almostEqWithDiff(steth_balance_before - steth_balance_after, REQUESTS_SUM, 2 * REQUESTS_COUNT) From fcbba2209b9e00dae79051aeb41d40773dbc8e76 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Fri, 2 Feb 2024 15:20:28 +0100 Subject: [PATCH 08/68] fix: sdvt happy path --- tests/regression/test_sdvt_rewards_happy_path.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/regression/test_sdvt_rewards_happy_path.py b/tests/regression/test_sdvt_rewards_happy_path.py index 6abc1197..3fab757e 100644 --- a/tests/regression/test_sdvt_rewards_happy_path.py +++ b/tests/regression/test_sdvt_rewards_happy_path.py @@ -107,13 +107,14 @@ def test_rewards_distribution_happy_path(simple_dvt_module_id, cluster_participa buffered_ether_before_submit = lido.getBufferedEther() withdrawal_unfinalized_steth = withdrawal_queue.unfinalizedStETH() - required_buffer = max(0, withdrawal_unfinalized_steth - buffered_ether_before_submit) - required_buffer += deposits_count * deposit_size + WEI_TOLERANCE + eth_to_deposit = deposits_count * deposit_size + eth_debt = max(0, withdrawal_unfinalized_steth - buffered_ether_before_submit) + eth_to_submit = eth_to_deposit + eth_debt + WEI_TOLERANCE eth_whale = accounts.at(staking_router.DEPOSIT_CONTRACT(), force=True) - lido.submit(ZERO_ADDRESS, {"from": eth_whale, "value": required_buffer}) + lido.submit(ZERO_ADDRESS, {"from": eth_whale, "value": eth_to_submit}) - assert lido.getDepositableEther() >= required_buffer + assert lido.getDepositableEther() >= eth_to_deposit # deposit to simple dvt module_summary_before = staking_router.getStakingModuleSummary(simple_dvt_module_id) From 6ced0f95b0c561a17a3bd129dcbd1242ee383fea Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Wed, 7 Feb 2024 16:35:58 +0700 Subject: [PATCH 09/68] feat: add easy track factories check --- tests/test_simple_dvt.py | 85 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/tests/test_simple_dvt.py b/tests/test_simple_dvt.py index 60027f7b..c21aa325 100644 --- a/tests/test_simple_dvt.py +++ b/tests/test_simple_dvt.py @@ -67,6 +67,8 @@ SET_NODE_OPERATOR_LIMIT_ROLE = "0x07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754" MANAGE_SIGNING_KEYS = "0x75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee" +# TODO: check trusted caller address +TRUSTED_CALLER = "0x08637515E85A4633E23dfc7861e2A9f53af640f7" simple_dvt_repo_ens = "simple-dvt.lidopm.eth" simple_dvt_content_uri = ( @@ -196,6 +198,88 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco assert update_target_validator_limits_evm_script_factory in evm_script_factories assert change_node_operator_managers_evm_script_factory in evm_script_factories + evs = group_voting_events(vote_tx) + + validate_evmscript_factory_added_event( + evs[8], + EVMScriptFactoryAdded( + factory_addr=add_node_operators_evm_script_factory, + permissions=create_permissions(simple_dvt, "addNodeOperator") + + create_permissions(contracts.acl, "grantPermissionP")[2:] + ) + ) + validate_evmscript_factory_added_event( + evs[9], + EVMScriptFactoryAdded( + factory_addr=activate_node_operators_evm_script_factory, + permissions=create_permissions(simple_dvt, "activateNodeOperator") + + create_permissions(contracts.acl, "grantPermissionP")[2:] + ) + ) + validate_evmscript_factory_added_event( + evs[10], + EVMScriptFactoryAdded( + factory_addr=deactivate_node_operators_evm_script_factory, + permissions=create_permissions(simple_dvt, "deactivateNodeOperator") + + create_permissions(contracts.acl, "revokePermission")[2:] + ) + ) + validate_evmscript_factory_added_event( + evs[11], + EVMScriptFactoryAdded( + factory_addr=set_vetted_validators_limits_evm_script_factory, + permissions=create_permissions(simple_dvt, "setNodeOperatorStakingLimit") + ) + ) + validate_evmscript_factory_added_event( + evs[12], + EVMScriptFactoryAdded( + factory_addr=update_target_validator_limits_evm_script_factory, + permissions=create_permissions(simple_dvt, "updateTargetValidatorsLimits") + ) + ) + validate_evmscript_factory_added_event( + evs[13], + EVMScriptFactoryAdded( + factory_addr=set_node_operator_names_evm_script_factory, + permissions=create_permissions(simple_dvt, "setNodeOperatorName") + ) + ) + validate_evmscript_factory_added_event( + evs[14], + EVMScriptFactoryAdded( + factory_addr=set_node_operator_reward_addresses_evm_script_factory, + permissions=create_permissions(simple_dvt, "setNodeOperatorRewardAddress") + ) + ) + validate_evmscript_factory_added_event( + evs[15], + EVMScriptFactoryAdded( + factory_addr=change_node_operator_managers_evm_script_factory, + permissions=create_permissions(contracts.acl, "revokePermission") + + create_permissions(contracts.acl, "grantPermissionP")[2:] + ) + ) + + assert interface.AddNodeOperators(add_node_operators_evm_script_factory).nodeOperatorsRegistry() == simple_dvt + assert interface.AddNodeOperators(add_node_operators_evm_script_factory).trustedCaller() == TRUSTED_CALLER + assert interface.ActivateNodeOperators(activate_node_operators_evm_script_factory).nodeOperatorsRegistry() == simple_dvt + assert interface.ActivateNodeOperators(activate_node_operators_evm_script_factory).trustedCaller() == TRUSTED_CALLER + assert interface.DeactivateNodeOperators(deactivate_node_operators_evm_script_factory).nodeOperatorsRegistry() == simple_dvt + assert interface.DeactivateNodeOperators(deactivate_node_operators_evm_script_factory).trustedCaller() == TRUSTED_CALLER + assert interface.SetVettedValidatorsLimits(set_vetted_validators_limits_evm_script_factory).nodeOperatorsRegistry() == simple_dvt + assert interface.SetVettedValidatorsLimits(set_vetted_validators_limits_evm_script_factory).trustedCaller() == TRUSTED_CALLER + assert interface.SetNodeOperatorNames(set_node_operator_names_evm_script_factory).nodeOperatorsRegistry() == simple_dvt + assert interface.SetNodeOperatorNames(set_node_operator_names_evm_script_factory).trustedCaller() == TRUSTED_CALLER + assert interface.SetNodeOperatorRewardAddresses(set_node_operator_reward_addresses_evm_script_factory).nodeOperatorsRegistry() == simple_dvt + assert interface.SetNodeOperatorRewardAddresses(set_node_operator_reward_addresses_evm_script_factory).trustedCaller() == TRUSTED_CALLER + assert interface.UpdateTargetValidatorLimits(update_target_validator_limits_evm_script_factory).nodeOperatorsRegistry() == simple_dvt + assert interface.UpdateTargetValidatorLimits(update_target_validator_limits_evm_script_factory).trustedCaller() == TRUSTED_CALLER + assert interface.ChangeNodeOperatorManagers(change_node_operator_managers_evm_script_factory).nodeOperatorsRegistry() == simple_dvt + assert interface.ChangeNodeOperatorManagers(change_node_operator_managers_evm_script_factory).trustedCaller() == TRUSTED_CALLER + + + # validate vote events # assert count_vote_items_by_events(vote_tx, contracts.voting) == 65, "Incorrect voting items count" @@ -204,7 +288,6 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco # if bypass_events_decoding or network_name() in ("goerli", "goerli-fork"): # return - # evs = group_voting_events(vote_tx) def has_permission(permission: Permission, how: List[int]) -> bool: From 9c4411c36af74818b90b219dc4b26629a1098a4d Mon Sep 17 00:00:00 2001 From: George Avsetsin Date: Wed, 7 Feb 2024 16:42:10 +0300 Subject: [PATCH 10/68] add stake distribution test --- .../test_sdvt_rewards_happy_path.py | 20 +--- .../test_staking_router_stake_distribution.py | 103 ++++++++++++++++++ utils/test/deposits_helpers.py | 24 ++++ 3 files changed, 129 insertions(+), 18 deletions(-) create mode 100644 tests/regression/test_staking_router_stake_distribution.py create mode 100644 utils/test/deposits_helpers.py diff --git a/tests/regression/test_sdvt_rewards_happy_path.py b/tests/regression/test_sdvt_rewards_happy_path.py index 3fab757e..87a34e37 100644 --- a/tests/regression/test_sdvt_rewards_happy_path.py +++ b/tests/regression/test_sdvt_rewards_happy_path.py @@ -1,9 +1,9 @@ import pytest -from brownie import accounts, ZERO_ADDRESS from utils.test.helpers import ETH from utils.config import contracts +from utils.test.deposits_helpers import fill_deposit_buffer from utils.test.reward_wrapper_helpers import deploy_reward_wrapper, wrap_and_split_rewards from utils.test.split_helpers import ( deploy_split_wallet, @@ -16,9 +16,6 @@ from utils.test.oracle_report_helpers import oracle_report -WEI_TOLERANCE = 5 # wei tolerance to avoid rounding issue - - # fixtures @@ -71,7 +68,6 @@ def test_rewards_distribution_happy_path(simple_dvt_module_id, cluster_participa """ simple_dvt, staking_router = contracts.simple_dvt, contracts.staking_router lido, deposit_security_module = contracts.lido, contracts.deposit_security_module - withdrawal_queue = contracts.withdrawal_queue stranger = cluster_participants[0] @@ -102,19 +98,7 @@ def test_rewards_distribution_happy_path(simple_dvt_module_id, cluster_participa # fill the deposit buffer deposits_count = 10 - deposit_size = ETH(32) - - buffered_ether_before_submit = lido.getBufferedEther() - withdrawal_unfinalized_steth = withdrawal_queue.unfinalizedStETH() - - eth_to_deposit = deposits_count * deposit_size - eth_debt = max(0, withdrawal_unfinalized_steth - buffered_ether_before_submit) - eth_to_submit = eth_to_deposit + eth_debt + WEI_TOLERANCE - - eth_whale = accounts.at(staking_router.DEPOSIT_CONTRACT(), force=True) - lido.submit(ZERO_ADDRESS, {"from": eth_whale, "value": eth_to_submit}) - - assert lido.getDepositableEther() >= eth_to_deposit + fill_deposit_buffer(deposits_count) # deposit to simple dvt module_summary_before = staking_router.getStakingModuleSummary(simple_dvt_module_id) diff --git a/tests/regression/test_staking_router_stake_distribution.py b/tests/regression/test_staking_router_stake_distribution.py new file mode 100644 index 00000000..8686d253 --- /dev/null +++ b/tests/regression/test_staking_router_stake_distribution.py @@ -0,0 +1,103 @@ +from utils.config import contracts +from utils.test.deposits_helpers import fill_deposit_buffer +from utils.test.staking_router_helpers import ModuleStatus + + +class Module: + def __init__(self, id, target_share, status, active_keys, depositable_keys): + self.id = id + self.target_share = target_share + self.status = status + self.active_keys = active_keys + self.depositable_keys = depositable_keys + self.allocated_keys = 0 + self.allocation_limit = 0 + + +def test_stake_distribution(): + """ + Test stake distribution among the staking modules + 1. checks that result of `getDepositsAllocation` matches the local allocation calculations + 2. checks that deposits to modules can be made according to the calculated allocation + """ + lido, deposit_security_module = contracts.lido, contracts.deposit_security_module + + staking_router = contracts.staking_router + module_digests = staking_router.getAllStakingModuleDigests() + + stake_to_allocate = 100 # keys to allocate to the modules + allocation_from_contract = staking_router.getDepositsAllocation(stake_to_allocate) + + # collect the modules information + + modules = {} + + for digest in module_digests: + (_, _, state, summary) = digest + (id, _, _, _, target_share, status, _, _, _, _) = state + (exited_keys, deposited_keys, depositable_keys) = summary + + active_keys = deposited_keys - exited_keys + assert active_keys >= 0 + + if status != ModuleStatus.ACTIVE.value: + # reset depositable keys in case of module is inactivated + # https://github.com/lidofinance/lido-dao/blob/331ecec7fe3c8d57841fd73ccca7fb1cc9bc174e/contracts/0.8.9/StakingRouter.sol#L1230-L1232 + depositable_keys = 0 + + modules[id] = Module(id, target_share, status, active_keys, depositable_keys) + + total_active_keys = sum([module.active_keys for module in modules.values()]) + + # simulate target share distribution + # https://github.com/lidofinance/lido-dao/blob/331ecec7fe3c8d57841fd73ccca7fb1cc9bc174e/contracts/0.8.9/StakingRouter.sol#L1266-L1268 + + target_total_active_keys = total_active_keys + stake_to_allocate + total_basis_points = staking_router.TOTAL_BASIS_POINTS() + + for module in modules.values(): + target_active_keys = module.target_share * target_total_active_keys // total_basis_points + module.allocation_limit = min(target_active_keys, module.active_keys + module.depositable_keys) + + # simulate min first strategy + # https://github.com/lidofinance/lido-dao/blob/331ecec7fe3c8d57841fd73ccca7fb1cc9bc174e/contracts/0.8.9/StakingRouter.sol#L1274 + + for _ in range(stake_to_allocate): + # find the module with the lowest active_keys + min_active_keys = modules[1].active_keys + min_active_keys_module = modules[1] + + for module in modules.values(): + if module.active_keys < min_active_keys and module.active_keys < module.allocation_limit: + min_active_keys = module.active_keys + min_active_keys_module = module + + # allocate one key to the module if possible + if min_active_keys_module.active_keys < min_active_keys_module.allocation_limit: + min_active_keys_module.active_keys += 1 + min_active_keys_module.allocated_keys += 1 + + total_allocated_keys = sum([module.allocated_keys for module in modules.values()]) + + # check that local allocation matches the contract allocation + assert allocation_from_contract == (total_allocated_keys, [module.active_keys for module in modules.values()]) + + # fill the deposit buffer + fill_deposit_buffer(total_allocated_keys) + + # perform deposits to the modules + for module in modules.values(): + if module.allocated_keys > 0: + lido.deposit(module.allocated_keys, module.id, "0x", {"from": deposit_security_module}) + + # check that the new active keys in the modules match the expected values + module_digests_after_deposit = staking_router.getAllStakingModuleDigests() + expected_modules_state = modules + + for digest in module_digests_after_deposit: + (_, _, state, summary) = digest + (id, _, _, _, _, _, _, _, _, _) = state + (exited_keys, deposited_keys, _) = summary + + active_keys_after_deposit = deposited_keys - exited_keys + assert expected_modules_state[id].active_keys == active_keys_after_deposit diff --git a/utils/test/deposits_helpers.py b/utils/test/deposits_helpers.py new file mode 100644 index 00000000..3c819754 --- /dev/null +++ b/utils/test/deposits_helpers.py @@ -0,0 +1,24 @@ +from utils.test.helpers import ETH +from utils.config import contracts +from brownie import ZERO_ADDRESS, accounts + + +WEI_TOLERANCE = 5 # wei tolerance to avoid rounding issue + + +def fill_deposit_buffer(deposits_count): + staking_router, lido, withdrawal_queue = contracts.staking_router, contracts.lido, contracts.withdrawal_queue + + deposit_size = ETH(32) + + buffered_ether_before_submit = lido.getBufferedEther() + withdrawal_unfinalized_steth = withdrawal_queue.unfinalizedStETH() + + eth_to_deposit = deposits_count * deposit_size + eth_debt = max(0, withdrawal_unfinalized_steth - buffered_ether_before_submit) + eth_to_submit = eth_to_deposit + eth_debt + WEI_TOLERANCE + + eth_whale = accounts.at(staking_router.DEPOSIT_CONTRACT(), force=True) + lido.submit(ZERO_ADDRESS, {"from": eth_whale, "value": eth_to_submit}) + + assert lido.getDepositableEther() >= eth_to_deposit From cfb17ac6268249c6070c7edd2672d560c070d14b Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 7 Feb 2024 17:34:21 +0100 Subject: [PATCH 11/68] fix: vote test --- tests/test_simple_dvt.py | 137 +++++++++++++++++++++++++-------------- 1 file changed, 89 insertions(+), 48 deletions(-) diff --git a/tests/test_simple_dvt.py b/tests/test_simple_dvt.py index c21aa325..1dd64f35 100644 --- a/tests/test_simple_dvt.py +++ b/tests/test_simple_dvt.py @@ -20,6 +20,7 @@ SIMPLE_DVT_MODULE_ID, SIMPLE_DVT_MODULE_NAME, SIMPLE_DVT_MODULE_TYPE, + EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, EASYTRACK_EVMSCRIPT_EXECUTOR, EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY, EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY, @@ -67,9 +68,6 @@ SET_NODE_OPERATOR_LIMIT_ROLE = "0x07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754" MANAGE_SIGNING_KEYS = "0x75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee" -# TODO: check trusted caller address -TRUSTED_CALLER = "0x08637515E85A4633E23dfc7861e2A9f53af640f7" - simple_dvt_repo_ens = "simple-dvt.lidopm.eth" simple_dvt_content_uri = ( "0x697066733a516d615353756a484347636e4675657441504777565735426567614d42766e355343736769334c5366767261536f" @@ -193,11 +191,82 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco assert activate_node_operators_evm_script_factory in evm_script_factories assert deactivate_node_operators_evm_script_factory in evm_script_factories assert set_vetted_validators_limits_evm_script_factory in evm_script_factories + assert update_target_validator_limits_evm_script_factory in evm_script_factories assert set_node_operator_names_evm_script_factory in evm_script_factories assert set_node_operator_reward_addresses_evm_script_factory in evm_script_factories - assert update_target_validator_limits_evm_script_factory in evm_script_factories assert change_node_operator_managers_evm_script_factory in evm_script_factories + assert interface.AddNodeOperators(add_node_operators_evm_script_factory).nodeOperatorsRegistry() == simple_dvt + assert ( + interface.AddNodeOperators(add_node_operators_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + assert ( + interface.ActivateNodeOperators(activate_node_operators_evm_script_factory).nodeOperatorsRegistry() + == simple_dvt + ) + assert ( + interface.ActivateNodeOperators(activate_node_operators_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + assert ( + interface.DeactivateNodeOperators(deactivate_node_operators_evm_script_factory).nodeOperatorsRegistry() + == simple_dvt + ) + assert ( + interface.DeactivateNodeOperators(deactivate_node_operators_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + assert ( + interface.SetVettedValidatorsLimits(set_vetted_validators_limits_evm_script_factory).nodeOperatorsRegistry() + == simple_dvt + ) + assert ( + interface.SetVettedValidatorsLimits(set_vetted_validators_limits_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + assert ( + interface.SetNodeOperatorNames(set_node_operator_names_evm_script_factory).nodeOperatorsRegistry() == simple_dvt + ) + assert ( + interface.SetNodeOperatorNames(set_node_operator_names_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + assert ( + interface.SetNodeOperatorRewardAddresses( + set_node_operator_reward_addresses_evm_script_factory + ).nodeOperatorsRegistry() + == simple_dvt + ) + assert ( + interface.SetNodeOperatorRewardAddresses(set_node_operator_reward_addresses_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + assert ( + interface.UpdateTargetValidatorLimits(update_target_validator_limits_evm_script_factory).nodeOperatorsRegistry() + == simple_dvt + ) + assert ( + interface.UpdateTargetValidatorLimits(update_target_validator_limits_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + assert ( + interface.ChangeNodeOperatorManagers(change_node_operator_managers_evm_script_factory).nodeOperatorsRegistry() + == simple_dvt + ) + assert ( + interface.ChangeNodeOperatorManagers(change_node_operator_managers_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + + if bypass_events_decoding or network_name() in ("goerli", "goerli-fork"): + return + + # validate vote events + assert count_vote_items_by_events(vote_tx, contracts.voting) == 18, "Incorrect voting items count" + + display_voting_events(vote_tx) + evs = group_voting_events(vote_tx) validate_evmscript_factory_added_event( @@ -205,90 +274,62 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco EVMScriptFactoryAdded( factory_addr=add_node_operators_evm_script_factory, permissions=create_permissions(simple_dvt, "addNodeOperator") - + create_permissions(contracts.acl, "grantPermissionP")[2:] - ) + + create_permissions(contracts.acl, "grantPermissionP")[2:], + ), ) validate_evmscript_factory_added_event( evs[9], EVMScriptFactoryAdded( factory_addr=activate_node_operators_evm_script_factory, permissions=create_permissions(simple_dvt, "activateNodeOperator") - + create_permissions(contracts.acl, "grantPermissionP")[2:] - ) + + create_permissions(contracts.acl, "grantPermissionP")[2:], + ), ) validate_evmscript_factory_added_event( evs[10], EVMScriptFactoryAdded( factory_addr=deactivate_node_operators_evm_script_factory, permissions=create_permissions(simple_dvt, "deactivateNodeOperator") - + create_permissions(contracts.acl, "revokePermission")[2:] - ) + + create_permissions(contracts.acl, "revokePermission")[2:], + ), ) validate_evmscript_factory_added_event( evs[11], EVMScriptFactoryAdded( factory_addr=set_vetted_validators_limits_evm_script_factory, - permissions=create_permissions(simple_dvt, "setNodeOperatorStakingLimit") - ) + permissions=create_permissions(simple_dvt, "setNodeOperatorStakingLimit"), + ), ) validate_evmscript_factory_added_event( evs[12], EVMScriptFactoryAdded( factory_addr=update_target_validator_limits_evm_script_factory, - permissions=create_permissions(simple_dvt, "updateTargetValidatorsLimits") - ) + permissions=create_permissions(simple_dvt, "updateTargetValidatorsLimits"), + ), ) validate_evmscript_factory_added_event( evs[13], EVMScriptFactoryAdded( factory_addr=set_node_operator_names_evm_script_factory, - permissions=create_permissions(simple_dvt, "setNodeOperatorName") - ) + permissions=create_permissions(simple_dvt, "setNodeOperatorName"), + ), ) validate_evmscript_factory_added_event( evs[14], EVMScriptFactoryAdded( factory_addr=set_node_operator_reward_addresses_evm_script_factory, - permissions=create_permissions(simple_dvt, "setNodeOperatorRewardAddress") - ) + permissions=create_permissions(simple_dvt, "setNodeOperatorRewardAddress"), + ), ) validate_evmscript_factory_added_event( evs[15], EVMScriptFactoryAdded( factory_addr=change_node_operator_managers_evm_script_factory, permissions=create_permissions(contracts.acl, "revokePermission") - + create_permissions(contracts.acl, "grantPermissionP")[2:] - ) + + create_permissions(contracts.acl, "grantPermissionP")[2:], + ), ) - assert interface.AddNodeOperators(add_node_operators_evm_script_factory).nodeOperatorsRegistry() == simple_dvt - assert interface.AddNodeOperators(add_node_operators_evm_script_factory).trustedCaller() == TRUSTED_CALLER - assert interface.ActivateNodeOperators(activate_node_operators_evm_script_factory).nodeOperatorsRegistry() == simple_dvt - assert interface.ActivateNodeOperators(activate_node_operators_evm_script_factory).trustedCaller() == TRUSTED_CALLER - assert interface.DeactivateNodeOperators(deactivate_node_operators_evm_script_factory).nodeOperatorsRegistry() == simple_dvt - assert interface.DeactivateNodeOperators(deactivate_node_operators_evm_script_factory).trustedCaller() == TRUSTED_CALLER - assert interface.SetVettedValidatorsLimits(set_vetted_validators_limits_evm_script_factory).nodeOperatorsRegistry() == simple_dvt - assert interface.SetVettedValidatorsLimits(set_vetted_validators_limits_evm_script_factory).trustedCaller() == TRUSTED_CALLER - assert interface.SetNodeOperatorNames(set_node_operator_names_evm_script_factory).nodeOperatorsRegistry() == simple_dvt - assert interface.SetNodeOperatorNames(set_node_operator_names_evm_script_factory).trustedCaller() == TRUSTED_CALLER - assert interface.SetNodeOperatorRewardAddresses(set_node_operator_reward_addresses_evm_script_factory).nodeOperatorsRegistry() == simple_dvt - assert interface.SetNodeOperatorRewardAddresses(set_node_operator_reward_addresses_evm_script_factory).trustedCaller() == TRUSTED_CALLER - assert interface.UpdateTargetValidatorLimits(update_target_validator_limits_evm_script_factory).nodeOperatorsRegistry() == simple_dvt - assert interface.UpdateTargetValidatorLimits(update_target_validator_limits_evm_script_factory).trustedCaller() == TRUSTED_CALLER - assert interface.ChangeNodeOperatorManagers(change_node_operator_managers_evm_script_factory).nodeOperatorsRegistry() == simple_dvt - assert interface.ChangeNodeOperatorManagers(change_node_operator_managers_evm_script_factory).trustedCaller() == TRUSTED_CALLER - - - - # validate vote events - # assert count_vote_items_by_events(vote_tx, contracts.voting) == 65, "Incorrect voting items count" - - # display_voting_events(vote_tx) - - # if bypass_events_decoding or network_name() in ("goerli", "goerli-fork"): - # return - - def has_permission(permission: Permission, how: List[int]) -> bool: return contracts.acl.hasPermission["address,address,bytes32,uint[]"]( From 745fac7ded6fe02758530647a8996dab9a8395a3 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 7 Feb 2024 17:34:52 +0100 Subject: [PATCH 12/68] build: fixed ganache pkg --- .gitmodules | 3 ++ package.json | 5 ++- yarn.lock | 91 +++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..fef73d95 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "_ganache"] + path = _ganache + url = https://github.com/krogla/ganache.git diff --git a/package.json b/package.json index 0a9de607..c4e87be6 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,10 @@ "license": "MIT", "readme": "README.md", "homepage": "https://mainnet.lido.fi", + "scripts": { + "preinstall": "git submodule update -i -r && cd ./_ganache && npm install && INFURA_KEY=00112233445566778899aabbccddeeff npm run build" + }, "dependencies": { - "ganache": "=7.6.0" + "ganache": "file:./_ganache/packages/ganache" } } diff --git a/yarn.lock b/yarn.lock index 76a508ce..1d9fcfbf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,6 +9,16 @@ dependencies: node-gyp-build "4.4.0" +"@trufflesuite/uws-js-unofficial@20.30.0-unofficial.0": + version "20.30.0-unofficial.0" + resolved "https://registry.yarnpkg.com/@trufflesuite/uws-js-unofficial/-/uws-js-unofficial-20.30.0-unofficial.0.tgz#2fbc2f8ef7e82fbeea6abaf7e8a9d42a02b479d3" + integrity sha512-r5X0aOQcuT6pLwTRLD+mPnAM/nlKtvIK4Z+My++A8tTOR0qTjNRx8UB8jzRj3D+p9PMAp5LnpCUUGmz7/TppwA== + dependencies: + ws "8.13.0" + optionalDependencies: + bufferutil "4.0.7" + utf-8-validate "6.0.3" + "@types/bn.js@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" @@ -31,7 +41,20 @@ resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-3.0.1.tgz#1254750a4fec4aff2ebec088ccd0bb02e91fedb4" integrity sha512-giB9gzDeiCeloIXDgzFBCgjj1k4WxcDrZtGl6h1IqmUPlxF+Nx8Ve+96QCyDZ/HseB/uvDsKbpib9hU5cU53pw== -abstract-leveldown@^7.2.0: +abstract-level@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.3.tgz#78a67d3d84da55ee15201486ab44c09560070741" + integrity sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA== + dependencies: + buffer "^6.0.3" + catering "^2.1.0" + is-buffer "^2.0.5" + level-supports "^4.0.0" + level-transcoder "^1.0.1" + module-error "^1.0.1" + queue-microtask "^1.2.3" + +abstract-leveldown@7.2.0, abstract-leveldown@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz#08d19d4e26fb5be426f7a57004851b39e1795a2e" integrity sha512-DnhQwcFEaYsvYDnACLZhMmCWd3rkOeEvglpa4q5i/5Jlm3UIsWaxVzuXvDLFCSCWRO3yy2/+V/G7FusFgejnfQ== @@ -43,6 +66,20 @@ abstract-leveldown@^7.2.0: level-supports "^2.0.1" queue-microtask "^1.2.3" +async-eventemitter@0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== + dependencies: + async "^2.4.0" + +async@^2.4.0: + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== + dependencies: + lodash "^4.17.14" + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -73,6 +110,13 @@ bufferutil@4.0.5: dependencies: node-gyp-build "^4.3.0" +bufferutil@4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad" + integrity sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw== + dependencies: + node-gyp-build "^4.3.0" + catering@^2.0.0, catering@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" @@ -96,15 +140,17 @@ emittery@0.10.0: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.0.tgz#bb373c660a9d421bb44706ec4967ed50c02a8026" integrity sha512-AGvFfs+d0JKCJQ4o01ASQLGPmSCxgfU9RFXvzPvZdjKK8oscynksuJhWrSTSw7j7Ep/sZct5b5ZhYCi8S/t0HQ== -ganache@=7.6.0: - version "7.6.0" - resolved "https://registry.yarnpkg.com/ganache/-/ganache-7.6.0.tgz#c64350f00c043ae0134460acbd7f4c178ac5aae4" - integrity sha512-TVpSHgIKPVCqvehGXOsXTYi5X0VvPeS+KHl0Ph8WQvtvoqxutZ8Ov4PclaAX9htIGmwns8DYJBg+yvola6wdrA== +"ganache@file:./_ganache/packages/ganache": + version "7.9.2-lido" dependencies: "@trufflesuite/bigint-buffer" "1.1.10" + "@trufflesuite/uws-js-unofficial" "20.30.0-unofficial.0" "@types/bn.js" "^5.1.0" "@types/lru-cache" "5.1.1" "@types/seedrandom" "3.0.1" + abstract-level "1.0.3" + abstract-leveldown "7.2.0" + async-eventemitter "0.2.4" emittery "0.10.0" keccak "3.0.2" leveldown "6.1.0" @@ -166,6 +212,19 @@ level-supports@^2.0.1: resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-2.1.0.tgz#9af908d853597ecd592293b2fad124375be79c5f" integrity sha512-E486g1NCjW5cF78KGPrMDRBYzPuueMZ6VBXHT6gC7A8UYWGiM14fGgp+s/L1oFfDWSPV/+SFkYCmZ0SiESkRKA== +level-supports@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-4.0.1.tgz#431546f9d81f10ff0fea0e74533a0e875c08c66a" + integrity sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA== + +level-transcoder@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-transcoder/-/level-transcoder-1.0.1.tgz#f8cef5990c4f1283d4c86d949e73631b0bc8ba9c" + integrity sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w== + dependencies: + buffer "^6.0.3" + module-error "^1.0.1" + leveldown@6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-6.1.0.tgz#7ab1297706f70c657d1a72b31b40323aa612b9ee" @@ -175,6 +234,11 @@ leveldown@6.1.0: napi-macros "~2.0.0" node-gyp-build "^4.3.0" +lodash@^4.17.14: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -185,6 +249,11 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= +module-error@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86" + integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA== + napi-macros@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" @@ -242,7 +311,19 @@ utf-8-validate@5.0.7: dependencies: node-gyp-build "^4.3.0" +utf-8-validate@6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-6.0.3.tgz#7d8c936d854e86b24d1d655f138ee27d2636d777" + integrity sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA== + dependencies: + node-gyp-build "^4.3.0" + util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +ws@8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== From 1612381e75f1dfb3aba610173f7ad061035ce02b Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 7 Feb 2024 17:53:38 +0100 Subject: [PATCH 13/68] fix: add submodule --- _ganache | 1 + package-lock.json | 20 +++ yarn.lock | 325 +--------------------------------------------- 3 files changed, 22 insertions(+), 324 deletions(-) create mode 160000 _ganache create mode 100644 package-lock.json diff --git a/_ganache b/_ganache new file mode 160000 index 00000000..655bcb24 --- /dev/null +++ b/_ganache @@ -0,0 +1 @@ +Subproject commit 655bcb24499b06a9773c24c719954ca13931bd24 diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..c03e754a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,20 @@ +{ + "name": "scripts", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "scripts", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "ganache": "file:./_ganache/packages/ganache" + } + }, + "_ganache/packages/ganache": {}, + "node_modules/ganache": { + "resolved": "_ganache/packages/ganache", + "link": true + } + } +} diff --git a/yarn.lock b/yarn.lock index 1d9fcfbf..927ecb8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,328 +2,5 @@ # yarn lockfile v1 -"@trufflesuite/bigint-buffer@1.1.10": - version "1.1.10" - resolved "https://registry.yarnpkg.com/@trufflesuite/bigint-buffer/-/bigint-buffer-1.1.10.tgz#a1d9ca22d3cad1a138b78baaf15543637a3e1692" - integrity sha512-pYIQC5EcMmID74t26GCC67946mgTJFiLXOT/BYozgrd4UEY2JHEGLhWi9cMiQCt5BSqFEvKkCHNnoj82SRjiEw== - dependencies: - node-gyp-build "4.4.0" - -"@trufflesuite/uws-js-unofficial@20.30.0-unofficial.0": - version "20.30.0-unofficial.0" - resolved "https://registry.yarnpkg.com/@trufflesuite/uws-js-unofficial/-/uws-js-unofficial-20.30.0-unofficial.0.tgz#2fbc2f8ef7e82fbeea6abaf7e8a9d42a02b479d3" - integrity sha512-r5X0aOQcuT6pLwTRLD+mPnAM/nlKtvIK4Z+My++A8tTOR0qTjNRx8UB8jzRj3D+p9PMAp5LnpCUUGmz7/TppwA== - dependencies: - ws "8.13.0" - optionalDependencies: - bufferutil "4.0.7" - utf-8-validate "6.0.3" - -"@types/bn.js@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" - integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== - dependencies: - "@types/node" "*" - -"@types/lru-cache@5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef" - integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== - -"@types/node@*": - version "17.0.42" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.42.tgz#d7e8f22700efc94d125103075c074396b5f41f9b" - integrity sha512-Q5BPGyGKcvQgAMbsr7qEGN/kIPN6zZecYYABeTDBizOsau+2NMdSVTar9UQw21A2+JyA2KRNDYaYrPB0Rpk2oQ== - -"@types/seedrandom@3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-3.0.1.tgz#1254750a4fec4aff2ebec088ccd0bb02e91fedb4" - integrity sha512-giB9gzDeiCeloIXDgzFBCgjj1k4WxcDrZtGl6h1IqmUPlxF+Nx8Ve+96QCyDZ/HseB/uvDsKbpib9hU5cU53pw== - -abstract-level@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.3.tgz#78a67d3d84da55ee15201486ab44c09560070741" - integrity sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA== - dependencies: - buffer "^6.0.3" - catering "^2.1.0" - is-buffer "^2.0.5" - level-supports "^4.0.0" - level-transcoder "^1.0.1" - module-error "^1.0.1" - queue-microtask "^1.2.3" - -abstract-leveldown@7.2.0, abstract-leveldown@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz#08d19d4e26fb5be426f7a57004851b39e1795a2e" - integrity sha512-DnhQwcFEaYsvYDnACLZhMmCWd3rkOeEvglpa4q5i/5Jlm3UIsWaxVzuXvDLFCSCWRO3yy2/+V/G7FusFgejnfQ== - dependencies: - buffer "^6.0.3" - catering "^2.0.0" - is-buffer "^2.0.5" - level-concat-iterator "^3.0.0" - level-supports "^2.0.1" - queue-microtask "^1.2.3" - -async-eventemitter@0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" - integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== - dependencies: - async "^2.4.0" - -async@^2.4.0: - version "2.6.4" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" - integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== - dependencies: - lodash "^4.17.14" - -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -bufferutil@4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.5.tgz#da9ea8166911cc276bf677b8aed2d02d31f59028" - integrity sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A== - dependencies: - node-gyp-build "^4.3.0" - -bufferutil@4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad" - integrity sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw== - dependencies: - node-gyp-build "^4.3.0" - -catering@^2.0.0, catering@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" - integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== - -elliptic@^6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - -emittery@0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.0.tgz#bb373c660a9d421bb44706ec4967ed50c02a8026" - integrity sha512-AGvFfs+d0JKCJQ4o01ASQLGPmSCxgfU9RFXvzPvZdjKK8oscynksuJhWrSTSw7j7Ep/sZct5b5ZhYCi8S/t0HQ== - "ganache@file:./_ganache/packages/ganache": - version "7.9.2-lido" - dependencies: - "@trufflesuite/bigint-buffer" "1.1.10" - "@trufflesuite/uws-js-unofficial" "20.30.0-unofficial.0" - "@types/bn.js" "^5.1.0" - "@types/lru-cache" "5.1.1" - "@types/seedrandom" "3.0.1" - abstract-level "1.0.3" - abstract-leveldown "7.2.0" - async-eventemitter "0.2.4" - emittery "0.10.0" - keccak "3.0.2" - leveldown "6.1.0" - secp256k1 "4.0.3" - optionalDependencies: - bufferutil "4.0.5" - utf-8-validate "5.0.7" - -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -inherits@^2.0.3, inherits@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-buffer@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" - integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== - -keccak@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" - integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== - dependencies: - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - readable-stream "^3.6.0" - -level-concat-iterator@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-3.1.0.tgz#5235b1f744bc34847ed65a50548aa88d22e881cf" - integrity sha512-BWRCMHBxbIqPxJ8vHOvKUsaO0v1sLYZtjN3K2iZJsRBYtp+ONsY6Jfi6hy9K3+zolgQRryhIn2NRZjZnWJ9NmQ== - dependencies: - catering "^2.1.0" - -level-supports@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-2.1.0.tgz#9af908d853597ecd592293b2fad124375be79c5f" - integrity sha512-E486g1NCjW5cF78KGPrMDRBYzPuueMZ6VBXHT6gC7A8UYWGiM14fGgp+s/L1oFfDWSPV/+SFkYCmZ0SiESkRKA== - -level-supports@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-4.0.1.tgz#431546f9d81f10ff0fea0e74533a0e875c08c66a" - integrity sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA== - -level-transcoder@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/level-transcoder/-/level-transcoder-1.0.1.tgz#f8cef5990c4f1283d4c86d949e73631b0bc8ba9c" - integrity sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w== - dependencies: - buffer "^6.0.3" - module-error "^1.0.1" - -leveldown@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-6.1.0.tgz#7ab1297706f70c657d1a72b31b40323aa612b9ee" - integrity sha512-8C7oJDT44JXxh04aSSsfcMI8YiaGRhOFI9/pMEL7nWJLVsWajDPTRxsSHTM2WcTVY5nXM+SuRHzPPi0GbnDX+w== - dependencies: - abstract-leveldown "^7.2.0" - napi-macros "~2.0.0" - node-gyp-build "^4.3.0" - -lodash@^4.17.14: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - -module-error@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86" - integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA== - -napi-macros@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" - integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== - -node-addon-api@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" - integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== - -node-gyp-build@4.4.0, node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4" - integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ== - -queue-microtask@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - -readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -secp256k1@4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" - integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== - dependencies: - elliptic "^6.5.4" - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -utf-8-validate@5.0.7: - version "5.0.7" - resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.7.tgz#c15a19a6af1f7ad9ec7ddc425747ca28c3644922" - integrity sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q== - dependencies: - node-gyp-build "^4.3.0" - -utf-8-validate@6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-6.0.3.tgz#7d8c936d854e86b24d1d655f138ee27d2636d777" - integrity sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA== - dependencies: - node-gyp-build "^4.3.0" - -util-deprecate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -ws@8.13.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + resolved "file:_ganache/packages/ganache" From d3d01c451cc3d1b9908d58881ff0bbd851c60a0c Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 7 Feb 2024 18:14:29 +0100 Subject: [PATCH 14/68] build: lock file --- yarn.lock | 337 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 336 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 927ecb8c..8ea6b1f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,5 +2,340 @@ # yarn lockfile v1 +"@trufflesuite/bigint-buffer@1.1.10": + version "1.1.10" + resolved "https://registry.yarnpkg.com/@trufflesuite/bigint-buffer/-/bigint-buffer-1.1.10.tgz#a1d9ca22d3cad1a138b78baaf15543637a3e1692" + integrity sha512-pYIQC5EcMmID74t26GCC67946mgTJFiLXOT/BYozgrd4UEY2JHEGLhWi9cMiQCt5BSqFEvKkCHNnoj82SRjiEw== + dependencies: + node-gyp-build "4.4.0" + +"@trufflesuite/uws-js-unofficial@20.30.0-unofficial.0": + version "20.30.0-unofficial.0" + resolved "https://registry.yarnpkg.com/@trufflesuite/uws-js-unofficial/-/uws-js-unofficial-20.30.0-unofficial.0.tgz#2fbc2f8ef7e82fbeea6abaf7e8a9d42a02b479d3" + integrity sha512-r5X0aOQcuT6pLwTRLD+mPnAM/nlKtvIK4Z+My++A8tTOR0qTjNRx8UB8jzRj3D+p9PMAp5LnpCUUGmz7/TppwA== + dependencies: + ws "8.13.0" + optionalDependencies: + bufferutil "4.0.7" + utf-8-validate "6.0.3" + +"@types/bn.js@^5.1.0": + version "5.1.5" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" + integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== + dependencies: + "@types/node" "*" + +"@types/lru-cache@5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef" + integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== + +"@types/node@*": + version "20.11.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.16.tgz#4411f79411514eb8e2926f036c86c9f0e4ec6708" + integrity sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ== + dependencies: + undici-types "~5.26.4" + +"@types/seedrandom@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-3.0.1.tgz#1254750a4fec4aff2ebec088ccd0bb02e91fedb4" + integrity sha512-giB9gzDeiCeloIXDgzFBCgjj1k4WxcDrZtGl6h1IqmUPlxF+Nx8Ve+96QCyDZ/HseB/uvDsKbpib9hU5cU53pw== + +abstract-level@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.3.tgz#78a67d3d84da55ee15201486ab44c09560070741" + integrity sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA== + dependencies: + buffer "^6.0.3" + catering "^2.1.0" + is-buffer "^2.0.5" + level-supports "^4.0.0" + level-transcoder "^1.0.1" + module-error "^1.0.1" + queue-microtask "^1.2.3" + +abstract-leveldown@7.2.0, abstract-leveldown@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz#08d19d4e26fb5be426f7a57004851b39e1795a2e" + integrity sha512-DnhQwcFEaYsvYDnACLZhMmCWd3rkOeEvglpa4q5i/5Jlm3UIsWaxVzuXvDLFCSCWRO3yy2/+V/G7FusFgejnfQ== + dependencies: + buffer "^6.0.3" + catering "^2.0.0" + is-buffer "^2.0.5" + level-concat-iterator "^3.0.0" + level-supports "^2.0.1" + queue-microtask "^1.2.3" + +async-eventemitter@0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== + dependencies: + async "^2.4.0" + +async@^2.4.0: + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== + dependencies: + lodash "^4.17.14" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +bufferutil@4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.5.tgz#da9ea8166911cc276bf677b8aed2d02d31f59028" + integrity sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A== + dependencies: + node-gyp-build "^4.3.0" + +bufferutil@4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad" + integrity sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw== + dependencies: + node-gyp-build "^4.3.0" + +catering@^2.0.0, catering@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" + integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== + +elliptic@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emittery@0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.0.tgz#bb373c660a9d421bb44706ec4967ed50c02a8026" + integrity sha512-AGvFfs+d0JKCJQ4o01ASQLGPmSCxgfU9RFXvzPvZdjKK8oscynksuJhWrSTSw7j7Ep/sZct5b5ZhYCi8S/t0HQ== + "ganache@file:./_ganache/packages/ganache": - resolved "file:_ganache/packages/ganache" + version "7.9.2-lido" + dependencies: + "@trufflesuite/bigint-buffer" "1.1.10" + "@trufflesuite/uws-js-unofficial" "20.30.0-unofficial.0" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "5.1.1" + "@types/seedrandom" "3.0.1" + abstract-level "1.0.3" + abstract-leveldown "7.2.0" + async-eventemitter "0.2.4" + emittery "0.10.0" + keccak "3.0.2" + leveldown "6.1.0" + secp256k1 "4.0.3" + optionalDependencies: + bufferutil "4.0.5" + utf-8-validate "5.0.7" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-buffer@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +keccak@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" + integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + +level-concat-iterator@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-3.1.0.tgz#5235b1f744bc34847ed65a50548aa88d22e881cf" + integrity sha512-BWRCMHBxbIqPxJ8vHOvKUsaO0v1sLYZtjN3K2iZJsRBYtp+ONsY6Jfi6hy9K3+zolgQRryhIn2NRZjZnWJ9NmQ== + dependencies: + catering "^2.1.0" + +level-supports@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-2.1.0.tgz#9af908d853597ecd592293b2fad124375be79c5f" + integrity sha512-E486g1NCjW5cF78KGPrMDRBYzPuueMZ6VBXHT6gC7A8UYWGiM14fGgp+s/L1oFfDWSPV/+SFkYCmZ0SiESkRKA== + +level-supports@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-4.0.1.tgz#431546f9d81f10ff0fea0e74533a0e875c08c66a" + integrity sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA== + +level-transcoder@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-transcoder/-/level-transcoder-1.0.1.tgz#f8cef5990c4f1283d4c86d949e73631b0bc8ba9c" + integrity sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w== + dependencies: + buffer "^6.0.3" + module-error "^1.0.1" + +leveldown@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-6.1.0.tgz#7ab1297706f70c657d1a72b31b40323aa612b9ee" + integrity sha512-8C7oJDT44JXxh04aSSsfcMI8YiaGRhOFI9/pMEL7nWJLVsWajDPTRxsSHTM2WcTVY5nXM+SuRHzPPi0GbnDX+w== + dependencies: + abstract-leveldown "^7.2.0" + napi-macros "~2.0.0" + node-gyp-build "^4.3.0" + +lodash@^4.17.14: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + +module-error@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86" + integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA== + +napi-macros@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" + integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-gyp-build@4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4" + integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ== + +node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd" + integrity sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og== + +queue-microtask@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +secp256k1@4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" + integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== + dependencies: + elliptic "^6.5.4" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +utf-8-validate@5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.7.tgz#c15a19a6af1f7ad9ec7ddc425747ca28c3644922" + integrity sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q== + dependencies: + node-gyp-build "^4.3.0" + +utf-8-validate@6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-6.0.3.tgz#7d8c936d854e86b24d1d655f138ee27d2636d777" + integrity sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA== + dependencies: + node-gyp-build "^4.3.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +ws@8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== From 909d3bca58448dbabd76db663008352d56a07b76 Mon Sep 17 00:00:00 2001 From: infloop Date: Wed, 7 Feb 2024 23:38:58 +0500 Subject: [PATCH 15/68] feat: ganache v7.9.2 patch --- patch-ganache-7.9.2.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 patch-ganache-7.9.2.js diff --git a/patch-ganache-7.9.2.js b/patch-ganache-7.9.2.js new file mode 100644 index 00000000..d9dfcdeb --- /dev/null +++ b/patch-ganache-7.9.2.js @@ -0,0 +1,22 @@ +/** + * Ganache v7.9.2 patch for JSON.stringify issue + * This fixes reading a lot of events events from debug_traceTransaction + */ +const fs = require('fs'); + +const searchString = '{const s=(0,a.makeResponse)(t.id,e);return"debug_traceTransaction"===t.method&&"object"==typeof e&&Array.isArray(e.structLogs)&&e.structLogs.length>this.BUFFERIFY_THRESHOLD?(0,u.bufferify)(s,""):JSON.stringify(s)}'; + +const replacementString = '{const r=(0,a.makeResponse)(t.id,e);if("debug_traceTransaction"===t.method&&"object"==typeof e&&Array.isArray(e.structLogs)&&e.structLogs.length>this.BUFFERIFY_THRESHOLD)return(0,u.bufferify)(r,"");try{return JSON.stringify(r)}catch(e){return(0,u.bufferify)(r,"")}}'; + +const filename = './node_modules/ganache/dist/node/1.js'; +fs.readFile(filename, 'utf8', function (err,data) { + if (err) { + return console.log(err); + } + const result = data.replace(searchString, replacementString); + + fs.writeFile(filename, result, 'utf8', function (err) { + if (err) return console.log(err); + }); +}); + From 94b33b59663935d953dd6c77fcce367445dc4272 Mon Sep 17 00:00:00 2001 From: infloop Date: Wed, 7 Feb 2024 23:46:45 +0500 Subject: [PATCH 16/68] feat: ganache v7.9.2 patch #2 --- package.json | 5 +++-- yarn.lock | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index c4e87be6..9294e854 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,10 @@ "readme": "README.md", "homepage": "https://mainnet.lido.fi", "scripts": { - "preinstall": "git submodule update -i -r && cd ./_ganache && npm install && INFURA_KEY=00112233445566778899aabbccddeeff npm run build" + "postinstall": "yarn patch:ganache", + "patch:ganache": "node patch-ganache-7.9.2.js" }, "dependencies": { - "ganache": "file:./_ganache/packages/ganache" + "ganache": "=7.9.2" } } diff --git a/yarn.lock b/yarn.lock index 8ea6b1f8..0f491b47 100644 --- a/yarn.lock +++ b/yarn.lock @@ -142,8 +142,10 @@ emittery@0.10.0: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.0.tgz#bb373c660a9d421bb44706ec4967ed50c02a8026" integrity sha512-AGvFfs+d0JKCJQ4o01ASQLGPmSCxgfU9RFXvzPvZdjKK8oscynksuJhWrSTSw7j7Ep/sZct5b5ZhYCi8S/t0HQ== -"ganache@file:./_ganache/packages/ganache": - version "7.9.2-lido" +ganache@=7.9.2: + version "7.9.2" + resolved "https://registry.yarnpkg.com/ganache/-/ganache-7.9.2.tgz#77f506ad2735dd9109696ffa1834a9dd2f806449" + integrity sha512-7gsVVDpO9AhrFyDMWWl7SpMsPpqGcnAzjxz3k32LheIPNd64p2XsY9GYRdhWmKuryb60W1iaWPZWDkFKlbRWHA== dependencies: "@trufflesuite/bigint-buffer" "1.1.10" "@trufflesuite/uws-js-unofficial" "20.30.0-unofficial.0" From fc6c0044324f2b7beba4fdb11c020080aa7fa64b Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 7 Feb 2024 22:02:52 +0100 Subject: [PATCH 17/68] fix: custom ganache --- .gitmodules | 3 --- _ganache | 1 - package.json | 6 +----- patch-ganache-7.9.2.js | 22 ---------------------- yarn.lock | 42 +++++++++++++++++++++--------------------- 5 files changed, 22 insertions(+), 52 deletions(-) delete mode 100644 .gitmodules delete mode 160000 _ganache delete mode 100644 patch-ganache-7.9.2.js diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index fef73d95..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "_ganache"] - path = _ganache - url = https://github.com/krogla/ganache.git diff --git a/_ganache b/_ganache deleted file mode 160000 index 655bcb24..00000000 --- a/_ganache +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 655bcb24499b06a9773c24c719954ca13931bd24 diff --git a/package.json b/package.json index 9294e854..ca9ca8b0 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,7 @@ "license": "MIT", "readme": "README.md", "homepage": "https://mainnet.lido.fi", - "scripts": { - "postinstall": "yarn patch:ganache", - "patch:ganache": "node patch-ganache-7.9.2.js" - }, "dependencies": { - "ganache": "=7.9.2" + "@lido-js/ganache": "=7.9.2-lido" } } diff --git a/patch-ganache-7.9.2.js b/patch-ganache-7.9.2.js deleted file mode 100644 index d9dfcdeb..00000000 --- a/patch-ganache-7.9.2.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Ganache v7.9.2 patch for JSON.stringify issue - * This fixes reading a lot of events events from debug_traceTransaction - */ -const fs = require('fs'); - -const searchString = '{const s=(0,a.makeResponse)(t.id,e);return"debug_traceTransaction"===t.method&&"object"==typeof e&&Array.isArray(e.structLogs)&&e.structLogs.length>this.BUFFERIFY_THRESHOLD?(0,u.bufferify)(s,""):JSON.stringify(s)}'; - -const replacementString = '{const r=(0,a.makeResponse)(t.id,e);if("debug_traceTransaction"===t.method&&"object"==typeof e&&Array.isArray(e.structLogs)&&e.structLogs.length>this.BUFFERIFY_THRESHOLD)return(0,u.bufferify)(r,"");try{return JSON.stringify(r)}catch(e){return(0,u.bufferify)(r,"")}}'; - -const filename = './node_modules/ganache/dist/node/1.js'; -fs.readFile(filename, 'utf8', function (err,data) { - if (err) { - return console.log(err); - } - const result = data.replace(searchString, replacementString); - - fs.writeFile(filename, result, 'utf8', function (err) { - if (err) return console.log(err); - }); -}); - diff --git a/yarn.lock b/yarn.lock index 0f491b47..e40a6f8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,27 @@ # yarn lockfile v1 +"@lido-js/ganache@=7.9.2-lido": + version "7.9.2-lido" + resolved "https://registry.yarnpkg.com/@lido-js/ganache/-/ganache-7.9.2-lido.tgz#927611f015c72dbb0d68767ce69dc8f1b2bba6b8" + integrity sha512-q40Q3760CCn4rzg7prpiuPw9sYSKnrVKcIQupqrgAnWagr/I3FkUmNNhBWw4rQpPla4K50vyOJoUtPnN+hRx9g== + dependencies: + "@trufflesuite/bigint-buffer" "1.1.10" + "@trufflesuite/uws-js-unofficial" "20.30.0-unofficial.0" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "5.1.1" + "@types/seedrandom" "3.0.1" + abstract-level "1.0.3" + abstract-leveldown "7.2.0" + async-eventemitter "0.2.4" + emittery "0.10.0" + keccak "3.0.2" + leveldown "6.1.0" + secp256k1 "4.0.3" + optionalDependencies: + bufferutil "4.0.5" + utf-8-validate "5.0.7" + "@trufflesuite/bigint-buffer@1.1.10": version "1.1.10" resolved "https://registry.yarnpkg.com/@trufflesuite/bigint-buffer/-/bigint-buffer-1.1.10.tgz#a1d9ca22d3cad1a138b78baaf15543637a3e1692" @@ -142,27 +163,6 @@ emittery@0.10.0: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.0.tgz#bb373c660a9d421bb44706ec4967ed50c02a8026" integrity sha512-AGvFfs+d0JKCJQ4o01ASQLGPmSCxgfU9RFXvzPvZdjKK8oscynksuJhWrSTSw7j7Ep/sZct5b5ZhYCi8S/t0HQ== -ganache@=7.9.2: - version "7.9.2" - resolved "https://registry.yarnpkg.com/ganache/-/ganache-7.9.2.tgz#77f506ad2735dd9109696ffa1834a9dd2f806449" - integrity sha512-7gsVVDpO9AhrFyDMWWl7SpMsPpqGcnAzjxz3k32LheIPNd64p2XsY9GYRdhWmKuryb60W1iaWPZWDkFKlbRWHA== - dependencies: - "@trufflesuite/bigint-buffer" "1.1.10" - "@trufflesuite/uws-js-unofficial" "20.30.0-unofficial.0" - "@types/bn.js" "^5.1.0" - "@types/lru-cache" "5.1.1" - "@types/seedrandom" "3.0.1" - abstract-level "1.0.3" - abstract-leveldown "7.2.0" - async-eventemitter "0.2.4" - emittery "0.10.0" - keccak "3.0.2" - leveldown "6.1.0" - secp256k1 "4.0.3" - optionalDependencies: - bufferutil "4.0.5" - utf-8-validate "5.0.7" - hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" From abdfdee563ff8fcd2fbb43c1ccb13c3e497d9b77 Mon Sep 17 00:00:00 2001 From: F4ever Date: Fri, 9 Feb 2024 22:44:30 +0100 Subject: [PATCH 18/68] Feat: add tests for easytracks + dvt happy path --- .gitignore | 2 + tests/regression/__init__.py | 0 tests/regression/conftest.py | 10 +- tests/regression/test_easy_track_factories.py | 318 +++++++++++++++++ .../test_sdvt_rewards_happy_path.py | 5 - ...h.py => test_staking_module_happy_path.py} | 324 +++++++++--------- utils/__init__.py | 0 7 files changed, 488 insertions(+), 171 deletions(-) create mode 100644 tests/regression/__init__.py create mode 100644 tests/regression/test_easy_track_factories.py rename tests/regression/{test_node-operators-registry-happy-path.py => test_staking_module_happy_path.py} (75%) create mode 100644 utils/__init__.py diff --git a/.gitignore b/.gitignore index aef6c2c3..b379fe71 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ __pycache__ build/ reports/ dist/ +.env +_ganache # PyCharm .idea diff --git a/tests/regression/__init__.py b/tests/regression/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/regression/conftest.py b/tests/regression/conftest.py index dbaac6d4..097e86e1 100644 --- a/tests/regression/conftest.py +++ b/tests/regression/conftest.py @@ -17,12 +17,12 @@ def autoexecute_vote(helpers, vote_ids_from_env, accounts, stranger): if vote_ids_from_env: helpers.execute_votes(accounts, vote_ids_from_env, contracts.voting, topup="0.5 ether") - else: - start_and_execute_votes(contracts.voting, helpers) + # else: + # start_and_execute_votes(contracts.voting, helpers) - if os.getenv(ENV_FILL_SIMPLE_DVT): - print(f"Prefilling SimpleDVT...") - fill_simple_dvt_ops_vetted_keys(stranger) + # if os.getenv(ENV_FILL_SIMPLE_DVT): + # print(f"Prefilling SimpleDVT...") + # fill_simple_dvt_ops_vetted_keys(stranger) if os.getenv(ENV_REPORT_AFTER_VOTE): oracle_report(cl_diff=ETH(523), exclude_vaults_balances=False) diff --git a/tests/regression/test_easy_track_factories.py b/tests/regression/test_easy_track_factories.py new file mode 100644 index 00000000..a7982209 --- /dev/null +++ b/tests/regression/test_easy_track_factories.py @@ -0,0 +1,318 @@ +import random + +from brownie import interface, accounts, chain +from brownie.exceptions import VirtualMachineError + +from configs.config_mainnet import * +from utils.config import contracts +from utils.test.easy_track_helpers import _encode_calldata +from utils.test.simple_dvt_helpers import MANAGERS + + +NODE_OPERATORS = [ + { + 'address': f'0x000000000000000000000000000000000000{i:04}', + 'manager': f'0x000000000000000000000000000000000001{i:04}', + 'name': f'Node operator {i}', + } + for i in range(1, 11) +] + + +def easy_track_executor(creator, factory, calldata): + tx = contracts.easy_track.createMotion( + factory, + calldata, + {'from': creator}, + ) + + motions = contracts.easy_track.getMotions() + + chain.sleep(60 * 60 * 24 * 3) + chain.mine() + + contracts.easy_track.enactMotion( + motions[-1][0], + tx.events['MotionCreated']['_evmScriptCallData'], + {'from': accounts[4]}, + ) + + +def add_node_operators(operators): + calldata = _encode_calldata( + '(uint256,(string,address,address)[])', + [ + contracts.simple_dvt.getNodeOperatorsCount(), + [ + (no['name'], no['address'], no['manager']) + for no in NODE_OPERATORS + ], + ] + ) + + factory = interface.AddNodeOperators(EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY) + + easy_track_executor( + factory.trustedCaller(), + factory, + calldata, + ) + + +def activate_node_operators(operators): + calldata = _encode_calldata( + '((uint256,address)[])', + [[(no['id'], no['manager']) for no in operators]], + ) + + factory = interface.ActivateNodeOperators(EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY) + + easy_track_executor( + factory.trustedCaller(), + factory, + calldata, + ) + + +def deactivate_node_operator(operators): + calldata = _encode_calldata( + '((uint256,address)[])', + [[(no['id'], no['manager']) for no in operators]], + ) + + factory = interface.DeactivateNodeOperators(EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY) + + easy_track_executor( + factory.trustedCaller(), + factory, + calldata, + ) + + +def set_vetted_validators_limits(operators): + calldata = _encode_calldata( + '((uint256,uint256)[])', + [[(no['id'], no['staking_limit']) for no in operators]] + ) + + factory = interface.SetVettedValidatorsLimits(EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY) + + easy_track_executor( + factory.trustedCaller(), + factory, + calldata, + ) + + +def set_node_operators_names(operators): + calldata = _encode_calldata( + '((uint256,string)[])', + [[(no['id'], no['name']) for no in operators]], + ) + + factory = interface.SetNodeOperatorNames(EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_NAMES_FACTORY) + + easy_track_executor( + factory.trustedCaller(), + factory, + calldata, + ) + + +def set_node_operator_reward_addresses(operators): + calldata = _encode_calldata( + '((uint256,address)[])', + [[(no['id'], no['address']) for no in operators]], + ) + + factory = interface.SetNodeOperatorRewardAddresses(EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY) + + easy_track_executor( + factory.trustedCaller(), + factory, + calldata, + ) + + +def update_target_validators_limits(operators): + calldata = _encode_calldata( + '((uint256,bool,uint256)[])', + [[(no['id'], no['is_target_limit_active'], no['target_limit']) for no in operators]], + ) + + factory = interface.UpdateTargetValidatorLimits(EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY) + + easy_track_executor( + factory.trustedCaller(), + factory, + calldata, + ) + + +def change_node_operator_managers(operators): + calldata = _encode_calldata( + '((uint256,address,address)[])', + [[(no['id'], no['old_manager'], no['manager']) for no in operators]], + ) + + factory = interface.ChangeNodeOperatorManagers(EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY) + + easy_track_executor( + factory.trustedCaller(), + factory, + calldata, + ) + + +def test_add_node_operators(): + # AddNodeOperators + node_operators_count = contracts.simple_dvt.getNodeOperatorsCount() + + add_node_operators(NODE_OPERATORS) + + no_ids = list(contracts.simple_dvt.getNodeOperatorIds(1, 100))[node_operators_count - 1:] + + for no_id, no in zip(no_ids, NODE_OPERATORS): + no_in_contract = contracts.simple_dvt.getNodeOperator(no_id, True) + + assert no_in_contract[0] + assert no_in_contract[1] == no['name'] + assert no_in_contract[2] == no['address'] + + assert node_operators_count + len(NODE_OPERATORS) == contracts.simple_dvt.getNodeOperatorsCount() + + +def test_node_operators_activations(): + assert contracts.simple_dvt.getNodeOperator(1, True)[0] + assert contracts.simple_dvt.getNodeOperator(2, True)[0] + + deactivate_node_operator([{ + 'id': 1, + 'manager': MANAGERS[1], + }, { + 'id': 2, + 'manager': MANAGERS[2], + }]) + + assert not contracts.simple_dvt.getNodeOperator(1, True)[0] + assert not contracts.simple_dvt.getNodeOperator(2, True)[0] + + # ActivateNodeOperators + activate_node_operators([{ + 'id': 1, + 'manager': MANAGERS[1], + }, { + 'id': 2, + 'manager': MANAGERS[2], + }]) + + assert contracts.simple_dvt.getNodeOperator(1, True)[0] + assert contracts.simple_dvt.getNodeOperator(2, True)[0] + + +def test_set_vetted_validators_limits(): + op_1 = contracts.simple_dvt.getNodeOperator(1, True) + op_2 = contracts.simple_dvt.getNodeOperator(2, True) + + new_vetted_keys_1 = random.randint(0, op_1[5]) + new_vetted_keys_2 = random.randint(0, op_2[5]) + + set_vetted_validators_limits([{ + 'id': 1, + 'staking_limit': new_vetted_keys_1, + }, { + 'id': 2, + 'staking_limit': new_vetted_keys_2, + }]) + + assert contracts.simple_dvt.getNodeOperator(1, True)[3] == new_vetted_keys_1 + assert contracts.simple_dvt.getNodeOperator(2, True)[3] == new_vetted_keys_2 + + +def test_set_node_operator_names(): + op_1 = contracts.simple_dvt.getNodeOperator(1, True) + op_2 = contracts.simple_dvt.getNodeOperator(2, True) + + new_name_1 = op_1[1] + ' new 1' + new_name_2 = op_2[1] + ' new 2' + + # SetNodeOperatorNames + set_node_operators_names([{ + 'id': 1, + 'name': new_name_1, + }, { + 'id': 2, + 'name': new_name_2, + }]) + + assert contracts.simple_dvt.getNodeOperator(1, True)[1] == new_name_1 + assert contracts.simple_dvt.getNodeOperator(2, True)[1] == new_name_2 + + +def test_set_node_operator_reward_addresses(): + address_1 = '0x0000000000000000000000000000000000001333' + address_2 = '0x0000000000000000000000000000000000001999' + + # SetNodeOperatorRewardAddresses + set_node_operator_reward_addresses([{ + 'id': 1, + 'address': address_1, + }, { + 'id': 2, + 'address': address_2, + }]) + + assert contracts.simple_dvt.getNodeOperator(1, True)[2] == address_1 + assert contracts.simple_dvt.getNodeOperator(2, True)[2] == address_2 + + +def test_update_target_validator_limits(): + # UpdateTargetValidatorLimits + update_target_validators_limits([{ + 'id': 1, + 'is_target_limit_active': True, + 'target_limit': 800, + }, { + 'id': 2, + 'is_target_limit_active': False, + 'target_limit': 900, + }]) + + # assert contracts.simple_dvt.getNodeOperator(1, True)[1] == address_1 + # assert contracts.simple_dvt.getNodeOperator(2, True)[2] == address_2 + + +def test_transfer_node_operator_manager(): + # TransferNodeOperatorManager + change_node_operator_managers([{ + 'id': 1, + 'old_manager': MANAGERS[1], + 'manager': '0x0000000000000000000000000000000000000222' + }, { + 'id': 2, + 'old_manager': MANAGERS[2], + 'manager': '0x0000000000000000000000000000000000000888' + }]) + + change_node_operator_managers([{ + 'id': 1, + 'old_manager': '0x0000000000000000000000000000000000000222', + 'manager': MANAGERS[1] + }, { + 'id': 2, + 'old_manager': '0x0000000000000000000000000000000000000888', + 'manager': MANAGERS[2] + }]) + + try: + change_node_operator_managers([{ + 'id': 1, + 'old_manager': '0x0000000000000000000000000000000000000222', + 'manager': MANAGERS[1] + }, { + 'id': 2, + 'old_manager': '0x0000000000000000000000000000000000000888', + 'manager': MANAGERS[2] + }]) + except VirtualMachineError as error: + assert 'OLD_MANAGER_HAS_NO_ROLE' in error.message diff --git a/tests/regression/test_sdvt_rewards_happy_path.py b/tests/regression/test_sdvt_rewards_happy_path.py index 87a34e37..78fdd19e 100644 --- a/tests/regression/test_sdvt_rewards_happy_path.py +++ b/tests/regression/test_sdvt_rewards_happy_path.py @@ -16,9 +16,6 @@ from utils.test.oracle_report_helpers import oracle_report -# fixtures - - @pytest.fixture(scope="module") def cluster_participants(accounts): CLUSTER_PARTICIPANTS = 5 @@ -59,8 +56,6 @@ def test_sdvt_module_connected_to_router(): # full happy path test - - def test_rewards_distribution_happy_path(simple_dvt_module_id, cluster_participants, reward_wrapper): """ Test happy path of rewards distribution diff --git a/tests/regression/test_node-operators-registry-happy-path.py b/tests/regression/test_staking_module_happy_path.py similarity index 75% rename from tests/regression/test_node-operators-registry-happy-path.py rename to tests/regression/test_staking_module_happy_path.py index 95fc0380..1ec53c91 100644 --- a/tests/regression/test_node-operators-registry-happy-path.py +++ b/tests/regression/test_staking_module_happy_path.py @@ -1,7 +1,7 @@ import pytest from web3 import Web3 import eth_abi -from brownie import chain, ZERO_ADDRESS, web3 +from brownie import chain, ZERO_ADDRESS, web3, interface from utils.test.extra_data import ( ExtraDataService, @@ -14,21 +14,11 @@ from utils.test.node_operators_helpers import node_operator_gindex -@pytest.fixture() -def extra_data_service(): - return ExtraDataService() - - @pytest.fixture(scope="module") def impersonated_voting(accounts): return accounts.at(contracts.voting.address, force=True) -@pytest.fixture(scope="module") -def nor(interface): - return interface.NodeOperatorsRegistry(contracts.node_operators_registry.address) - - def calc_no_rewards(nor, no_id, shares_minted_as_fees): operator_summary = nor.getNodeOperatorSummary(no_id) module_summary = nor.getStakingModuleSummary() @@ -97,8 +87,51 @@ def deposit_and_check_keys(nor, first_no_id, second_no_id, base_no_id, keys_coun ) -def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale): - (nor_exited_count, _, _) = contracts.staking_router.getStakingModuleSummary(1) +def filter_transfer_logs(logs, transfer_topic): + return list(filter(lambda l: l["topics"][0] == transfer_topic, logs)) + + +def parse_exited_signing_keys_count_changed_logs(logs): + res = [] + for l in logs: + res.append( + { + "nodeOperatorId": eth_abi.decode_abi(["uint256"], l["topics"][1])[0], + "exitedValidatorsCount": eth_abi.decode_single("uint256", bytes.fromhex(l["data"][2:])), + } + ) + return res + + +def parse_stuck_penalty_state_changed_logs(logs): + res = [] + for l in logs: + data = eth_abi.decode(["uint256","uint256","uint256"], bytes.fromhex(l["data"][2:])) + res.append( + { + "nodeOperatorId": eth_abi.decode_abi(["uint256"], l["topics"][1])[0], + "stuckValidatorsCount": data[0], + "refundedValidatorsCount": data[1], + "stuckPenaltyEndTimestamp": data[2], + } + ) + return res + + +def parse_target_validators_count_changed(logs): + res = [] + for l in logs: + res.append( + { + "nodeOperatorId": eth_abi.decode_abi(["uint256"], l["topics"][1])[0], + "targetValidatorsCount": eth_abi.decode_single("uint256", bytes.fromhex(l["data"][2:])), + } + ) + return res + + +def module_happy_path(staking_module, extra_data_service, impersonated_voting, eth_whale): + nor_exited_count, _, _ = contracts.staking_router.getStakingModuleSummary(staking_module.module_id) contracts.staking_router.grantRole( Web3.keccak(text="STAKING_MODULE_MANAGE_ROLE"), @@ -108,7 +141,7 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) contracts.acl.grantPermission( impersonated_voting, - nor, + staking_module, Web3.keccak(text="STAKING_ROUTER_ROLE"), {"from": impersonated_voting}, ) @@ -119,27 +152,27 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) tested_no_id_second = 28 base_no_id = 23 - NO_amount = nor.getNodeOperatorsCount() - for op_index in range(NO_amount): - no = nor.getNodeOperator(op_index, True) + no_amount = staking_module.getNodeOperatorsCount() + for op_index in range(no_amount): + no = staking_module.getNodeOperator(op_index, True) if not no["active"]: continue - nor.setNodeOperatorStakingLimit(op_index, no["totalDepositedValidators"], {"from": impersonated_voting}) + staking_module.setNodeOperatorStakingLimit(op_index, no["totalDepositedValidators"], {"from": impersonated_voting}) - increase_limit(nor, tested_no_id_first, tested_no_id_second, base_no_id, 3, impersonated_voting) + increase_limit(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 3, impersonated_voting) - penalty_delay = nor.getStuckPenaltyDelay() + penalty_delay = staking_module.getStuckPenaltyDelay() - node_operator_first = nor.getNodeOperatorSummary(tested_no_id_first) - address_first = nor.getNodeOperator(tested_no_id_first, False)["rewardAddress"] + node_operator_first = staking_module.getNodeOperatorSummary(tested_no_id_first) + address_first = staking_module.getNodeOperator(tested_no_id_first, False)["rewardAddress"] node_operator_first_balance_shares_before = shares_balance(address_first) - node_operator_second = nor.getNodeOperatorSummary(tested_no_id_second) - address_second = nor.getNodeOperator(tested_no_id_second, False)["rewardAddress"] + node_operator_second = staking_module.getNodeOperatorSummary(tested_no_id_second) + address_second = staking_module.getNodeOperator(tested_no_id_second, False)["rewardAddress"] node_operator_second_balance_shares_before = shares_balance(address_second) - node_operator_base = nor.getNodeOperatorSummary(base_no_id) - address_base_no = nor.getNodeOperator(base_no_id, False)["rewardAddress"] + node_operator_base = staking_module.getNodeOperatorSummary(base_no_id) + address_base_no = staking_module.getNodeOperator(base_no_id, False)["rewardAddress"] node_operator_base_balance_shares_before = shares_balance(address_base_no) # First report - base empty report @@ -150,32 +183,32 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) node_operator_base_balance_shares_after = shares_balance(address_base_no) # expected shares - node_operator_first_rewards_after_first_report = calc_no_rewards( - nor, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] - ) - node_operator_second_rewards_after_first_report = calc_no_rewards( - nor, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] - ) - node_operator_base_rewards_after_first_report = calc_no_rewards( - nor, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] - ) - - # check shares by empty report - assert almostEqWithDiff( - node_operator_first_balance_shares_after - node_operator_first_balance_shares_before, - node_operator_first_rewards_after_first_report, - 1, - ) - assert almostEqWithDiff( - node_operator_second_balance_shares_after - node_operator_second_balance_shares_before, - node_operator_second_rewards_after_first_report, - 1, - ) - assert almostEqWithDiff( - node_operator_base_balance_shares_after - node_operator_base_balance_shares_before, - node_operator_base_rewards_after_first_report, - 1, - ) + # node_operator_first_rewards_after_first_report = calc_no_rewards( + # staking_module, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + # ) + # node_operator_second_rewards_after_first_report = calc_no_rewards( + # staking_module, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + # ) + # node_operator_base_rewards_after_first_report = calc_no_rewards( + # staking_module, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + # ) + # + # # check shares by empty report + # assert almostEqWithDiff( + # node_operator_first_balance_shares_after - node_operator_first_balance_shares_before, + # node_operator_first_rewards_after_first_report, + # 1, + # ) + # assert almostEqWithDiff( + # node_operator_second_balance_shares_after - node_operator_second_balance_shares_before, + # node_operator_second_rewards_after_first_report, + # 1, + # ) + # assert almostEqWithDiff( + # node_operator_base_balance_shares_after - node_operator_base_balance_shares_before, + # node_operator_base_rewards_after_first_report, + # 1, + # ) # Case 1 # --- operator "First" had 5 keys (exited), and 2 keys got stuck (stuck) @@ -189,12 +222,12 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) # Prepare extra data vals_stuck_non_zero = { - node_operator_gindex(1, tested_no_id_first): 2, - node_operator_gindex(1, tested_no_id_second): 2, + node_operator_gindex(staking_module.module_id, tested_no_id_first): 2, + node_operator_gindex(staking_module.module_id, tested_no_id_second): 2, } vals_exited_non_zero = { - node_operator_gindex(1, tested_no_id_first): 5, - node_operator_gindex(1, tested_no_id_second): 5, + node_operator_gindex(staking_module.module_id, tested_no_id_first): 5, + node_operator_gindex(staking_module.module_id, tested_no_id_second): 5, } extra_data = extra_data_service.collect(vals_stuck_non_zero, vals_exited_non_zero, 10, 10) @@ -215,19 +248,19 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) ) # shares after report - node_operator_first = nor.getNodeOperatorSummary(tested_no_id_first) - node_operator_second = nor.getNodeOperatorSummary(tested_no_id_second) - node_operator_base = nor.getNodeOperatorSummary(base_no_id) + node_operator_first = staking_module.getNodeOperatorSummary(tested_no_id_first) + node_operator_second = staking_module.getNodeOperatorSummary(tested_no_id_second) + node_operator_base = staking_module.getNodeOperatorSummary(base_no_id) # expected shares node_operator_first_rewards_after_second_report = calc_no_rewards( - nor, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) node_operator_second_rewards_after_second_report = calc_no_rewards( - nor, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) node_operator_base_rewards_after_second_report = calc_no_rewards( - nor, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) node_operator_first_balance_shares_after = shares_balance(address_first) @@ -276,9 +309,9 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) assert node_operator_base["refundedValidatorsCount"] == 0 assert node_operator_base["stuckPenaltyEndTimestamp"] == 0 - assert nor.isOperatorPenalized(tested_no_id_first) == True - assert nor.isOperatorPenalized(tested_no_id_second) == True - assert nor.isOperatorPenalized(base_no_id) == False + assert staking_module.isOperatorPenalized(tested_no_id_first) + assert staking_module.isOperatorPenalized(tested_no_id_second) + assert not staking_module.isOperatorPenalized(base_no_id) # Events exited_signing_keys_count_events = parse_exited_signing_keys_count_changed_logs( @@ -307,7 +340,7 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) deposited_keys_first_after, deposited_keys_second_after, deposited_keys_base_after, - ) = deposit_and_check_keys(nor, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) + ) = deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) # check don't change deposited keys for penalized NO assert deposited_keys_first_before == deposited_keys_first_after @@ -325,10 +358,10 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) # Prepare extra data - first node operator has exited 2 + 5 keys an stuck 0 vals_stuck_non_zero = { - node_operator_gindex(1, tested_no_id_first): 0, + node_operator_gindex(staking_module.module_id, tested_no_id_first): 0, } vals_exited_non_zero = { - node_operator_gindex(1, tested_no_id_first): 7, + node_operator_gindex(staking_module.module_id, tested_no_id_first): 7, } extra_data = extra_data_service.collect(vals_stuck_non_zero, vals_exited_non_zero, 10, 10) @@ -350,9 +383,9 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) stakingModuleIdsWithNewlyExitedValidators=[1], ) - node_operator_first = nor.getNodeOperatorSummary(tested_no_id_first) - node_operator_second = nor.getNodeOperatorSummary(tested_no_id_second) - node_operator_base = nor.getNodeOperatorSummary(base_no_id) + node_operator_first = staking_module.getNodeOperatorSummary(tested_no_id_first) + node_operator_second = staking_module.getNodeOperatorSummary(tested_no_id_second) + node_operator_base = staking_module.getNodeOperatorSummary(base_no_id) # shares after report node_operator_first_balance_shares_after = shares_balance(address_first) @@ -361,13 +394,13 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) # expected shares node_operator_first_rewards_after_third_report = calc_no_rewards( - nor, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) node_operator_second_rewards_after__third_report = calc_no_rewards( - nor, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) node_operator_base_rewards_after__third_report = calc_no_rewards( - nor, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) # first NO has penalty has a penalty until stuckPenaltyEndTimestamp @@ -412,9 +445,9 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) assert node_operator_second["refundedValidatorsCount"] == 0 assert node_operator_second["stuckPenaltyEndTimestamp"] == 0 - assert nor.isOperatorPenalized(tested_no_id_first) == True - assert nor.isOperatorPenalized(tested_no_id_second) == True - assert nor.isOperatorPenalized(base_no_id) == False + assert staking_module.isOperatorPenalized(tested_no_id_first) == True + assert staking_module.isOperatorPenalized(tested_no_id_second) == True + assert staking_module.isOperatorPenalized(base_no_id) == False # events exited_signing_keys_count_events = parse_exited_signing_keys_count_changed_logs( @@ -445,14 +478,14 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) chain.mine() # Clear penalty for first NO after penalty delay - nor.clearNodeOperatorPenalty(tested_no_id_first, {"from": impersonated_voting}) + staking_module.clearNodeOperatorPenalty(tested_no_id_first, {"from": impersonated_voting}) # Prepare extra data for report by second NO vals_stuck_non_zero = { - node_operator_gindex(1, tested_no_id_second): 2, + node_operator_gindex(staking_module.module_id, tested_no_id_second): 2, } vals_exited_non_zero = { - node_operator_gindex(1, tested_no_id_second): 5, + node_operator_gindex(staking_module.module_id, tested_no_id_second): 5, } extra_data = extra_data_service.collect(vals_stuck_non_zero, vals_exited_non_zero, 10, 10) @@ -472,9 +505,9 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) stakingModuleIdsWithNewlyExitedValidators=[1], ) - node_operator_first = nor.getNodeOperatorSummary(tested_no_id_first) - node_operator_second = nor.getNodeOperatorSummary(tested_no_id_second) - node_operator_base = nor.getNodeOperatorSummary(base_no_id) + node_operator_first = staking_module.getNodeOperatorSummary(tested_no_id_first) + node_operator_second = staking_module.getNodeOperatorSummary(tested_no_id_second) + node_operator_base = staking_module.getNodeOperatorSummary(base_no_id) # shares after report node_operator_first_balance_shares_after = shares_balance(address_first) @@ -483,13 +516,13 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) # expected shares node_operator_first_rewards_after_fourth_report = calc_no_rewards( - nor, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) node_operator_second_rewards_after__fourth_report = calc_no_rewards( - nor, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) node_operator_base_rewards_after__fourth_report = calc_no_rewards( - nor, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) # Penalty ended for first operator @@ -531,9 +564,9 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) assert node_operator_second["refundedValidatorsCount"] == 0 assert node_operator_second["stuckPenaltyEndTimestamp"] == 0 - assert nor.isOperatorPenalized(tested_no_id_first) == False - assert nor.isOperatorPenalized(tested_no_id_second) == True - assert nor.isOperatorPenalized(base_no_id) == False + assert not staking_module.isOperatorPenalized(tested_no_id_first) + assert staking_module.isOperatorPenalized(tested_no_id_second) + assert not staking_module.isOperatorPenalized(base_no_id) # Deposit ( @@ -543,7 +576,7 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) deposited_keys_first_after, deposited_keys_second_after, deposited_keys_base_after, - ) = deposit_and_check_keys(nor, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) + ) = deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) # check don't change deposited keys for penalized NO (only second NO) assert deposited_keys_first_before != deposited_keys_first_after @@ -560,7 +593,7 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) # - Check NOs stats # # Refund 2 keys Second NO - contracts.staking_router.updateRefundedValidatorsCount(1, tested_no_id_second, 2, {"from": impersonated_voting}) + contracts.staking_router.updateRefundedValidatorsCount(staking_module.module_id, tested_no_id_second, 2, {"from": impersonated_voting}) # shares before report node_operator_first_balance_shares_before = shares_balance(address_first) @@ -575,19 +608,19 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) node_operator_second_balance_shares_after = shares_balance(address_second) node_operator_base_balance_shares_after = shares_balance(address_base_no) - node_operator_first = nor.getNodeOperatorSummary(tested_no_id_first) - node_operator_second = nor.getNodeOperatorSummary(tested_no_id_second) - node_operator_base = nor.getNodeOperatorSummary(base_no_id) + node_operator_first = staking_module.getNodeOperatorSummary(tested_no_id_first) + node_operator_second = staking_module.getNodeOperatorSummary(tested_no_id_second) + node_operator_base = staking_module.getNodeOperatorSummary(base_no_id) # expected shares node_operator_first_rewards_after_fifth_report = calc_no_rewards( - nor, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) node_operator_second_rewards_after_fifth_report = calc_no_rewards( - nor, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) node_operator_base_rewards_after_fifth_report = calc_no_rewards( - nor, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) # Penalty only for second operator @@ -627,8 +660,8 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) assert node_operator_second["refundedValidatorsCount"] == 2 assert node_operator_second["stuckPenaltyEndTimestamp"] > chain.time() - assert nor.isOperatorPenaltyCleared(tested_no_id_first) == True - assert nor.isOperatorPenaltyCleared(tested_no_id_second) == False + assert staking_module.isOperatorPenaltyCleared(tested_no_id_first) == True + assert staking_module.isOperatorPenaltyCleared(tested_no_id_second) == False # Case 5 # -- PENALTY_DELAY time passes @@ -644,7 +677,7 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) chain.mine() # Clear penalty for second NO after penalty delay - nor.clearNodeOperatorPenalty(tested_no_id_second, {"from": impersonated_voting}) + staking_module.clearNodeOperatorPenalty(tested_no_id_second, {"from": impersonated_voting}) # shares before report node_operator_first_balance_shares_before = shares_balance(address_first) @@ -654,30 +687,30 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) # Seventh report (report_tx, extra_report_tx) = oracle_report() - assert nor.isOperatorPenalized(tested_no_id_first) == False - assert nor.isOperatorPenalized(tested_no_id_second) == False - assert nor.isOperatorPenalized(base_no_id) == False + assert not staking_module.isOperatorPenalized(tested_no_id_first) + assert not staking_module.isOperatorPenalized(tested_no_id_second) + assert not staking_module.isOperatorPenalized(base_no_id) # shares after report node_operator_first_balance_shares_after = shares_balance(address_first) node_operator_second_balance_shares_after = shares_balance(address_second) node_operator_base_balance_shares_after = shares_balance(address_base_no) - assert nor.isOperatorPenaltyCleared(tested_no_id_first) == True - assert nor.isOperatorPenaltyCleared(tested_no_id_second) == True + assert staking_module.isOperatorPenaltyCleared(tested_no_id_first) + assert staking_module.isOperatorPenaltyCleared(tested_no_id_second) - node_operator_first = nor.getNodeOperatorSummary(tested_no_id_first) - node_operator_second = nor.getNodeOperatorSummary(tested_no_id_second) + node_operator_first = staking_module.getNodeOperatorSummary(tested_no_id_first) + node_operator_second = staking_module.getNodeOperatorSummary(tested_no_id_second) # expected shares node_operator_first_rewards_after_seventh_report = calc_no_rewards( - nor, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) node_operator_second_rewards_after_seventh_report = calc_no_rewards( - nor, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) node_operator_base_rewards_after_seventh_report = calc_no_rewards( - nor, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + staking_module, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) # No penalty @@ -716,7 +749,7 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) deposited_keys_first_after, deposited_keys_second_after, deposited_keys_base_after, - ) = deposit_and_check_keys(nor, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) + ) = deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) # check deposit is applied for all NOs assert deposited_keys_first_before != deposited_keys_first_after @@ -724,8 +757,8 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) assert deposited_keys_base_before != deposited_keys_base_after for op_index in (tested_no_id_first, tested_no_id_second, base_no_id): - no = nor.getNodeOperator(op_index, True) - nor.setNodeOperatorStakingLimit(op_index, no["totalDepositedValidators"] + 10, {"from": impersonated_voting}) + no = staking_module.getNodeOperator(op_index, True) + staking_module.setNodeOperatorStakingLimit(op_index, no["totalDepositedValidators"] + 10, {"from": impersonated_voting}) # Case 6 # -- SActivate target limit for "First" NO @@ -741,11 +774,11 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) # - Check deposits (should be not 0 for "First" NO) # Activate target limit - first_no_summary_before = nor.getNodeOperatorSummary(tested_no_id_first) + first_no_summary_before = staking_module.getNodeOperatorSummary(tested_no_id_first) assert first_no_summary_before["depositableValidatorsCount"] > 0 - target_limit_tx = nor.updateTargetValidatorsLimits(tested_no_id_first, True, 0, {"from": STAKING_ROUTER}) + target_limit_tx = staking_module.updateTargetValidatorsLimits(tested_no_id_first, True, 0, {"from": STAKING_ROUTER}) target_validators_count_changed_events = parse_target_validators_count_changed( filter_transfer_logs(target_limit_tx.logs, web3.keccak(text="TargetValidatorsCountChanged(uint256,uint256)")) @@ -753,10 +786,10 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) assert target_validators_count_changed_events[0]["nodeOperatorId"] == tested_no_id_first assert target_validators_count_changed_events[0]["targetValidatorsCount"] == 0 - first_no_summary_after = nor.getNodeOperatorSummary(tested_no_id_first) + first_no_summary_after = staking_module.getNodeOperatorSummary(tested_no_id_first) assert first_no_summary_after["depositableValidatorsCount"] == 0 - assert first_no_summary_after["isTargetLimitActive"] == True + assert first_no_summary_after["isTargetLimitActive"] # Deposit ( @@ -766,7 +799,7 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) deposited_keys_first_after, deposited_keys_second_after, deposited_keys_base_after, - ) = deposit_and_check_keys(nor, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) + ) = deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) # check deposit is not applied for first NO assert deposited_keys_first_before == deposited_keys_first_after @@ -774,16 +807,16 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) assert deposited_keys_base_before != deposited_keys_base_after # Disable target limit - target_limit_tx = nor.updateTargetValidatorsLimits(tested_no_id_first, False, 0, {"from": STAKING_ROUTER}) + target_limit_tx = staking_module.updateTargetValidatorsLimits(tested_no_id_first, False, 0, {"from": STAKING_ROUTER}) target_validators_count_changed_events = parse_target_validators_count_changed( filter_transfer_logs(target_limit_tx.logs, web3.keccak(text="TargetValidatorsCountChanged(uint256,uint256)")) ) assert target_validators_count_changed_events[0]["nodeOperatorId"] == tested_no_id_first - first_no_summary_after = nor.getNodeOperatorSummary(tested_no_id_first) + first_no_summary_after = staking_module.getNodeOperatorSummary(tested_no_id_first) assert first_no_summary_after["depositableValidatorsCount"] > 0 - assert first_no_summary_after["isTargetLimitActive"] == False + assert not first_no_summary_after["isTargetLimitActive"] # Deposit ( @@ -793,7 +826,7 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) deposited_keys_first_after, deposited_keys_second_after, deposited_keys_base_after, - ) = deposit_and_check_keys(nor, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) + ) = deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) # check - deposit not applied to NOs. assert deposited_keys_first_before != deposited_keys_first_after @@ -801,44 +834,13 @@ def test_node_operators(nor, extra_data_service, impersonated_voting, eth_whale) assert deposited_keys_base_before != deposited_keys_base_after -def filter_transfer_logs(logs, transfer_topic): - return list(filter(lambda l: l["topics"][0] == transfer_topic, logs)) +def test_node_operator_registry(impersonated_voting, eth_whale): + nor = contracts.node_operators_registry + nor.module_id = 1 + module_happy_path(nor, ExtraDataService(), impersonated_voting, eth_whale) -def parse_exited_signing_keys_count_changed_logs(logs): - res = [] - for l in logs: - res.append( - { - "nodeOperatorId": eth_abi.decode_abi(["uint256"], l["topics"][1])[0], - "exitedValidatorsCount": eth_abi.decode_single("uint256", bytes.fromhex(l["data"][2:])), - } - ) - return res - - -def parse_stuck_penalty_state_changed_logs(logs): - res = [] - for l in logs: - data = eth_abi.decode(["uint256","uint256","uint256"], bytes.fromhex(l["data"][2:])) - res.append( - { - "nodeOperatorId": eth_abi.decode_abi(["uint256"], l["topics"][1])[0], - "stuckValidatorsCount": data[0], - "refundedValidatorsCount": data[1], - "stuckPenaltyEndTimestamp": data[2], - } - ) - return res - -def parse_target_validators_count_changed(logs): - res = [] - for l in logs: - res.append( - { - "nodeOperatorId": eth_abi.decode_abi(["uint256"], l["topics"][1])[0], - "targetValidatorsCount": eth_abi.decode_single("uint256", bytes.fromhex(l["data"][2:])), - } - ) - return res - +def test_sdvt(impersonated_voting, eth_whale): + sdvt = contracts.simple_dvt + sdvt.module_id = 2 + module_happy_path(sdvt, ExtraDataService(), impersonated_voting, eth_whale) diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 00000000..e69de29b From d9f21c3054843a704285eaed9fdd1b924704877a Mon Sep 17 00:00:00 2001 From: F4ever Date: Fri, 9 Feb 2024 22:46:15 +0100 Subject: [PATCH 19/68] Fix: uncomment --- tests/regression/conftest.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/regression/conftest.py b/tests/regression/conftest.py index 097e86e1..dbaac6d4 100644 --- a/tests/regression/conftest.py +++ b/tests/regression/conftest.py @@ -17,12 +17,12 @@ def autoexecute_vote(helpers, vote_ids_from_env, accounts, stranger): if vote_ids_from_env: helpers.execute_votes(accounts, vote_ids_from_env, contracts.voting, topup="0.5 ether") - # else: - # start_and_execute_votes(contracts.voting, helpers) + else: + start_and_execute_votes(contracts.voting, helpers) - # if os.getenv(ENV_FILL_SIMPLE_DVT): - # print(f"Prefilling SimpleDVT...") - # fill_simple_dvt_ops_vetted_keys(stranger) + if os.getenv(ENV_FILL_SIMPLE_DVT): + print(f"Prefilling SimpleDVT...") + fill_simple_dvt_ops_vetted_keys(stranger) if os.getenv(ENV_REPORT_AFTER_VOTE): oracle_report(cl_diff=ETH(523), exclude_vaults_balances=False) From 1817fcdfa8560224640ec8b8f57b3b916f5eba1f Mon Sep 17 00:00:00 2001 From: KRogLA Date: Mon, 12 Feb 2024 01:56:49 +0100 Subject: [PATCH 20/68] fix: vote specific test --- configs/config_mainnet.py | 3 +- tests/test_simple_dvt.py | 142 +++++++++++++++--- utils/test/event_validators/repo_upgrade.py | 87 ++++++++++- utils/test/event_validators/staking_router.py | 48 ++++++ 4 files changed, 251 insertions(+), 29 deletions(-) create mode 100644 utils/test/event_validators/staking_router.py diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index badb2e99..8eece8af 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -30,6 +30,7 @@ NODE_OPERATORS_REGISTRY_IMPL_V1 = "0x5d39ABaa161e622B99D45616afC8B837E9F19a25" # Aragon APM Repos +REPO_APP_ID = "0xbe49cbb8894efb45c933fd09dc87bdd94909553a9e1f511d7fc10f3dad1564f2" APM_REGISTRY = "0x0cb113890b04B49455DfE06554e2D784598A29C9" VOTING_REPO = "0x4Ee3118E3858E8D7164A634825BfE0F73d99C792" LIDO_REPO = "0xF5Dc67E54FC96F993CD06073f71ca732C1E654B1" @@ -141,7 +142,7 @@ # NodeOperatorsRegistry clone aka SimpleDVT SIMPLE_DVT_IMPL = "0x8538930c385C0438A357d2c25CB3eAD95Ab6D8ed" ## see SimpleDVT's proxy appId() -# SIMPLE_DVT_ARAGON_APP_NAME = "simple-dvt" +SIMPLE_DVT_ARAGON_APP_NAME = "simple-dvt" SIMPLE_DVT_ARAGON_APP_ID = "0xe1635b63b5f7b5e545f2a637558a4029dea7905361a2f0fc28c66e9136cf86a4" SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY = 432000 SIMPLE_DVT_MODULE_TARGET_SHARE_BP = 50 diff --git a/tests/test_simple_dvt.py b/tests/test_simple_dvt.py index 1dd64f35..d293e44f 100644 --- a/tests/test_simple_dvt.py +++ b/tests/test_simple_dvt.py @@ -6,12 +6,15 @@ from typing import List from scripts.vote_simple_dvt import start_vote from brownie import interface, ZERO_ADDRESS, reverts, web3, accounts, convert, network +from utils.test.event_validators.aragon import validate_app_update_event, validate_push_to_repo_event +from utils.test.event_validators.common import validate_events_chain +from utils.test.event_validators.staking_router import StakingModuleItem, validate_staking_module_added_event from utils.test.tx_tracing_helpers import * -from utils.test.event_validators.permission import Permission from utils.config import contracts, LDO_HOLDER_ADDRESS_FOR_TESTS, network_name -from utils.test.helpers import almostEqWithDiff + from configs.config_mainnet import ( SIMPLE_DVT_IMPL, + SIMPLE_DVT_ARAGON_APP_NAME, SIMPLE_DVT_ARAGON_APP_ID, SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY, SIMPLE_DVT_MODULE_TARGET_SHARE_BP, @@ -30,31 +33,24 @@ EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY, EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY, EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY, + REPO_APP_ID, +) +from utils.test.event_validators.repo_upgrade import ( + CREATE_VERSION_ROLE, + NewRepoItem, + validate_new_repo_with_version_event, ) -from utils.test.easy_track_helpers import create_and_enact_payment_motion, check_add_and_remove_recipient_with_voting from utils.test.event_validators.permission import ( Permission, validate_grant_role_event, validate_permission_revoke_event, validate_permission_grantp_event, -) -from utils.test.event_validators.hash_consensus import ( - validate_hash_consensus_member_removed, - validate_hash_consensus_member_added, -) -from utils.test.event_validators.node_operators_registry import ( - validate_node_operator_deactivated, - validate_node_operator_name_set_event, - NodeOperatorNameSetItem, + validate_permission_grant_event, + validate_permission_create_event, ) from utils.test.event_validators.easy_track import ( validate_evmscript_factory_added_event, EVMScriptFactoryAdded, - validate_evmscript_factory_removed_event, -) -from utils.test.event_validators.allowed_recipients_registry import ( - validate_set_limit_parameter_event, - validate_update_spent_amount_event, ) from utils.easy_track import create_permissions from utils.voting import find_metadata_by_vote_id @@ -62,7 +58,6 @@ REQUEST_BURN_SHARES_ROLE = "0x4be29e0e4eb91f98f709d98803cba271592782e293b84a625e025cbb40197ba8" -CREATE_VERSION_ROLE = "0x1f56cfecd3595a2e6cc1a7e6cb0b20df84cdbd92eff2fee554e70e4e45a9a7d8" STAKING_ROUTER_ROLE = "0xbb75b874360e0bfd87f964eadd8276d8efb7c942134fc329b513032d0803e0c6" MANAGE_NODE_OPERATOR_ROLE = "0x78523850fdd761612f46e844cf5a16bda6b3151d6ae961fd7e8e7b92bfbca7f8" SET_NODE_OPERATOR_LIMIT_ROLE = "0x07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754" @@ -72,6 +67,7 @@ simple_dvt_content_uri = ( "0x697066733a516d615353756a484347636e4675657441504777565735426567614d42766e355343736769334c5366767261536f" ) +simple_dvt_semantic_version = (1, 0, 0) def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_decoding, ldo_holder): @@ -80,6 +76,7 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco burner = contracts.burner voting = contracts.voting acl = contracts.acl + agent = contracts.agent easy_track = contracts.easy_track staking_router = contracts.staking_router @@ -135,7 +132,7 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco # Latest version in repo is 1st and only one latest_ver = simple_dvt_repo.getLatest() - assert latest_ver["semanticVersion"] == (1, 0, 0) + assert latest_ver["semanticVersion"] == simple_dvt_semantic_version assert latest_ver["contractAddress"] == SIMPLE_DVT_IMPL assert latest_ver["contentURI"] == simple_dvt_content_uri @@ -149,9 +146,9 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco assert module["stakingModuleFee"] == SIMPLE_DVT_MODULE_MODULE_FEE_BP assert module["treasuryFee"] == SIMPLE_DVT_MODULE_TREASURY_FEE_BP assert module["targetShare"] == SIMPLE_DVT_MODULE_TARGET_SHARE_BP - # assert simple_dvt_module["status"] == simple_dvt.address + assert module["status"] == 0 # StakingModuleStatus.Active assert module["name"] == SIMPLE_DVT_MODULE_NAME - # assert simple_dvt_module["lastDepositBlock"] == vote_tx.block_number + assert module["lastDepositBlock"] == vote_tx.block_number assert module["exitedValidatorsCount"] == 0 # SimpleDVT app papams @@ -259,16 +256,73 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER ) - if bypass_events_decoding or network_name() in ("goerli", "goerli-fork"): - return - # validate vote events assert count_vote_items_by_events(vote_tx, contracts.voting) == 18, "Incorrect voting items count" + metadata = find_metadata_by_vote_id(vote_id) + print("metadata", metadata) + + # TODO fix description + # assert get_lido_vote_cid_from_str(metadata) == "bafkreibugpzhp7nexxg7c6jpmmszikvaj2vscxw426zewa6uyv3z5y6ak4" + display_voting_events(vote_tx) + if bypass_events_decoding or network_name() in ("goerli", "goerli-fork"): + return + evs = group_voting_events(vote_tx) + repo_params = NewRepoItem( + name=SIMPLE_DVT_ARAGON_APP_NAME, + app=simple_dvt.address, + app_id=SIMPLE_DVT_ARAGON_APP_ID, + repo_app_id=REPO_APP_ID, + semantic_version=simple_dvt_semantic_version, + apm=contracts.apm_registry.address, + manager=voting.address, + ) + validate_new_repo_with_version_event(evs[0], repo_params) + + validate_app_update_event(evs[1], SIMPLE_DVT_ARAGON_APP_ID, SIMPLE_DVT_IMPL) + + validate_simple_dvt_intialize_event(evs[2]) + + # Create and grant permission STAKING_ROUTER_ROLE on SimpleDVT module for StakingRouter + permission = Permission( + entity=staking_router, + app=simple_dvt, + role=STAKING_ROUTER_ROLE, # simple_dvt.STAKING_ROUTER_ROLE(), + ) + validate_permission_create_event(evs[3], permission, manager=voting) + + # Grant STAKING_ROUTER_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor + permission = Permission(entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=STAKING_ROUTER_ROLE) + validate_permission_grant_event(evs[4], permission) + + # Create and grant permission MANAGE_NODE_OPERATOR_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor + permission = Permission( + entity=EASYTRACK_EVMSCRIPT_EXECUTOR, + app=simple_dvt, + role=MANAGE_NODE_OPERATOR_ROLE, # simple_dvt.MANAGE_NODE_OPERATOR_ROLE(), + ) + validate_permission_create_event(evs[5], permission, manager=voting) + + # Create and grant permission SET_NODE_OPERATOR_LIMIT_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor + permission = Permission( + entity=EASYTRACK_EVMSCRIPT_EXECUTOR, + app=simple_dvt, + role=SET_NODE_OPERATOR_LIMIT_ROLE, # simple_dvt.SET_NODE_OPERATOR_LIMIT_ROLE(), + ) + validate_permission_create_event(evs[6], permission, manager=voting) + + # Create and grant permission MANAGE_SIGNING_KEYS on SimpleDVT module for EasyTrackEVMScriptExecutor + permission = Permission( + entity=EASYTRACK_EVMSCRIPT_EXECUTOR, + app=simple_dvt, + role=MANAGE_SIGNING_KEYS, # simple_dvt.MANAGE_SIGNING_KEYS(), + ) + validate_permission_create_event(evs[7], permission, manager=EASYTRACK_EVMSCRIPT_EXECUTOR) + validate_evmscript_factory_added_event( evs[8], EVMScriptFactoryAdded( @@ -330,8 +384,48 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ), ) + validate_grant_role_event(evs[16], REQUEST_BURN_SHARES_ROLE, simple_dvt, agent) + + module_item = StakingModuleItem( + SIMPLE_DVT_MODULE_ID, + simple_dvt.address, + SIMPLE_DVT_MODULE_NAME, + SIMPLE_DVT_MODULE_TARGET_SHARE_BP, + SIMPLE_DVT_MODULE_MODULE_FEE_BP, + SIMPLE_DVT_MODULE_TREASURY_FEE_BP, + ) + validate_staking_module_added_event(evs[17], module_item) + def has_permission(permission: Permission, how: List[int]) -> bool: return contracts.acl.hasPermission["address,address,bytes32,uint[]"]( permission.entity, permission.app, permission.role, how ) + + +def validate_simple_dvt_intialize_event(event: EventDict): + _repo_upgrade_events_chain = [ + "LogScriptCall", + "ContractVersionSet", + "StuckPenaltyDelayChanged", + "Approval", + "LocatorContractSet", + "StakingModuleTypeSet", + ] + + validate_events_chain([e.name for e in event], _repo_upgrade_events_chain) + + assert event.count("ContractVersionSet") == 1 + assert event.count("StuckPenaltyDelayChanged") == 1 + assert event.count("Approval") == 1 + assert event.count("LocatorContractSet") == 1 + assert event.count("StakingModuleTypeSet") == 1 + + assert event["Approval"]["owner"] == contracts.simple_dvt.address + assert event["Approval"]["spender"] == contracts.burner.address + assert event["Approval"]["value"] == 2**256 - 1 # uint256 max + + assert event["ContractVersionSet"]["version"] == 2 + assert event["StuckPenaltyDelayChanged"]["stuckPenaltyDelay"] == SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY + assert event["LocatorContractSet"]["locatorAddress"] == contracts.lido_locator.address + assert event["StakingModuleTypeSet"]["moduleType"] == SIMPLE_DVT_MODULE_TYPE diff --git a/utils/test/event_validators/repo_upgrade.py b/utils/test/event_validators/repo_upgrade.py index fd7efa37..049c1007 100644 --- a/utils/test/event_validators/repo_upgrade.py +++ b/utils/test/event_validators/repo_upgrade.py @@ -5,6 +5,8 @@ from brownie.network.event import EventDict from .common import validate_events_chain +CREATE_VERSION_ROLE = "0x1f56cfecd3595a2e6cc1a7e6cb0b20df84cdbd92eff2fee554e70e4e45a9a7d8" + class RepoUpgrade(NamedTuple): version_id: int @@ -12,11 +14,88 @@ class RepoUpgrade(NamedTuple): def validate_repo_upgrade_event(event: EventDict, ru: RepoUpgrade): - _repo_upgrade_events_chain = ['LogScriptCall', 'NewVersion'] + _repo_upgrade_events_chain = ["LogScriptCall", "NewVersion"] + + validate_events_chain([e.name for e in event], _repo_upgrade_events_chain) + + assert event.count("NewVersion") == 1 + + assert event["NewVersion"]["versionId"] == ru.version_id + assert event["NewVersion"]["semanticVersion"] == ru.semantic_version + + +class NewRepoItem(NamedTuple): + name: str + app: str + app_id: str + repo_app_id: str + semantic_version: Tuple[int, int, int] + apm: str + manager: str + + +def validate_new_repo_with_version_event(event: EventDict, new_repo_item: NewRepoItem): + _repo_upgrade_events_chain = [ + "LogScriptCall", + "NewAppProxy", + "SetPermission", + "ChangePermissionManager", + "NewOwner", + "NewName", + "NewResolver", + "AddressChanged", + "AddrChanged", + "NewRepo", + "NewVersion", + "SetPermission", + "SetPermission", + "ChangePermissionManager", + ] validate_events_chain([e.name for e in event], _repo_upgrade_events_chain) - assert event.count('NewVersion') == 1 + assert event.count("NewAppProxy") == 1 + assert event.count("NewRepo") == 1 + assert event.count("NewVersion") == 1 + + # expected ENS events + assert event.count("NewOwner") == 1 + assert event.count("NewName") == 1 + assert event.count("NewResolver") == 1 + + assert event["NewAppProxy"]["isUpgradeable"] == True + assert event["NewAppProxy"]["appId"] == new_repo_item.repo_app_id + + repo = event["NewAppProxy"]["proxy"] + + assert event["NewRepo"]["id"] == new_repo_item.app_id + assert event["NewRepo"]["name"] == new_repo_item.name + assert event["NewRepo"]["repo"] == repo + + assert event["NewVersion"]["semanticVersion"] == new_repo_item.semantic_version, "Wrong version" + + assert event.count("SetPermission") == 3 + assert event.count("ChangePermissionManager") == 2 + + assert event["SetPermission"][0]["entity"] == new_repo_item.apm + assert event["SetPermission"][0]["app"] == repo + assert event["SetPermission"][0]["role"] == CREATE_VERSION_ROLE + assert event["SetPermission"][0]["allowed"] == True + + assert event["ChangePermissionManager"][0]["app"] == repo + assert event["ChangePermissionManager"][0]["role"] == CREATE_VERSION_ROLE + assert event["ChangePermissionManager"][0]["manager"] == new_repo_item.apm + + assert event["SetPermission"][1]["entity"] == new_repo_item.apm + assert event["SetPermission"][1]["app"] == repo + assert event["SetPermission"][1]["role"] == CREATE_VERSION_ROLE + assert event["SetPermission"][1]["allowed"] == False + + assert event["SetPermission"][2]["entity"] == new_repo_item.manager + assert event["SetPermission"][2]["app"] == repo + assert event["SetPermission"][2]["role"] == CREATE_VERSION_ROLE + assert event["SetPermission"][2]["allowed"] == True - assert event['NewVersion']['versionId'] == ru.version_id - assert event['NewVersion']['semanticVersion'] == ru.semantic_version + assert event["ChangePermissionManager"][1]["app"] == repo + assert event["ChangePermissionManager"][1]["role"] == CREATE_VERSION_ROLE + assert event["ChangePermissionManager"][1]["manager"] == new_repo_item.manager diff --git a/utils/test/event_validators/staking_router.py b/utils/test/event_validators/staking_router.py new file mode 100644 index 00000000..8b36ad72 --- /dev/null +++ b/utils/test/event_validators/staking_router.py @@ -0,0 +1,48 @@ +#!/usr/bin/python3 + +from typing import NamedTuple, Tuple + +from brownie.network.event import EventDict +from .common import validate_events_chain + + +class StakingModuleItem(NamedTuple): + id: int + address: str + name: str + target_share: int + module_fee: int + treasury_fee: int + + +def validate_staking_module_added_event(event: EventDict, module_item: StakingModuleItem): + _events_chain = [ + "LogScriptCall", + "LogScriptCall", + "StakingRouterETHDeposited", + "StakingModuleAdded", + "StakingModuleTargetShareSet", + "StakingModuleFeesSet", + "ScriptResult", + ] + + validate_events_chain([e.name for e in event], _events_chain) + + assert event.count("StakingRouterETHDeposited") == 1 + assert event.count("StakingModuleAdded") == 1 + assert event.count("StakingModuleTargetShareSet") == 1 + assert event.count("StakingModuleFeesSet") == 1 + + assert event["StakingRouterETHDeposited"]["stakingModuleId"] == module_item.id + assert event["StakingRouterETHDeposited"]["amount"] == 0 + + assert event["StakingModuleAdded"]["stakingModuleId"] == module_item.id + assert event["StakingModuleAdded"]["stakingModule"] == module_item.address + assert event["StakingModuleAdded"]["name"] == module_item.name + + assert event["StakingModuleTargetShareSet"]["stakingModuleId"] == module_item.id + assert event["StakingModuleTargetShareSet"]["targetShare"] == module_item.target_share + + assert event["StakingModuleFeesSet"]["stakingModuleId"] == module_item.id + assert event["StakingModuleFeesSet"]["stakingModuleFee"] == module_item.module_fee + assert event["StakingModuleFeesSet"]["treasuryFee"] == module_item.treasury_fee From d0d9847e2f4675307918f217afd5eb008f15002a Mon Sep 17 00:00:00 2001 From: KRogLA Date: Mon, 12 Feb 2024 03:07:37 +0100 Subject: [PATCH 21/68] fix: simple dvt acceptance test --- tests/acceptance/test_simple_dvt_module.py | 256 +++++++++++++++++++++ tests/acceptance/test_staking_router.py | 7 +- 2 files changed, 259 insertions(+), 4 deletions(-) create mode 100644 tests/acceptance/test_simple_dvt_module.py diff --git a/tests/acceptance/test_simple_dvt_module.py b/tests/acceptance/test_simple_dvt_module.py new file mode 100644 index 00000000..4721fded --- /dev/null +++ b/tests/acceptance/test_simple_dvt_module.py @@ -0,0 +1,256 @@ +import pytest +from brownie import ZERO_ADDRESS, interface, web3, reverts # type: ignore + +from utils.config import ( + contracts, + SIMPLE_DVT, + SIMPLE_DVT_IMPL, + SIMPLE_DVT_ARAGON_APP_ID, + SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY, + SIMPLE_DVT_MODULE_TYPE, + EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, + EASYTRACK_EVMSCRIPT_EXECUTOR, + EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY, + EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY, + EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY, + EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY, + EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_NAMES_FACTORY, + EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY, + EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY, + EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY, +) + +REQUEST_BURN_SHARES_ROLE = "0x4be29e0e4eb91f98f709d98803cba271592782e293b84a625e025cbb40197ba8" +STAKING_ROUTER_ROLE = "0xbb75b874360e0bfd87f964eadd8276d8efb7c942134fc329b513032d0803e0c6" +MANAGE_NODE_OPERATOR_ROLE = "0x78523850fdd761612f46e844cf5a16bda6b3151d6ae961fd7e8e7b92bfbca7f8" +SET_NODE_OPERATOR_LIMIT_ROLE = "0x07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754" +MANAGE_SIGNING_KEYS = "0x75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee" + + +@pytest.fixture(scope="module") +def contract() -> interface.SimpleDVT: + return interface.SimpleDVT(SIMPLE_DVT) + + +def test_links(contract): + assert contract.getLocator() == contracts.lido_locator + + +def test_aragon(contract): + proxy = interface.AppProxyUpgradeable(contract) + assert proxy.implementation() == SIMPLE_DVT_IMPL + assert contract.kernel() == contracts.kernel + assert contract.appId() == SIMPLE_DVT_ARAGON_APP_ID + assert contract.hasInitialized() == True + assert contract.isPetrified() == False + + +def test_role_keccaks(contract): + assert contract.MANAGE_SIGNING_KEYS() == web3.keccak(text="MANAGE_SIGNING_KEYS").hex() + assert contract.SET_NODE_OPERATOR_LIMIT_ROLE() == web3.keccak(text="SET_NODE_OPERATOR_LIMIT_ROLE").hex() + assert contract.MANAGE_NODE_OPERATOR_ROLE() == web3.keccak(text="MANAGE_NODE_OPERATOR_ROLE").hex() + assert contract.STAKING_ROUTER_ROLE() == web3.keccak(text="STAKING_ROUTER_ROLE").hex() + + +def test_versioned(contract): + assert contract.getContractVersion() == 2 + + +def test_initialize(contract): + with reverts("INIT_ALREADY_INITIALIZED"): + contract.initialize( + contracts.lido_locator, + SIMPLE_DVT_MODULE_TYPE, + SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY, + {"from": contracts.voting}, + ) + + +def test_finalize_upgrade(contract): + with reverts("UNEXPECTED_CONTRACT_VERSION"): + contract.finalizeUpgrade_v2( + contracts.lido_locator, + SIMPLE_DVT_MODULE_TYPE, + SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY, + {"from": contracts.voting}, + ) + + +def test_petrified(): + contract = interface.SimpleDVT(SIMPLE_DVT_IMPL) + with reverts("INIT_ALREADY_INITIALIZED"): + contract.initialize( + contracts.lido_locator, + SIMPLE_DVT_MODULE_TYPE, + SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY, + {"from": contracts.voting}, + ) + + with reverts("CONTRACT_NOT_INITIALIZED"): + contract.finalizeUpgrade_v2( + contracts.lido_locator, + SIMPLE_DVT_MODULE_TYPE, + SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY, + {"from": contracts.voting}, + ) + + +def test_simple_dvt_state(contract): + node_operators_count = contract.getNodeOperatorsCount() + # assert node_operators_count == CURATED_STAKING_MODULE_OPERATORS_COUNT + # assert contract.getActiveNodeOperatorsCount() == CURATED_STAKING_MODULE_OPERATORS_ACTIVE_COUNT + assert contract.getNonce() >= 0 + assert contract.getStuckPenaltyDelay() == SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY + assert contract.getType() == _str_to_bytes32("curated-onchain-v1") + + summary = contract.getStakingModuleSummary() + assert summary["totalExitedValidators"] >= 0 + assert summary["totalDepositedValidators"] >= 0 + assert summary["depositableValidatorsCount"] > 0 + + deactivated_node_operators = [] # reserved for future use + exited_node_operators = [] # reserved for future use + + for id in range(node_operators_count): + node_operator = contract.getNodeOperator(id, True) + + assert node_operator["active"] == (id not in deactivated_node_operators) + assert node_operator["name"] is not None + assert node_operator["name"] != "" + assert node_operator["rewardAddress"] != ZERO_ADDRESS + + # Invariant check + # https://github.com/lidofinance/lido-dao/blob/cadffa46a2b8ed6cfa1127fca2468bae1a82d6bf/contracts/0.4.24/nos/NodeOperatorsRegistry.sol#L168 + assert node_operator["totalExitedValidators"] >= 0 + assert node_operator["totalExitedValidators"] <= node_operator["totalDepositedValidators"] + assert node_operator["totalDepositedValidators"] <= node_operator["totalVettedValidators"] + assert node_operator["totalVettedValidators"] <= node_operator["totalAddedValidators"] + + node_operator_summary = contract.getNodeOperatorSummary(id) + if id in exited_node_operators: + assert ( + node_operator_summary["isTargetLimitActive"] is True + ), f"isTargetLimitActive is inactive for node {id}" + else: + assert node_operator_summary["isTargetLimitActive"] is False, f"isTargetLimitActive is active for node {id}" + assert node_operator_summary["targetValidatorsCount"] == 0 + # Can be more than 0 in regular protocol operations + # assert node_operator_summary["stuckValidatorsCount"] == 0 + assert node_operator_summary["refundedValidatorsCount"] == 0 + # Can be more than 0 in regular protocol operations + # assert node_operator_summary["stuckPenaltyEndTimestamp"] == 0 + + # Invariant check + # https://github.com/lidofinance/lido-dao/blob/cadffa46a2b8ed6cfa1127fca2468bae1a82d6bf/contracts/0.4.24/nos/NodeOperatorsRegistry.sol#L168 + assert node_operator_summary["totalExitedValidators"] >= 0 + assert node_operator_summary["totalExitedValidators"] <= node_operator_summary["totalDepositedValidators"] + + assert node_operator_summary["depositableValidatorsCount"] is not None + + assert node_operator["totalExitedValidators"] == node_operator_summary["totalExitedValidators"] + assert node_operator["totalDepositedValidators"] == node_operator_summary["totalDepositedValidators"] + + no_depositable_validators_count = ( + node_operator["totalVettedValidators"] - node_operator["totalDepositedValidators"] + ) + + assert node_operator_summary["depositableValidatorsCount"] == no_depositable_validators_count + + +def test_simple_dvt_easytrack(contract): + + easy_track = contracts.easy_track + + assert contract.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, STAKING_ROUTER_ROLE, []) + assert contract.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, MANAGE_NODE_OPERATOR_ROLE, []) + assert contract.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, SET_NODE_OPERATOR_LIMIT_ROLE, []) + assert contracts.acl.getPermissionManager(contract.address, MANAGE_SIGNING_KEYS) == EASYTRACK_EVMSCRIPT_EXECUTOR + assert contract.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, MANAGE_SIGNING_KEYS, []) + + add_node_operators_evm_script_factory = EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY + activate_node_operators_evm_script_factory = EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY + deactivate_node_operators_evm_script_factory = EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY + set_vetted_validators_limits_evm_script_factory = EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY + set_node_operator_names_evm_script_factory = EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_NAMES_FACTORY + set_node_operator_reward_addresses_evm_script_factory = ( + EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY + ) + update_target_validator_limits_evm_script_factory = EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY + change_node_operator_managers_evm_script_factory = EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY + + evm_script_factories = easy_track.getEVMScriptFactories() + + assert add_node_operators_evm_script_factory in evm_script_factories + assert activate_node_operators_evm_script_factory in evm_script_factories + assert deactivate_node_operators_evm_script_factory in evm_script_factories + assert set_vetted_validators_limits_evm_script_factory in evm_script_factories + assert update_target_validator_limits_evm_script_factory in evm_script_factories + assert set_node_operator_names_evm_script_factory in evm_script_factories + assert set_node_operator_reward_addresses_evm_script_factory in evm_script_factories + assert change_node_operator_managers_evm_script_factory in evm_script_factories + + assert interface.AddNodeOperators(add_node_operators_evm_script_factory).nodeOperatorsRegistry() == contract + assert ( + interface.AddNodeOperators(add_node_operators_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + assert ( + interface.ActivateNodeOperators(activate_node_operators_evm_script_factory).nodeOperatorsRegistry() == contract + ) + assert ( + interface.ActivateNodeOperators(activate_node_operators_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + assert ( + interface.DeactivateNodeOperators(deactivate_node_operators_evm_script_factory).nodeOperatorsRegistry() + == contract + ) + assert ( + interface.DeactivateNodeOperators(deactivate_node_operators_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + assert ( + interface.SetVettedValidatorsLimits(set_vetted_validators_limits_evm_script_factory).nodeOperatorsRegistry() + == contract + ) + assert ( + interface.SetVettedValidatorsLimits(set_vetted_validators_limits_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + assert ( + interface.SetNodeOperatorNames(set_node_operator_names_evm_script_factory).nodeOperatorsRegistry() == contract + ) + assert ( + interface.SetNodeOperatorNames(set_node_operator_names_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + assert ( + interface.SetNodeOperatorRewardAddresses( + set_node_operator_reward_addresses_evm_script_factory + ).nodeOperatorsRegistry() + == contract + ) + assert ( + interface.SetNodeOperatorRewardAddresses(set_node_operator_reward_addresses_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + assert ( + interface.UpdateTargetValidatorLimits(update_target_validator_limits_evm_script_factory).nodeOperatorsRegistry() + == contract + ) + assert ( + interface.UpdateTargetValidatorLimits(update_target_validator_limits_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + assert ( + interface.ChangeNodeOperatorManagers(change_node_operator_managers_evm_script_factory).nodeOperatorsRegistry() + == contract + ) + assert ( + interface.ChangeNodeOperatorManagers(change_node_operator_managers_evm_script_factory).trustedCaller() + == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER + ) + + +def _str_to_bytes32(s: str) -> str: + return "0x{:0<64}".format(s.encode("utf-8").hex()) diff --git a/tests/acceptance/test_staking_router.py b/tests/acceptance/test_staking_router.py index 72825727..8a45a6f6 100644 --- a/tests/acceptance/test_staking_router.py +++ b/tests/acceptance/test_staking_router.py @@ -111,10 +111,9 @@ def test_staking_modules(contract): assert simple_dvt_module["targetShare"] == SIMPLE_DVT_MODULE_TARGET_SHARE_BP assert simple_dvt_module["status"] == 0 assert simple_dvt_module["name"] == SIMPLE_DVT_MODULE_NAME - # TODO: fix values after adding new keys - # assert simple_dvt_module["lastDepositAt"] >= 0 - # assert simple_dvt_module["lastDepositBlock"] >= 0 - # assert simple_dvt_module["exitedValidatorsCount"] >= 0 + assert simple_dvt_module["lastDepositAt"] > 0 + assert simple_dvt_module["lastDepositBlock"] > 0 + assert simple_dvt_module["exitedValidatorsCount"] >= 0 fee_aggregate_distribution = contract.getStakingFeeAggregateDistribution() assert fee_aggregate_distribution["modulesFee"] == SR_MODULES_FEE_E20 From 32fc66dd498232e1b5f92ef9965c4b3bda85eaee Mon Sep 17 00:00:00 2001 From: KRogLA Date: Mon, 12 Feb 2024 10:50:31 +0100 Subject: [PATCH 22/68] fix: rename var --- .../regression/test_staking_router_stake_distribution.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/regression/test_staking_router_stake_distribution.py b/tests/regression/test_staking_router_stake_distribution.py index 8686d253..871c2d40 100644 --- a/tests/regression/test_staking_router_stake_distribution.py +++ b/tests/regression/test_staking_router_stake_distribution.py @@ -25,8 +25,8 @@ def test_stake_distribution(): staking_router = contracts.staking_router module_digests = staking_router.getAllStakingModuleDigests() - stake_to_allocate = 100 # keys to allocate to the modules - allocation_from_contract = staking_router.getDepositsAllocation(stake_to_allocate) + keys_to_allocate = 100 # keys to allocate to the modules + allocation_from_contract = staking_router.getDepositsAllocation(keys_to_allocate) # collect the modules information @@ -52,7 +52,7 @@ def test_stake_distribution(): # simulate target share distribution # https://github.com/lidofinance/lido-dao/blob/331ecec7fe3c8d57841fd73ccca7fb1cc9bc174e/contracts/0.8.9/StakingRouter.sol#L1266-L1268 - target_total_active_keys = total_active_keys + stake_to_allocate + target_total_active_keys = total_active_keys + keys_to_allocate total_basis_points = staking_router.TOTAL_BASIS_POINTS() for module in modules.values(): @@ -62,7 +62,7 @@ def test_stake_distribution(): # simulate min first strategy # https://github.com/lidofinance/lido-dao/blob/331ecec7fe3c8d57841fd73ccca7fb1cc9bc174e/contracts/0.8.9/StakingRouter.sol#L1274 - for _ in range(stake_to_allocate): + for _ in range(keys_to_allocate): # find the module with the lowest active_keys min_active_keys = modules[1].active_keys min_active_keys_module = modules[1] From 40e4050265709282670217325b2a7cd8dbe12ba3 Mon Sep 17 00:00:00 2001 From: F4ever Date: Mon, 12 Feb 2024 15:40:56 +0100 Subject: [PATCH 23/68] simple dvt tests happy path --- tests/regression/conftest.py | 1 - .../test_staking_module_happy_path.py | 115 +++++++++--------- utils/test/simple_dvt_helpers.py | 2 +- 3 files changed, 61 insertions(+), 57 deletions(-) diff --git a/tests/regression/conftest.py b/tests/regression/conftest.py index dbaac6d4..d849fc31 100644 --- a/tests/regression/conftest.py +++ b/tests/regression/conftest.py @@ -7,7 +7,6 @@ from utils.test.helpers import ETH from utils.test.oracle_report_helpers import oracle_report from utils.test.simple_dvt_helpers import fill_simple_dvt_ops_vetted_keys -from brownie import chain ENV_REPORT_AFTER_VOTE = "REPORT_AFTER_VOTE" ENV_FILL_SIMPLE_DVT = "FILL_SIMPLE_DVT" diff --git a/tests/regression/test_staking_module_happy_path.py b/tests/regression/test_staking_module_happy_path.py index 1ec53c91..b7901274 100644 --- a/tests/regression/test_staking_module_happy_path.py +++ b/tests/regression/test_staking_module_happy_path.py @@ -10,11 +10,16 @@ from utils.test.oracle_report_helpers import ( oracle_report, ) -from utils.config import contracts, STAKING_ROUTER +from utils.config import contracts, STAKING_ROUTER, EASYTRACK_EVMSCRIPT_EXECUTOR from utils.test.node_operators_helpers import node_operator_gindex -@pytest.fixture(scope="module") +@pytest.fixture(scope="function") +def impersonate_es_executor(accounts): + return accounts.at(EASYTRACK_EVMSCRIPT_EXECUTOR, force=True) + + +@pytest.fixture(scope="function") def impersonated_voting(accounts): return accounts.at(contracts.voting.address, force=True) @@ -47,24 +52,24 @@ def increase_limit(nor, first_id, second_id, base_id, keys_count, impersonated_v nor.setNodeOperatorStakingLimit(base_id, current_base_keys + keys_count, {"from": impersonated_voting}) -def deposit_and_check_keys(nor, first_no_id, second_no_id, base_no_id, keys_count, impersonated_voting): +def deposit_and_check_keys(staking_module, first_no_id, second_no_id, base_no_id, keys_count, impersonated_voting): for op_index in (first_no_id, second_no_id, base_no_id): - no = nor.getNodeOperator(op_index, True) + no = staking_module.getNodeOperator(op_index, True) if not no["active"]: continue - nor.setNodeOperatorStakingLimit(op_index, no["totalDepositedValidators"] + 10, {"from": impersonated_voting}) + staking_module.setNodeOperatorStakingLimit(op_index, no["totalDepositedValidators"] + 10, {"from": impersonated_voting}) - deposited_keys_first_before = nor.getNodeOperatorSummary(first_no_id)["totalDepositedValidators"] - deposited_keys_second_before = nor.getNodeOperatorSummary(second_no_id)["totalDepositedValidators"] - deposited_keys_base_before = nor.getNodeOperatorSummary(base_no_id)["totalDepositedValidators"] + deposited_keys_first_before = staking_module.getNodeOperatorSummary(first_no_id)["totalDepositedValidators"] + deposited_keys_second_before = staking_module.getNodeOperatorSummary(second_no_id)["totalDepositedValidators"] + deposited_keys_base_before = staking_module.getNodeOperatorSummary(base_no_id)["totalDepositedValidators"] validators_before = contracts.lido.getBeaconStat().dict()["depositedValidators"] - module_total_deposited_keys_before = nor.getStakingModuleSummary()["totalDepositedValidators"] + module_total_deposited_keys_before = staking_module.getStakingModuleSummary()["totalDepositedValidators"] - tx = contracts.lido.deposit(keys_count, 1, "0x", {"from": contracts.deposit_security_module.address}) + tx = contracts.lido.deposit(keys_count, staking_module.module_id, "0x", {"from": contracts.deposit_security_module.address}) validators_after = contracts.lido.getBeaconStat().dict()["depositedValidators"] - module_total_deposited_keys_after = nor.getStakingModuleSummary()["totalDepositedValidators"] + module_total_deposited_keys_after = staking_module.getStakingModuleSummary()["totalDepositedValidators"] just_deposited = validators_after - validators_before print("---------", just_deposited) @@ -73,9 +78,9 @@ def deposit_and_check_keys(nor, first_no_id, second_no_id, base_no_id, keys_coun assert tx.events["Unbuffered"]["amount"] == just_deposited * ETH(32) assert module_total_deposited_keys_before + just_deposited == module_total_deposited_keys_after - deposited_keys_first_after = nor.getNodeOperatorSummary(first_no_id)["totalDepositedValidators"] - deposited_keys_second_after = nor.getNodeOperatorSummary(second_no_id)["totalDepositedValidators"] - deposited_keys_base_after = nor.getNodeOperatorSummary(base_no_id)["totalDepositedValidators"] + deposited_keys_first_after = staking_module.getNodeOperatorSummary(first_no_id)["totalDepositedValidators"] + deposited_keys_second_after = staking_module.getNodeOperatorSummary(second_no_id)["totalDepositedValidators"] + deposited_keys_base_after = staking_module.getNodeOperatorSummary(base_no_id)["totalDepositedValidators"] return ( deposited_keys_first_before, @@ -130,7 +135,7 @@ def parse_target_validators_count_changed(logs): return res -def module_happy_path(staking_module, extra_data_service, impersonated_voting, eth_whale): +def module_happy_path(staking_module, extra_data_service, impersonated_voting, impersonated_executor, eth_whale): nor_exited_count, _, _ = contracts.staking_router.getStakingModuleSummary(staking_module.module_id) contracts.staking_router.grantRole( @@ -148,18 +153,16 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e contracts.lido.submit(ZERO_ADDRESS, {"from": eth_whale, "amount": ETH(75000)}) - tested_no_id_first = 20 - tested_no_id_second = 28 - base_no_id = 23 + base_no_id, tested_no_id_first, tested_no_id_second = staking_module.testing_node_operator_ids no_amount = staking_module.getNodeOperatorsCount() for op_index in range(no_amount): no = staking_module.getNodeOperator(op_index, True) if not no["active"]: continue - staking_module.setNodeOperatorStakingLimit(op_index, no["totalDepositedValidators"], {"from": impersonated_voting}) + staking_module.setNodeOperatorStakingLimit(op_index, no["totalDepositedValidators"], {"from": impersonated_executor}) - increase_limit(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 3, impersonated_voting) + increase_limit(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 3, impersonated_executor) penalty_delay = staking_module.getStuckPenaltyDelay() @@ -183,32 +186,32 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e node_operator_base_balance_shares_after = shares_balance(address_base_no) # expected shares - # node_operator_first_rewards_after_first_report = calc_no_rewards( - # staking_module, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] - # ) - # node_operator_second_rewards_after_first_report = calc_no_rewards( - # staking_module, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] - # ) - # node_operator_base_rewards_after_first_report = calc_no_rewards( - # staking_module, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] - # ) - # - # # check shares by empty report - # assert almostEqWithDiff( - # node_operator_first_balance_shares_after - node_operator_first_balance_shares_before, - # node_operator_first_rewards_after_first_report, - # 1, - # ) - # assert almostEqWithDiff( - # node_operator_second_balance_shares_after - node_operator_second_balance_shares_before, - # node_operator_second_rewards_after_first_report, - # 1, - # ) - # assert almostEqWithDiff( - # node_operator_base_balance_shares_after - node_operator_base_balance_shares_before, - # node_operator_base_rewards_after_first_report, - # 1, - # ) + node_operator_first_rewards_after_first_report = calc_no_rewards( + staking_module, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + ) + node_operator_second_rewards_after_first_report = calc_no_rewards( + staking_module, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + ) + node_operator_base_rewards_after_first_report = calc_no_rewards( + staking_module, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + ) + + # check shares by empty report + assert almostEqWithDiff( + node_operator_first_balance_shares_after - node_operator_first_balance_shares_before, + node_operator_first_rewards_after_first_report, + 1, + ) + assert almostEqWithDiff( + node_operator_second_balance_shares_after - node_operator_second_balance_shares_before, + node_operator_second_rewards_after_first_report, + 1, + ) + assert almostEqWithDiff( + node_operator_base_balance_shares_after - node_operator_base_balance_shares_before, + node_operator_base_rewards_after_first_report, + 1, + ) # Case 1 # --- operator "First" had 5 keys (exited), and 2 keys got stuck (stuck) @@ -236,6 +239,8 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e node_operator_second_balance_shares_before = shares_balance(address_second) node_operator_base_balance_shares_before = shares_balance(address_base_no) + deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 30, impersonated_executor) + # Second report - first NO and second NO has stuck/exited (report_tx, extra_report_tx) = oracle_report( exclude_vaults_balances=True, @@ -244,7 +249,7 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e extraDataItemsCount=2, extraDataList=extra_data.extra_data, numExitedValidatorsByStakingModule=[nor_exited_count + 10], - stakingModuleIdsWithNewlyExitedValidators=[1], + stakingModuleIdsWithNewlyExitedValidators=[staking_module.module_id], ) # shares after report @@ -289,8 +294,6 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e amount_penalty_second_no = node_operator_second_rewards_after_second_report // 2 penalty_shares = amount_penalty_first_no + amount_penalty_second_no - # TODO: Fix below check when nor contains other penalized node operators - # assert almostEqWithDiff(extra_report_tx.events["StETHBurnRequested"]["amountOfShares"], penalty_shares, 2) assert extra_report_tx.events["StETHBurnRequested"]["amountOfShares"] >= penalty_shares # NO stats @@ -340,7 +343,7 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e deposited_keys_first_after, deposited_keys_second_after, deposited_keys_base_after, - ) = deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) + ) = deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_executor) # check don't change deposited keys for penalized NO assert deposited_keys_first_before == deposited_keys_first_after @@ -380,7 +383,7 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e extraDataItemsCount=2, extraDataList=extra_data.extra_data, numExitedValidatorsByStakingModule=[nor_exited_count + 12], - stakingModuleIdsWithNewlyExitedValidators=[1], + stakingModuleIdsWithNewlyExitedValidators=[staking_module.module_id], ) node_operator_first = staking_module.getNodeOperatorSummary(tested_no_id_first) @@ -502,7 +505,7 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e extraDataItemsCount=2, extraDataList=extra_data.extra_data, numExitedValidatorsByStakingModule=[nor_exited_count + 12], - stakingModuleIdsWithNewlyExitedValidators=[1], + stakingModuleIdsWithNewlyExitedValidators=[staking_module.module_id], ) node_operator_first = staking_module.getNodeOperatorSummary(tested_no_id_first) @@ -837,10 +840,12 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e def test_node_operator_registry(impersonated_voting, eth_whale): nor = contracts.node_operators_registry nor.module_id = 1 - module_happy_path(nor, ExtraDataService(), impersonated_voting, eth_whale) + nor.testing_node_operator_ids = [23, 20, 28] + module_happy_path(nor, ExtraDataService(), impersonated_voting, impersonated_voting, eth_whale) -def test_sdvt(impersonated_voting, eth_whale): +def test_sdvt(impersonated_voting, impersonate_es_executor, eth_whale): sdvt = contracts.simple_dvt sdvt.module_id = 2 - module_happy_path(sdvt, ExtraDataService(), impersonated_voting, eth_whale) + sdvt.testing_node_operator_ids = [0, 1, 2] + module_happy_path(sdvt, ExtraDataService(), impersonated_voting, impersonate_es_executor, eth_whale) diff --git a/utils/test/simple_dvt_helpers.py b/utils/test/simple_dvt_helpers.py index 222de7b9..0f40caf3 100644 --- a/utils/test/simple_dvt_helpers.py +++ b/utils/test/simple_dvt_helpers.py @@ -9,7 +9,7 @@ from utils.test.easy_track_helpers import _encode_calldata -MIN_OP_KEYS_CNT = 10 +MIN_OP_KEYS_CNT = 300 MIN_OPS_CNT = 3 OPERATOR_NAMES = [ From 1f910e1700c28d2d0c1d92c16ab6e56b34e536cc Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 13 Feb 2024 09:52:30 +0100 Subject: [PATCH 24/68] fix: upd sanity checker param --- scripts/vote_simple_dvt.py | 26 ++++++++++++++++++++++++++ tests/test_simple_dvt.py | 30 +++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/scripts/vote_simple_dvt.py b/scripts/vote_simple_dvt.py index 6692a59c..42076065 100644 --- a/scripts/vote_simple_dvt.py +++ b/scripts/vote_simple_dvt.py @@ -247,6 +247,32 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ] ), ), + # + # V. Update Sanity Checker parameters + # + ( + "19) Grant REQUEST_BURN_SHARES_ROLE on Burner for SimpleDVT module", + agent_forward( + [ + encode_oz_grant_role( + contract=contracts.oracle_report_sanity_checker, + role_name="MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE", + grant_to=contracts.agent, + ) + ] + ), + ), + ( + "20) Increase maxAccountingExtraDataListItemsCount sanity checker parameter from 2 to 4", + agent_forward( + [ + ( + contracts.oracle_report_sanity_checker.address, + contracts.oracle_report_sanity_checker.setMaxAccountingExtraDataListItemsCount.encode_input(4), + ), + ] + ), + ), ) vote_items = bake_vote_items(list(vote_desc_items), list(call_script_items)) diff --git a/tests/test_simple_dvt.py b/tests/test_simple_dvt.py index d293e44f..f2b63d2d 100644 --- a/tests/test_simple_dvt.py +++ b/tests/test_simple_dvt.py @@ -62,6 +62,7 @@ MANAGE_NODE_OPERATOR_ROLE = "0x78523850fdd761612f46e844cf5a16bda6b3151d6ae961fd7e8e7b92bfbca7f8" SET_NODE_OPERATOR_LIMIT_ROLE = "0x07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754" MANAGE_SIGNING_KEYS = "0x75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee" +MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE = "0x0cf253eb71298c92e2814969a122f66b781f9b217f8ecde5401e702beb9345f6" simple_dvt_repo_ens = "simple-dvt.lidopm.eth" simple_dvt_content_uri = ( @@ -110,6 +111,9 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco assert update_target_validator_limits_evm_script_factory not in evm_script_factories_before assert change_node_operator_managers_evm_script_factory not in evm_script_factories_before + sanity_checker_limits = contracts.oracle_report_sanity_checker.getOracleReportLimits() + assert sanity_checker_limits["maxAccountingExtraDataListItemsCount"] == 2 + # START VOTE if len(vote_ids_from_env) > 0: (vote_id,) = vote_ids_from_env @@ -256,8 +260,11 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco == EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER ) + sanity_checker_limits = contracts.oracle_report_sanity_checker.getOracleReportLimits() + assert sanity_checker_limits["maxAccountingExtraDataListItemsCount"] == 4 + # validate vote events - assert count_vote_items_by_events(vote_tx, contracts.voting) == 18, "Incorrect voting items count" + assert count_vote_items_by_events(vote_tx, contracts.voting) == 20, "Incorrect voting items count" metadata = find_metadata_by_vote_id(vote_id) print("metadata", metadata) @@ -396,6 +403,9 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ) validate_staking_module_added_event(evs[17], module_item) + validate_grant_role_event(evs[18], MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE, agent, agent) + validate_max_extra_data_list_items_count_event(evs[19], 4) + def has_permission(permission: Permission, how: List[int]) -> bool: return contracts.acl.hasPermission["address,address,bytes32,uint[]"]( @@ -403,8 +413,22 @@ def has_permission(permission: Permission, how: List[int]) -> bool: ) +def validate_max_extra_data_list_items_count_event(event: EventDict, value: int): + _events_chain = [ + "LogScriptCall", + "LogScriptCall", + "MaxAccountingExtraDataListItemsCountSet", + "ScriptResult", + ] + + validate_events_chain([e.name for e in event], _events_chain) + + assert event.count("MaxAccountingExtraDataListItemsCountSet") == 1 + assert event["MaxAccountingExtraDataListItemsCountSet"]["maxAccountingExtraDataListItemsCount"] == value + + def validate_simple_dvt_intialize_event(event: EventDict): - _repo_upgrade_events_chain = [ + _events_chain = [ "LogScriptCall", "ContractVersionSet", "StuckPenaltyDelayChanged", @@ -413,7 +437,7 @@ def validate_simple_dvt_intialize_event(event: EventDict): "StakingModuleTypeSet", ] - validate_events_chain([e.name for e in event], _repo_upgrade_events_chain) + validate_events_chain([e.name for e in event], _events_chain) assert event.count("ContractVersionSet") == 1 assert event.count("StuckPenaltyDelayChanged") == 1 From 0be0783e1e30bd537a128722c21d477b1dec8a9a Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Tue, 13 Feb 2024 14:03:18 +0400 Subject: [PATCH 25/68] draft: test --- ...accounting_oracle_extra_data_full_items.py | 295 ++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 tests/regression/test_accounting_oracle_extra_data_full_items.py diff --git a/tests/regression/test_accounting_oracle_extra_data_full_items.py b/tests/regression/test_accounting_oracle_extra_data_full_items.py new file mode 100644 index 00000000..b796158e --- /dev/null +++ b/tests/regression/test_accounting_oracle_extra_data_full_items.py @@ -0,0 +1,295 @@ +import random +import textwrap + +import pytest +from brownie import convert +from brownie.network.account import Account +from brownie.network.web3 import Web3 + +from utils.test.deposits_helpers import fill_deposit_buffer +from utils.test.extra_data import ExtraDataService +from utils.test.oracle_report_helpers import oracle_report + +from utils.config import MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT, MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT +from utils.config import contracts +from utils.test.simple_dvt_helpers import simple_dvt_add_node_operators, simple_dvt_add_keys, simple_dvt_vet_keys + +PUBKEY_LENGTH = 48 +SIGNATURE_LENGTH = 96 + + +@pytest.fixture() +def extra_data_service(): + return ExtraDataService() + + +@pytest.fixture +def voting_eoa(accounts): + return accounts.at(contracts.voting.address, force=True) + + +@pytest.fixture +def agent_eoa(accounts): + return accounts.at(contracts.agent.address, force=True) + + +@pytest.fixture +def evm_script_executor_eoa(accounts): + return accounts.at(contracts.easy_track.evmScriptExecutor(), force=True) + + +@pytest.fixture +def nor(interface): + return interface.NodeOperatorsRegistry(contracts.node_operators_registry.address) + + +@pytest.fixture +def sdvt(interface): + return interface.SimpleDVT(contracts.simple_dvt.address) + + +@pytest.mark.parametrize( + ("nor_stuck_items", "nor_exited_items", "sdvt_stuck_items", "sdvt_exited_items"), + [ + (1, 1, 1, 1), + (1, 1, 1, 0), + (1, 1, 0, 1), + (1, 1, 0, 0), + (1, 0, 1, 1), + (1, 0, 1, 0), + (1, 0, 0, 1), + (1, 0, 0, 0), + (0, 1, 1, 1), + (0, 1, 1, 0), + (0, 1, 0, 1), + (0, 1, 0, 0), + (0, 0, 1, 1), + (0, 0, 1, 0), + (0, 0, 0, 1), + ] +) +def test_extra_data_full_items( + stranger, voting_eoa, agent_eoa, evm_script_executor_eoa, nor, sdvt, extra_data_service, + nor_stuck_items, nor_exited_items, sdvt_stuck_items, sdvt_exited_items +): + max_node_operators_per_item = MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT + new_keys_per_operator = 2 + + # Fill NOR with new operators and keys + (nor_count_before, added_nor_operators_count) = add_nor_operators_with_keys( + nor, + new_keys_per_operator, + nor_stuck_items, + nor_exited_items, + max_node_operators_per_item, + voting_eoa, + agent_eoa, + evm_script_executor_eoa + ) + + # Fill SimpleDVT with new operators and keys + sdvt_operators_count = (sdvt_stuck_items + sdvt_exited_items) * max_node_operators_per_item + add_sdvt_operators_with_keys(stranger, sdvt_operators_count, new_keys_per_operator) + + # Deposit for new added keys from buffer + deposit_buffer_for_keys( + contracts.staking_router, + sdvt_operators_count * new_keys_per_operator, + (added_nor_operators_count * new_keys_per_operator) + (nor_count_before * new_keys_per_operator) + ) + + # Prepare report extra data + nor_stuck = {(1, i): 1 for i in range(0, nor_stuck_items * max_node_operators_per_item)} + nor_exited = {(1, i): nor.getNodeOperatorSummary(i)['totalExitedValidators'] + 1 for i in range(0, nor_exited_items * max_node_operators_per_item)} + sdvt_stuck = {(2, i): 1 for i in range(0, sdvt_stuck_items * max_node_operators_per_item)} + sdvt_exited = {(2, i): 1 for i in range(0, sdvt_exited_items * max_node_operators_per_item)} + extra_data = extra_data_service.collect( + {**nor_stuck, **sdvt_stuck}, + {**nor_exited, **sdvt_exited}, + 4, + MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT, + ) + modules_with_exited = [] + num_exited_validators_by_staking_module = [] + if nor_exited_items > 0: + modules_with_exited.append(1) + nor_exited_before = nor.getStakingModuleSummary()["totalExitedValidators"] + num_exited_validators_by_staking_module.append(nor_exited_before + (nor_exited_items * max_node_operators_per_item)) + if sdvt_exited_items > 0: + modules_with_exited.append(2) + num_exited_validators_by_staking_module.append(sdvt_exited_items * max_node_operators_per_item) + + oracle_report( + extraDataFormat=1, + extraDataHash=extra_data.data_hash, + extraDataItemsCount=4, + extraDataList=extra_data.extra_data, + stakingModuleIdsWithNewlyExitedValidators=modules_with_exited, + numExitedValidatorsByStakingModule=num_exited_validators_by_staking_module, + ) + + # TODO: Rewards are distributed correctly (1/2 of rewards for operators with staked keys are burned) + + # Exited and stuck keys are reported correctly + for i in range(0, len(nor_exited)): + assert nor.getNodeOperatorSummary(i)["totalExitedValidators"] == nor_exited[(1, i)] + for i in range(0, len(nor_stuck)): + assert nor.getNodeOperatorSummary(i)["stuckValidatorsCount"] == nor_stuck[(1, i)] + assert nor.isOperatorPenalized(i) == True + for i in range(0, len(sdvt_exited)): + assert sdvt.getNodeOperatorSummary(i)["totalExitedValidators"] == sdvt_exited[(2, i)] + for i in range(0, len(sdvt_stuck)): + assert sdvt.getNodeOperatorSummary(i)["stuckValidatorsCount"] == sdvt_stuck[(2, i)] + assert sdvt.isOperatorPenalized(i) == True + +############################################ +# HELPER FUNCTIONS +############################################ + + +def random_pubkeys_batch(pubkeys_count: int): + return random_hexstr(pubkeys_count * PUBKEY_LENGTH) + + +def random_signatures_batch(signautes_count: int): + return random_hexstr(signautes_count * SIGNATURE_LENGTH) + + +def parse_pubkeys_batch(pubkeys_batch: str): + return hex_chunks(pubkeys_batch, PUBKEY_LENGTH) + + +def parse_signatures_batch(signatures_batch: str): + return hex_chunks(signatures_batch, SIGNATURE_LENGTH) + + +def hex_chunks(hexstr: str, chunk_length: int): + stripped_hexstr = strip_0x(hexstr) + assert len(stripped_hexstr) % chunk_length == 0, "invalid hexstr length" + return [prefix_0x(chunk) for chunk in textwrap.wrap(stripped_hexstr, 2 * chunk_length)] + + +def random_hexstr(length: int): + return prefix_0x(random.randbytes(length).hex()) + + +def prefix_0x(hexstr: str): + return hexstr if hexstr.startswith("0x") else "0x" + hexstr + + +def strip_0x(hexstr: str): + return hexstr[2:] if hexstr.startswith("0x") else hexstr + + +def add_sdvt_operators_with_keys(enactor: Account, count: int, keys_per_operator: int): + names = [f"Name {i}" for i in range(0, count)] + reward_addresses = [f"0xab{str(i).zfill(38)}" for i in range(0, count)] + managers = [f"0xcd{str(i).zfill(38)}" for i in range(0, count)] + + # 30 at a time + for i in range(0, count, 30): + (operators_count_before, _) = simple_dvt_add_node_operators( + contracts.simple_dvt, + enactor, + [ + (names[j], reward_addresses[j], managers[j]) + for j in range(i, i + 30) if j < count + ] + ) + for j in range(i, i + 30): + if j >= count: + break + simple_dvt_add_keys(contracts.simple_dvt, j, keys_per_operator) + simple_dvt_vet_keys(j, enactor) + + +def add_new_nor_operators_with_keys(nor, voting_eoa: Account, evm_script_executor_eoa: Account, count: int, + keys_per_operator: int): + names = [f"Name {i}" for i in range(0, count)] + reward_addresses = [f"0xbb{str(i).zfill(38)}" for i in range(0, count)] + # managers = [f"0xdd{str(i).zfill(38)}" for i in range(0, count)] + + # 30 at a time + for i in range(0, count, 30): + # should be more than count + for j in range(i, i + 30): + if j >= count: + break + nor.addNodeOperator( + names[j], + reward_addresses[j], + {"from": voting_eoa} + ) + no_id = nor.getNodeOperatorsCount() - 1 + pubkeys_batch = random_pubkeys_batch(keys_per_operator) + signatures_batch = random_signatures_batch(keys_per_operator) + nor.addSigningKeysOperatorBH( + no_id, + keys_per_operator, + pubkeys_batch, + signatures_batch, + {"from": reward_addresses[j]}, + ) + nor.setNodeOperatorStakingLimit(no_id, keys_per_operator, {"from": evm_script_executor_eoa}) + + +def add_nor_operators_with_keys( + nor, new_keys_per_operator, nor_stuck_items, nor_exited_items, max_node_operators_per_item, voting_eoa, agent_eoa, evm_script_executor_eoa +) -> tuple[int, int]: + # Curated: Add new operators and keys + contracts.staking_router.grantRole( + contracts.staking_router.MANAGE_WITHDRAWAL_CREDENTIALS_ROLE(), voting_eoa, {"from": agent_eoa} + ) + contracts.acl.grantPermission( + contracts.voting, + contracts.node_operators_registry, + convert.to_uint(Web3.keccak(text="MANAGE_NODE_OPERATOR_ROLE")), + {"from": contracts.voting}, + ) + nor_count_before = nor.getNodeOperatorsCount() + added_nor_operators_count = ((nor_stuck_items + nor_exited_items) * max_node_operators_per_item) - nor_count_before + add_new_nor_operators_with_keys(nor, voting_eoa, evm_script_executor_eoa, added_nor_operators_count, + new_keys_per_operator) + # Activate old deactivated node operators + nor.activateNodeOperator(1, {"from": voting_eoa}) + nor.activateNodeOperator(12, {"from": voting_eoa}) + # Add keys to old node operators + for i in range(0, nor_count_before): + pubkeys_batch = random_pubkeys_batch(new_keys_per_operator) + signatures_batch = random_signatures_batch(new_keys_per_operator) + operator = nor.getNodeOperator(i, False) + new_deposit_limit = operator["totalDepositedValidators"] + new_keys_per_operator + nor.addSigningKeysOperatorBH( + i, + new_keys_per_operator, + pubkeys_batch, + signatures_batch, + {"from": operator["rewardAddress"]}, + ) + # Change staking limits for old node operators (change to new total added keys count) + nor.setNodeOperatorStakingLimit(i, new_deposit_limit, {"from": evm_script_executor_eoa}) + nor.updateTargetValidatorsLimits(i, True, new_deposit_limit, {"from": contracts.staking_router}) + return nor_count_before, added_nor_operators_count + + +def deposit_buffer_for_keys(staking_router, sdvt_keys_to_deposit, nor_keys_to_deposit): + total_depositable_keys = 0 + module_digests = staking_router.getAllStakingModuleDigests() + for digest in module_digests: + (_, _, state, summary) = digest + (id, _, _, _, target_share, status, _, _, _, _) = state + (exited_keys, deposited_keys, depositable_keys) = summary + total_depositable_keys += depositable_keys + + contracts.lido.removeStakingLimit({"from": contracts.voting}) + fill_deposit_buffer(total_depositable_keys) + keys_per_deposit = 50 + # Deposits for SDVT + times = sdvt_keys_to_deposit // keys_per_deposit + for _ in range(0, times): + contracts.lido.deposit(keys_per_deposit, 2, "0x", {"from": contracts.deposit_security_module}) + + # Deposits for NOR + times = nor_keys_to_deposit // keys_per_deposit; + for _ in range(0, times): + contracts.lido.deposit(keys_per_deposit, 1, "0x", {"from": contracts.deposit_security_module}) From 7f157ce71a18a7691c546797398f9513deebd435 Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Tue, 13 Feb 2024 14:05:12 +0400 Subject: [PATCH 26/68] draft: use MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT --- configs/config_mainnet.py | 2 +- .../test_accounting_oracle_extra_data_full_items.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index 8eece8af..7bc0803d 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -174,7 +174,7 @@ ANNUAL_BALANCE_INCREASE_BP_LIMIT = 1000 # 10% SIMULATED_SHARE_RATE_DEVIATION_BP_LIMIT = 50 # 0.5% MAX_VALIDATOR_EXIT_REQUESTS_PER_REPORT = 600 -MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT = 2 +MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT = 4 MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT = 100 REQUEST_TIMESTAMP_MARGIN = 7680 # 2 hours rounded to epoch length MAX_POSITIVE_TOKEN_REBASE = 750000 diff --git a/tests/regression/test_accounting_oracle_extra_data_full_items.py b/tests/regression/test_accounting_oracle_extra_data_full_items.py index b796158e..3c895dbd 100644 --- a/tests/regression/test_accounting_oracle_extra_data_full_items.py +++ b/tests/regression/test_accounting_oracle_extra_data_full_items.py @@ -106,7 +106,7 @@ def test_extra_data_full_items( extra_data = extra_data_service.collect( {**nor_stuck, **sdvt_stuck}, {**nor_exited, **sdvt_exited}, - 4, + MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT, MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT, ) modules_with_exited = [] @@ -122,7 +122,7 @@ def test_extra_data_full_items( oracle_report( extraDataFormat=1, extraDataHash=extra_data.data_hash, - extraDataItemsCount=4, + extraDataItemsCount=(nor_exited_items + nor_stuck_items + sdvt_exited_items + sdvt_stuck_items), extraDataList=extra_data.extra_data, stakingModuleIdsWithNewlyExitedValidators=modules_with_exited, numExitedValidatorsByStakingModule=num_exited_validators_by_staking_module, From 41cc9294ec5138d5a805c5f7bdff8066f26601b5 Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Tue, 13 Feb 2024 14:12:02 +0400 Subject: [PATCH 27/68] Revert "draft: use MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT" This reverts commit 7f157ce71a18a7691c546797398f9513deebd435. --- configs/config_mainnet.py | 2 +- .../test_accounting_oracle_extra_data_full_items.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index 7bc0803d..8eece8af 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -174,7 +174,7 @@ ANNUAL_BALANCE_INCREASE_BP_LIMIT = 1000 # 10% SIMULATED_SHARE_RATE_DEVIATION_BP_LIMIT = 50 # 0.5% MAX_VALIDATOR_EXIT_REQUESTS_PER_REPORT = 600 -MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT = 4 +MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT = 2 MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT = 100 REQUEST_TIMESTAMP_MARGIN = 7680 # 2 hours rounded to epoch length MAX_POSITIVE_TOKEN_REBASE = 750000 diff --git a/tests/regression/test_accounting_oracle_extra_data_full_items.py b/tests/regression/test_accounting_oracle_extra_data_full_items.py index 3c895dbd..b796158e 100644 --- a/tests/regression/test_accounting_oracle_extra_data_full_items.py +++ b/tests/regression/test_accounting_oracle_extra_data_full_items.py @@ -106,7 +106,7 @@ def test_extra_data_full_items( extra_data = extra_data_service.collect( {**nor_stuck, **sdvt_stuck}, {**nor_exited, **sdvt_exited}, - MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT, + 4, MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT, ) modules_with_exited = [] @@ -122,7 +122,7 @@ def test_extra_data_full_items( oracle_report( extraDataFormat=1, extraDataHash=extra_data.data_hash, - extraDataItemsCount=(nor_exited_items + nor_stuck_items + sdvt_exited_items + sdvt_stuck_items), + extraDataItemsCount=4, extraDataList=extra_data.extra_data, stakingModuleIdsWithNewlyExitedValidators=modules_with_exited, numExitedValidatorsByStakingModule=num_exited_validators_by_staking_module, From f1b5f67582c406dee92ce521c835eda45f53debc Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Tue, 13 Feb 2024 14:12:04 +0400 Subject: [PATCH 28/68] Revert "draft: test" This reverts commit 0be0783e1e30bd537a128722c21d477b1dec8a9a. --- ...accounting_oracle_extra_data_full_items.py | 295 ------------------ 1 file changed, 295 deletions(-) delete mode 100644 tests/regression/test_accounting_oracle_extra_data_full_items.py diff --git a/tests/regression/test_accounting_oracle_extra_data_full_items.py b/tests/regression/test_accounting_oracle_extra_data_full_items.py deleted file mode 100644 index b796158e..00000000 --- a/tests/regression/test_accounting_oracle_extra_data_full_items.py +++ /dev/null @@ -1,295 +0,0 @@ -import random -import textwrap - -import pytest -from brownie import convert -from brownie.network.account import Account -from brownie.network.web3 import Web3 - -from utils.test.deposits_helpers import fill_deposit_buffer -from utils.test.extra_data import ExtraDataService -from utils.test.oracle_report_helpers import oracle_report - -from utils.config import MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT, MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT -from utils.config import contracts -from utils.test.simple_dvt_helpers import simple_dvt_add_node_operators, simple_dvt_add_keys, simple_dvt_vet_keys - -PUBKEY_LENGTH = 48 -SIGNATURE_LENGTH = 96 - - -@pytest.fixture() -def extra_data_service(): - return ExtraDataService() - - -@pytest.fixture -def voting_eoa(accounts): - return accounts.at(contracts.voting.address, force=True) - - -@pytest.fixture -def agent_eoa(accounts): - return accounts.at(contracts.agent.address, force=True) - - -@pytest.fixture -def evm_script_executor_eoa(accounts): - return accounts.at(contracts.easy_track.evmScriptExecutor(), force=True) - - -@pytest.fixture -def nor(interface): - return interface.NodeOperatorsRegistry(contracts.node_operators_registry.address) - - -@pytest.fixture -def sdvt(interface): - return interface.SimpleDVT(contracts.simple_dvt.address) - - -@pytest.mark.parametrize( - ("nor_stuck_items", "nor_exited_items", "sdvt_stuck_items", "sdvt_exited_items"), - [ - (1, 1, 1, 1), - (1, 1, 1, 0), - (1, 1, 0, 1), - (1, 1, 0, 0), - (1, 0, 1, 1), - (1, 0, 1, 0), - (1, 0, 0, 1), - (1, 0, 0, 0), - (0, 1, 1, 1), - (0, 1, 1, 0), - (0, 1, 0, 1), - (0, 1, 0, 0), - (0, 0, 1, 1), - (0, 0, 1, 0), - (0, 0, 0, 1), - ] -) -def test_extra_data_full_items( - stranger, voting_eoa, agent_eoa, evm_script_executor_eoa, nor, sdvt, extra_data_service, - nor_stuck_items, nor_exited_items, sdvt_stuck_items, sdvt_exited_items -): - max_node_operators_per_item = MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT - new_keys_per_operator = 2 - - # Fill NOR with new operators and keys - (nor_count_before, added_nor_operators_count) = add_nor_operators_with_keys( - nor, - new_keys_per_operator, - nor_stuck_items, - nor_exited_items, - max_node_operators_per_item, - voting_eoa, - agent_eoa, - evm_script_executor_eoa - ) - - # Fill SimpleDVT with new operators and keys - sdvt_operators_count = (sdvt_stuck_items + sdvt_exited_items) * max_node_operators_per_item - add_sdvt_operators_with_keys(stranger, sdvt_operators_count, new_keys_per_operator) - - # Deposit for new added keys from buffer - deposit_buffer_for_keys( - contracts.staking_router, - sdvt_operators_count * new_keys_per_operator, - (added_nor_operators_count * new_keys_per_operator) + (nor_count_before * new_keys_per_operator) - ) - - # Prepare report extra data - nor_stuck = {(1, i): 1 for i in range(0, nor_stuck_items * max_node_operators_per_item)} - nor_exited = {(1, i): nor.getNodeOperatorSummary(i)['totalExitedValidators'] + 1 for i in range(0, nor_exited_items * max_node_operators_per_item)} - sdvt_stuck = {(2, i): 1 for i in range(0, sdvt_stuck_items * max_node_operators_per_item)} - sdvt_exited = {(2, i): 1 for i in range(0, sdvt_exited_items * max_node_operators_per_item)} - extra_data = extra_data_service.collect( - {**nor_stuck, **sdvt_stuck}, - {**nor_exited, **sdvt_exited}, - 4, - MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT, - ) - modules_with_exited = [] - num_exited_validators_by_staking_module = [] - if nor_exited_items > 0: - modules_with_exited.append(1) - nor_exited_before = nor.getStakingModuleSummary()["totalExitedValidators"] - num_exited_validators_by_staking_module.append(nor_exited_before + (nor_exited_items * max_node_operators_per_item)) - if sdvt_exited_items > 0: - modules_with_exited.append(2) - num_exited_validators_by_staking_module.append(sdvt_exited_items * max_node_operators_per_item) - - oracle_report( - extraDataFormat=1, - extraDataHash=extra_data.data_hash, - extraDataItemsCount=4, - extraDataList=extra_data.extra_data, - stakingModuleIdsWithNewlyExitedValidators=modules_with_exited, - numExitedValidatorsByStakingModule=num_exited_validators_by_staking_module, - ) - - # TODO: Rewards are distributed correctly (1/2 of rewards for operators with staked keys are burned) - - # Exited and stuck keys are reported correctly - for i in range(0, len(nor_exited)): - assert nor.getNodeOperatorSummary(i)["totalExitedValidators"] == nor_exited[(1, i)] - for i in range(0, len(nor_stuck)): - assert nor.getNodeOperatorSummary(i)["stuckValidatorsCount"] == nor_stuck[(1, i)] - assert nor.isOperatorPenalized(i) == True - for i in range(0, len(sdvt_exited)): - assert sdvt.getNodeOperatorSummary(i)["totalExitedValidators"] == sdvt_exited[(2, i)] - for i in range(0, len(sdvt_stuck)): - assert sdvt.getNodeOperatorSummary(i)["stuckValidatorsCount"] == sdvt_stuck[(2, i)] - assert sdvt.isOperatorPenalized(i) == True - -############################################ -# HELPER FUNCTIONS -############################################ - - -def random_pubkeys_batch(pubkeys_count: int): - return random_hexstr(pubkeys_count * PUBKEY_LENGTH) - - -def random_signatures_batch(signautes_count: int): - return random_hexstr(signautes_count * SIGNATURE_LENGTH) - - -def parse_pubkeys_batch(pubkeys_batch: str): - return hex_chunks(pubkeys_batch, PUBKEY_LENGTH) - - -def parse_signatures_batch(signatures_batch: str): - return hex_chunks(signatures_batch, SIGNATURE_LENGTH) - - -def hex_chunks(hexstr: str, chunk_length: int): - stripped_hexstr = strip_0x(hexstr) - assert len(stripped_hexstr) % chunk_length == 0, "invalid hexstr length" - return [prefix_0x(chunk) for chunk in textwrap.wrap(stripped_hexstr, 2 * chunk_length)] - - -def random_hexstr(length: int): - return prefix_0x(random.randbytes(length).hex()) - - -def prefix_0x(hexstr: str): - return hexstr if hexstr.startswith("0x") else "0x" + hexstr - - -def strip_0x(hexstr: str): - return hexstr[2:] if hexstr.startswith("0x") else hexstr - - -def add_sdvt_operators_with_keys(enactor: Account, count: int, keys_per_operator: int): - names = [f"Name {i}" for i in range(0, count)] - reward_addresses = [f"0xab{str(i).zfill(38)}" for i in range(0, count)] - managers = [f"0xcd{str(i).zfill(38)}" for i in range(0, count)] - - # 30 at a time - for i in range(0, count, 30): - (operators_count_before, _) = simple_dvt_add_node_operators( - contracts.simple_dvt, - enactor, - [ - (names[j], reward_addresses[j], managers[j]) - for j in range(i, i + 30) if j < count - ] - ) - for j in range(i, i + 30): - if j >= count: - break - simple_dvt_add_keys(contracts.simple_dvt, j, keys_per_operator) - simple_dvt_vet_keys(j, enactor) - - -def add_new_nor_operators_with_keys(nor, voting_eoa: Account, evm_script_executor_eoa: Account, count: int, - keys_per_operator: int): - names = [f"Name {i}" for i in range(0, count)] - reward_addresses = [f"0xbb{str(i).zfill(38)}" for i in range(0, count)] - # managers = [f"0xdd{str(i).zfill(38)}" for i in range(0, count)] - - # 30 at a time - for i in range(0, count, 30): - # should be more than count - for j in range(i, i + 30): - if j >= count: - break - nor.addNodeOperator( - names[j], - reward_addresses[j], - {"from": voting_eoa} - ) - no_id = nor.getNodeOperatorsCount() - 1 - pubkeys_batch = random_pubkeys_batch(keys_per_operator) - signatures_batch = random_signatures_batch(keys_per_operator) - nor.addSigningKeysOperatorBH( - no_id, - keys_per_operator, - pubkeys_batch, - signatures_batch, - {"from": reward_addresses[j]}, - ) - nor.setNodeOperatorStakingLimit(no_id, keys_per_operator, {"from": evm_script_executor_eoa}) - - -def add_nor_operators_with_keys( - nor, new_keys_per_operator, nor_stuck_items, nor_exited_items, max_node_operators_per_item, voting_eoa, agent_eoa, evm_script_executor_eoa -) -> tuple[int, int]: - # Curated: Add new operators and keys - contracts.staking_router.grantRole( - contracts.staking_router.MANAGE_WITHDRAWAL_CREDENTIALS_ROLE(), voting_eoa, {"from": agent_eoa} - ) - contracts.acl.grantPermission( - contracts.voting, - contracts.node_operators_registry, - convert.to_uint(Web3.keccak(text="MANAGE_NODE_OPERATOR_ROLE")), - {"from": contracts.voting}, - ) - nor_count_before = nor.getNodeOperatorsCount() - added_nor_operators_count = ((nor_stuck_items + nor_exited_items) * max_node_operators_per_item) - nor_count_before - add_new_nor_operators_with_keys(nor, voting_eoa, evm_script_executor_eoa, added_nor_operators_count, - new_keys_per_operator) - # Activate old deactivated node operators - nor.activateNodeOperator(1, {"from": voting_eoa}) - nor.activateNodeOperator(12, {"from": voting_eoa}) - # Add keys to old node operators - for i in range(0, nor_count_before): - pubkeys_batch = random_pubkeys_batch(new_keys_per_operator) - signatures_batch = random_signatures_batch(new_keys_per_operator) - operator = nor.getNodeOperator(i, False) - new_deposit_limit = operator["totalDepositedValidators"] + new_keys_per_operator - nor.addSigningKeysOperatorBH( - i, - new_keys_per_operator, - pubkeys_batch, - signatures_batch, - {"from": operator["rewardAddress"]}, - ) - # Change staking limits for old node operators (change to new total added keys count) - nor.setNodeOperatorStakingLimit(i, new_deposit_limit, {"from": evm_script_executor_eoa}) - nor.updateTargetValidatorsLimits(i, True, new_deposit_limit, {"from": contracts.staking_router}) - return nor_count_before, added_nor_operators_count - - -def deposit_buffer_for_keys(staking_router, sdvt_keys_to_deposit, nor_keys_to_deposit): - total_depositable_keys = 0 - module_digests = staking_router.getAllStakingModuleDigests() - for digest in module_digests: - (_, _, state, summary) = digest - (id, _, _, _, target_share, status, _, _, _, _) = state - (exited_keys, deposited_keys, depositable_keys) = summary - total_depositable_keys += depositable_keys - - contracts.lido.removeStakingLimit({"from": contracts.voting}) - fill_deposit_buffer(total_depositable_keys) - keys_per_deposit = 50 - # Deposits for SDVT - times = sdvt_keys_to_deposit // keys_per_deposit - for _ in range(0, times): - contracts.lido.deposit(keys_per_deposit, 2, "0x", {"from": contracts.deposit_security_module}) - - # Deposits for NOR - times = nor_keys_to_deposit // keys_per_deposit; - for _ in range(0, times): - contracts.lido.deposit(keys_per_deposit, 1, "0x", {"from": contracts.deposit_security_module}) From 2b3182f18a41794c83d2e5261f920c305ccdded2 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 13 Feb 2024 16:10:27 +0100 Subject: [PATCH 29/68] fix: sanity checker params --- scripts/vote_simple_dvt.py | 124 +++++++++++++-------- tests/acceptance/test_simple_dvt_module.py | 17 ++- tests/test_simple_dvt.py | 86 ++++++++------ 3 files changed, 142 insertions(+), 85 deletions(-) diff --git a/scripts/vote_simple_dvt.py b/scripts/vote_simple_dvt.py index 42076065..376348d0 100644 --- a/scripts/vote_simple_dvt.py +++ b/scripts/vote_simple_dvt.py @@ -68,7 +68,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra vote_desc_items, call_script_items = zip( # - # I. Setup SimpleDVT module as new Aragon app + # I. Create new Aragon DAO Application Repo for SimpleDVT # ( "1) Create new Repo for SimpleDVT app", @@ -80,8 +80,11 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra content_uri=create_simple_dvt_app["content_uri"], ), ), + # + # II. Setup and initialize SimpleDVT module as new Aragon app + # ( - "2) Link SimpleDVT app to 0x8538930c385C0438A357d2c25CB3eAD95Ab6D8ed implementation", + "2) Setup SimpleDVT as Aragon DAO app", update_app_implementation(create_simple_dvt_app["id"], create_simple_dvt_app["new_address"]), ), ( @@ -96,7 +99,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), # - # II. Set permissions + # III. Add SimpleDVT module to Staking Router # ( "4) Create and grant permission STAKING_ROUTER_ROLE on SimpleDVT module for StakingRouter", @@ -108,17 +111,39 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "5) Grant STAKING_ROUTER_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor", - # 9. Grant permission for STAKING_ROUTER_ROLE of SimpleDVT app - # assigning it to StakingRouter - encode_permission_grant( - target_app=contracts.simple_dvt, - permission_name="STAKING_ROUTER_ROLE", - grant_to=EASYTRACK_EVMSCRIPT_EXECUTOR, + "5) Grant REQUEST_BURN_SHARES_ROLE on Burner for SimpleDVT module", + agent_forward( + [ + encode_oz_grant_role( + contract=contracts.burner, + role_name="REQUEST_BURN_SHARES_ROLE", + grant_to=contracts.simple_dvt, + ) + ] + ), + ), + ( + "6) Add SimpleDVT module to StakingRouter", + agent_forward( + [ + ( + contracts.staking_router.address, + contracts.staking_router.addStakingModule.encode_input( + SIMPLE_DVT_MODULE_NAME, + contracts.simple_dvt, + SIMPLE_DVT_MODULE_TARGET_SHARE_BP, + SIMPLE_DVT_MODULE_MODULE_FEE_BP, + SIMPLE_DVT_MODULE_TREASURY_FEE_BP, + ), + ), + ] ), ), + # + # IV. Grant permissions to EasyTrackEVMScriptExecutor to make operational changes to SimpleDVT module + # ( - "6) Create and grant permission MANAGE_NODE_OPERATOR_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor", + "7) Create and grant permission MANAGE_NODE_OPERATOR_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor", encode_permission_create( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, target_app=contracts.simple_dvt, @@ -127,7 +152,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "7) Create and grant permission SET_NODE_OPERATOR_LIMIT_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor", + "8) Create and grant permission SET_NODE_OPERATOR_LIMIT_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor", encode_permission_create( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, target_app=contracts.simple_dvt, @@ -136,7 +161,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "8) Create and grant permission MANAGE_SIGNING_KEYS on SimpleDVT module for EasyTrackEVMScriptExecutor", + "9) Create and grant permission MANAGE_SIGNING_KEYS on SimpleDVT module for EasyTrackEVMScriptExecutor", encode_permission_create( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, target_app=contracts.simple_dvt, @@ -144,11 +169,19 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra manager=EASYTRACK_EVMSCRIPT_EXECUTOR, ), ), + ( + "10) Grant STAKING_ROUTER_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor", + encode_permission_grant( + target_app=contracts.simple_dvt, + permission_name="STAKING_ROUTER_ROLE", + grant_to=EASYTRACK_EVMSCRIPT_EXECUTOR, + ), + ), # - # III. Add EasyTrack factories for SimpleDVT module + # V. Add EasyTrack EVM script factories for SimpleDVT module to EasyTrack registry # ( - "9) Add AddNodeOperators EVM script factory", + "11) Add AddNodeOperators EVM script factory", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY, permissions=( @@ -158,7 +191,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "10) Add ActivateNodeOperators EVM script factory", + "12) Add ActivateNodeOperators EVM script factory", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY, permissions=( @@ -168,7 +201,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "11) Add DeactivateNodeOperators EVM script factory", + "13) Add DeactivateNodeOperators EVM script factory", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY, permissions=( @@ -178,35 +211,35 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "12) Add SetVettedValidatorsLimits EVM script factory", + "14) Add SetVettedValidatorsLimits EVM script factory", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY, permissions=(create_permissions(contracts.simple_dvt, "setNodeOperatorStakingLimit")), ), ), ( - "13) Add UpdateTargetValidatorLimits EVM script factory", + "15) Add UpdateTargetValidatorLimits EVM script factory", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY, permissions=(create_permissions(contracts.simple_dvt, "updateTargetValidatorsLimits")), ), ), ( - "14) Add SetNodeOperatorNames EVM script factory", + "16) Add SetNodeOperatorNames EVM script factory", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_NAMES_FACTORY, permissions=(create_permissions(contracts.simple_dvt, "setNodeOperatorName")), ), ), ( - "15) Add SetNodeOperatorRewardAddresses EVM script factory", + "17) Add SetNodeOperatorRewardAddresses EVM script factory", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY, permissions=(create_permissions(contracts.simple_dvt, "setNodeOperatorRewardAddress")), ), ), ( - "16) Add ChangeNodeOperatorManagers EVM script factory", + "18) Add ChangeNodeOperatorManagers EVM script factory", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY, permissions=( @@ -216,59 +249,52 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), # - # IV. Finish SimpleDVT module setup + # VI. Update Oracle Report Sanity Checker parameters # ( - "17) Grant REQUEST_BURN_SHARES_ROLE on Burner for SimpleDVT module", + "19) Grant MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE to the Lido DAO Agent on OracleReportSanityChecker contract", agent_forward( [ encode_oz_grant_role( - contract=contracts.burner, - role_name="REQUEST_BURN_SHARES_ROLE", - grant_to=contracts.simple_dvt, + contract=contracts.oracle_report_sanity_checker, + role_name="MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE", + grant_to=contracts.agent, ) ] ), ), ( - "18) Add SimpleDVT module to StakingRouter", + "20) Grant MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE to the Lido DAO Agent on OracleReportSanityChecker contract", agent_forward( [ - ( - contracts.staking_router.address, - contracts.staking_router.addStakingModule.encode_input( - SIMPLE_DVT_MODULE_NAME, - contracts.simple_dvt, - SIMPLE_DVT_MODULE_TARGET_SHARE_BP, - SIMPLE_DVT_MODULE_MODULE_FEE_BP, - SIMPLE_DVT_MODULE_TREASURY_FEE_BP, - ), - ), + encode_oz_grant_role( + contract=contracts.oracle_report_sanity_checker, + role_name="MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE", + grant_to=contracts.agent, + ) ] ), ), - # - # V. Update Sanity Checker parameters - # ( - "19) Grant REQUEST_BURN_SHARES_ROLE on Burner for SimpleDVT module", + "21) Set maxAccountingExtraDataListItemsCount sanity checker parameter to 4", agent_forward( [ - encode_oz_grant_role( - contract=contracts.oracle_report_sanity_checker, - role_name="MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE", - grant_to=contracts.agent, - ) + ( + contracts.oracle_report_sanity_checker.address, + contracts.oracle_report_sanity_checker.setMaxAccountingExtraDataListItemsCount.encode_input(4), + ), ] ), ), ( - "20) Increase maxAccountingExtraDataListItemsCount sanity checker parameter from 2 to 4", + "22) Set maxNodeOperatorsPerExtraDataItemCount sanity checker parameter to 50", agent_forward( [ ( contracts.oracle_report_sanity_checker.address, - contracts.oracle_report_sanity_checker.setMaxAccountingExtraDataListItemsCount.encode_input(4), + contracts.oracle_report_sanity_checker.setMaxNodeOperatorsPerExtraDataItemCount.encode_input( + 50 + ), ), ] ), diff --git a/tests/acceptance/test_simple_dvt_module.py b/tests/acceptance/test_simple_dvt_module.py index 4721fded..169672ae 100644 --- a/tests/acceptance/test_simple_dvt_module.py +++ b/tests/acceptance/test_simple_dvt_module.py @@ -157,16 +157,25 @@ def test_simple_dvt_state(contract): assert node_operator_summary["depositableValidatorsCount"] == no_depositable_validators_count -def test_simple_dvt_easytrack(contract): - - easy_track = contracts.easy_track - +def test_simple_dvt_permissions(contract): + assert contracts.acl.getPermissionManager(contract.address, STAKING_ROUTER_ROLE) == contract.voting.address + assert contract.canPerform(contract.address, STAKING_ROUTER_ROLE, []) assert contract.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, STAKING_ROUTER_ROLE, []) + + assert contracts.acl.getPermissionManager(contract.address, MANAGE_NODE_OPERATOR_ROLE) == contract.voting.address assert contract.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, MANAGE_NODE_OPERATOR_ROLE, []) + + assert contracts.acl.getPermissionManager(contract.address, SET_NODE_OPERATOR_LIMIT_ROLE) == contract.voting.address assert contract.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, SET_NODE_OPERATOR_LIMIT_ROLE, []) + assert contracts.acl.getPermissionManager(contract.address, MANAGE_SIGNING_KEYS) == EASYTRACK_EVMSCRIPT_EXECUTOR assert contract.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, MANAGE_SIGNING_KEYS, []) + +def test_simple_dvt_easytrack(contract): + + easy_track = contracts.easy_track + add_node_operators_evm_script_factory = EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY activate_node_operators_evm_script_factory = EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY deactivate_node_operators_evm_script_factory = EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY diff --git a/tests/test_simple_dvt.py b/tests/test_simple_dvt.py index f2b63d2d..b467194f 100644 --- a/tests/test_simple_dvt.py +++ b/tests/test_simple_dvt.py @@ -63,6 +63,7 @@ SET_NODE_OPERATOR_LIMIT_ROLE = "0x07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754" MANAGE_SIGNING_KEYS = "0x75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee" MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE = "0x0cf253eb71298c92e2814969a122f66b781f9b217f8ecde5401e702beb9345f6" +MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE = "0xf6ac39904c42f8e23056f1b678e4892fc92caa68ae836dc474e137f0e67f5716" simple_dvt_repo_ens = "simple-dvt.lidopm.eth" simple_dvt_content_uri = ( @@ -113,6 +114,7 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco sanity_checker_limits = contracts.oracle_report_sanity_checker.getOracleReportLimits() assert sanity_checker_limits["maxAccountingExtraDataListItemsCount"] == 2 + assert sanity_checker_limits["maxNodeOperatorsPerExtraDataItemCount"] == 100 # START VOTE if len(vote_ids_from_env) > 0: @@ -262,9 +264,10 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco sanity_checker_limits = contracts.oracle_report_sanity_checker.getOracleReportLimits() assert sanity_checker_limits["maxAccountingExtraDataListItemsCount"] == 4 + assert sanity_checker_limits["maxNodeOperatorsPerExtraDataItemCount"] == 50 # validate vote events - assert count_vote_items_by_events(vote_tx, contracts.voting) == 20, "Incorrect voting items count" + assert count_vote_items_by_events(vote_tx, contracts.voting) == 22, "Incorrect voting items count" metadata = find_metadata_by_vote_id(vote_id) print("metadata", metadata) @@ -279,6 +282,7 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco evs = group_voting_events(vote_tx) + # I. Create new Aragon DAO Application Repo for SimpleDVT repo_params = NewRepoItem( name=SIMPLE_DVT_ARAGON_APP_NAME, app=simple_dvt.address, @@ -290,10 +294,11 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ) validate_new_repo_with_version_event(evs[0], repo_params) + # II. Setup and initialize SimpleDVT module as new Aragon app validate_app_update_event(evs[1], SIMPLE_DVT_ARAGON_APP_ID, SIMPLE_DVT_IMPL) - validate_simple_dvt_intialize_event(evs[2]) + # III. Add SimpleDVT module to Staking Router # Create and grant permission STAKING_ROUTER_ROLE on SimpleDVT module for StakingRouter permission = Permission( entity=staking_router, @@ -302,36 +307,48 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ) validate_permission_create_event(evs[3], permission, manager=voting) - # Grant STAKING_ROUTER_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor - permission = Permission(entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=STAKING_ROUTER_ROLE) - validate_permission_grant_event(evs[4], permission) + # Grant REQUEST_BURN_SHARES_ROLE on Burner for SimpleDVT module + validate_grant_role_event(evs[4], REQUEST_BURN_SHARES_ROLE, simple_dvt, agent) + + # Add SimpleDVT module to StakingRouter + module_item = StakingModuleItem( + SIMPLE_DVT_MODULE_ID, + simple_dvt.address, + SIMPLE_DVT_MODULE_NAME, + SIMPLE_DVT_MODULE_TARGET_SHARE_BP, + SIMPLE_DVT_MODULE_MODULE_FEE_BP, + SIMPLE_DVT_MODULE_TREASURY_FEE_BP, + ) + validate_staking_module_added_event(evs[5], module_item) - # Create and grant permission MANAGE_NODE_OPERATOR_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor + # IV. Grant permissions to make operational changes to SimpleDVT module permission = Permission( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=MANAGE_NODE_OPERATOR_ROLE, # simple_dvt.MANAGE_NODE_OPERATOR_ROLE(), ) - validate_permission_create_event(evs[5], permission, manager=voting) + validate_permission_create_event(evs[6], permission, manager=voting) - # Create and grant permission SET_NODE_OPERATOR_LIMIT_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor permission = Permission( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=SET_NODE_OPERATOR_LIMIT_ROLE, # simple_dvt.SET_NODE_OPERATOR_LIMIT_ROLE(), ) - validate_permission_create_event(evs[6], permission, manager=voting) + validate_permission_create_event(evs[7], permission, manager=voting) - # Create and grant permission MANAGE_SIGNING_KEYS on SimpleDVT module for EasyTrackEVMScriptExecutor permission = Permission( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=MANAGE_SIGNING_KEYS, # simple_dvt.MANAGE_SIGNING_KEYS(), ) - validate_permission_create_event(evs[7], permission, manager=EASYTRACK_EVMSCRIPT_EXECUTOR) + validate_permission_create_event(evs[8], permission, manager=EASYTRACK_EVMSCRIPT_EXECUTOR) + + permission = Permission(entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=STAKING_ROUTER_ROLE) + validate_permission_grant_event(evs[9], permission) + # IV. Add EasyTrack EVM script factories for SimpleDVT module validate_evmscript_factory_added_event( - evs[8], + evs[10], EVMScriptFactoryAdded( factory_addr=add_node_operators_evm_script_factory, permissions=create_permissions(simple_dvt, "addNodeOperator") @@ -339,7 +356,7 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ), ) validate_evmscript_factory_added_event( - evs[9], + evs[11], EVMScriptFactoryAdded( factory_addr=activate_node_operators_evm_script_factory, permissions=create_permissions(simple_dvt, "activateNodeOperator") @@ -347,7 +364,7 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ), ) validate_evmscript_factory_added_event( - evs[10], + evs[12], EVMScriptFactoryAdded( factory_addr=deactivate_node_operators_evm_script_factory, permissions=create_permissions(simple_dvt, "deactivateNodeOperator") @@ -355,35 +372,35 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ), ) validate_evmscript_factory_added_event( - evs[11], + evs[13], EVMScriptFactoryAdded( factory_addr=set_vetted_validators_limits_evm_script_factory, permissions=create_permissions(simple_dvt, "setNodeOperatorStakingLimit"), ), ) validate_evmscript_factory_added_event( - evs[12], + evs[14], EVMScriptFactoryAdded( factory_addr=update_target_validator_limits_evm_script_factory, permissions=create_permissions(simple_dvt, "updateTargetValidatorsLimits"), ), ) validate_evmscript_factory_added_event( - evs[13], + evs[15], EVMScriptFactoryAdded( factory_addr=set_node_operator_names_evm_script_factory, permissions=create_permissions(simple_dvt, "setNodeOperatorName"), ), ) validate_evmscript_factory_added_event( - evs[14], + evs[16], EVMScriptFactoryAdded( factory_addr=set_node_operator_reward_addresses_evm_script_factory, permissions=create_permissions(simple_dvt, "setNodeOperatorRewardAddress"), ), ) validate_evmscript_factory_added_event( - evs[15], + evs[17], EVMScriptFactoryAdded( factory_addr=change_node_operator_managers_evm_script_factory, permissions=create_permissions(contracts.acl, "revokePermission") @@ -391,20 +408,11 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ), ) - validate_grant_role_event(evs[16], REQUEST_BURN_SHARES_ROLE, simple_dvt, agent) - - module_item = StakingModuleItem( - SIMPLE_DVT_MODULE_ID, - simple_dvt.address, - SIMPLE_DVT_MODULE_NAME, - SIMPLE_DVT_MODULE_TARGET_SHARE_BP, - SIMPLE_DVT_MODULE_MODULE_FEE_BP, - SIMPLE_DVT_MODULE_TREASURY_FEE_BP, - ) - validate_staking_module_added_event(evs[17], module_item) - + # VI. Update Oracle Report Sanity Checker parameters validate_grant_role_event(evs[18], MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE, agent, agent) - validate_max_extra_data_list_items_count_event(evs[19], 4) + validate_grant_role_event(evs[19], MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE, agent, agent) + validate_max_extra_data_list_items_count_event(evs[20], 4) + validate_max_operators_per_extra_data_item_count_event(evs[21], 50) def has_permission(permission: Permission, how: List[int]) -> bool: @@ -427,6 +435,20 @@ def validate_max_extra_data_list_items_count_event(event: EventDict, value: int) assert event["MaxAccountingExtraDataListItemsCountSet"]["maxAccountingExtraDataListItemsCount"] == value +def validate_max_operators_per_extra_data_item_count_event(event: EventDict, value: int): + _events_chain = [ + "LogScriptCall", + "LogScriptCall", + "MaxNodeOperatorsPerExtraDataItemCountSet", + "ScriptResult", + ] + + validate_events_chain([e.name for e in event], _events_chain) + + assert event.count("MaxNodeOperatorsPerExtraDataItemCountSet") == 1 + assert event["MaxNodeOperatorsPerExtraDataItemCountSet"]["maxNodeOperatorsPerExtraDataItemCount"] == value + + def validate_simple_dvt_intialize_event(event: EventDict): _events_chain = [ "LogScriptCall", From 132d4fe90d62540d62e1ba9ab75422e643fb3319 Mon Sep 17 00:00:00 2001 From: F4ever Date: Tue, 13 Feb 2024 16:33:28 +0100 Subject: [PATCH 30/68] keys fixtures --- tests/regression/test_staking_module_happy_path.py | 6 +++++- utils/test/simple_dvt_helpers.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/regression/test_staking_module_happy_path.py b/tests/regression/test_staking_module_happy_path.py index b7901274..c99f2c3b 100644 --- a/tests/regression/test_staking_module_happy_path.py +++ b/tests/regression/test_staking_module_happy_path.py @@ -12,6 +12,7 @@ ) from utils.config import contracts, STAKING_ROUTER, EASYTRACK_EVMSCRIPT_EXECUTOR from utils.test.node_operators_helpers import node_operator_gindex +from utils.test.simple_dvt_helpers import fill_simple_dvt_ops_vetted_keys @pytest.fixture(scope="function") @@ -844,8 +845,11 @@ def test_node_operator_registry(impersonated_voting, eth_whale): module_happy_path(nor, ExtraDataService(), impersonated_voting, impersonated_voting, eth_whale) -def test_sdvt(impersonated_voting, impersonate_es_executor, eth_whale): +def test_sdvt(impersonated_voting, impersonate_es_executor, stranger, eth_whale): sdvt = contracts.simple_dvt sdvt.module_id = 2 sdvt.testing_node_operator_ids = [0, 1, 2] + fill_simple_dvt_ops_vetted_keys(stranger, 3, 100) + fill_simple_dvt_ops_vetted_keys(stranger, 3, 200) + fill_simple_dvt_ops_vetted_keys(stranger, 3, 300) module_happy_path(sdvt, ExtraDataService(), impersonated_voting, impersonate_es_executor, eth_whale) diff --git a/utils/test/simple_dvt_helpers.py b/utils/test/simple_dvt_helpers.py index 0f40caf3..222de7b9 100644 --- a/utils/test/simple_dvt_helpers.py +++ b/utils/test/simple_dvt_helpers.py @@ -9,7 +9,7 @@ from utils.test.easy_track_helpers import _encode_calldata -MIN_OP_KEYS_CNT = 300 +MIN_OP_KEYS_CNT = 10 MIN_OPS_CNT = 3 OPERATOR_NAMES = [ From cc5a9af123f2ab1c016bad95993b8969a97954f4 Mon Sep 17 00:00:00 2001 From: F4ever Date: Tue, 13 Feb 2024 16:54:24 +0100 Subject: [PATCH 31/68] keys fixtures --- tests/regression/test_staking_module_happy_path.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/regression/test_staking_module_happy_path.py b/tests/regression/test_staking_module_happy_path.py index c99f2c3b..4b9b7bc8 100644 --- a/tests/regression/test_staking_module_happy_path.py +++ b/tests/regression/test_staking_module_happy_path.py @@ -164,6 +164,7 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, i staking_module.setNodeOperatorStakingLimit(op_index, no["totalDepositedValidators"], {"from": impersonated_executor}) increase_limit(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 3, impersonated_executor) + deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 9, impersonated_executor) penalty_delay = staking_module.getStuckPenaltyDelay() From da338ba384209c709fde7bcac02d471abd9c505a Mon Sep 17 00:00:00 2001 From: F4ever Date: Tue, 13 Feb 2024 17:06:44 +0100 Subject: [PATCH 32/68] predeposit --- tests/regression/test_staking_module_happy_path.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/regression/test_staking_module_happy_path.py b/tests/regression/test_staking_module_happy_path.py index 4b9b7bc8..fa3e3e44 100644 --- a/tests/regression/test_staking_module_happy_path.py +++ b/tests/regression/test_staking_module_happy_path.py @@ -839,7 +839,9 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, i assert deposited_keys_base_before != deposited_keys_base_after -def test_node_operator_registry(impersonated_voting, eth_whale): +def test_node_operator_registry(impersonated_voting, impersonate_es_executor, eth_whale): + deposit_and_check_keys(contracts.simple_dvt, 0, 1, 2, 9, impersonate_es_executor) + nor = contracts.node_operators_registry nor.module_id = 1 nor.testing_node_operator_ids = [23, 20, 28] From c490ad2420d8af5dda856a722d8cd48ca925b67d Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Wed, 14 Feb 2024 09:43:56 +0400 Subject: [PATCH 33/68] feat: add SimpleDVT to VEBO happy path test --- .../test_validator_exit_bus_happy_path.py | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/tests/regression/test_validator_exit_bus_happy_path.py b/tests/regression/test_validator_exit_bus_happy_path.py index 6053274d..36914323 100644 --- a/tests/regression/test_validator_exit_bus_happy_path.py +++ b/tests/regression/test_validator_exit_bus_happy_path.py @@ -9,6 +9,7 @@ reach_consensus, prepare_exit_bus_report, ) +from utils.test.simple_dvt_helpers import simple_dvt_add_node_operators, simple_dvt_add_keys, simple_dvt_vet_keys @dataclasses.dataclass @@ -130,22 +131,33 @@ def test_send_validator_to_exit(helpers, web3): assert processing_state_after.requests_submitted == processing_state_before.requests_submitted + 1 -def test_send_multiple_validators_to_exit(helpers, web3): +def test_send_multiple_validators_to_exit(helpers, web3, stranger): """ - The same as test above but with multiple validators on different node operators + The same as test above but with multiple validators on different node operators and modules """ + # Fill SDVT + simple_dvt_add_node_operators( + contracts.simple_dvt, stranger, [("SDVT Operator", f"0xab{'1' * 38}", f"0xcd{'1' * 38}")] + ) + simple_dvt_add_keys(contracts.simple_dvt, 0, 2) + simple_dvt_vet_keys(0, stranger) + first_no_global_index = (first_module_id, first_no_id) = (1, 37) second_no_global_index = (second_module_id, second_no_id) = (1, 38) + third_no_global_index = (third_module_id, third_no_id) = (2, 0) first_validator_id = 2 second_validator_id = 3 + third_validator_id = 0 first_validator_key = contracts.node_operators_registry.getSigningKey(first_no_id, first_validator_id)[0] second_validator_key = contracts.node_operators_registry.getSigningKey(second_no_id, second_validator_id)[0] + third_validator_key = contracts.simple_dvt.getSigningKey(third_no_id, third_validator_id)[0] first_validator = LidoValidator(first_validator_id, first_validator_key) second_validator = LidoValidator(second_validator_id, second_validator_key) + third_validator = LidoValidator(third_validator_id, third_validator_key) ref_slot = _wait_for_next_ref_slot() report, report_hash = prepare_exit_bus_report( - [(first_no_global_index, first_validator), (second_no_global_index, second_validator)], ref_slot + [(first_no_global_index, first_validator), (second_no_global_index, second_validator), (third_no_global_index, third_validator)], ref_slot ) report_hash_hex = HexString(report_hash, "bytes") @@ -159,6 +171,9 @@ def test_send_multiple_validators_to_exit(helpers, web3): second_last_requested_validator_index_before = ( contracts.validators_exit_bus_oracle.getLastRequestedValidatorIndices(second_module_id, [second_no_id]) ) + third_last_requested_validator_index_before = ( + contracts.validators_exit_bus_oracle.getLastRequestedValidatorIndices(third_module_id, [third_no_id]) + ) tx = send_report_with_consensus(ref_slot, report, report_hash) @@ -172,11 +187,14 @@ def test_send_multiple_validators_to_exit(helpers, web3): second_last_requested_validator_index_after = contracts.validators_exit_bus_oracle.getLastRequestedValidatorIndices( second_module_id, [second_no_id] ) + third_last_requested_validator_index_after = contracts.validators_exit_bus_oracle.getLastRequestedValidatorIndices( + third_module_id, [third_no_id] + ) # Asserts helpers.assert_single_event_named("ProcessingStarted", tx, {"refSlot": ref_slot, "hash": report_hash_hex}) events = helpers.filter_events_from(tx.receiver, tx.events["ValidatorExitRequest"]) - assert len(events) == 2 + assert len(events) == 3 assert dict(events[0]) == { "stakingModuleId": first_module_id, "nodeOperatorId": first_no_id, @@ -191,13 +209,22 @@ def test_send_multiple_validators_to_exit(helpers, web3): "validatorPubkey": second_validator_key, "timestamp": web3.eth.get_block(web3.eth.block_number).timestamp, } + assert dict(events[2]) == { + "stakingModuleId": third_module_id, + "nodeOperatorId": third_no_id, + "validatorIndex": third_validator_id, + "validatorPubkey": third_validator_key, + "timestamp": web3.eth.get_block(web3.eth.block_number).timestamp, + } - assert total_requests_after == total_requests_before + 2 + assert total_requests_after == total_requests_before + 3 assert first_last_requested_validator_index_before == (-1,) assert second_last_requested_validator_index_before == (-1,) + assert third_last_requested_validator_index_before == (-1,) assert first_last_requested_validator_index_after == (first_validator_id,) assert second_last_requested_validator_index_after == (second_validator_id,) + assert third_last_requested_validator_index_after == (third_validator_id,) assert last_processing_ref_slot_after != last_processing_ref_slot_before assert last_processing_ref_slot_after == ref_slot @@ -205,5 +232,5 @@ def test_send_multiple_validators_to_exit(helpers, web3): assert processing_state_after.data_hash == report_hash_hex assert processing_state_after.data_submitted assert processing_state_after.data_format == contracts.validators_exit_bus_oracle.DATA_FORMAT_LIST() - assert processing_state_after.requests_count == processing_state_before.requests_count + 2 - assert processing_state_after.requests_submitted == processing_state_before.requests_submitted + 2 + assert processing_state_after.requests_count == processing_state_before.requests_count + 3 + assert processing_state_after.requests_submitted == processing_state_before.requests_submitted + 3 From 72e6752f80aaef5dc9ecff8f21b26e0aec55540d Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Tue, 13 Feb 2024 14:03:18 +0400 Subject: [PATCH 34/68] draft: test --- ...accounting_oracle_extra_data_full_items.py | 295 ++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 tests/regression/test_accounting_oracle_extra_data_full_items.py diff --git a/tests/regression/test_accounting_oracle_extra_data_full_items.py b/tests/regression/test_accounting_oracle_extra_data_full_items.py new file mode 100644 index 00000000..b796158e --- /dev/null +++ b/tests/regression/test_accounting_oracle_extra_data_full_items.py @@ -0,0 +1,295 @@ +import random +import textwrap + +import pytest +from brownie import convert +from brownie.network.account import Account +from brownie.network.web3 import Web3 + +from utils.test.deposits_helpers import fill_deposit_buffer +from utils.test.extra_data import ExtraDataService +from utils.test.oracle_report_helpers import oracle_report + +from utils.config import MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT, MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT +from utils.config import contracts +from utils.test.simple_dvt_helpers import simple_dvt_add_node_operators, simple_dvt_add_keys, simple_dvt_vet_keys + +PUBKEY_LENGTH = 48 +SIGNATURE_LENGTH = 96 + + +@pytest.fixture() +def extra_data_service(): + return ExtraDataService() + + +@pytest.fixture +def voting_eoa(accounts): + return accounts.at(contracts.voting.address, force=True) + + +@pytest.fixture +def agent_eoa(accounts): + return accounts.at(contracts.agent.address, force=True) + + +@pytest.fixture +def evm_script_executor_eoa(accounts): + return accounts.at(contracts.easy_track.evmScriptExecutor(), force=True) + + +@pytest.fixture +def nor(interface): + return interface.NodeOperatorsRegistry(contracts.node_operators_registry.address) + + +@pytest.fixture +def sdvt(interface): + return interface.SimpleDVT(contracts.simple_dvt.address) + + +@pytest.mark.parametrize( + ("nor_stuck_items", "nor_exited_items", "sdvt_stuck_items", "sdvt_exited_items"), + [ + (1, 1, 1, 1), + (1, 1, 1, 0), + (1, 1, 0, 1), + (1, 1, 0, 0), + (1, 0, 1, 1), + (1, 0, 1, 0), + (1, 0, 0, 1), + (1, 0, 0, 0), + (0, 1, 1, 1), + (0, 1, 1, 0), + (0, 1, 0, 1), + (0, 1, 0, 0), + (0, 0, 1, 1), + (0, 0, 1, 0), + (0, 0, 0, 1), + ] +) +def test_extra_data_full_items( + stranger, voting_eoa, agent_eoa, evm_script_executor_eoa, nor, sdvt, extra_data_service, + nor_stuck_items, nor_exited_items, sdvt_stuck_items, sdvt_exited_items +): + max_node_operators_per_item = MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT + new_keys_per_operator = 2 + + # Fill NOR with new operators and keys + (nor_count_before, added_nor_operators_count) = add_nor_operators_with_keys( + nor, + new_keys_per_operator, + nor_stuck_items, + nor_exited_items, + max_node_operators_per_item, + voting_eoa, + agent_eoa, + evm_script_executor_eoa + ) + + # Fill SimpleDVT with new operators and keys + sdvt_operators_count = (sdvt_stuck_items + sdvt_exited_items) * max_node_operators_per_item + add_sdvt_operators_with_keys(stranger, sdvt_operators_count, new_keys_per_operator) + + # Deposit for new added keys from buffer + deposit_buffer_for_keys( + contracts.staking_router, + sdvt_operators_count * new_keys_per_operator, + (added_nor_operators_count * new_keys_per_operator) + (nor_count_before * new_keys_per_operator) + ) + + # Prepare report extra data + nor_stuck = {(1, i): 1 for i in range(0, nor_stuck_items * max_node_operators_per_item)} + nor_exited = {(1, i): nor.getNodeOperatorSummary(i)['totalExitedValidators'] + 1 for i in range(0, nor_exited_items * max_node_operators_per_item)} + sdvt_stuck = {(2, i): 1 for i in range(0, sdvt_stuck_items * max_node_operators_per_item)} + sdvt_exited = {(2, i): 1 for i in range(0, sdvt_exited_items * max_node_operators_per_item)} + extra_data = extra_data_service.collect( + {**nor_stuck, **sdvt_stuck}, + {**nor_exited, **sdvt_exited}, + 4, + MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT, + ) + modules_with_exited = [] + num_exited_validators_by_staking_module = [] + if nor_exited_items > 0: + modules_with_exited.append(1) + nor_exited_before = nor.getStakingModuleSummary()["totalExitedValidators"] + num_exited_validators_by_staking_module.append(nor_exited_before + (nor_exited_items * max_node_operators_per_item)) + if sdvt_exited_items > 0: + modules_with_exited.append(2) + num_exited_validators_by_staking_module.append(sdvt_exited_items * max_node_operators_per_item) + + oracle_report( + extraDataFormat=1, + extraDataHash=extra_data.data_hash, + extraDataItemsCount=4, + extraDataList=extra_data.extra_data, + stakingModuleIdsWithNewlyExitedValidators=modules_with_exited, + numExitedValidatorsByStakingModule=num_exited_validators_by_staking_module, + ) + + # TODO: Rewards are distributed correctly (1/2 of rewards for operators with staked keys are burned) + + # Exited and stuck keys are reported correctly + for i in range(0, len(nor_exited)): + assert nor.getNodeOperatorSummary(i)["totalExitedValidators"] == nor_exited[(1, i)] + for i in range(0, len(nor_stuck)): + assert nor.getNodeOperatorSummary(i)["stuckValidatorsCount"] == nor_stuck[(1, i)] + assert nor.isOperatorPenalized(i) == True + for i in range(0, len(sdvt_exited)): + assert sdvt.getNodeOperatorSummary(i)["totalExitedValidators"] == sdvt_exited[(2, i)] + for i in range(0, len(sdvt_stuck)): + assert sdvt.getNodeOperatorSummary(i)["stuckValidatorsCount"] == sdvt_stuck[(2, i)] + assert sdvt.isOperatorPenalized(i) == True + +############################################ +# HELPER FUNCTIONS +############################################ + + +def random_pubkeys_batch(pubkeys_count: int): + return random_hexstr(pubkeys_count * PUBKEY_LENGTH) + + +def random_signatures_batch(signautes_count: int): + return random_hexstr(signautes_count * SIGNATURE_LENGTH) + + +def parse_pubkeys_batch(pubkeys_batch: str): + return hex_chunks(pubkeys_batch, PUBKEY_LENGTH) + + +def parse_signatures_batch(signatures_batch: str): + return hex_chunks(signatures_batch, SIGNATURE_LENGTH) + + +def hex_chunks(hexstr: str, chunk_length: int): + stripped_hexstr = strip_0x(hexstr) + assert len(stripped_hexstr) % chunk_length == 0, "invalid hexstr length" + return [prefix_0x(chunk) for chunk in textwrap.wrap(stripped_hexstr, 2 * chunk_length)] + + +def random_hexstr(length: int): + return prefix_0x(random.randbytes(length).hex()) + + +def prefix_0x(hexstr: str): + return hexstr if hexstr.startswith("0x") else "0x" + hexstr + + +def strip_0x(hexstr: str): + return hexstr[2:] if hexstr.startswith("0x") else hexstr + + +def add_sdvt_operators_with_keys(enactor: Account, count: int, keys_per_operator: int): + names = [f"Name {i}" for i in range(0, count)] + reward_addresses = [f"0xab{str(i).zfill(38)}" for i in range(0, count)] + managers = [f"0xcd{str(i).zfill(38)}" for i in range(0, count)] + + # 30 at a time + for i in range(0, count, 30): + (operators_count_before, _) = simple_dvt_add_node_operators( + contracts.simple_dvt, + enactor, + [ + (names[j], reward_addresses[j], managers[j]) + for j in range(i, i + 30) if j < count + ] + ) + for j in range(i, i + 30): + if j >= count: + break + simple_dvt_add_keys(contracts.simple_dvt, j, keys_per_operator) + simple_dvt_vet_keys(j, enactor) + + +def add_new_nor_operators_with_keys(nor, voting_eoa: Account, evm_script_executor_eoa: Account, count: int, + keys_per_operator: int): + names = [f"Name {i}" for i in range(0, count)] + reward_addresses = [f"0xbb{str(i).zfill(38)}" for i in range(0, count)] + # managers = [f"0xdd{str(i).zfill(38)}" for i in range(0, count)] + + # 30 at a time + for i in range(0, count, 30): + # should be more than count + for j in range(i, i + 30): + if j >= count: + break + nor.addNodeOperator( + names[j], + reward_addresses[j], + {"from": voting_eoa} + ) + no_id = nor.getNodeOperatorsCount() - 1 + pubkeys_batch = random_pubkeys_batch(keys_per_operator) + signatures_batch = random_signatures_batch(keys_per_operator) + nor.addSigningKeysOperatorBH( + no_id, + keys_per_operator, + pubkeys_batch, + signatures_batch, + {"from": reward_addresses[j]}, + ) + nor.setNodeOperatorStakingLimit(no_id, keys_per_operator, {"from": evm_script_executor_eoa}) + + +def add_nor_operators_with_keys( + nor, new_keys_per_operator, nor_stuck_items, nor_exited_items, max_node_operators_per_item, voting_eoa, agent_eoa, evm_script_executor_eoa +) -> tuple[int, int]: + # Curated: Add new operators and keys + contracts.staking_router.grantRole( + contracts.staking_router.MANAGE_WITHDRAWAL_CREDENTIALS_ROLE(), voting_eoa, {"from": agent_eoa} + ) + contracts.acl.grantPermission( + contracts.voting, + contracts.node_operators_registry, + convert.to_uint(Web3.keccak(text="MANAGE_NODE_OPERATOR_ROLE")), + {"from": contracts.voting}, + ) + nor_count_before = nor.getNodeOperatorsCount() + added_nor_operators_count = ((nor_stuck_items + nor_exited_items) * max_node_operators_per_item) - nor_count_before + add_new_nor_operators_with_keys(nor, voting_eoa, evm_script_executor_eoa, added_nor_operators_count, + new_keys_per_operator) + # Activate old deactivated node operators + nor.activateNodeOperator(1, {"from": voting_eoa}) + nor.activateNodeOperator(12, {"from": voting_eoa}) + # Add keys to old node operators + for i in range(0, nor_count_before): + pubkeys_batch = random_pubkeys_batch(new_keys_per_operator) + signatures_batch = random_signatures_batch(new_keys_per_operator) + operator = nor.getNodeOperator(i, False) + new_deposit_limit = operator["totalDepositedValidators"] + new_keys_per_operator + nor.addSigningKeysOperatorBH( + i, + new_keys_per_operator, + pubkeys_batch, + signatures_batch, + {"from": operator["rewardAddress"]}, + ) + # Change staking limits for old node operators (change to new total added keys count) + nor.setNodeOperatorStakingLimit(i, new_deposit_limit, {"from": evm_script_executor_eoa}) + nor.updateTargetValidatorsLimits(i, True, new_deposit_limit, {"from": contracts.staking_router}) + return nor_count_before, added_nor_operators_count + + +def deposit_buffer_for_keys(staking_router, sdvt_keys_to_deposit, nor_keys_to_deposit): + total_depositable_keys = 0 + module_digests = staking_router.getAllStakingModuleDigests() + for digest in module_digests: + (_, _, state, summary) = digest + (id, _, _, _, target_share, status, _, _, _, _) = state + (exited_keys, deposited_keys, depositable_keys) = summary + total_depositable_keys += depositable_keys + + contracts.lido.removeStakingLimit({"from": contracts.voting}) + fill_deposit_buffer(total_depositable_keys) + keys_per_deposit = 50 + # Deposits for SDVT + times = sdvt_keys_to_deposit // keys_per_deposit + for _ in range(0, times): + contracts.lido.deposit(keys_per_deposit, 2, "0x", {"from": contracts.deposit_security_module}) + + # Deposits for NOR + times = nor_keys_to_deposit // keys_per_deposit; + for _ in range(0, times): + contracts.lido.deposit(keys_per_deposit, 1, "0x", {"from": contracts.deposit_security_module}) From 6b600260b39f7f68f25a96a577dad901a2ce1c80 Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Tue, 13 Feb 2024 14:05:12 +0400 Subject: [PATCH 35/68] draft: use MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT --- configs/config_mainnet.py | 2 +- .../test_accounting_oracle_extra_data_full_items.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index 8eece8af..7bc0803d 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -174,7 +174,7 @@ ANNUAL_BALANCE_INCREASE_BP_LIMIT = 1000 # 10% SIMULATED_SHARE_RATE_DEVIATION_BP_LIMIT = 50 # 0.5% MAX_VALIDATOR_EXIT_REQUESTS_PER_REPORT = 600 -MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT = 2 +MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT = 4 MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT = 100 REQUEST_TIMESTAMP_MARGIN = 7680 # 2 hours rounded to epoch length MAX_POSITIVE_TOKEN_REBASE = 750000 diff --git a/tests/regression/test_accounting_oracle_extra_data_full_items.py b/tests/regression/test_accounting_oracle_extra_data_full_items.py index b796158e..3c895dbd 100644 --- a/tests/regression/test_accounting_oracle_extra_data_full_items.py +++ b/tests/regression/test_accounting_oracle_extra_data_full_items.py @@ -106,7 +106,7 @@ def test_extra_data_full_items( extra_data = extra_data_service.collect( {**nor_stuck, **sdvt_stuck}, {**nor_exited, **sdvt_exited}, - 4, + MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT, MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT, ) modules_with_exited = [] @@ -122,7 +122,7 @@ def test_extra_data_full_items( oracle_report( extraDataFormat=1, extraDataHash=extra_data.data_hash, - extraDataItemsCount=4, + extraDataItemsCount=(nor_exited_items + nor_stuck_items + sdvt_exited_items + sdvt_stuck_items), extraDataList=extra_data.extra_data, stakingModuleIdsWithNewlyExitedValidators=modules_with_exited, numExitedValidatorsByStakingModule=num_exited_validators_by_staking_module, From fbf94284e1170f61a9de0a22f3f584b9b152c7a0 Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Tue, 13 Feb 2024 15:12:59 +0400 Subject: [PATCH 36/68] draft: comment first case --- .../regression/test_accounting_oracle_extra_data_full_items.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/regression/test_accounting_oracle_extra_data_full_items.py b/tests/regression/test_accounting_oracle_extra_data_full_items.py index 3c895dbd..b4024372 100644 --- a/tests/regression/test_accounting_oracle_extra_data_full_items.py +++ b/tests/regression/test_accounting_oracle_extra_data_full_items.py @@ -51,7 +51,8 @@ def sdvt(interface): @pytest.mark.parametrize( ("nor_stuck_items", "nor_exited_items", "sdvt_stuck_items", "sdvt_exited_items"), [ - (1, 1, 1, 1), + # TODO: Doesn't work with full items per 100 operators. Probably, out of gas + # (1, 1, 1, 1), (1, 1, 1, 0), (1, 1, 0, 1), (1, 1, 0, 0), From 28ae98612693486773b6ff4ce5751d544ecec5b8 Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Tue, 13 Feb 2024 15:31:33 +0400 Subject: [PATCH 37/68] fix: old test --- tests/regression/test_accounting_oracle_extra_data.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/regression/test_accounting_oracle_extra_data.py b/tests/regression/test_accounting_oracle_extra_data.py index 13b991cd..dba97b07 100644 --- a/tests/regression/test_accounting_oracle_extra_data.py +++ b/tests/regression/test_accounting_oracle_extra_data.py @@ -2,7 +2,6 @@ from utils.test.extra_data import ExtraDataService from utils.test.oracle_report_helpers import oracle_report -from utils.config import MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT, MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT from utils.config import contracts @@ -18,7 +17,7 @@ def get_exited_count(node_operator_id): def test_accounting_oracle_too_node_ops_per_extra_data_item(extra_data_service): nos_per_item_count = 10 - item_count = MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT + item_count = 2 extra_data = extra_data_service.collect( {(1, i): i for i in range(20, 20 + nos_per_item_count)}, From d23d3dc2618e2457ea0a738adcf1c13d70a13f29 Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Tue, 13 Feb 2024 18:34:20 +0400 Subject: [PATCH 38/68] feat: check penalty amount --- ...accounting_oracle_extra_data_full_items.py | 69 ++++++++++++++++--- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/tests/regression/test_accounting_oracle_extra_data_full_items.py b/tests/regression/test_accounting_oracle_extra_data_full_items.py index b4024372..e9767c00 100644 --- a/tests/regression/test_accounting_oracle_extra_data_full_items.py +++ b/tests/regression/test_accounting_oracle_extra_data_full_items.py @@ -8,6 +8,7 @@ from utils.test.deposits_helpers import fill_deposit_buffer from utils.test.extra_data import ExtraDataService +from utils.test.helpers import shares_balance, almostEqWithDiff from utils.test.oracle_report_helpers import oracle_report from utils.config import MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT, MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT @@ -89,14 +90,18 @@ def test_extra_data_full_items( ) # Fill SimpleDVT with new operators and keys - sdvt_operators_count = (sdvt_stuck_items + sdvt_exited_items) * max_node_operators_per_item + sdvt_operators_count = max(sdvt_stuck_items, sdvt_exited_items) * max_node_operators_per_item add_sdvt_operators_with_keys(stranger, sdvt_operators_count, new_keys_per_operator) # Deposit for new added keys from buffer + keys_for_sdvt = sdvt_operators_count * new_keys_per_operator + keys_for_nor = 0 + if added_nor_operators_count > 0: + keys_for_nor = (added_nor_operators_count * new_keys_per_operator) + (nor_count_before * new_keys_per_operator) deposit_buffer_for_keys( contracts.staking_router, - sdvt_operators_count * new_keys_per_operator, - (added_nor_operators_count * new_keys_per_operator) + (nor_count_before * new_keys_per_operator) + keys_for_sdvt, + keys_for_nor ) # Prepare report extra data @@ -120,7 +125,14 @@ def test_extra_data_full_items( modules_with_exited.append(2) num_exited_validators_by_staking_module.append(sdvt_exited_items * max_node_operators_per_item) - oracle_report( + nor_balance_shares_before = [] + for i in range(0, len(nor_stuck)): + nor_balance_shares_before.append(shares_balance(nor.getNodeOperator(i, False)["rewardAddress"])) + sdvt_balance_shares_before = [] + for i in range(0, len(sdvt_stuck)): + sdvt_balance_shares_before.append(shares_balance(sdvt.getNodeOperator(i, False)["rewardAddress"])) + + (report_tx, extra_report_tx) = oracle_report( extraDataFormat=1, extraDataHash=extra_data.data_hash, extraDataItemsCount=(nor_exited_items + nor_stuck_items + sdvt_exited_items + sdvt_stuck_items), @@ -129,25 +141,64 @@ def test_extra_data_full_items( numExitedValidatorsByStakingModule=num_exited_validators_by_staking_module, ) - # TODO: Rewards are distributed correctly (1/2 of rewards for operators with staked keys are burned) - - # Exited and stuck keys are reported correctly + penalty_shares = 0 + # Check NOR for i in range(0, len(nor_exited)): assert nor.getNodeOperatorSummary(i)["totalExitedValidators"] == nor_exited[(1, i)] + nor_rewards = [e for e in report_tx.events["TransferShares"] if e['to'] == nor.address][0]['sharesValue'] for i in range(0, len(nor_stuck)): assert nor.getNodeOperatorSummary(i)["stuckValidatorsCount"] == nor_stuck[(1, i)] assert nor.isOperatorPenalized(i) == True + shares_after = shares_balance(nor.getNodeOperator(i, False)["rewardAddress"]) + rewards_after = calc_no_rewards( + nor, no_id=i, shares_minted_as_fees=nor_rewards + ) + assert almostEqWithDiff( + shares_after - nor_balance_shares_before[i], + rewards_after // 2, + 1, + ) + penalty_shares += rewards_after // 2 + + # Check SDVT for i in range(0, len(sdvt_exited)): assert sdvt.getNodeOperatorSummary(i)["totalExitedValidators"] == sdvt_exited[(2, i)] + sdvt_rewards = [e for e in report_tx.events["TransferShares"] if e['to'] == sdvt.address][0]['sharesValue'] for i in range(0, len(sdvt_stuck)): assert sdvt.getNodeOperatorSummary(i)["stuckValidatorsCount"] == sdvt_stuck[(2, i)] assert sdvt.isOperatorPenalized(i) == True + shares_after = shares_balance(sdvt.getNodeOperator(i, False)["rewardAddress"]) + rewards_after = calc_no_rewards( + sdvt, no_id=i, shares_minted_as_fees=sdvt_rewards + ) + assert almostEqWithDiff( + shares_after - sdvt_balance_shares_before[i], + rewards_after // 2, + 1, + ) + penalty_shares += rewards_after // 2 + + if penalty_shares > 0: + # TODO: Fix below check when contains other penalized node operators + assert almostEqWithDiff(sum(e['amountOfShares'] for e in extra_report_tx.events["StETHBurnRequested"]), penalty_shares, 100) ############################################ # HELPER FUNCTIONS ############################################ +def calc_no_rewards(module, no_id, shares_minted_as_fees): + operator_summary = module.getNodeOperatorSummary(no_id) + module_summary = module.getStakingModuleSummary() + + operator_total_active_keys = ( + operator_summary["totalDepositedValidators"] - operator_summary["totalExitedValidators"] + ) + module_total_active_keys = module_summary["totalDepositedValidators"] - module_summary["totalExitedValidators"] + + return shares_minted_as_fees * operator_total_active_keys // module_total_active_keys + + def random_pubkeys_batch(pubkeys_count: int): return random_hexstr(pubkeys_count * PUBKEY_LENGTH) @@ -248,7 +299,9 @@ def add_nor_operators_with_keys( {"from": contracts.voting}, ) nor_count_before = nor.getNodeOperatorsCount() - added_nor_operators_count = ((nor_stuck_items + nor_exited_items) * max_node_operators_per_item) - nor_count_before + added_nor_operators_count = (max(nor_stuck_items, nor_exited_items) * max_node_operators_per_item) - nor_count_before + if added_nor_operators_count <= 0: + return nor_count_before, added_nor_operators_count add_new_nor_operators_with_keys(nor, voting_eoa, evm_script_executor_eoa, added_nor_operators_count, new_keys_per_operator) # Activate old deactivated node operators From c29838abc41bc722408739fe0ee789389c3fb2dc Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Tue, 13 Feb 2024 19:53:09 +0400 Subject: [PATCH 39/68] feat: some cool things --- ...accounting_oracle_extra_data_full_items.py | 181 +++++++----------- tests/regression/test_node_operators_flow.py | 49 +---- utils/test/keys_helpers.py | 39 ++++ utils/test/simple_dvt_helpers.py | 3 +- 4 files changed, 114 insertions(+), 158 deletions(-) create mode 100644 utils/test/keys_helpers.py diff --git a/tests/regression/test_accounting_oracle_extra_data_full_items.py b/tests/regression/test_accounting_oracle_extra_data_full_items.py index e9767c00..82edea84 100644 --- a/tests/regression/test_accounting_oracle_extra_data_full_items.py +++ b/tests/regression/test_accounting_oracle_extra_data_full_items.py @@ -1,6 +1,3 @@ -import random -import textwrap - import pytest from brownie import convert from brownie.network.account import Account @@ -9,15 +6,13 @@ from utils.test.deposits_helpers import fill_deposit_buffer from utils.test.extra_data import ExtraDataService from utils.test.helpers import shares_balance, almostEqWithDiff +from utils.test.keys_helpers import random_pubkeys_batch, random_signatures_batch from utils.test.oracle_report_helpers import oracle_report from utils.config import MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT, MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT from utils.config import contracts from utils.test.simple_dvt_helpers import simple_dvt_add_node_operators, simple_dvt_add_keys, simple_dvt_vet_keys -PUBKEY_LENGTH = 48 -SIGNATURE_LENGTH = 96 - @pytest.fixture() def extra_data_service(): @@ -145,38 +140,40 @@ def test_extra_data_full_items( # Check NOR for i in range(0, len(nor_exited)): assert nor.getNodeOperatorSummary(i)["totalExitedValidators"] == nor_exited[(1, i)] - nor_rewards = [e for e in report_tx.events["TransferShares"] if e['to'] == nor.address][0]['sharesValue'] - for i in range(0, len(nor_stuck)): - assert nor.getNodeOperatorSummary(i)["stuckValidatorsCount"] == nor_stuck[(1, i)] - assert nor.isOperatorPenalized(i) == True - shares_after = shares_balance(nor.getNodeOperator(i, False)["rewardAddress"]) - rewards_after = calc_no_rewards( - nor, no_id=i, shares_minted_as_fees=nor_rewards - ) - assert almostEqWithDiff( - shares_after - nor_balance_shares_before[i], - rewards_after // 2, - 1, - ) - penalty_shares += rewards_after // 2 + if len(nor_stuck) > 0: + nor_rewards = [e for e in report_tx.events["TransferShares"] if e['to'] == nor.address][0]['sharesValue'] + for i in range(0, len(nor_stuck)): + assert nor.getNodeOperatorSummary(i)["stuckValidatorsCount"] == nor_stuck[(1, i)] + assert nor.isOperatorPenalized(i) == True + shares_after = shares_balance(nor.getNodeOperator(i, False)["rewardAddress"]) + rewards_after = calc_no_rewards( + nor, no_id=i, shares_minted_as_fees=nor_rewards + ) + assert almostEqWithDiff( + shares_after - nor_balance_shares_before[i], + rewards_after // 2, + 1, + ) + penalty_shares += rewards_after // 2 # Check SDVT for i in range(0, len(sdvt_exited)): assert sdvt.getNodeOperatorSummary(i)["totalExitedValidators"] == sdvt_exited[(2, i)] - sdvt_rewards = [e for e in report_tx.events["TransferShares"] if e['to'] == sdvt.address][0]['sharesValue'] - for i in range(0, len(sdvt_stuck)): - assert sdvt.getNodeOperatorSummary(i)["stuckValidatorsCount"] == sdvt_stuck[(2, i)] - assert sdvt.isOperatorPenalized(i) == True - shares_after = shares_balance(sdvt.getNodeOperator(i, False)["rewardAddress"]) - rewards_after = calc_no_rewards( - sdvt, no_id=i, shares_minted_as_fees=sdvt_rewards - ) - assert almostEqWithDiff( - shares_after - sdvt_balance_shares_before[i], - rewards_after // 2, - 1, - ) - penalty_shares += rewards_after // 2 + if len(sdvt_stuck) > 0: + sdvt_rewards = [e for e in report_tx.events["TransferShares"] if e['to'] == sdvt.address][0]['sharesValue'] + for i in range(0, len(sdvt_stuck)): + assert sdvt.getNodeOperatorSummary(i)["stuckValidatorsCount"] == sdvt_stuck[(2, i)] + assert sdvt.isOperatorPenalized(i) == True + shares_after = shares_balance(sdvt.getNodeOperator(i, False)["rewardAddress"]) + rewards_after = calc_no_rewards( + sdvt, no_id=i, shares_minted_as_fees=sdvt_rewards + ) + assert almostEqWithDiff( + shares_after - sdvt_balance_shares_before[i], + rewards_after // 2, + 1, + ) + penalty_shares += rewards_after // 2 if penalty_shares > 0: # TODO: Fix below check when contains other penalized node operators @@ -187,102 +184,48 @@ def test_extra_data_full_items( ############################################ -def calc_no_rewards(module, no_id, shares_minted_as_fees): - operator_summary = module.getNodeOperatorSummary(no_id) - module_summary = module.getStakingModuleSummary() - - operator_total_active_keys = ( - operator_summary["totalDepositedValidators"] - operator_summary["totalExitedValidators"] - ) - module_total_active_keys = module_summary["totalDepositedValidators"] - module_summary["totalExitedValidators"] - - return shares_minted_as_fees * operator_total_active_keys // module_total_active_keys - - -def random_pubkeys_batch(pubkeys_count: int): - return random_hexstr(pubkeys_count * PUBKEY_LENGTH) - - -def random_signatures_batch(signautes_count: int): - return random_hexstr(signautes_count * SIGNATURE_LENGTH) - - -def parse_pubkeys_batch(pubkeys_batch: str): - return hex_chunks(pubkeys_batch, PUBKEY_LENGTH) - - -def parse_signatures_batch(signatures_batch: str): - return hex_chunks(signatures_batch, SIGNATURE_LENGTH) - - -def hex_chunks(hexstr: str, chunk_length: int): - stripped_hexstr = strip_0x(hexstr) - assert len(stripped_hexstr) % chunk_length == 0, "invalid hexstr length" - return [prefix_0x(chunk) for chunk in textwrap.wrap(stripped_hexstr, 2 * chunk_length)] - - -def random_hexstr(length: int): - return prefix_0x(random.randbytes(length).hex()) - - -def prefix_0x(hexstr: str): - return hexstr if hexstr.startswith("0x") else "0x" + hexstr - - -def strip_0x(hexstr: str): - return hexstr[2:] if hexstr.startswith("0x") else hexstr - - def add_sdvt_operators_with_keys(enactor: Account, count: int, keys_per_operator: int): names = [f"Name {i}" for i in range(0, count)] reward_addresses = [f"0xab{str(i).zfill(38)}" for i in range(0, count)] managers = [f"0xcd{str(i).zfill(38)}" for i in range(0, count)] - # 30 at a time - for i in range(0, count, 30): - (operators_count_before, _) = simple_dvt_add_node_operators( + node_operators_per_tx = 20 + for i in range(0, count, node_operators_per_tx): + simple_dvt_add_node_operators( contracts.simple_dvt, enactor, [ (names[j], reward_addresses[j], managers[j]) - for j in range(i, i + 30) if j < count + for j in range(i, i + node_operators_per_tx) if j < count ] ) - for j in range(i, i + 30): - if j >= count: - break - simple_dvt_add_keys(contracts.simple_dvt, j, keys_per_operator) - simple_dvt_vet_keys(j, enactor) + for i in range(0, count): + simple_dvt_add_keys(contracts.simple_dvt, i, keys_per_operator) + simple_dvt_vet_keys(i, enactor) def add_new_nor_operators_with_keys(nor, voting_eoa: Account, evm_script_executor_eoa: Account, count: int, keys_per_operator: int): names = [f"Name {i}" for i in range(0, count)] reward_addresses = [f"0xbb{str(i).zfill(38)}" for i in range(0, count)] - # managers = [f"0xdd{str(i).zfill(38)}" for i in range(0, count)] - - # 30 at a time - for i in range(0, count, 30): - # should be more than count - for j in range(i, i + 30): - if j >= count: - break - nor.addNodeOperator( - names[j], - reward_addresses[j], - {"from": voting_eoa} - ) - no_id = nor.getNodeOperatorsCount() - 1 - pubkeys_batch = random_pubkeys_batch(keys_per_operator) - signatures_batch = random_signatures_batch(keys_per_operator) - nor.addSigningKeysOperatorBH( - no_id, - keys_per_operator, - pubkeys_batch, - signatures_batch, - {"from": reward_addresses[j]}, - ) - nor.setNodeOperatorStakingLimit(no_id, keys_per_operator, {"from": evm_script_executor_eoa}) + + for i in range(0, count): + nor.addNodeOperator( + names[i], + reward_addresses[i], + {"from": voting_eoa} + ) + no_id = nor.getNodeOperatorsCount() - 1 + pubkeys_batch = random_pubkeys_batch(keys_per_operator) + signatures_batch = random_signatures_batch(keys_per_operator) + nor.addSigningKeysOperatorBH( + no_id, + keys_per_operator, + pubkeys_batch, + signatures_batch, + {"from": reward_addresses[i]}, + ) + nor.setNodeOperatorStakingLimit(no_id, keys_per_operator, {"from": evm_script_executor_eoa}) def add_nor_operators_with_keys( @@ -347,3 +290,15 @@ def deposit_buffer_for_keys(staking_router, sdvt_keys_to_deposit, nor_keys_to_de times = nor_keys_to_deposit // keys_per_deposit; for _ in range(0, times): contracts.lido.deposit(keys_per_deposit, 1, "0x", {"from": contracts.deposit_security_module}) + + +def calc_no_rewards(module, no_id, shares_minted_as_fees): + operator_summary = module.getNodeOperatorSummary(no_id) + module_summary = module.getStakingModuleSummary() + + operator_total_active_keys = ( + operator_summary["totalDepositedValidators"] - operator_summary["totalExitedValidators"] + ) + module_total_active_keys = module_summary["totalDepositedValidators"] - module_summary["totalExitedValidators"] + + return shares_minted_as_fees * operator_total_active_keys // module_total_active_keys diff --git a/tests/regression/test_node_operators_flow.py b/tests/regression/test_node_operators_flow.py index 55c70d10..6cd83ee5 100644 --- a/tests/regression/test_node_operators_flow.py +++ b/tests/regression/test_node_operators_flow.py @@ -1,10 +1,14 @@ import pytest -import random -import textwrap from web3 import Web3 from brownie import Wei, convert from utils.config import contracts +from utils.test.keys_helpers import ( + parse_pubkeys_batch, + parse_signatures_batch, + random_pubkeys_batch, + random_signatures_batch +) from utils.test.node_operators_helpers import ( assert_signing_key, assert_node_operators, @@ -12,8 +16,6 @@ assert_node_operator_added_event, ) -PUBKEY_LENGTH = 48 -SIGNATURE_LENGTH = 96 DEPOSIT_SIZE = Wei("32 ether") @@ -103,7 +105,6 @@ def test_add_node_operator(nor, voting_eoa, reward_address, new_node_operator_id ) assert_node_operator_added_event(tx, new_node_operator_id, new_node_operator_name, reward_address, staking_limit=0) - keys_count = 13 pubkeys_batch = random_pubkeys_batch(keys_count) signatures_batch = random_signatures_batch(keys_count) @@ -149,7 +150,6 @@ def test_add_node_operator(nor, voting_eoa, reward_address, new_node_operator_id # TODO: validate events - nonce_before = nor.getNonce() node_operator_before = nor.getNodeOperator(new_node_operator_id, True) node_operator_summary_before = nor.getNodeOperatorSummary(new_node_operator_id) @@ -173,7 +173,6 @@ def test_add_node_operator(nor, voting_eoa, reward_address, new_node_operator_id # TODO: validate events - node_operators_count_before = nor.getNodeOperatorsCount() active_node_operators_count_before = nor.getActiveNodeOperatorsCount() @@ -203,7 +202,6 @@ def test_add_node_operator(nor, voting_eoa, reward_address, new_node_operator_id # TODO: validate events - node_operator_before = nor.getNodeOperator(new_node_operator_id, True) assert node_operator_before["active"] == False @@ -228,7 +226,6 @@ def test_add_node_operator(nor, voting_eoa, reward_address, new_node_operator_id # TODO: validate events - nonce_before = nor.getNonce() node_operator_before = nor.getNodeOperator(new_node_operator_id, True) node_operator_summary_before = nor.getNodeOperatorSummary(new_node_operator_id) @@ -251,37 +248,3 @@ def test_add_node_operator(nor, voting_eoa, reward_address, new_node_operator_id assert node_operator_summary_after["depositableValidatorsCount"] == new_staking_limit # TODO: validate events - - -def random_pubkeys_batch(pubkeys_count: int): - return random_hexstr(pubkeys_count * PUBKEY_LENGTH) - - -def random_signatures_batch(signautes_count: int): - return random_hexstr(signautes_count * SIGNATURE_LENGTH) - - -def parse_pubkeys_batch(pubkeys_batch: str): - return hex_chunks(pubkeys_batch, PUBKEY_LENGTH) - - -def parse_signatures_batch(signatures_batch: str): - return hex_chunks(signatures_batch, SIGNATURE_LENGTH) - - -def hex_chunks(hexstr: str, chunk_length: int): - stripped_hexstr = strip_0x(hexstr) - assert len(stripped_hexstr) % chunk_length == 0, "invalid hexstr length" - return [prefix_0x(chunk) for chunk in textwrap.wrap(stripped_hexstr, 2 * chunk_length)] - - -def random_hexstr(length: int): - return prefix_0x(random.randbytes(length).hex()) - - -def prefix_0x(hexstr: str): - return hexstr if hexstr.startswith("0x") else "0x" + hexstr - - -def strip_0x(hexstr: str): - return hexstr[2:] if hexstr.startswith("0x") else hexstr diff --git a/utils/test/keys_helpers.py b/utils/test/keys_helpers.py new file mode 100644 index 00000000..08599d09 --- /dev/null +++ b/utils/test/keys_helpers.py @@ -0,0 +1,39 @@ +import random +import textwrap + +PUBKEY_LENGTH = 48 +SIGNATURE_LENGTH = 96 + + +def random_pubkeys_batch(pubkeys_count: int): + return random_hexstr(pubkeys_count * PUBKEY_LENGTH) + + +def random_signatures_batch(signautes_count: int): + return random_hexstr(signautes_count * SIGNATURE_LENGTH) + + +def parse_pubkeys_batch(pubkeys_batch: str): + return hex_chunks(pubkeys_batch, PUBKEY_LENGTH) + + +def parse_signatures_batch(signatures_batch: str): + return hex_chunks(signatures_batch, SIGNATURE_LENGTH) + + +def hex_chunks(hexstr: str, chunk_length: int): + stripped_hexstr = strip_0x(hexstr) + assert len(stripped_hexstr) % chunk_length == 0, "invalid hexstr length" + return [prefix_0x(chunk) for chunk in textwrap.wrap(stripped_hexstr, 2 * chunk_length)] + + +def random_hexstr(length: int): + return prefix_0x(random.randbytes(length).hex()) + + +def prefix_0x(hexstr: str): + return hexstr if hexstr.startswith("0x") else "0x" + hexstr + + +def strip_0x(hexstr: str): + return hexstr[2:] if hexstr.startswith("0x") else hexstr diff --git a/utils/test/simple_dvt_helpers.py b/utils/test/simple_dvt_helpers.py index 222de7b9..3d32c4e7 100644 --- a/utils/test/simple_dvt_helpers.py +++ b/utils/test/simple_dvt_helpers.py @@ -1,5 +1,4 @@ from brownie import chain, accounts, interface -from tests.regression.test_node_operators_flow import random_pubkeys_batch, random_signatures_batch # type: ignore from utils.config import ( contracts, EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, @@ -7,7 +6,7 @@ EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY, ) from utils.test.easy_track_helpers import _encode_calldata - +from utils.test.keys_helpers import random_pubkeys_batch, random_signatures_batch MIN_OP_KEYS_CNT = 10 MIN_OPS_CNT = 3 From 95edf59f7eedc7f3ec0440cbff0b6db827004c76 Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Tue, 13 Feb 2024 21:07:09 +0400 Subject: [PATCH 40/68] feat: polish --- ...accounting_oracle_extra_data_full_items.py | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tests/regression/test_accounting_oracle_extra_data_full_items.py b/tests/regression/test_accounting_oracle_extra_data_full_items.py index 82edea84..d43e12f7 100644 --- a/tests/regression/test_accounting_oracle_extra_data_full_items.py +++ b/tests/regression/test_accounting_oracle_extra_data_full_items.py @@ -73,15 +73,15 @@ def test_extra_data_full_items( new_keys_per_operator = 2 # Fill NOR with new operators and keys - (nor_count_before, added_nor_operators_count) = add_nor_operators_with_keys( + (nor_count_before, added_nor_operators_count) = fill_nor_with_old_and_new_operators( nor, + voting_eoa, + agent_eoa, + evm_script_executor_eoa, new_keys_per_operator, nor_stuck_items, nor_exited_items, max_node_operators_per_item, - voting_eoa, - agent_eoa, - evm_script_executor_eoa ) # Fill SimpleDVT with new operators and keys @@ -127,6 +127,7 @@ def test_extra_data_full_items( for i in range(0, len(sdvt_stuck)): sdvt_balance_shares_before.append(shares_balance(sdvt.getNodeOperator(i, False)["rewardAddress"])) + # Perform report (report_tx, extra_report_tx) = oracle_report( extraDataFormat=1, extraDataHash=extra_data.data_hash, @@ -137,9 +138,10 @@ def test_extra_data_full_items( ) penalty_shares = 0 - # Check NOR + # Check NOR exited for i in range(0, len(nor_exited)): assert nor.getNodeOperatorSummary(i)["totalExitedValidators"] == nor_exited[(1, i)] + # Check NOR stuck. Check penalties and rewards if len(nor_stuck) > 0: nor_rewards = [e for e in report_tx.events["TransferShares"] if e['to'] == nor.address][0]['sharesValue'] for i in range(0, len(nor_stuck)): @@ -156,9 +158,10 @@ def test_extra_data_full_items( ) penalty_shares += rewards_after // 2 - # Check SDVT + # Check SDVT exited for i in range(0, len(sdvt_exited)): assert sdvt.getNodeOperatorSummary(i)["totalExitedValidators"] == sdvt_exited[(2, i)] + # Check SDVT stuck. Check penalties and rewards if len(sdvt_stuck) > 0: sdvt_rewards = [e for e in report_tx.events["TransferShares"] if e['to'] == sdvt.address][0]['sharesValue'] for i in range(0, len(sdvt_stuck)): @@ -204,8 +207,7 @@ def add_sdvt_operators_with_keys(enactor: Account, count: int, keys_per_operator simple_dvt_vet_keys(i, enactor) -def add_new_nor_operators_with_keys(nor, voting_eoa: Account, evm_script_executor_eoa: Account, count: int, - keys_per_operator: int): +def add_nor_operators_with_keys(nor, voting_eoa: Account, evm_script_executor_eoa: Account, count: int, keys_per_operator: int): names = [f"Name {i}" for i in range(0, count)] reward_addresses = [f"0xbb{str(i).zfill(38)}" for i in range(0, count)] @@ -228,8 +230,8 @@ def add_new_nor_operators_with_keys(nor, voting_eoa: Account, evm_script_executo nor.setNodeOperatorStakingLimit(no_id, keys_per_operator, {"from": evm_script_executor_eoa}) -def add_nor_operators_with_keys( - nor, new_keys_per_operator, nor_stuck_items, nor_exited_items, max_node_operators_per_item, voting_eoa, agent_eoa, evm_script_executor_eoa +def fill_nor_with_old_and_new_operators( + nor, voting_eoa, agent_eoa, evm_script_executor_eoa, new_keys_per_operator, nor_stuck_items, nor_exited_items, max_node_operators_per_item, ) -> tuple[int, int]: # Curated: Add new operators and keys contracts.staking_router.grantRole( @@ -245,8 +247,8 @@ def add_nor_operators_with_keys( added_nor_operators_count = (max(nor_stuck_items, nor_exited_items) * max_node_operators_per_item) - nor_count_before if added_nor_operators_count <= 0: return nor_count_before, added_nor_operators_count - add_new_nor_operators_with_keys(nor, voting_eoa, evm_script_executor_eoa, added_nor_operators_count, - new_keys_per_operator) + # Add new node operators and keys + add_nor_operators_with_keys(nor, voting_eoa, evm_script_executor_eoa, added_nor_operators_count, new_keys_per_operator) # Activate old deactivated node operators nor.activateNodeOperator(1, {"from": voting_eoa}) nor.activateNodeOperator(12, {"from": voting_eoa}) From 1e22a85d88c5523c94826dec089c4a0b79b61269 Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Wed, 14 Feb 2024 09:45:34 +0400 Subject: [PATCH 41/68] fix: the first case --- configs/config_mainnet.py | 2 +- .../regression/test_accounting_oracle_extra_data_full_items.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index 7bc0803d..485ca4d7 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -175,7 +175,7 @@ SIMULATED_SHARE_RATE_DEVIATION_BP_LIMIT = 50 # 0.5% MAX_VALIDATOR_EXIT_REQUESTS_PER_REPORT = 600 MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT = 4 -MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT = 100 +MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT = 50 REQUEST_TIMESTAMP_MARGIN = 7680 # 2 hours rounded to epoch length MAX_POSITIVE_TOKEN_REBASE = 750000 diff --git a/tests/regression/test_accounting_oracle_extra_data_full_items.py b/tests/regression/test_accounting_oracle_extra_data_full_items.py index d43e12f7..8f248173 100644 --- a/tests/regression/test_accounting_oracle_extra_data_full_items.py +++ b/tests/regression/test_accounting_oracle_extra_data_full_items.py @@ -47,8 +47,7 @@ def sdvt(interface): @pytest.mark.parametrize( ("nor_stuck_items", "nor_exited_items", "sdvt_stuck_items", "sdvt_exited_items"), [ - # TODO: Doesn't work with full items per 100 operators. Probably, out of gas - # (1, 1, 1, 1), + (1, 1, 1, 1), (1, 1, 1, 0), (1, 1, 0, 1), (1, 1, 0, 0), From 7cf616a1236ad9b798ba71c1c669aebf188af782 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 14 Feb 2024 16:15:58 +0100 Subject: [PATCH 42/68] tmp --- .../test_staking_module_happy_path.py | 777 ++++++++++-------- tests/test_simple_dvt.py | 86 +- 2 files changed, 462 insertions(+), 401 deletions(-) diff --git a/tests/regression/test_staking_module_happy_path.py b/tests/regression/test_staking_module_happy_path.py index fa3e3e44..56c1032b 100644 --- a/tests/regression/test_staking_module_happy_path.py +++ b/tests/regression/test_staking_module_happy_path.py @@ -1,4 +1,5 @@ import pytest +from utils.test.tx_tracing_helpers import display_voting_events from web3 import Web3 import eth_abi from brownie import chain, ZERO_ADDRESS, web3, interface @@ -12,7 +13,12 @@ ) from utils.config import contracts, STAKING_ROUTER, EASYTRACK_EVMSCRIPT_EXECUTOR from utils.test.node_operators_helpers import node_operator_gindex -from utils.test.simple_dvt_helpers import fill_simple_dvt_ops_vetted_keys +from utils.test.simple_dvt_helpers import fill_simple_dvt_ops_keys, fill_simple_dvt_ops_vetted_keys + + +STAKING_ROUTER_ROLE = Web3.keccak(text="STAKING_ROUTER_ROLE") +STAKING_MODULE_MANAGE_ROLE = Web3.keccak(text="STAKING_MODULE_MANAGE_ROLE") +SET_NODE_OPERATOR_LIMIT_ROLE = Web3.keccak(text="SET_NODE_OPERATOR_LIMIT_ROLE") @pytest.fixture(scope="function") @@ -39,38 +45,49 @@ def calc_no_rewards(nor, no_id, shares_minted_as_fees): return nor_shares * operator_total_active_keys // module_total_active_keys -def increase_limit(nor, first_id, second_id, base_id, keys_count, impersonated_voting): - first_no = nor.getNodeOperator(first_id, True) - second_no = nor.getNodeOperator(second_id, True) - base_no = nor.getNodeOperator(base_id, True) +def set_staking_limit(nor, ops_ids, keys_count, impersonated_voting): + for op_index in ops_ids: + no = nor.getNodeOperator(op_index, True) + if not no["active"]: + continue + cur_deposited_keys = no["totalDepositedValidators"] + cur_vetted_keys = no["totalVettedValidators"] + new_vetted_keys = cur_deposited_keys + keys_count + print( + f"Set staking limit for OP: {op_index} (total deposited: {cur_deposited_keys}) from: {cur_vetted_keys} to: {new_vetted_keys}" + ) + nor.setNodeOperatorStakingLimit(op_index, new_vetted_keys, {"from": impersonated_voting}) - current_first_keys = first_no["totalVettedValidators"] - first_no["totalExitedValidators"] - current_second_keys = second_no["totalVettedValidators"] - second_no["totalExitedValidators"] - current_base_keys = base_no["totalVettedValidators"] - base_no["totalExitedValidators"] + # first_no = nor.getNodeOperator(first_id, True) + # second_no = nor.getNodeOperator(second_id, True) + # base_no = nor.getNodeOperator(third_id, True) - nor.setNodeOperatorStakingLimit(first_id, current_first_keys + keys_count, {"from": impersonated_voting}) - nor.setNodeOperatorStakingLimit(second_id, current_second_keys + keys_count, {"from": impersonated_voting}) - nor.setNodeOperatorStakingLimit(base_id, current_base_keys + keys_count, {"from": impersonated_voting}) + # current_first_keys = first_no["totalVettedValidators"] - first_no["totalExitedValidators"] + # current_second_keys = second_no["totalVettedValidators"] - second_no["totalExitedValidators"] + # current_base_keys = base_no["totalVettedValidators"] - base_no["totalExitedValidators"] + # nor.setNodeOperatorStakingLimit(first_id, current_first_keys + keys_count, {"from": impersonated_voting}) + # nor.setNodeOperatorStakingLimit(second_id, current_second_keys + keys_count, {"from": impersonated_voting}) + # nor.setNodeOperatorStakingLimit(third_id, current_base_keys + keys_count, {"from": impersonated_voting}) -def deposit_and_check_keys(staking_module, first_no_id, second_no_id, base_no_id, keys_count, impersonated_voting): - for op_index in (first_no_id, second_no_id, base_no_id): - no = staking_module.getNodeOperator(op_index, True) - if not no["active"]: - continue - staking_module.setNodeOperatorStakingLimit(op_index, no["totalDepositedValidators"] + 10, {"from": impersonated_voting}) - deposited_keys_first_before = staking_module.getNodeOperatorSummary(first_no_id)["totalDepositedValidators"] - deposited_keys_second_before = staking_module.getNodeOperatorSummary(second_no_id)["totalDepositedValidators"] - deposited_keys_base_before = staking_module.getNodeOperatorSummary(base_no_id)["totalDepositedValidators"] +def deposit_and_check_keys(nor, first_id, second_id, third_id, keys_count): + deposited_keys_first_before = nor.getNodeOperatorSummary(first_id)["totalDepositedValidators"] + deposited_keys_second_before = nor.getNodeOperatorSummary(second_id)["totalDepositedValidators"] + deposited_keys_base_before = nor.getNodeOperatorSummary(third_id)["totalDepositedValidators"] validators_before = contracts.lido.getBeaconStat().dict()["depositedValidators"] - module_total_deposited_keys_before = staking_module.getStakingModuleSummary()["totalDepositedValidators"] + module_total_deposited_keys_before = nor.getStakingModuleSummary()["totalDepositedValidators"] - tx = contracts.lido.deposit(keys_count, staking_module.module_id, "0x", {"from": contracts.deposit_security_module.address}) + print(f"Deposit {keys_count} keys for module {nor.module_id}") + print(f"validators_before {validators_before}") + tx = contracts.lido.deposit(keys_count, nor.module_id, "0x", {"from": contracts.deposit_security_module.address}) + display_voting_events(tx) validators_after = contracts.lido.getBeaconStat().dict()["depositedValidators"] - module_total_deposited_keys_after = staking_module.getStakingModuleSummary()["totalDepositedValidators"] + module_total_deposited_keys_after = nor.getStakingModuleSummary()["totalDepositedValidators"] + + print(f"validators_before {validators_after}") just_deposited = validators_after - validators_before print("---------", just_deposited) @@ -79,9 +96,9 @@ def deposit_and_check_keys(staking_module, first_no_id, second_no_id, base_no_id assert tx.events["Unbuffered"]["amount"] == just_deposited * ETH(32) assert module_total_deposited_keys_before + just_deposited == module_total_deposited_keys_after - deposited_keys_first_after = staking_module.getNodeOperatorSummary(first_no_id)["totalDepositedValidators"] - deposited_keys_second_after = staking_module.getNodeOperatorSummary(second_no_id)["totalDepositedValidators"] - deposited_keys_base_after = staking_module.getNodeOperatorSummary(base_no_id)["totalDepositedValidators"] + deposited_keys_first_after = nor.getNodeOperatorSummary(first_id)["totalDepositedValidators"] + deposited_keys_second_after = nor.getNodeOperatorSummary(second_id)["totalDepositedValidators"] + deposited_keys_base_after = nor.getNodeOperatorSummary(third_id)["totalDepositedValidators"] return ( deposited_keys_first_before, @@ -112,7 +129,7 @@ def parse_exited_signing_keys_count_changed_logs(logs): def parse_stuck_penalty_state_changed_logs(logs): res = [] for l in logs: - data = eth_abi.decode(["uint256","uint256","uint256"], bytes.fromhex(l["data"][2:])) + data = eth_abi.decode(["uint256", "uint256", "uint256"], bytes.fromhex(l["data"][2:])) res.append( { "nodeOperatorId": eth_abi.decode_abi(["uint256"], l["topics"][1])[0], @@ -136,11 +153,13 @@ def parse_target_validators_count_changed(logs): return res -def module_happy_path(staking_module, extra_data_service, impersonated_voting, impersonated_executor, eth_whale): +def module_happy_path(staking_module, extra_data_service, impersonated_voting, eth_whale): nor_exited_count, _, _ = contracts.staking_router.getStakingModuleSummary(staking_module.module_id) + # all_modules = contracts.staking_router.getStakingModules() + contracts.staking_router.grantRole( - Web3.keccak(text="STAKING_MODULE_MANAGE_ROLE"), + STAKING_MODULE_MANAGE_ROLE, impersonated_voting, {"from": contracts.agent.address}, ) @@ -148,78 +167,106 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, i contracts.acl.grantPermission( impersonated_voting, staking_module, - Web3.keccak(text="STAKING_ROUTER_ROLE"), + STAKING_ROUTER_ROLE, {"from": impersonated_voting}, ) - contracts.lido.submit(ZERO_ADDRESS, {"from": eth_whale, "amount": ETH(75000)}) + contracts.acl.grantPermission( + impersonated_voting, + staking_module, + SET_NODE_OPERATOR_LIMIT_ROLE, + {"from": impersonated_voting}, + ) - base_no_id, tested_no_id_first, tested_no_id_second = staking_module.testing_node_operator_ids + contracts.lido.submit(ZERO_ADDRESS, {"from": eth_whale, "amount": ETH(75000)}) + # # disable deposit for all operators in all modules + # for module_id, module_address, _, _, _, _, _, _, _, _ in all_modules: + # print("!!!", module_id, module_address) + # module = interface.IStakingModule(module_address) + # contracts.acl.grantPermission( + # impersonated_voting, + # module, + # STAKING_ROUTER_ROLE, + # {"from": impersonated_voting}, + # ) + # no_amount = module.getNodeOperatorsCount() + # for op_index in range(no_amount): + # no = module.getNodeOperator(op_index, True) + # if not no["active"]: + # continue + # module.setNodeOperatorStakingLimit(op_index, no["totalDepositedValidators"], {"from": impersonated_voting}) + + print("Reset staking limit for all OPs...") no_amount = staking_module.getNodeOperatorsCount() - for op_index in range(no_amount): - no = staking_module.getNodeOperator(op_index, True) - if not no["active"]: - continue - staking_module.setNodeOperatorStakingLimit(op_index, no["totalDepositedValidators"], {"from": impersonated_executor}) + set_staking_limit(staking_module, range(no_amount), 0, impersonated_voting) + + no3_id, no1_id, no2_id = staking_module.testing_node_operator_ids - increase_limit(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 3, impersonated_executor) - deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 9, impersonated_executor) + set_staking_limit(staking_module, [no1_id, no2_id, no3_id], 3, impersonated_voting) + deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 9) penalty_delay = staking_module.getStuckPenaltyDelay() - node_operator_first = staking_module.getNodeOperatorSummary(tested_no_id_first) - address_first = staking_module.getNodeOperator(tested_no_id_first, False)["rewardAddress"] - node_operator_first_balance_shares_before = shares_balance(address_first) + no1_summary = staking_module.getNodeOperatorSummary(no1_id) + no1_reward_address = staking_module.getNodeOperator(no1_id, False)["rewardAddress"] + no1_balance_shares_before = shares_balance(no1_reward_address) - node_operator_second = staking_module.getNodeOperatorSummary(tested_no_id_second) - address_second = staking_module.getNodeOperator(tested_no_id_second, False)["rewardAddress"] - node_operator_second_balance_shares_before = shares_balance(address_second) + no2_summary = staking_module.getNodeOperatorSummary(no2_id) + no2_reward_address = staking_module.getNodeOperator(no2_id, False)["rewardAddress"] + no2_balance_shares_before = shares_balance(no2_reward_address) - node_operator_base = staking_module.getNodeOperatorSummary(base_no_id) - address_base_no = staking_module.getNodeOperator(base_no_id, False)["rewardAddress"] - node_operator_base_balance_shares_before = shares_balance(address_base_no) + no3_summary = staking_module.getNodeOperatorSummary(no3_id) + no3_reward_address = staking_module.getNodeOperator(no3_id, False)["rewardAddress"] + no3_balance_shares_before = shares_balance(no3_reward_address) # First report - base empty report (report_tx, extra_report_tx) = oracle_report(exclude_vaults_balances=True) - node_operator_first_balance_shares_after = shares_balance(address_first) - node_operator_second_balance_shares_after = shares_balance(address_second) - node_operator_base_balance_shares_after = shares_balance(address_base_no) + display_voting_events(report_tx) + display_voting_events(extra_report_tx) + + no1_balance_shares_after = shares_balance(no1_reward_address) + no2_balance_shares_after = shares_balance(no2_reward_address) + no3_balance_shares_after = shares_balance(no3_reward_address) # expected shares - node_operator_first_rewards_after_first_report = calc_no_rewards( - staking_module, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no1_rewards_after_first_report = calc_no_rewards( + staking_module, + no_id=no1_id, + shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], ) - node_operator_second_rewards_after_first_report = calc_no_rewards( - staking_module, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no2_rewards_after_first_report = calc_no_rewards( + staking_module, + no_id=no2_id, + shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], ) - node_operator_base_rewards_after_first_report = calc_no_rewards( - staking_module, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no3_rewards_after_first_report = calc_no_rewards( + staking_module, no_id=no3_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) # check shares by empty report assert almostEqWithDiff( - node_operator_first_balance_shares_after - node_operator_first_balance_shares_before, - node_operator_first_rewards_after_first_report, + no1_balance_shares_after - no1_balance_shares_before, + no1_rewards_after_first_report, 1, ) assert almostEqWithDiff( - node_operator_second_balance_shares_after - node_operator_second_balance_shares_before, - node_operator_second_rewards_after_first_report, + no2_balance_shares_after - no2_balance_shares_before, + no2_rewards_after_first_report, 1, ) assert almostEqWithDiff( - node_operator_base_balance_shares_after - node_operator_base_balance_shares_before, - node_operator_base_rewards_after_first_report, + no3_balance_shares_after - no3_balance_shares_before, + no3_rewards_after_first_report, 1, ) # Case 1 - # --- operator "First" had 5 keys (exited), and 2 keys got stuck (stuck) - # --- operator "Second" had 5 keys (exited), and 2 keys got stuck (stuck) + # --- operator "1st" had 5 keys (exited), and 2 keys got stuck (stuck) + # --- operator "2nd" had 5 keys (exited), and 2 keys got stuck (stuck) # - Send report - # - Check rewards shares for base NO and tested NO (should be half of expected) + # - Check rewards shares for "3d" NO and tested NO (should be half of expected) # - Check deposits (should be 0 for penalized NOs) # - Check burned shares # - Check NOs stats @@ -227,21 +274,22 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, i # Prepare extra data vals_stuck_non_zero = { - node_operator_gindex(staking_module.module_id, tested_no_id_first): 2, - node_operator_gindex(staking_module.module_id, tested_no_id_second): 2, + node_operator_gindex(staking_module.module_id, no1_id): 2, + node_operator_gindex(staking_module.module_id, no2_id): 2, } vals_exited_non_zero = { - node_operator_gindex(staking_module.module_id, tested_no_id_first): 5, - node_operator_gindex(staking_module.module_id, tested_no_id_second): 5, + node_operator_gindex(staking_module.module_id, no1_id): 5, + node_operator_gindex(staking_module.module_id, no2_id): 5, } extra_data = extra_data_service.collect(vals_stuck_non_zero, vals_exited_non_zero, 10, 10) # shares before report - node_operator_first_balance_shares_before = shares_balance(address_first) - node_operator_second_balance_shares_before = shares_balance(address_second) - node_operator_base_balance_shares_before = shares_balance(address_base_no) + no1_balance_shares_before = shares_balance(no1_reward_address) + no2_balance_shares_before = shares_balance(no2_reward_address) + no3_balance_shares_before = shares_balance(no3_reward_address) - deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 30, impersonated_executor) + set_staking_limit(staking_module, [no1_id, no2_id, no3_id], 10, impersonated_voting) + deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 30) # Second report - first NO and second NO has stuck/exited (report_tx, extra_report_tx) = oracle_report( @@ -255,125 +303,133 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, i ) # shares after report - node_operator_first = staking_module.getNodeOperatorSummary(tested_no_id_first) - node_operator_second = staking_module.getNodeOperatorSummary(tested_no_id_second) - node_operator_base = staking_module.getNodeOperatorSummary(base_no_id) + no1_summary = staking_module.getNodeOperatorSummary(no1_id) + no2_summary = staking_module.getNodeOperatorSummary(no2_id) + no3_summary = staking_module.getNodeOperatorSummary(no3_id) # expected shares - node_operator_first_rewards_after_second_report = calc_no_rewards( - staking_module, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no1_rewards_after_second_report = calc_no_rewards( + staking_module, + no_id=no1_id, + shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], ) - node_operator_second_rewards_after_second_report = calc_no_rewards( - staking_module, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no2_rewards_after_second_report = calc_no_rewards( + staking_module, + no_id=no2_id, + shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], ) - node_operator_base_rewards_after_second_report = calc_no_rewards( - staking_module, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no3_rewards_after_second_report = calc_no_rewards( + staking_module, no_id=no3_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) - node_operator_first_balance_shares_after = shares_balance(address_first) - node_operator_second_balance_shares_after = shares_balance(address_second) - node_operator_base_balance_shares_after = shares_balance(address_base_no) + no1_balance_shares_after = shares_balance(no1_reward_address) + no2_balance_shares_after = shares_balance(no2_reward_address) + no3_balance_shares_after = shares_balance(no3_reward_address) # check shares by report with penalty assert almostEqWithDiff( - node_operator_first_balance_shares_after - node_operator_first_balance_shares_before, - node_operator_first_rewards_after_second_report // 2, + no1_balance_shares_after - no1_balance_shares_before, + no1_rewards_after_second_report // 2, 1, ) assert almostEqWithDiff( - node_operator_second_balance_shares_after - node_operator_second_balance_shares_before, - node_operator_second_rewards_after_second_report // 2, + no2_balance_shares_after - no2_balance_shares_before, + no2_rewards_after_second_report // 2, 1, ) assert almostEqWithDiff( - node_operator_base_balance_shares_after - node_operator_base_balance_shares_before, - node_operator_base_rewards_after_second_report, + no3_balance_shares_after - no3_balance_shares_before, + no3_rewards_after_second_report, 1, ) # Check burn shares - amount_penalty_first_no = node_operator_first_rewards_after_second_report // 2 - amount_penalty_second_no = node_operator_second_rewards_after_second_report // 2 - penalty_shares = amount_penalty_first_no + amount_penalty_second_no + no1_amount_penalty = no1_rewards_after_second_report // 2 + no2_amount_penalty = no2_rewards_after_second_report // 2 + penalty_shares = no1_amount_penalty + no2_amount_penalty assert extra_report_tx.events["StETHBurnRequested"]["amountOfShares"] >= penalty_shares # NO stats - assert node_operator_first["stuckValidatorsCount"] == 2 - assert node_operator_first["totalExitedValidators"] == 5 - assert node_operator_first["refundedValidatorsCount"] == 0 - assert node_operator_first["stuckPenaltyEndTimestamp"] == 0 + assert no1_summary["stuckValidatorsCount"] == 2 + assert no1_summary["totalExitedValidators"] == 5 + assert no1_summary["refundedValidatorsCount"] == 0 + assert no1_summary["stuckPenaltyEndTimestamp"] == 0 - assert node_operator_second["stuckValidatorsCount"] == 2 - assert node_operator_second["totalExitedValidators"] == 5 - assert node_operator_second["refundedValidatorsCount"] == 0 - assert node_operator_second["stuckPenaltyEndTimestamp"] == 0 + assert no2_summary["stuckValidatorsCount"] == 2 + assert no2_summary["totalExitedValidators"] == 5 + assert no2_summary["refundedValidatorsCount"] == 0 + assert no2_summary["stuckPenaltyEndTimestamp"] == 0 - assert node_operator_base["stuckValidatorsCount"] == 0 - assert node_operator_base["totalExitedValidators"] == 0 - assert node_operator_base["refundedValidatorsCount"] == 0 - assert node_operator_base["stuckPenaltyEndTimestamp"] == 0 + assert no3_summary["stuckValidatorsCount"] == 0 + assert no3_summary["totalExitedValidators"] == 0 + assert no3_summary["refundedValidatorsCount"] == 0 + assert no3_summary["stuckPenaltyEndTimestamp"] == 0 - assert staking_module.isOperatorPenalized(tested_no_id_first) - assert staking_module.isOperatorPenalized(tested_no_id_second) - assert not staking_module.isOperatorPenalized(base_no_id) + assert staking_module.isOperatorPenalized(no1_id) + assert staking_module.isOperatorPenalized(no2_id) + assert not staking_module.isOperatorPenalized(no3_id) # Events exited_signing_keys_count_events = parse_exited_signing_keys_count_changed_logs( filter_transfer_logs(extra_report_tx.logs, web3.keccak(text="ExitedSigningKeysCountChanged(uint256,uint256)")) ) - assert exited_signing_keys_count_events[0]["nodeOperatorId"] == tested_no_id_first + assert exited_signing_keys_count_events[0]["nodeOperatorId"] == no1_id assert exited_signing_keys_count_events[0]["exitedValidatorsCount"] == 5 - assert exited_signing_keys_count_events[1]["nodeOperatorId"] == tested_no_id_second + assert exited_signing_keys_count_events[1]["nodeOperatorId"] == no2_id assert exited_signing_keys_count_events[1]["exitedValidatorsCount"] == 5 stuck_penalty_state_changed_events = parse_stuck_penalty_state_changed_logs( - filter_transfer_logs(extra_report_tx.logs, web3.keccak(text="StuckPenaltyStateChanged(uint256,uint256,uint256,uint256)")) + filter_transfer_logs( + extra_report_tx.logs, web3.keccak(text="StuckPenaltyStateChanged(uint256,uint256,uint256,uint256)") + ) ) - assert stuck_penalty_state_changed_events[0]["nodeOperatorId"] == tested_no_id_first + assert stuck_penalty_state_changed_events[0]["nodeOperatorId"] == no1_id assert stuck_penalty_state_changed_events[0]["stuckValidatorsCount"] == 2 - assert stuck_penalty_state_changed_events[1]["nodeOperatorId"] == tested_no_id_second + assert stuck_penalty_state_changed_events[1]["nodeOperatorId"] == no2_id assert stuck_penalty_state_changed_events[1]["stuckValidatorsCount"] == 2 + set_staking_limit(staking_module, [no1_id, no2_id, no3_id], 10, impersonated_voting) + # Deposit keys ( - deposited_keys_first_before, - deposited_keys_second_before, - deposited_keys_base_before, - deposited_keys_first_after, - deposited_keys_second_after, - deposited_keys_base_after, - ) = deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_executor) + no1_deposited_keys_before, + no2_deposited_keys_before, + no3_deposited_keys_before, + no1_deposited_keys_after, + no2_deposited_keys_after, + no3_deposited_keys_after, + ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50) # check don't change deposited keys for penalized NO - assert deposited_keys_first_before == deposited_keys_first_after - assert deposited_keys_second_before == deposited_keys_second_after - assert deposited_keys_base_before != deposited_keys_base_after + assert no1_deposited_keys_before == no1_deposited_keys_after + assert no2_deposited_keys_before == no2_deposited_keys_after + assert no3_deposited_keys_before != no3_deposited_keys_after # Case 2 - # --- "First" NO exited the keys (stuck == 0, exited increased by the number of stacks) + # --- "1st" NO exited the keys (stuck == 0, exited increased by the number of stacks) # --- BUT the penalty still affects both # - Send report - # - Check rewards shares for base NO and tested NO (should be half of expected) + # - Check rewards shares for NO3 and tested NO (should be half of expected) # - Check burned shares # - Check NOs stats # - Check Report events # Prepare extra data - first node operator has exited 2 + 5 keys an stuck 0 vals_stuck_non_zero = { - node_operator_gindex(staking_module.module_id, tested_no_id_first): 0, + node_operator_gindex(staking_module.module_id, no1_id): 0, } vals_exited_non_zero = { - node_operator_gindex(staking_module.module_id, tested_no_id_first): 7, + node_operator_gindex(staking_module.module_id, no1_id): 7, } extra_data = extra_data_service.collect(vals_stuck_non_zero, vals_exited_non_zero, 10, 10) # shares before report - node_operator_first_balance_shares_before = shares_balance(address_first) - node_operator_second_balance_shares_before = shares_balance(address_second) - node_operator_base_balance_shares_before = shares_balance(address_base_no) + no1_balance_shares_before = shares_balance(no1_reward_address) + no2_balance_shares_before = shares_balance(no2_reward_address) + no3_balance_shares_before = shares_balance(no3_reward_address) # Third report - first NO: increase stuck to 0, desc exited to 7 = 5 + 2 # Second NO: same as prev report @@ -388,93 +444,99 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, i stakingModuleIdsWithNewlyExitedValidators=[staking_module.module_id], ) - node_operator_first = staking_module.getNodeOperatorSummary(tested_no_id_first) - node_operator_second = staking_module.getNodeOperatorSummary(tested_no_id_second) - node_operator_base = staking_module.getNodeOperatorSummary(base_no_id) + no1_summary = staking_module.getNodeOperatorSummary(no1_id) + no2_summary = staking_module.getNodeOperatorSummary(no2_id) + no3_summary = staking_module.getNodeOperatorSummary(no3_id) # shares after report - node_operator_first_balance_shares_after = shares_balance(address_first) - node_operator_second_balance_shares_after = shares_balance(address_second) - node_operator_base_balance_shares_after = shares_balance(address_base_no) + no1_balance_shares_after = shares_balance(no1_reward_address) + no2_balance_shares_after = shares_balance(no2_reward_address) + no3_balance_shares_after = shares_balance(no3_reward_address) # expected shares - node_operator_first_rewards_after_third_report = calc_no_rewards( - staking_module, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no1_rewards_after_third_report = calc_no_rewards( + staking_module, + no_id=no1_id, + shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], ) - node_operator_second_rewards_after__third_report = calc_no_rewards( - staking_module, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no2_rewards_after__third_report = calc_no_rewards( + staking_module, + no_id=no2_id, + shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], ) - node_operator_base_rewards_after__third_report = calc_no_rewards( - staking_module, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no3_rewards_after__third_report = calc_no_rewards( + staking_module, no_id=no3_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) # first NO has penalty has a penalty until stuckPenaltyEndTimestamp # check shares by report with penalty # diff by 1 share because of rounding assert almostEqWithDiff( - node_operator_first_balance_shares_after - node_operator_first_balance_shares_before, - node_operator_first_rewards_after_third_report // 2, + no1_balance_shares_after - no1_balance_shares_before, + no1_rewards_after_third_report // 2, 1, ) assert almostEqWithDiff( - node_operator_second_balance_shares_after - node_operator_second_balance_shares_before, - node_operator_second_rewards_after__third_report // 2, + no2_balance_shares_after - no2_balance_shares_before, + no2_rewards_after__third_report // 2, 1, ) assert almostEqWithDiff( - node_operator_base_balance_shares_after - node_operator_base_balance_shares_before, - node_operator_base_rewards_after__third_report, + no3_balance_shares_after - no3_balance_shares_before, + no3_rewards_after__third_report, 1, ) # Check burn shares - amount_penalty_first_no = node_operator_first_rewards_after_third_report // 2 - amount_penalty_second_no = node_operator_second_rewards_after__third_report // 2 - penalty_shares = amount_penalty_first_no + amount_penalty_second_no + no1_amount_penalty = no1_rewards_after_third_report // 2 + no2_amount_penalty = no2_rewards_after__third_report // 2 + penalty_shares = no1_amount_penalty + no2_amount_penalty # diff by 2 share because of rounding # TODO: Fix below check when nor contains other penalized node operators # assert almostEqWithDiff(extra_report_tx.events["StETHBurnRequested"]["amountOfShares"], penalty_shares, 2) assert extra_report_tx.events["StETHBurnRequested"]["amountOfShares"] >= penalty_shares # NO stats - assert node_operator_base["stuckPenaltyEndTimestamp"] == 0 + assert no3_summary["stuckPenaltyEndTimestamp"] == 0 - assert node_operator_first["stuckValidatorsCount"] == 0 - assert node_operator_first["totalExitedValidators"] == 7 - assert node_operator_first["refundedValidatorsCount"] == 0 + assert no1_summary["stuckValidatorsCount"] == 0 + assert no1_summary["totalExitedValidators"] == 7 + assert no1_summary["refundedValidatorsCount"] == 0 # first NO has penalty has a penalty until stuckPenaltyEndTimestamp - assert node_operator_first["stuckPenaltyEndTimestamp"] > chain.time() + assert no1_summary["stuckPenaltyEndTimestamp"] > chain.time() - assert node_operator_second["stuckValidatorsCount"] == 2 - assert node_operator_second["totalExitedValidators"] == 5 - assert node_operator_second["refundedValidatorsCount"] == 0 - assert node_operator_second["stuckPenaltyEndTimestamp"] == 0 + assert no2_summary["stuckValidatorsCount"] == 2 + assert no2_summary["totalExitedValidators"] == 5 + assert no2_summary["refundedValidatorsCount"] == 0 + assert no2_summary["stuckPenaltyEndTimestamp"] == 0 - assert staking_module.isOperatorPenalized(tested_no_id_first) == True - assert staking_module.isOperatorPenalized(tested_no_id_second) == True - assert staking_module.isOperatorPenalized(base_no_id) == False + assert staking_module.isOperatorPenalized(no1_id) == True + assert staking_module.isOperatorPenalized(no2_id) == True + assert staking_module.isOperatorPenalized(no3_id) == False # events exited_signing_keys_count_events = parse_exited_signing_keys_count_changed_logs( filter_transfer_logs(extra_report_tx.logs, web3.keccak(text="ExitedSigningKeysCountChanged(uint256,uint256)")) ) - assert exited_signing_keys_count_events[0]["nodeOperatorId"] == tested_no_id_first + assert exited_signing_keys_count_events[0]["nodeOperatorId"] == no1_id assert exited_signing_keys_count_events[0]["exitedValidatorsCount"] == 7 stuck_penalty_state_changed_events = parse_stuck_penalty_state_changed_logs( - filter_transfer_logs(extra_report_tx.logs, web3.keccak(text="StuckPenaltyStateChanged(uint256,uint256,uint256,uint256)")) + filter_transfer_logs( + extra_report_tx.logs, web3.keccak(text="StuckPenaltyStateChanged(uint256,uint256,uint256,uint256)") + ) ) - assert stuck_penalty_state_changed_events[0]["nodeOperatorId"] == tested_no_id_first + assert stuck_penalty_state_changed_events[0]["nodeOperatorId"] == no1_id assert stuck_penalty_state_changed_events[0]["stuckValidatorsCount"] == 0 # Case 3 # -- PENALTY_DELAY time passes - # -- A new report comes in and says "Second" NO still has a stuck of keys - # -- "First" NO is fine + # -- A new report comes in and says "2nd" NO still has a stuck of keys + # -- "1st" NO is fine # - Wait PENALTY_DELAY time # - Send report - # - Check rewards shares for base NO and tested NO (should be half for "Second" NO) - # - Check deposits (should be 0 for "Second" NO) + # - Check rewards shares for "3d" NO and tested NO (should be half for "2nd" NO) + # - Check deposits (should be 0 for "2nd" NO) # - Check burned shares # - Check NOs stats @@ -483,21 +545,21 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, i chain.mine() # Clear penalty for first NO after penalty delay - staking_module.clearNodeOperatorPenalty(tested_no_id_first, {"from": impersonated_voting}) + staking_module.clearNodeOperatorPenalty(no1_id, {"from": impersonated_voting}) # Prepare extra data for report by second NO vals_stuck_non_zero = { - node_operator_gindex(staking_module.module_id, tested_no_id_second): 2, + node_operator_gindex(staking_module.module_id, no2_id): 2, } vals_exited_non_zero = { - node_operator_gindex(staking_module.module_id, tested_no_id_second): 5, + node_operator_gindex(staking_module.module_id, no2_id): 5, } extra_data = extra_data_service.collect(vals_stuck_non_zero, vals_exited_non_zero, 10, 10) # shares before report - node_operator_first_balance_shares_before = shares_balance(address_first) - node_operator_second_balance_shares_before = shares_balance(address_second) - node_operator_base_balance_shares_before = shares_balance(address_base_no) + no1_balance_shares_before = shares_balance(no1_reward_address) + no2_balance_shares_before = shares_balance(no2_reward_address) + no3_balance_shares_before = shares_balance(no3_reward_address) # Fourth report - second NO: has stuck 2 keys (report_tx, extra_report_tx) = oracle_report( @@ -510,170 +572,181 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, i stakingModuleIdsWithNewlyExitedValidators=[staking_module.module_id], ) - node_operator_first = staking_module.getNodeOperatorSummary(tested_no_id_first) - node_operator_second = staking_module.getNodeOperatorSummary(tested_no_id_second) - node_operator_base = staking_module.getNodeOperatorSummary(base_no_id) + no1_summary = staking_module.getNodeOperatorSummary(no1_id) + no2_summary = staking_module.getNodeOperatorSummary(no2_id) + no3_summary = staking_module.getNodeOperatorSummary(no3_id) # shares after report - node_operator_first_balance_shares_after = shares_balance(address_first) - node_operator_second_balance_shares_after = shares_balance(address_second) - node_operator_base_balance_shares_after = shares_balance(address_base_no) + no1_balance_shares_after = shares_balance(no1_reward_address) + no2_balance_shares_after = shares_balance(no2_reward_address) + no3_balance_shares_after = shares_balance(no3_reward_address) # expected shares - node_operator_first_rewards_after_fourth_report = calc_no_rewards( - staking_module, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no1_rewards_after_fourth_report = calc_no_rewards( + staking_module, + no_id=no1_id, + shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], ) - node_operator_second_rewards_after__fourth_report = calc_no_rewards( - staking_module, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no2_rewards_after__fourth_report = calc_no_rewards( + staking_module, + no_id=no2_id, + shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], ) - node_operator_base_rewards_after__fourth_report = calc_no_rewards( - staking_module, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no3_rewards_after__fourth_report = calc_no_rewards( + staking_module, no_id=no3_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) # Penalty ended for first operator # check shares by report with penalty for second NO # diff by 1 share because of rounding assert almostEqWithDiff( - node_operator_first_balance_shares_after - node_operator_first_balance_shares_before, - node_operator_first_rewards_after_fourth_report, + no1_balance_shares_after - no1_balance_shares_before, + no1_rewards_after_fourth_report, 1, ) assert almostEqWithDiff( - node_operator_second_balance_shares_after - node_operator_second_balance_shares_before, - node_operator_second_rewards_after__fourth_report // 2, + no2_balance_shares_after - no2_balance_shares_before, + no2_rewards_after__fourth_report // 2, 1, ) assert almostEqWithDiff( - node_operator_base_balance_shares_after - node_operator_base_balance_shares_before, - node_operator_base_rewards_after__fourth_report, + no3_balance_shares_after - no3_balance_shares_before, + no3_rewards_after__fourth_report, 1, ) # Check burn shares - amount_penalty_second_no = node_operator_second_rewards_after__fourth_report // 2 + no2_amount_penalty = no2_rewards_after__fourth_report // 2 # diff by 2 share because of rounding # TODO: Fix below check when nor contains other penalized node operators # assert almostEqWithDiff(extra_report_tx.events["StETHBurnRequested"]["amountOfShares"], amount_penalty_second_no, 1) - assert extra_report_tx.events["StETHBurnRequested"]["amountOfShares"] >= amount_penalty_second_no + assert extra_report_tx.events["StETHBurnRequested"]["amountOfShares"] >= no2_amount_penalty - assert node_operator_base["stuckPenaltyEndTimestamp"] == 0 + assert no3_summary["stuckPenaltyEndTimestamp"] == 0 - assert node_operator_first["stuckValidatorsCount"] == 0 - assert node_operator_first["totalExitedValidators"] == 7 - assert node_operator_first["refundedValidatorsCount"] == 0 + assert no1_summary["stuckValidatorsCount"] == 0 + assert no1_summary["totalExitedValidators"] == 7 + assert no1_summary["refundedValidatorsCount"] == 0 # Penalty ended for first operator - assert node_operator_first["stuckPenaltyEndTimestamp"] < chain.time() + assert no1_summary["stuckPenaltyEndTimestamp"] < chain.time() - assert node_operator_second["stuckValidatorsCount"] == 2 - assert node_operator_second["totalExitedValidators"] == 5 - assert node_operator_second["refundedValidatorsCount"] == 0 - assert node_operator_second["stuckPenaltyEndTimestamp"] == 0 + assert no2_summary["stuckValidatorsCount"] == 2 + assert no2_summary["totalExitedValidators"] == 5 + assert no2_summary["refundedValidatorsCount"] == 0 + assert no2_summary["stuckPenaltyEndTimestamp"] == 0 - assert not staking_module.isOperatorPenalized(tested_no_id_first) - assert staking_module.isOperatorPenalized(tested_no_id_second) - assert not staking_module.isOperatorPenalized(base_no_id) + assert not staking_module.isOperatorPenalized(no1_id) + assert staking_module.isOperatorPenalized(no2_id) + assert not staking_module.isOperatorPenalized(no3_id) # Deposit + set_staking_limit(staking_module, [no1_id, no2_id, no3_id], 10, impersonated_voting) ( - deposited_keys_first_before, - deposited_keys_second_before, - deposited_keys_base_before, - deposited_keys_first_after, - deposited_keys_second_after, - deposited_keys_base_after, - ) = deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) + no1_deposited_keys_before, + no2_deposited_keys_before, + no3_deposited_keys_before, + no1_deposited_keys_after, + no2_deposited_keys_after, + no3_deposited_keys_after, + ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50) # check don't change deposited keys for penalized NO (only second NO) - assert deposited_keys_first_before != deposited_keys_first_after - assert deposited_keys_second_before == deposited_keys_second_after - assert deposited_keys_base_before != deposited_keys_base_after + assert no1_deposited_keys_before != no1_deposited_keys_after + assert no2_deposited_keys_before == no2_deposited_keys_after + assert no3_deposited_keys_before != no3_deposited_keys_after # Case 4 # -- Do key refend (redunded == stuck) for X2 # -- A new report arrives and says that everything remains the same # _ Refund 2 keys Second NO # - Send report - # - Check rewards shares for base NO and tested NO (should be half for "Second" NO) + # - Check rewards shares for "3d" NO and tested NO (should be half for "2nd" NO) # - Check burned shares # - Check NOs stats # # Refund 2 keys Second NO - contracts.staking_router.updateRefundedValidatorsCount(staking_module.module_id, tested_no_id_second, 2, {"from": impersonated_voting}) + contracts.staking_router.updateRefundedValidatorsCount( + staking_module.module_id, no2_id, 2, {"from": impersonated_voting} + ) # shares before report - node_operator_first_balance_shares_before = shares_balance(address_first) - node_operator_second_balance_shares_before = shares_balance(address_second) - node_operator_base_balance_shares_before = shares_balance(address_base_no) + no1_balance_shares_before = shares_balance(no1_reward_address) + no2_balance_shares_before = shares_balance(no2_reward_address) + no3_balance_shares_before = shares_balance(no3_reward_address) # Fifth report (report_tx, extra_report_tx) = oracle_report(exclude_vaults_balances=True) # shares after report - node_operator_first_balance_shares_after = shares_balance(address_first) - node_operator_second_balance_shares_after = shares_balance(address_second) - node_operator_base_balance_shares_after = shares_balance(address_base_no) + no1_balance_shares_after = shares_balance(no1_reward_address) + no2_balance_shares_after = shares_balance(no2_reward_address) + no3_balance_shares_after = shares_balance(no3_reward_address) - node_operator_first = staking_module.getNodeOperatorSummary(tested_no_id_first) - node_operator_second = staking_module.getNodeOperatorSummary(tested_no_id_second) - node_operator_base = staking_module.getNodeOperatorSummary(base_no_id) + no1_summary = staking_module.getNodeOperatorSummary(no1_id) + no2_summary = staking_module.getNodeOperatorSummary(no2_id) + no3_summary = staking_module.getNodeOperatorSummary(no3_id) # expected shares - node_operator_first_rewards_after_fifth_report = calc_no_rewards( - staking_module, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no1_rewards_after_fifth_report = calc_no_rewards( + staking_module, + no_id=no1_id, + shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], ) - node_operator_second_rewards_after_fifth_report = calc_no_rewards( - staking_module, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no2_rewards_after_fifth_report = calc_no_rewards( + staking_module, + no_id=no2_id, + shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], ) - node_operator_base_rewards_after_fifth_report = calc_no_rewards( - staking_module, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no3_rewards_after_fifth_report = calc_no_rewards( + staking_module, no_id=no3_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) # Penalty only for second operator # diff by 1 share because of rounding assert almostEqWithDiff( - node_operator_first_balance_shares_after - node_operator_first_balance_shares_before, - node_operator_first_rewards_after_fifth_report, + no1_balance_shares_after - no1_balance_shares_before, + no1_rewards_after_fifth_report, 1, ) assert almostEqWithDiff( - node_operator_second_balance_shares_after - node_operator_second_balance_shares_before, - node_operator_second_rewards_after_fifth_report // 2, + no2_balance_shares_after - no2_balance_shares_before, + no2_rewards_after_fifth_report // 2, 1, ) assert almostEqWithDiff( - node_operator_base_balance_shares_after - node_operator_base_balance_shares_before, - node_operator_base_rewards_after_fifth_report, + no3_balance_shares_after - no3_balance_shares_before, + no3_rewards_after_fifth_report, 1, ) # Check burn shares - amount_penalty_second_no = node_operator_second_rewards_after_fifth_report // 2 + no2_amount_penalty = no2_rewards_after_fifth_report // 2 # diff by 2 share because of rounding # TODO: Fix below check when nor contains other penalized node operators # assert almostEqWithDiff(extra_report_tx.events["StETHBurnRequested"]["amountOfShares"], amount_penalty_second_no, 1) - assert extra_report_tx.events["StETHBurnRequested"]["amountOfShares"] >= amount_penalty_second_no + assert extra_report_tx.events["StETHBurnRequested"]["amountOfShares"] >= no2_amount_penalty - assert node_operator_base["stuckPenaltyEndTimestamp"] == 0 + assert no3_summary["stuckPenaltyEndTimestamp"] == 0 - assert node_operator_first["stuckValidatorsCount"] == 0 - assert node_operator_first["totalExitedValidators"] == 7 - assert node_operator_first["refundedValidatorsCount"] == 0 - assert node_operator_first["stuckPenaltyEndTimestamp"] < chain.time() + assert no1_summary["stuckValidatorsCount"] == 0 + assert no1_summary["totalExitedValidators"] == 7 + assert no1_summary["refundedValidatorsCount"] == 0 + assert no1_summary["stuckPenaltyEndTimestamp"] < chain.time() - assert node_operator_second["stuckValidatorsCount"] == 2 - assert node_operator_second["totalExitedValidators"] == 5 - assert node_operator_second["refundedValidatorsCount"] == 2 - assert node_operator_second["stuckPenaltyEndTimestamp"] > chain.time() + assert no2_summary["stuckValidatorsCount"] == 2 + assert no2_summary["totalExitedValidators"] == 5 + assert no2_summary["refundedValidatorsCount"] == 2 + assert no2_summary["stuckPenaltyEndTimestamp"] > chain.time() - assert staking_module.isOperatorPenaltyCleared(tested_no_id_first) == True - assert staking_module.isOperatorPenaltyCleared(tested_no_id_second) == False + assert staking_module.isOperatorPenaltyCleared(no1_id) == True + assert staking_module.isOperatorPenaltyCleared(no2_id) == False # Case 5 # -- PENALTY_DELAY time passes # -- A new report arrives # - Wait for penalty delay time # - Send report - # - Check rewards shares for base NO and tested NO (should be full for all NOs) + # - Check rewards shares for "3d" NO and tested NO (should be full for all NOs) # - Check deposits (should be full for all NOs) # - Check NOs stats @@ -682,177 +755,187 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, i chain.mine() # Clear penalty for second NO after penalty delay - staking_module.clearNodeOperatorPenalty(tested_no_id_second, {"from": impersonated_voting}) + staking_module.clearNodeOperatorPenalty(no2_id, {"from": impersonated_voting}) # shares before report - node_operator_first_balance_shares_before = shares_balance(address_first) - node_operator_second_balance_shares_before = shares_balance(address_second) - node_operator_base_balance_shares_before = shares_balance(address_base_no) + no1_balance_shares_before = shares_balance(no1_reward_address) + no2_balance_shares_before = shares_balance(no2_reward_address) + no3_balance_shares_before = shares_balance(no3_reward_address) # Seventh report (report_tx, extra_report_tx) = oracle_report() - assert not staking_module.isOperatorPenalized(tested_no_id_first) - assert not staking_module.isOperatorPenalized(tested_no_id_second) - assert not staking_module.isOperatorPenalized(base_no_id) + assert not staking_module.isOperatorPenalized(no1_id) + assert not staking_module.isOperatorPenalized(no2_id) + assert not staking_module.isOperatorPenalized(no3_id) # shares after report - node_operator_first_balance_shares_after = shares_balance(address_first) - node_operator_second_balance_shares_after = shares_balance(address_second) - node_operator_base_balance_shares_after = shares_balance(address_base_no) + no1_balance_shares_after = shares_balance(no1_reward_address) + no2_balance_shares_after = shares_balance(no2_reward_address) + no3_balance_shares_after = shares_balance(no3_reward_address) - assert staking_module.isOperatorPenaltyCleared(tested_no_id_first) - assert staking_module.isOperatorPenaltyCleared(tested_no_id_second) + assert staking_module.isOperatorPenaltyCleared(no1_id) + assert staking_module.isOperatorPenaltyCleared(no2_id) - node_operator_first = staking_module.getNodeOperatorSummary(tested_no_id_first) - node_operator_second = staking_module.getNodeOperatorSummary(tested_no_id_second) + no1_summary = staking_module.getNodeOperatorSummary(no1_id) + no2_summary = staking_module.getNodeOperatorSummary(no2_id) # expected shares - node_operator_first_rewards_after_seventh_report = calc_no_rewards( - staking_module, no_id=tested_no_id_first, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no1_rewards_after_seventh_report = calc_no_rewards( + staking_module, + no_id=no1_id, + shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], ) - node_operator_second_rewards_after_seventh_report = calc_no_rewards( - staking_module, no_id=tested_no_id_second, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no2_rewards_after_seventh_report = calc_no_rewards( + staking_module, + no_id=no2_id, + shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], ) - node_operator_base_rewards_after_seventh_report = calc_no_rewards( - staking_module, no_id=base_no_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + no3_rewards_after_seventh_report = calc_no_rewards( + staking_module, no_id=no3_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) # No penalty # diff by 1 share because of rounding assert almostEqWithDiff( - node_operator_first_balance_shares_after - node_operator_first_balance_shares_before, - node_operator_first_rewards_after_seventh_report, + no1_balance_shares_after - no1_balance_shares_before, + no1_rewards_after_seventh_report, 1, ) assert almostEqWithDiff( - node_operator_second_balance_shares_after - node_operator_second_balance_shares_before, - node_operator_second_rewards_after_seventh_report, + no2_balance_shares_after - no2_balance_shares_before, + no2_rewards_after_seventh_report, 1, ) assert almostEqWithDiff( - node_operator_base_balance_shares_after - node_operator_base_balance_shares_before, - node_operator_base_rewards_after_seventh_report, + no3_balance_shares_after - no3_balance_shares_before, + no3_rewards_after_seventh_report, 1, ) - assert node_operator_first["stuckValidatorsCount"] == 0 - assert node_operator_first["totalExitedValidators"] == 7 - assert node_operator_first["refundedValidatorsCount"] == 0 - assert node_operator_first["stuckPenaltyEndTimestamp"] < chain.time() + assert no1_summary["stuckValidatorsCount"] == 0 + assert no1_summary["totalExitedValidators"] == 7 + assert no1_summary["refundedValidatorsCount"] == 0 + assert no1_summary["stuckPenaltyEndTimestamp"] < chain.time() - assert node_operator_second["stuckValidatorsCount"] == 2 - assert node_operator_second["totalExitedValidators"] == 5 - assert node_operator_second["refundedValidatorsCount"] == 2 - assert node_operator_second["stuckPenaltyEndTimestamp"] < chain.time() + assert no2_summary["stuckValidatorsCount"] == 2 + assert no2_summary["totalExitedValidators"] == 5 + assert no2_summary["refundedValidatorsCount"] == 2 + assert no2_summary["stuckPenaltyEndTimestamp"] < chain.time() # Deposit + set_staking_limit(staking_module, [no1_id, no2_id, no3_id], 10, impersonated_voting) ( - deposited_keys_first_before, - deposited_keys_second_before, - deposited_keys_base_before, - deposited_keys_first_after, - deposited_keys_second_after, - deposited_keys_base_after, - ) = deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) + no1_deposited_keys_before, + no2_deposited_keys_before, + no3_deposited_keys_before, + no1_deposited_keys_after, + no2_deposited_keys_after, + no3_deposited_keys_after, + ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50) # check deposit is applied for all NOs - assert deposited_keys_first_before != deposited_keys_first_after - assert deposited_keys_second_before != deposited_keys_second_after - assert deposited_keys_base_before != deposited_keys_base_after + assert no1_deposited_keys_before != no1_deposited_keys_after + assert no2_deposited_keys_before != no2_deposited_keys_after + assert no3_deposited_keys_before != no3_deposited_keys_after - for op_index in (tested_no_id_first, tested_no_id_second, base_no_id): + for op_index in (no1_id, no2_id, no3_id): no = staking_module.getNodeOperator(op_index, True) - staking_module.setNodeOperatorStakingLimit(op_index, no["totalDepositedValidators"] + 10, {"from": impersonated_voting}) + staking_module.setNodeOperatorStakingLimit( + op_index, no["totalDepositedValidators"] + 10, {"from": impersonated_voting} + ) # Case 6 - # -- SActivate target limit for "First" NO + # -- SActivate target limit for "1st" NO # -- Check deposits - # -- Disable target limit for "First" NO - # - Set target limit for "First" NO with 0 validators + # -- Disable target limit for "1st" NO + # - Set target limit for "1st" NO with 0 validators # - Check events # - Check NO stats - # - Check deposits (should be 0 for "First" NO) - # - Disable target limit for "First" NO + # - Check deposits (should be 0 for "1st" NO) + # - Disable target limit for "1st" NO # - Check events # - Check NO stats - # - Check deposits (should be not 0 for "First" NO) + # - Check deposits (should be not 0 for "1st" NO) # Activate target limit - first_no_summary_before = staking_module.getNodeOperatorSummary(tested_no_id_first) + first_no_summary_before = staking_module.getNodeOperatorSummary(no1_id) assert first_no_summary_before["depositableValidatorsCount"] > 0 - target_limit_tx = staking_module.updateTargetValidatorsLimits(tested_no_id_first, True, 0, {"from": STAKING_ROUTER}) + target_limit_tx = staking_module.updateTargetValidatorsLimits(no1_id, True, 0, {"from": STAKING_ROUTER}) target_validators_count_changed_events = parse_target_validators_count_changed( filter_transfer_logs(target_limit_tx.logs, web3.keccak(text="TargetValidatorsCountChanged(uint256,uint256)")) ) - assert target_validators_count_changed_events[0]["nodeOperatorId"] == tested_no_id_first + assert target_validators_count_changed_events[0]["nodeOperatorId"] == no1_id assert target_validators_count_changed_events[0]["targetValidatorsCount"] == 0 - first_no_summary_after = staking_module.getNodeOperatorSummary(tested_no_id_first) + first_no_summary_after = staking_module.getNodeOperatorSummary(no1_id) assert first_no_summary_after["depositableValidatorsCount"] == 0 assert first_no_summary_after["isTargetLimitActive"] # Deposit + set_staking_limit(staking_module, [no1_id, no2_id, no3_id], 10, impersonated_voting) ( - deposited_keys_first_before, - deposited_keys_second_before, - deposited_keys_base_before, - deposited_keys_first_after, - deposited_keys_second_after, - deposited_keys_base_after, - ) = deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) + no1_deposited_keys_before, + no2_deposited_keys_before, + no3_deposited_keys_before, + no1_deposited_keys_after, + no2_deposited_keys_after, + no3_deposited_keys_after, + ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50) # check deposit is not applied for first NO - assert deposited_keys_first_before == deposited_keys_first_after - assert deposited_keys_second_before != deposited_keys_second_after - assert deposited_keys_base_before != deposited_keys_base_after + assert no1_deposited_keys_before == no1_deposited_keys_after + assert no2_deposited_keys_before != no2_deposited_keys_after + assert no3_deposited_keys_before != no3_deposited_keys_after # Disable target limit - target_limit_tx = staking_module.updateTargetValidatorsLimits(tested_no_id_first, False, 0, {"from": STAKING_ROUTER}) + target_limit_tx = staking_module.updateTargetValidatorsLimits(no1_id, False, 0, {"from": STAKING_ROUTER}) target_validators_count_changed_events = parse_target_validators_count_changed( filter_transfer_logs(target_limit_tx.logs, web3.keccak(text="TargetValidatorsCountChanged(uint256,uint256)")) ) - assert target_validators_count_changed_events[0]["nodeOperatorId"] == tested_no_id_first + assert target_validators_count_changed_events[0]["nodeOperatorId"] == no1_id - first_no_summary_after = staking_module.getNodeOperatorSummary(tested_no_id_first) + first_no_summary_after = staking_module.getNodeOperatorSummary(no1_id) assert first_no_summary_after["depositableValidatorsCount"] > 0 assert not first_no_summary_after["isTargetLimitActive"] # Deposit + set_staking_limit(staking_module, [no1_id, no2_id, no3_id], 10, impersonated_voting) ( - deposited_keys_first_before, - deposited_keys_second_before, - deposited_keys_base_before, - deposited_keys_first_after, - deposited_keys_second_after, - deposited_keys_base_after, - ) = deposit_and_check_keys(staking_module, tested_no_id_first, tested_no_id_second, base_no_id, 50, impersonated_voting) + no1_deposited_keys_before, + no2_deposited_keys_before, + no3_deposited_keys_before, + no1_deposited_keys_after, + no2_deposited_keys_after, + no3_deposited_keys_after, + ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50) # check - deposit not applied to NOs. - assert deposited_keys_first_before != deposited_keys_first_after - assert deposited_keys_second_before != deposited_keys_second_after - assert deposited_keys_base_before != deposited_keys_base_after - + assert no1_deposited_keys_before != no1_deposited_keys_after + assert no2_deposited_keys_before != no2_deposited_keys_after + assert no3_deposited_keys_before != no3_deposited_keys_after -def test_node_operator_registry(impersonated_voting, impersonate_es_executor, eth_whale): - deposit_and_check_keys(contracts.simple_dvt, 0, 1, 2, 9, impersonate_es_executor) - nor = contracts.node_operators_registry - nor.module_id = 1 - nor.testing_node_operator_ids = [23, 20, 28] - module_happy_path(nor, ExtraDataService(), impersonated_voting, impersonated_voting, eth_whale) +# def test_node_operator_registry(impersonated_voting, eth_whale): +# nor = contracts.node_operators_registry +# nor.module_id = 1 +# nor.testing_node_operator_ids = [23, 20, 28] +# module_happy_path(nor, ExtraDataService(), impersonated_voting, eth_whale) -def test_sdvt(impersonated_voting, impersonate_es_executor, stranger, eth_whale): +def test_sdvt(impersonated_voting, stranger, eth_whale): sdvt = contracts.simple_dvt sdvt.module_id = 2 sdvt.testing_node_operator_ids = [0, 1, 2] fill_simple_dvt_ops_vetted_keys(stranger, 3, 100) - fill_simple_dvt_ops_vetted_keys(stranger, 3, 200) - fill_simple_dvt_ops_vetted_keys(stranger, 3, 300) - module_happy_path(sdvt, ExtraDataService(), impersonated_voting, impersonate_es_executor, eth_whale) + + # simulate already deposited keys + deposit_and_check_keys(sdvt, 0, 1, 2, 30) + oracle_report(exclude_vaults_balances=True, cl_diff=ETH(3)) + + module_happy_path(sdvt, ExtraDataService(), impersonated_voting, eth_whale) diff --git a/tests/test_simple_dvt.py b/tests/test_simple_dvt.py index b467194f..f2b63d2d 100644 --- a/tests/test_simple_dvt.py +++ b/tests/test_simple_dvt.py @@ -63,7 +63,6 @@ SET_NODE_OPERATOR_LIMIT_ROLE = "0x07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754" MANAGE_SIGNING_KEYS = "0x75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee" MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE = "0x0cf253eb71298c92e2814969a122f66b781f9b217f8ecde5401e702beb9345f6" -MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE = "0xf6ac39904c42f8e23056f1b678e4892fc92caa68ae836dc474e137f0e67f5716" simple_dvt_repo_ens = "simple-dvt.lidopm.eth" simple_dvt_content_uri = ( @@ -114,7 +113,6 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco sanity_checker_limits = contracts.oracle_report_sanity_checker.getOracleReportLimits() assert sanity_checker_limits["maxAccountingExtraDataListItemsCount"] == 2 - assert sanity_checker_limits["maxNodeOperatorsPerExtraDataItemCount"] == 100 # START VOTE if len(vote_ids_from_env) > 0: @@ -264,10 +262,9 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco sanity_checker_limits = contracts.oracle_report_sanity_checker.getOracleReportLimits() assert sanity_checker_limits["maxAccountingExtraDataListItemsCount"] == 4 - assert sanity_checker_limits["maxNodeOperatorsPerExtraDataItemCount"] == 50 # validate vote events - assert count_vote_items_by_events(vote_tx, contracts.voting) == 22, "Incorrect voting items count" + assert count_vote_items_by_events(vote_tx, contracts.voting) == 20, "Incorrect voting items count" metadata = find_metadata_by_vote_id(vote_id) print("metadata", metadata) @@ -282,7 +279,6 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco evs = group_voting_events(vote_tx) - # I. Create new Aragon DAO Application Repo for SimpleDVT repo_params = NewRepoItem( name=SIMPLE_DVT_ARAGON_APP_NAME, app=simple_dvt.address, @@ -294,11 +290,10 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ) validate_new_repo_with_version_event(evs[0], repo_params) - # II. Setup and initialize SimpleDVT module as new Aragon app validate_app_update_event(evs[1], SIMPLE_DVT_ARAGON_APP_ID, SIMPLE_DVT_IMPL) + validate_simple_dvt_intialize_event(evs[2]) - # III. Add SimpleDVT module to Staking Router # Create and grant permission STAKING_ROUTER_ROLE on SimpleDVT module for StakingRouter permission = Permission( entity=staking_router, @@ -307,48 +302,36 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ) validate_permission_create_event(evs[3], permission, manager=voting) - # Grant REQUEST_BURN_SHARES_ROLE on Burner for SimpleDVT module - validate_grant_role_event(evs[4], REQUEST_BURN_SHARES_ROLE, simple_dvt, agent) - - # Add SimpleDVT module to StakingRouter - module_item = StakingModuleItem( - SIMPLE_DVT_MODULE_ID, - simple_dvt.address, - SIMPLE_DVT_MODULE_NAME, - SIMPLE_DVT_MODULE_TARGET_SHARE_BP, - SIMPLE_DVT_MODULE_MODULE_FEE_BP, - SIMPLE_DVT_MODULE_TREASURY_FEE_BP, - ) - validate_staking_module_added_event(evs[5], module_item) + # Grant STAKING_ROUTER_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor + permission = Permission(entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=STAKING_ROUTER_ROLE) + validate_permission_grant_event(evs[4], permission) - # IV. Grant permissions to make operational changes to SimpleDVT module + # Create and grant permission MANAGE_NODE_OPERATOR_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor permission = Permission( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=MANAGE_NODE_OPERATOR_ROLE, # simple_dvt.MANAGE_NODE_OPERATOR_ROLE(), ) - validate_permission_create_event(evs[6], permission, manager=voting) + validate_permission_create_event(evs[5], permission, manager=voting) + # Create and grant permission SET_NODE_OPERATOR_LIMIT_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor permission = Permission( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=SET_NODE_OPERATOR_LIMIT_ROLE, # simple_dvt.SET_NODE_OPERATOR_LIMIT_ROLE(), ) - validate_permission_create_event(evs[7], permission, manager=voting) + validate_permission_create_event(evs[6], permission, manager=voting) + # Create and grant permission MANAGE_SIGNING_KEYS on SimpleDVT module for EasyTrackEVMScriptExecutor permission = Permission( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=MANAGE_SIGNING_KEYS, # simple_dvt.MANAGE_SIGNING_KEYS(), ) - validate_permission_create_event(evs[8], permission, manager=EASYTRACK_EVMSCRIPT_EXECUTOR) - - permission = Permission(entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=STAKING_ROUTER_ROLE) - validate_permission_grant_event(evs[9], permission) + validate_permission_create_event(evs[7], permission, manager=EASYTRACK_EVMSCRIPT_EXECUTOR) - # IV. Add EasyTrack EVM script factories for SimpleDVT module validate_evmscript_factory_added_event( - evs[10], + evs[8], EVMScriptFactoryAdded( factory_addr=add_node_operators_evm_script_factory, permissions=create_permissions(simple_dvt, "addNodeOperator") @@ -356,7 +339,7 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ), ) validate_evmscript_factory_added_event( - evs[11], + evs[9], EVMScriptFactoryAdded( factory_addr=activate_node_operators_evm_script_factory, permissions=create_permissions(simple_dvt, "activateNodeOperator") @@ -364,7 +347,7 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ), ) validate_evmscript_factory_added_event( - evs[12], + evs[10], EVMScriptFactoryAdded( factory_addr=deactivate_node_operators_evm_script_factory, permissions=create_permissions(simple_dvt, "deactivateNodeOperator") @@ -372,35 +355,35 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ), ) validate_evmscript_factory_added_event( - evs[13], + evs[11], EVMScriptFactoryAdded( factory_addr=set_vetted_validators_limits_evm_script_factory, permissions=create_permissions(simple_dvt, "setNodeOperatorStakingLimit"), ), ) validate_evmscript_factory_added_event( - evs[14], + evs[12], EVMScriptFactoryAdded( factory_addr=update_target_validator_limits_evm_script_factory, permissions=create_permissions(simple_dvt, "updateTargetValidatorsLimits"), ), ) validate_evmscript_factory_added_event( - evs[15], + evs[13], EVMScriptFactoryAdded( factory_addr=set_node_operator_names_evm_script_factory, permissions=create_permissions(simple_dvt, "setNodeOperatorName"), ), ) validate_evmscript_factory_added_event( - evs[16], + evs[14], EVMScriptFactoryAdded( factory_addr=set_node_operator_reward_addresses_evm_script_factory, permissions=create_permissions(simple_dvt, "setNodeOperatorRewardAddress"), ), ) validate_evmscript_factory_added_event( - evs[17], + evs[15], EVMScriptFactoryAdded( factory_addr=change_node_operator_managers_evm_script_factory, permissions=create_permissions(contracts.acl, "revokePermission") @@ -408,11 +391,20 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ), ) - # VI. Update Oracle Report Sanity Checker parameters + validate_grant_role_event(evs[16], REQUEST_BURN_SHARES_ROLE, simple_dvt, agent) + + module_item = StakingModuleItem( + SIMPLE_DVT_MODULE_ID, + simple_dvt.address, + SIMPLE_DVT_MODULE_NAME, + SIMPLE_DVT_MODULE_TARGET_SHARE_BP, + SIMPLE_DVT_MODULE_MODULE_FEE_BP, + SIMPLE_DVT_MODULE_TREASURY_FEE_BP, + ) + validate_staking_module_added_event(evs[17], module_item) + validate_grant_role_event(evs[18], MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE, agent, agent) - validate_grant_role_event(evs[19], MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE, agent, agent) - validate_max_extra_data_list_items_count_event(evs[20], 4) - validate_max_operators_per_extra_data_item_count_event(evs[21], 50) + validate_max_extra_data_list_items_count_event(evs[19], 4) def has_permission(permission: Permission, how: List[int]) -> bool: @@ -435,20 +427,6 @@ def validate_max_extra_data_list_items_count_event(event: EventDict, value: int) assert event["MaxAccountingExtraDataListItemsCountSet"]["maxAccountingExtraDataListItemsCount"] == value -def validate_max_operators_per_extra_data_item_count_event(event: EventDict, value: int): - _events_chain = [ - "LogScriptCall", - "LogScriptCall", - "MaxNodeOperatorsPerExtraDataItemCountSet", - "ScriptResult", - ] - - validate_events_chain([e.name for e in event], _events_chain) - - assert event.count("MaxNodeOperatorsPerExtraDataItemCountSet") == 1 - assert event["MaxNodeOperatorsPerExtraDataItemCountSet"]["maxNodeOperatorsPerExtraDataItemCount"] == value - - def validate_simple_dvt_intialize_event(event: EventDict): _events_chain = [ "LogScriptCall", From 419a2607c83a15eb01a1403ad358a1b283e987bc Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 14 Feb 2024 18:18:14 +0100 Subject: [PATCH 43/68] fix tests --- tests/regression/test_node_operators_flow.py | 17 +- .../test_staking_module_happy_path.py | 215 ++++++------------ utils/test/simple_dvt_helpers.py | 4 +- 3 files changed, 74 insertions(+), 162 deletions(-) diff --git a/tests/regression/test_node_operators_flow.py b/tests/regression/test_node_operators_flow.py index 55c70d10..93bbe503 100644 --- a/tests/regression/test_node_operators_flow.py +++ b/tests/regression/test_node_operators_flow.py @@ -103,7 +103,6 @@ def test_add_node_operator(nor, voting_eoa, reward_address, new_node_operator_id ) assert_node_operator_added_event(tx, new_node_operator_id, new_node_operator_name, reward_address, staking_limit=0) - keys_count = 13 pubkeys_batch = random_pubkeys_batch(keys_count) signatures_batch = random_signatures_batch(keys_count) @@ -149,7 +148,6 @@ def test_add_node_operator(nor, voting_eoa, reward_address, new_node_operator_id # TODO: validate events - nonce_before = nor.getNonce() node_operator_before = nor.getNodeOperator(new_node_operator_id, True) node_operator_summary_before = nor.getNodeOperatorSummary(new_node_operator_id) @@ -173,7 +171,6 @@ def test_add_node_operator(nor, voting_eoa, reward_address, new_node_operator_id # TODO: validate events - node_operators_count_before = nor.getNodeOperatorsCount() active_node_operators_count_before = nor.getActiveNodeOperatorsCount() @@ -203,7 +200,6 @@ def test_add_node_operator(nor, voting_eoa, reward_address, new_node_operator_id # TODO: validate events - node_operator_before = nor.getNodeOperator(new_node_operator_id, True) assert node_operator_before["active"] == False @@ -228,7 +224,6 @@ def test_add_node_operator(nor, voting_eoa, reward_address, new_node_operator_id # TODO: validate events - nonce_before = nor.getNonce() node_operator_before = nor.getNodeOperator(new_node_operator_id, True) node_operator_summary_before = nor.getNodeOperatorSummary(new_node_operator_id) @@ -253,12 +248,12 @@ def test_add_node_operator(nor, voting_eoa, reward_address, new_node_operator_id # TODO: validate events -def random_pubkeys_batch(pubkeys_count: int): - return random_hexstr(pubkeys_count * PUBKEY_LENGTH) +def random_pubkeys_batch(pubkeys_count: int, seed=None): + return random_hexstr(pubkeys_count * PUBKEY_LENGTH, seed) -def random_signatures_batch(signautes_count: int): - return random_hexstr(signautes_count * SIGNATURE_LENGTH) +def random_signatures_batch(signautes_count: int, seed=None): + return random_hexstr(signautes_count * SIGNATURE_LENGTH, seed) def parse_pubkeys_batch(pubkeys_batch: str): @@ -275,7 +270,9 @@ def hex_chunks(hexstr: str, chunk_length: int): return [prefix_0x(chunk) for chunk in textwrap.wrap(stripped_hexstr, 2 * chunk_length)] -def random_hexstr(length: int): +def random_hexstr(length: int, seed=None): + if seed: + random.seed(seed) return prefix_0x(random.randbytes(length).hex()) diff --git a/tests/regression/test_staking_module_happy_path.py b/tests/regression/test_staking_module_happy_path.py index 56c1032b..5c0fece4 100644 --- a/tests/regression/test_staking_module_happy_path.py +++ b/tests/regression/test_staking_module_happy_path.py @@ -1,5 +1,4 @@ import pytest -from utils.test.tx_tracing_helpers import display_voting_events from web3 import Web3 import eth_abi from brownie import chain, ZERO_ADDRESS, web3, interface @@ -13,7 +12,7 @@ ) from utils.config import contracts, STAKING_ROUTER, EASYTRACK_EVMSCRIPT_EXECUTOR from utils.test.node_operators_helpers import node_operator_gindex -from utils.test.simple_dvt_helpers import fill_simple_dvt_ops_keys, fill_simple_dvt_ops_vetted_keys +from utils.test.simple_dvt_helpers import fill_simple_dvt_ops_keys STAKING_ROUTER_ROLE = Web3.keccak(text="STAKING_ROUTER_ROLE") @@ -31,7 +30,13 @@ def impersonated_voting(accounts): return accounts.at(contracts.voting.address, force=True) -def calc_no_rewards(nor, no_id, shares_minted_as_fees): +def calc_module_reward_shares(module_id, shares_minted_as_fees): + distribution = contracts.staking_router.getStakingRewardsDistribution() + module_idx = distribution[1].index(module_id) + return distribution[2][module_idx] * shares_minted_as_fees // distribution[3] + + +def calc_no_rewards(nor, no_id, minted_shares): operator_summary = nor.getNodeOperatorSummary(no_id) module_summary = nor.getStakingModuleSummary() @@ -40,9 +45,7 @@ def calc_no_rewards(nor, no_id, shares_minted_as_fees): ) module_total_active_keys = module_summary["totalDepositedValidators"] - module_summary["totalExitedValidators"] - nor_shares = shares_minted_as_fees // 2 - - return nor_shares * operator_total_active_keys // module_total_active_keys + return minted_shares * operator_total_active_keys // module_total_active_keys def set_staking_limit(nor, ops_ids, keys_count, impersonated_voting): @@ -58,20 +61,11 @@ def set_staking_limit(nor, ops_ids, keys_count, impersonated_voting): ) nor.setNodeOperatorStakingLimit(op_index, new_vetted_keys, {"from": impersonated_voting}) - # first_no = nor.getNodeOperator(first_id, True) - # second_no = nor.getNodeOperator(second_id, True) - # base_no = nor.getNodeOperator(third_id, True) - - # current_first_keys = first_no["totalVettedValidators"] - first_no["totalExitedValidators"] - # current_second_keys = second_no["totalVettedValidators"] - second_no["totalExitedValidators"] - # current_base_keys = base_no["totalVettedValidators"] - base_no["totalExitedValidators"] - - # nor.setNodeOperatorStakingLimit(first_id, current_first_keys + keys_count, {"from": impersonated_voting}) - # nor.setNodeOperatorStakingLimit(second_id, current_second_keys + keys_count, {"from": impersonated_voting}) - # nor.setNodeOperatorStakingLimit(third_id, current_base_keys + keys_count, {"from": impersonated_voting}) +def deposit_and_check_keys(nor, first_id, second_id, third_id, keys_count, impersonated_voting): + # increase limit by 10 keys + set_staking_limit(nor, (first_id, second_id, third_id), 10, impersonated_voting) -def deposit_and_check_keys(nor, first_id, second_id, third_id, keys_count): deposited_keys_first_before = nor.getNodeOperatorSummary(first_id)["totalDepositedValidators"] deposited_keys_second_before = nor.getNodeOperatorSummary(second_id)["totalDepositedValidators"] deposited_keys_base_before = nor.getNodeOperatorSummary(third_id)["totalDepositedValidators"] @@ -80,17 +74,13 @@ def deposit_and_check_keys(nor, first_id, second_id, third_id, keys_count): module_total_deposited_keys_before = nor.getStakingModuleSummary()["totalDepositedValidators"] print(f"Deposit {keys_count} keys for module {nor.module_id}") - print(f"validators_before {validators_before}") tx = contracts.lido.deposit(keys_count, nor.module_id, "0x", {"from": contracts.deposit_security_module.address}) - display_voting_events(tx) validators_after = contracts.lido.getBeaconStat().dict()["depositedValidators"] module_total_deposited_keys_after = nor.getStakingModuleSummary()["totalDepositedValidators"] - print(f"validators_before {validators_after}") - just_deposited = validators_after - validators_before - print("---------", just_deposited) + print("Deposited:", just_deposited) if just_deposited: assert tx.events["DepositedValidatorsChanged"]["depositedValidators"] == validators_after assert tx.events["Unbuffered"]["amount"] == just_deposited * ETH(32) @@ -180,31 +170,13 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e contracts.lido.submit(ZERO_ADDRESS, {"from": eth_whale, "amount": ETH(75000)}) - # # disable deposit for all operators in all modules - # for module_id, module_address, _, _, _, _, _, _, _, _ in all_modules: - # print("!!!", module_id, module_address) - # module = interface.IStakingModule(module_address) - # contracts.acl.grantPermission( - # impersonated_voting, - # module, - # STAKING_ROUTER_ROLE, - # {"from": impersonated_voting}, - # ) - # no_amount = module.getNodeOperatorsCount() - # for op_index in range(no_amount): - # no = module.getNodeOperator(op_index, True) - # if not no["active"]: - # continue - # module.setNodeOperatorStakingLimit(op_index, no["totalDepositedValidators"], {"from": impersonated_voting}) - print("Reset staking limit for all OPs...") no_amount = staking_module.getNodeOperatorsCount() set_staking_limit(staking_module, range(no_amount), 0, impersonated_voting) no3_id, no1_id, no2_id = staking_module.testing_node_operator_ids - set_staking_limit(staking_module, [no1_id, no2_id, no3_id], 3, impersonated_voting) - deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 9) + deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 30, impersonated_voting) penalty_delay = staking_module.getStuckPenaltyDelay() @@ -223,27 +195,16 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e # First report - base empty report (report_tx, extra_report_tx) = oracle_report(exclude_vaults_balances=True) - display_voting_events(report_tx) - display_voting_events(extra_report_tx) - no1_balance_shares_after = shares_balance(no1_reward_address) no2_balance_shares_after = shares_balance(no2_reward_address) no3_balance_shares_after = shares_balance(no3_reward_address) - # expected shares - no1_rewards_after_first_report = calc_no_rewards( - staking_module, - no_id=no1_id, - shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], - ) - no2_rewards_after_first_report = calc_no_rewards( - staking_module, - no_id=no2_id, - shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], - ) - no3_rewards_after_first_report = calc_no_rewards( - staking_module, no_id=no3_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + minted_share = calc_module_reward_shares( + staking_module.module_id, report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) + no1_rewards_after_first_report = calc_no_rewards(staking_module, no_id=no1_id, minted_shares=minted_share) + no2_rewards_after_first_report = calc_no_rewards(staking_module, no_id=no2_id, minted_shares=minted_share) + no3_rewards_after_first_report = calc_no_rewards(staking_module, no_id=no3_id, minted_shares=minted_share) # check shares by empty report assert almostEqWithDiff( @@ -288,8 +249,7 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e no2_balance_shares_before = shares_balance(no2_reward_address) no3_balance_shares_before = shares_balance(no3_reward_address) - set_staking_limit(staking_module, [no1_id, no2_id, no3_id], 10, impersonated_voting) - deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 30) + deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 30, impersonated_voting) # Second report - first NO and second NO has stuck/exited (report_tx, extra_report_tx) = oracle_report( @@ -308,19 +268,12 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e no3_summary = staking_module.getNodeOperatorSummary(no3_id) # expected shares - no1_rewards_after_second_report = calc_no_rewards( - staking_module, - no_id=no1_id, - shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], - ) - no2_rewards_after_second_report = calc_no_rewards( - staking_module, - no_id=no2_id, - shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], - ) - no3_rewards_after_second_report = calc_no_rewards( - staking_module, no_id=no3_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + minted_share = calc_module_reward_shares( + staking_module.module_id, report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) + no1_rewards_after_second_report = calc_no_rewards(staking_module, no_id=no1_id, minted_shares=minted_share) + no2_rewards_after_second_report = calc_no_rewards(staking_module, no_id=no2_id, minted_shares=minted_share) + no3_rewards_after_second_report = calc_no_rewards(staking_module, no_id=no3_id, minted_shares=minted_share) no1_balance_shares_after = shares_balance(no1_reward_address) no2_balance_shares_after = shares_balance(no2_reward_address) @@ -391,8 +344,6 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e assert stuck_penalty_state_changed_events[1]["nodeOperatorId"] == no2_id assert stuck_penalty_state_changed_events[1]["stuckValidatorsCount"] == 2 - set_staking_limit(staking_module, [no1_id, no2_id, no3_id], 10, impersonated_voting) - # Deposit keys ( no1_deposited_keys_before, @@ -401,7 +352,7 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e no1_deposited_keys_after, no2_deposited_keys_after, no3_deposited_keys_after, - ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50) + ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50, impersonated_voting) # check don't change deposited keys for penalized NO assert no1_deposited_keys_before == no1_deposited_keys_after @@ -454,19 +405,12 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e no3_balance_shares_after = shares_balance(no3_reward_address) # expected shares - no1_rewards_after_third_report = calc_no_rewards( - staking_module, - no_id=no1_id, - shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], - ) - no2_rewards_after__third_report = calc_no_rewards( - staking_module, - no_id=no2_id, - shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], - ) - no3_rewards_after__third_report = calc_no_rewards( - staking_module, no_id=no3_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + minted_share = calc_module_reward_shares( + staking_module.module_id, report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) + no1_rewards_after_third_report = calc_no_rewards(staking_module, no_id=no1_id, minted_shares=minted_share) + no2_rewards_after_third_report = calc_no_rewards(staking_module, no_id=no2_id, minted_shares=minted_share) + no3_rewards_after_third_report = calc_no_rewards(staking_module, no_id=no3_id, minted_shares=minted_share) # first NO has penalty has a penalty until stuckPenaltyEndTimestamp # check shares by report with penalty @@ -478,18 +422,18 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e ) assert almostEqWithDiff( no2_balance_shares_after - no2_balance_shares_before, - no2_rewards_after__third_report // 2, + no2_rewards_after_third_report // 2, 1, ) assert almostEqWithDiff( no3_balance_shares_after - no3_balance_shares_before, - no3_rewards_after__third_report, + no3_rewards_after_third_report, 1, ) # Check burn shares no1_amount_penalty = no1_rewards_after_third_report // 2 - no2_amount_penalty = no2_rewards_after__third_report // 2 + no2_amount_penalty = no2_rewards_after_third_report // 2 penalty_shares = no1_amount_penalty + no2_amount_penalty # diff by 2 share because of rounding # TODO: Fix below check when nor contains other penalized node operators @@ -582,19 +526,12 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e no3_balance_shares_after = shares_balance(no3_reward_address) # expected shares - no1_rewards_after_fourth_report = calc_no_rewards( - staking_module, - no_id=no1_id, - shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], - ) - no2_rewards_after__fourth_report = calc_no_rewards( - staking_module, - no_id=no2_id, - shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], - ) - no3_rewards_after__fourth_report = calc_no_rewards( - staking_module, no_id=no3_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + minted_share = calc_module_reward_shares( + staking_module.module_id, report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) + no1_rewards_after_fourth_report = calc_no_rewards(staking_module, no_id=no1_id, minted_shares=minted_share) + no2_rewards_after_fourth_report = calc_no_rewards(staking_module, no_id=no2_id, minted_shares=minted_share) + no3_rewards_after_fourth_report = calc_no_rewards(staking_module, no_id=no3_id, minted_shares=minted_share) # Penalty ended for first operator # check shares by report with penalty for second NO @@ -606,17 +543,17 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e ) assert almostEqWithDiff( no2_balance_shares_after - no2_balance_shares_before, - no2_rewards_after__fourth_report // 2, + no2_rewards_after_fourth_report // 2, 1, ) assert almostEqWithDiff( no3_balance_shares_after - no3_balance_shares_before, - no3_rewards_after__fourth_report, + no3_rewards_after_fourth_report, 1, ) # Check burn shares - no2_amount_penalty = no2_rewards_after__fourth_report // 2 + no2_amount_penalty = no2_rewards_after_fourth_report // 2 # diff by 2 share because of rounding # TODO: Fix below check when nor contains other penalized node operators # assert almostEqWithDiff(extra_report_tx.events["StETHBurnRequested"]["amountOfShares"], amount_penalty_second_no, 1) @@ -640,7 +577,6 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e assert not staking_module.isOperatorPenalized(no3_id) # Deposit - set_staking_limit(staking_module, [no1_id, no2_id, no3_id], 10, impersonated_voting) ( no1_deposited_keys_before, no2_deposited_keys_before, @@ -648,7 +584,7 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e no1_deposited_keys_after, no2_deposited_keys_after, no3_deposited_keys_after, - ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50) + ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50, impersonated_voting) # check don't change deposited keys for penalized NO (only second NO) assert no1_deposited_keys_before != no1_deposited_keys_after @@ -687,19 +623,12 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e no3_summary = staking_module.getNodeOperatorSummary(no3_id) # expected shares - no1_rewards_after_fifth_report = calc_no_rewards( - staking_module, - no_id=no1_id, - shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], - ) - no2_rewards_after_fifth_report = calc_no_rewards( - staking_module, - no_id=no2_id, - shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], - ) - no3_rewards_after_fifth_report = calc_no_rewards( - staking_module, no_id=no3_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + minted_share = calc_module_reward_shares( + staking_module.module_id, report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) + no1_rewards_after_fifth_report = calc_no_rewards(staking_module, no_id=no1_id, minted_shares=minted_share) + no2_rewards_after_fifth_report = calc_no_rewards(staking_module, no_id=no2_id, minted_shares=minted_share) + no3_rewards_after_fifth_report = calc_no_rewards(staking_module, no_id=no3_id, minted_shares=minted_share) # Penalty only for second operator # diff by 1 share because of rounding @@ -781,35 +710,28 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e no2_summary = staking_module.getNodeOperatorSummary(no2_id) # expected shares - no1_rewards_after_seventh_report = calc_no_rewards( - staking_module, - no_id=no1_id, - shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], - ) - no2_rewards_after_seventh_report = calc_no_rewards( - staking_module, - no_id=no2_id, - shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"], - ) - no3_rewards_after_seventh_report = calc_no_rewards( - staking_module, no_id=no3_id, shares_minted_as_fees=report_tx.events["TokenRebased"]["sharesMintedAsFees"] + minted_share = calc_module_reward_shares( + staking_module.module_id, report_tx.events["TokenRebased"]["sharesMintedAsFees"] ) + no1_rewards_after_sixth_report = calc_no_rewards(staking_module, no_id=no1_id, minted_shares=minted_share) + no2_rewards_after_sixth_report = calc_no_rewards(staking_module, no_id=no2_id, minted_shares=minted_share) + no3_rewards_after_sixth_report = calc_no_rewards(staking_module, no_id=no3_id, minted_shares=minted_share) # No penalty # diff by 1 share because of rounding assert almostEqWithDiff( no1_balance_shares_after - no1_balance_shares_before, - no1_rewards_after_seventh_report, + no1_rewards_after_sixth_report, 1, ) assert almostEqWithDiff( no2_balance_shares_after - no2_balance_shares_before, - no2_rewards_after_seventh_report, + no2_rewards_after_sixth_report, 1, ) assert almostEqWithDiff( no3_balance_shares_after - no3_balance_shares_before, - no3_rewards_after_seventh_report, + no3_rewards_after_sixth_report, 1, ) @@ -824,7 +746,6 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e assert no2_summary["stuckPenaltyEndTimestamp"] < chain.time() # Deposit - set_staking_limit(staking_module, [no1_id, no2_id, no3_id], 10, impersonated_voting) ( no1_deposited_keys_before, no2_deposited_keys_before, @@ -832,7 +753,7 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e no1_deposited_keys_after, no2_deposited_keys_after, no3_deposited_keys_after, - ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50) + ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50, impersonated_voting) # check deposit is applied for all NOs assert no1_deposited_keys_before != no1_deposited_keys_after @@ -877,7 +798,6 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e assert first_no_summary_after["isTargetLimitActive"] # Deposit - set_staking_limit(staking_module, [no1_id, no2_id, no3_id], 10, impersonated_voting) ( no1_deposited_keys_before, no2_deposited_keys_before, @@ -885,7 +805,7 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e no1_deposited_keys_after, no2_deposited_keys_after, no3_deposited_keys_after, - ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50) + ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50, impersonated_voting) # check deposit is not applied for first NO assert no1_deposited_keys_before == no1_deposited_keys_after @@ -905,7 +825,6 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e assert not first_no_summary_after["isTargetLimitActive"] # Deposit - set_staking_limit(staking_module, [no1_id, no2_id, no3_id], 10, impersonated_voting) ( no1_deposited_keys_before, no2_deposited_keys_before, @@ -913,7 +832,7 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e no1_deposited_keys_after, no2_deposited_keys_after, no3_deposited_keys_after, - ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50) + ) = deposit_and_check_keys(staking_module, no1_id, no2_id, no3_id, 50, impersonated_voting) # check - deposit not applied to NOs. assert no1_deposited_keys_before != no1_deposited_keys_after @@ -921,21 +840,17 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e assert no3_deposited_keys_before != no3_deposited_keys_after -# def test_node_operator_registry(impersonated_voting, eth_whale): -# nor = contracts.node_operators_registry -# nor.module_id = 1 -# nor.testing_node_operator_ids = [23, 20, 28] -# module_happy_path(nor, ExtraDataService(), impersonated_voting, eth_whale) +def test_node_operator_registry(impersonated_voting, eth_whale): + nor = contracts.node_operators_registry + nor.module_id = 1 + nor.testing_node_operator_ids = [23, 20, 28] + module_happy_path(nor, ExtraDataService(), impersonated_voting, eth_whale) def test_sdvt(impersonated_voting, stranger, eth_whale): sdvt = contracts.simple_dvt sdvt.module_id = 2 sdvt.testing_node_operator_ids = [0, 1, 2] - fill_simple_dvt_ops_vetted_keys(stranger, 3, 100) - - # simulate already deposited keys - deposit_and_check_keys(sdvt, 0, 1, 2, 30) - oracle_report(exclude_vaults_balances=True, cl_diff=ETH(3)) + fill_simple_dvt_ops_keys(stranger, 3, 100) module_happy_path(sdvt, ExtraDataService(), impersonated_voting, eth_whale) diff --git a/utils/test/simple_dvt_helpers.py b/utils/test/simple_dvt_helpers.py index 222de7b9..b29c2b1f 100644 --- a/utils/test/simple_dvt_helpers.py +++ b/utils/test/simple_dvt_helpers.py @@ -164,8 +164,8 @@ def simple_dvt_add_node_operators(simple_dvt, stranger, input_params=[]): def simple_dvt_add_keys(simple_dvt, node_operator_id, keys_count=1): - pubkeys_batch = random_pubkeys_batch(keys_count) - signatures_batch = random_signatures_batch(keys_count) + pubkeys_batch = random_pubkeys_batch(keys_count, "keys_" + str(node_operator_id)) + signatures_batch = random_signatures_batch(keys_count, "signatures_" + str(node_operator_id)) total_signing_keys_count_before = simple_dvt.getTotalSigningKeyCount(node_operator_id) unused_signing_keys_count_before = simple_dvt.getUnusedSigningKeyCount(node_operator_id) From b17f63298795711385c4dfdc883e889480a4a1b0 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 14 Feb 2024 18:24:41 +0100 Subject: [PATCH 44/68] fix: test refactor --- tests/regression/test_node_operators_flow.py | 12 +++++------- utils/test/simple_dvt_helpers.py | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/tests/regression/test_node_operators_flow.py b/tests/regression/test_node_operators_flow.py index 93bbe503..79eb18ba 100644 --- a/tests/regression/test_node_operators_flow.py +++ b/tests/regression/test_node_operators_flow.py @@ -248,12 +248,12 @@ def test_add_node_operator(nor, voting_eoa, reward_address, new_node_operator_id # TODO: validate events -def random_pubkeys_batch(pubkeys_count: int, seed=None): - return random_hexstr(pubkeys_count * PUBKEY_LENGTH, seed) +def random_pubkeys_batch(pubkeys_count: int): + return random_hexstr(pubkeys_count * PUBKEY_LENGTH) -def random_signatures_batch(signautes_count: int, seed=None): - return random_hexstr(signautes_count * SIGNATURE_LENGTH, seed) +def random_signatures_batch(signautes_count: int): + return random_hexstr(signautes_count * SIGNATURE_LENGTH) def parse_pubkeys_batch(pubkeys_batch: str): @@ -270,9 +270,7 @@ def hex_chunks(hexstr: str, chunk_length: int): return [prefix_0x(chunk) for chunk in textwrap.wrap(stripped_hexstr, 2 * chunk_length)] -def random_hexstr(length: int, seed=None): - if seed: - random.seed(seed) +def random_hexstr(length: int): return prefix_0x(random.randbytes(length).hex()) diff --git a/utils/test/simple_dvt_helpers.py b/utils/test/simple_dvt_helpers.py index b29c2b1f..222de7b9 100644 --- a/utils/test/simple_dvt_helpers.py +++ b/utils/test/simple_dvt_helpers.py @@ -164,8 +164,8 @@ def simple_dvt_add_node_operators(simple_dvt, stranger, input_params=[]): def simple_dvt_add_keys(simple_dvt, node_operator_id, keys_count=1): - pubkeys_batch = random_pubkeys_batch(keys_count, "keys_" + str(node_operator_id)) - signatures_batch = random_signatures_batch(keys_count, "signatures_" + str(node_operator_id)) + pubkeys_batch = random_pubkeys_batch(keys_count) + signatures_batch = random_signatures_batch(keys_count) total_signing_keys_count_before = simple_dvt.getTotalSigningKeyCount(node_operator_id) unused_signing_keys_count_before = simple_dvt.getUnusedSigningKeyCount(node_operator_id) From cd6fa64319e4cfecf757a27f879ca5e273cf1841 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 14 Feb 2024 18:26:38 +0100 Subject: [PATCH 45/68] fix: test refactor --- tests/regression/test_staking_module_happy_path.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/regression/test_staking_module_happy_path.py b/tests/regression/test_staking_module_happy_path.py index 5c0fece4..ea0a0177 100644 --- a/tests/regression/test_staking_module_happy_path.py +++ b/tests/regression/test_staking_module_happy_path.py @@ -1,7 +1,7 @@ import pytest from web3 import Web3 import eth_abi -from brownie import chain, ZERO_ADDRESS, web3, interface +from brownie import chain, ZERO_ADDRESS, web3 from utils.test.extra_data import ( ExtraDataService, @@ -20,11 +20,6 @@ SET_NODE_OPERATOR_LIMIT_ROLE = Web3.keccak(text="SET_NODE_OPERATOR_LIMIT_ROLE") -@pytest.fixture(scope="function") -def impersonate_es_executor(accounts): - return accounts.at(EASYTRACK_EVMSCRIPT_EXECUTOR, force=True) - - @pytest.fixture(scope="function") def impersonated_voting(accounts): return accounts.at(contracts.voting.address, force=True) From aad091c0d9e6d6592a19abe8b2dbe96b9481c4a0 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 14 Feb 2024 19:25:53 +0100 Subject: [PATCH 46/68] fix: vote test --- configs/config_mainnet.py | 4 +- ..._simple_dvt.py => test_vote_simple_dvt.py} | 86 ++++++++++++------- 2 files changed, 56 insertions(+), 34 deletions(-) rename tests/{test_simple_dvt.py => test_vote_simple_dvt.py} (91%) diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index 8eece8af..485ca4d7 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -174,8 +174,8 @@ ANNUAL_BALANCE_INCREASE_BP_LIMIT = 1000 # 10% SIMULATED_SHARE_RATE_DEVIATION_BP_LIMIT = 50 # 0.5% MAX_VALIDATOR_EXIT_REQUESTS_PER_REPORT = 600 -MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT = 2 -MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT = 100 +MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT = 4 +MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT = 50 REQUEST_TIMESTAMP_MARGIN = 7680 # 2 hours rounded to epoch length MAX_POSITIVE_TOKEN_REBASE = 750000 diff --git a/tests/test_simple_dvt.py b/tests/test_vote_simple_dvt.py similarity index 91% rename from tests/test_simple_dvt.py rename to tests/test_vote_simple_dvt.py index f2b63d2d..b467194f 100644 --- a/tests/test_simple_dvt.py +++ b/tests/test_vote_simple_dvt.py @@ -63,6 +63,7 @@ SET_NODE_OPERATOR_LIMIT_ROLE = "0x07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754" MANAGE_SIGNING_KEYS = "0x75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee" MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE = "0x0cf253eb71298c92e2814969a122f66b781f9b217f8ecde5401e702beb9345f6" +MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE = "0xf6ac39904c42f8e23056f1b678e4892fc92caa68ae836dc474e137f0e67f5716" simple_dvt_repo_ens = "simple-dvt.lidopm.eth" simple_dvt_content_uri = ( @@ -113,6 +114,7 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco sanity_checker_limits = contracts.oracle_report_sanity_checker.getOracleReportLimits() assert sanity_checker_limits["maxAccountingExtraDataListItemsCount"] == 2 + assert sanity_checker_limits["maxNodeOperatorsPerExtraDataItemCount"] == 100 # START VOTE if len(vote_ids_from_env) > 0: @@ -262,9 +264,10 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco sanity_checker_limits = contracts.oracle_report_sanity_checker.getOracleReportLimits() assert sanity_checker_limits["maxAccountingExtraDataListItemsCount"] == 4 + assert sanity_checker_limits["maxNodeOperatorsPerExtraDataItemCount"] == 50 # validate vote events - assert count_vote_items_by_events(vote_tx, contracts.voting) == 20, "Incorrect voting items count" + assert count_vote_items_by_events(vote_tx, contracts.voting) == 22, "Incorrect voting items count" metadata = find_metadata_by_vote_id(vote_id) print("metadata", metadata) @@ -279,6 +282,7 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco evs = group_voting_events(vote_tx) + # I. Create new Aragon DAO Application Repo for SimpleDVT repo_params = NewRepoItem( name=SIMPLE_DVT_ARAGON_APP_NAME, app=simple_dvt.address, @@ -290,10 +294,11 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ) validate_new_repo_with_version_event(evs[0], repo_params) + # II. Setup and initialize SimpleDVT module as new Aragon app validate_app_update_event(evs[1], SIMPLE_DVT_ARAGON_APP_ID, SIMPLE_DVT_IMPL) - validate_simple_dvt_intialize_event(evs[2]) + # III. Add SimpleDVT module to Staking Router # Create and grant permission STAKING_ROUTER_ROLE on SimpleDVT module for StakingRouter permission = Permission( entity=staking_router, @@ -302,36 +307,48 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ) validate_permission_create_event(evs[3], permission, manager=voting) - # Grant STAKING_ROUTER_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor - permission = Permission(entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=STAKING_ROUTER_ROLE) - validate_permission_grant_event(evs[4], permission) + # Grant REQUEST_BURN_SHARES_ROLE on Burner for SimpleDVT module + validate_grant_role_event(evs[4], REQUEST_BURN_SHARES_ROLE, simple_dvt, agent) + + # Add SimpleDVT module to StakingRouter + module_item = StakingModuleItem( + SIMPLE_DVT_MODULE_ID, + simple_dvt.address, + SIMPLE_DVT_MODULE_NAME, + SIMPLE_DVT_MODULE_TARGET_SHARE_BP, + SIMPLE_DVT_MODULE_MODULE_FEE_BP, + SIMPLE_DVT_MODULE_TREASURY_FEE_BP, + ) + validate_staking_module_added_event(evs[5], module_item) - # Create and grant permission MANAGE_NODE_OPERATOR_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor + # IV. Grant permissions to make operational changes to SimpleDVT module permission = Permission( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=MANAGE_NODE_OPERATOR_ROLE, # simple_dvt.MANAGE_NODE_OPERATOR_ROLE(), ) - validate_permission_create_event(evs[5], permission, manager=voting) + validate_permission_create_event(evs[6], permission, manager=voting) - # Create and grant permission SET_NODE_OPERATOR_LIMIT_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor permission = Permission( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=SET_NODE_OPERATOR_LIMIT_ROLE, # simple_dvt.SET_NODE_OPERATOR_LIMIT_ROLE(), ) - validate_permission_create_event(evs[6], permission, manager=voting) + validate_permission_create_event(evs[7], permission, manager=voting) - # Create and grant permission MANAGE_SIGNING_KEYS on SimpleDVT module for EasyTrackEVMScriptExecutor permission = Permission( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=MANAGE_SIGNING_KEYS, # simple_dvt.MANAGE_SIGNING_KEYS(), ) - validate_permission_create_event(evs[7], permission, manager=EASYTRACK_EVMSCRIPT_EXECUTOR) + validate_permission_create_event(evs[8], permission, manager=EASYTRACK_EVMSCRIPT_EXECUTOR) + + permission = Permission(entity=EASYTRACK_EVMSCRIPT_EXECUTOR, app=simple_dvt, role=STAKING_ROUTER_ROLE) + validate_permission_grant_event(evs[9], permission) + # IV. Add EasyTrack EVM script factories for SimpleDVT module validate_evmscript_factory_added_event( - evs[8], + evs[10], EVMScriptFactoryAdded( factory_addr=add_node_operators_evm_script_factory, permissions=create_permissions(simple_dvt, "addNodeOperator") @@ -339,7 +356,7 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ), ) validate_evmscript_factory_added_event( - evs[9], + evs[11], EVMScriptFactoryAdded( factory_addr=activate_node_operators_evm_script_factory, permissions=create_permissions(simple_dvt, "activateNodeOperator") @@ -347,7 +364,7 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ), ) validate_evmscript_factory_added_event( - evs[10], + evs[12], EVMScriptFactoryAdded( factory_addr=deactivate_node_operators_evm_script_factory, permissions=create_permissions(simple_dvt, "deactivateNodeOperator") @@ -355,35 +372,35 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ), ) validate_evmscript_factory_added_event( - evs[11], + evs[13], EVMScriptFactoryAdded( factory_addr=set_vetted_validators_limits_evm_script_factory, permissions=create_permissions(simple_dvt, "setNodeOperatorStakingLimit"), ), ) validate_evmscript_factory_added_event( - evs[12], + evs[14], EVMScriptFactoryAdded( factory_addr=update_target_validator_limits_evm_script_factory, permissions=create_permissions(simple_dvt, "updateTargetValidatorsLimits"), ), ) validate_evmscript_factory_added_event( - evs[13], + evs[15], EVMScriptFactoryAdded( factory_addr=set_node_operator_names_evm_script_factory, permissions=create_permissions(simple_dvt, "setNodeOperatorName"), ), ) validate_evmscript_factory_added_event( - evs[14], + evs[16], EVMScriptFactoryAdded( factory_addr=set_node_operator_reward_addresses_evm_script_factory, permissions=create_permissions(simple_dvt, "setNodeOperatorRewardAddress"), ), ) validate_evmscript_factory_added_event( - evs[15], + evs[17], EVMScriptFactoryAdded( factory_addr=change_node_operator_managers_evm_script_factory, permissions=create_permissions(contracts.acl, "revokePermission") @@ -391,20 +408,11 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco ), ) - validate_grant_role_event(evs[16], REQUEST_BURN_SHARES_ROLE, simple_dvt, agent) - - module_item = StakingModuleItem( - SIMPLE_DVT_MODULE_ID, - simple_dvt.address, - SIMPLE_DVT_MODULE_NAME, - SIMPLE_DVT_MODULE_TARGET_SHARE_BP, - SIMPLE_DVT_MODULE_MODULE_FEE_BP, - SIMPLE_DVT_MODULE_TREASURY_FEE_BP, - ) - validate_staking_module_added_event(evs[17], module_item) - + # VI. Update Oracle Report Sanity Checker parameters validate_grant_role_event(evs[18], MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE, agent, agent) - validate_max_extra_data_list_items_count_event(evs[19], 4) + validate_grant_role_event(evs[19], MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE, agent, agent) + validate_max_extra_data_list_items_count_event(evs[20], 4) + validate_max_operators_per_extra_data_item_count_event(evs[21], 50) def has_permission(permission: Permission, how: List[int]) -> bool: @@ -427,6 +435,20 @@ def validate_max_extra_data_list_items_count_event(event: EventDict, value: int) assert event["MaxAccountingExtraDataListItemsCountSet"]["maxAccountingExtraDataListItemsCount"] == value +def validate_max_operators_per_extra_data_item_count_event(event: EventDict, value: int): + _events_chain = [ + "LogScriptCall", + "LogScriptCall", + "MaxNodeOperatorsPerExtraDataItemCountSet", + "ScriptResult", + ] + + validate_events_chain([e.name for e in event], _events_chain) + + assert event.count("MaxNodeOperatorsPerExtraDataItemCountSet") == 1 + assert event["MaxNodeOperatorsPerExtraDataItemCountSet"]["maxNodeOperatorsPerExtraDataItemCount"] == value + + def validate_simple_dvt_intialize_event(event: EventDict): _events_chain = [ "LogScriptCall", From 51573bf765f2584607c6ec8c9e8d8d8feef2ed56 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 14 Feb 2024 22:56:57 +0100 Subject: [PATCH 47/68] fix: simple dvt helpers, perm test --- tests/acceptance/test_simple_dvt_module.py | 10 ++++---- utils/test/simple_dvt_helpers.py | 27 +++++++++------------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/tests/acceptance/test_simple_dvt_module.py b/tests/acceptance/test_simple_dvt_module.py index 169672ae..6426d598 100644 --- a/tests/acceptance/test_simple_dvt_module.py +++ b/tests/acceptance/test_simple_dvt_module.py @@ -158,14 +158,16 @@ def test_simple_dvt_state(contract): def test_simple_dvt_permissions(contract): - assert contracts.acl.getPermissionManager(contract.address, STAKING_ROUTER_ROLE) == contract.voting.address - assert contract.canPerform(contract.address, STAKING_ROUTER_ROLE, []) + assert contracts.acl.getPermissionManager(contract.address, STAKING_ROUTER_ROLE) == contracts.voting.address + assert contract.canPerform(contracts.staking_router.address, STAKING_ROUTER_ROLE, []) assert contract.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, STAKING_ROUTER_ROLE, []) - assert contracts.acl.getPermissionManager(contract.address, MANAGE_NODE_OPERATOR_ROLE) == contract.voting.address + assert contracts.acl.getPermissionManager(contract.address, MANAGE_NODE_OPERATOR_ROLE) == contracts.voting.address assert contract.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, MANAGE_NODE_OPERATOR_ROLE, []) - assert contracts.acl.getPermissionManager(contract.address, SET_NODE_OPERATOR_LIMIT_ROLE) == contract.voting.address + assert ( + contracts.acl.getPermissionManager(contract.address, SET_NODE_OPERATOR_LIMIT_ROLE) == contracts.voting.address + ) assert contract.canPerform(EASYTRACK_EVMSCRIPT_EXECUTOR, SET_NODE_OPERATOR_LIMIT_ROLE, []) assert contracts.acl.getPermissionManager(contract.address, MANAGE_SIGNING_KEYS) == EASYTRACK_EVMSCRIPT_EXECUTOR diff --git a/utils/test/simple_dvt_helpers.py b/utils/test/simple_dvt_helpers.py index 222de7b9..23e15a7b 100644 --- a/utils/test/simple_dvt_helpers.py +++ b/utils/test/simple_dvt_helpers.py @@ -12,23 +12,17 @@ MIN_OP_KEYS_CNT = 10 MIN_OPS_CNT = 3 -OPERATOR_NAMES = [ - "Name 1", - "Name 2", - "Name 3", -] -REWARD_ADDRESSES = [ - "0x0000000000000000000000000000000000001111", - "0x0000000000000000000000000000000000002222", - "0x0000000000000000000000000000000000003333", -] +def get_operator_name(n: int): + return f"Name {n}" -MANAGERS = [ - "0x0000000000000000000000000000000011111111", - "0x0000000000000000000000000000000022222222", - "0x0000000000000000000000000000000033333333", -] + +def get_operator_address(id: int): + return f"0x111{id:037x}" + + +def get_managers_address(id: int): + return f"0x222{id:037x}" def fill_simple_dvt_ops(stranger, min_ops_cnt=MIN_OPS_CNT): @@ -36,7 +30,8 @@ def fill_simple_dvt_ops(stranger, min_ops_cnt=MIN_OPS_CNT): cnt = 0 input_params = [] while node_operators_count_before + cnt < min_ops_cnt: - input_params.append((OPERATOR_NAMES[cnt], REWARD_ADDRESSES[cnt], MANAGERS[cnt])) + op_id = node_operators_count_before + cnt + input_params.append((get_operator_name(op_id), get_operator_address(op_id), get_managers_address(op_id))) cnt += 1 (node_operators_count_before, node_operator_count_after) = simple_dvt_add_node_operators( From d718a0a4ff6c282ecc2e33754f4dde4f27273ce8 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 14 Feb 2024 23:48:20 +0100 Subject: [PATCH 48/68] fix: easytrack test --- tests/regression/test_easy_track.py | 68 +++-- tests/regression/test_easy_track_factories.py | 241 ++++++++++-------- utils/test/simple_dvt_helpers.py | 16 +- 3 files changed, 170 insertions(+), 155 deletions(-) diff --git a/tests/regression/test_easy_track.py b/tests/regression/test_easy_track.py index 9d3398ab..b51b1fdc 100644 --- a/tests/regression/test_easy_track.py +++ b/tests/regression/test_easy_track.py @@ -13,24 +13,15 @@ EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY, EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY, ) -from utils.test.simple_dvt_helpers import simple_dvt_add_keys, simple_dvt_add_node_operators +from utils.test.simple_dvt_helpers import ( + get_managers_address, + get_operator_address, + get_operator_name, + simple_dvt_add_keys, + simple_dvt_add_node_operators, +) from utils.test.easy_track_helpers import _encode_calldata -NODE_OPERATOR_ID = 0 -NEW_OPERATOR_NAMES = [ - "New Name 1", - "New Name 2", -] - -NEW_REWARD_ADDRESSES = [ - "0x1110000000000000000000000000000000001111", - "0x1110000000000000000000000000000000002222", -] - -NEW_MANAGERS = [ - "0x1110000000000000000000000000000011111111", - "0x1110000000000000000000000000000022222222", -] MANAGE_SIGNING_KEYS = "0x75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee" @@ -38,8 +29,9 @@ def test_increase_nop_staking_limit( stranger, ): + no_id = 0 factory = interface.IncreaseNodeOperatorStakingLimit(EASYTRACK_INCREASE_NOP_STAKING_LIMIT_FACTORY) - node_operator = contracts.node_operators_registry.getNodeOperator(NODE_OPERATOR_ID, False) + node_operator = contracts.node_operators_registry.getNodeOperator(no_id, False) trusted_caller = accounts.at(node_operator["rewardAddress"], force=True) new_staking_limit = node_operator["totalVettedValidators"] + 1 @@ -47,14 +39,14 @@ def test_increase_nop_staking_limit( if node_operator["totalAddedValidators"] < new_staking_limit: contracts.node_operators_registry.addSigningKeys( - NODE_OPERATOR_ID, + no_id, 1, "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aa0101", "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a1", {"from": contracts.voting}, ) - calldata = _encode_calldata("(uint256,uint256)", [NODE_OPERATOR_ID, new_staking_limit]) + calldata = _encode_calldata("(uint256,uint256)", [no_id, new_staking_limit]) tx = contracts.easy_track.createMotion(factory, calldata, {"from": trusted_caller}) @@ -70,7 +62,7 @@ def test_increase_nop_staking_limit( {"from": stranger}, ) - updated_node_operator = contracts.node_operators_registry.getNodeOperator(NODE_OPERATOR_ID, False) + updated_node_operator = contracts.node_operators_registry.getNodeOperator(no_id, False) assert updated_node_operator["totalVettedValidators"] == new_staking_limit @@ -79,8 +71,8 @@ def test_simple_dvt_add_node_operators( stranger, ): input_params = [ - (NEW_OPERATOR_NAMES[0], NEW_REWARD_ADDRESSES[0], NEW_MANAGERS[0]), - (NEW_OPERATOR_NAMES[1], NEW_REWARD_ADDRESSES[1], NEW_MANAGERS[1]), + (get_operator_name(0, 1), get_operator_address(0, 1), get_managers_address(0, 1)), + (get_operator_name(1, 1), get_operator_address(1, 1), get_managers_address(1, 1)), ] (node_operators_count_before, node_operator_count_after) = simple_dvt_add_node_operators( @@ -94,7 +86,7 @@ def test_simple_dvt_set_vetted_validators_limits( stranger, ): input_params = [ - (NEW_OPERATOR_NAMES[0], NEW_REWARD_ADDRESSES[0], NEW_MANAGERS[0]), + (get_operator_name(0, 1), get_operator_address(0, 1), get_managers_address(0, 1)), ] (_, node_operator_count_after) = simple_dvt_add_node_operators(contracts.simple_dvt, stranger, input_params) @@ -136,9 +128,9 @@ def test_simple_dvt_set_vetted_validators_limits( def test_simple_dvt_activate_deactivate_operators( stranger, ): - op_name = NEW_OPERATOR_NAMES[0] - op_addr = NEW_REWARD_ADDRESSES[0] - op_manager = NEW_MANAGERS[0] + op_name = get_operator_name(0, 1) + op_addr = get_operator_address(0, 1) + op_manager = get_managers_address(0, 1) (_, node_operator_count_after) = simple_dvt_add_node_operators( contracts.simple_dvt, @@ -223,11 +215,11 @@ def test_simple_dvt_activate_deactivate_operators( def test_simple_dvt_set_operator_name_reward_address( stranger, ): - op_name = NEW_OPERATOR_NAMES[0] - op_addr = NEW_REWARD_ADDRESSES[0] - op_manager = NEW_MANAGERS[0] - op_name_upd = NEW_OPERATOR_NAMES[1] - op_addr_upd = NEW_REWARD_ADDRESSES[1] + op_name = get_operator_name(0, 1) + op_addr = get_operator_address(0, 1) + op_manager = get_managers_address(0, 1) + op_name_upd = get_operator_name(1, 1) + op_addr_upd = get_operator_address(1, 1) (_, node_operator_count_after) = simple_dvt_add_node_operators( contracts.simple_dvt, @@ -284,9 +276,9 @@ def test_simple_dvt_set_operator_name_reward_address( def test_simple_dvt_set_operator_target_limit( stranger, ): - op_name = NEW_OPERATOR_NAMES[0] - op_addr = NEW_REWARD_ADDRESSES[0] - op_manager = NEW_MANAGERS[0] + op_name = get_operator_name(0, 1) + op_addr = get_operator_address(0, 1) + op_manager = get_managers_address(0, 1) target_limit = 2 @@ -335,10 +327,10 @@ def test_simple_dvt_set_operator_target_limit( def test_simple_dvt_change_operator_manager( stranger, ): - op_name = NEW_OPERATOR_NAMES[0] - op_addr = NEW_REWARD_ADDRESSES[0] - op_manager = NEW_MANAGERS[0] - op_manager_upd = NEW_MANAGERS[1] + op_name = get_operator_name(0, 1) + op_addr = get_operator_address(0, 1) + op_manager = get_managers_address(0, 1) + op_manager_upd = get_managers_address(1, 1) (_, node_operator_count_after) = simple_dvt_add_node_operators( contracts.simple_dvt, diff --git a/tests/regression/test_easy_track_factories.py b/tests/regression/test_easy_track_factories.py index a7982209..87c0c020 100644 --- a/tests/regression/test_easy_track_factories.py +++ b/tests/regression/test_easy_track_factories.py @@ -6,14 +6,14 @@ from configs.config_mainnet import * from utils.config import contracts from utils.test.easy_track_helpers import _encode_calldata -from utils.test.simple_dvt_helpers import MANAGERS +from utils.test.simple_dvt_helpers import get_managers_address, get_operator_address, get_operator_name NODE_OPERATORS = [ { - 'address': f'0x000000000000000000000000000000000000{i:04}', - 'manager': f'0x000000000000000000000000000000000001{i:04}', - 'name': f'Node operator {i}', + "address": get_operator_address(i, 2), + "manager": get_managers_address(i, 2), + "name": get_operator_name(i, 2), } for i in range(1, 11) ] @@ -23,7 +23,7 @@ def easy_track_executor(creator, factory, calldata): tx = contracts.easy_track.createMotion( factory, calldata, - {'from': creator}, + {"from": creator}, ) motions = contracts.easy_track.getMotions() @@ -33,21 +33,18 @@ def easy_track_executor(creator, factory, calldata): contracts.easy_track.enactMotion( motions[-1][0], - tx.events['MotionCreated']['_evmScriptCallData'], - {'from': accounts[4]}, + tx.events["MotionCreated"]["_evmScriptCallData"], + {"from": accounts[4]}, ) def add_node_operators(operators): calldata = _encode_calldata( - '(uint256,(string,address,address)[])', + "(uint256,(string,address,address)[])", [ contracts.simple_dvt.getNodeOperatorsCount(), - [ - (no['name'], no['address'], no['manager']) - for no in NODE_OPERATORS - ], - ] + [(no["name"], no["address"], no["manager"]) for no in NODE_OPERATORS], + ], ) factory = interface.AddNodeOperators(EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY) @@ -61,8 +58,8 @@ def add_node_operators(operators): def activate_node_operators(operators): calldata = _encode_calldata( - '((uint256,address)[])', - [[(no['id'], no['manager']) for no in operators]], + "((uint256,address)[])", + [[(no["id"], no["manager"]) for no in operators]], ) factory = interface.ActivateNodeOperators(EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY) @@ -76,8 +73,8 @@ def activate_node_operators(operators): def deactivate_node_operator(operators): calldata = _encode_calldata( - '((uint256,address)[])', - [[(no['id'], no['manager']) for no in operators]], + "((uint256,address)[])", + [[(no["id"], no["manager"]) for no in operators]], ) factory = interface.DeactivateNodeOperators(EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY) @@ -90,10 +87,7 @@ def deactivate_node_operator(operators): def set_vetted_validators_limits(operators): - calldata = _encode_calldata( - '((uint256,uint256)[])', - [[(no['id'], no['staking_limit']) for no in operators]] - ) + calldata = _encode_calldata("((uint256,uint256)[])", [[(no["id"], no["staking_limit"]) for no in operators]]) factory = interface.SetVettedValidatorsLimits(EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY) @@ -106,8 +100,8 @@ def set_vetted_validators_limits(operators): def set_node_operators_names(operators): calldata = _encode_calldata( - '((uint256,string)[])', - [[(no['id'], no['name']) for no in operators]], + "((uint256,string)[])", + [[(no["id"], no["name"]) for no in operators]], ) factory = interface.SetNodeOperatorNames(EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_NAMES_FACTORY) @@ -121,8 +115,8 @@ def set_node_operators_names(operators): def set_node_operator_reward_addresses(operators): calldata = _encode_calldata( - '((uint256,address)[])', - [[(no['id'], no['address']) for no in operators]], + "((uint256,address)[])", + [[(no["id"], no["address"]) for no in operators]], ) factory = interface.SetNodeOperatorRewardAddresses(EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY) @@ -136,8 +130,8 @@ def set_node_operator_reward_addresses(operators): def update_target_validators_limits(operators): calldata = _encode_calldata( - '((uint256,bool,uint256)[])', - [[(no['id'], no['is_target_limit_active'], no['target_limit']) for no in operators]], + "((uint256,bool,uint256)[])", + [[(no["id"], no["is_target_limit_active"], no["target_limit"]) for no in operators]], ) factory = interface.UpdateTargetValidatorLimits(EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY) @@ -151,8 +145,8 @@ def update_target_validators_limits(operators): def change_node_operator_managers(operators): calldata = _encode_calldata( - '((uint256,address,address)[])', - [[(no['id'], no['old_manager'], no['manager']) for no in operators]], + "((uint256,address,address)[])", + [[(no["id"], no["old_manager"], no["manager"]) for no in operators]], ) factory = interface.ChangeNodeOperatorManagers(EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY) @@ -170,14 +164,14 @@ def test_add_node_operators(): add_node_operators(NODE_OPERATORS) - no_ids = list(contracts.simple_dvt.getNodeOperatorIds(1, 100))[node_operators_count - 1:] + no_ids = list(contracts.simple_dvt.getNodeOperatorIds(1, 100))[node_operators_count - 1 :] for no_id, no in zip(no_ids, NODE_OPERATORS): no_in_contract = contracts.simple_dvt.getNodeOperator(no_id, True) assert no_in_contract[0] - assert no_in_contract[1] == no['name'] - assert no_in_contract[2] == no['address'] + assert no_in_contract[1] == no["name"] + assert no_in_contract[2] == no["address"] assert node_operators_count + len(NODE_OPERATORS) == contracts.simple_dvt.getNodeOperatorsCount() @@ -186,25 +180,35 @@ def test_node_operators_activations(): assert contracts.simple_dvt.getNodeOperator(1, True)[0] assert contracts.simple_dvt.getNodeOperator(2, True)[0] - deactivate_node_operator([{ - 'id': 1, - 'manager': MANAGERS[1], - }, { - 'id': 2, - 'manager': MANAGERS[2], - }]) + deactivate_node_operator( + [ + { + "id": 1, + "manager": get_managers_address(1), + }, + { + "id": 2, + "manager": get_managers_address(2), + }, + ] + ) assert not contracts.simple_dvt.getNodeOperator(1, True)[0] assert not contracts.simple_dvt.getNodeOperator(2, True)[0] # ActivateNodeOperators - activate_node_operators([{ - 'id': 1, - 'manager': MANAGERS[1], - }, { - 'id': 2, - 'manager': MANAGERS[2], - }]) + activate_node_operators( + [ + { + "id": 1, + "manager": get_managers_address(1), + }, + { + "id": 2, + "manager": get_managers_address(2), + }, + ] + ) assert contracts.simple_dvt.getNodeOperator(1, True)[0] assert contracts.simple_dvt.getNodeOperator(2, True)[0] @@ -217,13 +221,18 @@ def test_set_vetted_validators_limits(): new_vetted_keys_1 = random.randint(0, op_1[5]) new_vetted_keys_2 = random.randint(0, op_2[5]) - set_vetted_validators_limits([{ - 'id': 1, - 'staking_limit': new_vetted_keys_1, - }, { - 'id': 2, - 'staking_limit': new_vetted_keys_2, - }]) + set_vetted_validators_limits( + [ + { + "id": 1, + "staking_limit": new_vetted_keys_1, + }, + { + "id": 2, + "staking_limit": new_vetted_keys_2, + }, + ] + ) assert contracts.simple_dvt.getNodeOperator(1, True)[3] == new_vetted_keys_1 assert contracts.simple_dvt.getNodeOperator(2, True)[3] == new_vetted_keys_2 @@ -233,34 +242,44 @@ def test_set_node_operator_names(): op_1 = contracts.simple_dvt.getNodeOperator(1, True) op_2 = contracts.simple_dvt.getNodeOperator(2, True) - new_name_1 = op_1[1] + ' new 1' - new_name_2 = op_2[1] + ' new 2' + new_name_1 = op_1[1] + " new 1" + new_name_2 = op_2[1] + " new 2" # SetNodeOperatorNames - set_node_operators_names([{ - 'id': 1, - 'name': new_name_1, - }, { - 'id': 2, - 'name': new_name_2, - }]) + set_node_operators_names( + [ + { + "id": 1, + "name": new_name_1, + }, + { + "id": 2, + "name": new_name_2, + }, + ] + ) assert contracts.simple_dvt.getNodeOperator(1, True)[1] == new_name_1 assert contracts.simple_dvt.getNodeOperator(2, True)[1] == new_name_2 def test_set_node_operator_reward_addresses(): - address_1 = '0x0000000000000000000000000000000000001333' - address_2 = '0x0000000000000000000000000000000000001999' + address_1 = "0x0000000000000000000000000000000000001333" + address_2 = "0x0000000000000000000000000000000000001999" # SetNodeOperatorRewardAddresses - set_node_operator_reward_addresses([{ - 'id': 1, - 'address': address_1, - }, { - 'id': 2, - 'address': address_2, - }]) + set_node_operator_reward_addresses( + [ + { + "id": 1, + "address": address_1, + }, + { + "id": 2, + "address": address_2, + }, + ] + ) assert contracts.simple_dvt.getNodeOperator(1, True)[2] == address_1 assert contracts.simple_dvt.getNodeOperator(2, True)[2] == address_2 @@ -268,15 +287,20 @@ def test_set_node_operator_reward_addresses(): def test_update_target_validator_limits(): # UpdateTargetValidatorLimits - update_target_validators_limits([{ - 'id': 1, - 'is_target_limit_active': True, - 'target_limit': 800, - }, { - 'id': 2, - 'is_target_limit_active': False, - 'target_limit': 900, - }]) + update_target_validators_limits( + [ + { + "id": 1, + "is_target_limit_active": True, + "target_limit": 800, + }, + { + "id": 2, + "is_target_limit_active": False, + "target_limit": 900, + }, + ] + ) # assert contracts.simple_dvt.getNodeOperator(1, True)[1] == address_1 # assert contracts.simple_dvt.getNodeOperator(2, True)[2] == address_2 @@ -284,35 +308,34 @@ def test_update_target_validator_limits(): def test_transfer_node_operator_manager(): # TransferNodeOperatorManager - change_node_operator_managers([{ - 'id': 1, - 'old_manager': MANAGERS[1], - 'manager': '0x0000000000000000000000000000000000000222' - }, { - 'id': 2, - 'old_manager': MANAGERS[2], - 'manager': '0x0000000000000000000000000000000000000888' - }]) - - change_node_operator_managers([{ - 'id': 1, - 'old_manager': '0x0000000000000000000000000000000000000222', - 'manager': MANAGERS[1] - }, { - 'id': 2, - 'old_manager': '0x0000000000000000000000000000000000000888', - 'manager': MANAGERS[2] - }]) + change_node_operator_managers( + [ + {"id": 1, "old_manager": get_managers_address(1), "manager": "0x0000000000000000000000000000000000000222"}, + {"id": 2, "old_manager": get_managers_address(2), "manager": "0x0000000000000000000000000000000000000888"}, + ] + ) + + change_node_operator_managers( + [ + {"id": 1, "old_manager": "0x0000000000000000000000000000000000000222", "manager": get_managers_address(1)}, + {"id": 2, "old_manager": "0x0000000000000000000000000000000000000888", "manager": get_managers_address(2)}, + ] + ) try: - change_node_operator_managers([{ - 'id': 1, - 'old_manager': '0x0000000000000000000000000000000000000222', - 'manager': MANAGERS[1] - }, { - 'id': 2, - 'old_manager': '0x0000000000000000000000000000000000000888', - 'manager': MANAGERS[2] - }]) + change_node_operator_managers( + [ + { + "id": 1, + "old_manager": "0x0000000000000000000000000000000000000222", + "manager": get_managers_address(1), + }, + { + "id": 2, + "old_manager": "0x0000000000000000000000000000000000000888", + "manager": get_managers_address(2), + }, + ] + ) except VirtualMachineError as error: - assert 'OLD_MANAGER_HAS_NO_ROLE' in error.message + assert "OLD_MANAGER_HAS_NO_ROLE" in error.message diff --git a/utils/test/simple_dvt_helpers.py b/utils/test/simple_dvt_helpers.py index 3f3b7a28..79c2eb0d 100644 --- a/utils/test/simple_dvt_helpers.py +++ b/utils/test/simple_dvt_helpers.py @@ -12,16 +12,16 @@ MIN_OPS_CNT = 3 -def get_operator_name(n: int): - return f"Name {n}" +def get_operator_name(id: int, group: int = 0): + return f"OP-{group}-{id}" -def get_operator_address(id: int): - return f"0x111{id:037x}" +def get_operator_address(id: int, group: int = 0): + return f"0x11{group:05x}{id:033x}" -def get_managers_address(id: int): - return f"0x222{id:037x}" +def get_managers_address(id: int, group: int = 0): + return f"0x22{group:05x}{id:033x}" def fill_simple_dvt_ops(stranger, min_ops_cnt=MIN_OPS_CNT): @@ -127,8 +127,8 @@ def simple_dvt_add_node_operators(simple_dvt, stranger, input_params=[]): node_operators_count_before = simple_dvt.getNodeOperatorsCount() # input_params = [ - # (OPERATOR_NAMES[0], REWARD_ADDRESSES[0], MANAGERS[0]), - # (OPERATOR_NAMES[1], REWARD_ADDRESSES[1], MANAGERS[1]), + # (get_operator_address(0), get_operator_address(0), get_managers_address(0)), + # (get_operator_address(1), get_operator_address(1), get_managers_address(1)), # ] if len(input_params) > 0: calldata = _encode_calldata( From 545d17b40fb13bf6cfb3be9f10c8af2fe2cf4d0f Mon Sep 17 00:00:00 2001 From: KRogLA Date: Thu, 15 Feb 2024 02:53:08 +0100 Subject: [PATCH 49/68] fix: permission test --- scripts/vote_simple_dvt.py | 4 +- tests/regression/test_permissions.py | 10 +++- tests/test_vote_simple_dvt.py | 71 ++++++++++++++++++++++++++-- utils/test/simple_dvt_helpers.py | 2 +- 4 files changed, 78 insertions(+), 9 deletions(-) diff --git a/scripts/vote_simple_dvt.py b/scripts/vote_simple_dvt.py index 376348d0..07e2b0d5 100644 --- a/scripts/vote_simple_dvt.py +++ b/scripts/vote_simple_dvt.py @@ -252,7 +252,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra # VI. Update Oracle Report Sanity Checker parameters # ( - "19) Grant MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE to the Lido DAO Agent on OracleReportSanityChecker contract", + "19) Grant MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE to the Lido DAO Agent on OracleReportSanityChecker contract", agent_forward( [ encode_oz_grant_role( @@ -264,7 +264,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "20) Grant MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE to the Lido DAO Agent on OracleReportSanityChecker contract", + "20) Grant MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE to the Lido DAO Agent on OracleReportSanityChecker contract", agent_forward( [ encode_oz_grant_role( diff --git a/tests/regression/test_permissions.py b/tests/regression/test_permissions.py index b8f2c265..d3a29773 100644 --- a/tests/regression/test_permissions.py +++ b/tests/regression/test_permissions.py @@ -1,6 +1,7 @@ """ Tests for permissions setup """ + import pytest import os @@ -165,8 +166,8 @@ def protocol_permissions(): "ANNUAL_BALANCE_INCREASE_LIMIT_MANAGER_ROLE": [], "SHARE_RATE_DEVIATION_LIMIT_MANAGER_ROLE": [], "MAX_VALIDATOR_EXIT_REQUESTS_PER_REPORT_ROLE": [], - "MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE": [], - "MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE": [], + "MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE": [contracts.agent], + "MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE": [contracts.agent], "REQUEST_TIMESTAMP_MARGIN_MANAGER_ROLE": [], "MAX_POSITIVE_TOKEN_REBASE_MANAGER_ROLE": [], }, @@ -333,6 +334,11 @@ def test_protocol_permissions(protocol_permissions): if role in aragon_acl_active_permissions[contract_address] else [] ) + + # temp ugly hack to exclude parametrized role members (OP managers) for SIMPLE_DVT + if contract_address == SIMPLE_DVT and role == "MANAGE_SIGNING_KEYS": + current_holders = [h for h in current_holders if h == EASYTRACK_EVMSCRIPT_EXECUTOR] + assert len(current_holders) == len( holders ), "number of {} role holders in contract {} mismatched expected {} , actual {} ".format( diff --git a/tests/test_vote_simple_dvt.py b/tests/test_vote_simple_dvt.py index b467194f..83e7de3e 100644 --- a/tests/test_vote_simple_dvt.py +++ b/tests/test_vote_simple_dvt.py @@ -6,13 +6,17 @@ from typing import List from scripts.vote_simple_dvt import start_vote from brownie import interface, ZERO_ADDRESS, reverts, web3, accounts, convert, network +from tests.regression.test_permissions import active_aragon_roles, protocol_permissions from utils.test.event_validators.aragon import validate_app_update_event, validate_push_to_repo_event from utils.test.event_validators.common import validate_events_chain from utils.test.event_validators.staking_router import StakingModuleItem, validate_staking_module_added_event +from utils.test.simple_dvt_helpers import fill_simple_dvt_ops, get_managers_address from utils.test.tx_tracing_helpers import * -from utils.config import contracts, LDO_HOLDER_ADDRESS_FOR_TESTS, network_name - -from configs.config_mainnet import ( +from utils.config import ( + contracts, + LDO_HOLDER_ADDRESS_FOR_TESTS, + network_name, + SIMPLE_DVT, SIMPLE_DVT_IMPL, SIMPLE_DVT_ARAGON_APP_NAME, SIMPLE_DVT_ARAGON_APP_ID, @@ -72,7 +76,7 @@ simple_dvt_semantic_version = (1, 0, 0) -def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_decoding, ldo_holder): +def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_decoding, ldo_holder, protocol_permissions): simple_dvt = contracts.simple_dvt kernel = contracts.kernel burner = contracts.burner @@ -414,6 +418,8 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco validate_max_extra_data_list_items_count_event(evs[20], 4) validate_max_operators_per_extra_data_item_count_event(evs[21], 50) + validate_manage_keys_role_members(stranger, protocol_permissions) + def has_permission(permission: Permission, how: List[int]) -> bool: return contracts.acl.hasPermission["address,address,bytes32,uint[]"]( @@ -475,3 +481,60 @@ def validate_simple_dvt_intialize_event(event: EventDict): assert event["StuckPenaltyDelayChanged"]["stuckPenaltyDelay"] == SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY assert event["LocatorContractSet"]["locatorAddress"] == contracts.lido_locator.address assert event["StakingModuleTypeSet"]["moduleType"] == SIMPLE_DVT_MODULE_TYPE + + +def validate_manage_keys_role_members(stranger, protocol_permissions): + # add 5 NOs + fill_simple_dvt_ops(stranger, 5) + # collect managers + sdvt_managers = [get_managers_address(i) for i in range(5)] + aragon_acl_active_permissions = active_aragon_roles(protocol_permissions) + + for contract_address, permissions_config in protocol_permissions.items(): + if contract_address != SIMPLE_DVT: + continue + + print("Contract: {0} {1}".format(contract_address, permissions_config["contract_name"])) + + abi_roles_list = [ + method for method in permissions_config["contract"].signatures.keys() if method.endswith("_ROLE") + ] + + # add MANAGE_SIGNING_KEYS to the list of roles + abi_roles_list.append("MANAGE_SIGNING_KEYS") + # add new managers to expected list of role members + permissions_config["roles"]["MANAGE_SIGNING_KEYS"].extend(sdvt_managers) + + roles = permissions_config["roles"] + + assert len(abi_roles_list) == len( + roles.keys() + ), "Contract {} . number of roles doesn't match. expected {} actual {}".format( + permissions_config["contract_name"], abi_roles_list, roles.keys() + ) + for role in set(permissions_config["roles"].keys()): + assert role in abi_roles_list, "no {} described for contract {}".format( + role, permissions_config["contract_name"] + ) + + for role, holders in permissions_config["roles"].items(): + current_holders = ( + aragon_acl_active_permissions[contract_address][role] + if role in aragon_acl_active_permissions[contract_address] + else [] + ) + assert len(current_holders) == len( + holders + ), "number of {} role holders in contract {} mismatched expected {} , actual {} ".format( + role, permissions_config["contract_name"], holders, current_holders + ) + + for holder in holders: + assert holder in current_holders, "Entity {} has no role {} at {}".format( + holder, role, permissions_config["contract_name"] + ) + + for holder in current_holders: + assert holder in holders, "Unexpected entity {} has role {} at {}".format( + holder, role, permissions_config["contract_name"] + ) diff --git a/utils/test/simple_dvt_helpers.py b/utils/test/simple_dvt_helpers.py index 79c2eb0d..aaeb06e0 100644 --- a/utils/test/simple_dvt_helpers.py +++ b/utils/test/simple_dvt_helpers.py @@ -163,7 +163,7 @@ def simple_dvt_add_keys(simple_dvt, node_operator_id, keys_count=1): total_signing_keys_count_before = simple_dvt.getTotalSigningKeyCount(node_operator_id) unused_signing_keys_count_before = simple_dvt.getUnusedSigningKeyCount(node_operator_id) - node_operator_before = simple_dvt.getNodeOperator(node_operator_id, True) + node_operator_before = simple_dvt.getNodeOperator(node_operator_id, False) tx = simple_dvt.addSigningKeys( node_operator_id, From 7821293d3011c8adcd7bf9c8747f249efdc838ca Mon Sep 17 00:00:00 2001 From: KRogLA Date: Thu, 15 Feb 2024 02:56:28 +0100 Subject: [PATCH 50/68] fix: test refactor --- .../test_accounting_oracle_extra_data.py | 2 +- tests/regression/test_all_round_happy_path.py | 4 +-- tests/regression/test_easy_track_factories.py | 28 +++++++++---------- tests/regression/test_node_operators_flow.py | 2 +- .../test_staking_module_happy_path.py | 4 +-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/regression/test_accounting_oracle_extra_data.py b/tests/regression/test_accounting_oracle_extra_data.py index dba97b07..dcdf0808 100644 --- a/tests/regression/test_accounting_oracle_extra_data.py +++ b/tests/regression/test_accounting_oracle_extra_data.py @@ -11,7 +11,7 @@ def extra_data_service(): def get_exited_count(node_operator_id): - no = contracts.node_operators_registry.getNodeOperator(node_operator_id, True) + no = contracts.node_operators_registry.getNodeOperator(node_operator_id, False) return no["totalExitedValidators"] diff --git a/tests/regression/test_all_round_happy_path.py b/tests/regression/test_all_round_happy_path.py index ae0fa5ba..ed70cd14 100644 --- a/tests/regression/test_all_round_happy_path.py +++ b/tests/regression/test_all_round_happy_path.py @@ -134,7 +134,7 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): penalized_node_operator_ids_nor = [] for i in range(nor_operators_count): - no = contracts.node_operators_registry.getNodeOperator(i, True) + no = contracts.node_operators_registry.getNodeOperator(i, False) is_node_operator_penalized = contracts.node_operators_registry.isOperatorPenalized(i) if is_node_operator_penalized: penalized_node_operator_ids_nor.append(i) @@ -143,7 +143,7 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): penalized_node_operator_ids_sdvt = [] for i in range(sdvt_operators_count): - no = contracts.simple_dvt.getNodeOperator(i, True) + no = contracts.simple_dvt.getNodeOperator(i, False) is_node_operator_penalized = contracts.simple_dvt.isOperatorPenalized(i) if is_node_operator_penalized: penalized_node_operator_ids_sdvt.append(i) diff --git a/tests/regression/test_easy_track_factories.py b/tests/regression/test_easy_track_factories.py index 87c0c020..dba1b19d 100644 --- a/tests/regression/test_easy_track_factories.py +++ b/tests/regression/test_easy_track_factories.py @@ -177,8 +177,8 @@ def test_add_node_operators(): def test_node_operators_activations(): - assert contracts.simple_dvt.getNodeOperator(1, True)[0] - assert contracts.simple_dvt.getNodeOperator(2, True)[0] + assert contracts.simple_dvt.getNodeOperator(1, False)[0] + assert contracts.simple_dvt.getNodeOperator(2, False)[0] deactivate_node_operator( [ @@ -193,8 +193,8 @@ def test_node_operators_activations(): ] ) - assert not contracts.simple_dvt.getNodeOperator(1, True)[0] - assert not contracts.simple_dvt.getNodeOperator(2, True)[0] + assert not contracts.simple_dvt.getNodeOperator(1, False)[0] + assert not contracts.simple_dvt.getNodeOperator(2, False)[0] # ActivateNodeOperators activate_node_operators( @@ -210,13 +210,13 @@ def test_node_operators_activations(): ] ) - assert contracts.simple_dvt.getNodeOperator(1, True)[0] - assert contracts.simple_dvt.getNodeOperator(2, True)[0] + assert contracts.simple_dvt.getNodeOperator(1, False)[0] + assert contracts.simple_dvt.getNodeOperator(2, False)[0] def test_set_vetted_validators_limits(): - op_1 = contracts.simple_dvt.getNodeOperator(1, True) - op_2 = contracts.simple_dvt.getNodeOperator(2, True) + op_1 = contracts.simple_dvt.getNodeOperator(1, False) + op_2 = contracts.simple_dvt.getNodeOperator(2, False) new_vetted_keys_1 = random.randint(0, op_1[5]) new_vetted_keys_2 = random.randint(0, op_2[5]) @@ -234,8 +234,8 @@ def test_set_vetted_validators_limits(): ] ) - assert contracts.simple_dvt.getNodeOperator(1, True)[3] == new_vetted_keys_1 - assert contracts.simple_dvt.getNodeOperator(2, True)[3] == new_vetted_keys_2 + assert contracts.simple_dvt.getNodeOperator(1, False)[3] == new_vetted_keys_1 + assert contracts.simple_dvt.getNodeOperator(2, False)[3] == new_vetted_keys_2 def test_set_node_operator_names(): @@ -281,8 +281,8 @@ def test_set_node_operator_reward_addresses(): ] ) - assert contracts.simple_dvt.getNodeOperator(1, True)[2] == address_1 - assert contracts.simple_dvt.getNodeOperator(2, True)[2] == address_2 + assert contracts.simple_dvt.getNodeOperator(1, False)[2] == address_1 + assert contracts.simple_dvt.getNodeOperator(2, False)[2] == address_2 def test_update_target_validator_limits(): @@ -302,8 +302,8 @@ def test_update_target_validator_limits(): ] ) - # assert contracts.simple_dvt.getNodeOperator(1, True)[1] == address_1 - # assert contracts.simple_dvt.getNodeOperator(2, True)[2] == address_2 + # assert contracts.simple_dvt.getNodeOperator(1, False)[1] == address_1 + # assert contracts.simple_dvt.getNodeOperator(2, False)[2] == address_2 def test_transfer_node_operator_manager(): diff --git a/tests/regression/test_node_operators_flow.py b/tests/regression/test_node_operators_flow.py index 6cd83ee5..76995f28 100644 --- a/tests/regression/test_node_operators_flow.py +++ b/tests/regression/test_node_operators_flow.py @@ -7,7 +7,7 @@ parse_pubkeys_batch, parse_signatures_batch, random_pubkeys_batch, - random_signatures_batch + random_signatures_batch, ) from utils.test.node_operators_helpers import ( assert_signing_key, diff --git a/tests/regression/test_staking_module_happy_path.py b/tests/regression/test_staking_module_happy_path.py index ea0a0177..fcac2ebb 100644 --- a/tests/regression/test_staking_module_happy_path.py +++ b/tests/regression/test_staking_module_happy_path.py @@ -45,7 +45,7 @@ def calc_no_rewards(nor, no_id, minted_shares): def set_staking_limit(nor, ops_ids, keys_count, impersonated_voting): for op_index in ops_ids: - no = nor.getNodeOperator(op_index, True) + no = nor.getNodeOperator(op_index, False) if not no["active"]: continue cur_deposited_keys = no["totalDepositedValidators"] @@ -756,7 +756,7 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e assert no3_deposited_keys_before != no3_deposited_keys_after for op_index in (no1_id, no2_id, no3_id): - no = staking_module.getNodeOperator(op_index, True) + no = staking_module.getNodeOperator(op_index, False) staking_module.setNodeOperatorStakingLimit( op_index, no["totalDepositedValidators"] + 10, {"from": impersonated_voting} ) From bdf50f1887bfad1e25e2ffc64e52daca9d386c12 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Thu, 15 Feb 2024 04:23:43 +0100 Subject: [PATCH 51/68] fix: vote test --- tests/test_vote_simple_dvt.py | 99 +++++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 3 deletions(-) diff --git a/tests/test_vote_simple_dvt.py b/tests/test_vote_simple_dvt.py index 83e7de3e..bfb8140c 100644 --- a/tests/test_vote_simple_dvt.py +++ b/tests/test_vote_simple_dvt.py @@ -3,14 +3,24 @@ """ +import pytest from typing import List from scripts.vote_simple_dvt import start_vote from brownie import interface, ZERO_ADDRESS, reverts, web3, accounts, convert, network from tests.regression.test_permissions import active_aragon_roles, protocol_permissions +from utils.test.deposits_helpers import fill_deposit_buffer from utils.test.event_validators.aragon import validate_app_update_event, validate_push_to_repo_event from utils.test.event_validators.common import validate_events_chain from utils.test.event_validators.staking_router import StakingModuleItem, validate_staking_module_added_event -from utils.test.simple_dvt_helpers import fill_simple_dvt_ops, get_managers_address +from utils.test.oracle_report_helpers import oracle_report +from utils.test.extra_data import ExtraDataService +from utils.test.oracle_report_helpers import oracle_report +from utils.test.simple_dvt_helpers import ( + fill_simple_dvt_ops, + fill_simple_dvt_ops_vetted_keys, + get_managers_address, + get_operator_address, +) from utils.test.tx_tracing_helpers import * from utils.config import ( contracts, @@ -76,7 +86,21 @@ simple_dvt_semantic_version = (1, 0, 0) -def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_decoding, ldo_holder, protocol_permissions): +@pytest.fixture() +def extra_data_service(): + return ExtraDataService() + + +def test_vote( + helpers, + accounts, + vote_ids_from_env, + stranger, + bypass_events_decoding, + ldo_holder, + protocol_permissions, + extra_data_service, +): simple_dvt = contracts.simple_dvt kernel = contracts.kernel burner = contracts.burner @@ -418,6 +442,8 @@ def test_vote(helpers, accounts, vote_ids_from_env, stranger, bypass_events_deco validate_max_extra_data_list_items_count_event(evs[20], 4) validate_max_operators_per_extra_data_item_count_event(evs[21], 50) + # scenario tests + validate_accounting_oracle_reports(simple_dvt, extra_data_service, stranger) validate_manage_keys_role_members(stranger, protocol_permissions) @@ -483,10 +509,77 @@ def validate_simple_dvt_intialize_event(event: EventDict): assert event["StakingModuleTypeSet"]["moduleType"] == SIMPLE_DVT_MODULE_TYPE +def get_exited_stuck_count(module, node_operator_id): + no = module.getNodeOperator(node_operator_id, False) + + new_exited = no["totalExitedValidators"] + if new_exited < no["totalDepositedValidators"]: + new_exited += 1 + + new_stuck = 1 + if new_exited + new_stuck > no["totalDepositedValidators"]: + new_stuck = 0 + return (new_exited, new_stuck) + + +def validate_accounting_oracle_reports(sdvt, extra_data_service, stranger): + module_id_sdvt = 2 + + # report when SimpleDVT has no node operators + oracle_report() + + # add 1 NO and 5 keys + fill_simple_dvt_ops_vetted_keys(stranger, 1, 5) + no_id = 0 + no_reward_address = get_operator_address(no_id) + + # report when SimpleDVT has just added, but not deposited keys + oracle_report() + + # fill the deposit buffer for 5 keys + fill_deposit_buffer(5) + contracts.lido.deposit(5, module_id_sdvt, "0x", {"from": contracts.deposit_security_module.address}) + summary = sdvt.getNodeOperatorSummary(no_id) + assert summary["totalDepositedValidators"] == 5 + assert summary["totalExitedValidators"] == 0 + assert summary["stuckValidatorsCount"] == 0 + + # report when SimpleDVT has some deposited keys + no_balance_before = contracts.lido.balanceOf(no_reward_address) + oracle_report() + reward1 = contracts.lido.balanceOf(no_reward_address) - no_balance_before + assert reward1 > 0 + + # report 1 exited and 1 stuck keys + item_count = 2 + extra_data = extra_data_service.collect( + {(module_id_sdvt, 0): 1}, # stuck + {(module_id_sdvt, 0): 1}, # exited + item_count, + 1, # 1 item + ) + + no_balance_before = contracts.lido.balanceOf(no_reward_address) + oracle_report( + extraDataFormat=1, + extraDataHash=extra_data.data_hash, + extraDataItemsCount=item_count, + extraDataList=extra_data.extra_data, + ) + summary = sdvt.getNodeOperatorSummary(no_id) + assert summary["totalDepositedValidators"] == 5 + assert summary["totalExitedValidators"] == 1 + assert summary["stuckValidatorsCount"] == 1 + + reward2 = contracts.lido.balanceOf(no_reward_address) - no_balance_before + # expected reward is less than 1/2 of the previous reward (because of 1 exited and 1 stuck) + assert reward2 > 0 and reward2 < reward1 // 2 + + def validate_manage_keys_role_members(stranger, protocol_permissions): # add 5 NOs fill_simple_dvt_ops(stranger, 5) - # collect managers + # collect 5 expected managers sdvt_managers = [get_managers_address(i) for i in range(5)] aragon_acl_active_permissions = active_aragon_roles(protocol_permissions) From 5ec7fdebdb5c213551431cae49814758cb87db8e Mon Sep 17 00:00:00 2001 From: KRogLA Date: Thu, 15 Feb 2024 05:18:03 +0100 Subject: [PATCH 52/68] fix: vote description --- scripts/vote_simple_dvt.py | 98 ++++++++++++++++++++++++++++------- tests/test_vote_simple_dvt.py | 6 +-- 2 files changed, 82 insertions(+), 22 deletions(-) diff --git a/scripts/vote_simple_dvt.py b/scripts/vote_simple_dvt.py index 07e2b0d5..cbb98493 100644 --- a/scripts/vote_simple_dvt.py +++ b/scripts/vote_simple_dvt.py @@ -37,11 +37,8 @@ from utils.permissions import ( encode_permission_create, encode_permission_grant, - encode_permission_revoke, - encode_permission_grant_p, encode_oz_grant_role, ) -from utils.permission_parameters import Param, SpecialArgumentID, encode_argument_value_if, ArgumentValue, Op from utils.easy_track import ( add_evmscript_factory, create_permissions, @@ -59,7 +56,72 @@ description = """ -Deploy SimpleDVT +### I. Create new Aragon DAO Application Repo for Simple DVT + +1. **Create new Aragon DAO Application Repo for Simple DVT app** with parameters: + * Name: `simple-dvt` + * Version: `1.0.0` + * Implementation address: [0x8538930c385C0438A357d2c25CB3eAD95Ab6D8ed](https://etherscan.io/address/0x8538930c385C0438A357d2c25CB3eAD95Ab6D8ed) + * Content IPFS URI: [ipfs:QmaSSujHCGcnFuetAPGwVW5BegaMBvn5SCsgi3LSfvraSo](https://ipfs.io/ipfs/QmaSSujHCGcnFuetAPGwVW5BegaMBvn5SCsgi3LSfvraSo) (hex-encoded: `0x697066733a516d615353756a484347636e4675657441504777565735426567614d42766e355343736769334c5366767261536f`) + + +### II. Setup and initialize Simple DVT module as new Aragon app + +2. **Setup Simple DVT module as Aragon DAO app** with the same [contract implementation](https://etherscan.io/address/0x8538930c385C0438A357d2c25CB3eAD95Ab6D8ed) as the NodeOperatorsRegistry. + +3. **Initialize of Simple DVT module** with parameters: + * Module Type = `curated-onchain-v1` (hex-encoded: `0x637572617465642d6f6e636861696e2d76310000000000000000000000000000`) + * Stuck Penalty Delay = `432000` (5 days - the same as in Curated Module) + + +### III. Add Simple DVT module to StakingRouter + +4. **Create and grant permission `STAKING_ROUTER_ROLE` on Simple DVT module for StakingRouter**. This is necessary for the Simple DVT module to function as a staking module + +5. **Grant `REQUEST_BURN_SHARES_ROLE` on Burner for Simple DVT module**. This role is required for ability to burn stETH tokens in case of operator’s penalties, you can read more [here](https://docs.lido.fi/guides/protocol-levers/#burning-steth-tokens) + +6. **Add Simple DVT module to StakingRouter**. This action finally sets up new Simple DVT module contract to the [StakingRouter](https://etherscan.io/address/0xFdDf38947aFB03C621C71b06C9C70bce73f12999) as the second module + + +### IV. Grant permissions to EasyTrackEVMScriptExecutor to make operational changes to Simple DVT module + +7. **Create and grant permission `MANAGE_NODE_OPERATOR_ROLE` on Simple DVT module for [EasyTrackEVMScriptExecutor](https://etherscan.io/address/0xFE5986E06210aC1eCC1aDCafc0cc7f8D63B3F977)**. This one and next 3 actions are needed to setup permissions required for [EasyTrack](https://etherscan.io/address/0xF0211b7660680B49De1A7E9f25C65660F0a13Fea) to manage operational tasks on the Simple DVT module + +8. **Create and grant permission `SET_NODE_OPERATOR_LIMIT_ROLE` on Simple DVT module for EasyTrackEVMScriptExecutor** + +9. **Create and grant permission `MANAGE_SIGNING_KEYS` on Simple DVT module for EasyTrackEVMScriptExecutor** + +10. **Grant `STAKING_ROUTER_ROLE` on Simple DVT module for EasyTrackEVMScriptExecutor** + + +### V. Add Easy Track EVM script factories for Simple DVT module to EasyTrack registry + +11. **Add AddNodeOperators EVM script factory** with address [0xcAa3AF7460E83E665EEFeC73a7a542E5005C9639](https://etherscan.io/address/0xcAa3AF7460E83E665EEFeC73a7a542E5005C9639) + +12. **Add ActivateNodeOperators EVM script factory** with address [0xCBb418F6f9BFd3525CE6aADe8F74ECFEfe2DB5C8](https://etherscan.io/address/0xCBb418F6f9BFd3525CE6aADe8F74ECFEfe2DB5C8) + +13. **Add DeactivateNodeOperators EVM script factory** with address [0x8B82C1546D47330335a48406cc3a50Da732672E7](https://etherscan.io/address/0x8B82C1546D47330335a48406cc3a50Da732672E7) + +14. **Add SetVettedValidatorsLimits EVM script factory** with address [0xD75778b855886Fc5e1eA7D6bFADA9EB68b35C19D](https://etherscan.io/address/0xD75778b855886Fc5e1eA7D6bFADA9EB68b35C19D) + +15. **Add UpdateTargetValidatorLimits EVM script factory** with address [0x41CF3DbDc939c5115823Fba1432c4EC5E7bD226C](https://etherscan.io/address/0x41CF3DbDc939c5115823Fba1432c4EC5E7bD226C) + +16. **Add SetNodeOperatorNames EVM script factory** with address [0x7d509BFF310d9460b1F613e4e40d342201a83Ae4](https://etherscan.io/address/0x7d509BFF310d9460b1F613e4e40d342201a83Ae4) + +17. **Add SetNodeOperatorRewardAddresses EVM script factory** with address [0x589e298964b9181D9938B84bB034C3BB9024E2C0](https://etherscan.io/address/0x589e298964b9181D9938B84bB034C3BB9024E2C0) + +18. **Add ChangeNodeOperatorManagers EVM script factory** with address [0xE31A0599A6772BCf9b2bFc9e25cf941e793c9a7D](https://etherscan.io/address/0xE31A0599A6772BCf9b2bFc9e25cf941e793c9a7D) + + +### VI. Update Oracle Report Sanity Checker parameters + +19. **Grant `MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE` to the [Lido DAO Agent](https://etherscan.io/address/0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c) on [OracleReportSanityChecker](https://etherscan.io/address/0x9305c1Dbfe22c12c66339184C0025d7006f0f1cC) contract**. This makes possible to set sanity checker parameter values (see next actions) + +20. **Grant `MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE` to the `Lido DAO Agent` on `OracleReportSanityChecker` contract** + +22. **Set `maxAccountingExtraDataListItemsCount` sanity checker parameter to 4**. This will allow to report extra data for 2 modules in 3rd phase of the accounting oracle report simultaneously + +22. **Set `maxNodeOperatorsPerExtraDataItemCount` sanity checker parameter to `50`**. Limiting the number of operators guarantees a successful oracle report within the block gas limit """ @@ -68,10 +130,10 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra vote_desc_items, call_script_items = zip( # - # I. Create new Aragon DAO Application Repo for SimpleDVT + # I. Create new Aragon DAO Application Repo for Simple DVT # ( - "1) Create new Repo for SimpleDVT app", + "1) Create new Aragon DAO Application Repo for Simple DVT app", create_new_app_repo( name=create_simple_dvt_app["name"], manager=contracts.voting, @@ -81,14 +143,14 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), # - # II. Setup and initialize SimpleDVT module as new Aragon app + # II. Setup and initialize Simple DVT module as new Aragon app # ( - "2) Setup SimpleDVT as Aragon DAO app", + "2) Setup Simple DVT module as Aragon DAO app", update_app_implementation(create_simple_dvt_app["id"], create_simple_dvt_app["new_address"]), ), ( - "3) Initialize SimpleDVT module", + "3) Initialize of Simple DVT module", ( contracts.simple_dvt.address, contracts.simple_dvt.initialize.encode_input( @@ -99,10 +161,10 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), # - # III. Add SimpleDVT module to Staking Router + # III. Add Simple DVT module to Staking Router # ( - "4) Create and grant permission STAKING_ROUTER_ROLE on SimpleDVT module for StakingRouter", + "4) *Create and grant permission STAKING_ROUTER_ROLE on Simple DVT module for StakingRouter", encode_permission_create( entity=contracts.staking_router, target_app=contracts.simple_dvt, @@ -111,7 +173,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "5) Grant REQUEST_BURN_SHARES_ROLE on Burner for SimpleDVT module", + "5) Grant REQUEST_BURN_SHARES_ROLE on Burner for Simple DVT module", agent_forward( [ encode_oz_grant_role( @@ -123,7 +185,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "6) Add SimpleDVT module to StakingRouter", + "6) Add Simple DVT module to StakingRouter", agent_forward( [ ( @@ -140,10 +202,10 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), # - # IV. Grant permissions to EasyTrackEVMScriptExecutor to make operational changes to SimpleDVT module + # IV. Grant permissions to EasyTrackEVMScriptExecutor to make operational changes to Simple DVT module # ( - "7) Create and grant permission MANAGE_NODE_OPERATOR_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor", + "7) Create and grant permission MANAGE_NODE_OPERATOR_ROLE on Simple DVT module for EasyTrackEVMScriptExecutor", encode_permission_create( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, target_app=contracts.simple_dvt, @@ -152,7 +214,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "8) Create and grant permission SET_NODE_OPERATOR_LIMIT_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor", + "8) Create and grant permission SET_NODE_OPERATOR_LIMIT_ROLE on Simple DVT module for EasyTrackEVMScriptExecutor", encode_permission_create( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, target_app=contracts.simple_dvt, @@ -161,7 +223,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "9) Create and grant permission MANAGE_SIGNING_KEYS on SimpleDVT module for EasyTrackEVMScriptExecutor", + "9) Create and grant permission MANAGE_SIGNING_KEYS on Simple DVT module for EasyTrackEVMScriptExecutor", encode_permission_create( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, target_app=contracts.simple_dvt, @@ -170,7 +232,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "10) Grant STAKING_ROUTER_ROLE on SimpleDVT module for EasyTrackEVMScriptExecutor", + "10) Grant STAKING_ROUTER_ROLE on Simple DVT module for EasyTrackEVMScriptExecutor", encode_permission_grant( target_app=contracts.simple_dvt, permission_name="STAKING_ROUTER_ROLE", diff --git a/tests/test_vote_simple_dvt.py b/tests/test_vote_simple_dvt.py index bfb8140c..ab232cba 100644 --- a/tests/test_vote_simple_dvt.py +++ b/tests/test_vote_simple_dvt.py @@ -114,10 +114,9 @@ def test_vote( assert kernel.getApp(kernel.APP_BASES_NAMESPACE(), SIMPLE_DVT_ARAGON_APP_ID) == ZERO_ADDRESS assert not burner.hasRole(kernel.APP_BASES_NAMESPACE(), simple_dvt.address) + # check resolve failure assert not network.web3.ens.resolve(simple_dvt_repo_ens) - # TODO: check absence of repo ? - evm_script_factories_before = easy_track.getEVMScriptFactories() add_node_operators_evm_script_factory = EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY @@ -300,8 +299,7 @@ def test_vote( metadata = find_metadata_by_vote_id(vote_id) print("metadata", metadata) - # TODO fix description - # assert get_lido_vote_cid_from_str(metadata) == "bafkreibugpzhp7nexxg7c6jpmmszikvaj2vscxw426zewa6uyv3z5y6ak4" + assert get_lido_vote_cid_from_str(metadata) == "bafkreietqdaevzjnhog4ui4a23iswak2haom4zfobeueefy3lmwkxnr2hq" display_voting_events(vote_tx) From 20ff5e83171994829a58c85056c1ced297acf24b Mon Sep 17 00:00:00 2001 From: KRogLA Date: Thu, 15 Feb 2024 10:23:42 +0100 Subject: [PATCH 53/68] fix: et tests --- tests/regression/test_easy_track.py | 121 +------------ tests/regression/test_easy_track_factories.py | 161 ++++++++---------- utils/test/easy_track_helpers.py | 78 +++------ utils/test/simple_dvt_helpers.py | 59 +------ 4 files changed, 114 insertions(+), 305 deletions(-) diff --git a/tests/regression/test_easy_track.py b/tests/regression/test_easy_track.py index b51b1fdc..1f047b6e 100644 --- a/tests/regression/test_easy_track.py +++ b/tests/regression/test_easy_track.py @@ -20,7 +20,7 @@ simple_dvt_add_keys, simple_dvt_add_node_operators, ) -from utils.test.easy_track_helpers import _encode_calldata +from utils.test.easy_track_helpers import _encode_calldata, create_and_enact_motion MANAGE_SIGNING_KEYS = "0x75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee" @@ -35,8 +35,6 @@ def test_increase_nop_staking_limit( trusted_caller = accounts.at(node_operator["rewardAddress"], force=True) new_staking_limit = node_operator["totalVettedValidators"] + 1 - motions_before = contracts.easy_track.getMotions() - if node_operator["totalAddedValidators"] < new_staking_limit: contracts.node_operators_registry.addSigningKeys( no_id, @@ -48,19 +46,7 @@ def test_increase_nop_staking_limit( calldata = _encode_calldata("(uint256,uint256)", [no_id, new_staking_limit]) - tx = contracts.easy_track.createMotion(factory, calldata, {"from": trusted_caller}) - - motions = contracts.easy_track.getMotions() - assert len(motions) == len(motions_before) + 1 - - chain.sleep(60 * 60 * 24 * 3) - chain.mine() - - contracts.easy_track.enactMotion( - motions[-1][0], - tx.events["MotionCreated"]["_evmScriptCallData"], - {"from": stranger}, - ) + create_and_enact_motion(contracts.easy_track, trusted_caller, factory, calldata, stranger) updated_node_operator = contracts.node_operators_registry.getNodeOperator(no_id, False) @@ -104,21 +90,7 @@ def test_simple_dvt_set_vetted_validators_limits( calldata = _encode_calldata("((uint256,uint256)[])", [[(no_id, new_staking_limit)]]) - motions_before = contracts.easy_track.getMotions() - - tx = contracts.easy_track.createMotion(factory, calldata, {"from": trusted_caller}) - - motions = contracts.easy_track.getMotions() - assert len(motions) == len(motions_before) + 1 - - chain.sleep(60 * 60 * 24 * 3) - chain.mine() - - contracts.easy_track.enactMotion( - motions[-1][0], - tx.events["MotionCreated"]["_evmScriptCallData"], - {"from": stranger}, - ) + create_and_enact_motion(contracts.easy_track, trusted_caller, factory, calldata, stranger) updated_node_operator = contracts.simple_dvt.getNodeOperator(no_id, False) @@ -158,21 +130,7 @@ def test_simple_dvt_activate_deactivate_operators( # deactivating calldata = _encode_calldata("((uint256,address)[])", [[(no_id, op_manager)]]) - motions_before = contracts.easy_track.getMotions() - - tx = contracts.easy_track.createMotion(factory_deactivate, calldata, {"from": trusted_caller}) - - motions = contracts.easy_track.getMotions() - assert len(motions) == len(motions_before) + 1 - - chain.sleep(60 * 60 * 24 * 3) - chain.mine() - - contracts.easy_track.enactMotion( - motions[-1][0], - tx.events["MotionCreated"]["_evmScriptCallData"], - {"from": stranger}, - ) + create_and_enact_motion(contracts.easy_track, trusted_caller, factory_deactivate, calldata, stranger) is_active = contracts.simple_dvt.getNodeOperatorIsActive(no_id) is_manager = contracts.simple_dvt.canPerform( @@ -186,21 +144,7 @@ def test_simple_dvt_activate_deactivate_operators( # activating # calldata = _encode_calldata("((uint256,address)[])", [[(no_id, op_manager)]]) - motions_before = contracts.easy_track.getMotions() - - tx = contracts.easy_track.createMotion(factory_activate, calldata, {"from": trusted_caller}) - - motions = contracts.easy_track.getMotions() - assert len(motions) == len(motions_before) + 1 - - chain.sleep(60 * 60 * 24 * 3) - chain.mine() - - contracts.easy_track.enactMotion( - motions[-1][0], - tx.events["MotionCreated"]["_evmScriptCallData"], - {"from": stranger}, - ) + create_and_enact_motion(contracts.easy_track, trusted_caller, factory_activate, calldata, stranger) is_active = contracts.simple_dvt.getNodeOperatorIsActive(no_id) is_manager = contracts.simple_dvt.canPerform( @@ -245,27 +189,8 @@ def test_simple_dvt_set_operator_name_reward_address( calldata_name = _encode_calldata("((uint256,string)[])", [[(no_id, op_name_upd)]]) calldata_addr = _encode_calldata("((uint256,address)[])", [[(no_id, op_addr_upd)]]) - motions_before = contracts.easy_track.getMotions() - - tx1 = contracts.easy_track.createMotion(factory_name, calldata_name, {"from": trusted_caller}) - tx2 = contracts.easy_track.createMotion(factory_addr, calldata_addr, {"from": trusted_caller}) - - motions = contracts.easy_track.getMotions() - assert len(motions) == len(motions_before) + 2 - - chain.sleep(60 * 60 * 24 * 3) - chain.mine() - - contracts.easy_track.enactMotion( - motions[-2][0], - tx1.events["MotionCreated"]["_evmScriptCallData"], - {"from": stranger}, - ) - contracts.easy_track.enactMotion( - motions[-1][0], - tx2.events["MotionCreated"]["_evmScriptCallData"], - {"from": stranger}, - ) + create_and_enact_motion(contracts.easy_track, trusted_caller, factory_name, calldata_name, stranger) + create_and_enact_motion(contracts.easy_track, trusted_caller, factory_addr, calldata_addr, stranger) node_operator = contracts.simple_dvt.getNodeOperator(no_id, True) @@ -302,21 +227,7 @@ def test_simple_dvt_set_operator_target_limit( calldata = _encode_calldata("((uint256,bool,uint256)[])", [[(no_id, True, target_limit)]]) - motions_before = contracts.easy_track.getMotions() - - tx = contracts.easy_track.createMotion(factory, calldata, {"from": trusted_caller}) - - motions = contracts.easy_track.getMotions() - assert len(motions) == len(motions_before) + 1 - - chain.sleep(60 * 60 * 24 * 3) - chain.mine() - - contracts.easy_track.enactMotion( - motions[-1][0], - tx.events["MotionCreated"]["_evmScriptCallData"], - {"from": stranger}, - ) + create_and_enact_motion(contracts.easy_track, trusted_caller, factory, calldata, stranger) no_summary = contracts.simple_dvt.getNodeOperatorSummary(no_id) @@ -357,21 +268,7 @@ def test_simple_dvt_change_operator_manager( calldata = _encode_calldata("((uint256,address,address)[])", [[(no_id, op_manager, op_manager_upd)]]) - motions_before = contracts.easy_track.getMotions() - - tx = contracts.easy_track.createMotion(factory, calldata, {"from": trusted_caller}) - - motions = contracts.easy_track.getMotions() - assert len(motions) == len(motions_before) + 1 - - chain.sleep(60 * 60 * 24 * 3) - chain.mine() - - contracts.easy_track.enactMotion( - motions[-1][0], - tx.events["MotionCreated"]["_evmScriptCallData"], - {"from": stranger}, - ) + create_and_enact_motion(contracts.easy_track, trusted_caller, factory, calldata, stranger) is_manager = contracts.simple_dvt.canPerform( op_manager, diff --git a/tests/regression/test_easy_track_factories.py b/tests/regression/test_easy_track_factories.py index dba1b19d..ffd4046e 100644 --- a/tests/regression/test_easy_track_factories.py +++ b/tests/regression/test_easy_track_factories.py @@ -1,12 +1,17 @@ import random -from brownie import interface, accounts, chain +from brownie import interface from brownie.exceptions import VirtualMachineError from configs.config_mainnet import * -from utils.config import contracts -from utils.test.easy_track_helpers import _encode_calldata -from utils.test.simple_dvt_helpers import get_managers_address, get_operator_address, get_operator_name +from utils.config import contracts, EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER +from utils.test.easy_track_helpers import _encode_calldata, create_and_enact_motion +from utils.test.simple_dvt_helpers import ( + fill_simple_dvt_ops_keys, + get_managers_address, + get_operator_address, + get_operator_name, +) NODE_OPERATORS = [ @@ -19,44 +24,21 @@ ] -def easy_track_executor(creator, factory, calldata): - tx = contracts.easy_track.createMotion( - factory, - calldata, - {"from": creator}, - ) - - motions = contracts.easy_track.getMotions() - - chain.sleep(60 * 60 * 24 * 3) - chain.mine() - - contracts.easy_track.enactMotion( - motions[-1][0], - tx.events["MotionCreated"]["_evmScriptCallData"], - {"from": accounts[4]}, - ) - - -def add_node_operators(operators): +def add_node_operators(operators, stranger): calldata = _encode_calldata( "(uint256,(string,address,address)[])", [ contracts.simple_dvt.getNodeOperatorsCount(), - [(no["name"], no["address"], no["manager"]) for no in NODE_OPERATORS], + [(no["name"], no["address"], no["manager"]) for no in operators], ], ) factory = interface.AddNodeOperators(EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY) - easy_track_executor( - factory.trustedCaller(), - factory, - calldata, - ) + create_and_enact_motion(contracts.easy_track, EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, factory, calldata, stranger) -def activate_node_operators(operators): +def activate_node_operators(operators, stranger): calldata = _encode_calldata( "((uint256,address)[])", [[(no["id"], no["manager"]) for no in operators]], @@ -64,14 +46,10 @@ def activate_node_operators(operators): factory = interface.ActivateNodeOperators(EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY) - easy_track_executor( - factory.trustedCaller(), - factory, - calldata, - ) + create_and_enact_motion(contracts.easy_track, EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, factory, calldata, stranger) -def deactivate_node_operator(operators): +def deactivate_node_operator(operators, stranger): calldata = _encode_calldata( "((uint256,address)[])", [[(no["id"], no["manager"]) for no in operators]], @@ -79,26 +57,18 @@ def deactivate_node_operator(operators): factory = interface.DeactivateNodeOperators(EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY) - easy_track_executor( - factory.trustedCaller(), - factory, - calldata, - ) + create_and_enact_motion(contracts.easy_track, EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, factory, calldata, stranger) -def set_vetted_validators_limits(operators): +def set_vetted_validators_limits(operators, stranger): calldata = _encode_calldata("((uint256,uint256)[])", [[(no["id"], no["staking_limit"]) for no in operators]]) factory = interface.SetVettedValidatorsLimits(EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY) - easy_track_executor( - factory.trustedCaller(), - factory, - calldata, - ) + create_and_enact_motion(contracts.easy_track, EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, factory, calldata, stranger) -def set_node_operators_names(operators): +def set_node_operators_names(operators, stranger): calldata = _encode_calldata( "((uint256,string)[])", [[(no["id"], no["name"]) for no in operators]], @@ -106,14 +76,10 @@ def set_node_operators_names(operators): factory = interface.SetNodeOperatorNames(EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_NAMES_FACTORY) - easy_track_executor( - factory.trustedCaller(), - factory, - calldata, - ) + create_and_enact_motion(contracts.easy_track, EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, factory, calldata, stranger) -def set_node_operator_reward_addresses(operators): +def set_node_operator_reward_addresses(operators, stranger): calldata = _encode_calldata( "((uint256,address)[])", [[(no["id"], no["address"]) for no in operators]], @@ -121,14 +87,10 @@ def set_node_operator_reward_addresses(operators): factory = interface.SetNodeOperatorRewardAddresses(EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY) - easy_track_executor( - factory.trustedCaller(), - factory, - calldata, - ) + create_and_enact_motion(contracts.easy_track, EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, factory, calldata, stranger) -def update_target_validators_limits(operators): +def update_target_validators_limits(operators, stranger): calldata = _encode_calldata( "((uint256,bool,uint256)[])", [[(no["id"], no["is_target_limit_active"], no["target_limit"]) for no in operators]], @@ -136,14 +98,10 @@ def update_target_validators_limits(operators): factory = interface.UpdateTargetValidatorLimits(EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY) - easy_track_executor( - factory.trustedCaller(), - factory, - calldata, - ) + create_and_enact_motion(contracts.easy_track, EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, factory, calldata, stranger) -def change_node_operator_managers(operators): +def change_node_operator_managers(operators, stranger): calldata = _encode_calldata( "((uint256,address,address)[])", [[(no["id"], no["old_manager"], no["manager"]) for no in operators]], @@ -151,18 +109,15 @@ def change_node_operator_managers(operators): factory = interface.ChangeNodeOperatorManagers(EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY) - easy_track_executor( - factory.trustedCaller(), - factory, - calldata, - ) + create_and_enact_motion(contracts.easy_track, EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, factory, calldata, stranger) -def test_add_node_operators(): +def test_add_node_operators(stranger): + fill_simple_dvt_ops_keys(stranger, 3, 5) # AddNodeOperators node_operators_count = contracts.simple_dvt.getNodeOperatorsCount() - add_node_operators(NODE_OPERATORS) + add_node_operators(NODE_OPERATORS, stranger) no_ids = list(contracts.simple_dvt.getNodeOperatorIds(1, 100))[node_operators_count - 1 :] @@ -176,7 +131,9 @@ def test_add_node_operators(): assert node_operators_count + len(NODE_OPERATORS) == contracts.simple_dvt.getNodeOperatorsCount() -def test_node_operators_activations(): +def test_node_operators_activations(stranger): + fill_simple_dvt_ops_keys(stranger, 3, 5) + assert contracts.simple_dvt.getNodeOperator(1, False)[0] assert contracts.simple_dvt.getNodeOperator(2, False)[0] @@ -190,7 +147,8 @@ def test_node_operators_activations(): "id": 2, "manager": get_managers_address(2), }, - ] + ], + stranger, ) assert not contracts.simple_dvt.getNodeOperator(1, False)[0] @@ -207,14 +165,17 @@ def test_node_operators_activations(): "id": 2, "manager": get_managers_address(2), }, - ] + ], + stranger, ) assert contracts.simple_dvt.getNodeOperator(1, False)[0] assert contracts.simple_dvt.getNodeOperator(2, False)[0] -def test_set_vetted_validators_limits(): +def test_set_vetted_validators_limits(stranger): + fill_simple_dvt_ops_keys(stranger, 3, 5) + op_1 = contracts.simple_dvt.getNodeOperator(1, False) op_2 = contracts.simple_dvt.getNodeOperator(2, False) @@ -231,14 +192,17 @@ def test_set_vetted_validators_limits(): "id": 2, "staking_limit": new_vetted_keys_2, }, - ] + ], + stranger, ) assert contracts.simple_dvt.getNodeOperator(1, False)[3] == new_vetted_keys_1 assert contracts.simple_dvt.getNodeOperator(2, False)[3] == new_vetted_keys_2 -def test_set_node_operator_names(): +def test_set_node_operator_names(stranger): + fill_simple_dvt_ops_keys(stranger, 3, 5) + op_1 = contracts.simple_dvt.getNodeOperator(1, True) op_2 = contracts.simple_dvt.getNodeOperator(2, True) @@ -256,14 +220,17 @@ def test_set_node_operator_names(): "id": 2, "name": new_name_2, }, - ] + ], + stranger, ) assert contracts.simple_dvt.getNodeOperator(1, True)[1] == new_name_1 assert contracts.simple_dvt.getNodeOperator(2, True)[1] == new_name_2 -def test_set_node_operator_reward_addresses(): +def test_set_node_operator_reward_addresses(stranger): + fill_simple_dvt_ops_keys(stranger, 3, 5) + address_1 = "0x0000000000000000000000000000000000001333" address_2 = "0x0000000000000000000000000000000000001999" @@ -278,14 +245,16 @@ def test_set_node_operator_reward_addresses(): "id": 2, "address": address_2, }, - ] + ], + stranger, ) assert contracts.simple_dvt.getNodeOperator(1, False)[2] == address_1 assert contracts.simple_dvt.getNodeOperator(2, False)[2] == address_2 -def test_update_target_validator_limits(): +def test_update_target_validator_limits(stranger): + fill_simple_dvt_ops_keys(stranger, 3, 5) # UpdateTargetValidatorLimits update_target_validators_limits( [ @@ -299,27 +268,36 @@ def test_update_target_validator_limits(): "is_target_limit_active": False, "target_limit": 900, }, - ] + ], + stranger, ) - # assert contracts.simple_dvt.getNodeOperator(1, False)[1] == address_1 - # assert contracts.simple_dvt.getNodeOperator(2, False)[2] == address_2 + summary_1 = contracts.simple_dvt.getNodeOperatorSummary(1) + assert summary_1["isTargetLimitActive"] == True + assert summary_1["targetValidatorsCount"] == 800 + summary_2 = contracts.simple_dvt.getNodeOperatorSummary(2) + assert summary_2["isTargetLimitActive"] == False + assert summary_2["targetValidatorsCount"] == 0 # should be 0 because isTargetLimitActive is False -def test_transfer_node_operator_manager(): + +def test_transfer_node_operator_manager(stranger): + fill_simple_dvt_ops_keys(stranger, 3, 5) # TransferNodeOperatorManager change_node_operator_managers( [ {"id": 1, "old_manager": get_managers_address(1), "manager": "0x0000000000000000000000000000000000000222"}, {"id": 2, "old_manager": get_managers_address(2), "manager": "0x0000000000000000000000000000000000000888"}, - ] + ], + stranger, ) change_node_operator_managers( [ {"id": 1, "old_manager": "0x0000000000000000000000000000000000000222", "manager": get_managers_address(1)}, {"id": 2, "old_manager": "0x0000000000000000000000000000000000000888", "manager": get_managers_address(2)}, - ] + ], + stranger, ) try: @@ -335,7 +313,8 @@ def test_transfer_node_operator_manager(): "old_manager": "0x0000000000000000000000000000000000000888", "manager": get_managers_address(2), }, - ] + ], + stranger, ) except VirtualMachineError as error: assert "OLD_MANAGER_HAS_NO_ROLE" in error.message diff --git a/utils/test/easy_track_helpers.py b/utils/test/easy_track_helpers.py index 0a37a7ab..c40e5f67 100644 --- a/utils/test/easy_track_helpers.py +++ b/utils/test/easy_track_helpers.py @@ -9,10 +9,29 @@ STETH_ERROR_MARGIN_WEI: int = 2 + def _encode_calldata(signature, values): return "0x" + encode_single(signature, values).hex() +def create_and_enact_motion(easy_track, trusted_caller, factory, calldata, stranger): + motions_before = easy_track.getMotions() + + tx = easy_track.createMotion(factory, calldata, {"from": trusted_caller}) + + motions = easy_track.getMotions() + assert len(motions) == len(motions_before) + 1 + + chain.sleep(60 * 60 * 24 * 3) + chain.mine() + + easy_track.enactMotion( + motions[-1][0], + tx.events["MotionCreated"]["_evmScriptCallData"], + {"from": stranger}, + ) + + def create_and_enact_payment_motion( easy_track, trusted_caller, @@ -25,7 +44,6 @@ def create_and_enact_payment_motion( agent = contracts.agent agent_balance_before = balance_of(agent, token) recievers_balance_before = [balance_of(reciever, token) for reciever in recievers] - motions_before = easy_track.getMotions() recievers_addresses = [reciever.address for reciever in recievers] @@ -43,35 +61,17 @@ def create_and_enact_payment_motion( else _encode_calldata("(address[],uint256[])", [recievers_addresses, transfer_amounts]) ) - tx = easy_track.createMotion(factory, calldata, {"from": trusted_caller}) - - motions = easy_track.getMotions() - assert len(motions) == len(motions_before) + 1 - - chain.sleep(60 * 60 * 24 * 3) - chain.mine() - - easy_track.enactMotion( - motions[-1][0], - tx.events["MotionCreated"]["_evmScriptCallData"], - {"from": stranger}, - ) + create_and_enact_motion(easy_track, trusted_caller, factory, calldata, stranger) recievers_balance_after = [balance_of(reciever, token) for reciever in recievers] for i in range(len(recievers)): assert almostEqWithDiff( - recievers_balance_after[i], - recievers_balance_before[i] + transfer_amounts[i], - STETH_ERROR_MARGIN_WEI + recievers_balance_after[i], recievers_balance_before[i] + transfer_amounts[i], STETH_ERROR_MARGIN_WEI ) agent_balance_after = balance_of(agent, token) - assert almostEqWithDiff( - agent_balance_after, - agent_balance_before - sum(transfer_amounts), - STETH_ERROR_MARGIN_WEI - ) + assert almostEqWithDiff(agent_balance_after, agent_balance_before - sum(transfer_amounts), STETH_ERROR_MARGIN_WEI) def balance_of(address, token): @@ -92,23 +92,10 @@ def create_and_enact_add_recipient_motion( ): recipients_count = len(registry.getAllowedRecipients()) assert not registry.isRecipientAllowed(recipient) - motions_before = easy_track.getMotions() calldata = _encode_calldata("(address,string)", [recipient.address, title]) - tx = easy_track.createMotion(factory, calldata, {"from": trusted_caller}) - - motions = easy_track.getMotions() - assert len(motions) == len(motions_before) + 1 - - chain.sleep(60 * 60 * 24 * 3) - chain.mine() - - easy_track.enactMotion( - motions[-1][0], - tx.events["MotionCreated"]["_evmScriptCallData"], - {"from": stranger}, - ) + create_and_enact_motion(easy_track, trusted_caller, factory, calldata, stranger) assert len(registry.getAllowedRecipients()) == recipients_count + 1 assert registry.isRecipientAllowed(recipient) @@ -124,23 +111,10 @@ def create_and_enact_remove_recipient_motion( ): recipients_count = len(registry.getAllowedRecipients()) assert registry.isRecipientAllowed(recipient) - motions_before = easy_track.getMotions() calldata = _encode_calldata("(address)", [recipient.address]) - tx = easy_track.createMotion(factory, calldata, {"from": trusted_caller}) - - motions = easy_track.getMotions() - assert len(motions) == len(motions_before) + 1 - - chain.sleep(60 * 60 * 24 * 3) - chain.mine() - - easy_track.enactMotion( - motions[-1][0], - tx.events["MotionCreated"]["_evmScriptCallData"], - {"from": stranger}, - ) + create_and_enact_motion(easy_track, trusted_caller, factory, calldata, stranger) assert len(registry.getAllowedRecipients()) == recipients_count - 1 assert not registry.isRecipientAllowed(recipient) @@ -176,7 +150,7 @@ def check_add_and_remove_recipient_with_voting(registry, helpers, ldo_holder, da ) assert registry.isRecipientAllowed(recipient_candidate) - assert len(registry.getAllowedRecipients()) == recipients_length_before + 1, 'Wrong whitelist length' + assert len(registry.getAllowedRecipients()) == recipients_length_before + 1, "Wrong whitelist length" call_script_items = [ agent_forward( @@ -201,4 +175,4 @@ def check_add_and_remove_recipient_with_voting(registry, helpers, ldo_holder, da ) assert not registry.isRecipientAllowed(recipient_candidate) - assert len(registry.getAllowedRecipients()) == recipients_length_before, 'Wrong whitelist length' + assert len(registry.getAllowedRecipients()) == recipients_length_before, "Wrong whitelist length" diff --git a/utils/test/simple_dvt_helpers.py b/utils/test/simple_dvt_helpers.py index aaeb06e0..061e248e 100644 --- a/utils/test/simple_dvt_helpers.py +++ b/utils/test/simple_dvt_helpers.py @@ -5,7 +5,7 @@ EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY, EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY, ) -from utils.test.easy_track_helpers import _encode_calldata +from utils.test.easy_track_helpers import _encode_calldata, create_and_enact_motion from utils.test.keys_helpers import random_pubkeys_batch, random_signatures_batch MIN_OP_KEYS_CNT = 10 @@ -66,21 +66,7 @@ def fill_simple_dvt_ops_vetted_keys(stranger, min_ops_cnt=MIN_OPS_CNT, min_keys_ if len(input_params) > 0: calldata = _encode_calldata("((uint256,uint256)[])", [input_params]) - motions_before = contracts.easy_track.getMotions() - - tx = contracts.easy_track.createMotion(factory, calldata, {"from": trusted_caller}) - motions = contracts.easy_track.getMotions() - - assert len(motions) == len(motions_before) + 1 - - chain.sleep(60 * 60 * 24 * 3) - chain.mine() - - contracts.easy_track.enactMotion( - motions[-1][0], - tx.events["MotionCreated"]["_evmScriptCallData"], - {"from": stranger}, - ) + create_and_enact_motion(contracts.easy_track, trusted_caller, factory, calldata, stranger) for no_id in range(0, min_ops_cnt): no = contracts.simple_dvt.getNodeOperator(no_id, False) @@ -92,31 +78,16 @@ def simple_dvt_vet_keys(operator_id, stranger): factory = interface.SetVettedValidatorsLimits(EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY) trusted_caller = accounts.at(EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, force=True) - simple_dvt, easy_track = contracts.simple_dvt, contracts.easy_track - - operator = simple_dvt.getNodeOperator(operator_id, False) + operator = contracts.simple_dvt.getNodeOperator(operator_id, False) if operator["totalVettedValidators"] == operator["totalAddedValidators"]: return calldata = _encode_calldata("((uint256,uint256)[])", [[(operator_id, operator["totalAddedValidators"])]]) - motions_before = easy_track.getMotions() - - tx = easy_track.createMotion(factory, calldata, {"from": trusted_caller}) - motions = easy_track.getMotions() - - assert len(motions) == len(motions_before) + 1 - chain.sleep(60 * 60 * 24 * 3) - chain.mine() + create_and_enact_motion(contracts.easy_track, trusted_caller, factory, calldata, stranger) - easy_track.enactMotion( - motions[-1][0], - tx.events["MotionCreated"]["_evmScriptCallData"], - {"from": stranger}, - ) - - operator = simple_dvt.getNodeOperator(operator_id, False) + operator = contracts.simple_dvt.getNodeOperator(operator_id, False) assert operator["totalVettedValidators"] == operator["totalAddedValidators"] @@ -125,6 +96,7 @@ def simple_dvt_add_node_operators(simple_dvt, stranger, input_params=[]): trusted_caller = accounts.at(EASYTRACK_SIMPLE_DVT_TRUSTED_CALLER, force=True) node_operators_count_before = simple_dvt.getNodeOperatorsCount() + node_operators_count_after = node_operators_count_before # input_params = [ # (get_operator_address(0), get_operator_address(0), get_managers_address(0)), @@ -138,23 +110,10 @@ def simple_dvt_add_node_operators(simple_dvt, stranger, input_params=[]): input_params, ], ) - motions_before = contracts.easy_track.getMotions() - - tx = contracts.easy_track.createMotion(factory, calldata, {"from": trusted_caller}) - - motions = contracts.easy_track.getMotions() - assert len(motions) == len(motions_before) + 1 - - chain.sleep(60 * 60 * 24 * 3) - chain.mine() - - contracts.easy_track.enactMotion( - motions[-1][0], - tx.events["MotionCreated"]["_evmScriptCallData"], - {"from": stranger}, - ) + create_and_enact_motion(contracts.easy_track, trusted_caller, factory, calldata, stranger) + node_operators_count_after = simple_dvt.getNodeOperatorsCount() - return (node_operators_count_before, simple_dvt.getNodeOperatorsCount()) + return (node_operators_count_before, node_operators_count_after) def simple_dvt_add_keys(simple_dvt, node_operator_id, keys_count=1): From 44094d43569ecfbe69805b937bee17bd82c6a746 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Thu, 15 Feb 2024 19:05:32 +0100 Subject: [PATCH 54/68] fix: tests --- tests/acceptance/test_simple_dvt_module.py | 6 +- tests/acceptance/test_withdrawals_negative.py | 6 +- tests/regression/test_all_round_happy_path.py | 5 +- .../test_staking_router_stake_distribution.py | 183 +++++++++++++++--- 4 files changed, 165 insertions(+), 35 deletions(-) diff --git a/tests/acceptance/test_simple_dvt_module.py b/tests/acceptance/test_simple_dvt_module.py index 6426d598..47e930c7 100644 --- a/tests/acceptance/test_simple_dvt_module.py +++ b/tests/acceptance/test_simple_dvt_module.py @@ -97,8 +97,8 @@ def test_petrified(): def test_simple_dvt_state(contract): node_operators_count = contract.getNodeOperatorsCount() - # assert node_operators_count == CURATED_STAKING_MODULE_OPERATORS_COUNT - # assert contract.getActiveNodeOperatorsCount() == CURATED_STAKING_MODULE_OPERATORS_ACTIVE_COUNT + assert node_operators_count >= 0 + assert contract.getActiveNodeOperatorsCount() >= 0 assert contract.getNonce() >= 0 assert contract.getStuckPenaltyDelay() == SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY assert contract.getType() == _str_to_bytes32("curated-onchain-v1") @@ -106,7 +106,7 @@ def test_simple_dvt_state(contract): summary = contract.getStakingModuleSummary() assert summary["totalExitedValidators"] >= 0 assert summary["totalDepositedValidators"] >= 0 - assert summary["depositableValidatorsCount"] > 0 + assert summary["depositableValidatorsCount"] >= 0 deactivated_node_operators = [] # reserved for future use exited_node_operators = [] # reserved for future use diff --git a/tests/acceptance/test_withdrawals_negative.py b/tests/acceptance/test_withdrawals_negative.py index 9fc94782..2c5b775c 100644 --- a/tests/acceptance/test_withdrawals_negative.py +++ b/tests/acceptance/test_withdrawals_negative.py @@ -100,10 +100,11 @@ def test_wq_prefinalize(wq: Contract, steth_whale: Account): oracle_report(withdrawalFinalizationBatches=[last_finalized_id + 1, last_finalized_id + 1]) with reverts(encode_error("ZeroShareRate()")): - oracle_report(withdrawalFinalizationBatches=[last_finalized_id + 1, last_finalized_id + 2], simulatedShareRate=0) + oracle_report( + withdrawalFinalizationBatches=[last_finalized_id + 1, last_finalized_id + 2], simulatedShareRate=0 + ) -@pytest.mark.skip() def test_request_to_finalize_to_close(wq: Contract, steth_whale: Account): fill_wq(wq, steth_whale, count=1) with reverts(encode_error("IncorrectRequestFinalization(uint256)", [chain.time()])): @@ -132,6 +133,7 @@ def test_wq_finalize(wq: Contract, steth_whale: Account): with reverts(encode_error("InvalidRequestId(uint256)", [4])): wq.finalize(4, 1, {"from": contracts.lido}) + # === Fixtures === @pytest.fixture(scope="module") def wq() -> Contract: diff --git a/tests/regression/test_all_round_happy_path.py b/tests/regression/test_all_round_happy_path.py index ed70cd14..6cf46ad2 100644 --- a/tests/regression/test_all_round_happy_path.py +++ b/tests/regression/test_all_round_happy_path.py @@ -4,6 +4,7 @@ from utils.test.oracle_report_helpers import oracle_report from utils.test.helpers import ETH, almostEqEth from utils.config import contracts +from utils.test.simple_dvt_helpers import fill_simple_dvt_ops_vetted_keys def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): @@ -32,8 +33,10 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale): assert steth_balance_before_submit == 0 - # Submitting ETH + # ensure SimpleDVT has some keys to deposit + fill_simple_dvt_ops_vetted_keys(stranger, 3, 5) + # Submitting ETH stakeLimitInfo = contracts.lido.getStakeLimitFullInfo() growthPerBlock = stakeLimitInfo["maxStakeLimit"] // stakeLimitInfo["maxStakeLimitGrowthBlocks"] diff --git a/tests/regression/test_staking_router_stake_distribution.py b/tests/regression/test_staking_router_stake_distribution.py index 871c2d40..42976759 100644 --- a/tests/regression/test_staking_router_stake_distribution.py +++ b/tests/regression/test_staking_router_stake_distribution.py @@ -1,63 +1,80 @@ +from typing import Dict + from utils.config import contracts from utils.test.deposits_helpers import fill_deposit_buffer +from utils.test.simple_dvt_helpers import fill_simple_dvt_ops_vetted_keys from utils.test.staking_router_helpers import ModuleStatus +TOTAL_BASIS_POINTS = 10000 + class Module: - def __init__(self, id, target_share, status, active_keys, depositable_keys): + def __init__( + self, id, target_share, module_fee, treasury_fee, deposited_keys, exited_keys, depositable_keys, status + ): self.id = id self.target_share = target_share self.status = status - self.active_keys = active_keys + self.active_keys = 0 self.depositable_keys = depositable_keys self.allocated_keys = 0 self.allocation_limit = 0 + self.module_fee = module_fee + self.treasury_fee = treasury_fee + self.deposited_keys = deposited_keys + self.exited_keys = exited_keys -def test_stake_distribution(): - """ - Test stake distribution among the staking modules - 1. checks that result of `getDepositsAllocation` matches the local allocation calculations - 2. checks that deposits to modules can be made according to the calculated allocation - """ - lido, deposit_security_module = contracts.lido, contracts.deposit_security_module - - staking_router = contracts.staking_router - module_digests = staking_router.getAllStakingModuleDigests() - - keys_to_allocate = 100 # keys to allocate to the modules - allocation_from_contract = staking_router.getDepositsAllocation(keys_to_allocate) - +def get_modules_info(staking_router): # collect the modules information - + module_digests = staking_router.getAllStakingModuleDigests() modules = {} for digest in module_digests: (_, _, state, summary) = digest - (id, _, _, _, target_share, status, _, _, _, _) = state + (id, _, module_fee, treasury_fee, target_share, status, _, _, _, _) = state (exited_keys, deposited_keys, depositable_keys) = summary - - active_keys = deposited_keys - exited_keys - assert active_keys >= 0 - if status != ModuleStatus.ACTIVE.value: # reset depositable keys in case of module is inactivated # https://github.com/lidofinance/lido-dao/blob/331ecec7fe3c8d57841fd73ccca7fb1cc9bc174e/contracts/0.8.9/StakingRouter.sol#L1230-L1232 depositable_keys = 0 - modules[id] = Module(id, target_share, status, active_keys, depositable_keys) + modules[id] = Module( + id, target_share, module_fee, treasury_fee, deposited_keys, exited_keys, depositable_keys, status + ) + + # total_active_keys = sum([module.active_keys for module in modules.values()]) + return modules + + +def prep_modules_info(modules: Dict[int, Module]): + # reset keys counters + total_active_keys = 0 + + for module in modules.values(): + module.active_keys = module.deposited_keys - module.exited_keys + assert module.active_keys >= 0 + total_active_keys += module.active_keys + + return total_active_keys - total_active_keys = sum([module.active_keys for module in modules.values()]) +def calc_allocation(modules: Dict[int, Module], keys_to_allocate: int, ignore_depositable: bool = False): + + total_active_keys = prep_modules_info(modules) # simulate target share distribution # https://github.com/lidofinance/lido-dao/blob/331ecec7fe3c8d57841fd73ccca7fb1cc9bc174e/contracts/0.8.9/StakingRouter.sol#L1266-L1268 target_total_active_keys = total_active_keys + keys_to_allocate - total_basis_points = staking_router.TOTAL_BASIS_POINTS() for module in modules.values(): - target_active_keys = module.target_share * target_total_active_keys // total_basis_points - module.allocation_limit = min(target_active_keys, module.active_keys + module.depositable_keys) + target_active_keys = module.target_share * target_total_active_keys // TOTAL_BASIS_POINTS + module.allocation_limit = ( + target_active_keys + if ignore_depositable + else min(target_active_keys, module.active_keys + module.depositable_keys) + ) + module.allocated_keys = 0 # simulate min first strategy # https://github.com/lidofinance/lido-dao/blob/331ecec7fe3c8d57841fd73ccca7fb1cc9bc174e/contracts/0.8.9/StakingRouter.sol#L1274 @@ -78,6 +95,21 @@ def test_stake_distribution(): min_active_keys_module.allocated_keys += 1 total_allocated_keys = sum([module.allocated_keys for module in modules.values()]) + return total_allocated_keys, target_total_active_keys + + +def test_stake_distribution(): + """ + Test stake distribution among the staking modules + 1. checks that result of `getDepositsAllocation` matches the local allocation calculations + 2. checks that deposits to modules can be made according to the calculated allocation + """ + keys_to_allocate = 100 # keys to allocate to the modules + allocation_from_contract = contracts.staking_router.getDepositsAllocation(keys_to_allocate) + + # collect the modules information + modules = get_modules_info(contracts.staking_router) + total_allocated_keys, _ = calc_allocation(modules, keys_to_allocate) # check that local allocation matches the contract allocation assert allocation_from_contract == (total_allocated_keys, [module.active_keys for module in modules.values()]) @@ -88,10 +120,103 @@ def test_stake_distribution(): # perform deposits to the modules for module in modules.values(): if module.allocated_keys > 0: - lido.deposit(module.allocated_keys, module.id, "0x", {"from": deposit_security_module}) + contracts.lido.deposit(module.allocated_keys, module.id, "0x", {"from": contracts.deposit_security_module}) + + # check that the new active keys in the modules match the expected values + module_digests_after_deposit = contracts.staking_router.getAllStakingModuleDigests() + expected_modules_state = modules + + for digest in module_digests_after_deposit: + (_, _, state, summary) = digest + (id, _, _, _, _, _, _, _, _, _) = state + (exited_keys, deposited_keys, _) = summary + + active_keys_after_deposit = deposited_keys - exited_keys + assert expected_modules_state[id].active_keys == active_keys_after_deposit + + +def test_target_share_distribution(stranger): + keys_to_allocate = 100 # keys to allocate to the modules + keys_to_allocate_double = keys_to_allocate * 2 + + modules = get_modules_info(contracts.staking_router) + min_target_share = 1 # 0.01% = 1 / 10000 + nor_m_id = 1 + nor_m = modules[nor_m_id] + sdvt_m_id = 2 + sdvt_m = modules[sdvt_m_id] + + cur_total_active_keys = prep_modules_info(modules) + + # calc some hypothetical module allocation share for testing + expected_active_keys_1 = sdvt_m.active_keys + keys_to_allocate + expected_active_keys_2 = sdvt_m.active_keys + keys_to_allocate_double + + expected_total_active_keys = cur_total_active_keys + keys_to_allocate + expected_total_active_keys_2 = cur_total_active_keys + keys_to_allocate_double + + # calc module share that is guaranteed to fit `keys_to_allocate` deposited keys amount (upper cap) + expected_target_share_1 = (expected_active_keys_1 * TOTAL_BASIS_POINTS // expected_total_active_keys) + 1 + # calc module share for doubled `keys_to_allocate` keys amount, expected to overcome the 1st target share + expected_target_share_2 = expected_active_keys_2 * TOTAL_BASIS_POINTS // expected_total_active_keys_2 + + # ensure 2nd keys amount is enough to overcome the 1st target share (after 1st keys amount) at least by 1 basis point + assert expected_target_share_1 >= min_target_share + assert expected_target_share_2 > expected_target_share_1 + + # force update module `targetShare` value to simulate new allocation + sdvt_m.target_share = expected_target_share_1 + + expected_total_allocated_keys, expected_total_active_keys = calc_allocation(modules, keys_to_allocate, True) + assert expected_total_allocated_keys == keys_to_allocate + assert sdvt_m.active_keys >= expected_active_keys_1 + assert sdvt_m.allocated_keys == keys_to_allocate + assert nor_m.allocated_keys == 0 + + expected_total_allocated_keys, expected_total_active_keys = calc_allocation(modules, keys_to_allocate_double, True) + assert expected_total_allocated_keys == keys_to_allocate_double + assert sdvt_m.active_keys < expected_active_keys_2 + assert sdvt_m.allocated_keys < keys_to_allocate_double + assert nor_m.allocated_keys <= keys_to_allocate_double + + # aet the new target share value, which will be reached after 1s deposit of `keys_to_allocate`` batch + contracts.staking_router.updateStakingModule( + sdvt_m_id, + expected_target_share_1, + sdvt_m.module_fee, + sdvt_m.treasury_fee, + {"from": contracts.agent}, + ) + # add enough depositable keys to the target module to overcome the target share + # at least first 3 NOs, each with 1/3 of the `keys_to_allocate_double` available keys + fill_simple_dvt_ops_vetted_keys(stranger, 3, (sdvt_m.deposited_keys + keys_to_allocate_double + 3) // 3) + + # update the modules info and recalc the allocation according to the module limits + modules = get_modules_info(contracts.staking_router) + expected_total_allocated_keys, expected_total_active_keys = calc_allocation(modules, keys_to_allocate_double, False) + + assert expected_total_allocated_keys == keys_to_allocate_double + assert sdvt_m.active_keys < expected_active_keys_2 + assert sdvt_m.allocated_keys < keys_to_allocate_double + assert nor_m.allocated_keys <= keys_to_allocate_double + + allocation_from_contract = contracts.staking_router.getDepositsAllocation(keys_to_allocate_double) + # check that local allocation matches the contract allocation + assert allocation_from_contract == ( + expected_total_allocated_keys, + [module.active_keys for module in modules.values()], + ) + + # fill the deposit buffer + fill_deposit_buffer(keys_to_allocate_double) + + # perform deposits to the modules + for module in modules.values(): + if module.allocated_keys > 0: + contracts.lido.deposit(module.allocated_keys, module.id, "0x", {"from": contracts.deposit_security_module}) # check that the new active keys in the modules match the expected values - module_digests_after_deposit = staking_router.getAllStakingModuleDigests() + module_digests_after_deposit = contracts.staking_router.getAllStakingModuleDigests() expected_modules_state = modules for digest in module_digests_after_deposit: From eba14cffea71e91a75d51bd5034d6288d9370be6 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Mon, 19 Feb 2024 06:56:18 +0100 Subject: [PATCH 55/68] fix: module happy path --- .../test_staking_module_happy_path.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/regression/test_staking_module_happy_path.py b/tests/regression/test_staking_module_happy_path.py index fcac2ebb..99c37181 100644 --- a/tests/regression/test_staking_module_happy_path.py +++ b/tests/regression/test_staking_module_happy_path.py @@ -413,17 +413,17 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e assert almostEqWithDiff( no1_balance_shares_after - no1_balance_shares_before, no1_rewards_after_third_report // 2, - 1, + 2, ) assert almostEqWithDiff( no2_balance_shares_after - no2_balance_shares_before, no2_rewards_after_third_report // 2, - 1, + 2, ) assert almostEqWithDiff( no3_balance_shares_after - no3_balance_shares_before, no3_rewards_after_third_report, - 1, + 2, ) # Check burn shares @@ -534,17 +534,17 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e assert almostEqWithDiff( no1_balance_shares_after - no1_balance_shares_before, no1_rewards_after_fourth_report, - 1, + 2, ) assert almostEqWithDiff( no2_balance_shares_after - no2_balance_shares_before, no2_rewards_after_fourth_report // 2, - 1, + 2, ) assert almostEqWithDiff( no3_balance_shares_after - no3_balance_shares_before, no3_rewards_after_fourth_report, - 1, + 2, ) # Check burn shares @@ -630,17 +630,17 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e assert almostEqWithDiff( no1_balance_shares_after - no1_balance_shares_before, no1_rewards_after_fifth_report, - 1, + 2, ) assert almostEqWithDiff( no2_balance_shares_after - no2_balance_shares_before, no2_rewards_after_fifth_report // 2, - 1, + 2, ) assert almostEqWithDiff( no3_balance_shares_after - no3_balance_shares_before, no3_rewards_after_fifth_report, - 1, + 2, ) # Check burn shares @@ -717,17 +717,17 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e assert almostEqWithDiff( no1_balance_shares_after - no1_balance_shares_before, no1_rewards_after_sixth_report, - 1, + 2, ) assert almostEqWithDiff( no2_balance_shares_after - no2_balance_shares_before, no2_rewards_after_sixth_report, - 1, + 2, ) assert almostEqWithDiff( no3_balance_shares_after - no3_balance_shares_before, no3_rewards_after_sixth_report, - 1, + 2, ) assert no1_summary["stuckValidatorsCount"] == 0 @@ -838,7 +838,7 @@ def module_happy_path(staking_module, extra_data_service, impersonated_voting, e def test_node_operator_registry(impersonated_voting, eth_whale): nor = contracts.node_operators_registry nor.module_id = 1 - nor.testing_node_operator_ids = [23, 20, 28] + nor.testing_node_operator_ids = [23, 25, 28] module_happy_path(nor, ExtraDataService(), impersonated_voting, eth_whale) From c0a1c493e3c7263dda24c9a2308a7c122e01e7e1 Mon Sep 17 00:00:00 2001 From: infloop Date: Mon, 19 Feb 2024 15:12:22 +0500 Subject: [PATCH 56/68] fix: remove REPORT_AFTER_VOTE=1 env variable for tests --- .github/actions/brownie_fork_tests/action.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/actions/brownie_fork_tests/action.yml b/.github/actions/brownie_fork_tests/action.yml index e33c44f8..6c7aadfb 100644 --- a/.github/actions/brownie_fork_tests/action.yml +++ b/.github/actions/brownie_fork_tests/action.yml @@ -77,4 +77,3 @@ runs: env: WEB3_INFURA_PROJECT_ID: ${{ inputs.infura }} ETHERSCAN_TOKEN: ${{ inputs.etherscan }} - REPORT_AFTER_VOTE: 1 From 59ce7c882b9ce239c5ec4f08fedb86892e1408c6 Mon Sep 17 00:00:00 2001 From: infloop Date: Mon, 19 Feb 2024 15:26:43 +0500 Subject: [PATCH 57/68] fix: remove package-lock --- package-lock.json | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index c03e754a..00000000 --- a/package-lock.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "scripts", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "scripts", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "ganache": "file:./_ganache/packages/ganache" - } - }, - "_ganache/packages/ganache": {}, - "node_modules/ganache": { - "resolved": "_ganache/packages/ganache", - "link": true - } - } -} From 1c6332062059471a859aa787e49d67ca971e3e69 Mon Sep 17 00:00:00 2001 From: George Avsetsin Date: Mon, 19 Feb 2024 15:08:53 +0300 Subject: [PATCH 58/68] fix: test_request_to_finalize_to_close --- tests/acceptance/test_withdrawals_negative.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/acceptance/test_withdrawals_negative.py b/tests/acceptance/test_withdrawals_negative.py index 2c5b775c..43b7309e 100644 --- a/tests/acceptance/test_withdrawals_negative.py +++ b/tests/acceptance/test_withdrawals_negative.py @@ -4,7 +4,7 @@ from utils.config import WITHDRAWAL_QUEUE, contracts from utils.evm_script import encode_error -from utils.test.oracle_report_helpers import oracle_report +from utils.test.oracle_report_helpers import oracle_report, wait_to_next_available_report_time MIN_STETH_WITHDRAWAL_AMOUNT = Wei(100) MAX_STETH_WITHDRAWAL_AMOUNT = Wei(1000 * 10**18) @@ -106,6 +106,7 @@ def test_wq_prefinalize(wq: Contract, steth_whale: Account): def test_request_to_finalize_to_close(wq: Contract, steth_whale: Account): + wait_to_next_available_report_time(contracts.hash_consensus_for_accounting_oracle) fill_wq(wq, steth_whale, count=1) with reverts(encode_error("IncorrectRequestFinalization(uint256)", [chain.time()])): oracle_report( From 744d69ba3ed21b719605e0feb248b54d5851176b Mon Sep 17 00:00:00 2001 From: infloop Date: Mon, 19 Feb 2024 17:22:23 +0500 Subject: [PATCH 59/68] fix: add py-solc-ast==1.2.10 install --- .github/actions/brownie_fork_tests/action.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/actions/brownie_fork_tests/action.yml b/.github/actions/brownie_fork_tests/action.yml index 6c7aadfb..bd11ff2e 100644 --- a/.github/actions/brownie_fork_tests/action.yml +++ b/.github/actions/brownie_fork_tests/action.yml @@ -46,6 +46,10 @@ runs: shell: bash run: poetry install + - name: Install py-solc-ast + shell: bash + run: poetry run pip install py-solc-ast==1.2.10 + - name: Install ganache shell: bash run: yarn install --frozen-lockfile From b7247ebe15149628b0e1a2fc9fba43f4ed185a00 Mon Sep 17 00:00:00 2001 From: infloop Date: Mon, 19 Feb 2024 18:45:42 +0500 Subject: [PATCH 60/68] fix: add brownie update --- .github/actions/brownie_fork_tests/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/brownie_fork_tests/action.yml b/.github/actions/brownie_fork_tests/action.yml index bd11ff2e..4cc6d94e 100644 --- a/.github/actions/brownie_fork_tests/action.yml +++ b/.github/actions/brownie_fork_tests/action.yml @@ -46,9 +46,9 @@ runs: shell: bash run: poetry install - - name: Install py-solc-ast + - name: Update brownie shell: bash - run: poetry run pip install py-solc-ast==1.2.10 + run: poetry update eth-brownie - name: Install ganache shell: bash From 6f69225d2c11210c19057538684c665917b2b1f8 Mon Sep 17 00:00:00 2001 From: infloop Date: Mon, 19 Feb 2024 19:24:50 +0500 Subject: [PATCH 61/68] fix: update brownie to 1.19.5 --- .github/actions/brownie_fork_tests/action.yml | 4 - poetry.lock | 165 ++++++++++-------- 2 files changed, 95 insertions(+), 74 deletions(-) diff --git a/.github/actions/brownie_fork_tests/action.yml b/.github/actions/brownie_fork_tests/action.yml index 4cc6d94e..6c7aadfb 100644 --- a/.github/actions/brownie_fork_tests/action.yml +++ b/.github/actions/brownie_fork_tests/action.yml @@ -46,10 +46,6 @@ runs: shell: bash run: poetry install - - name: Update brownie - shell: bash - run: poetry update eth-brownie - - name: Install ganache shell: bash run: yarn install --frozen-lockfile diff --git a/poetry.lock b/poetry.lock index 89e788e3..da987744 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "aiohttp" @@ -592,13 +592,13 @@ test = ["hypothesis (>=4.18.0,<5)", "pytest (>=6.2.5,<7)", "pytest-xdist", "tox [[package]] name = "eth-brownie" -version = "1.19.3" +version = "1.19.5" description = "A Python framework for Ethereum smart contract deployment, testing and interaction." optional = false python-versions = ">=3.7,<4" files = [ - {file = "eth-brownie-1.19.3.tar.gz", hash = "sha256:a3659dc23ecf1ab96586d32ffa382736e711b38ccd2fe0fab9cc1617e36e00b0"}, - {file = "eth_brownie-1.19.3-py3-none-any.whl", hash = "sha256:d8d3e497d8e056a093006fb27fab6c3a3f97ac5b166f7946db5a46bdcc4eaa8a"}, + {file = "eth-brownie-1.19.5.tar.gz", hash = "sha256:9ab89df37d5f3b14a63da4c61f84ef71b96792741953149549598bfa69811f7d"}, + {file = "eth_brownie-1.19.5-py3-none-any.whl", hash = "sha256:bc5c281df50de9a7a04ba0f3b72430150d29b2c38eec8a82f34cc96ad0b6e67f"}, ] [package.dependencies] @@ -608,7 +608,7 @@ asttokens = "2.0.5" async-timeout = "4.0.2" attrs = "22.1.0" base58 = "2.1.1" -bitarray = "2.6.0" +bitarray = ">=2.6.0,<3" black = "22.10.0" certifi = "2022.9.24" charset-normalizer = "2.1.1" @@ -650,7 +650,7 @@ prompt-toolkit = "3.0.31" protobuf = "3.19.5" psutil = "5.9.2" py = "1.11.0" -py-solc-ast = "1.2.9" +py-solc-ast = "1.2.10" py-solc-x = "1.1.1" pycryptodome = "3.15.0" pygments = "2.13.0" @@ -684,7 +684,7 @@ web3 = "5.31.3" websockets = "9.1" wheel = "0.37.1" wrapt = "1.14.1" -yarl = "1.8.1" +yarl = "1.8.2" [[package]] name = "eth-event" @@ -1500,13 +1500,13 @@ files = [ [[package]] name = "py-solc-ast" -version = "1.2.9" +version = "1.2.10" description = "A tool for exploring the abstract syntax tree generated by solc." optional = false python-versions = ">=3.6, <4" files = [ - {file = "py-solc-ast-1.2.9.tar.gz", hash = "sha256:5a5c3bb1998de32eed4b793ebbf2f14f1fd5c681cf8b62af6b8f9f76b805164d"}, - {file = "py_solc_ast-1.2.9-py3-none-any.whl", hash = "sha256:f636217ef77bbe0f9c87a71af2f6cc9577f6301aa2ffb9af119f4c8fa8522b2d"}, + {file = "py-solc-ast-1.2.10.tar.gz", hash = "sha256:fb5defdb6e82ca4175a0ecd1c04ce37134df0c141fc60b08a5068e119d7e9850"}, + {file = "py_solc_ast-1.2.10-py3-none-any.whl", hash = "sha256:afef589268bea5ce10e217cf147c0a5e539a83ee48efba91d9b0d6f611cd05cd"}, ] [[package]] @@ -2233,6 +2233,16 @@ files = [ {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, + {file = "wrapt-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ecee4132c6cd2ce5308e21672015ddfed1ff975ad0ac8d27168ea82e71413f55"}, + {file = "wrapt-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2020f391008ef874c6d9e208b24f28e31bcb85ccff4f335f15a3251d222b92d9"}, + {file = "wrapt-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2feecf86e1f7a86517cab34ae6c2f081fd2d0dac860cb0c0ded96d799d20b335"}, + {file = "wrapt-1.14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:240b1686f38ae665d1b15475966fe0472f78e71b1b4903c143a842659c8e4cb9"}, + {file = "wrapt-1.14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9008dad07d71f68487c91e96579c8567c98ca4c3881b9b113bc7b33e9fd78b8"}, + {file = "wrapt-1.14.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6447e9f3ba72f8e2b985a1da758767698efa72723d5b59accefd716e9e8272bf"}, + {file = "wrapt-1.14.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:acae32e13a4153809db37405f5eba5bac5fbe2e2ba61ab227926a22901051c0a"}, + {file = "wrapt-1.14.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49ef582b7a1152ae2766557f0550a9fcbf7bbd76f43fbdc94dd3bf07cc7168be"}, + {file = "wrapt-1.14.1-cp311-cp311-win32.whl", hash = "sha256:358fe87cc899c6bb0ddc185bf3dbfa4ba646f05b1b0b9b5a27c2cb92c2cea204"}, + {file = "wrapt-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:26046cd03936ae745a502abf44dac702a5e6880b2b01c29aea8ddf3353b68224"}, {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, @@ -2282,70 +2292,85 @@ files = [ [[package]] name = "yarl" -version = "1.8.1" +version = "1.8.2" description = "Yet another URL library" optional = false python-versions = ">=3.7" files = [ - {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:abc06b97407868ef38f3d172762f4069323de52f2b70d133d096a48d72215d28"}, - {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:07b21e274de4c637f3e3b7104694e53260b5fc10d51fb3ec5fed1da8e0f754e3"}, - {file = "yarl-1.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9de955d98e02fab288c7718662afb33aab64212ecb368c5dc866d9a57bf48880"}, - {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ec362167e2c9fd178f82f252b6d97669d7245695dc057ee182118042026da40"}, - {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20df6ff4089bc86e4a66e3b1380460f864df3dd9dccaf88d6b3385d24405893b"}, - {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5999c4662631cb798496535afbd837a102859568adc67d75d2045e31ec3ac497"}, - {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed19b74e81b10b592084a5ad1e70f845f0aacb57577018d31de064e71ffa267a"}, - {file = "yarl-1.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e4808f996ca39a6463f45182e2af2fae55e2560be586d447ce8016f389f626f"}, - {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2d800b9c2eaf0684c08be5f50e52bfa2aa920e7163c2ea43f4f431e829b4f0fd"}, - {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6628d750041550c5d9da50bb40b5cf28a2e63b9388bac10fedd4f19236ef4957"}, - {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f5af52738e225fcc526ae64071b7e5342abe03f42e0e8918227b38c9aa711e28"}, - {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:76577f13333b4fe345c3704811ac7509b31499132ff0181f25ee26619de2c843"}, - {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0c03f456522d1ec815893d85fccb5def01ffaa74c1b16ff30f8aaa03eb21e453"}, - {file = "yarl-1.8.1-cp310-cp310-win32.whl", hash = "sha256:ea30a42dc94d42f2ba4d0f7c0ffb4f4f9baa1b23045910c0c32df9c9902cb272"}, - {file = "yarl-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:9130ddf1ae9978abe63808b6b60a897e41fccb834408cde79522feb37fb72fb0"}, - {file = "yarl-1.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0ab5a138211c1c366404d912824bdcf5545ccba5b3ff52c42c4af4cbdc2c5035"}, - {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0fb2cb4204ddb456a8e32381f9a90000429489a25f64e817e6ff94879d432fc"}, - {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85cba594433915d5c9a0d14b24cfba0339f57a2fff203a5d4fd070e593307d0b"}, - {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ca7e596c55bd675432b11320b4eacc62310c2145d6801a1f8e9ad160685a231"}, - {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0f77539733e0ec2475ddcd4e26777d08996f8cd55d2aef82ec4d3896687abda"}, - {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29e256649f42771829974e742061c3501cc50cf16e63f91ed8d1bf98242e5507"}, - {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7fce6cbc6c170ede0221cc8c91b285f7f3c8b9fe28283b51885ff621bbe0f8ee"}, - {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:59ddd85a1214862ce7c7c66457f05543b6a275b70a65de366030d56159a979f0"}, - {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:12768232751689c1a89b0376a96a32bc7633c08da45ad985d0c49ede691f5c0d"}, - {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:b19255dde4b4f4c32e012038f2c169bb72e7f081552bea4641cab4d88bc409dd"}, - {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6c8148e0b52bf9535c40c48faebb00cb294ee577ca069d21bd5c48d302a83780"}, - {file = "yarl-1.8.1-cp37-cp37m-win32.whl", hash = "sha256:de839c3a1826a909fdbfe05f6fe2167c4ab033f1133757b5936efe2f84904c07"}, - {file = "yarl-1.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:dd032e8422a52e5a4860e062eb84ac94ea08861d334a4bcaf142a63ce8ad4802"}, - {file = "yarl-1.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:19cd801d6f983918a3f3a39f3a45b553c015c5aac92ccd1fac619bd74beece4a"}, - {file = "yarl-1.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6347f1a58e658b97b0a0d1ff7658a03cb79bdbda0331603bed24dd7054a6dea1"}, - {file = "yarl-1.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c0da7e44d0c9108d8b98469338705e07f4bb7dab96dbd8fa4e91b337db42548"}, - {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5587bba41399854703212b87071c6d8638fa6e61656385875f8c6dff92b2e461"}, - {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31a9a04ecccd6b03e2b0e12e82131f1488dea5555a13a4d32f064e22a6003cfe"}, - {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:205904cffd69ae972a1707a1bd3ea7cded594b1d773a0ce66714edf17833cdae"}, - {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea513a25976d21733bff523e0ca836ef1679630ef4ad22d46987d04b372d57fc"}, - {file = "yarl-1.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0b51530877d3ad7a8d47b2fff0c8df3b8f3b8deddf057379ba50b13df2a5eae"}, - {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d2b8f245dad9e331540c350285910b20dd913dc86d4ee410c11d48523c4fd546"}, - {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ab2a60d57ca88e1d4ca34a10e9fb4ab2ac5ad315543351de3a612bbb0560bead"}, - {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:449c957ffc6bc2309e1fbe67ab7d2c1efca89d3f4912baeb8ead207bb3cc1cd4"}, - {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a165442348c211b5dea67c0206fc61366212d7082ba8118c8c5c1c853ea4d82e"}, - {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b3ded839a5c5608eec8b6f9ae9a62cb22cd037ea97c627f38ae0841a48f09eae"}, - {file = "yarl-1.8.1-cp38-cp38-win32.whl", hash = "sha256:c1445a0c562ed561d06d8cbc5c8916c6008a31c60bc3655cdd2de1d3bf5174a0"}, - {file = "yarl-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:56c11efb0a89700987d05597b08a1efcd78d74c52febe530126785e1b1a285f4"}, - {file = "yarl-1.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e80ed5a9939ceb6fda42811542f31c8602be336b1fb977bccb012e83da7e4936"}, - {file = "yarl-1.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6afb336e23a793cd3b6476c30f030a0d4c7539cd81649683b5e0c1b0ab0bf350"}, - {file = "yarl-1.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4c322cbaa4ed78a8aac89b2174a6df398faf50e5fc12c4c191c40c59d5e28357"}, - {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fae37373155f5ef9b403ab48af5136ae9851151f7aacd9926251ab26b953118b"}, - {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5395da939ffa959974577eff2cbfc24b004a2fb6c346918f39966a5786874e54"}, - {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:076eede537ab978b605f41db79a56cad2e7efeea2aa6e0fa8f05a26c24a034fb"}, - {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d1a50e461615747dd93c099f297c1994d472b0f4d2db8a64e55b1edf704ec1c"}, - {file = "yarl-1.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7de89c8456525650ffa2bb56a3eee6af891e98f498babd43ae307bd42dca98f6"}, - {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4a88510731cd8d4befaba5fbd734a7dd914de5ab8132a5b3dde0bbd6c9476c64"}, - {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2d93a049d29df172f48bcb09acf9226318e712ce67374f893b460b42cc1380ae"}, - {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:21ac44b763e0eec15746a3d440f5e09ad2ecc8b5f6dcd3ea8cb4773d6d4703e3"}, - {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d0272228fabe78ce00a3365ffffd6f643f57a91043e119c289aaba202f4095b0"}, - {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99449cd5366fe4608e7226c6cae80873296dfa0cde45d9b498fefa1de315a09e"}, - {file = "yarl-1.8.1-cp39-cp39-win32.whl", hash = "sha256:8b0af1cf36b93cee99a31a545fe91d08223e64390c5ecc5e94c39511832a4bb6"}, - {file = "yarl-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:de49d77e968de6626ba7ef4472323f9d2e5a56c1d85b7c0e2a190b2173d3b9be"}, - {file = "yarl-1.8.1.tar.gz", hash = "sha256:af887845b8c2e060eb5605ff72b6f2dd2aab7a761379373fd89d314f4752abbf"}, + {file = "yarl-1.8.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bb81f753c815f6b8e2ddd2eef3c855cf7da193b82396ac013c661aaa6cc6b0a5"}, + {file = "yarl-1.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:47d49ac96156f0928f002e2424299b2c91d9db73e08c4cd6742923a086f1c863"}, + {file = "yarl-1.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3fc056e35fa6fba63248d93ff6e672c096f95f7836938241ebc8260e062832fe"}, + {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58a3c13d1c3005dbbac5c9f0d3210b60220a65a999b1833aa46bd6677c69b08e"}, + {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10b08293cda921157f1e7c2790999d903b3fd28cd5c208cf8826b3b508026996"}, + {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de986979bbd87272fe557e0a8fcb66fd40ae2ddfe28a8b1ce4eae22681728fef"}, + {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c4fcfa71e2c6a3cb568cf81aadc12768b9995323186a10827beccf5fa23d4f8"}, + {file = "yarl-1.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae4d7ff1049f36accde9e1ef7301912a751e5bae0a9d142459646114c70ecba6"}, + {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bf071f797aec5b96abfc735ab97da9fd8f8768b43ce2abd85356a3127909d146"}, + {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:74dece2bfc60f0f70907c34b857ee98f2c6dd0f75185db133770cd67300d505f"}, + {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:df60a94d332158b444301c7f569659c926168e4d4aad2cfbf4bce0e8fb8be826"}, + {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:63243b21c6e28ec2375f932a10ce7eda65139b5b854c0f6b82ed945ba526bff3"}, + {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cfa2bbca929aa742b5084fd4663dd4b87c191c844326fcb21c3afd2d11497f80"}, + {file = "yarl-1.8.2-cp310-cp310-win32.whl", hash = "sha256:b05df9ea7496df11b710081bd90ecc3a3db6adb4fee36f6a411e7bc91a18aa42"}, + {file = "yarl-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:24ad1d10c9db1953291f56b5fe76203977f1ed05f82d09ec97acb623a7976574"}, + {file = "yarl-1.8.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2a1fca9588f360036242f379bfea2b8b44cae2721859b1c56d033adfd5893634"}, + {file = "yarl-1.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f37db05c6051eff17bc832914fe46869f8849de5b92dc4a3466cd63095d23dfd"}, + {file = "yarl-1.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77e913b846a6b9c5f767b14dc1e759e5aff05502fe73079f6f4176359d832581"}, + {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0978f29222e649c351b173da2b9b4665ad1feb8d1daa9d971eb90df08702668a"}, + {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388a45dc77198b2460eac0aca1efd6a7c09e976ee768b0d5109173e521a19daf"}, + {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2305517e332a862ef75be8fad3606ea10108662bc6fe08509d5ca99503ac2aee"}, + {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42430ff511571940d51e75cf42f1e4dbdded477e71c1b7a17f4da76c1da8ea76"}, + {file = "yarl-1.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3150078118f62371375e1e69b13b48288e44f6691c1069340081c3fd12c94d5b"}, + {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c15163b6125db87c8f53c98baa5e785782078fbd2dbeaa04c6141935eb6dab7a"}, + {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4d04acba75c72e6eb90745447d69f84e6c9056390f7a9724605ca9c56b4afcc6"}, + {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e7fd20d6576c10306dea2d6a5765f46f0ac5d6f53436217913e952d19237efc4"}, + {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:75c16b2a900b3536dfc7014905a128a2bea8fb01f9ee26d2d7d8db0a08e7cb2c"}, + {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6d88056a04860a98341a0cf53e950e3ac9f4e51d1b6f61a53b0609df342cc8b2"}, + {file = "yarl-1.8.2-cp311-cp311-win32.whl", hash = "sha256:fb742dcdd5eec9f26b61224c23baea46c9055cf16f62475e11b9b15dfd5c117b"}, + {file = "yarl-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8c46d3d89902c393a1d1e243ac847e0442d0196bbd81aecc94fcebbc2fd5857c"}, + {file = "yarl-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ceff9722e0df2e0a9e8a79c610842004fa54e5b309fe6d218e47cd52f791d7ef"}, + {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f6b4aca43b602ba0f1459de647af954769919c4714706be36af670a5f44c9c1"}, + {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1684a9bd9077e922300ecd48003ddae7a7474e0412bea38d4631443a91d61077"}, + {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ebb78745273e51b9832ef90c0898501006670d6e059f2cdb0e999494eb1450c2"}, + {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3adeef150d528ded2a8e734ebf9ae2e658f4c49bf413f5f157a470e17a4a2e89"}, + {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57a7c87927a468e5a1dc60c17caf9597161d66457a34273ab1760219953f7f4c"}, + {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:efff27bd8cbe1f9bd127e7894942ccc20c857aa8b5a0327874f30201e5ce83d0"}, + {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a783cd344113cb88c5ff7ca32f1f16532a6f2142185147822187913eb989f739"}, + {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:705227dccbe96ab02c7cb2c43e1228e2826e7ead880bb19ec94ef279e9555b5b"}, + {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:34c09b43bd538bf6c4b891ecce94b6fa4f1f10663a8d4ca589a079a5018f6ed7"}, + {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a48f4f7fea9a51098b02209d90297ac324241bf37ff6be6d2b0149ab2bd51b37"}, + {file = "yarl-1.8.2-cp37-cp37m-win32.whl", hash = "sha256:0414fd91ce0b763d4eadb4456795b307a71524dbacd015c657bb2a39db2eab89"}, + {file = "yarl-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:d881d152ae0007809c2c02e22aa534e702f12071e6b285e90945aa3c376463c5"}, + {file = "yarl-1.8.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5df5e3d04101c1e5c3b1d69710b0574171cc02fddc4b23d1b2813e75f35a30b1"}, + {file = "yarl-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7a66c506ec67eb3159eea5096acd05f5e788ceec7b96087d30c7d2865a243918"}, + {file = "yarl-1.8.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2b4fa2606adf392051d990c3b3877d768771adc3faf2e117b9de7eb977741229"}, + {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e21fb44e1eff06dd6ef971d4bdc611807d6bd3691223d9c01a18cec3677939e"}, + {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93202666046d9edadfe9f2e7bf5e0782ea0d497b6d63da322e541665d65a044e"}, + {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fc77086ce244453e074e445104f0ecb27530d6fd3a46698e33f6c38951d5a0f1"}, + {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dd68a92cab699a233641f5929a40f02a4ede8c009068ca8aa1fe87b8c20ae3"}, + {file = "yarl-1.8.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b372aad2b5f81db66ee7ec085cbad72c4da660d994e8e590c997e9b01e44901"}, + {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e6f3515aafe0209dd17fb9bdd3b4e892963370b3de781f53e1746a521fb39fc0"}, + {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dfef7350ee369197106805e193d420b75467b6cceac646ea5ed3049fcc950a05"}, + {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:728be34f70a190566d20aa13dc1f01dc44b6aa74580e10a3fb159691bc76909d"}, + {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ff205b58dc2929191f68162633d5e10e8044398d7a45265f90a0f1d51f85f72c"}, + {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baf211dcad448a87a0d9047dc8282d7de59473ade7d7fdf22150b1d23859f946"}, + {file = "yarl-1.8.2-cp38-cp38-win32.whl", hash = "sha256:272b4f1599f1b621bf2aabe4e5b54f39a933971f4e7c9aa311d6d7dc06965165"}, + {file = "yarl-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:326dd1d3caf910cd26a26ccbfb84c03b608ba32499b5d6eeb09252c920bcbe4f"}, + {file = "yarl-1.8.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f8ca8ad414c85bbc50f49c0a106f951613dfa5f948ab69c10ce9b128d368baf8"}, + {file = "yarl-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:418857f837347e8aaef682679f41e36c24250097f9e2f315d39bae3a99a34cbf"}, + {file = "yarl-1.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ae0eec05ab49e91a78700761777f284c2df119376e391db42c38ab46fd662b77"}, + {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:009a028127e0a1755c38b03244c0bea9d5565630db9c4cf9572496e947137a87"}, + {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3edac5d74bb3209c418805bda77f973117836e1de7c000e9755e572c1f7850d0"}, + {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da65c3f263729e47351261351b8679c6429151ef9649bba08ef2528ff2c423b2"}, + {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ef8fb25e52663a1c85d608f6dd72e19bd390e2ecaf29c17fb08f730226e3a08"}, + {file = "yarl-1.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcd7bb1e5c45274af9a1dd7494d3c52b2be5e6bd8d7e49c612705fd45420b12d"}, + {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44ceac0450e648de86da8e42674f9b7077d763ea80c8ceb9d1c3e41f0f0a9951"}, + {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:97209cc91189b48e7cfe777237c04af8e7cc51eb369004e061809bcdf4e55220"}, + {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:48dd18adcf98ea9cd721a25313aef49d70d413a999d7d89df44f469edfb38a06"}, + {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e59399dda559688461762800d7fb34d9e8a6a7444fd76ec33220a926c8be1516"}, + {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d617c241c8c3ad5c4e78a08429fa49e4b04bedfc507b34b4d8dceb83b4af3588"}, + {file = "yarl-1.8.2-cp39-cp39-win32.whl", hash = "sha256:cb6d48d80a41f68de41212f3dfd1a9d9898d7841c8f7ce6696cf2fd9cb57ef83"}, + {file = "yarl-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:6604711362f2dbf7160df21c416f81fac0de6dbcf0b5445a2ef25478ecc4c778"}, + {file = "yarl-1.8.2.tar.gz", hash = "sha256:49d43402c6e3013ad0978602bf6bf5328535c48d192304b91b97a3c6790b1562"}, ] [package.dependencies] From dcc972e3bc156e1f1d147fcc4920a1aa2f283c19 Mon Sep 17 00:00:00 2001 From: zuzueeka Date: Mon, 19 Feb 2024 20:20:01 +0300 Subject: [PATCH 62/68] change desc --- scripts/vote_simple_dvt.py | 152 +++++++++++++++---------------------- 1 file changed, 60 insertions(+), 92 deletions(-) diff --git a/scripts/vote_simple_dvt.py b/scripts/vote_simple_dvt.py index cbb98493..3311744f 100644 --- a/scripts/vote_simple_dvt.py +++ b/scripts/vote_simple_dvt.py @@ -1,5 +1,28 @@ """ -Voting SimpleDVT +Voting 20/02/2024. + +1. Create new Aragon repo for Simple DVT app +2. Setup Simple DVT module as Aragon app +3. Initialize Simple DVT module with module parameters +4. Create and grant permission `STAKING_ROUTER_ROLE` on Simple DVT module for `StakingRouter` +5. Grant `REQUEST_BURN_SHARES_ROLE` on `Burner` for Simple DVT module +6. Add Simple DVT module to `StakingRouter` +7. Create and grant permission `MANAGE_NODE_OPERATOR_ROLE` on Simple DVT module for `EasyTrackEVMScriptExecutor` +8. Create and grant permission `SET_NODE_OPERATOR_LIMIT_ROLE` on Simple DVT module for `EasyTrackEVMScriptExecutor` +9. Create and grant permission `MANAGE_SIGNING_KEYS` on Simple DVT module for `EasyTrackEVMScriptExecutor` +10. Grant `STAKING_ROUTER_ROLE` on Simple DVT module for `EasyTrackEVMScriptExecutor` +11. Add `AddNodeOperators` EVM script factory with address 0xcAa3AF7460E83E665EEFeC73a7a542E5005C9639 +12. Add `ActivateNodeOperators` EVM script factory with address 0xCBb418F6f9BFd3525CE6aADe8F74ECFEfe2DB5C8 +13. Add `DeactivateNodeOperators` EVM script factory with address 0x8B82C1546D47330335a48406cc3a50Da732672E7 +14. Add `SetVettedValidatorsLimits` EVM script factory with address 0xD75778b855886Fc5e1eA7D6bFADA9EB68b35C19D +15. Add `UpdateTargetValidatorLimits` EVM script factory with address 0x41CF3DbDc939c5115823Fba1432c4EC5E7bD226C +16. Add `SetNodeOperatorNames` EVM script factory with address 0x7d509BFF310d9460b1F613e4e40d342201a83Ae4 +17. Add `SetNodeOperatorRewardAddresses` EVM script factory with address 0x589e298964b9181D9938B84bB034C3BB9024E2C0 +18. Add `ChangeNodeOperatorManagers` EVM script factory with address 0xE31A0599A6772BCf9b2bFc9e25cf941e793c9a7D +19. Grant `MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE` to the Lido DAO Agent on `OracleReportSanityChecker` contract +20. Grant `MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE` to the Lido DAO Agent on `OracleReportSanityChecker` contract +22. Set `maxAccountingExtraDataListItemsCount` sanity checker parameter to 4 +22. Set `maxNodeOperatorsPerExtraDataItemCount` sanity checker parameter to 50 """ import time @@ -56,72 +79,17 @@ description = """ -### I. Create new Aragon DAO Application Repo for Simple DVT +This vote follows a [Lido DAO decision on Snapshot](https://snapshot.org/#/lido-snapshot.eth/proposal/0xf3ac657484444f0b54eba2c251135c47f875e3d1821496247d11bdd7fab0f291) and [proposes to release](https://research.lido.fi/t/simple-dvt-release/6613) the Simple DVT module on the mainnet, introducing new Easy Track factories for operational efficiency, and adjusting Oracle Report Sanity Checker parameters. +All audit reports can be found here: [Simple DVT app](https://github.com/lidofinance/audits/blob/main/Certora%20Lido%20V2%20Audit%20Report%2004-23.pdf) (same implementation as Node Operators Registry), [Easy Track factories](https://github.com/lidofinance/audits/blob/main/Statemind%20Lido%20Simple%20DVT%20Easy%20Track%20Factories%20Audit%20Report%2001-24.pdf), [SSV module](https://github.com/bloxapp/ssv-network/blob/v1.0.2/contracts/audits/2023-30-10_Quantstamp_v1.0.2.pdf), [Obol module](https://obol.tech/charon_quantstamp_audit.pdf). -1. **Create new Aragon DAO Application Repo for Simple DVT app** with parameters: - * Name: `simple-dvt` - * Version: `1.0.0` - * Implementation address: [0x8538930c385C0438A357d2c25CB3eAD95Ab6D8ed](https://etherscan.io/address/0x8538930c385C0438A357d2c25CB3eAD95Ab6D8ed) - * Content IPFS URI: [ipfs:QmaSSujHCGcnFuetAPGwVW5BegaMBvn5SCsgi3LSfvraSo](https://ipfs.io/ipfs/QmaSSujHCGcnFuetAPGwVW5BegaMBvn5SCsgi3LSfvraSo) (hex-encoded: `0x697066733a516d615353756a484347636e4675657441504777565735426567614d42766e355343736769334c5366767261536f`) +The proposed actions include: - -### II. Setup and initialize Simple DVT module as new Aragon app - -2. **Setup Simple DVT module as Aragon DAO app** with the same [contract implementation](https://etherscan.io/address/0x8538930c385C0438A357d2c25CB3eAD95Ab6D8ed) as the NodeOperatorsRegistry. - -3. **Initialize of Simple DVT module** with parameters: - * Module Type = `curated-onchain-v1` (hex-encoded: `0x637572617465642d6f6e636861696e2d76310000000000000000000000000000`) - * Stuck Penalty Delay = `432000` (5 days - the same as in Curated Module) - - -### III. Add Simple DVT module to StakingRouter - -4. **Create and grant permission `STAKING_ROUTER_ROLE` on Simple DVT module for StakingRouter**. This is necessary for the Simple DVT module to function as a staking module - -5. **Grant `REQUEST_BURN_SHARES_ROLE` on Burner for Simple DVT module**. This role is required for ability to burn stETH tokens in case of operator’s penalties, you can read more [here](https://docs.lido.fi/guides/protocol-levers/#burning-steth-tokens) - -6. **Add Simple DVT module to StakingRouter**. This action finally sets up new Simple DVT module contract to the [StakingRouter](https://etherscan.io/address/0xFdDf38947aFB03C621C71b06C9C70bce73f12999) as the second module - - -### IV. Grant permissions to EasyTrackEVMScriptExecutor to make operational changes to Simple DVT module - -7. **Create and grant permission `MANAGE_NODE_OPERATOR_ROLE` on Simple DVT module for [EasyTrackEVMScriptExecutor](https://etherscan.io/address/0xFE5986E06210aC1eCC1aDCafc0cc7f8D63B3F977)**. This one and next 3 actions are needed to setup permissions required for [EasyTrack](https://etherscan.io/address/0xF0211b7660680B49De1A7E9f25C65660F0a13Fea) to manage operational tasks on the Simple DVT module - -8. **Create and grant permission `SET_NODE_OPERATOR_LIMIT_ROLE` on Simple DVT module for EasyTrackEVMScriptExecutor** - -9. **Create and grant permission `MANAGE_SIGNING_KEYS` on Simple DVT module for EasyTrackEVMScriptExecutor** - -10. **Grant `STAKING_ROUTER_ROLE` on Simple DVT module for EasyTrackEVMScriptExecutor** - - -### V. Add Easy Track EVM script factories for Simple DVT module to EasyTrack registry - -11. **Add AddNodeOperators EVM script factory** with address [0xcAa3AF7460E83E665EEFeC73a7a542E5005C9639](https://etherscan.io/address/0xcAa3AF7460E83E665EEFeC73a7a542E5005C9639) - -12. **Add ActivateNodeOperators EVM script factory** with address [0xCBb418F6f9BFd3525CE6aADe8F74ECFEfe2DB5C8](https://etherscan.io/address/0xCBb418F6f9BFd3525CE6aADe8F74ECFEfe2DB5C8) - -13. **Add DeactivateNodeOperators EVM script factory** with address [0x8B82C1546D47330335a48406cc3a50Da732672E7](https://etherscan.io/address/0x8B82C1546D47330335a48406cc3a50Da732672E7) - -14. **Add SetVettedValidatorsLimits EVM script factory** with address [0xD75778b855886Fc5e1eA7D6bFADA9EB68b35C19D](https://etherscan.io/address/0xD75778b855886Fc5e1eA7D6bFADA9EB68b35C19D) - -15. **Add UpdateTargetValidatorLimits EVM script factory** with address [0x41CF3DbDc939c5115823Fba1432c4EC5E7bD226C](https://etherscan.io/address/0x41CF3DbDc939c5115823Fba1432c4EC5E7bD226C) - -16. **Add SetNodeOperatorNames EVM script factory** with address [0x7d509BFF310d9460b1F613e4e40d342201a83Ae4](https://etherscan.io/address/0x7d509BFF310d9460b1F613e4e40d342201a83Ae4) - -17. **Add SetNodeOperatorRewardAddresses EVM script factory** with address [0x589e298964b9181D9938B84bB034C3BB9024E2C0](https://etherscan.io/address/0x589e298964b9181D9938B84bB034C3BB9024E2C0) - -18. **Add ChangeNodeOperatorManagers EVM script factory** with address [0xE31A0599A6772BCf9b2bFc9e25cf941e793c9a7D](https://etherscan.io/address/0xE31A0599A6772BCf9b2bFc9e25cf941e793c9a7D) - - -### VI. Update Oracle Report Sanity Checker parameters - -19. **Grant `MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE` to the [Lido DAO Agent](https://etherscan.io/address/0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c) on [OracleReportSanityChecker](https://etherscan.io/address/0x9305c1Dbfe22c12c66339184C0025d7006f0f1cC) contract**. This makes possible to set sanity checker parameter values (see next actions) - -20. **Grant `MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE` to the `Lido DAO Agent` on `OracleReportSanityChecker` contract** - -22. **Set `maxAccountingExtraDataListItemsCount` sanity checker parameter to 4**. This will allow to report extra data for 2 modules in 3rd phase of the accounting oracle report simultaneously - -22. **Set `maxNodeOperatorsPerExtraDataItemCount` sanity checker parameter to `50`**. Limiting the number of operators guarantees a successful oracle report within the block gas limit +1. **Create new Aragon repo for Simple DVT app:** Establish a dedicated repository with the implementation address 0x8538930c385C0438A357d2c25CB3eAD95Ab6D8ed. View the content at the URI: ipfs:[QmaSSujHCGcnFuetAPGwVW5BegaMBvn5SCsgi3LSfvraSo](https://ipfs.io/ipfs/QmaSSujHCGcnFuetAPGwVW5BegaMBvn5SCsgi3LSfvraSo/). Item 1. +2. **Setup and Initialize Simple DVT as a new Aragon App:** Model after the [NodeOperatorsRegistry's contract](https://etherscan.io/address/0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5) implementation for the Simple DVT app. Items 2, 3. +3. **Integrate Simple DVT Module with StakingRouter:** Add the Simple DVT module to [the StakingRouter](https://etherscan.io/address/0xFdDf38947aFB03C621C71b06C9C70bce73f12999). Items 4-6. +4. **Grant permissions to** [EasyTrackEVMScriptExecutor](https://etherscan.io/address/0xFE5986E06210aC1eCC1aDCafc0cc7f8D63B3F977): Enabling operational adjustments within the Simple DVT module via Easy Track. Items 7-10. +5. **Attach new Easy Track EVM Script Factories for the Simple DVT Module to Easy Track registry:** Equip the [Simple DVT Module Committee Multisig](https://app.safe.global/settings/setup?safe=eth:0x08637515E85A4633E23dfc7861e2A9f53af640f7) ([forum proposal](https://research.lido.fi/t/simple-dvt-module-committee-multisig/6520)) with the ability to manage Node Operators: adding, activating, deactivating, and adjusting validator limits and details. All factories addresses could be found on the [forum proposal](https://research.lido.fi/t/simple-dvt-release/6613). Items 11-18. +6. **Adjust Oracle Report Sanity Checker Parameters:** Modify parameters to support several-module reporting, enhancing the system's reporting capabilities. Items 19-22. """ @@ -130,10 +98,10 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra vote_desc_items, call_script_items = zip( # - # I. Create new Aragon DAO Application Repo for Simple DVT + # I. Create new Aragon repo for Simple DVT app # ( - "1) Create new Aragon DAO Application Repo for Simple DVT app", + "1) Create new Aragon repo for Simple DVT app", create_new_app_repo( name=create_simple_dvt_app["name"], manager=contracts.voting, @@ -143,14 +111,14 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), # - # II. Setup and initialize Simple DVT module as new Aragon app + # II. Setup and initialize Simple DVT module as a new Aragon app # ( - "2) Setup Simple DVT module as Aragon DAO app", + "2) Setup Simple DVT module as Aragon app", update_app_implementation(create_simple_dvt_app["id"], create_simple_dvt_app["new_address"]), ), ( - "3) Initialize of Simple DVT module", + "3) Initialize Simple DVT module with module parameters", ( contracts.simple_dvt.address, contracts.simple_dvt.initialize.encode_input( @@ -161,10 +129,10 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), # - # III. Add Simple DVT module to Staking Router + # III. Integrate Simple DVT Module with StakingRouter # ( - "4) *Create and grant permission STAKING_ROUTER_ROLE on Simple DVT module for StakingRouter", + "4) Create and grant permission `STAKING_ROUTER_ROLE` on Simple DVT module for `StakingRouter`", encode_permission_create( entity=contracts.staking_router, target_app=contracts.simple_dvt, @@ -173,7 +141,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "5) Grant REQUEST_BURN_SHARES_ROLE on Burner for Simple DVT module", + "5) Grant `REQUEST_BURN_SHARES_ROLE` on `Burner` for Simple DVT module", agent_forward( [ encode_oz_grant_role( @@ -185,7 +153,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "6) Add Simple DVT module to StakingRouter", + "6) Add Simple DVT module to `StakingRouter`", agent_forward( [ ( @@ -205,7 +173,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra # IV. Grant permissions to EasyTrackEVMScriptExecutor to make operational changes to Simple DVT module # ( - "7) Create and grant permission MANAGE_NODE_OPERATOR_ROLE on Simple DVT module for EasyTrackEVMScriptExecutor", + "7) Create and grant permission `MANAGE_NODE_OPERATOR_ROLE` on Simple DVT module for `EasyTrackEVMScriptExecutor`", encode_permission_create( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, target_app=contracts.simple_dvt, @@ -214,7 +182,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "8) Create and grant permission SET_NODE_OPERATOR_LIMIT_ROLE on Simple DVT module for EasyTrackEVMScriptExecutor", + "8) Create and grant permission `SET_NODE_OPERATOR_LIMIT_ROLE` on Simple DVT module for `EasyTrackEVMScriptExecutor`", encode_permission_create( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, target_app=contracts.simple_dvt, @@ -223,7 +191,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "9) Create and grant permission MANAGE_SIGNING_KEYS on Simple DVT module for EasyTrackEVMScriptExecutor", + "9) Create and grant permission `MANAGE_SIGNING_KEYS` on Simple DVT module for `EasyTrackEVMScriptExecutor`", encode_permission_create( entity=EASYTRACK_EVMSCRIPT_EXECUTOR, target_app=contracts.simple_dvt, @@ -232,7 +200,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "10) Grant STAKING_ROUTER_ROLE on Simple DVT module for EasyTrackEVMScriptExecutor", + "10) Grant `STAKING_ROUTER_ROLE` on Simple DVT module for `EasyTrackEVMScriptExecutor`", encode_permission_grant( target_app=contracts.simple_dvt, permission_name="STAKING_ROUTER_ROLE", @@ -240,10 +208,10 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), # - # V. Add EasyTrack EVM script factories for SimpleDVT module to EasyTrack registry + # V. Attach new Easy Track EVM Script Factories for the Simple DVT Module to Easy Track registry # ( - "11) Add AddNodeOperators EVM script factory", + "11) Add `AddNodeOperators` EVM script factory with address 0xcAa3AF7460E83E665EEFeC73a7a542E5005C9639", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_ADD_NODE_OPERATORS_FACTORY, permissions=( @@ -253,7 +221,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "12) Add ActivateNodeOperators EVM script factory", + "12) Add `ActivateNodeOperators` EVM script factory with address 0xCBb418F6f9BFd3525CE6aADe8F74ECFEfe2DB5C8", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_ACTIVATE_NODE_OPERATORS_FACTORY, permissions=( @@ -263,7 +231,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "13) Add DeactivateNodeOperators EVM script factory", + "13) Add `DeactivateNodeOperators` EVM script factory with address 0x8B82C1546D47330335a48406cc3a50Da732672E7", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_DEACTIVATE_NODE_OPERATORS_FACTORY, permissions=( @@ -273,35 +241,35 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "14) Add SetVettedValidatorsLimits EVM script factory", + "14) Add `SetVettedValidatorsLimits` EVM script factory with address 0xD75778b855886Fc5e1eA7D6bFADA9EB68b35C19D", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_SET_VETTED_VALIDATORS_LIMITS_FACTORY, permissions=(create_permissions(contracts.simple_dvt, "setNodeOperatorStakingLimit")), ), ), ( - "15) Add UpdateTargetValidatorLimits EVM script factory", + "15) Add `UpdateTargetValidatorLimits` EVM script factory with address 0x41CF3DbDc939c5115823Fba1432c4EC5E7bD226C", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY, permissions=(create_permissions(contracts.simple_dvt, "updateTargetValidatorsLimits")), ), ), ( - "16) Add SetNodeOperatorNames EVM script factory", + "16) Add `SetNodeOperatorNames` EVM script factory with address 0x7d509BFF310d9460b1F613e4e40d342201a83Ae4", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_NAMES_FACTORY, permissions=(create_permissions(contracts.simple_dvt, "setNodeOperatorName")), ), ), ( - "17) Add SetNodeOperatorRewardAddresses EVM script factory", + "17) Add `SetNodeOperatorRewardAddresses` EVM script factory with address 0x589e298964b9181D9938B84bB034C3BB9024E2C0", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_SET_NODE_OPERATOR_REWARD_ADDRESSES_FACTORY, permissions=(create_permissions(contracts.simple_dvt, "setNodeOperatorRewardAddress")), ), ), ( - "18) Add ChangeNodeOperatorManagers EVM script factory", + "18) Add `ChangeNodeOperatorManagers` EVM script factory with address 0xE31A0599A6772BCf9b2bFc9e25cf941e793c9a7D", add_evmscript_factory( factory=EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY, permissions=( @@ -311,10 +279,10 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), # - # VI. Update Oracle Report Sanity Checker parameters + # VI. Adjust Oracle Report Sanity Checker Parameters # ( - "19) Grant MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE to the Lido DAO Agent on OracleReportSanityChecker contract", + "19) Grant `MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT_ROLE` to the Lido DAO Agent on `OracleReportSanityChecker` contract", agent_forward( [ encode_oz_grant_role( @@ -326,7 +294,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "20) Grant MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE to the Lido DAO Agent on OracleReportSanityChecker contract", + "20) Grant `MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT_ROLE` to the Lido DAO Agent on `OracleReportSanityChecker` contract", agent_forward( [ encode_oz_grant_role( @@ -338,7 +306,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "21) Set maxAccountingExtraDataListItemsCount sanity checker parameter to 4", + "21) Set `maxAccountingExtraDataListItemsCount` sanity checker parameter to 4", agent_forward( [ ( @@ -349,7 +317,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | Tra ), ), ( - "22) Set maxNodeOperatorsPerExtraDataItemCount sanity checker parameter to 50", + "22) Set `maxNodeOperatorsPerExtraDataItemCount` sanity checker parameter to 50", agent_forward( [ ( From 5c0bb0ec210008fe3a30f5a1c6abf98559d233d9 Mon Sep 17 00:00:00 2001 From: zuzueeka Date: Mon, 19 Feb 2024 20:40:45 +0300 Subject: [PATCH 63/68] change ipfs link --- tests/test_vote_simple_dvt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_vote_simple_dvt.py b/tests/test_vote_simple_dvt.py index ab232cba..baf22def 100644 --- a/tests/test_vote_simple_dvt.py +++ b/tests/test_vote_simple_dvt.py @@ -299,7 +299,7 @@ def test_vote( metadata = find_metadata_by_vote_id(vote_id) print("metadata", metadata) - assert get_lido_vote_cid_from_str(metadata) == "bafkreietqdaevzjnhog4ui4a23iswak2haom4zfobeueefy3lmwkxnr2hq" + assert get_lido_vote_cid_from_str(metadata) == "bafkreid4xlf2dawv3wrm62n44uckq6k6bxt332wz2hi65qbq6qirczv2ey" display_voting_events(vote_tx) From 36b54cccdfdddb829d7f3c6106d97630a5dc6ae0 Mon Sep 17 00:00:00 2001 From: infloop Date: Tue, 20 Feb 2024 00:00:24 +0500 Subject: [PATCH 64/68] fix: update brownie to 1.19.4 --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index da987744..7243e243 100644 --- a/poetry.lock +++ b/poetry.lock @@ -592,13 +592,13 @@ test = ["hypothesis (>=4.18.0,<5)", "pytest (>=6.2.5,<7)", "pytest-xdist", "tox [[package]] name = "eth-brownie" -version = "1.19.5" +version = "1.19.4" description = "A Python framework for Ethereum smart contract deployment, testing and interaction." optional = false python-versions = ">=3.7,<4" files = [ - {file = "eth-brownie-1.19.5.tar.gz", hash = "sha256:9ab89df37d5f3b14a63da4c61f84ef71b96792741953149549598bfa69811f7d"}, - {file = "eth_brownie-1.19.5-py3-none-any.whl", hash = "sha256:bc5c281df50de9a7a04ba0f3b72430150d29b2c38eec8a82f34cc96ad0b6e67f"}, + {file = "eth-brownie-1.19.4.tar.gz", hash = "sha256:3e47fbd28f8cf239a108b7fd13bbce35d98b71ef90ad102b8348b6d55cb174f4"}, + {file = "eth_brownie-1.19.4-py3-none-any.whl", hash = "sha256:81a9549351213a83f0807c370d8c4f6ac3c269cf694340c6cf2a0fef5375e9cd"}, ] [package.dependencies] @@ -2380,4 +2380,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.11" -content-hash = "f47bbdb22efd2f6439ba59ce9f79fc1169ca2544e56b84d4ee17e2c6dfbd45a6" +content-hash = "f92f81e7e2e1c7d336cc3e96fa1f47b46c57431fcaa5f899314fe075c8b69bfd" diff --git a/pyproject.toml b/pyproject.toml index 68222aa4..4792e6cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ homepage = "https://mainnet.lido.fi" [tool.poetry.dependencies] python = ">=3.10,<3.11" -eth-brownie = "~1.19.3" +eth-brownie = "1.19.4" avotes-parser-core = "~0.5.4" setuptools = "~65.5.1" hexbytes = "0.2.3" From ba41d418481ab5c5ef7b9596dcd444197de47712 Mon Sep 17 00:00:00 2001 From: infloop Date: Tue, 20 Feb 2024 01:12:33 +0500 Subject: [PATCH 65/68] fix: change transact call --- tests/acceptance/test_withdrawals_negative.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/acceptance/test_withdrawals_negative.py b/tests/acceptance/test_withdrawals_negative.py index 43b7309e..47304588 100644 --- a/tests/acceptance/test_withdrawals_negative.py +++ b/tests/acceptance/test_withdrawals_negative.py @@ -85,7 +85,7 @@ def test_request_withdrawals_wsteth(wq: Contract, wsteth_whale: Account): def test_wq_prefinalize(wq: Contract, steth_whale: Account): with reverts(encode_error("EmptyBatches()")): - wq.prefinalize.transact([], 1, {"from": wq.address}) + wq.prefinalize.transact(False, [], 1, {"from": wq.address}) last_finalized_id = wq.getLastFinalizedRequestId() # 0 after enacment fill_wq(wq, steth_whale, count=3) From 4abf04a9defa5a9b954ef9bf251d09c16a3fc15c Mon Sep 17 00:00:00 2001 From: zuzueeka Date: Tue, 20 Feb 2024 15:25:47 +0300 Subject: [PATCH 66/68] update ipfs desc --- scripts/vote_simple_dvt.py | 12 ++++++------ tests/test_vote_simple_dvt.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/vote_simple_dvt.py b/scripts/vote_simple_dvt.py index 3311744f..6730cdbc 100644 --- a/scripts/vote_simple_dvt.py +++ b/scripts/vote_simple_dvt.py @@ -84,12 +84,12 @@ The proposed actions include: -1. **Create new Aragon repo for Simple DVT app:** Establish a dedicated repository with the implementation address 0x8538930c385C0438A357d2c25CB3eAD95Ab6D8ed. View the content at the URI: ipfs:[QmaSSujHCGcnFuetAPGwVW5BegaMBvn5SCsgi3LSfvraSo](https://ipfs.io/ipfs/QmaSSujHCGcnFuetAPGwVW5BegaMBvn5SCsgi3LSfvraSo/). Item 1. -2. **Setup and Initialize Simple DVT as a new Aragon App:** Model after the [NodeOperatorsRegistry's contract](https://etherscan.io/address/0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5) implementation for the Simple DVT app. Items 2, 3. -3. **Integrate Simple DVT Module with StakingRouter:** Add the Simple DVT module to [the StakingRouter](https://etherscan.io/address/0xFdDf38947aFB03C621C71b06C9C70bce73f12999). Items 4-6. -4. **Grant permissions to** [EasyTrackEVMScriptExecutor](https://etherscan.io/address/0xFE5986E06210aC1eCC1aDCafc0cc7f8D63B3F977): Enabling operational adjustments within the Simple DVT module via Easy Track. Items 7-10. -5. **Attach new Easy Track EVM Script Factories for the Simple DVT Module to Easy Track registry:** Equip the [Simple DVT Module Committee Multisig](https://app.safe.global/settings/setup?safe=eth:0x08637515E85A4633E23dfc7861e2A9f53af640f7) ([forum proposal](https://research.lido.fi/t/simple-dvt-module-committee-multisig/6520)) with the ability to manage Node Operators: adding, activating, deactivating, and adjusting validator limits and details. All factories addresses could be found on the [forum proposal](https://research.lido.fi/t/simple-dvt-release/6613). Items 11-18. -6. **Adjust Oracle Report Sanity Checker Parameters:** Modify parameters to support several-module reporting, enhancing the system's reporting capabilities. Items 19-22. +1. Create new Aragon repo for Simple DVT app: Establish a dedicated repository with the implementation address 0x8538930c385C0438A357d2c25CB3eAD95Ab6D8ed. View the content at the URI: ipfs:[QmaSSujHCGcnFuetAPGwVW5BegaMBvn5SCsgi3LSfvraSo](https://ipfs.io/ipfs/QmaSSujHCGcnFuetAPGwVW5BegaMBvn5SCsgi3LSfvraSo/). Item 1. +2. Setup and Initialize Simple DVT as a new Aragon App: Model after the [NodeOperatorsRegistry's contract](https://etherscan.io/address/0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5) implementation for the Simple DVT app. Items 2, 3. +3. Integrate Simple DVT Module with StakingRouter: Add the Simple DVT module to [the StakingRouter](https://etherscan.io/address/0xFdDf38947aFB03C621C71b06C9C70bce73f12999) capped at 0.5% of total Lido stake, with 10% fee split as 2% to DAO Treasury / 8% to the Simple DVT module. Items 4-6. +4. Grant permissions to [EasyTrackEVMScriptExecutor](https://etherscan.io/address/0xFE5986E06210aC1eCC1aDCafc0cc7f8D63B3F977): Enabling operational adjustments within the Simple DVT module via Easy Track. Items 7-10. +5. Attach new Easy Track EVM Script Factories for the Simple DVT Module to Easy Track registry: Equip the [Simple DVT Module Committee Multisig](https://app.safe.global/settings/setup?safe=eth:0x08637515E85A4633E23dfc7861e2A9f53af640f7) ([forum proposal](https://research.lido.fi/t/simple-dvt-module-committee-multisig/6520)) with the ability to manage Node Operators: adding, activating, deactivating, and adjusting validator limits and details. All factories addresses could be found on the [forum proposal](https://research.lido.fi/t/simple-dvt-release/6613). Items 11-18. +6. Adjust Oracle Report Sanity Checker Parameters: Modify parameters to support several-module reporting, enhancing the system's reporting capabilities. Items 19-22. """ diff --git a/tests/test_vote_simple_dvt.py b/tests/test_vote_simple_dvt.py index baf22def..6e04f4db 100644 --- a/tests/test_vote_simple_dvt.py +++ b/tests/test_vote_simple_dvt.py @@ -299,7 +299,7 @@ def test_vote( metadata = find_metadata_by_vote_id(vote_id) print("metadata", metadata) - assert get_lido_vote_cid_from_str(metadata) == "bafkreid4xlf2dawv3wrm62n44uckq6k6bxt332wz2hi65qbq6qirczv2ey" + assert get_lido_vote_cid_from_str(metadata) == "bafkreihibltsumjlg3zog4nzuq4rw5jfbwnbxtsgtixbsod3kmk7r5bd6u" display_voting_events(vote_tx) From cdb18a55a44a8d709f0c1ada41b36467908aba69 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Fri, 23 Feb 2024 16:17:11 +0100 Subject: [PATCH 67/68] fix: move vote simple dvt to archive --- .../vote_simple_dvt.py => archive/scripts/vote_2024_02_23.py | 0 .../test_vote_simple_dvt.py => archive/tests/test_2024_02_23.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename scripts/vote_simple_dvt.py => archive/scripts/vote_2024_02_23.py (100%) rename tests/test_vote_simple_dvt.py => archive/tests/test_2024_02_23.py (99%) diff --git a/scripts/vote_simple_dvt.py b/archive/scripts/vote_2024_02_23.py similarity index 100% rename from scripts/vote_simple_dvt.py rename to archive/scripts/vote_2024_02_23.py diff --git a/tests/test_vote_simple_dvt.py b/archive/tests/test_2024_02_23.py similarity index 99% rename from tests/test_vote_simple_dvt.py rename to archive/tests/test_2024_02_23.py index 6e04f4db..550209a2 100644 --- a/tests/test_vote_simple_dvt.py +++ b/archive/tests/test_2024_02_23.py @@ -5,7 +5,7 @@ import pytest from typing import List -from scripts.vote_simple_dvt import start_vote +from archive.scripts.vote_2024_02_23 import start_vote from brownie import interface, ZERO_ADDRESS, reverts, web3, accounts, convert, network from tests.regression.test_permissions import active_aragon_roles, protocol_permissions from utils.test.deposits_helpers import fill_deposit_buffer From 548b6d95c5416eaf2115485b0370bfd2184e4d8c Mon Sep 17 00:00:00 2001 From: KRogLA Date: Fri, 23 Feb 2024 16:46:23 +0100 Subject: [PATCH 68/68] fix: cleanup --- archive/scripts/vote_2024_02_23.py | 4 ++-- archive/tests/test_2024_02_23.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/archive/scripts/vote_2024_02_23.py b/archive/scripts/vote_2024_02_23.py index 6730cdbc..f7d978a0 100644 --- a/archive/scripts/vote_2024_02_23.py +++ b/archive/scripts/vote_2024_02_23.py @@ -27,9 +27,9 @@ import time -from typing import Dict, List, NamedTuple +from typing import Dict from brownie.network.transaction import TransactionReceipt -from utils.agent import agent_execute, agent_forward +from utils.agent import agent_forward from utils.kernel import update_app_implementation from utils.repo import create_new_app_repo from utils.voting import bake_vote_items, confirm_vote_script, create_vote diff --git a/archive/tests/test_2024_02_23.py b/archive/tests/test_2024_02_23.py index 550209a2..50dc0fd5 100644 --- a/archive/tests/test_2024_02_23.py +++ b/archive/tests/test_2024_02_23.py @@ -6,10 +6,10 @@ import pytest from typing import List from archive.scripts.vote_2024_02_23 import start_vote -from brownie import interface, ZERO_ADDRESS, reverts, web3, accounts, convert, network -from tests.regression.test_permissions import active_aragon_roles, protocol_permissions +from brownie import interface, ZERO_ADDRESS, network +from tests.regression.test_permissions import active_aragon_roles from utils.test.deposits_helpers import fill_deposit_buffer -from utils.test.event_validators.aragon import validate_app_update_event, validate_push_to_repo_event +from utils.test.event_validators.aragon import validate_app_update_event from utils.test.event_validators.common import validate_events_chain from utils.test.event_validators.staking_router import StakingModuleItem, validate_staking_module_added_event from utils.test.oracle_report_helpers import oracle_report