Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/commands/consolidate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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),
Expand Down
8 changes: 4 additions & 4 deletions src/validators/consensus.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 3 additions & 3 deletions src/validators/register_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/validators/signing/tests/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ async def test_local(
fork=fork,
)

@pytest.mark.usefixtures('fake_settings')
@pytest.mark.parametrize(
['_mocked_oracle_committee'],
[
Expand Down
27 changes: 16 additions & 11 deletions src/validators/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
)

Expand All @@ -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,
Expand All @@ -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')
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
64 changes: 31 additions & 33 deletions src/validators/tests/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
):
Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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()

Expand Down Expand Up @@ -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)
Expand All @@ -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."""
Expand Down Expand Up @@ -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(
Expand All @@ -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."""
Expand Down Expand Up @@ -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(
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -551,15 +549,15 @@ 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),
pub_key_3: ether_to_gwei(34),
}
),
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)
Expand Down
Loading