mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-21 23:08:23 +00:00
Merge branch 'unstable' into eip4844
This commit is contained in:
@@ -60,7 +60,7 @@ use crate::validator_monitor::{
|
||||
};
|
||||
use crate::validator_pubkey_cache::ValidatorPubkeyCache;
|
||||
use crate::{metrics, BeaconChainError, BeaconForkChoiceStore, BeaconSnapshot, CachedHead};
|
||||
use eth2::types::{EventKind, SseBlock, SyncDuty};
|
||||
use eth2::types::{EventKind, SseBlock, SseExtendedPayloadAttributes, SyncDuty};
|
||||
use execution_layer::{
|
||||
BlockProposalContents, BuilderParams, ChainHealth, ExecutionLayer, FailedCondition,
|
||||
PayloadAttributes, PayloadStatus,
|
||||
@@ -92,6 +92,7 @@ use state_processing::{
|
||||
state_advance::{complete_state_advance, partial_state_advance},
|
||||
BlockSignatureStrategy, ConsensusContext, SigVerifiedOp, VerifyBlockRoot, VerifyOperation,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
@@ -3987,6 +3988,75 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn get_expected_withdrawals(
|
||||
&self,
|
||||
forkchoice_update_params: &ForkchoiceUpdateParameters,
|
||||
proposal_slot: Slot,
|
||||
) -> Result<Withdrawals<T::EthSpec>, Error> {
|
||||
let cached_head = self.canonical_head.cached_head();
|
||||
let head_state = &cached_head.snapshot.beacon_state;
|
||||
|
||||
let parent_block_root = forkchoice_update_params.head_root;
|
||||
|
||||
let (unadvanced_state, unadvanced_state_root) =
|
||||
if cached_head.head_block_root() == parent_block_root {
|
||||
(Cow::Borrowed(head_state), cached_head.head_state_root())
|
||||
} else if let Some(snapshot) = self
|
||||
.snapshot_cache
|
||||
.try_read_for(BLOCK_PROCESSING_CACHE_LOCK_TIMEOUT)
|
||||
.ok_or(Error::SnapshotCacheLockTimeout)?
|
||||
.get_cloned(parent_block_root, CloneConfig::none())
|
||||
{
|
||||
debug!(
|
||||
self.log,
|
||||
"Hit snapshot cache during withdrawals calculation";
|
||||
"slot" => proposal_slot,
|
||||
"parent_block_root" => ?parent_block_root,
|
||||
);
|
||||
let state_root = snapshot.beacon_state_root();
|
||||
(Cow::Owned(snapshot.beacon_state), state_root)
|
||||
} else {
|
||||
info!(
|
||||
self.log,
|
||||
"Missed snapshot cache during withdrawals calculation";
|
||||
"slot" => proposal_slot,
|
||||
"parent_block_root" => ?parent_block_root
|
||||
);
|
||||
let block = self
|
||||
.get_blinded_block(&parent_block_root)?
|
||||
.ok_or(Error::MissingBeaconBlock(parent_block_root))?;
|
||||
let state = self
|
||||
.get_state(&block.state_root(), Some(block.slot()))?
|
||||
.ok_or(Error::MissingBeaconState(block.state_root()))?;
|
||||
(Cow::Owned(state), block.state_root())
|
||||
};
|
||||
|
||||
// Parent state epoch is the same as the proposal, we don't need to advance because the
|
||||
// list of expected withdrawals can only change after an epoch advance or a
|
||||
// block application.
|
||||
let proposal_epoch = proposal_slot.epoch(T::EthSpec::slots_per_epoch());
|
||||
if head_state.current_epoch() == proposal_epoch {
|
||||
return get_expected_withdrawals(&unadvanced_state, &self.spec)
|
||||
.map_err(Error::PrepareProposerFailed);
|
||||
}
|
||||
|
||||
// Advance the state using the partial method.
|
||||
debug!(
|
||||
self.log,
|
||||
"Advancing state for withdrawals calculation";
|
||||
"proposal_slot" => proposal_slot,
|
||||
"parent_block_root" => ?parent_block_root,
|
||||
);
|
||||
let mut advanced_state = unadvanced_state.into_owned();
|
||||
partial_state_advance(
|
||||
&mut advanced_state,
|
||||
Some(unadvanced_state_root),
|
||||
proposal_epoch.start_slot(T::EthSpec::slots_per_epoch()),
|
||||
&self.spec,
|
||||
)?;
|
||||
get_expected_withdrawals(&advanced_state, &self.spec).map_err(Error::PrepareProposerFailed)
|
||||
}
|
||||
|
||||
/// Determine whether a fork choice update to the execution layer should be overridden.
|
||||
///
|
||||
/// This is *only* necessary when proposer re-orgs are enabled, because we have to prevent the
|
||||
@@ -4858,7 +4928,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
// Nothing to do if there are no proposers registered with the EL, exit early to avoid
|
||||
// wasting cycles.
|
||||
if !execution_layer.has_any_proposer_preparation_data().await {
|
||||
if !self.config.always_prepare_payload
|
||||
&& !execution_layer.has_any_proposer_preparation_data().await
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -4915,64 +4987,60 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// If the execution layer doesn't have any proposer data for this validator then we assume
|
||||
// it's not connected to this BN and no action is required.
|
||||
let proposer = pre_payload_attributes.proposer_index;
|
||||
if !execution_layer
|
||||
.has_proposer_preparation_data(proposer)
|
||||
.await
|
||||
if !self.config.always_prepare_payload
|
||||
&& !execution_layer
|
||||
.has_proposer_preparation_data(proposer)
|
||||
.await
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
.transpose()
|
||||
.map_err(|e| {
|
||||
error!(self.log, "Error preparing beacon proposer"; "error" => ?e);
|
||||
e
|
||||
})
|
||||
.map(|withdrawals_opt| withdrawals_opt.map(|w| w.into()))
|
||||
.map_err(Error::PrepareProposerFailed)?;
|
||||
|
||||
// Fetch payoad attributes from the execution layer's cache, or compute them from scratch
|
||||
// if no matching entry is found. This saves recomputing the withdrawals which can take
|
||||
// considerable time to compute if a state load is required.
|
||||
let head_root = forkchoice_update_params.head_root;
|
||||
let payload_attributes = PayloadAttributes::new(
|
||||
self.slot_clock
|
||||
.start_of(prepare_slot)
|
||||
.ok_or(Error::InvalidSlot(prepare_slot))?
|
||||
.as_secs(),
|
||||
pre_payload_attributes.prev_randao,
|
||||
execution_layer.get_suggested_fee_recipient(proposer).await,
|
||||
withdrawals,
|
||||
);
|
||||
let payload_attributes = if let Some(payload_attributes) = execution_layer
|
||||
.payload_attributes(prepare_slot, head_root)
|
||||
.await
|
||||
{
|
||||
payload_attributes
|
||||
} else {
|
||||
let withdrawals = match self.spec.fork_name_at_slot::<T::EthSpec>(prepare_slot) {
|
||||
ForkName::Base | ForkName::Altair | ForkName::Merge => None,
|
||||
ForkName::Capella | ForkName::Eip4844 => {
|
||||
let chain = self.clone();
|
||||
self.spawn_blocking_handle(
|
||||
move || {
|
||||
chain.get_expected_withdrawals(&forkchoice_update_params, prepare_slot)
|
||||
},
|
||||
"prepare_beacon_proposer_withdrawals",
|
||||
)
|
||||
.await?
|
||||
.map(Some)?
|
||||
}
|
||||
};
|
||||
|
||||
debug!(
|
||||
self.log,
|
||||
"Preparing beacon proposer";
|
||||
"payload_attributes" => ?payload_attributes,
|
||||
"prepare_slot" => prepare_slot,
|
||||
"validator" => proposer,
|
||||
"parent_root" => ?head_root,
|
||||
);
|
||||
let payload_attributes = PayloadAttributes::new(
|
||||
self.slot_clock
|
||||
.start_of(prepare_slot)
|
||||
.ok_or(Error::InvalidSlot(prepare_slot))?
|
||||
.as_secs(),
|
||||
pre_payload_attributes.prev_randao,
|
||||
execution_layer.get_suggested_fee_recipient(proposer).await,
|
||||
withdrawals.map(Into::into),
|
||||
);
|
||||
|
||||
let already_known = execution_layer
|
||||
.insert_proposer(prepare_slot, head_root, proposer, payload_attributes)
|
||||
.await;
|
||||
execution_layer
|
||||
.insert_proposer(
|
||||
prepare_slot,
|
||||
head_root,
|
||||
proposer,
|
||||
payload_attributes.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Only push a log to the user if this is the first time we've seen this proposer for this
|
||||
// slot.
|
||||
if !already_known {
|
||||
// Only push a log to the user if this is the first time we've seen this proposer for
|
||||
// this slot.
|
||||
info!(
|
||||
self.log,
|
||||
"Prepared beacon proposer";
|
||||
@@ -4980,6 +5048,23 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
"validator" => proposer,
|
||||
"parent_root" => ?head_root,
|
||||
);
|
||||
payload_attributes
|
||||
};
|
||||
|
||||
// Push a server-sent event (probably to a block builder or relay).
|
||||
if let Some(event_handler) = &self.event_handler {
|
||||
if event_handler.has_payload_attributes_subscribers() {
|
||||
event_handler.register(EventKind::PayloadAttributes(ForkVersionedResponse {
|
||||
data: SseExtendedPayloadAttributes {
|
||||
proposal_slot: prepare_slot,
|
||||
proposer_index: proposer,
|
||||
parent_block_root: head_root,
|
||||
parent_block_hash: forkchoice_update_params.head_hash.unwrap_or_default(),
|
||||
payload_attributes: payload_attributes.into(),
|
||||
},
|
||||
version: Some(self.spec.fork_name_at_slot::<T::EthSpec>(prepare_slot)),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
let till_prepare_slot =
|
||||
@@ -5002,7 +5087,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
// If we are close enough to the proposal slot, send an fcU, which will have payload
|
||||
// attributes filled in by the execution layer cache we just primed.
|
||||
if till_prepare_slot <= self.config.prepare_payload_lookahead {
|
||||
if self.config.always_prepare_payload
|
||||
|| till_prepare_slot <= self.config.prepare_payload_lookahead
|
||||
{
|
||||
debug!(
|
||||
self.log,
|
||||
"Sending forkchoiceUpdate for proposer prep";
|
||||
|
||||
@@ -67,6 +67,10 @@ pub struct ChainConfig {
|
||||
pub prepare_payload_lookahead: Duration,
|
||||
/// Use EL-free optimistic sync for the finalized part of the chain.
|
||||
pub optimistic_finalized_sync: bool,
|
||||
/// Whether to send payload attributes every slot, regardless of connected proposers.
|
||||
///
|
||||
/// This is useful for block builders and testing.
|
||||
pub always_prepare_payload: bool,
|
||||
}
|
||||
|
||||
impl Default for ChainConfig {
|
||||
@@ -93,6 +97,7 @@ impl Default for ChainConfig {
|
||||
prepare_payload_lookahead: Duration::from_secs(4),
|
||||
// This value isn't actually read except in tests.
|
||||
optimistic_finalized_sync: true,
|
||||
always_prepare_payload: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ pub struct ServerSentEventHandler<T: EthSpec> {
|
||||
exit_tx: Sender<EventKind<T>>,
|
||||
chain_reorg_tx: Sender<EventKind<T>>,
|
||||
contribution_tx: Sender<EventKind<T>>,
|
||||
payload_attributes_tx: Sender<EventKind<T>>,
|
||||
late_head: Sender<EventKind<T>>,
|
||||
block_reward_tx: Sender<EventKind<T>>,
|
||||
log: Logger,
|
||||
@@ -32,6 +33,7 @@ impl<T: EthSpec> ServerSentEventHandler<T> {
|
||||
let (exit_tx, _) = broadcast::channel(capacity);
|
||||
let (chain_reorg_tx, _) = broadcast::channel(capacity);
|
||||
let (contribution_tx, _) = broadcast::channel(capacity);
|
||||
let (payload_attributes_tx, _) = broadcast::channel(capacity);
|
||||
let (late_head, _) = broadcast::channel(capacity);
|
||||
let (block_reward_tx, _) = broadcast::channel(capacity);
|
||||
|
||||
@@ -43,6 +45,7 @@ impl<T: EthSpec> ServerSentEventHandler<T> {
|
||||
exit_tx,
|
||||
chain_reorg_tx,
|
||||
contribution_tx,
|
||||
payload_attributes_tx,
|
||||
late_head,
|
||||
block_reward_tx,
|
||||
log,
|
||||
@@ -50,28 +53,55 @@ impl<T: EthSpec> ServerSentEventHandler<T> {
|
||||
}
|
||||
|
||||
pub fn register(&self, kind: EventKind<T>) {
|
||||
let result = match kind {
|
||||
EventKind::Attestation(attestation) => self
|
||||
let log_count = |name, count| {
|
||||
trace!(
|
||||
self.log,
|
||||
"Registering server-sent event";
|
||||
"kind" => name,
|
||||
"receiver_count" => count
|
||||
);
|
||||
};
|
||||
let result = match &kind {
|
||||
EventKind::Attestation(_) => self
|
||||
.attestation_tx
|
||||
.send(EventKind::Attestation(attestation))
|
||||
.map(|count| trace!(self.log, "Registering server-sent attestation event"; "receiver_count" => count)),
|
||||
EventKind::Block(block) => self.block_tx.send(EventKind::Block(block))
|
||||
.map(|count| trace!(self.log, "Registering server-sent block event"; "receiver_count" => count)),
|
||||
EventKind::FinalizedCheckpoint(checkpoint) => self.finalized_tx
|
||||
.send(EventKind::FinalizedCheckpoint(checkpoint))
|
||||
.map(|count| trace!(self.log, "Registering server-sent finalized checkpoint event"; "receiver_count" => count)),
|
||||
EventKind::Head(head) => self.head_tx.send(EventKind::Head(head))
|
||||
.map(|count| trace!(self.log, "Registering server-sent head event"; "receiver_count" => count)),
|
||||
EventKind::VoluntaryExit(exit) => self.exit_tx.send(EventKind::VoluntaryExit(exit))
|
||||
.map(|count| trace!(self.log, "Registering server-sent voluntary exit event"; "receiver_count" => count)),
|
||||
EventKind::ChainReorg(reorg) => self.chain_reorg_tx.send(EventKind::ChainReorg(reorg))
|
||||
.map(|count| trace!(self.log, "Registering server-sent chain reorg event"; "receiver_count" => count)),
|
||||
EventKind::ContributionAndProof(contribution_and_proof) => self.contribution_tx.send(EventKind::ContributionAndProof(contribution_and_proof))
|
||||
.map(|count| trace!(self.log, "Registering server-sent contribution and proof event"; "receiver_count" => count)),
|
||||
EventKind::LateHead(late_head) => self.late_head.send(EventKind::LateHead(late_head))
|
||||
.map(|count| trace!(self.log, "Registering server-sent late head event"; "receiver_count" => count)),
|
||||
EventKind::BlockReward(block_reward) => self.block_reward_tx.send(EventKind::BlockReward(block_reward))
|
||||
.map(|count| trace!(self.log, "Registering server-sent contribution and proof event"; "receiver_count" => count)),
|
||||
.send(kind)
|
||||
.map(|count| log_count(count, "attestation")),
|
||||
EventKind::Block(_) => self
|
||||
.block_tx
|
||||
.send(kind)
|
||||
.map(|count| log_count(count, "block")),
|
||||
EventKind::FinalizedCheckpoint(_) => self
|
||||
.finalized_tx
|
||||
.send(kind)
|
||||
.map(|count| log_count(count, "finalized checkpoint")),
|
||||
EventKind::Head(_) => self
|
||||
.head_tx
|
||||
.send(kind)
|
||||
.map(|count| log_count(count, "head")),
|
||||
EventKind::VoluntaryExit(_) => self
|
||||
.exit_tx
|
||||
.send(kind)
|
||||
.map(|count| log_count(count, "exit")),
|
||||
EventKind::ChainReorg(_) => self
|
||||
.chain_reorg_tx
|
||||
.send(kind)
|
||||
.map(|count| log_count(count, "chain reorg")),
|
||||
EventKind::ContributionAndProof(_) => self
|
||||
.contribution_tx
|
||||
.send(kind)
|
||||
.map(|count| log_count(count, "contribution and proof")),
|
||||
EventKind::PayloadAttributes(_) => self
|
||||
.payload_attributes_tx
|
||||
.send(kind)
|
||||
.map(|count| log_count(count, "payload attributes")),
|
||||
EventKind::LateHead(_) => self
|
||||
.late_head
|
||||
.send(kind)
|
||||
.map(|count| log_count(count, "late head")),
|
||||
EventKind::BlockReward(_) => self
|
||||
.block_reward_tx
|
||||
.send(kind)
|
||||
.map(|count| log_count(count, "block reward")),
|
||||
};
|
||||
if let Err(SendError(event)) = result {
|
||||
trace!(self.log, "No receivers registered to listen for event"; "event" => ?event);
|
||||
@@ -106,6 +136,10 @@ impl<T: EthSpec> ServerSentEventHandler<T> {
|
||||
self.contribution_tx.subscribe()
|
||||
}
|
||||
|
||||
pub fn subscribe_payload_attributes(&self) -> Receiver<EventKind<T>> {
|
||||
self.payload_attributes_tx.subscribe()
|
||||
}
|
||||
|
||||
pub fn subscribe_late_head(&self) -> Receiver<EventKind<T>> {
|
||||
self.late_head.subscribe()
|
||||
}
|
||||
@@ -142,6 +176,10 @@ impl<T: EthSpec> ServerSentEventHandler<T> {
|
||||
self.contribution_tx.receiver_count() > 0
|
||||
}
|
||||
|
||||
pub fn has_payload_attributes_subscribers(&self) -> bool {
|
||||
self.payload_attributes_tx.receiver_count() > 0
|
||||
}
|
||||
|
||||
pub fn has_late_head_subscribers(&self) -> bool {
|
||||
self.late_head.receiver_count() > 0
|
||||
}
|
||||
|
||||
@@ -111,6 +111,14 @@ pub enum AttestationStrategy {
|
||||
SomeValidators(Vec<usize>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SyncCommitteeStrategy {
|
||||
/// All sync committee validators sign.
|
||||
AllValidators,
|
||||
/// No validators sign.
|
||||
NoValidators,
|
||||
}
|
||||
|
||||
/// Indicates whether the `BeaconChainHarness` should use the `state.current_sync_committee` or
|
||||
/// `state.next_sync_committee` when creating sync messages or contributions.
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -1772,15 +1780,64 @@ where
|
||||
self.process_attestations(attestations);
|
||||
}
|
||||
|
||||
pub fn sync_committee_sign_block(
|
||||
&self,
|
||||
state: &BeaconState<E>,
|
||||
block_hash: Hash256,
|
||||
slot: Slot,
|
||||
relative_sync_committee: RelativeSyncCommittee,
|
||||
) {
|
||||
let sync_contributions =
|
||||
self.make_sync_contributions(state, block_hash, slot, relative_sync_committee);
|
||||
self.process_sync_contributions(sync_contributions).unwrap()
|
||||
}
|
||||
|
||||
pub async fn add_attested_block_at_slot(
|
||||
&self,
|
||||
slot: Slot,
|
||||
state: BeaconState<E>,
|
||||
state_root: Hash256,
|
||||
validators: &[usize],
|
||||
) -> Result<(SignedBeaconBlockHash, BeaconState<E>), BlockError<E>> {
|
||||
self.add_attested_block_at_slot_with_sync(
|
||||
slot,
|
||||
state,
|
||||
state_root,
|
||||
validators,
|
||||
SyncCommitteeStrategy::NoValidators,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn add_attested_block_at_slot_with_sync(
|
||||
&self,
|
||||
slot: Slot,
|
||||
state: BeaconState<E>,
|
||||
state_root: Hash256,
|
||||
validators: &[usize],
|
||||
sync_committee_strategy: SyncCommitteeStrategy,
|
||||
) -> Result<(SignedBeaconBlockHash, BeaconState<E>), BlockError<E>> {
|
||||
let (block_hash, block, state) = self.add_block_at_slot(slot, state).await?;
|
||||
self.attest_block(&state, state_root, block_hash, &block, validators);
|
||||
|
||||
if sync_committee_strategy == SyncCommitteeStrategy::AllValidators
|
||||
&& state.current_sync_committee().is_ok()
|
||||
{
|
||||
self.sync_committee_sign_block(
|
||||
&state,
|
||||
block_hash.into(),
|
||||
slot,
|
||||
if (slot + 1).epoch(E::slots_per_epoch())
|
||||
% self.spec.epochs_per_sync_committee_period
|
||||
== 0
|
||||
{
|
||||
RelativeSyncCommittee::Next
|
||||
} else {
|
||||
RelativeSyncCommittee::Current
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Ok((block_hash, state))
|
||||
}
|
||||
|
||||
@@ -1790,10 +1847,35 @@ where
|
||||
state_root: Hash256,
|
||||
slots: &[Slot],
|
||||
validators: &[usize],
|
||||
) -> AddBlocksResult<E> {
|
||||
self.add_attested_blocks_at_slots_with_sync(
|
||||
state,
|
||||
state_root,
|
||||
slots,
|
||||
validators,
|
||||
SyncCommitteeStrategy::NoValidators,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn add_attested_blocks_at_slots_with_sync(
|
||||
&self,
|
||||
state: BeaconState<E>,
|
||||
state_root: Hash256,
|
||||
slots: &[Slot],
|
||||
validators: &[usize],
|
||||
sync_committee_strategy: SyncCommitteeStrategy,
|
||||
) -> AddBlocksResult<E> {
|
||||
assert!(!slots.is_empty());
|
||||
self.add_attested_blocks_at_slots_given_lbh(state, state_root, slots, validators, None)
|
||||
.await
|
||||
self.add_attested_blocks_at_slots_given_lbh(
|
||||
state,
|
||||
state_root,
|
||||
slots,
|
||||
validators,
|
||||
None,
|
||||
sync_committee_strategy,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn add_attested_blocks_at_slots_given_lbh(
|
||||
@@ -1803,6 +1885,7 @@ where
|
||||
slots: &[Slot],
|
||||
validators: &[usize],
|
||||
mut latest_block_hash: Option<SignedBeaconBlockHash>,
|
||||
sync_committee_strategy: SyncCommitteeStrategy,
|
||||
) -> AddBlocksResult<E> {
|
||||
assert!(
|
||||
slots.windows(2).all(|w| w[0] <= w[1]),
|
||||
@@ -1812,7 +1895,13 @@ where
|
||||
let mut state_hash_from_slot: HashMap<Slot, BeaconStateHash> = HashMap::new();
|
||||
for slot in slots {
|
||||
let (block_hash, new_state) = self
|
||||
.add_attested_block_at_slot(*slot, state, state_root, validators)
|
||||
.add_attested_block_at_slot_with_sync(
|
||||
*slot,
|
||||
state,
|
||||
state_root,
|
||||
validators,
|
||||
sync_committee_strategy,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
state = new_state;
|
||||
@@ -1894,6 +1983,7 @@ where
|
||||
&epoch_slots,
|
||||
&validators,
|
||||
Some(head_block),
|
||||
SyncCommitteeStrategy::NoValidators, // for backwards compat
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -2011,6 +2101,22 @@ where
|
||||
num_blocks: usize,
|
||||
block_strategy: BlockStrategy,
|
||||
attestation_strategy: AttestationStrategy,
|
||||
) -> Hash256 {
|
||||
self.extend_chain_with_sync(
|
||||
num_blocks,
|
||||
block_strategy,
|
||||
attestation_strategy,
|
||||
SyncCommitteeStrategy::NoValidators,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn extend_chain_with_sync(
|
||||
&self,
|
||||
num_blocks: usize,
|
||||
block_strategy: BlockStrategy,
|
||||
attestation_strategy: AttestationStrategy,
|
||||
sync_committee_strategy: SyncCommitteeStrategy,
|
||||
) -> Hash256 {
|
||||
let (mut state, slots) = match block_strategy {
|
||||
BlockStrategy::OnCanonicalHead => {
|
||||
@@ -2042,7 +2148,13 @@ where
|
||||
};
|
||||
let state_root = state.update_tree_hash_cache().unwrap();
|
||||
let (_, _, last_produced_block_hash, _) = self
|
||||
.add_attested_blocks_at_slots(state, state_root, &slots, &validators)
|
||||
.add_attested_blocks_at_slots_with_sync(
|
||||
state,
|
||||
state_root,
|
||||
&slots,
|
||||
&validators,
|
||||
sync_committee_strategy,
|
||||
)
|
||||
.await;
|
||||
last_produced_block_hash.into()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user