Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use is_electra_activated #599

Merged
merged 5 commits into from
Jan 24, 2025
Merged
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
6 changes: 2 additions & 4 deletions src/modules/accounting/accounting.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,8 @@ 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:
spec = self.w3.cc.get_config_spec()
if blockstamp.ref_epoch >= int(spec.ELECTRA_FORK_EPOCH):
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(
lido_validators, state.pending_deposits
Expand Down
14 changes: 6 additions & 8 deletions src/modules/ejector/ejector.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)

Expand Down
5 changes: 5 additions & 0 deletions src/providers/consensus/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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", <slot>, <hex encoded stateRoot with 0x prefix>.
"""

PROVIDER_EXCEPTION = ConsensusClientError
PROMETHEUS_HISTOGRAM = CL_REQUESTS_DURATION

Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions src/providers/consensus/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@


@dataclass
class BeaconSpecResponse(FromResponse):
class BeaconSpecResponse(FromResponse, Nested):
DEPOSIT_CHAIN_ID: str
SLOTS_PER_EPOCH: str
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
Expand Down
10 changes: 8 additions & 2 deletions src/services/bunker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"})
Expand Down
3 changes: 1 addition & 2 deletions src/services/bunker_cases/abnormal_cl_rebase.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
75 changes: 41 additions & 34 deletions src/services/bunker_cases/midterm_slashing_penalty.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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}"})
Expand All @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -200,21 +205,23 @@ 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,
) -> dict[FrameNumber, Gwei]:
"""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
Expand All @@ -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
Expand All @@ -236,21 +243,21 @@ 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):
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
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
Expand Down Expand Up @@ -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)
Expand Down
Loading
Loading