From 6cecf70b29d07e13af71d2ea0a8b374a590e071a Mon Sep 17 00:00:00 2001 From: Evgeny Gusarov <123374581+evgeny-stakewise@users.noreply.github.com> Date: Tue, 24 Oct 2023 13:06:35 +0300 Subject: [PATCH] Add logging for submit_exit_signatures error (#211) --- pyproject.toml | 2 +- src/common/utils.py | 5 +++ src/exits/execution.py | 23 +++++++++---- src/exits/tasks.py | 3 ++ src/harvest/execution.py | 31 ++++++++++++------ src/harvest/tasks.py | 4 ++- src/validators/execution.py | 65 ++++++++++++++++++++++++------------- src/validators/tasks.py | 14 +++++--- 8 files changed, 101 insertions(+), 46 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a43ac1a6..7eb9e7ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ exclude_dirs = ["*/tests/*", "src/config/networks.py", "conftest.py"] skips = ["B608"] [tool.pylint."pre-commit-hook"] -disable = ["C0103", "C0114", "C0115", "C0116", "W0511", "W0703"] +disable = ["C0103", "C0114", "C0115", "C0116", "W0511", "W0703", "R0801"] ignore-paths=["src/.*/tests/.*", "src/test_fixtures/.*"] ignore=["conftest.py"] diff --git a/src/common/utils.py b/src/common/utils.py index a54ed2c9..3cf00cc5 100644 --- a/src/common/utils.py +++ b/src/common/utils.py @@ -8,6 +8,7 @@ import tenacity from eth_typing import BlockNumber, ChecksumAddress from web3 import Web3 +from web3.exceptions import Web3Exception from web3.types import Timestamp, Wei from src.common.clients import consensus_client @@ -59,6 +60,10 @@ def format_error(e: Exception) -> str: # str(e) returns empty string return repr(e) + if isinstance(e, Web3Exception): + # str(e) gives hex output. Not user-friendly. + return e.__class__.__name__ + return str(e) diff --git a/src/exits/execution.py b/src/exits/execution.py index 9c646edd..73ff4973 100644 --- a/src/exits/execution.py +++ b/src/exits/execution.py @@ -6,6 +6,7 @@ from src.common.clients import execution_client from src.common.contracts import keeper_contract from src.common.typings import OraclesApproval +from src.common.utils import format_error from src.config.networks import ETH_NETWORKS from src.config.settings import settings @@ -14,18 +15,26 @@ async def submit_exit_signatures( approval: OraclesApproval, -) -> HexStr: +) -> HexStr | None: """Sends updateExitSignatures transaction to keeper contract""" if settings.network not in ETH_NETWORKS: raise NotImplementedError('networks other than Ethereum not supported') logger.info('Submitting UpdateExitSignatures transaction') - tx = await keeper_contract.functions.updateExitSignatures( - settings.vault, - approval.deadline, - approval.ipfs_hash, - approval.signatures, - ).transact() + try: + tx = await keeper_contract.functions.updateExitSignatures( + settings.vault, + approval.deadline, + approval.ipfs_hash, + approval.signatures, + ).transact() + except Exception as e: + logger.error('Failed to update exit signatures: %s', format_error(e)) + + if settings.verbose: + logger.exception(e) + return None + logger.info('Waiting for transaction %s confirmation', Web3.to_hex(tx)) await execution_client.eth.wait_for_transaction_receipt(tx, timeout=300) return Web3.to_hex(tx) diff --git a/src/exits/tasks.py b/src/exits/tasks.py index a73996fb..c09a1817 100644 --- a/src/exits/tasks.py +++ b/src/exits/tasks.py @@ -105,6 +105,9 @@ async def _update_exit_signatures( log_verbose(e) tx_hash = await submit_exit_signatures(oracles_approval) + if not tx_hash: + return + logger.info( 'Successfully rotated exit signatures for validators with indexes %s, tx hash: %s', ', '.join([str(index) for index in outdated_indexes]), diff --git a/src/harvest/execution.py b/src/harvest/execution.py index a3f4ee17..d9d6a854 100644 --- a/src/harvest/execution.py +++ b/src/harvest/execution.py @@ -1,28 +1,39 @@ import logging +from eth_typing import HexStr from web3 import Web3 from src.common.clients import execution_client from src.common.contracts import vault_contract from src.common.typings import HarvestParams +from src.common.utils import format_error from src.config.networks import ETH_NETWORKS from src.config.settings import settings logger = logging.getLogger(__name__) -async def submit_harvest_transaction(harvest_params: HarvestParams) -> None: +async def submit_harvest_transaction(harvest_params: HarvestParams) -> HexStr | None: if settings.network not in ETH_NETWORKS: raise NotImplementedError('networks other than Ethereum not supported') logger.info('Submitting harvest transaction...') - tx = await vault_contract.functions.updateState( - ( - harvest_params.rewards_root, - harvest_params.reward, - harvest_params.unlocked_mev_reward, - harvest_params.proof, - ) - ).transact() - logger.info('Waiting for transaction %s confirmation', Web3.to_hex(tx)) + try: + tx = await vault_contract.functions.updateState( + ( + harvest_params.rewards_root, + harvest_params.reward, + harvest_params.unlocked_mev_reward, + harvest_params.proof, + ) + ).transact() + except Exception as e: + logger.error('Failed to harvest: %s', format_error(e)) + if settings.verbose: + logger.exception(e) + return None + + tx_hash = Web3.to_hex(tx) + logger.info('Waiting for transaction %s confirmation', tx_hash) await execution_client.eth.wait_for_transaction_receipt(tx, timeout=300) + return tx_hash diff --git a/src/harvest/tasks.py b/src/harvest/tasks.py index ee50a697..032bface 100644 --- a/src/harvest/tasks.py +++ b/src/harvest/tasks.py @@ -31,5 +31,7 @@ async def harvest_vault() -> None: return logger.info('Starting vault harvest') - await submit_harvest_transaction(harvest_params) + tx_hash = await submit_harvest_transaction(harvest_params) + if not tx_hash: + return logger.info('Successfully harvested vault') diff --git a/src/validators/execution.py b/src/validators/execution.py index 25c81480..099a523a 100644 --- a/src/validators/execution.py +++ b/src/validators/execution.py @@ -18,6 +18,7 @@ from src.common.ipfs import fetch_harvest_params from src.common.metrics import metrics from src.common.typings import OraclesApproval +from src.common.utils import format_error from src.config.networks import ETH_NETWORKS from src.config.settings import DEPOSIT_AMOUNT, settings from src.validators.database import NetworkValidatorCrud @@ -253,7 +254,7 @@ async def register_single_validator( tx_validators: list[bytes], update_state_call: HexStr | None, validators_registry_root: Bytes32, -) -> None: +) -> HexStr | None: """Registers single validator.""" if settings.network not in ETH_NETWORKS: raise NotImplementedError('networks other than Ethereum not supported') @@ -269,17 +270,27 @@ async def register_single_validator( ), multi_proof.proof, ] - if update_state_call is not None: - register_call = vault_contract.encode_abi( - fn_name='registerValidator', - args=register_call_args, - ) - tx = await vault_contract.functions.multicall([update_state_call, register_call]).transact() - else: - tx = await vault_contract.functions.registerValidator(*register_call_args).transact() - - logger.info('Waiting for transaction %s confirmation', Web3.to_hex(tx)) + try: + if update_state_call is not None: + register_call = vault_contract.encode_abi( + fn_name='registerValidator', + args=register_call_args, + ) + tx = await vault_contract.functions.multicall( + [update_state_call, register_call] + ).transact() + else: + tx = await vault_contract.functions.registerValidator(*register_call_args).transact() + except Exception as e: + logger.error('Failed to register validator: %s', format_error(e)) + if settings.verbose: + logger.exception(e) + return None + + tx_hash = Web3.to_hex(tx) + logger.info('Waiting for transaction %s confirmation', tx_hash) await execution_client.eth.wait_for_transaction_receipt(tx, timeout=300) + return tx_hash async def register_multiple_validator( @@ -288,7 +299,7 @@ async def register_multiple_validator( approval: OraclesApproval, update_state_call: HexStr | None, validators_registry_root: Bytes32, -) -> None: +) -> HexStr | None: """Registers multiple validators.""" if settings.network not in ETH_NETWORKS: raise NotImplementedError('networks other than Ethereum not supported') @@ -309,14 +320,24 @@ async def register_multiple_validator( multi_proof.proof_flags, multi_proof.proof, ] - if update_state_call is not None: - register_call = vault_contract.encode_abi( - fn_name='registerValidators', - args=register_call_args, - ) - tx = await vault_contract.functions.multicall([update_state_call, register_call]).transact() - else: - tx = await vault_contract.functions.registerValidators(*register_call_args).transact() - - logger.info('Waiting for transaction %s confirmation', Web3.to_hex(tx)) + try: + if update_state_call is not None: + register_call = vault_contract.encode_abi( + fn_name='registerValidators', + args=register_call_args, + ) + tx = await vault_contract.functions.multicall( + [update_state_call, register_call] + ).transact() + else: + tx = await vault_contract.functions.registerValidators(*register_call_args).transact() + except Exception as e: + logger.error('Failed to register validators: %s', format_error(e)) + if settings.verbose: + logger.exception(e) + return None + + tx_hash = Web3.to_hex(tx) + logger.info('Waiting for transaction %s confirmation', tx_hash) await execution_client.eth.wait_for_transaction_receipt(tx, timeout=300) + return tx_hash diff --git a/src/validators/tasks.py b/src/validators/tasks.py index c616a11d..b305d6ca 100644 --- a/src/validators/tasks.py +++ b/src/validators/tasks.py @@ -134,25 +134,29 @@ async def register_validators( if len(validators) == 1: validator = validators[0] - await register_single_validator( + tx_hash = await register_single_validator( approval=oracles_approval, multi_proof=multi_proof, tx_validators=tx_validators, update_state_call=update_state_call, validators_registry_root=registry_root, ) - logger.info('Successfully registered validator with public key %s', validator.public_key) + if tx_hash: + logger.info( + 'Successfully registered validator with public key %s', validator.public_key + ) if len(validators) > 1: - await register_multiple_validator( + tx_hash = await register_multiple_validator( approval=oracles_approval, multi_proof=multi_proof, tx_validators=tx_validators, update_state_call=update_state_call, validators_registry_root=registry_root, ) - pub_keys = ', '.join([val.public_key for val in validators]) - logger.info('Successfully registered validators with public keys %s', pub_keys) + if tx_hash: + pub_keys = ', '.join([val.public_key for val in validators]) + logger.info('Successfully registered validators with public keys %s', pub_keys) # pylint: disable-next=too-many-arguments