mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-14 18:32:42 +00:00
Server sent events (#1920)
## Issue Addressed Resolves #1434 (this is the last major feature in the standard spec. There are only a couple of places we may be off-spec due to recent spec changes or ongoing discussion) Partly addresses #1669 ## Proposed Changes - remove the websocket server - remove the `TeeEventHandler` and `NullEventHandler` - add server sent events according to the eth2 API spec ## Additional Info This is according to the currently unmerged PR here: https://github.com/ethereum/eth2.0-APIs/pull/117 Co-authored-by: realbigsean <seananderson33@gmail.com>
This commit is contained in:
@@ -10,7 +10,7 @@ use crate::block_verification::{
|
||||
use crate::chain_config::ChainConfig;
|
||||
use crate::errors::{BeaconChainError as Error, BlockProductionError};
|
||||
use crate::eth1_chain::{Eth1Chain, Eth1ChainBackend};
|
||||
use crate::events::{EventHandler, EventKind};
|
||||
use crate::events::ServerSentEventHandler;
|
||||
use crate::head_tracker::HeadTracker;
|
||||
use crate::migrate::BackgroundMigrator;
|
||||
use crate::naive_aggregation_pool::{Error as NaiveAggregationError, NaiveAggregationPool};
|
||||
@@ -27,6 +27,7 @@ use crate::validator_pubkey_cache::ValidatorPubkeyCache;
|
||||
use crate::BeaconForkChoiceStore;
|
||||
use crate::BeaconSnapshot;
|
||||
use crate::{metrics, BeaconChainError};
|
||||
use eth2::types::{EventKind, SseBlock, SseFinalizedCheckpoint, SseHead};
|
||||
use fork_choice::ForkChoice;
|
||||
use futures::channel::mpsc::Sender;
|
||||
use itertools::process_results;
|
||||
@@ -157,7 +158,6 @@ pub trait BeaconChainTypes: Send + Sync + 'static {
|
||||
type SlotClock: slot_clock::SlotClock;
|
||||
type Eth1Chain: Eth1ChainBackend<Self::EthSpec>;
|
||||
type EthSpec: types::EthSpec;
|
||||
type EventHandler: EventHandler<Self::EthSpec>;
|
||||
}
|
||||
|
||||
/// Represents the "Beacon Chain" component of Ethereum 2.0. Allows import of blocks and block
|
||||
@@ -214,8 +214,9 @@ pub struct BeaconChain<T: BeaconChainTypes> {
|
||||
pub fork_choice: RwLock<
|
||||
ForkChoice<BeaconForkChoiceStore<T::EthSpec, T::HotStore, T::ColdStore>, T::EthSpec>,
|
||||
>,
|
||||
/// A handler for events generated by the beacon chain.
|
||||
pub event_handler: T::EventHandler,
|
||||
/// A handler for events generated by the beacon chain. This is only initialized when the
|
||||
/// HTTP server is enabled.
|
||||
pub event_handler: Option<ServerSentEventHandler<T::EthSpec>>,
|
||||
/// Used to track the heads of the beacon chain.
|
||||
pub(crate) head_tracker: Arc<HeadTracker>,
|
||||
/// A cache dedicated to block processing.
|
||||
@@ -955,17 +956,25 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// aggregation bit set.
|
||||
pub fn verify_unaggregated_attestation_for_gossip(
|
||||
&self,
|
||||
attestation: Attestation<T::EthSpec>,
|
||||
unaggregated_attestation: Attestation<T::EthSpec>,
|
||||
subnet_id: Option<SubnetId>,
|
||||
) -> Result<VerifiedUnaggregatedAttestation<T>, AttestationError> {
|
||||
metrics::inc_counter(&metrics::UNAGGREGATED_ATTESTATION_PROCESSING_REQUESTS);
|
||||
let _timer =
|
||||
metrics::start_timer(&metrics::UNAGGREGATED_ATTESTATION_GOSSIP_VERIFICATION_TIMES);
|
||||
|
||||
VerifiedUnaggregatedAttestation::verify(attestation, subnet_id, self).map(|v| {
|
||||
metrics::inc_counter(&metrics::UNAGGREGATED_ATTESTATION_PROCESSING_SUCCESSES);
|
||||
v
|
||||
})
|
||||
VerifiedUnaggregatedAttestation::verify(unaggregated_attestation, subnet_id, self).map(
|
||||
|v| {
|
||||
// This method is called for API and gossip attestations, so this covers all unaggregated attestation events
|
||||
if let Some(event_handler) = self.event_handler.as_ref() {
|
||||
if event_handler.has_attestation_subscribers() {
|
||||
event_handler.register(EventKind::Attestation(v.attestation().clone()));
|
||||
}
|
||||
}
|
||||
metrics::inc_counter(&metrics::UNAGGREGATED_ATTESTATION_PROCESSING_SUCCESSES);
|
||||
v
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Accepts some `SignedAggregateAndProof` from the network and attempts to verify it,
|
||||
@@ -979,6 +988,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
metrics::start_timer(&metrics::AGGREGATED_ATTESTATION_GOSSIP_VERIFICATION_TIMES);
|
||||
|
||||
VerifiedAggregatedAttestation::verify(signed_aggregate, self).map(|v| {
|
||||
// This method is called for API and gossip attestations, so this covers all aggregated attestation events
|
||||
if let Some(event_handler) = self.event_handler.as_ref() {
|
||||
if event_handler.has_attestation_subscribers() {
|
||||
event_handler.register(EventKind::Attestation(v.attestation().clone()));
|
||||
}
|
||||
}
|
||||
metrics::inc_counter(&metrics::AGGREGATED_ATTESTATION_PROCESSING_SUCCESSES);
|
||||
v
|
||||
})
|
||||
@@ -1222,11 +1237,21 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
) -> Result<ObservationOutcome<SignedVoluntaryExit>, Error> {
|
||||
// NOTE: this could be more efficient if it avoided cloning the head state
|
||||
let wall_clock_state = self.wall_clock_state()?;
|
||||
Ok(self.observed_voluntary_exits.lock().verify_and_observe(
|
||||
exit,
|
||||
&wall_clock_state,
|
||||
&self.spec,
|
||||
)?)
|
||||
Ok(self
|
||||
.observed_voluntary_exits
|
||||
.lock()
|
||||
.verify_and_observe(exit, &wall_clock_state, &self.spec)
|
||||
.map(|exit| {
|
||||
// this method is called for both API and gossip exits, so this covers all exit events
|
||||
if let Some(event_handler) = self.event_handler.as_ref() {
|
||||
if event_handler.has_exit_subscribers() {
|
||||
if let ObservationOutcome::New(exit) = exit.clone() {
|
||||
event_handler.register(EventKind::VoluntaryExit(exit.into_inner()));
|
||||
}
|
||||
}
|
||||
}
|
||||
exit
|
||||
})?)
|
||||
}
|
||||
|
||||
/// Accept a pre-verified exit and queue it for inclusion in an appropriate block.
|
||||
@@ -1510,11 +1535,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// Increment the Prometheus counter for block processing successes.
|
||||
metrics::inc_counter(&metrics::BLOCK_PROCESSING_SUCCESSES);
|
||||
|
||||
let _ = self.event_handler.register(EventKind::BeaconBlockImported {
|
||||
block_root,
|
||||
block: Box::new(block),
|
||||
});
|
||||
|
||||
Ok(block_root)
|
||||
}
|
||||
// There was an error whilst attempting to verify and import the block. The block might
|
||||
@@ -1525,12 +1545,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
"Beacon block processing error";
|
||||
"error" => format!("{:?}", e),
|
||||
);
|
||||
|
||||
let _ = self.event_handler.register(EventKind::BeaconBlockRejected {
|
||||
reason: format!("Internal error: {:?}", e),
|
||||
block: Box::new(block),
|
||||
});
|
||||
|
||||
Err(BlockError::BeaconChainError(e))
|
||||
}
|
||||
// The block failed verification.
|
||||
@@ -1540,12 +1554,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
"Beacon block rejected";
|
||||
"reason" => other.to_string(),
|
||||
);
|
||||
|
||||
let _ = self.event_handler.register(EventKind::BeaconBlockRejected {
|
||||
reason: format!("Invalid block: {}", other),
|
||||
block: Box::new(block),
|
||||
});
|
||||
|
||||
Err(other)
|
||||
}
|
||||
}
|
||||
@@ -1664,7 +1672,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
);
|
||||
crit!(self.log, "You must use the `--purge-db` flag to clear the database and restart sync. You may be on a hostile network.");
|
||||
shutdown_sender.try_send("Weak subjectivity checkpoint verification failed. Provided block root is not a checkpoint.")
|
||||
.map_err(|err|BlockError::BeaconChainError(BeaconChainError::WeakSubjectivtyShutdownError(err)))?;
|
||||
.map_err(|err| BlockError::BeaconChainError(BeaconChainError::WeakSubjectivtyShutdownError(err)))?;
|
||||
return Err(BlockError::WeakSubjectivityConflict);
|
||||
}
|
||||
}
|
||||
@@ -1745,6 +1753,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
self.head_tracker
|
||||
.register_block(block_root, parent_root, slot);
|
||||
|
||||
// send an event to the `events` endpoint after fully processing the block
|
||||
if let Some(event_handler) = self.event_handler.as_ref() {
|
||||
if event_handler.has_block_subscribers() {
|
||||
event_handler.register(EventKind::Block(SseBlock {
|
||||
slot,
|
||||
block: block_root,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
metrics::stop_timer(db_write_timer);
|
||||
|
||||
metrics::inc_counter(&metrics::BLOCK_PROCESSING_SUCCESSES);
|
||||
@@ -1993,7 +2011,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
"previous_slot" => current_head.slot,
|
||||
"new_head_parent" => format!("{}", new_head.beacon_block.parent_root()),
|
||||
"new_head" => format!("{}", beacon_block_root),
|
||||
"new_slot" => new_head.beacon_block.slot()
|
||||
"new_slot" => new_head.beacon_block.slot(),
|
||||
);
|
||||
} else {
|
||||
debug!(
|
||||
@@ -2018,13 +2036,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
});
|
||||
}
|
||||
|
||||
if current_head.slot.epoch(T::EthSpec::slots_per_epoch())
|
||||
let is_epoch_transition = current_head.slot.epoch(T::EthSpec::slots_per_epoch())
|
||||
< new_head
|
||||
.beacon_state
|
||||
.slot
|
||||
.epoch(T::EthSpec::slots_per_epoch())
|
||||
|| is_reorg
|
||||
{
|
||||
.epoch(T::EthSpec::slots_per_epoch());
|
||||
|
||||
if is_epoch_transition || is_reorg {
|
||||
self.persist_head_and_fork_choice()?;
|
||||
self.op_pool.prune_attestations(self.epoch()?);
|
||||
self.ingest_slashings_to_op_pool(&new_head.beacon_state);
|
||||
@@ -2033,6 +2051,18 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
let update_head_timer = metrics::start_timer(&metrics::UPDATE_HEAD_TIMES);
|
||||
|
||||
// These fields are used for server-sent events
|
||||
let state_root = new_head.beacon_state_root;
|
||||
let head_slot = new_head.beacon_state.slot;
|
||||
let target_epoch_start_slot = new_head
|
||||
.beacon_state
|
||||
.current_epoch()
|
||||
.start_slot(T::EthSpec::slots_per_epoch());
|
||||
let prev_target_epoch_start_slot = new_head
|
||||
.beacon_state
|
||||
.previous_epoch()
|
||||
.start_slot(T::EthSpec::slots_per_epoch());
|
||||
|
||||
// Update the snapshot that stores the head of the chain at the time it received the
|
||||
// block.
|
||||
*self
|
||||
@@ -2092,11 +2122,37 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
self.after_finalization(&head.beacon_state, new_finalized_state_root)?;
|
||||
}
|
||||
|
||||
let _ = self.event_handler.register(EventKind::BeaconHeadChanged {
|
||||
reorg: is_reorg,
|
||||
previous_head_beacon_block_root: current_head.block_root,
|
||||
current_head_beacon_block_root: beacon_block_root,
|
||||
});
|
||||
// Register a server-sent event if necessary
|
||||
if let Some(event_handler) = self.event_handler.as_ref() {
|
||||
if event_handler.has_head_subscribers() {
|
||||
if let Ok(Some(current_duty_dependent_root)) =
|
||||
self.root_at_slot(target_epoch_start_slot - 1)
|
||||
{
|
||||
if let Ok(Some(previous_duty_dependent_root)) =
|
||||
self.root_at_slot(prev_target_epoch_start_slot - 1)
|
||||
{
|
||||
event_handler.register(EventKind::Head(SseHead {
|
||||
slot: head_slot,
|
||||
block: beacon_block_root,
|
||||
state: state_root,
|
||||
current_duty_dependent_root,
|
||||
previous_duty_dependent_root,
|
||||
epoch_transition: is_epoch_transition,
|
||||
}));
|
||||
} else {
|
||||
warn!(
|
||||
self.log,
|
||||
"Unable to find previous target root, cannot register head event"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
self.log,
|
||||
"Unable to find current target root, cannot register head event"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -2204,10 +2260,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
self.head_tracker.clone(),
|
||||
)?;
|
||||
|
||||
let _ = self.event_handler.register(EventKind::BeaconFinalization {
|
||||
epoch: new_finalized_checkpoint.epoch,
|
||||
root: new_finalized_checkpoint.root,
|
||||
});
|
||||
if let Some(event_handler) = self.event_handler.as_ref() {
|
||||
if event_handler.has_finalized_subscribers() {
|
||||
event_handler.register(EventKind::FinalizedCheckpoint(SseFinalizedCheckpoint {
|
||||
epoch: new_finalized_checkpoint.epoch,
|
||||
block: new_finalized_checkpoint.root,
|
||||
state: new_finalized_state_root,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use crate::beacon_chain::{
|
||||
BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, FORK_CHOICE_DB_KEY, OP_POOL_DB_KEY,
|
||||
};
|
||||
use crate::eth1_chain::{CachingEth1Backend, SszEth1};
|
||||
use crate::events::NullEventHandler;
|
||||
use crate::head_tracker::HeadTracker;
|
||||
use crate::migrate::{BackgroundMigrator, MigratorConfig};
|
||||
use crate::persisted_beacon_chain::PersistedBeaconChain;
|
||||
@@ -14,7 +13,7 @@ use crate::validator_pubkey_cache::ValidatorPubkeyCache;
|
||||
use crate::ChainConfig;
|
||||
use crate::{
|
||||
BeaconChain, BeaconChainTypes, BeaconForkChoiceStore, BeaconSnapshot, Eth1Chain,
|
||||
Eth1ChainBackend, EventHandler,
|
||||
Eth1ChainBackend, ServerSentEventHandler,
|
||||
};
|
||||
use eth1::Config as Eth1Config;
|
||||
use fork_choice::ForkChoice;
|
||||
@@ -38,33 +37,24 @@ pub const PUBKEY_CACHE_FILENAME: &str = "pubkey_cache.ssz";
|
||||
|
||||
/// An empty struct used to "witness" all the `BeaconChainTypes` traits. It has no user-facing
|
||||
/// functionality and only exists to satisfy the type system.
|
||||
pub struct Witness<TSlotClock, TEth1Backend, TEthSpec, TEventHandler, THotStore, TColdStore>(
|
||||
PhantomData<(
|
||||
TSlotClock,
|
||||
TEth1Backend,
|
||||
TEthSpec,
|
||||
TEventHandler,
|
||||
THotStore,
|
||||
TColdStore,
|
||||
)>,
|
||||
pub struct Witness<TSlotClock, TEth1Backend, TEthSpec, THotStore, TColdStore>(
|
||||
PhantomData<(TSlotClock, TEth1Backend, TEthSpec, THotStore, TColdStore)>,
|
||||
);
|
||||
|
||||
impl<TSlotClock, TEth1Backend, TEthSpec, TEventHandler, THotStore, TColdStore> BeaconChainTypes
|
||||
for Witness<TSlotClock, TEth1Backend, TEthSpec, TEventHandler, THotStore, TColdStore>
|
||||
impl<TSlotClock, TEth1Backend, TEthSpec, THotStore, TColdStore> BeaconChainTypes
|
||||
for Witness<TSlotClock, TEth1Backend, TEthSpec, THotStore, TColdStore>
|
||||
where
|
||||
THotStore: ItemStore<TEthSpec> + 'static,
|
||||
TColdStore: ItemStore<TEthSpec> + 'static,
|
||||
TSlotClock: SlotClock + 'static,
|
||||
TEth1Backend: Eth1ChainBackend<TEthSpec> + 'static,
|
||||
TEthSpec: EthSpec + 'static,
|
||||
TEventHandler: EventHandler<TEthSpec> + 'static,
|
||||
{
|
||||
type HotStore = THotStore;
|
||||
type ColdStore = TColdStore;
|
||||
type SlotClock = TSlotClock;
|
||||
type Eth1Chain = TEth1Backend;
|
||||
type EthSpec = TEthSpec;
|
||||
type EventHandler = TEventHandler;
|
||||
}
|
||||
|
||||
/// Builds a `BeaconChain` by either creating anew from genesis, or, resuming from an existing chain
|
||||
@@ -88,7 +78,7 @@ pub struct BeaconChainBuilder<T: BeaconChainTypes> {
|
||||
>,
|
||||
op_pool: Option<OperationPool<T::EthSpec>>,
|
||||
eth1_chain: Option<Eth1Chain<T::Eth1Chain, T::EthSpec>>,
|
||||
event_handler: Option<T::EventHandler>,
|
||||
event_handler: Option<ServerSentEventHandler<T::EthSpec>>,
|
||||
slot_clock: Option<T::SlotClock>,
|
||||
shutdown_sender: Option<Sender<&'static str>>,
|
||||
head_tracker: Option<HeadTracker>,
|
||||
@@ -103,17 +93,14 @@ pub struct BeaconChainBuilder<T: BeaconChainTypes> {
|
||||
slasher: Option<Arc<Slasher<T::EthSpec>>>,
|
||||
}
|
||||
|
||||
impl<TSlotClock, TEth1Backend, TEthSpec, TEventHandler, THotStore, TColdStore>
|
||||
BeaconChainBuilder<
|
||||
Witness<TSlotClock, TEth1Backend, TEthSpec, TEventHandler, THotStore, TColdStore>,
|
||||
>
|
||||
impl<TSlotClock, TEth1Backend, TEthSpec, THotStore, TColdStore>
|
||||
BeaconChainBuilder<Witness<TSlotClock, TEth1Backend, TEthSpec, THotStore, TColdStore>>
|
||||
where
|
||||
THotStore: ItemStore<TEthSpec> + 'static,
|
||||
TColdStore: ItemStore<TEthSpec> + 'static,
|
||||
TSlotClock: SlotClock + 'static,
|
||||
TEth1Backend: Eth1ChainBackend<TEthSpec> + 'static,
|
||||
TEthSpec: EthSpec + 'static,
|
||||
TEventHandler: EventHandler<TEthSpec> + 'static,
|
||||
{
|
||||
/// Returns a new builder.
|
||||
///
|
||||
@@ -377,9 +364,9 @@ where
|
||||
|
||||
/// Sets the `BeaconChain` event handler backend.
|
||||
///
|
||||
/// For example, provide `WebSocketSender` as a `handler`.
|
||||
pub fn event_handler(mut self, handler: TEventHandler) -> Self {
|
||||
self.event_handler = Some(handler);
|
||||
/// For example, provide `ServerSentEventHandler` as a `handler`.
|
||||
pub fn event_handler(mut self, handler: Option<ServerSentEventHandler<TEthSpec>>) -> Self {
|
||||
self.event_handler = handler;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -425,9 +412,7 @@ where
|
||||
pub fn build(
|
||||
self,
|
||||
) -> Result<
|
||||
BeaconChain<
|
||||
Witness<TSlotClock, TEth1Backend, TEthSpec, TEventHandler, THotStore, TColdStore>,
|
||||
>,
|
||||
BeaconChain<Witness<TSlotClock, TEth1Backend, TEthSpec, THotStore, TColdStore>>,
|
||||
String,
|
||||
> {
|
||||
let log = self.log.ok_or("Cannot build without a logger")?;
|
||||
@@ -549,9 +534,7 @@ where
|
||||
genesis_block_root,
|
||||
genesis_state_root,
|
||||
fork_choice: RwLock::new(fork_choice),
|
||||
event_handler: self
|
||||
.event_handler
|
||||
.ok_or("Cannot build without an event handler")?,
|
||||
event_handler: self.event_handler,
|
||||
head_tracker: Arc::new(self.head_tracker.unwrap_or_default()),
|
||||
snapshot_cache: TimeoutRwLock::new(SnapshotCache::new(
|
||||
DEFAULT_SNAPSHOT_CACHE_SIZE,
|
||||
@@ -605,23 +588,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<TSlotClock, TEthSpec, TEventHandler, THotStore, TColdStore>
|
||||
impl<TSlotClock, TEthSpec, THotStore, TColdStore>
|
||||
BeaconChainBuilder<
|
||||
Witness<
|
||||
TSlotClock,
|
||||
CachingEth1Backend<TEthSpec>,
|
||||
TEthSpec,
|
||||
TEventHandler,
|
||||
THotStore,
|
||||
TColdStore,
|
||||
>,
|
||||
Witness<TSlotClock, CachingEth1Backend<TEthSpec>, TEthSpec, THotStore, TColdStore>,
|
||||
>
|
||||
where
|
||||
THotStore: ItemStore<TEthSpec> + 'static,
|
||||
TColdStore: ItemStore<TEthSpec> + 'static,
|
||||
TSlotClock: SlotClock + 'static,
|
||||
TEthSpec: EthSpec + 'static,
|
||||
TEventHandler: EventHandler<TEthSpec> + 'static,
|
||||
{
|
||||
/// Do not use any eth1 backend. The client will not be able to produce beacon blocks.
|
||||
pub fn no_eth1_backend(self) -> Self {
|
||||
@@ -644,16 +619,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<TEth1Backend, TEthSpec, TEventHandler, THotStore, TColdStore>
|
||||
BeaconChainBuilder<
|
||||
Witness<TestingSlotClock, TEth1Backend, TEthSpec, TEventHandler, THotStore, TColdStore>,
|
||||
>
|
||||
impl<TEth1Backend, TEthSpec, THotStore, TColdStore>
|
||||
BeaconChainBuilder<Witness<TestingSlotClock, TEth1Backend, TEthSpec, THotStore, TColdStore>>
|
||||
where
|
||||
THotStore: ItemStore<TEthSpec> + 'static,
|
||||
TColdStore: ItemStore<TEthSpec> + 'static,
|
||||
TEth1Backend: Eth1ChainBackend<TEthSpec> + 'static,
|
||||
TEthSpec: EthSpec + 'static,
|
||||
TEventHandler: EventHandler<TEthSpec> + 'static,
|
||||
{
|
||||
/// Sets the `BeaconChain` slot clock to `TestingSlotClock`.
|
||||
///
|
||||
@@ -673,31 +645,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<TSlotClock, TEth1Backend, TEthSpec, THotStore, TColdStore>
|
||||
BeaconChainBuilder<
|
||||
Witness<
|
||||
TSlotClock,
|
||||
TEth1Backend,
|
||||
TEthSpec,
|
||||
NullEventHandler<TEthSpec>,
|
||||
THotStore,
|
||||
TColdStore,
|
||||
>,
|
||||
>
|
||||
where
|
||||
THotStore: ItemStore<TEthSpec> + 'static,
|
||||
TColdStore: ItemStore<TEthSpec> + 'static,
|
||||
TSlotClock: SlotClock + 'static,
|
||||
TEth1Backend: Eth1ChainBackend<TEthSpec> + 'static,
|
||||
TEthSpec: EthSpec + 'static,
|
||||
{
|
||||
/// Sets the `BeaconChain` event handler to `NullEventHandler`.
|
||||
pub fn null_event_handler(self) -> Self {
|
||||
let handler = NullEventHandler::default();
|
||||
self.event_handler(handler)
|
||||
}
|
||||
}
|
||||
|
||||
fn genesis_block<T: EthSpec>(
|
||||
genesis_state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
@@ -767,7 +714,6 @@ mod test {
|
||||
.expect("should build state using recent genesis")
|
||||
.dummy_eth1_backend()
|
||||
.expect("should build the dummy eth1 backend")
|
||||
.null_event_handler()
|
||||
.testing_slot_clock(Duration::from_secs(1))
|
||||
.expect("should configure testing slot clock")
|
||||
.shutdown_sender(shutdown_tx)
|
||||
|
||||
@@ -1,147 +1,113 @@
|
||||
use bus::Bus;
|
||||
use parking_lot::Mutex;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use slog::{error, Logger};
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use types::{Attestation, Epoch, EthSpec, Hash256, SignedBeaconBlock, SignedBeaconBlockHash};
|
||||
pub use websocket_server::WebSocketSender;
|
||||
pub use eth2::types::{EventKind, SseBlock, SseFinalizedCheckpoint, SseHead};
|
||||
use slog::{trace, Logger};
|
||||
use tokio::sync::broadcast;
|
||||
use tokio::sync::broadcast::{error::SendError, Receiver, Sender};
|
||||
use types::EthSpec;
|
||||
|
||||
pub trait EventHandler<T: EthSpec>: Sized + Send + Sync {
|
||||
fn register(&self, kind: EventKind<T>) -> Result<(), String>;
|
||||
}
|
||||
const DEFAULT_CHANNEL_CAPACITY: usize = 16;
|
||||
|
||||
pub struct NullEventHandler<T: EthSpec>(PhantomData<T>);
|
||||
|
||||
impl<T: EthSpec> EventHandler<T> for WebSocketSender<T> {
|
||||
fn register(&self, kind: EventKind<T>) -> Result<(), String> {
|
||||
self.send_string(
|
||||
serde_json::to_string(&kind)
|
||||
.map_err(|e| format!("Unable to serialize event: {:?}", e))?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ServerSentEvents<T: EthSpec> {
|
||||
// Bus<> is itself Sync + Send. We use Mutex<> here only because of the surrounding code does
|
||||
// not enforce mutability statically (i.e. relies on interior mutability).
|
||||
head_changed_queue: Arc<Mutex<Bus<SignedBeaconBlockHash>>>,
|
||||
pub struct ServerSentEventHandler<T: EthSpec> {
|
||||
attestation_tx: Sender<EventKind<T>>,
|
||||
block_tx: Sender<EventKind<T>>,
|
||||
finalized_tx: Sender<EventKind<T>>,
|
||||
head_tx: Sender<EventKind<T>>,
|
||||
exit_tx: Sender<EventKind<T>>,
|
||||
log: Logger,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> ServerSentEvents<T> {
|
||||
pub fn new(log: Logger) -> (Self, Arc<Mutex<Bus<SignedBeaconBlockHash>>>) {
|
||||
let bus = Bus::new(T::slots_per_epoch() as usize);
|
||||
let mutex = Mutex::new(bus);
|
||||
let arc = Arc::new(mutex);
|
||||
let this = Self {
|
||||
head_changed_queue: arc.clone(),
|
||||
impl<T: EthSpec> ServerSentEventHandler<T> {
|
||||
pub fn new(log: Logger) -> Self {
|
||||
let (attestation_tx, _) = broadcast::channel(DEFAULT_CHANNEL_CAPACITY);
|
||||
let (block_tx, _) = broadcast::channel(DEFAULT_CHANNEL_CAPACITY);
|
||||
let (finalized_tx, _) = broadcast::channel(DEFAULT_CHANNEL_CAPACITY);
|
||||
let (head_tx, _) = broadcast::channel(DEFAULT_CHANNEL_CAPACITY);
|
||||
let (exit_tx, _) = broadcast::channel(DEFAULT_CHANNEL_CAPACITY);
|
||||
|
||||
Self {
|
||||
attestation_tx,
|
||||
block_tx,
|
||||
finalized_tx,
|
||||
head_tx,
|
||||
exit_tx,
|
||||
log,
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
(this, arc)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> EventHandler<T> for ServerSentEvents<T> {
|
||||
fn register(&self, kind: EventKind<T>) -> Result<(), String> {
|
||||
match kind {
|
||||
EventKind::BeaconHeadChanged {
|
||||
current_head_beacon_block_root,
|
||||
..
|
||||
} => {
|
||||
let mut guard = self.head_changed_queue.lock();
|
||||
if guard
|
||||
.try_broadcast(current_head_beacon_block_root.into())
|
||||
.is_err()
|
||||
{
|
||||
error!(
|
||||
self.log,
|
||||
"Head change streaming queue full";
|
||||
"dropped_change" => format!("{}", current_head_beacon_block_root),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// An event handler that pushes events to both the websockets handler and the SSE handler.
|
||||
// Named after the unix `tee` command. Meant as a temporary solution before ditching WebSockets
|
||||
// completely once SSE functions well enough.
|
||||
pub struct TeeEventHandler<E: EthSpec> {
|
||||
websockets_handler: WebSocketSender<E>,
|
||||
sse_handler: ServerSentEvents<E>,
|
||||
}
|
||||
pub fn new_with_capacity(log: Logger, capacity: usize) -> Self {
|
||||
let (attestation_tx, _) = broadcast::channel(capacity);
|
||||
let (block_tx, _) = broadcast::channel(capacity);
|
||||
let (finalized_tx, _) = broadcast::channel(capacity);
|
||||
let (head_tx, _) = broadcast::channel(capacity);
|
||||
let (exit_tx, _) = broadcast::channel(capacity);
|
||||
|
||||
impl<E: EthSpec> TeeEventHandler<E> {
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn new(
|
||||
log: Logger,
|
||||
websockets_handler: WebSocketSender<E>,
|
||||
) -> Result<(Self, Arc<Mutex<Bus<SignedBeaconBlockHash>>>), String> {
|
||||
let (sse_handler, bus) = ServerSentEvents::new(log);
|
||||
let result = Self {
|
||||
websockets_handler,
|
||||
sse_handler,
|
||||
Self {
|
||||
attestation_tx,
|
||||
block_tx,
|
||||
finalized_tx,
|
||||
head_tx,
|
||||
exit_tx,
|
||||
log,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register(&self, kind: EventKind<T>) {
|
||||
let result = match kind {
|
||||
EventKind::Attestation(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)),
|
||||
};
|
||||
Ok((result, bus))
|
||||
if let Err(SendError(event)) = result {
|
||||
trace!(self.log, "No receivers registered to listen for event"; "event" => ?event);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscribe_attestation(&self) -> Receiver<EventKind<T>> {
|
||||
self.attestation_tx.subscribe()
|
||||
}
|
||||
|
||||
pub fn subscribe_block(&self) -> Receiver<EventKind<T>> {
|
||||
self.block_tx.subscribe()
|
||||
}
|
||||
|
||||
pub fn subscribe_finalized(&self) -> Receiver<EventKind<T>> {
|
||||
self.finalized_tx.subscribe()
|
||||
}
|
||||
|
||||
pub fn subscribe_head(&self) -> Receiver<EventKind<T>> {
|
||||
self.head_tx.subscribe()
|
||||
}
|
||||
|
||||
pub fn subscribe_exit(&self) -> Receiver<EventKind<T>> {
|
||||
self.exit_tx.subscribe()
|
||||
}
|
||||
|
||||
pub fn has_attestation_subscribers(&self) -> bool {
|
||||
self.attestation_tx.receiver_count() > 0
|
||||
}
|
||||
|
||||
pub fn has_block_subscribers(&self) -> bool {
|
||||
self.block_tx.receiver_count() > 0
|
||||
}
|
||||
|
||||
pub fn has_finalized_subscribers(&self) -> bool {
|
||||
self.finalized_tx.receiver_count() > 0
|
||||
}
|
||||
|
||||
pub fn has_head_subscribers(&self) -> bool {
|
||||
self.head_tx.receiver_count() > 0
|
||||
}
|
||||
|
||||
pub fn has_exit_subscribers(&self) -> bool {
|
||||
self.exit_tx.receiver_count() > 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> EventHandler<E> for TeeEventHandler<E> {
|
||||
fn register(&self, kind: EventKind<E>) -> Result<(), String> {
|
||||
self.websockets_handler.register(kind.clone())?;
|
||||
self.sse_handler.register(kind)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> EventHandler<T> for NullEventHandler<T> {
|
||||
fn register(&self, _kind: EventKind<T>) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> Default for NullEventHandler<T> {
|
||||
fn default() -> Self {
|
||||
NullEventHandler(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(
|
||||
bound = "T: EthSpec",
|
||||
rename_all = "snake_case",
|
||||
tag = "event",
|
||||
content = "data"
|
||||
)]
|
||||
pub enum EventKind<T: EthSpec> {
|
||||
BeaconHeadChanged {
|
||||
reorg: bool,
|
||||
current_head_beacon_block_root: Hash256,
|
||||
previous_head_beacon_block_root: Hash256,
|
||||
},
|
||||
BeaconFinalization {
|
||||
epoch: Epoch,
|
||||
root: Hash256,
|
||||
},
|
||||
BeaconBlockImported {
|
||||
block_root: Hash256,
|
||||
block: Box<SignedBeaconBlock<T>>,
|
||||
},
|
||||
BeaconBlockRejected {
|
||||
reason: String,
|
||||
block: Box<SignedBeaconBlock<T>>,
|
||||
},
|
||||
BeaconAttestationImported {
|
||||
attestation: Box<Attestation<T>>,
|
||||
},
|
||||
BeaconAttestationRejected {
|
||||
reason: String,
|
||||
attestation: Box<Attestation<T>>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ pub use attestation_verification::Error as AttestationError;
|
||||
pub use beacon_fork_choice_store::{BeaconForkChoiceStore, Error as ForkChoiceStoreError};
|
||||
pub use block_verification::{BlockError, GossipVerifiedBlock};
|
||||
pub use eth1_chain::{Eth1Chain, Eth1ChainBackend};
|
||||
pub use events::EventHandler;
|
||||
pub use events::ServerSentEventHandler;
|
||||
pub use metrics::scrape_for_metrics;
|
||||
pub use parking_lot;
|
||||
pub use slot_clock;
|
||||
|
||||
@@ -7,8 +7,8 @@ pub use crate::{
|
||||
use crate::{
|
||||
builder::{BeaconChainBuilder, Witness},
|
||||
eth1_chain::CachingEth1Backend,
|
||||
events::NullEventHandler,
|
||||
BeaconChain, BeaconChainTypes, BlockError, ChainConfig, StateSkipConfig,
|
||||
BeaconChain, BeaconChainTypes, BlockError, ChainConfig, ServerSentEventHandler,
|
||||
StateSkipConfig,
|
||||
};
|
||||
use futures::channel::mpsc::Receiver;
|
||||
use genesis::interop_genesis_state;
|
||||
@@ -42,14 +42,8 @@ pub const HARNESS_GENESIS_TIME: u64 = 1_567_552_690;
|
||||
// This parameter is required by a builder but not used because we use the `TestingSlotClock`.
|
||||
pub const HARNESS_SLOT_TIME: Duration = Duration::from_secs(1);
|
||||
|
||||
pub type BaseHarnessType<TEthSpec, THotStore, TColdStore> = Witness<
|
||||
TestingSlotClock,
|
||||
CachingEth1Backend<TEthSpec>,
|
||||
TEthSpec,
|
||||
NullEventHandler<TEthSpec>,
|
||||
THotStore,
|
||||
TColdStore,
|
||||
>;
|
||||
pub type BaseHarnessType<TEthSpec, THotStore, TColdStore> =
|
||||
Witness<TestingSlotClock, CachingEth1Backend<TEthSpec>, TEthSpec, THotStore, TColdStore>;
|
||||
|
||||
pub type DiskHarnessType<E> = BaseHarnessType<E, LevelDB<E>, LevelDB<E>>;
|
||||
pub type EphemeralHarnessType<E> = BaseHarnessType<E, MemoryStore<E>, MemoryStore<E>>;
|
||||
@@ -188,7 +182,7 @@ impl<E: EthSpec> BeaconChainHarness<EphemeralHarnessType<E>> {
|
||||
|
||||
let store = HotColdDB::open_ephemeral(store_config, spec.clone(), log.clone()).unwrap();
|
||||
let chain = BeaconChainBuilder::new(eth_spec_instance)
|
||||
.logger(log)
|
||||
.logger(log.clone())
|
||||
.custom_spec(spec.clone())
|
||||
.store(Arc::new(store))
|
||||
.store_migrator_config(MigratorConfig::default().blocking())
|
||||
@@ -200,11 +194,11 @@ impl<E: EthSpec> BeaconChainHarness<EphemeralHarnessType<E>> {
|
||||
.expect("should build state using recent genesis")
|
||||
.dummy_eth1_backend()
|
||||
.expect("should build dummy backend")
|
||||
.null_event_handler()
|
||||
.testing_slot_clock(HARNESS_SLOT_TIME)
|
||||
.expect("should configure testing slot clock")
|
||||
.shutdown_sender(shutdown_tx)
|
||||
.chain_config(chain_config)
|
||||
.event_handler(Some(ServerSentEventHandler::new_with_capacity(log, 1)))
|
||||
.build()
|
||||
.expect("should build");
|
||||
|
||||
@@ -246,7 +240,6 @@ impl<E: EthSpec> BeaconChainHarness<DiskHarnessType<E>> {
|
||||
.expect("should build state using recent genesis")
|
||||
.dummy_eth1_backend()
|
||||
.expect("should build dummy backend")
|
||||
.null_event_handler()
|
||||
.testing_slot_clock(HARNESS_SLOT_TIME)
|
||||
.expect("should configure testing slot clock")
|
||||
.shutdown_sender(shutdown_tx)
|
||||
@@ -288,7 +281,6 @@ impl<E: EthSpec> BeaconChainHarness<DiskHarnessType<E>> {
|
||||
.expect("should resume beacon chain from db")
|
||||
.dummy_eth1_backend()
|
||||
.expect("should build dummy backend")
|
||||
.null_event_handler()
|
||||
.testing_slot_clock(Duration::from_secs(1))
|
||||
.expect("should configure testing slot clock")
|
||||
.shutdown_sender(shutdown_tx)
|
||||
|
||||
Reference in New Issue
Block a user