From 73a6c002518efdf107b0a42a0471d668d9e8c3e9 Mon Sep 17 00:00:00 2001 From: madlabman <10616301+madlabman@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:25:22 +0100 Subject: [PATCH 1/5] feat: use is_electra_activated --- src/modules/accounting/accounting.py | 3 +- src/modules/ejector/ejector.py | 14 ++- src/providers/consensus/client.py | 5 ++ src/providers/consensus/types.py | 2 +- src/services/bunker.py | 10 ++- .../bunker_cases/abnormal_cl_rebase.py | 3 +- .../bunker_cases/midterm_slashing_penalty.py | 67 +++++++------- .../bunker/test_bunker_midterm_penalty.py | 88 +++++++++---------- tests/modules/ejector/test_ejector.py | 2 +- .../consensus/test_consensus_client.py | 7 +- 10 files changed, 107 insertions(+), 94 deletions(-) diff --git a/src/modules/accounting/accounting.py b/src/modules/accounting/accounting.py index eacf337e7..e929a2edb 100644 --- a/src/modules/accounting/accounting.py +++ b/src/modules/accounting/accounting.py @@ -219,8 +219,7 @@ def _get_consensus_lido_state(self, blockstamp: ReferenceBlockStamp) -> tuple[Va consensus_version = self.w3.lido_contracts.accounting_oracle.get_consensus_version(blockstamp.block_hash) if consensus_version > 2: - spec = self.w3.cc.get_config_spec() - if blockstamp.ref_epoch >= int(spec.ELECTRA_FORK_EPOCH): + if self.w3.cc.is_electra_activated(blockstamp.ref_epoch): state = self.w3.cc.get_state_view(blockstamp) total_lido_eth1_bridge_deposits_amount = self.w3.lido_validators.calculate_total_eth1_bridge_deposits_amount( lido_validators, state.pending_deposits diff --git a/src/modules/ejector/ejector.py b/src/modules/ejector/ejector.py index 6b0e5992c..46b4f5280 100644 --- a/src/modules/ejector/ejector.py +++ b/src/modules/ejector/ejector.py @@ -236,12 +236,11 @@ def _get_predicted_withdrawable_epoch( """ Returns epoch when all validators in queue and validators_to_eject will be withdrawn. """ - spec = self.w3.cc.get_config_spec() - if blockstamp.ref_epoch < int(spec.ELECTRA_FORK_EPOCH): - return self._get_predicted_withdrawable_epoch_pre_electra(blockstamp, validators_to_eject) + if self.w3.cc.is_electra_activated(blockstamp.ref_epoch): + return self._get_predicted_withdrawable_epoch_post_electra(blockstamp, validators_to_eject) - return self._get_predicted_withdrawable_epoch_post_electra(blockstamp, validators_to_eject) + return self._get_predicted_withdrawable_epoch_pre_electra(blockstamp, validators_to_eject) def _get_predicted_withdrawable_epoch_pre_electra( self, @@ -320,11 +319,10 @@ def _get_latest_exit_epoch(self, blockstamp: ReferenceBlockStamp) -> tuple[Epoch @lru_cache(maxsize=1) def _get_sweep_delay_in_epochs(self, blockstamp: ReferenceBlockStamp) -> int: """Returns amount of epochs that will take to sweep all validators in chain.""" - spec = self.w3.cc.get_config_spec() - chain_config = self.get_chain_config(blockstamp) - electra_epoch = int(spec.ELECTRA_FORK_EPOCH) - if self.get_consensus_version(blockstamp) < 3 or blockstamp.ref_epoch < electra_epoch: + if self.get_consensus_version(blockstamp) < 3 or not self.w3.cc.is_electra_activated(blockstamp.ref_epoch): return self._get_sweep_delay_in_epochs_pre_electra(blockstamp) + + chain_config = self.get_chain_config(blockstamp) state = self.w3.cc.get_state_view(blockstamp) return get_sweep_delay_in_epochs_post_electra(state, chain_config) diff --git a/src/providers/consensus/client.py b/src/providers/consensus/client.py index c0fc6b9c8..2fcf9cc9c 100644 --- a/src/providers/consensus/client.py +++ b/src/providers/consensus/client.py @@ -40,6 +40,7 @@ class ConsensusClient(HTTPProvider): state_id State identifier. Can be one of: "head" (canonical head in node's view), "genesis", "finalized", "justified", , . """ + PROVIDER_EXCEPTION = ConsensusClientError PROMETHEUS_HISTOGRAM = CL_REQUESTS_DURATION @@ -52,6 +53,10 @@ class ConsensusClient(HTTPProvider): API_GET_SPEC = 'eth/v1/config/spec' API_GET_GENESIS = 'eth/v1/beacon/genesis' + def is_electra_activated(self, epoch: EpochNumber) -> bool: + spec = self.get_config_spec() + return epoch >= spec.ELECTRA_FORK_EPOCH + def get_config_spec(self) -> BeaconSpecResponse: """Spec: https://ethereum.github.io/beacon-APIs/#/Config/getSpec""" data, _ = self._get(self.API_GET_SPEC) diff --git a/src/providers/consensus/types.py b/src/providers/consensus/types.py index 77176408e..2c673487b 100644 --- a/src/providers/consensus/types.py +++ b/src/providers/consensus/types.py @@ -13,7 +13,7 @@ class BeaconSpecResponse(FromResponse): SECONDS_PER_SLOT: str DEPOSIT_CONTRACT_ADDRESS: str SLOTS_PER_HISTORICAL_ROOT: str - ELECTRA_FORK_EPOCH: str = str(FAR_FUTURE_EPOCH) + ELECTRA_FORK_EPOCH: int = FAR_FUTURE_EPOCH @dataclass diff --git a/src/services/bunker.py b/src/services/bunker.py index 34f8b2dd5..15d25a1a7 100644 --- a/src/services/bunker.py +++ b/src/services/bunker.py @@ -74,11 +74,17 @@ def is_bunker_mode( logger.info({"msg": "Bunker ON. CL rebase is negative"}) return True - cl_spec = self.w3.cc.get_config_spec() consensus_version = self.w3.lido_contracts.accounting_oracle.get_consensus_version(blockstamp.block_hash) web3_converter = Web3Converter(chain_config, frame_config) high_midterm_slashing_penalty = MidtermSlashingPenalty.is_high_midterm_slashing_penalty( - blockstamp, consensus_version, cl_spec, web3_converter, all_validators, lido_validators, current_report_cl_rebase, last_report_ref_slot + blockstamp, + consensus_version, + self.w3.cc.is_electra_activated, + web3_converter, + all_validators, + lido_validators, + current_report_cl_rebase, + last_report_ref_slot, ) if high_midterm_slashing_penalty: logger.info({"msg": "Bunker ON. High midterm slashing penalty"}) diff --git a/src/services/bunker_cases/abnormal_cl_rebase.py b/src/services/bunker_cases/abnormal_cl_rebase.py index a5ecf76a8..1b1c98e72 100644 --- a/src/services/bunker_cases/abnormal_cl_rebase.py +++ b/src/services/bunker_cases/abnormal_cl_rebase.py @@ -226,8 +226,7 @@ def _get_lido_validators_balance_with_vault( consensus_version = self.w3.lido_contracts.accounting_oracle.get_consensus_version(blockstamp.block_hash) if consensus_version > 2: epoch = EpochNumber(blockstamp.slot_number // self.c_conf.slots_per_epoch) - spec = self.w3.cc.get_config_spec() - if epoch >= int(spec.ELECTRA_FORK_EPOCH): + if self.w3.cc.is_electra_activated(epoch): state = self.w3.cc.get_state_view(blockstamp) total_eth1_bridge_deposits_amount = LidoValidatorsProvider.calculate_total_eth1_bridge_deposits_amount( lido_validators, state.pending_deposits diff --git a/src/services/bunker_cases/midterm_slashing_penalty.py b/src/services/bunker_cases/midterm_slashing_penalty.py index 3d3d08b22..1f46c294a 100644 --- a/src/services/bunker_cases/midterm_slashing_penalty.py +++ b/src/services/bunker_cases/midterm_slashing_penalty.py @@ -1,15 +1,16 @@ import logging from collections import defaultdict +from typing import Callable from src.constants import ( + EFFECTIVE_BALANCE_INCREMENT, EPOCHS_PER_SLASHINGS_VECTOR, + MAX_EFFECTIVE_BALANCE, MIN_VALIDATOR_WITHDRAWABILITY_DELAY, PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX, - EFFECTIVE_BALANCE_INCREMENT, - MAX_EFFECTIVE_BALANCE, ) -from src.providers.consensus.types import Validator, BeaconSpecResponse -from src.types import EpochNumber, Gwei, ReferenceBlockStamp, FrameNumber, SlotNumber +from src.providers.consensus.types import Validator +from src.types import EpochNumber, FrameNumber, Gwei, ReferenceBlockStamp, SlotNumber from src.utils.validator_state import calculate_total_active_effective_balance from src.utils.web3converter import Web3Converter from src.web3py.extensions.lido_validators import LidoValidator @@ -20,17 +21,16 @@ class MidtermSlashingPenalty: - @staticmethod def is_high_midterm_slashing_penalty( blockstamp: ReferenceBlockStamp, consensus_version: int, - cl_spec: BeaconSpecResponse, + is_electra_activated: Callable[[EpochNumber], bool], web3_converter: Web3Converter, all_validators: list[Validator], lido_validators: list[LidoValidator], current_report_cl_rebase: Gwei, - last_report_ref_slot: SlotNumber + last_report_ref_slot: SlotNumber, ) -> bool: """ Check if there is a high midterm slashing penalty in the future frames. @@ -66,8 +66,14 @@ def is_high_midterm_slashing_penalty( blockstamp.ref_epoch, all_slashed_validators, total_balance, future_frames_lido_validators ) else: - frames_lido_midterm_penalties = MidtermSlashingPenalty.get_future_midterm_penalty_sum_in_frames_post_electra( - blockstamp.ref_epoch, cl_spec, all_slashed_validators, total_balance, future_frames_lido_validators, + frames_lido_midterm_penalties = ( + MidtermSlashingPenalty.get_future_midterm_penalty_sum_in_frames_post_electra( + blockstamp.ref_epoch, + is_electra_activated, + all_slashed_validators, + total_balance, + future_frames_lido_validators, + ) ) max_lido_midterm_penalty = max(frames_lido_midterm_penalties.values()) logger.info({"msg": f"Max lido midterm penalty: {max_lido_midterm_penalty}"}) @@ -84,8 +90,7 @@ def is_high_midterm_slashing_penalty( @staticmethod def get_slashed_validators_with_impact_on_midterm_penalties( - validators: list[Validator], - ref_epoch: EpochNumber + validators: list[Validator], ref_epoch: EpochNumber ) -> list[Validator]: """ Get slashed validators which have impact on midterm penalties @@ -104,6 +109,7 @@ def get_slashed_validators_with_impact_on_midterm_penalties( https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#slash_validator """ + def is_have_impact(v: Validator) -> bool: return v.validator.slashed and int(v.validator.withdrawable_epoch) > ref_epoch @@ -169,11 +175,10 @@ def get_future_midterm_penalty_sum_in_frames_pre_electra( """Calculate sum of midterm penalties in each frame""" per_frame_midterm_penalty_sum: dict[FrameNumber, Gwei] = {} for (frame_number, _), validators_in_future_frame in per_frame_validators.items(): - per_frame_midterm_penalty_sum[frame_number] = MidtermSlashingPenalty.predict_midterm_penalty_in_frame_pre_electra( - ref_epoch, - all_slashed_validators, - total_balance, - validators_in_future_frame + per_frame_midterm_penalty_sum[frame_number] = ( + MidtermSlashingPenalty.predict_midterm_penalty_in_frame_pre_electra( + ref_epoch, all_slashed_validators, total_balance, validators_in_future_frame + ) ) return per_frame_midterm_penalty_sum @@ -183,7 +188,7 @@ def predict_midterm_penalty_in_frame_pre_electra( ref_epoch: EpochNumber, all_slashed_validators: list[Validator], total_balance: Gwei, - midterm_penalized_validators_in_frame: list[LidoValidator] + midterm_penalized_validators_in_frame: list[LidoValidator], ) -> Gwei: """Predict penalty in frame""" penalty_in_frame = 0 @@ -200,7 +205,7 @@ def predict_midterm_penalty_in_frame_pre_electra( @staticmethod def get_future_midterm_penalty_sum_in_frames_post_electra( ref_epoch: EpochNumber, - cl_spec: BeaconSpecResponse, + is_electra_activated: Callable[[EpochNumber], bool], all_slashed_validators: list[Validator], total_balance: Gwei, per_frame_validators: SlashedValidatorsFrameBuckets, @@ -208,13 +213,15 @@ def get_future_midterm_penalty_sum_in_frames_post_electra( """Calculate sum of midterm penalties in each frame""" per_frame_midterm_penalty_sum: dict[FrameNumber, Gwei] = {} for (frame_number, frame_ref_epoch), validators_in_future_frame in per_frame_validators.items(): - per_frame_midterm_penalty_sum[frame_number] = MidtermSlashingPenalty.predict_midterm_penalty_in_frame_post_electra( - ref_epoch, - frame_ref_epoch, - cl_spec, - all_slashed_validators, - total_balance, - validators_in_future_frame + per_frame_midterm_penalty_sum[frame_number] = ( + MidtermSlashingPenalty.predict_midterm_penalty_in_frame_post_electra( + ref_epoch, + frame_ref_epoch, + is_electra_activated, + all_slashed_validators, + total_balance, + validators_in_future_frame, + ) ) return per_frame_midterm_penalty_sum @@ -223,10 +230,10 @@ def get_future_midterm_penalty_sum_in_frames_post_electra( def predict_midterm_penalty_in_frame_post_electra( report_ref_epoch: EpochNumber, frame_ref_epoch: EpochNumber, - cl_spec: BeaconSpecResponse, + is_electra_activated: Callable[[EpochNumber], bool], all_slashed_validators: list[Validator], total_balance: Gwei, - midterm_penalized_validators_in_frame: list[LidoValidator] + midterm_penalized_validators_in_frame: list[LidoValidator], ) -> Gwei: """Predict penalty in frame""" penalty_in_frame = 0 @@ -236,7 +243,7 @@ def predict_midterm_penalty_in_frame_post_electra( report_ref_epoch, all_slashed_validators, EpochNumber(midterm_penalty_epoch) ) - if frame_ref_epoch < int(cl_spec.ELECTRA_FORK_EPOCH): + if not is_electra_activated(frame_ref_epoch): penalty_in_frame += MidtermSlashingPenalty.get_validator_midterm_penalty( validator, len(bound_slashed_validators), total_balance ) @@ -250,7 +257,7 @@ def predict_midterm_penalty_in_frame_post_electra( def get_validator_midterm_penalty( validator: LidoValidator, bound_slashed_validators_count: int, - total_balance: Gwei + total_balance: Gwei, ) -> Gwei: """ Calculate midterm penalty for particular validator @@ -312,7 +319,7 @@ def get_frame_cl_rebase_from_report_cl_rebase( web3_converter: Web3Converter, report_cl_rebase: Gwei, curr_report_blockstamp: ReferenceBlockStamp, - last_report_ref_slot: SlotNumber + last_report_ref_slot: SlotNumber, ) -> Gwei: """Get frame rebase from report rebase""" last_report_ref_epoch = web3_converter.get_epoch_by_slot(last_report_ref_slot) diff --git a/tests/modules/accounting/bunker/test_bunker_midterm_penalty.py b/tests/modules/accounting/bunker/test_bunker_midterm_penalty.py index 8932bb1df..0d0e58a31 100644 --- a/tests/modules/accounting/bunker/test_bunker_midterm_penalty.py +++ b/tests/modules/accounting/bunker/test_bunker_midterm_penalty.py @@ -2,12 +2,12 @@ import pytest -from src.constants import MAX_EFFECTIVE_BALANCE_ELECTRA, MAX_EFFECTIVE_BALANCE +from src.constants import MAX_EFFECTIVE_BALANCE, MAX_EFFECTIVE_BALANCE_ELECTRA from src.modules.submodules.consensus import FrameConfig from src.modules.submodules.types import ChainConfig from src.providers.consensus.types import Validator, ValidatorState from src.services.bunker_cases.midterm_slashing_penalty import MidtermSlashingPenalty -from src.types import EpochNumber, ReferenceBlockStamp, Gwei +from src.types import EpochNumber, Gwei, ReferenceBlockStamp, SlotNumber from src.utils.web3converter import Web3Converter @@ -45,15 +45,6 @@ def simple_validators( return validators -TEST_ELECTRA_FORK_EPOCH = 450 - - -@pytest.fixture(params=[TEST_ELECTRA_FORK_EPOCH]) -def spec_with_electra(request): - # sets the electra fork epoch to the test value for calculating the penalty - return Mock(ELECTRA_FORK_EPOCH=request.param) - - @pytest.mark.unit @pytest.mark.parametrize( ("blockstamp", "all_validators", "lido_validators", "report_cl_rebase", "expected_result"), @@ -122,7 +113,6 @@ def spec_with_electra(request): def test_is_high_midterm_slashing_penalty_pre_electra( blockstamp, all_validators, lido_validators, report_cl_rebase, expected_result ): - cl_spec = Mock() chain_config = ChainConfig( slots_per_epoch=32, seconds_per_slot=12, @@ -136,7 +126,14 @@ def test_is_high_midterm_slashing_penalty_pre_electra( web3_converter = Web3Converter(chain_config, frame_config) result = MidtermSlashingPenalty.is_high_midterm_slashing_penalty( - blockstamp, 2, cl_spec, web3_converter, all_validators, lido_validators, report_cl_rebase, 0 + blockstamp, + 2, + lambda _: True, # FIXME: doens't affect the test outcome + web3_converter, + all_validators, + lido_validators, + report_cl_rebase, + 0, ) assert result == expected_result @@ -207,7 +204,7 @@ def test_is_high_midterm_slashing_penalty_pre_electra( ], ) def test_is_high_midterm_slashing_penalty_post_electra( - blockstamp, spec_with_electra, all_validators, lido_validators, report_cl_rebase, expected_result + blockstamp, all_validators, lido_validators, report_cl_rebase, expected_result ): chain_config = ChainConfig( slots_per_epoch=32, @@ -223,12 +220,12 @@ def test_is_high_midterm_slashing_penalty_post_electra( result = MidtermSlashingPenalty.is_high_midterm_slashing_penalty( blockstamp, 3, - spec_with_electra, + lambda _: True, # FIXME: It doesn't change the test outcome. web3_converter, all_validators, lido_validators, report_cl_rebase, - 0, + SlotNumber(0), ) assert result == expected_result @@ -259,7 +256,7 @@ def test_is_high_midterm_slashing_penalty_post_electra( ), ], ) -def test_get_possible_slashed_epochs(validator, spec_with_electra, ref_epoch, expected_result): +def test_get_possible_slashed_epochs(validator, ref_epoch, expected_result): result = MidtermSlashingPenalty.get_possible_slashed_epochs(validator, ref_epoch) assert result == expected_result @@ -297,7 +294,7 @@ def test_get_possible_slashed_epochs(validator, spec_with_electra, ref_epoch, ex ], ) def test_get_per_frame_lido_validators_with_future_midterm_epoch( - ref_epoch, spec_with_electra, future_midterm_penalty_lido_slashed_validators, expected_result + ref_epoch, future_midterm_penalty_lido_slashed_validators, expected_result ): chain_config = ChainConfig( slots_per_epoch=32, @@ -393,7 +390,7 @@ def test_get_future_midterm_penalty_sum_in_frames_pre_electra( @pytest.mark.parametrize( ( "ref_epoch", - "spec_with_electra", + "is_electra_activated", "per_frame_validators", "all_slashed_validators", "active_validators_count", @@ -404,7 +401,7 @@ def test_get_future_midterm_penalty_sum_in_frames_pre_electra( ( # one is slashed before electra 225, - 4500, + lambda epoch: epoch >= 4500, {(18, 4049): simple_validators(0, 0, slashed=True)}, simple_validators(0, 0, slashed=True), 50000, @@ -413,7 +410,7 @@ def test_get_future_midterm_penalty_sum_in_frames_pre_electra( ( # one is slashed after electra 225, - 225, + lambda epoch: epoch >= 225, {(18, 4049): simple_validators(0, 0, slashed=True)}, simple_validators(0, 0, slashed=True), 50000, @@ -422,7 +419,7 @@ def test_get_future_midterm_penalty_sum_in_frames_pre_electra( ( # all are slashed before electra 225, - 4500, + lambda epoch: epoch >= 4500, {(18, 4049): simple_validators(0, 99, slashed=True)}, simple_validators(0, 99, slashed=True), 50000, @@ -431,7 +428,7 @@ def test_get_future_midterm_penalty_sum_in_frames_pre_electra( ( # all are slashed after electra 225, - 225, + lambda epoch: epoch >= 225, {(18, 4049): simple_validators(0, 99, slashed=True)}, simple_validators(0, 99, slashed=True), 50000, @@ -440,7 +437,7 @@ def test_get_future_midterm_penalty_sum_in_frames_pre_electra( ( # slashed in different frames with determined slashing epochs in different forks 225, - 4500, + lambda epoch: epoch >= 4500, { (18, 4049): simple_validators(0, 0, slashed=True), (19, 4724): simple_validators(10, 59, slashed=True, exit_epoch="8000", withdrawable_epoch="8417"), @@ -455,7 +452,7 @@ def test_get_future_midterm_penalty_sum_in_frames_pre_electra( ( # slashed in different epochs in different frames without determined slashing epochs in different forks 225, - 4500, + lambda epoch: epoch >= 4500, { (18, 4049): [ *simple_validators(0, 5), @@ -476,11 +473,10 @@ def test_get_future_midterm_penalty_sum_in_frames_pre_electra( {18: 0, 19: 5_760_000_000}, ), ], - indirect=["spec_with_electra"], ) def test_get_future_midterm_penalty_sum_in_frames_post_electra( ref_epoch, - spec_with_electra, + is_electra_activated, per_frame_validators, all_slashed_validators, active_validators_count, @@ -488,7 +484,7 @@ def test_get_future_midterm_penalty_sum_in_frames_post_electra( ): result = MidtermSlashingPenalty.get_future_midterm_penalty_sum_in_frames_post_electra( EpochNumber(ref_epoch), - spec_with_electra, + is_electra_activated, all_slashed_validators, active_validators_count * 32 * 10**9, per_frame_validators, @@ -556,7 +552,7 @@ def test_predict_midterm_penalty_in_frame_pre_electra( @pytest.mark.parametrize( ( "ref_epoch", - "is_after_electra", + "is_electra_activated", "all_slashed_validators", "total_balance", "validators_in_frame", @@ -568,7 +564,7 @@ def test_predict_midterm_penalty_in_frame_pre_electra( ( # one is slashed 225, - False, + lambda _: False, simple_validators(0, 0, slashed=True), 100 * 32 * 10**9, simple_validators(0, 0, slashed=True), @@ -577,7 +573,7 @@ def test_predict_midterm_penalty_in_frame_pre_electra( ( # all are slashed 225, - False, + lambda _: False, simple_validators(0, 99, slashed=True), 100 * 32 * 10**9, simple_validators(0, 99, slashed=True), @@ -586,7 +582,7 @@ def test_predict_midterm_penalty_in_frame_pre_electra( ( # several are slashed 225, - False, + lambda _: False, simple_validators(0, 9, slashed=True), 100 * 32 * 10**9, simple_validators(0, 9, slashed=True), @@ -595,7 +591,7 @@ def test_predict_midterm_penalty_in_frame_pre_electra( ( # slashed in different epochs in different frames without determined slashing epochs 225, - False, + lambda _: False, [ *simple_validators(0, 5, slashed=True), *simple_validators(6, 9, slashed=True, exit_epoch="8192", withdrawable_epoch="8197"), @@ -608,11 +604,18 @@ def test_predict_midterm_penalty_in_frame_pre_electra( 10 * 9 * 10**9, ), # AFTER ELECTRA - (225, True, [], 100 * 32 * 10**9, [], 0), + ( + 225, + lambda _: True, + [], + 100 * 32 * 10**9, + [], + 0, + ), ( # one is slashed 225, - True, + lambda _: True, simple_validators(0, 0, slashed=True), 100 * 32 * 10**9, simple_validators(0, 0, slashed=True), @@ -621,7 +624,7 @@ def test_predict_midterm_penalty_in_frame_pre_electra( ( # all are slashed 225, - True, + lambda _: True, simple_validators(0, 99, slashed=True), 100 * 32 * 10**9, simple_validators(0, 99, slashed=True), @@ -630,7 +633,7 @@ def test_predict_midterm_penalty_in_frame_pre_electra( ( # several are slashed 225, - True, + lambda _: True, simple_validators(0, 9, slashed=True), 100 * 32 * 10**9, simple_validators(0, 9, slashed=True), @@ -639,7 +642,7 @@ def test_predict_midterm_penalty_in_frame_pre_electra( ( # slashed in different epochs in different frames without determined slashing epochs 225, - True, + lambda _: True, [ *simple_validators(0, 5, slashed=True), *simple_validators(6, 9, slashed=True, exit_epoch="8192", withdrawable_epoch="8197"), @@ -655,19 +658,16 @@ def test_predict_midterm_penalty_in_frame_pre_electra( ) def test_predict_midterm_penalty_in_frame_post_electra( ref_epoch, - is_after_electra, + is_electra_activated, all_slashed_validators, total_balance, validators_in_frame, expected_result, - spec_with_electra, ): result = MidtermSlashingPenalty.predict_midterm_penalty_in_frame_post_electra( report_ref_epoch=EpochNumber(ref_epoch), - frame_ref_epoch=EpochNumber( - spec_with_electra.ELECTRA_FORK_EPOCH if is_after_electra else spec_with_electra.ELECTRA_FORK_EPOCH - 1 - ), - cl_spec=spec_with_electra, + frame_ref_epoch=Mock(), + is_electra_activated=is_electra_activated, all_slashed_validators=all_slashed_validators, total_balance=total_balance, midterm_penalized_validators_in_frame=validators_in_frame, diff --git a/tests/modules/ejector/test_ejector.py b/tests/modules/ejector/test_ejector.py index 2d7c29799..7146232eb 100644 --- a/tests/modules/ejector/test_ejector.py +++ b/tests/modules/ejector/test_ejector.py @@ -221,7 +221,7 @@ def test_is_contract_reportable(ejector: Ejector, blockstamp: BlockStamp) -> Non @pytest.mark.unit def test_get_predicted_withdrawable_epoch_pre_electra(ejector: Ejector) -> None: ejector.w3.cc = Mock() - ejector.w3.cc.get_config_spec = Mock(return_value=Mock(ELECTRA_FORK_EPOCH=FAR_FUTURE_EPOCH)) + ejector.w3.cc.is_electra_activated = Mock(return_value=False) ejector._get_latest_exit_epoch = Mock(return_value=[1, 32]) ejector._get_churn_limit = Mock(return_value=2) ref_blockstamp = ReferenceBlockStampFactory.build(ref_epoch=3546) diff --git a/tests/providers/consensus/test_consensus_client.py b/tests/providers/consensus/test_consensus_client.py index 79dde0f6b..ba51aa51f 100644 --- a/tests/providers/consensus/test_consensus_client.py +++ b/tests/providers/consensus/test_consensus_client.py @@ -7,7 +7,7 @@ from src.providers.consensus.client import ConsensusClient from src.providers.consensus.types import Validator -from src.types import SlotNumber +from src.types import EpochNumber, SlotNumber from src.utils.blockstamp import build_blockstamp from src.variables import CONSENSUS_CLIENT_URI from tests.factory.blockstamp import BlockStampFactory @@ -79,9 +79,8 @@ def test_get_state_view(consensus_client: ConsensusClient): state_view = consensus_client.get_state_view(blockstamp) assert state_view.slot == blockstamp.slot_number - spec = consensus_client.get_config_spec() - epoch = int(state_view.slot) // 32 - if epoch >= int(spec.ELECTRA_FORK_EPOCH): + epoch = EpochNumber(state_view.slot // 32) + if consensus_client.is_electra_activated(epoch): assert state_view.earliest_exit_epoch != 0 assert state_view.exit_balance_to_consume >= 0 From 06338c18b0edbd2b2a56a639414e456cef9be94c Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Fri, 24 Jan 2025 12:30:07 +0100 Subject: [PATCH 2/5] fix: testdata for `test_is_high_midterm_slashing_penalty_*` --- .../bunker/test_bunker_midterm_penalty.py | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/tests/modules/accounting/bunker/test_bunker_midterm_penalty.py b/tests/modules/accounting/bunker/test_bunker_midterm_penalty.py index 0d0e58a31..d1eb04395 100644 --- a/tests/modules/accounting/bunker/test_bunker_midterm_penalty.py +++ b/tests/modules/accounting/bunker/test_bunker_midterm_penalty.py @@ -79,33 +79,33 @@ def simple_validators( ( # one day since last report, penalty greater than report rebase simple_blockstamp(225 * 32), - [*simple_validators(0, 49), *simple_validators(50, 99, slashed=True)], - simple_validators(50, 99, slashed=True), - 49 * 32 * 10**9, + [*simple_validators(0, 999), *simple_validators(1000, 1049, slashed=True)], + simple_validators(1000, 1049, slashed=True), + 199 * 10**9, True, ), ( # three days since last report, penalty greater than frame rebase simple_blockstamp(3 * 225 * 32), - [*simple_validators(0, 49), *simple_validators(50, 99, slashed=True)], - simple_validators(50, 99, slashed=True), - 3 * 49 * 32 * 10**9, + [*simple_validators(0, 999), *simple_validators(1000, 1049, slashed=True)], + simple_validators(1000, 1049, slashed=True), + 3 * 199 * 10**9, True, ), ( - # one day since last report,penalty equal report rebase + # one day since last report, penalty equal report rebase simple_blockstamp(225 * 32), - [*simple_validators(0, 49), *simple_validators(50, 99, slashed=True)], - simple_validators(50, 99, slashed=True), - 50 * 32 * 10**9, + [*simple_validators(0, 999), *simple_validators(1000, 1049, slashed=True)], + simple_validators(1000, 1049, slashed=True), + 200 * 10**9, False, ), ( # one day since last report, penalty less report rebase simple_blockstamp(225 * 32), - [*simple_validators(0, 49), *simple_validators(50, 99, slashed=True)], - simple_validators(50, 99, slashed=True), - 51 * 32 * 10**9, + [*simple_validators(0, 999), *simple_validators(1000, 1049, slashed=True)], + simple_validators(1000, 1049, slashed=True), + 201 * 10**9, False, ), ], @@ -128,12 +128,12 @@ def test_is_high_midterm_slashing_penalty_pre_electra( result = MidtermSlashingPenalty.is_high_midterm_slashing_penalty( blockstamp, 2, - lambda _: True, # FIXME: doens't affect the test outcome + lambda _: True, # doesn't matter because consensus version == 2 web3_converter, all_validators, lido_validators, report_cl_rebase, - 0, + SlotNumber(0), ) assert result == expected_result @@ -172,33 +172,33 @@ def test_is_high_midterm_slashing_penalty_pre_electra( ( # one day since last report, penalty greater than report rebase simple_blockstamp(225 * 32), - [*simple_validators(0, 49), *simple_validators(50, 99, slashed=True)], - simple_validators(50, 99, slashed=True), - 49 * 32 * 10**9, + [*simple_validators(0, 999), *simple_validators(1000, 1049, slashed=True)], + simple_validators(1000, 1049, slashed=True), + 199 * 10**9, True, ), ( # three days since last report, penalty greater than frame rebase simple_blockstamp(3 * 225 * 32), - [*simple_validators(0, 49), *simple_validators(50, 99, slashed=True)], - simple_validators(50, 99, slashed=True), - 3 * 49 * 32 * 10**9, - True, + [*simple_validators(0, 999), *simple_validators(1000, 1049, slashed=True)], + simple_validators(1000, 1049, slashed=True), + 3 * 199 * 10**9, + True, # Because penalty is 200 * 10**9 than one frame rebase ), ( - # one day since last report,penalty equal report rebase + # one day since last report, penalty equal report rebase simple_blockstamp(225 * 32), - [*simple_validators(0, 49), *simple_validators(50, 99, slashed=True)], - simple_validators(50, 99, slashed=True), - 50 * 32 * 10**9, + [*simple_validators(0, 999), *simple_validators(1000, 1049, slashed=True)], + simple_validators(1000, 1049, slashed=True), + 228_571_427_200, False, ), ( # one day since last report, penalty less report rebase simple_blockstamp(225 * 32), - [*simple_validators(0, 49), *simple_validators(50, 99, slashed=True)], - simple_validators(50, 99, slashed=True), - 51 * 32 * 10**9, + [*simple_validators(0, 999), *simple_validators(1000, 1049, slashed=True)], + simple_validators(1000, 1049, slashed=True), + 228_571_427_200 + 1, False, ), ], @@ -220,7 +220,7 @@ def test_is_high_midterm_slashing_penalty_post_electra( result = MidtermSlashingPenalty.is_high_midterm_slashing_penalty( blockstamp, 3, - lambda _: True, # FIXME: It doesn't change the test outcome. + lambda _: True, web3_converter, all_validators, lido_validators, From e668bf9dc7f06b3416349480a630e777bcc7afb7 Mon Sep 17 00:00:00 2001 From: madlabman <10616301+madlabman@users.noreply.github.com> Date: Fri, 24 Jan 2025 12:41:19 +0100 Subject: [PATCH 3/5] fix: BeaconSpecResponse from Nested to convert ints --- src/providers/consensus/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/consensus/types.py b/src/providers/consensus/types.py index 2c673487b..438adb71e 100644 --- a/src/providers/consensus/types.py +++ b/src/providers/consensus/types.py @@ -7,7 +7,7 @@ @dataclass -class BeaconSpecResponse(FromResponse): +class BeaconSpecResponse(FromResponse, Nested): DEPOSIT_CHAIN_ID: str SLOTS_PER_EPOCH: str SECONDS_PER_SLOT: str From 2febb9a4738adaf0fd79f88f3ddba95429b846f3 Mon Sep 17 00:00:00 2001 From: madlabman <10616301+madlabman@users.noreply.github.com> Date: Fri, 24 Jan 2025 14:20:47 +0100 Subject: [PATCH 4/5] chore: use get_consensus_version --- src/modules/accounting/accounting.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/accounting/accounting.py b/src/modules/accounting/accounting.py index e929a2edb..f59e76f2b 100644 --- a/src/modules/accounting/accounting.py +++ b/src/modules/accounting/accounting.py @@ -217,8 +217,7 @@ def _get_consensus_lido_state(self, blockstamp: ReferenceBlockStamp) -> tuple[Va total_lido_balance = lido_validators_state_balance = sum(int(validator.balance) for validator in lido_validators) logger.info({'msg': 'Calculate Lido validators state balance (in Gwei)', 'value': lido_validators_state_balance}) - consensus_version = self.w3.lido_contracts.accounting_oracle.get_consensus_version(blockstamp.block_hash) - if consensus_version > 2: + if self.get_consensus_version(blockstamp) > 2: if self.w3.cc.is_electra_activated(blockstamp.ref_epoch): state = self.w3.cc.get_state_view(blockstamp) total_lido_eth1_bridge_deposits_amount = self.w3.lido_validators.calculate_total_eth1_bridge_deposits_amount( From d148a1c154c8b0ae16ab54a93b97fe88eec02073 Mon Sep 17 00:00:00 2001 From: madlabman <10616301+madlabman@users.noreply.github.com> Date: Fri, 24 Jan 2025 14:26:49 +0100 Subject: [PATCH 5/5] refactor: invert is_electra_activated check --- src/services/bunker_cases/midterm_slashing_penalty.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/services/bunker_cases/midterm_slashing_penalty.py b/src/services/bunker_cases/midterm_slashing_penalty.py index 1f46c294a..a8f9a07de 100644 --- a/src/services/bunker_cases/midterm_slashing_penalty.py +++ b/src/services/bunker_cases/midterm_slashing_penalty.py @@ -243,14 +243,14 @@ def predict_midterm_penalty_in_frame_post_electra( report_ref_epoch, all_slashed_validators, EpochNumber(midterm_penalty_epoch) ) - if not is_electra_activated(frame_ref_epoch): - penalty_in_frame += MidtermSlashingPenalty.get_validator_midterm_penalty( - validator, len(bound_slashed_validators), total_balance - ) - else: + if is_electra_activated(frame_ref_epoch): penalty_in_frame += MidtermSlashingPenalty.get_validator_midterm_penalty_electra( validator, bound_slashed_validators, total_balance ) + else: + penalty_in_frame += MidtermSlashingPenalty.get_validator_midterm_penalty( + validator, len(bound_slashed_validators), total_balance + ) return Gwei(penalty_in_frame) @staticmethod