From abdfdee563ff8fcd2fbb43c1ccb13c3e497d9b77 Mon Sep 17 00:00:00 2001 From: F4ever Date: Fri, 9 Feb 2024 22:44:30 +0100 Subject: [PATCH 01/10] 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 02/10] 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 40e4050265709282670217325b2a7cd8dbe12ba3 Mon Sep 17 00:00:00 2001 From: F4ever Date: Mon, 12 Feb 2024 15:40:56 +0100 Subject: [PATCH 03/10] 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 132d4fe90d62540d62e1ba9ab75422e643fb3319 Mon Sep 17 00:00:00 2001 From: F4ever Date: Tue, 13 Feb 2024 16:33:28 +0100 Subject: [PATCH 04/10] 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 05/10] 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 06/10] 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 7cf616a1236ad9b798ba71c1c669aebf188af782 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 14 Feb 2024 16:15:58 +0100 Subject: [PATCH 07/10] 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 08/10] 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 09/10] 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 10/10] 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)