mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-07 00:42:42 +00:00
Electra other containers (#5652)
* add new fields to execution payload and header
* beacon state changes
* partial beacon state
* safe arith in upgrade to electra
* initialize balances cache in interop genesis state
* Revert "initialize balances cache in interop genesis state"
This reverts commit c60b522865.
* always initialize balances cache if necessary in electra upgrade
* build cache earlier
* fix block test
* per fork NUM_FIELDS_POW2
* Merge branch 'unstable' of https://github.com/sigp/lighthouse into electra-other-containers
* fix lints
* get fields based on post state, as is spec'd
* fix type and move cache build
This commit is contained in:
@@ -632,6 +632,9 @@ impl<E: EthSpec> ExecutionPayloadBodyV1<E> {
|
||||
withdrawals,
|
||||
blob_gas_used: header.blob_gas_used,
|
||||
excess_blob_gas: header.excess_blob_gas,
|
||||
// TODO(electra)
|
||||
deposit_receipts: <_>::default(),
|
||||
withdrawal_requests: <_>::default(),
|
||||
}))
|
||||
} else {
|
||||
Err(format!(
|
||||
|
||||
@@ -319,6 +319,9 @@ impl<E: EthSpec> From<JsonExecutionPayloadV4<E>> for ExecutionPayloadElectra<E>
|
||||
.into(),
|
||||
blob_gas_used: payload.blob_gas_used,
|
||||
excess_blob_gas: payload.excess_blob_gas,
|
||||
// TODO(electra)
|
||||
deposit_receipts: Default::default(),
|
||||
withdrawal_requests: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2003,6 +2003,11 @@ impl<E: EthSpec> ExecutionLayer<E> {
|
||||
withdrawals,
|
||||
blob_gas_used: electra_block.blob_gas_used,
|
||||
excess_blob_gas: electra_block.excess_blob_gas,
|
||||
// TODO(electra)
|
||||
// deposit_receipts: electra_block.deposit_receipts,
|
||||
// withdrawal_requests: electra_block.withdrawal_requests,
|
||||
deposit_receipts: <_>::default(),
|
||||
withdrawal_requests: <_>::default(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
@@ -659,6 +659,8 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
|
||||
withdrawals: pa.withdrawals.clone().into(),
|
||||
blob_gas_used: 0,
|
||||
excess_blob_gas: 0,
|
||||
deposit_receipts: vec![].into(),
|
||||
withdrawal_requests: vec![].into(),
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
||||
@@ -118,6 +118,29 @@ where
|
||||
#[ssz(skip_serializing, skip_deserializing)]
|
||||
#[superstruct(only(Capella, Deneb, Electra))]
|
||||
pub historical_summaries: Option<List<HistoricalSummary, E::HistoricalRootsLimit>>,
|
||||
|
||||
// Electra
|
||||
#[superstruct(only(Electra))]
|
||||
pub deposit_receipts_start_index: u64,
|
||||
#[superstruct(only(Electra))]
|
||||
pub deposit_balance_to_consume: u64,
|
||||
#[superstruct(only(Electra))]
|
||||
pub exit_balance_to_consume: u64,
|
||||
#[superstruct(only(Electra))]
|
||||
pub earliest_exit_epoch: Epoch,
|
||||
#[superstruct(only(Electra))]
|
||||
pub consolidation_balance_to_consume: u64,
|
||||
#[superstruct(only(Electra))]
|
||||
pub earliest_consolidation_epoch: Epoch,
|
||||
|
||||
// TODO(electra) should these be optional?
|
||||
#[superstruct(only(Electra))]
|
||||
pub pending_balance_deposits: List<PendingBalanceDeposit, E::PendingBalanceDepositsLimit>,
|
||||
#[superstruct(only(Electra))]
|
||||
pub pending_partial_withdrawals:
|
||||
List<PendingPartialWithdrawal, E::PendingPartialWithdrawalsLimit>,
|
||||
#[superstruct(only(Electra))]
|
||||
pub pending_consolidations: List<PendingConsolidation, E::PendingConsolidationsLimit>,
|
||||
}
|
||||
|
||||
/// Implement the conversion function from BeaconState -> PartialBeaconState.
|
||||
@@ -261,7 +284,16 @@ impl<E: EthSpec> PartialBeaconState<E> {
|
||||
inactivity_scores,
|
||||
latest_execution_payload_header,
|
||||
next_withdrawal_index,
|
||||
next_withdrawal_validator_index
|
||||
next_withdrawal_validator_index,
|
||||
deposit_receipts_start_index,
|
||||
deposit_balance_to_consume,
|
||||
exit_balance_to_consume,
|
||||
earliest_exit_epoch,
|
||||
consolidation_balance_to_consume,
|
||||
earliest_consolidation_epoch,
|
||||
pending_balance_deposits,
|
||||
pending_partial_withdrawals,
|
||||
pending_consolidations
|
||||
],
|
||||
[historical_summaries]
|
||||
),
|
||||
@@ -525,7 +557,16 @@ impl<E: EthSpec> TryInto<BeaconState<E>> for PartialBeaconState<E> {
|
||||
inactivity_scores,
|
||||
latest_execution_payload_header,
|
||||
next_withdrawal_index,
|
||||
next_withdrawal_validator_index
|
||||
next_withdrawal_validator_index,
|
||||
deposit_receipts_start_index,
|
||||
deposit_balance_to_consume,
|
||||
exit_balance_to_consume,
|
||||
earliest_exit_epoch,
|
||||
consolidation_balance_to_consume,
|
||||
earliest_consolidation_epoch,
|
||||
pending_balance_deposits,
|
||||
pending_partial_withdrawals,
|
||||
pending_consolidations
|
||||
],
|
||||
[historical_summaries]
|
||||
),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use safe_arith::SafeArith;
|
||||
use std::mem;
|
||||
use types::{
|
||||
BeaconState, BeaconStateElectra, BeaconStateError as Error, ChainSpec, EpochCache, EthSpec,
|
||||
@@ -10,14 +11,28 @@ pub fn upgrade_to_electra<E: EthSpec>(
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let epoch = pre_state.current_epoch();
|
||||
let pre = pre_state.as_deneb_mut()?;
|
||||
|
||||
let earliest_exit_epoch = pre_state
|
||||
.validators()
|
||||
.iter()
|
||||
.filter(|v| v.exit_epoch != spec.far_future_epoch)
|
||||
.map(|v| v.exit_epoch)
|
||||
.max()
|
||||
.unwrap_or(epoch)
|
||||
.safe_add(1)?;
|
||||
|
||||
// The total active balance cache must be built before the consolidation churn limit
|
||||
// is calculated.
|
||||
pre_state.build_total_active_balance_cache(spec)?;
|
||||
let earliest_consolidation_epoch = spec.compute_activation_exit_epoch(epoch)?;
|
||||
|
||||
let pre = pre_state.as_deneb_mut()?;
|
||||
// Where possible, use something like `mem::take` to move fields from behind the &mut
|
||||
// reference. For other fields that don't have a good default value, use `clone`.
|
||||
//
|
||||
// Fixed size vectors get cloned because replacing them would require the same size
|
||||
// allocation as cloning.
|
||||
let post = BeaconState::Electra(BeaconStateElectra {
|
||||
let mut post = BeaconState::Electra(BeaconStateElectra {
|
||||
// Versioning
|
||||
genesis_time: pre.genesis_time,
|
||||
genesis_validators_root: pre.genesis_validators_root,
|
||||
@@ -62,6 +77,16 @@ pub fn upgrade_to_electra<E: EthSpec>(
|
||||
next_withdrawal_index: pre.next_withdrawal_index,
|
||||
next_withdrawal_validator_index: pre.next_withdrawal_validator_index,
|
||||
historical_summaries: pre.historical_summaries.clone(),
|
||||
// Electra
|
||||
deposit_receipts_start_index: spec.unset_deposit_receipts_start_index,
|
||||
deposit_balance_to_consume: 0,
|
||||
exit_balance_to_consume: 0,
|
||||
earliest_exit_epoch,
|
||||
consolidation_balance_to_consume: 0,
|
||||
earliest_consolidation_epoch,
|
||||
pending_balance_deposits: Default::default(),
|
||||
pending_partial_withdrawals: Default::default(),
|
||||
pending_consolidations: Default::default(),
|
||||
// Caches
|
||||
total_active_balance: pre.total_active_balance,
|
||||
progressive_balances_cache: mem::take(&mut pre.progressive_balances_cache),
|
||||
@@ -71,6 +96,39 @@ pub fn upgrade_to_electra<E: EthSpec>(
|
||||
slashings_cache: mem::take(&mut pre.slashings_cache),
|
||||
epoch_cache: EpochCache::default(),
|
||||
});
|
||||
*post.exit_balance_to_consume_mut()? = post.get_activation_exit_churn_limit(spec)?;
|
||||
*post.consolidation_balance_to_consume_mut()? = post.get_consolidation_churn_limit(spec)?;
|
||||
|
||||
// Add validators that are not yet active to pending balance deposits
|
||||
let validators = post.validators().clone();
|
||||
let mut pre_activation = validators
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, validator)| validator.activation_epoch == spec.far_future_epoch)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Sort the indices by activation_eligibility_epoch and then by index
|
||||
pre_activation.sort_by(|(index_a, val_a), (index_b, val_b)| {
|
||||
if val_a.activation_eligibility_epoch == val_b.activation_eligibility_epoch {
|
||||
index_a.cmp(index_b)
|
||||
} else {
|
||||
val_a
|
||||
.activation_eligibility_epoch
|
||||
.cmp(&val_b.activation_eligibility_epoch)
|
||||
}
|
||||
});
|
||||
|
||||
// Process validators to queue entire balance and reset them
|
||||
for (index, _) in pre_activation {
|
||||
post.queue_entire_balance_and_reset_validator(index, spec)?;
|
||||
}
|
||||
|
||||
// Ensure early adopters of compounding credentials go through the activation churn
|
||||
for (index, validator) in validators.iter().enumerate() {
|
||||
if validator.has_compounding_withdrawal_credential(spec) {
|
||||
post.queue_excess_active_balance(index, spec)?;
|
||||
}
|
||||
}
|
||||
|
||||
*pre_state = post;
|
||||
|
||||
|
||||
@@ -1074,9 +1074,8 @@ mod tests {
|
||||
.expect("good electra block can be decoded"),
|
||||
good_block
|
||||
);
|
||||
// TODO(electra): once the Electra block is changed from Deneb, update this to match
|
||||
// the other forks.
|
||||
assert!(BeaconBlock::from_ssz_bytes(&bad_block.as_ssz_bytes(), &spec).is_ok());
|
||||
BeaconBlock::from_ssz_bytes(&bad_block.as_ssz_bytes(), &spec)
|
||||
.expect_err("bad electra block cannot be decoded");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,6 +467,40 @@ where
|
||||
#[test_random(default)]
|
||||
pub historical_summaries: List<HistoricalSummary, E::HistoricalRootsLimit>,
|
||||
|
||||
// Electra
|
||||
#[superstruct(only(Electra), partial_getter(copy))]
|
||||
#[metastruct(exclude_from(tree_lists))]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub deposit_receipts_start_index: u64,
|
||||
#[superstruct(only(Electra), partial_getter(copy))]
|
||||
#[metastruct(exclude_from(tree_lists))]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub deposit_balance_to_consume: u64,
|
||||
#[superstruct(only(Electra), partial_getter(copy))]
|
||||
#[metastruct(exclude_from(tree_lists))]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub exit_balance_to_consume: u64,
|
||||
#[superstruct(only(Electra), partial_getter(copy))]
|
||||
#[metastruct(exclude_from(tree_lists))]
|
||||
pub earliest_exit_epoch: Epoch,
|
||||
#[superstruct(only(Electra), partial_getter(copy))]
|
||||
#[metastruct(exclude_from(tree_lists))]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub consolidation_balance_to_consume: u64,
|
||||
#[superstruct(only(Electra), partial_getter(copy))]
|
||||
#[metastruct(exclude_from(tree_lists))]
|
||||
pub earliest_consolidation_epoch: Epoch,
|
||||
#[test_random(default)]
|
||||
#[superstruct(only(Electra))]
|
||||
pub pending_balance_deposits: List<PendingBalanceDeposit, E::PendingBalanceDepositsLimit>,
|
||||
#[test_random(default)]
|
||||
#[superstruct(only(Electra))]
|
||||
pub pending_partial_withdrawals:
|
||||
List<PendingPartialWithdrawal, E::PendingPartialWithdrawalsLimit>,
|
||||
#[test_random(default)]
|
||||
#[superstruct(only(Electra))]
|
||||
pub pending_consolidations: List<PendingConsolidation, E::PendingConsolidationsLimit>,
|
||||
|
||||
// Caching (not in the spec)
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
#[ssz(skip_serializing, skip_deserializing)]
|
||||
@@ -2031,6 +2065,83 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
self.epoch_cache().get_base_reward(validator_index)
|
||||
}
|
||||
|
||||
// ******* Electra accessors *******
|
||||
|
||||
/// Return the churn limit for the current epoch.
|
||||
pub fn get_balance_churn_limit(&self, spec: &ChainSpec) -> Result<u64, Error> {
|
||||
let total_active_balance = self.get_total_active_balance()?;
|
||||
let churn = std::cmp::max(
|
||||
spec.min_per_epoch_churn_limit_electra,
|
||||
total_active_balance.safe_div(spec.churn_limit_quotient)?,
|
||||
);
|
||||
|
||||
Ok(churn.safe_sub(churn.safe_rem(spec.effective_balance_increment)?)?)
|
||||
}
|
||||
|
||||
/// Return the churn limit for the current epoch dedicated to activations and exits.
|
||||
pub fn get_activation_exit_churn_limit(&self, spec: &ChainSpec) -> Result<u64, Error> {
|
||||
Ok(std::cmp::min(
|
||||
spec.max_per_epoch_activation_exit_churn_limit,
|
||||
self.get_balance_churn_limit(spec)?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_consolidation_churn_limit(&self, spec: &ChainSpec) -> Result<u64, Error> {
|
||||
self.get_balance_churn_limit(spec)?
|
||||
.safe_sub(self.get_activation_exit_churn_limit(spec)?)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
// ******* Electra mutators *******
|
||||
|
||||
pub fn queue_excess_active_balance(
|
||||
&mut self,
|
||||
validator_index: usize,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let balance = self
|
||||
.balances_mut()
|
||||
.get_mut(validator_index)
|
||||
.ok_or(Error::UnknownValidator(validator_index))?;
|
||||
if *balance > spec.min_activation_balance {
|
||||
let excess_balance = balance.safe_sub(spec.min_activation_balance)?;
|
||||
*balance = spec.min_activation_balance;
|
||||
self.pending_balance_deposits_mut()?
|
||||
.push(PendingBalanceDeposit {
|
||||
index: validator_index as u64,
|
||||
amount: excess_balance,
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn queue_entire_balance_and_reset_validator(
|
||||
&mut self,
|
||||
validator_index: usize,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let balance = self
|
||||
.balances_mut()
|
||||
.get_mut(validator_index)
|
||||
.ok_or(Error::UnknownValidator(validator_index))?;
|
||||
let balance_copy = *balance;
|
||||
*balance = 0_u64;
|
||||
|
||||
let validator = self
|
||||
.validators_mut()
|
||||
.get_mut(validator_index)
|
||||
.ok_or(Error::UnknownValidator(validator_index))?;
|
||||
validator.effective_balance = 0;
|
||||
validator.activation_eligibility_epoch = spec.far_future_epoch;
|
||||
|
||||
self.pending_balance_deposits_mut()?
|
||||
.push(PendingBalanceDeposit {
|
||||
index: validator_index as u64,
|
||||
amount: balance_copy,
|
||||
})
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[allow(clippy::arithmetic_side_effects)]
|
||||
pub fn rebase_on(&mut self, base: &Self, spec: &ChainSpec) -> Result<(), Error> {
|
||||
// Required for macros (which use type-hints internally).
|
||||
@@ -2147,10 +2258,17 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
/// The number of fields of the `BeaconState` rounded up to the nearest power of two.
|
||||
///
|
||||
/// This is relevant to tree-hashing of the `BeaconState`.
|
||||
///
|
||||
/// We assume this value is stable across forks. This assumption is checked in the
|
||||
/// `check_num_fields_pow2` test.
|
||||
pub const NUM_FIELDS_POW2: usize = BeaconStateBellatrix::<E>::NUM_FIELDS.next_power_of_two();
|
||||
pub fn num_fields_pow2(&self) -> usize {
|
||||
let fork_name = self.fork_name_unchecked();
|
||||
match fork_name {
|
||||
ForkName::Base => BeaconStateBase::<E>::NUM_FIELDS.next_power_of_two(),
|
||||
ForkName::Altair => BeaconStateAltair::<E>::NUM_FIELDS.next_power_of_two(),
|
||||
ForkName::Bellatrix => BeaconStateBellatrix::<E>::NUM_FIELDS.next_power_of_two(),
|
||||
ForkName::Capella => BeaconStateCapella::<E>::NUM_FIELDS.next_power_of_two(),
|
||||
ForkName::Deneb => BeaconStateDeneb::<E>::NUM_FIELDS.next_power_of_two(),
|
||||
ForkName::Electra => BeaconStateElectra::<E>::NUM_FIELDS.next_power_of_two(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Specialised deserialisation method that uses the `ChainSpec` as context.
|
||||
#[allow(clippy::arithmetic_side_effects)]
|
||||
@@ -2211,7 +2329,7 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
// in the `BeaconState`:
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#beaconstate
|
||||
generalized_index
|
||||
.checked_sub(Self::NUM_FIELDS_POW2)
|
||||
.checked_sub(self.num_fields_pow2())
|
||||
.ok_or(Error::IndexNotSupported(generalized_index))?
|
||||
}
|
||||
light_client_update::FINALIZED_ROOT_INDEX => {
|
||||
@@ -2221,7 +2339,7 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
// Subtract off the internal nodes. Result should be 105/2 - 32 = 20 which matches
|
||||
// position of `finalized_checkpoint` in `BeaconState`.
|
||||
finalized_checkpoint_generalized_index
|
||||
.checked_sub(Self::NUM_FIELDS_POW2)
|
||||
.checked_sub(self.num_fields_pow2())
|
||||
.ok_or(Error::IndexNotSupported(generalized_index))?
|
||||
}
|
||||
_ => return Err(Error::IndexNotSupported(generalized_index)),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#![cfg(test)]
|
||||
use crate::{test_utils::*, ForkName};
|
||||
use crate::test_utils::*;
|
||||
use beacon_chain::test_utils::{BeaconChainHarness, EphemeralHarnessType};
|
||||
use beacon_chain::types::{
|
||||
test_utils::TestRandom, BeaconState, BeaconStateAltair, BeaconStateBase, BeaconStateBellatrix,
|
||||
BeaconStateCapella, BeaconStateDeneb, BeaconStateElectra, BeaconStateError, ChainSpec, Domain,
|
||||
Epoch, EthSpec, Hash256, Keypair, MainnetEthSpec, MinimalEthSpec, RelativeEpoch, Slot, Vector,
|
||||
test_utils::TestRandom, BeaconState, BeaconStateAltair, BeaconStateBase, BeaconStateError,
|
||||
ChainSpec, Domain, Epoch, EthSpec, Hash256, Keypair, MainnetEthSpec, MinimalEthSpec,
|
||||
RelativeEpoch, Slot, Vector,
|
||||
};
|
||||
use ssz::Encode;
|
||||
use std::ops::Mul;
|
||||
@@ -403,24 +403,3 @@ fn decode_base_and_altair() {
|
||||
.expect_err("bad altair state cannot be decoded");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_num_fields_pow2() {
|
||||
use metastruct::NumFields;
|
||||
pub type E = MainnetEthSpec;
|
||||
|
||||
for fork_name in ForkName::list_all() {
|
||||
let num_fields = match fork_name {
|
||||
ForkName::Base => BeaconStateBase::<E>::NUM_FIELDS,
|
||||
ForkName::Altair => BeaconStateAltair::<E>::NUM_FIELDS,
|
||||
ForkName::Bellatrix => BeaconStateBellatrix::<E>::NUM_FIELDS,
|
||||
ForkName::Capella => BeaconStateCapella::<E>::NUM_FIELDS,
|
||||
ForkName::Deneb => BeaconStateDeneb::<E>::NUM_FIELDS,
|
||||
ForkName::Electra => BeaconStateElectra::<E>::NUM_FIELDS,
|
||||
};
|
||||
assert_eq!(
|
||||
num_fields.next_power_of_two(),
|
||||
BeaconState::<E>::NUM_FIELDS_POW2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,11 @@ pub struct ExecutionPayload<E: EthSpec> {
|
||||
#[superstruct(only(Deneb, Electra), partial_getter(copy))]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub excess_blob_gas: u64,
|
||||
#[superstruct(only(Electra))]
|
||||
pub deposit_receipts: VariableList<DepositReceipt, E::MaxDepositReceiptsPerPayload>,
|
||||
#[superstruct(only(Electra))]
|
||||
pub withdrawal_requests:
|
||||
VariableList<ExecutionLayerWithdrawalRequest, E::MaxWithdrawalRequestsPerPayload>,
|
||||
}
|
||||
|
||||
impl<'a, E: EthSpec> ExecutionPayloadRef<'a, E> {
|
||||
|
||||
@@ -88,6 +88,10 @@ pub struct ExecutionPayloadHeader<E: EthSpec> {
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
#[superstruct(getter(copy))]
|
||||
pub excess_blob_gas: u64,
|
||||
#[superstruct(only(Electra), partial_getter(copy))]
|
||||
pub deposit_receipts_root: Hash256,
|
||||
#[superstruct(only(Electra), partial_getter(copy))]
|
||||
pub withdrawal_requests_root: Hash256,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> ExecutionPayloadHeader<E> {
|
||||
@@ -206,6 +210,8 @@ impl<E: EthSpec> ExecutionPayloadHeaderDeneb<E> {
|
||||
withdrawals_root: self.withdrawals_root,
|
||||
blob_gas_used: self.blob_gas_used,
|
||||
excess_blob_gas: self.excess_blob_gas,
|
||||
deposit_receipts_root: Hash256::zero(),
|
||||
withdrawal_requests_root: Hash256::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -297,6 +303,8 @@ impl<'a, E: EthSpec> From<&'a ExecutionPayloadElectra<E>> for ExecutionPayloadHe
|
||||
withdrawals_root: payload.withdrawals.tree_hash_root(),
|
||||
blob_gas_used: payload.blob_gas_used,
|
||||
excess_blob_gas: payload.excess_blob_gas,
|
||||
deposit_receipts_root: payload.deposit_receipts.tree_hash_root(),
|
||||
withdrawal_requests_root: payload.withdrawal_requests.tree_hash_root(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,11 @@ impl Validator {
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Check if ``validator`` has an 0x02 prefixed "compounding" withdrawal credential.
|
||||
pub fn has_compounding_withdrawal_credential(&self, spec: &ChainSpec) -> bool {
|
||||
is_compounding_withdrawal_credential(self.withdrawal_credentials, spec)
|
||||
}
|
||||
|
||||
/// Get the eth1 withdrawal address if this validator has one initialized.
|
||||
pub fn get_eth1_withdrawal_address(&self, spec: &ChainSpec) -> Option<Address> {
|
||||
self.has_eth1_withdrawal_credential(spec)
|
||||
@@ -153,6 +158,17 @@ impl Default for Validator {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_compounding_withdrawal_credential(
|
||||
withdrawal_credentials: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> bool {
|
||||
withdrawal_credentials
|
||||
.as_bytes()
|
||||
.first()
|
||||
.map(|prefix_byte| *prefix_byte == spec.compounding_withdrawal_prefix_byte)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user