Added process_withdrawals
This commit is contained in:
parent
81319dfcae
commit
276e1845fd
@ -42,6 +42,7 @@ mod verify_deposit;
|
|||||||
mod verify_exit;
|
mod verify_exit;
|
||||||
mod verify_proposer_slashing;
|
mod verify_proposer_slashing;
|
||||||
|
|
||||||
|
use crate::common::decrease_balance;
|
||||||
#[cfg(feature = "arbitrary-fuzz")]
|
#[cfg(feature = "arbitrary-fuzz")]
|
||||||
use arbitrary::Arbitrary;
|
use arbitrary::Arbitrary;
|
||||||
|
|
||||||
@ -165,6 +166,8 @@ pub fn per_block_processing<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
|||||||
// previous block.
|
// previous block.
|
||||||
if is_execution_enabled(state, block.body()) {
|
if is_execution_enabled(state, block.body()) {
|
||||||
let payload = block.body().execution_payload()?;
|
let payload = block.body().execution_payload()?;
|
||||||
|
#[cfg(all(feature = "withdrawals", feature = "withdrawals-processing"))]
|
||||||
|
process_withdrawals::<T, Payload>(state, payload, spec)?;
|
||||||
process_execution_payload::<T, Payload>(state, payload, spec)?;
|
process_execution_payload::<T, Payload>(state, payload, spec)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,3 +458,88 @@ pub fn compute_timestamp_at_slot<T: EthSpec>(
|
|||||||
.safe_mul(spec.seconds_per_slot)
|
.safe_mul(spec.seconds_per_slot)
|
||||||
.and_then(|since_genesis| state.genesis_time().safe_add(since_genesis))
|
.and_then(|since_genesis| state.genesis_time().safe_add(since_genesis))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// FIXME: add link to this function once the spec is stable
|
||||||
|
#[cfg(all(feature = "withdrawals", feature = "withdrawals-processing"))]
|
||||||
|
pub fn get_expected_withdrawals<T: EthSpec>(
|
||||||
|
state: &BeaconState<T>,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<Withdrawals<T>, BlockProcessingError> {
|
||||||
|
let epoch = state.current_epoch();
|
||||||
|
let mut withdrawal_index = *state.next_withdrawal_index()?;
|
||||||
|
let mut validator_index = *state.next_withdrawal_validator_index()?;
|
||||||
|
let mut withdrawals = vec![];
|
||||||
|
|
||||||
|
for _ in 0..state.validators().len() {
|
||||||
|
let validator = state.get_validator(validator_index as usize)?;
|
||||||
|
let balance = *state
|
||||||
|
.balances()
|
||||||
|
.get(validator_index as usize)
|
||||||
|
.ok_or_else(|| BeaconStateError::BalancesOutOfBounds(validator_index as usize))?;
|
||||||
|
if validator.is_fully_withdrawable_at(balance, epoch, spec) {
|
||||||
|
withdrawals.push(Withdrawal {
|
||||||
|
index: withdrawal_index,
|
||||||
|
validator_index,
|
||||||
|
address: Address::from_slice(&validator.withdrawal_credentials[12..]),
|
||||||
|
amount: balance,
|
||||||
|
});
|
||||||
|
withdrawal_index.safe_add_assign(1)?;
|
||||||
|
} else if validator.is_partially_withdrawable_validator(balance, spec) {
|
||||||
|
withdrawals.push(Withdrawal {
|
||||||
|
index: withdrawal_index,
|
||||||
|
validator_index,
|
||||||
|
address: Address::from_slice(&validator.withdrawal_credentials[12..]),
|
||||||
|
amount: balance.safe_sub(spec.max_effective_balance)?,
|
||||||
|
});
|
||||||
|
withdrawal_index.safe_add_assign(1)?;
|
||||||
|
}
|
||||||
|
if withdrawals.len() == T::max_withdrawals_per_payload() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
validator_index = validator_index.safe_add(1)? % state.validators().len() as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(withdrawals.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// FIXME: add link to this function once the spec is stable
|
||||||
|
#[cfg(all(feature = "withdrawals", feature = "withdrawals-processing"))]
|
||||||
|
pub fn process_withdrawals<'payload, T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||||
|
state: &mut BeaconState<T>,
|
||||||
|
payload: Payload::Ref<'payload>,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<(), BlockProcessingError> {
|
||||||
|
match state {
|
||||||
|
BeaconState::Merge(_) => Ok(()),
|
||||||
|
BeaconState::Capella(_) | BeaconState::Eip4844(_) => {
|
||||||
|
let expected_withdrawals = get_expected_withdrawals(state, spec)?;
|
||||||
|
let withdrawals_root = payload.withdrawals_root()?;
|
||||||
|
|
||||||
|
if expected_withdrawals.tree_hash_root() != payload.withdrawals_root()? {
|
||||||
|
return Err(BlockProcessingError::WithdrawalsRootMismatch {
|
||||||
|
expected: expected_withdrawals.tree_hash_root(),
|
||||||
|
found: payload.withdrawals_root()?,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for withdrawal in expected_withdrawals.iter() {
|
||||||
|
decrease_balance(
|
||||||
|
state,
|
||||||
|
withdrawal.validator_index as usize,
|
||||||
|
withdrawal.amount,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(latest_withdrawal) = expected_withdrawals.last() {
|
||||||
|
*state.next_withdrawal_index_mut()? = latest_withdrawal.index + 1;
|
||||||
|
let next_validator_index =
|
||||||
|
(latest_withdrawal.validator_index + 1) % state.validators().len() as u64;
|
||||||
|
*state.next_withdrawal_validator_index_mut()? = next_validator_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
// these shouldn't even be encountered but they're here for completeness
|
||||||
|
BeaconState::Base(_) | BeaconState::Altair(_) => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -78,6 +78,10 @@ pub enum BlockProcessingError {
|
|||||||
},
|
},
|
||||||
ExecutionInvalid,
|
ExecutionInvalid,
|
||||||
ConsensusContext(ContextError),
|
ConsensusContext(ContextError),
|
||||||
|
WithdrawalsRootMismatch {
|
||||||
|
expected: Hash256,
|
||||||
|
found: Hash256,
|
||||||
|
},
|
||||||
BlobVersionHashMismatch,
|
BlobVersionHashMismatch,
|
||||||
/// The number of commitments in blob transactions in the payload does not match the number
|
/// The number of commitments in blob transactions in the payload does not match the number
|
||||||
/// of commitments in the block.
|
/// of commitments in the block.
|
||||||
|
@ -38,7 +38,7 @@ pub trait ExecPayload<T: EthSpec>: Debug + Clone + PartialEq + Hash + TreeHash +
|
|||||||
fn transactions(&self) -> Option<&Transactions<T>>;
|
fn transactions(&self) -> Option<&Transactions<T>>;
|
||||||
/// fork-specific fields
|
/// fork-specific fields
|
||||||
#[cfg(feature = "withdrawals")]
|
#[cfg(feature = "withdrawals")]
|
||||||
fn withdrawals(&self) -> Option<Result<&Withdrawals<T>, Error>>;
|
fn withdrawals_root(&self) -> Result<Hash256, Error>;
|
||||||
|
|
||||||
/// Is this a default payload? (pre-merge)
|
/// Is this a default payload? (pre-merge)
|
||||||
fn is_default(&self) -> bool;
|
fn is_default(&self) -> bool;
|
||||||
@ -229,11 +229,15 @@ impl<T: EthSpec> ExecPayload<T> for FullPayload<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "withdrawals")]
|
#[cfg(feature = "withdrawals")]
|
||||||
fn withdrawals(&self) -> Option<Result<&Withdrawals<T>, Error>> {
|
fn withdrawals_root(&self) -> Result<Hash256, Error> {
|
||||||
match self {
|
match self {
|
||||||
FullPayload::Merge(_) => Some(Err(Error::IncorrectStateVariant)),
|
FullPayload::Merge(_) => Err(Error::IncorrectStateVariant),
|
||||||
FullPayload::Capella(ref inner) => Some(Ok(&inner.execution_payload.withdrawals)),
|
FullPayload::Capella(ref inner) => {
|
||||||
FullPayload::Eip4844(ref inner) => Some(Ok(&inner.execution_payload.withdrawals)),
|
Ok(inner.execution_payload.withdrawals.tree_hash_root())
|
||||||
|
}
|
||||||
|
FullPayload::Eip4844(ref inner) => {
|
||||||
|
Ok(inner.execution_payload.withdrawals.tree_hash_root())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,11 +326,15 @@ impl<'b, T: EthSpec> ExecPayload<T> for FullPayloadRef<'b, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "withdrawals")]
|
#[cfg(feature = "withdrawals")]
|
||||||
fn withdrawals(&self) -> Option<Result<&Withdrawals<T>, Error>> {
|
fn withdrawals_root(&self) -> Result<Hash256, Error> {
|
||||||
match self {
|
match self {
|
||||||
FullPayloadRef::Merge(_inner) => Some(Err(Error::IncorrectStateVariant)),
|
FullPayloadRef::Merge(_) => Err(Error::IncorrectStateVariant),
|
||||||
FullPayloadRef::Capella(inner) => Some(Ok(&inner.execution_payload.withdrawals)),
|
FullPayloadRef::Capella(inner) => {
|
||||||
FullPayloadRef::Eip4844(inner) => Some(Ok(&inner.execution_payload.withdrawals)),
|
Ok(inner.execution_payload.withdrawals.tree_hash_root())
|
||||||
|
}
|
||||||
|
FullPayloadRef::Eip4844(inner) => {
|
||||||
|
Ok(inner.execution_payload.withdrawals.tree_hash_root())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -485,8 +493,16 @@ impl<T: EthSpec> ExecPayload<T> for BlindedPayload<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "withdrawals")]
|
#[cfg(feature = "withdrawals")]
|
||||||
fn withdrawals(&self) -> Option<Result<&Withdrawals<T>, Error>> {
|
fn withdrawals_root(&self) -> Result<Hash256, Error> {
|
||||||
None
|
match self {
|
||||||
|
BlindedPayload::Merge(_) => Err(Error::IncorrectStateVariant),
|
||||||
|
BlindedPayload::Capella(ref inner) => {
|
||||||
|
Ok(inner.execution_payload_header.withdrawals_root)
|
||||||
|
}
|
||||||
|
BlindedPayload::Eip4844(ref inner) => {
|
||||||
|
Ok(inner.execution_payload_header.withdrawals_root)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_default<'a>(&'a self) -> bool {
|
fn is_default<'a>(&'a self) -> bool {
|
||||||
@ -563,8 +579,16 @@ impl<'b, T: EthSpec> ExecPayload<T> for BlindedPayloadRef<'b, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "withdrawals")]
|
#[cfg(feature = "withdrawals")]
|
||||||
fn withdrawals<'a>(&'a self) -> Option<Result<&Withdrawals<T>, Error>> {
|
fn withdrawals_root(&self) -> Result<Hash256, Error> {
|
||||||
None
|
match self {
|
||||||
|
BlindedPayloadRef::Merge(_) => Err(Error::IncorrectStateVariant),
|
||||||
|
BlindedPayloadRef::Capella(inner) => {
|
||||||
|
Ok(inner.execution_payload_header.withdrawals_root)
|
||||||
|
}
|
||||||
|
BlindedPayloadRef::Eip4844(inner) => {
|
||||||
|
Ok(inner.execution_payload_header.withdrawals_root)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: can this function be optimized?
|
// TODO: can this function be optimized?
|
||||||
@ -627,7 +651,7 @@ macro_rules! impl_exec_payload_common {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "withdrawals")]
|
#[cfg(feature = "withdrawals")]
|
||||||
fn withdrawals(&self) -> Option<Result<&Withdrawals<T>, Error>> {
|
fn withdrawals_root(&self) -> Result<Hash256, Error> {
|
||||||
let g = $g;
|
let g = $g;
|
||||||
g(self)
|
g(self)
|
||||||
}
|
}
|
||||||
@ -642,7 +666,7 @@ macro_rules! impl_exec_payload_common {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_exec_payload_for_fork {
|
macro_rules! impl_exec_payload_for_fork {
|
||||||
($wrapper_type_header:ident, $wrapper_type_full:ident, $wrapped_type_header:ident, $wrapped_type_full:ident, $fork_variant:ident, $withdrawal_fn:block) => {
|
($wrapper_type_header:ident, $wrapper_type_full:ident, $wrapped_type_header:ident, $wrapped_type_full:ident, $fork_variant:ident) => {
|
||||||
//*************** Blinded payload implementations ******************//
|
//*************** Blinded payload implementations ******************//
|
||||||
|
|
||||||
impl_exec_payload_common!(
|
impl_exec_payload_common!(
|
||||||
@ -653,7 +677,14 @@ macro_rules! impl_exec_payload_for_fork {
|
|||||||
$fork_variant,
|
$fork_variant,
|
||||||
Blinded,
|
Blinded,
|
||||||
{ |_| { None } },
|
{ |_| { None } },
|
||||||
{ |_| { None } }
|
{
|
||||||
|
let c: for<'a> fn(&'a $wrapper_type_header<T>) -> Result<Hash256, Error> =
|
||||||
|
|payload: &$wrapper_type_header<T>| {
|
||||||
|
let wrapper_ref_type = BlindedPayloadRef::$fork_variant(&payload);
|
||||||
|
wrapper_ref_type.withdrawals_root()
|
||||||
|
};
|
||||||
|
c
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
impl<T: EthSpec> TryInto<$wrapper_type_header<T>> for BlindedPayload<T> {
|
impl<T: EthSpec> TryInto<$wrapper_type_header<T>> for BlindedPayload<T> {
|
||||||
@ -719,7 +750,14 @@ macro_rules! impl_exec_payload_for_fork {
|
|||||||
|payload: &$wrapper_type_full<T>| Some(&payload.execution_payload.transactions);
|
|payload: &$wrapper_type_full<T>| Some(&payload.execution_payload.transactions);
|
||||||
c
|
c
|
||||||
},
|
},
|
||||||
$withdrawal_fn
|
{
|
||||||
|
let c: for<'a> fn(&'a $wrapper_type_full<T>) -> Result<Hash256, Error> =
|
||||||
|
|payload: &$wrapper_type_full<T>| {
|
||||||
|
let wrapper_ref_type = FullPayloadRef::$fork_variant(&payload);
|
||||||
|
wrapper_ref_type.withdrawals_root()
|
||||||
|
};
|
||||||
|
c
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
impl<T: EthSpec> Default for $wrapper_type_full<T> {
|
impl<T: EthSpec> Default for $wrapper_type_full<T> {
|
||||||
@ -762,36 +800,21 @@ impl_exec_payload_for_fork!(
|
|||||||
FullPayloadMerge,
|
FullPayloadMerge,
|
||||||
ExecutionPayloadHeaderMerge,
|
ExecutionPayloadHeaderMerge,
|
||||||
ExecutionPayloadMerge,
|
ExecutionPayloadMerge,
|
||||||
Merge,
|
Merge
|
||||||
{
|
|
||||||
let c: for<'a> fn(&'a FullPayloadMerge<T>) -> Option<Result<&'a Withdrawals<T>, Error>> =
|
|
||||||
|_| Some(Err(Error::IncorrectStateVariant));
|
|
||||||
c
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
impl_exec_payload_for_fork!(
|
impl_exec_payload_for_fork!(
|
||||||
BlindedPayloadCapella,
|
BlindedPayloadCapella,
|
||||||
FullPayloadCapella,
|
FullPayloadCapella,
|
||||||
ExecutionPayloadHeaderCapella,
|
ExecutionPayloadHeaderCapella,
|
||||||
ExecutionPayloadCapella,
|
ExecutionPayloadCapella,
|
||||||
Capella,
|
Capella
|
||||||
{
|
|
||||||
let c: for<'a> fn(&'a FullPayloadCapella<T>) -> Option<Result<&Withdrawals<T>, Error>> =
|
|
||||||
|payload: &FullPayloadCapella<T>| Some(Ok(&payload.execution_payload.withdrawals));
|
|
||||||
c
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
impl_exec_payload_for_fork!(
|
impl_exec_payload_for_fork!(
|
||||||
BlindedPayloadEip4844,
|
BlindedPayloadEip4844,
|
||||||
FullPayloadEip4844,
|
FullPayloadEip4844,
|
||||||
ExecutionPayloadHeaderEip4844,
|
ExecutionPayloadHeaderEip4844,
|
||||||
ExecutionPayloadEip4844,
|
ExecutionPayloadEip4844,
|
||||||
Eip4844,
|
Eip4844
|
||||||
{
|
|
||||||
let c: for<'a> fn(&'a FullPayloadEip4844<T>) -> Option<Result<&Withdrawals<T>, Error>> =
|
|
||||||
|payload: &FullPayloadEip4844<T>| Some(Ok(&payload.execution_payload.withdrawals));
|
|
||||||
c
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
impl<T: EthSpec> AbstractExecPayload<T> for BlindedPayload<T> {
|
impl<T: EthSpec> AbstractExecPayload<T> for BlindedPayload<T> {
|
||||||
|
Loading…
Reference in New Issue
Block a user