Skip to content

Commit

Permalink
Refactored exit signature fetch
Browse files Browse the repository at this point in the history
Signed-off-by: cyc60 <avsysoev60@gmail.com>
  • Loading branch information
cyc60 committed Oct 4, 2023
1 parent abe4be3 commit 6e4e240
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 86 deletions.
13 changes: 5 additions & 8 deletions src/commands/start.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,14 +298,6 @@ async def main() -> None:
if settings.enable_metrics:
await metrics_server()

# process outdated exit signatures
asyncio.create_task(
update_exit_signatures_periodically(
keystores=keystores,
remote_signer_config=remote_signer_config,
)
)

logger.info('Started operator service')
with InterruptHandler() as interrupt_handler:
while not interrupt_handler.exit:
Expand Down Expand Up @@ -333,6 +325,11 @@ async def main() -> None:
if settings.harvest_vault:
await harvest_vault_task()

# process outdated exit signatures
await update_exit_signatures_periodically(
keystores=keystores,
remote_signer_config=remote_signer_config,
)
# check balance
await check_hot_wallet_balance()

Expand Down
20 changes: 3 additions & 17 deletions src/common/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import asyncio
import logging
import time
from collections import defaultdict
from datetime import datetime, timezone
from pathlib import Path
Expand All @@ -9,7 +7,7 @@
from web3 import Web3
from web3.types import Timestamp, Wei

from src.common.clients import consensus_client, execution_client
from src.common.clients import consensus_client
from src.common.exceptions import (
InvalidOraclesRequestError,
NotEnoughOracleApprovalsError,
Expand Down Expand Up @@ -44,23 +42,11 @@ def log_verbose(e: Exception):
logger.error(repr(e))


async def wait_block_finalization(block_number: BlockNumber | None = None):
block_number = block_number or await execution_client.eth.get_block_number()
sleep_time = 0.0
async def is_block_synced(block_number: BlockNumber):
chain_head = await consensus_client.get_chain_finalized_head(
settings.network_config.SLOTS_PER_EPOCH
)
while chain_head.execution_block < block_number:
logger.info('Waiting for block %d finalization...', block_number)
await asyncio.sleep(sleep_time)
start = time.time()

chain_head = await consensus_client.get_chain_finalized_head(
settings.network_config.SLOTS_PER_EPOCH
)

elapsed = time.time() - start
sleep_time = float(settings.network_config.SECONDS_PER_BLOCK) - elapsed
return chain_head.execution_block < block_number


def get_current_timestamp() -> Timestamp:
Expand Down
50 changes: 22 additions & 28 deletions src/exits/tasks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import asyncio
import logging
import time
from random import shuffle

from eth_typing import BlockNumber, BLSPubkey
Expand All @@ -12,7 +10,7 @@
from src.common.execution import get_oracles
from src.common.metrics import metrics
from src.common.typings import Oracles
from src.common.utils import get_current_timestamp, wait_block_finalization
from src.common.utils import get_current_timestamp, is_block_synced
from src.config.settings import settings
from src.exits.consensus import get_validator_public_keys
from src.exits.execution import submit_exit_signatures
Expand All @@ -35,33 +33,29 @@ async def update_exit_signatures_periodically(
keystores: Keystores,
remote_signer_config: RemoteSignerConfiguration | None,
):
while True:
timer_start = time.time()
try:
oracles = await get_oracles()
outdated_indexes = await _fetch_outdated_indexes(oracles)

if outdated_indexes:
await _update_exit_signatures(
keystores=keystores,
remote_signer_config=remote_signer_config,
oracles=oracles,
outdated_indexes=outdated_indexes,
)
except Exception as e:
logger.exception(e)

elapsed = time.time() - timer_start
await asyncio.sleep(float(settings.network_config.SECONDS_PER_BLOCK) - elapsed)
oracles = await get_oracles()
update_block = await _fetch_last_update_block()
if update_block and await is_block_synced(update_block):
logger.info('Waiting for block %d finalization...', update_block)
return
outdated_indexes = await _fetch_outdated_indexes(oracles, update_block)
if outdated_indexes:
await _update_exit_signatures(
keystores=keystores,
remote_signer_config=remote_signer_config,
oracles=oracles,
outdated_indexes=outdated_indexes,
)


async def _fetch_last_update_block() -> BlockNumber | None:
last_event = await keeper_contract.get_exit_signatures_updated_event(vault=settings.vault)
if last_event:
return BlockNumber(last_event['blockNumber'])
return None


async def _fetch_outdated_indexes(oracles) -> list[int]:
last_event = await keeper_contract.get_exit_signatures_updated_event(vault=settings.vault)
if not last_event:
update_block = None
else:
update_block = BlockNumber(last_event['blockNumber'])
await wait_block_finalization(update_block)
async def _fetch_outdated_indexes(oracles: Oracles, update_block: BlockNumber | None) -> list[int]:
endpoints = [endpoint for replicas in oracles.endpoints for endpoint in replicas]
shuffle(endpoints)

Expand Down
35 changes: 2 additions & 33 deletions src/exits/tests/test_tasks.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,20 @@
import asyncio
from pathlib import Path
from random import randint
from typing import Callable
from unittest import mock

import pytest
from eth_typing import BlockNumber, ChecksumAddress
from eth_typing import ChecksumAddress
from sw_utils.typings import ConsensusFork

from src.common.typings import Oracles
from src.common.utils import get_current_timestamp
from src.config.settings import settings
from src.exits.tasks import _get_oracles_request, _wait_oracle_signature_update
from src.exits.tasks import _get_oracles_request
from src.validators.signing.remote import RemoteSignerConfiguration
from src.validators.typings import ExitSignatureShards, Keystores


@pytest.mark.usefixtures('fake_settings')
class TestWaitOracleSignatureUpdate:
async def test_normal(self):
update_block = BlockNumber(3)
with (
mock.patch('asyncio.sleep'),
mock.patch('src.exits.tasks.time.time', return_value=100),
mock.patch(
'src.exits.tasks._fetch_exit_signature_block', side_effect=[None, 1, 2, 3]
) as fetch_mock,
):
await _wait_oracle_signature_update(update_block, 'http://oracle', max_time=5)

assert fetch_mock.call_count == 4

async def test_timeout(self):
update_block = BlockNumber(3)
with (
mock.patch('asyncio.sleep'),
mock.patch('src.exits.tasks.time.time', side_effect=[100, 103, 106]),
mock.patch(
'src.exits.tasks._fetch_exit_signature_block', return_value=None
) as fetch_mock,
pytest.raises(asyncio.TimeoutError),
):
await _wait_oracle_signature_update(update_block, 'http://oracle', max_time=5)

assert fetch_mock.call_count == 2


@pytest.mark.usefixtures('fake_settings')
class TestGetOraclesRequest:
async def test_local_keystores(
Expand Down

0 comments on commit 6e4e240

Please sign in to comment.