From 03d7d6e48e567b9be132a6d1ec8c04722caef3ee Mon Sep 17 00:00:00 2001 From: Evgeny Gusarov Date: Thu, 28 May 2026 20:36:33 +0300 Subject: [PATCH 1/2] Refactor naming in validators --- src/commands/consolidate.py | 4 ++-- src/validators/consensus.py | 8 ++++---- src/validators/register_validators.py | 6 +++--- src/validators/tasks.py | 27 ++++++++++++++++----------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/commands/consolidate.py b/src/commands/consolidate.py index f5048dd3..c9f60b8c 100644 --- a/src/commands/consolidate.py +++ b/src/commands/consolidate.py @@ -34,7 +34,7 @@ from src.validators.consolidation_manager import ConsolidationManager from src.validators.exceptions import ConsolidationError from src.validators.oracles import poll_consolidation_signature -from src.validators.register_validators import submit_consolidate_validators +from src.validators.register_validators import tx_consolidate_validators from src.validators.relayer import RelayerClient from src.validators.typings import ConsolidationKeys @@ -393,7 +393,7 @@ async def process( target_source_public_keys ) - tx_hash = await submit_consolidate_validators( + tx_hash = await tx_consolidate_validators( validators=encoded_validators, oracle_signatures=oracle_signatures, tx_fee=Wei(consolidation_request_fee * consolidations_count), diff --git a/src/validators/consensus.py b/src/validators/consensus.py index b8abacd5..7191e34e 100644 --- a/src/validators/consensus.py +++ b/src/validators/consensus.py @@ -17,13 +17,13 @@ logger = logging.getLogger(__name__) -async def fetch_compounding_validators_balances() -> dict[HexStr, Gwei]: +async def fetch_funding_validators_balances() -> dict[HexStr, Gwei]: """ - Retrieves the actual balances of compounding validators in the vault. - Also includes balances from pending deposits + Retrieves the consensus balances of vault validators eligible for funding. + Includes balances from pending deposits that have not yet been processed by the consensus node. """ - vault_public_keys = {key.public_key for key in VaultValidatorCrud().get_vault_validators()} + vault_public_keys = {v.public_key for v in VaultValidatorCrud().get_vault_validators()} non_finalized_public_keys = await get_latest_vault_v2_validator_public_keys(settings.vault) vault_public_keys.update(non_finalized_public_keys) if not vault_public_keys: diff --git a/src/validators/register_validators.py b/src/validators/register_validators.py index 81d8daa8..9323a2bc 100644 --- a/src/validators/register_validators.py +++ b/src/validators/register_validators.py @@ -21,7 +21,7 @@ # pylint: disable=too-many-arguments,too-many-locals -async def register_validators( +async def tx_register_validators( approval: OraclesApproval, validators: Sequence[Validator], harvest_params: HarvestParams | None, @@ -111,7 +111,7 @@ async def register_validators( return tx_hash -async def fund_validators( +async def tx_fund_validators( validators: list[Validator], validators_manager_signature: HexStr, harvest_params: HarvestParams | None, @@ -155,7 +155,7 @@ async def fund_validators( return tx_hash -async def submit_consolidate_validators( +async def tx_consolidate_validators( validators: bytes, oracle_signatures: bytes | None, tx_fee: Wei, diff --git a/src/validators/tasks.py b/src/validators/tasks.py index 809cc1df..4ea9d772 100644 --- a/src/validators/tasks.py +++ b/src/validators/tasks.py @@ -20,14 +20,17 @@ VALIDATORS_FUNDING_BATCH_SIZE, settings, ) -from src.validators.consensus import fetch_compounding_validators_balances +from src.validators.consensus import fetch_funding_validators_balances from src.validators.database import NetworkValidatorCrud from src.validators.exceptions import EmptyRelayerResponseException, FundingException from src.validators.execution import get_withdrawable_assets from src.validators.keystores.base import BaseKeystore from src.validators.metrics import update_unused_validator_keys_metric from src.validators.oracles import poll_validation_approval -from src.validators.register_validators import fund_validators, register_validators +from src.validators.register_validators import ( + tx_fund_validators, + tx_register_validators, +) from src.validators.relayer import RelayerClient from src.validators.typings import NetworkValidator, Validator from src.validators.utils import get_validators_for_registration @@ -99,9 +102,9 @@ async def process_funding( Returns the remaining vault assets if funding is successful. Raises FundingException on failure. """ - compounding_validators_balances = await fetch_compounding_validators_balances() + validators_balances = await fetch_funding_validators_balances() funding_amounts = _get_funding_amounts( - compounding_validators_balances=compounding_validators_balances, + validators_balances=validators_balances, vault_assets=vault_assets, ) @@ -115,7 +118,7 @@ async def process_funding( list(funding_amounts.items()), VALIDATORS_FUNDING_BATCH_SIZE ): try: - tx_hash = await fund_compounding_validators( + tx_hash = await fund_validators_chunk( validator_fundings=validator_fundings_chunk, harvest_params=harvest_params, relayer=self.relayer, @@ -132,13 +135,15 @@ async def process_funding( return vault_assets -async def fund_compounding_validators( +async def fund_validators_chunk( validator_fundings: Sequence[tuple[HexStr, Gwei]], harvest_params: HarvestParams | None, relayer: RelayerClient | None = None, ) -> HexStr | None: """ - Funds vault compounding validators with the specified amount. + Funds vault validators with the specified amount. + Makes single transaction for a chunk of validators. + Returns transaction hash if funding is successful, None otherwise. """ logger.info('Started funding of %d validator(s)', len(validator_fundings)) validators_manager_signature = HexStr('0x') @@ -165,7 +170,7 @@ async def fund_compounding_validators( ) pub_keys.append(public_key) - tx_hash = await fund_validators( + tx_hash = await tx_fund_validators( harvest_params=harvest_params, validators=validators, validators_manager_signature=validators_manager_signature, @@ -244,7 +249,7 @@ async def register_new_validators( validators_registry_root=validators_registry_root, validators_manager_signature=validators_manager_signature, ) - tx_hash = await register_validators( + tx_hash = await tx_register_validators( approval=oracles_approval, validators=validators, harvest_params=harvest_params, @@ -318,12 +323,12 @@ def _get_deposits_amounts(vault_assets: Gwei, validator_type: ValidatorType) -> def _get_funding_amounts( - compounding_validators_balances: dict[HexStr, Gwei], vault_assets: Gwei + validators_balances: dict[HexStr, Gwei], vault_assets: Gwei ) -> dict[HexStr, Gwei]: result = {} # Order validators by balance descending to fund those with the highest balance first for public_key, balance in sorted( - compounding_validators_balances.items(), key=lambda item: item[1], reverse=True + validators_balances.items(), key=lambda item: item[1], reverse=True ): # Fund as much as possible to reach max balance remaining_capacity = settings.max_validator_balance_gwei - balance From 7a769ff5ca54a1bac8961c46959923c3b8002d60 Mon Sep 17 00:00:00 2001 From: Evgeny Gusarov Date: Thu, 28 May 2026 22:11:52 +0300 Subject: [PATCH 2/2] Renamed patching methods accordingly --- src/validators/signing/tests/test_common.py | 1 + src/validators/tests/test_tasks.py | 64 ++++++++++----------- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/validators/signing/tests/test_common.py b/src/validators/signing/tests/test_common.py index f20d8204..5c318532 100644 --- a/src/validators/signing/tests/test_common.py +++ b/src/validators/signing/tests/test_common.py @@ -55,6 +55,7 @@ async def test_local( fork=fork, ) + @pytest.mark.usefixtures('fake_settings') @pytest.mark.parametrize( ['_mocked_oracle_committee'], [ diff --git a/src/validators/tests/test_tasks.py b/src/validators/tests/test_tasks.py index e1c6ec55..f2563a19 100644 --- a/src/validators/tests/test_tasks.py +++ b/src/validators/tests/test_tasks.py @@ -134,9 +134,9 @@ def setup_method(self): @staticmethod @contextmanager - def patch_compounding_validators_balances(return_value): + def patch_funding_validators_balances(return_value): with patch( - 'src.validators.tasks.fetch_compounding_validators_balances', + 'src.validators.tasks.fetch_funding_validators_balances', new_callable=AsyncMock, return_value=return_value, ): @@ -154,9 +154,9 @@ def patch_is_funding_interval_passed(return_value): @staticmethod @contextmanager - def patch_fund_compounding_validators(return_value): + def patch_fund_validators_chunk(return_value): with patch( - 'src.validators.tasks.fund_compounding_validators', + 'src.validators.tasks.fund_validators_chunk', new_callable=AsyncMock, return_value=return_value, ) as mock_fund: @@ -179,12 +179,12 @@ def patch_settings(**kwargs): yield @pytest.mark.usefixtures('fake_settings') - async def test_no_compounding_validators(self): - """Returns vault_assets unchanged when no compounding validators exist.""" + async def test_no_funding_validators(self): + """Returns vault_assets unchanged when no funding validators exist.""" vault_assets = ether_to_gwei(100) with ( - self.patch_compounding_validators_balances({}), - self.patch_fund_compounding_validators(None) as mock_fund, + self.patch_funding_validators_balances({}), + self.patch_fund_validators_chunk(None) as mock_fund, ): result = await self.subtask.process_funding( vault_assets=vault_assets, harvest_params=None @@ -198,10 +198,8 @@ async def test_no_funding_needed(self): vault_assets = ether_to_gwei(100) pub_key = faker.validator_public_key() with ( - self.patch_compounding_validators_balances( - {pub_key: settings.max_validator_balance_gwei} - ), - self.patch_fund_compounding_validators(None) as mock_fund, + self.patch_funding_validators_balances({pub_key: settings.max_validator_balance_gwei}), + self.patch_fund_validators_chunk(None) as mock_fund, ): result = await self.subtask.process_funding( vault_assets=vault_assets, harvest_params=None @@ -215,9 +213,9 @@ async def test_funding_interval_not_passed(self): vault_assets = ether_to_gwei(100) pub_key = faker.validator_public_key() with ( - self.patch_compounding_validators_balances({pub_key: ether_to_gwei(32)}), + self.patch_funding_validators_balances({pub_key: ether_to_gwei(32)}), self.patch_is_funding_interval_passed(False), - self.patch_fund_compounding_validators(None) as mock_fund, + self.patch_fund_validators_chunk(None) as mock_fund, ): with pytest.raises(FundingException, match='Funding interval has not passed yet'): await self.subtask.process_funding(vault_assets=vault_assets, harvest_params=None) @@ -231,9 +229,9 @@ async def test_successful_funding_single_validator(self): tx_hash = HexStr('0xabc') with ( - self.patch_compounding_validators_balances({pub_key: ether_to_gwei(32)}), + self.patch_funding_validators_balances({pub_key: ether_to_gwei(32)}), self.patch_is_funding_interval_passed(True), - self.patch_fund_compounding_validators(tx_hash) as mock_fund, + self.patch_fund_validators_chunk(tx_hash) as mock_fund, ): result = await self.subtask.process_funding( vault_assets=vault_assets, harvest_params=None @@ -246,14 +244,14 @@ async def test_successful_funding_single_validator(self): @pytest.mark.usefixtures('fake_settings') async def test_funding_tx_failed(self): - """Raises FundingException when fund_compounding_validators returns None.""" + """Raises FundingException when fund_validators_chunk returns None.""" vault_assets = ether_to_gwei(100) pub_key = faker.validator_public_key() with ( - self.patch_compounding_validators_balances({pub_key: ether_to_gwei(32)}), + self.patch_funding_validators_balances({pub_key: ether_to_gwei(32)}), self.patch_is_funding_interval_passed(True), - self.patch_fund_compounding_validators(None), + self.patch_fund_validators_chunk(None), ): with pytest.raises(FundingException, match='Funding transaction failed'): await self.subtask.process_funding(vault_assets=vault_assets, harvest_params=None) @@ -268,14 +266,14 @@ async def test_funding_multiple_validators(self): with ( self.patch_settings(max_validator_balance_gwei=ether_to_gwei(64)), - self.patch_compounding_validators_balances( + self.patch_funding_validators_balances( { pub_key_1: ether_to_gwei(32), pub_key_2: ether_to_gwei(33), } ), self.patch_is_funding_interval_passed(True), - self.patch_fund_compounding_validators(tx_hash) as mock_fund, + self.patch_fund_validators_chunk(tx_hash) as mock_fund, ): result = await self.subtask.process_funding( vault_assets=vault_assets, harvest_params=None @@ -299,10 +297,10 @@ async def test_funding_multiple_validators(self): 'active_balance, exiting_balance', [(40, 32), (32, 40)], ) - async def test_fetch_compounding_filters_exiting_validators( + async def test_fetch_funding_filters_exiting_validators( self, vault_validator_crud, compounding_creds, active_balance, exiting_balance ): - """fetch_compounding_validators_balances excludes exiting/exited validators.""" + """fetch_funding_validators_balances excludes exiting/exited validators.""" pub_key_active = faker.validator_public_key() pub_key_exiting = faker.validator_public_key() @@ -345,7 +343,7 @@ async def test_fetch_compounding_filters_exiting_validators( self.patch_get_latest_vault_v2_validator_public_keys(), patch('src.validators.consensus.consensus_client', mock_consensus), self.patch_is_funding_interval_passed(True), - self.patch_fund_compounding_validators(HexStr('0xabc')) as mock_fund, + self.patch_fund_validators_chunk(HexStr('0xabc')) as mock_fund, ): vault_assets = ether_to_gwei(100) @@ -360,7 +358,7 @@ async def test_fetch_compounding_filters_exiting_validators( } assert result == Gwei(0) - async def test_fetch_compounding_includes_pending_deposits( + async def test_fetch_funding_includes_pending_deposits( self, vault_validator_crud, compounding_creds ): """Pending deposits reduce remaining capacity, causing overflow to second validator.""" @@ -416,7 +414,7 @@ async def test_fetch_compounding_includes_pending_deposits( self.patch_get_latest_vault_v2_validator_public_keys(), patch('src.validators.consensus.consensus_client', mock_consensus), self.patch_is_funding_interval_passed(True), - self.patch_fund_compounding_validators(HexStr('0xabc')) as mock_fund, + self.patch_fund_validators_chunk(HexStr('0xabc')) as mock_fund, ): vault_assets = ether_to_gwei(30) result = await self.subtask.process_funding( @@ -432,7 +430,7 @@ async def test_fetch_compounding_includes_pending_deposits( } assert result == Gwei(0) - async def test_fetch_compounding_excludes_non_compounding( + async def test_fetch_funding_excludes_non_compounding( self, vault_validator_crud, compounding_creds ): """Non-compounding (0x01) validator is excluded even when vault has excess assets.""" @@ -484,7 +482,7 @@ async def test_fetch_compounding_excludes_non_compounding( self.patch_get_latest_vault_v2_validator_public_keys(), patch('src.validators.consensus.consensus_client', mock_consensus), self.patch_is_funding_interval_passed(True), - self.patch_fund_compounding_validators(HexStr('0xabc')) as mock_fund, + self.patch_fund_validators_chunk(HexStr('0xabc')) as mock_fund, ): vault_assets = ether_to_gwei(40) result = await self.subtask.process_funding( @@ -500,8 +498,8 @@ async def test_fetch_compounding_excludes_non_compounding( assert result == ether_to_gwei(16) @pytest.mark.usefixtures('vault_validator_crud') - async def test_fetch_compounding_includes_non_finalized_keys(self, compounding_creds): - """fetch_compounding_validators_balances includes non-finalized V2 validator keys.""" + async def test_fetch_funding_includes_non_finalized_keys(self, compounding_creds): + """fetch_funding_validators_balances includes non-finalized V2 validator keys.""" pub_key = faker.validator_public_key() # No vault validators in DB — key comes from non-finalized V2 events @@ -527,7 +525,7 @@ async def test_fetch_compounding_includes_non_finalized_keys(self, compounding_c self.patch_get_latest_vault_v2_validator_public_keys({pub_key}), patch('src.validators.consensus.consensus_client', mock_consensus), self.patch_is_funding_interval_passed(True), - self.patch_fund_compounding_validators(HexStr('0xabc')) as mock_fund, + self.patch_fund_validators_chunk(HexStr('0xabc')) as mock_fund, ): vault_assets = ether_to_gwei(100) @@ -551,7 +549,7 @@ async def test_funding_batching(self): with ( self.patch_settings(max_validator_balance_gwei=ether_to_gwei(64)), - self.patch_compounding_validators_balances( + self.patch_funding_validators_balances( { pub_key_1: ether_to_gwei(32), pub_key_2: ether_to_gwei(33), @@ -559,7 +557,7 @@ async def test_funding_batching(self): } ), self.patch_is_funding_interval_passed(True), - self.patch_fund_compounding_validators(tx_hash) as mock_fund, + self.patch_fund_validators_chunk(tx_hash) as mock_fund, patch('src.validators.tasks.VALIDATORS_FUNDING_BATCH_SIZE', 2), ): vault_assets = ether_to_gwei(100)