Skip to content

Commit a33f25e

Browse files
committed
Enforce Checkpoint State Inside DA Boundary
1 parent dba609a commit a33f25e

File tree

1 file changed

+51
-11
lines changed

1 file changed

+51
-11
lines changed

beacon_node/client/src/builder.rs

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,6 @@ where
397397
let remote_state_id = if let Some(id) = remote_state {
398398
id
399399
} else {
400-
// TODO: Check finalized state inside DA boundary
401400
StateId::Finalized
402401
};
403402

@@ -413,15 +412,57 @@ where
413412
};
414413
debug!(context.log(), "Downloading {}", state_string);
415414

416-
let state = remote
415+
let checkpoint_state = remote
417416
.get_debug_beacon_states_ssz::<TEthSpec>(remote_state_id, &spec)
418417
.await
419418
.map_err(|e| format!("Error loading checkpoint state from remote: {:?}", e))?
420419
.ok_or_else(|| "Checkpoint state missing from remote".to_string())?;
421420

422-
debug!(context.log(), "Downloaded {}", state_string; "slot" => ?state.slot());
421+
debug!(context.log(), "Downloaded {}", state_string; "slot" => ?checkpoint_state.slot());
423422

424-
let state_block_slot = state.latest_block_header().slot;
423+
let genesis_state = genesis_state(&runtime_context, &config, log).await?;
424+
425+
if let Some(deneb_fork_epoch) = spec.deneb_fork_epoch {
426+
// Deneb is enabled. We must ensure the checkpoint state is within the data availability boundary.
427+
let genesis_time = genesis_state.genesis_time();
428+
let slot_clock = SystemTimeSlotClock::new(
429+
spec.genesis_slot,
430+
Duration::from_secs(genesis_time),
431+
Duration::from_secs(spec.seconds_per_slot),
432+
);
433+
let slot_clock_current_epoch = slot_clock
434+
.now()
435+
.map(|slot| slot.epoch(TEthSpec::slots_per_epoch()))
436+
.ok_or("Unable to determine current slot for DA boundary calculation")?;
437+
438+
if slot_clock_current_epoch > deneb_fork_epoch {
439+
let cutoff_epoch = if checkpoint_state.current_epoch() < deneb_fork_epoch {
440+
// The chain is past the deneb fork and we are trying to checkpoint sync to a state
441+
// before the fork. Here we use a modified DA boundary calculation. As long as we can
442+
// sync up to the deneb fork before the DA boundary advances past it, we are good.
443+
slot_clock_current_epoch.saturating_sub(
444+
spec.min_epochs_for_blob_sidecars_requests
445+
.saturating_sub(BLOB_AVAILABILITY_REDUCTION_EPOCHS),
446+
)
447+
} else {
448+
std::cmp::max(
449+
deneb_fork_epoch,
450+
slot_clock_current_epoch
451+
.saturating_sub(spec.min_epochs_for_blob_sidecars_requests),
452+
)
453+
};
454+
455+
if checkpoint_state.current_epoch() < cutoff_epoch {
456+
return Err("Requested checkpoint state is outside of the data availability boundary".to_string());
457+
}
458+
}
459+
debug!(
460+
context.log(),
461+
"Checkpoint state is within data availability boundary"
462+
);
463+
}
464+
465+
let state_block_slot = checkpoint_state.latest_block_header().slot;
425466

426467
debug!(context.log(), "Downloading corresponding block"; "block_slot" => ?state_block_slot);
427468
let block = remote
@@ -439,7 +480,7 @@ where
439480

440481
debug!(context.log(), "Downloaded corresponding block");
441482

442-
let anchor_state = if matches!(remote_state_id, StateId::Finalized) {
483+
let anchor_state = if remote_state_id == StateId::Finalized {
443484
AnchorState::Finalized
444485
} else {
445486
debug!(context.log(), "Downloading finalized block header");
@@ -459,21 +500,20 @@ where
459500
.header
460501
.message;
461502
debug!(context.log(), "Downloaded finalized block header"; "anchor_slot" => block.slot(), "finalized_slot" => finalized_header.slot);
462-
if finalized_header.slot < state.slot() {
463-
debug!(context.log(), "Checkpoint state is newer than remote finalized checkpoint! Treating as non-revertible!");
503+
if finalized_header.slot < checkpoint_state.slot() {
504+
debug!(context.log(), "Checkpoint state is newer than remote finalized checkpoint. Treating anchor state as non-revertible.");
505+
// TODO: does this work with checkpointz?
464506
AnchorState::NonRevertible
465507
} else {
466508
AnchorState::Finalized
467509
}
468510
};
469511

470-
let genesis_state = genesis_state(&runtime_context, &config, log).await?;
471-
472512
info!(
473513
context.log(),
474514
"Loaded checkpoint block and state";
475515
"block_slot" => block.slot(),
476-
"state_slot" => state.slot(),
516+
"state_slot" => checkpoint_state.slot(),
477517
"block_root" => ?block.canonical_root(),
478518
);
479519

@@ -502,7 +542,7 @@ where
502542
});
503543

504544
builder
505-
.weak_subjectivity_state(state, block, genesis_state, anchor_state)
545+
.weak_subjectivity_state(checkpoint_state, block, genesis_state, anchor_state)
506546
.map(|v| (v, service))?
507547
}
508548
ClientGenesis::DepositContract => {

0 commit comments

Comments
 (0)