-
Notifications
You must be signed in to change notification settings - Fork 901
Description
Description
Presently we do a state advance for every block produced with withdrawals, here:
lighthouse/beacon_node/beacon_chain/src/beacon_chain.rs
Lines 4703 to 4718 in 5108872
let withdrawals = match self.spec.fork_name_at_slot::<T::EthSpec>(prepare_slot) { | |
ForkName::Base | ForkName::Altair | ForkName::Merge => None, | |
ForkName::Capella | ForkName::Eip4844 => { | |
// We must use the advanced state because balances can change at epoch boundaries | |
// and balances affect withdrawals. | |
// FIXME(mark) | |
// Might implement caching here in the future.. | |
let prepare_state = self | |
.state_at_slot(prepare_slot, StateSkipConfig::WithoutStateRoots) | |
.map_err(|e| { | |
error!(self.log, "State advance for withdrawals failed"; "error" => ?e); | |
e | |
})?; | |
Some(get_expected_withdrawals(&prepare_state, &self.spec)) | |
} | |
} |
This happens during fcU
when constructing the payload attributes, so it's kind of already off the hot path. However, we recalculate the withdrawals for every call.
To fix this I think there are a few options:
- Try to use the advanced state from the snapshot cache. Downsides: state advance happens at 9s which is usually after the first payload preparation call at 8s, and the state advance only gives a single slot of advancement (doesn't work for multiple skips).
- Compute the withdrawals the same way we do currently the first time the function is called (at 8s) and cache them in the
execution_layer
. Re-wire the function to attempt to load them from theexecution_layer
on subsequent calls (e.g. for the fork choice runs at 11.5s and 0s). The withdrawals are already stored in theproposers
map on the execution layer.
I think I prefer the 2nd solution at the moment.
We may also be able to benefit from a cache for block processing, although with the sweep length capped at 16K the iteration shouldn't take very long (by my estimate <2ms, even for tree-states
). For block processing we'd probably want the cache as a (Hash256, Vec<Withdrawal>)
on the BeaconState
.
Block processing:
lighthouse/consensus/state_processing/src/per_block_processing.rs
Lines 527 to 528 in 5108872
let expected_withdrawals = get_expected_withdrawals(state, spec)?; | |
let expected_root = expected_withdrawals.tree_hash_root(); |