Skip to content

Commit 382f12e

Browse files
authored
Merge of #5510
2 parents 82b131d + 958bac4 commit 382f12e

File tree

12 files changed

+391
-156
lines changed

12 files changed

+391
-156
lines changed

beacon_node/beacon_chain/src/beacon_chain.rs

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2953,7 +2953,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
29532953
}
29542954

29552955
/// Wraps `process_block` in logic to cache the block's commitments in the processing cache
2956-
/// and evict if the block was imported or erred.
2956+
/// and evict if the block was imported or errored.
29572957
pub async fn process_block_with_early_caching<B: IntoExecutionPendingBlock<T>>(
29582958
self: &Arc<Self>,
29592959
block_root: Hash256,
@@ -2998,22 +2998,20 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
29982998
// Increment the Prometheus counter for block processing requests.
29992999
metrics::inc_counter(&metrics::BLOCK_PROCESSING_REQUESTS);
30003000

3001+
let block_slot = unverified_block.block().slot();
3002+
30013003
// Set observed time if not already set. Usually this should be set by gossip or RPC,
30023004
// but just in case we set it again here (useful for tests).
3003-
if let (Some(seen_timestamp), Some(current_slot)) =
3004-
(self.slot_clock.now_duration(), self.slot_clock.now())
3005-
{
3005+
if let Some(seen_timestamp) = self.slot_clock.now_duration() {
30063006
self.block_times_cache.write().set_time_observed(
30073007
block_root,
3008-
current_slot,
3008+
block_slot,
30093009
seen_timestamp,
30103010
None,
30113011
None,
30123012
);
30133013
}
30143014

3015-
let block_slot = unverified_block.block().slot();
3016-
30173015
// A small closure to group the verification and import errors.
30183016
let chain = self.clone();
30193017
let import_block = async move {
@@ -3024,6 +3022,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
30243022
)?;
30253023
publish_fn()?;
30263024
let executed_block = chain.into_executed_block(execution_pending).await?;
3025+
// Record the time it took to ask the execution layer.
3026+
if let Some(seen_timestamp) = self.slot_clock.now_duration() {
3027+
self.block_times_cache.write().set_execution_time(
3028+
block_root,
3029+
block_slot,
3030+
seen_timestamp,
3031+
)
3032+
}
3033+
30273034
match executed_block {
30283035
ExecutedBlock::Available(block) => {
30293036
self.import_available_block(Box::new(block)).await
@@ -3090,8 +3097,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
30903097
}
30913098
}
30923099

3093-
/// Accepts a fully-verified block and awaits on it's payload verification handle to
3094-
/// get a fully `ExecutedBlock`
3100+
/// Accepts a fully-verified block and awaits on its payload verification handle to
3101+
/// get a fully `ExecutedBlock`.
30953102
///
30963103
/// An error is returned if the verification handle couldn't be awaited.
30973104
pub async fn into_executed_block(
@@ -3224,10 +3231,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
32243231
) -> Result<AvailabilityProcessingStatus, BlockError<T::EthSpec>> {
32253232
match availability {
32263233
Availability::Available(block) => {
3227-
// This is the time since start of the slot where all the components of the block have become available
3228-
let delay =
3229-
get_slot_delay_ms(timestamp_now(), block.block.slot(), &self.slot_clock);
3230-
metrics::observe_duration(&metrics::BLOCK_AVAILABILITY_DELAY, delay);
32313234
// Block is fully available, import into fork choice
32323235
self.import_available_block(block).await
32333236
}
@@ -3256,6 +3259,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
32563259
consensus_context,
32573260
} = import_data;
32583261

3262+
// Record the time at which this block's blobs became available.
3263+
if let Some(blobs_available) = block.blobs_available_timestamp() {
3264+
self.block_times_cache.write().set_time_blob_observed(
3265+
block_root,
3266+
block.slot(),
3267+
blobs_available,
3268+
);
3269+
}
3270+
32593271
// import
32603272
let chain = self.clone();
32613273
let block_root = self
@@ -3396,6 +3408,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
33963408
"Early attester cache insert failed";
33973409
"error" => ?e
33983410
);
3411+
} else {
3412+
let attestable_timestamp =
3413+
self.slot_clock.now_duration().unwrap_or_default();
3414+
self.block_times_cache.write().set_time_attestable(
3415+
block_root,
3416+
signed_block.slot(),
3417+
attestable_timestamp,
3418+
)
33993419
}
34003420
} else {
34013421
warn!(
@@ -3885,25 +3905,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
38853905
);
38863906
}
38873907

3888-
// Do not store metrics if the block was > 4 slots old, this helps prevent noise during
3889-
// sync.
3890-
if block_delay_total < self.slot_clock.slot_duration() * 4 {
3891-
// Observe the delay between when we observed the block and when we imported it.
3892-
let block_delays = self.block_times_cache.read().get_block_delays(
3893-
block_root,
3894-
self.slot_clock
3895-
.start_of(current_slot)
3896-
.unwrap_or_else(|| Duration::from_secs(0)),
3897-
);
3898-
3899-
metrics::observe_duration(
3900-
&metrics::BEACON_BLOCK_IMPORTED_OBSERVED_DELAY_TIME,
3901-
block_delays
3902-
.imported
3903-
.unwrap_or_else(|| Duration::from_secs(0)),
3904-
);
3905-
}
3906-
39073908
if let Some(event_handler) = self.event_handler.as_ref() {
39083909
if event_handler.has_block_subscribers() {
39093910
event_handler.register(EventKind::Block(SseBlock {

beacon_node/beacon_chain/src/blob_verification.rs

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use merkle_proof::MerkleTreeError;
1414
use slog::{debug, warn};
1515
use ssz_derive::{Decode, Encode};
1616
use ssz_types::VariableList;
17+
use std::time::Duration;
1718
use tree_hash::TreeHash;
1819
use types::blob_sidecar::BlobIdentifier;
1920
use types::{
@@ -214,7 +215,10 @@ impl<T: BeaconChainTypes> GossipVerifiedBlob<T> {
214215
pub fn __assumed_valid(blob: Arc<BlobSidecar<T::EthSpec>>) -> Self {
215216
Self {
216217
block_root: blob.block_root(),
217-
blob: KzgVerifiedBlob { blob },
218+
blob: KzgVerifiedBlob {
219+
blob,
220+
seen_timestamp: Duration::from_secs(0),
221+
},
218222
}
219223
}
220224
pub fn id(&self) -> BlobIdentifier {
@@ -260,6 +264,8 @@ impl<T: BeaconChainTypes> GossipVerifiedBlob<T> {
260264
#[ssz(struct_behaviour = "transparent")]
261265
pub struct KzgVerifiedBlob<E: EthSpec> {
262266
blob: Arc<BlobSidecar<E>>,
267+
#[ssz(skip_serializing, skip_deserializing)]
268+
seen_timestamp: Duration,
263269
}
264270

265271
impl<E: EthSpec> PartialOrd for KzgVerifiedBlob<E> {
@@ -275,8 +281,12 @@ impl<E: EthSpec> Ord for KzgVerifiedBlob<E> {
275281
}
276282

277283
impl<E: EthSpec> KzgVerifiedBlob<E> {
278-
pub fn new(blob: Arc<BlobSidecar<E>>, kzg: &Kzg) -> Result<Self, KzgError> {
279-
verify_kzg_for_blob(blob, kzg)
284+
pub fn new(
285+
blob: Arc<BlobSidecar<E>>,
286+
kzg: &Kzg,
287+
seen_timestamp: Duration,
288+
) -> Result<Self, KzgError> {
289+
verify_kzg_for_blob(blob, kzg, seen_timestamp)
280290
}
281291
pub fn to_blob(self) -> Arc<BlobSidecar<E>> {
282292
self.blob
@@ -294,12 +304,18 @@ impl<E: EthSpec> KzgVerifiedBlob<E> {
294304
pub fn blob_index(&self) -> u64 {
295305
self.blob.index
296306
}
307+
pub fn seen_timestamp(&self) -> Duration {
308+
self.seen_timestamp
309+
}
297310
/// Construct a `KzgVerifiedBlob` that is assumed to be valid.
298311
///
299312
/// This should ONLY be used for testing.
300313
#[cfg(test)]
301314
pub fn __assumed_valid(blob: Arc<BlobSidecar<E>>) -> Self {
302-
Self { blob }
315+
Self {
316+
blob,
317+
seen_timestamp: Duration::from_secs(0),
318+
}
303319
}
304320
}
305321

@@ -309,9 +325,13 @@ impl<E: EthSpec> KzgVerifiedBlob<E> {
309325
pub fn verify_kzg_for_blob<E: EthSpec>(
310326
blob: Arc<BlobSidecar<E>>,
311327
kzg: &Kzg,
328+
seen_timestamp: Duration,
312329
) -> Result<KzgVerifiedBlob<E>, KzgError> {
313330
validate_blob::<E>(kzg, &blob.blob, blob.kzg_commitment, blob.kzg_proof)?;
314-
Ok(KzgVerifiedBlob { blob })
331+
Ok(KzgVerifiedBlob {
332+
blob,
333+
seen_timestamp,
334+
})
315335
}
316336

317337
pub struct KzgVerifiedBlobList<E: EthSpec> {
@@ -322,13 +342,17 @@ impl<E: EthSpec> KzgVerifiedBlobList<E> {
322342
pub fn new<I: IntoIterator<Item = Arc<BlobSidecar<E>>>>(
323343
blob_list: I,
324344
kzg: &Kzg,
345+
seen_timestamp: Duration,
325346
) -> Result<Self, KzgError> {
326347
let blobs = blob_list.into_iter().collect::<Vec<_>>();
327348
verify_kzg_for_blob_list(blobs.iter(), kzg)?;
328349
Ok(Self {
329350
verified_blobs: blobs
330351
.into_iter()
331-
.map(|blob| KzgVerifiedBlob { blob })
352+
.map(|blob| KzgVerifiedBlob {
353+
blob,
354+
seen_timestamp,
355+
})
332356
.collect(),
333357
})
334358
}
@@ -374,6 +398,8 @@ pub fn validate_blob_sidecar_for_gossip<T: BeaconChainTypes>(
374398
let blob_epoch = blob_slot.epoch(T::EthSpec::slots_per_epoch());
375399
let signed_block_header = &blob_sidecar.signed_block_header;
376400

401+
let seen_timestamp = chain.slot_clock.now_duration().unwrap_or_default();
402+
377403
// This condition is not possible if we have received the blob from the network
378404
// since we only subscribe to `MaxBlobsPerBlock` subnets over gossip network.
379405
// We include this check only for completeness.
@@ -641,8 +667,8 @@ pub fn validate_blob_sidecar_for_gossip<T: BeaconChainTypes>(
641667
.kzg
642668
.as_ref()
643669
.ok_or(GossipBlobError::KzgNotInitialized)?;
644-
let kzg_verified_blob =
645-
KzgVerifiedBlob::new(blob_sidecar, kzg).map_err(GossipBlobError::KzgError)?;
670+
let kzg_verified_blob = KzgVerifiedBlob::new(blob_sidecar, kzg, seen_timestamp)
671+
.map_err(GossipBlobError::KzgError)?;
646672

647673
Ok(GossipVerifiedBlob {
648674
block_root,

beacon_node/beacon_chain/src/block_times_cache.rs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,35 @@ type BlockRoot = Hash256;
1818
#[derive(Clone, Default)]
1919
pub struct Timestamps {
2020
pub observed: Option<Duration>,
21+
pub all_blobs_observed: Option<Duration>,
22+
pub execution_time: Option<Duration>,
23+
pub attestable: Option<Duration>,
2124
pub imported: Option<Duration>,
2225
pub set_as_head: Option<Duration>,
2326
}
2427

2528
// Helps arrange delay data so it is more relevant to metrics.
2629
#[derive(Debug, Default)]
2730
pub struct BlockDelays {
31+
/// Time after start of slot we saw the block.
2832
pub observed: Option<Duration>,
33+
/// The time after the start of the slot we saw all blobs.
34+
pub all_blobs_observed: Option<Duration>,
35+
/// The time it took to get verification from the EL for the block.
36+
pub execution_time: Option<Duration>,
37+
/// The delay from the start of the slot before the block became available
38+
///
39+
/// Equal to max(`observed + execution_time`, `all_blobs_observed`).
40+
pub available: Option<Duration>,
41+
/// Time after `available`.
42+
pub attestable: Option<Duration>,
43+
/// Time
44+
/// ALSO time after `available`.
45+
///
46+
/// We need to use `available` again rather than `attestable` to handle the case where the block
47+
/// does not get added to the early-attester cache.
2948
pub imported: Option<Duration>,
49+
/// Time after `imported`.
3050
pub set_as_head: Option<Duration>,
3151
}
3252

@@ -35,14 +55,34 @@ impl BlockDelays {
3555
let observed = times
3656
.observed
3757
.and_then(|observed_time| observed_time.checked_sub(slot_start_time));
58+
let all_blobs_observed = times
59+
.all_blobs_observed
60+
.and_then(|all_blobs_observed| all_blobs_observed.checked_sub(slot_start_time));
61+
let execution_time = times
62+
.execution_time
63+
.and_then(|execution_time| execution_time.checked_sub(times.observed?));
64+
// Duration since UNIX epoch at which block became available.
65+
let available_time = times.execution_time.map(|execution_time| {
66+
std::cmp::max(execution_time, times.all_blobs_observed.unwrap_or_default())
67+
});
68+
// Duration from the start of the slot until the block became available.
69+
let available_delay =
70+
available_time.and_then(|available_time| available_time.checked_sub(slot_start_time));
71+
let attestable = times
72+
.attestable
73+
.and_then(|attestable_time| attestable_time.checked_sub(slot_start_time));
3874
let imported = times
3975
.imported
40-
.and_then(|imported_time| imported_time.checked_sub(times.observed?));
76+
.and_then(|imported_time| imported_time.checked_sub(available_time?));
4177
let set_as_head = times
4278
.set_as_head
4379
.and_then(|set_as_head_time| set_as_head_time.checked_sub(times.imported?));
4480
BlockDelays {
4581
observed,
82+
all_blobs_observed,
83+
execution_time,
84+
available: available_delay,
85+
attestable,
4686
imported,
4787
set_as_head,
4888
}
@@ -109,6 +149,53 @@ impl BlockTimesCache {
109149
}
110150
}
111151

152+
pub fn set_time_blob_observed(
153+
&mut self,
154+
block_root: BlockRoot,
155+
slot: Slot,
156+
timestamp: Duration,
157+
) {
158+
let block_times = self
159+
.cache
160+
.entry(block_root)
161+
.or_insert_with(|| BlockTimesCacheValue::new(slot));
162+
if block_times
163+
.timestamps
164+
.all_blobs_observed
165+
.map_or(true, |prev| timestamp > prev)
166+
{
167+
block_times.timestamps.all_blobs_observed = Some(timestamp);
168+
}
169+
}
170+
171+
pub fn set_execution_time(&mut self, block_root: BlockRoot, slot: Slot, timestamp: Duration) {
172+
let block_times = self
173+
.cache
174+
.entry(block_root)
175+
.or_insert_with(|| BlockTimesCacheValue::new(slot));
176+
if block_times
177+
.timestamps
178+
.execution_time
179+
.map_or(true, |prev| timestamp < prev)
180+
{
181+
block_times.timestamps.execution_time = Some(timestamp);
182+
}
183+
}
184+
185+
pub fn set_time_attestable(&mut self, block_root: BlockRoot, slot: Slot, timestamp: Duration) {
186+
let block_times = self
187+
.cache
188+
.entry(block_root)
189+
.or_insert_with(|| BlockTimesCacheValue::new(slot));
190+
if block_times
191+
.timestamps
192+
.attestable
193+
.map_or(true, |prev| timestamp < prev)
194+
{
195+
block_times.timestamps.attestable = Some(timestamp);
196+
}
197+
}
198+
112199
pub fn set_time_imported(&mut self, block_root: BlockRoot, slot: Slot, timestamp: Duration) {
113200
let block_times = self
114201
.cache

beacon_node/beacon_chain/src/block_verification.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -666,8 +666,7 @@ type PayloadVerificationHandle<E> =
666666
/// - Parent is known
667667
/// - Signatures
668668
/// - State root check
669-
/// - Per block processing
670-
/// - Blobs sidecar has been validated if present
669+
/// - Block processing
671670
///
672671
/// Note: a `ExecutionPendingBlock` is not _forever_ valid to be imported, it may later become invalid
673672
/// due to finality or some other event. A `ExecutionPendingBlock` should be imported into the

0 commit comments

Comments
 (0)