add load_snapshot_from_state_root that can be used when we've already aquired a

This commit is contained in:
Eitan Seri- Levi
2026-02-24 11:35:01 -08:00
parent fc7d6c9d24
commit 30241f54c4
2 changed files with 46 additions and 29 deletions

View File

@@ -22,7 +22,7 @@ use crate::{
payload_envelope_verification::{
EnvelopeError, EnvelopeImportData, EnvelopeProcessingSnapshot, ExecutionPendingEnvelope,
IntoExecutionPendingEnvelope, MaybeAvailableEnvelope, load_snapshot,
payload_notifier::PayloadNotifier,
load_snapshot_from_state_root, payload_notifier::PayloadNotifier,
},
validator_pubkey_cache::ValidatorPubkeyCache,
};
@@ -161,7 +161,6 @@ impl<T: BeaconChainTypes> GossipVerifiedEnvelope<T> {
let (signature_is_valid, opt_snapshot) = if builder_index == BUILDER_INDEX_SELF_BUILD {
// Fast path: self-built envelopes can be verified without loading the state.
let envelope_ref = signed_envelope.as_ref();
let mut opt_snapshot = None;
let proposer = beacon_proposer_cache::with_proposer_cache(
ctx.beacon_proposer_cache,
@@ -174,7 +173,11 @@ impl<T: BeaconChainTypes> GossipVerifiedEnvelope<T> {
%beacon_block_root,
"Proposer shuffling cache miss for envelope verification"
);
let snapshot = load_snapshot(envelope_ref, ctx.canonical_head, ctx.store)?;
let snapshot = load_snapshot_from_state_root::<T>(
beacon_block_root,
proto_block.state_root,
ctx.store,
)?;
opt_snapshot = Some(Box::new(snapshot.clone()));
Ok::<_, EnvelopeError>((snapshot.state_root, snapshot.pre_state))
},
@@ -205,7 +208,11 @@ impl<T: BeaconChainTypes> GossipVerifiedEnvelope<T> {
} else {
// TODO(gloas) if we implement a builder pubkey cache, we'll need to use it here.
// External builder: must load the state to get the builder pubkey.
let snapshot = load_snapshot(signed_envelope.as_ref(), ctx.canonical_head, ctx.store)?;
let snapshot = load_snapshot_from_state_root::<T>(
beacon_block_root,
proto_block.state_root,
ctx.store,
)?;
let is_valid =
signed_envelope.verify_signature_with_state(&snapshot.pre_state, ctx.spec)?;
(is_valid, Some(Box::new(snapshot)))

View File

@@ -291,35 +291,16 @@ impl From<EnvelopeProcessingError> for EnvelopeError {
}
#[allow(clippy::type_complexity)]
#[instrument(skip_all, level = "debug", fields(beacon_block_root = %envelope.beacon_block_root()))]
pub(crate) fn load_snapshot<T: BeaconChainTypes>(
envelope: &SignedExecutionPayloadEnvelope<T::EthSpec>,
canonical_head: &CanonicalHead<T>,
#[instrument(skip_all, level = "debug", fields(beacon_block_root = %beacon_block_root))]
/// Load state from store given a known state root and block root.
/// Use this when the proto block has already been looked up from fork choice.
pub(crate) fn load_snapshot_from_state_root<T: BeaconChainTypes>(
beacon_block_root: Hash256,
block_state_root: Hash256,
store: &BeaconStore<T>,
) -> Result<EnvelopeProcessingSnapshot<T::EthSpec>, EnvelopeError> {
// Reject any envelope if its block is not known to fork choice.
//
// A block that is not in fork choice is either:
//
// - Not yet imported: we should reject this envelope because we should only import it after its parent block
// has been fully imported.
// - Pre-finalized: if the parent block is _prior_ to finalization, we should ignore the envelope
// because it will revert finalization. Note that the finalized block is stored in fork
// choice, so we will not reject any child of the finalized block (this is relevant during
// genesis).
let fork_choice_read_lock = canonical_head.fork_choice_read_lock();
let beacon_block_root = envelope.beacon_block_root();
let Some(proto_beacon_block) = fork_choice_read_lock.get_block(&beacon_block_root) else {
return Err(EnvelopeError::BlockRootUnknown {
block_root: beacon_block_root,
});
};
drop(fork_choice_read_lock);
// TODO(EIP-7732): add metrics here
let block_state_root = proto_beacon_block.state_root;
// We can use `get_hot_state` here rather than `get_advanced_hot_state` because the envelope
// must be from the same slot as its block (so no advance is required).
let cache_state = true;
@@ -339,6 +320,35 @@ pub(crate) fn load_snapshot<T: BeaconChainTypes>(
})
}
#[instrument(skip_all, level = "debug", fields(beacon_block_root = %envelope.beacon_block_root()))]
pub(crate) fn load_snapshot<T: BeaconChainTypes>(
envelope: &SignedExecutionPayloadEnvelope<T::EthSpec>,
canonical_head: &CanonicalHead<T>,
store: &BeaconStore<T>,
) -> Result<EnvelopeProcessingSnapshot<T::EthSpec>, EnvelopeError> {
// Reject any envelope if its block is not known to fork choice.
//
// A block that is not in fork choice is either:
//
// - Not yet imported: we should reject this envelope because we should only import it after
// its parent block has been fully imported.
// - Pre-finalized: if the parent block is _prior_ to finalization, we should ignore the
// envelope because it will revert finalization. Note that the finalized block is stored in
// fork choice, so we will not reject any child of the finalized block (this is relevant
// during genesis).
let fork_choice_read_lock = canonical_head.fork_choice_read_lock();
let beacon_block_root = envelope.beacon_block_root();
let Some(proto_beacon_block) = fork_choice_read_lock.get_block(&beacon_block_root) else {
return Err(EnvelopeError::BlockRootUnknown {
block_root: beacon_block_root,
});
};
drop(fork_choice_read_lock);
load_snapshot_from_state_root::<T>(beacon_block_root, proto_beacon_block.state_root, store)
}
impl<T: BeaconChainTypes> IntoExecutionPendingEnvelope<T>
for Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>
{