mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-14 18:32:42 +00:00
Handle early blocks (#2155)
## Issue Addressed
NA
## Problem this PR addresses
There's an issue where Lighthouse is banning a lot of peers due to the following sequence of events:
1. Gossip block 0xabc arrives ~200ms early
- It is propagated across the network, with respect to [`MAXIMUM_GOSSIP_CLOCK_DISPARITY`](https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/p2p-interface.md#why-is-there-maximum_gossip_clock_disparity-when-validating-slot-ranges-of-messages-in-gossip-subnets).
- However, it is not imported to our database since the block is early.
2. Attestations for 0xabc arrive, but the block was not imported.
- The peer that sent the attestation is down-voted.
- Each unknown-block attestation causes a score loss of 1, the peer is banned at -100.
- When the peer is on an attestation subnet there can be hundreds of attestations, so the peer is banned quickly (before the missed block can be obtained via rpc).
## Potential solutions
I can think of three solutions to this:
1. Wait for attestation-queuing (#635) to arrive and solve this.
- Easy
- Not immediate fix.
- Whilst this would work, I don't think it's a perfect solution for this particular issue, rather (3) is better.
1. Allow importing blocks with a tolerance of `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
- Easy
- ~~I have implemented this, for now.~~
1. If a block is verified for gossip propagation (i.e., signature verified) and it's within `MAXIMUM_GOSSIP_CLOCK_DISPARITY`, then queue it to be processed at the start of the appropriate slot.
- More difficult
- Feels like the best solution, I will try to implement this.
**This PR takes approach (3).**
## Changes included
- Implement the `block_delay_queue`, based upon a [`DelayQueue`](https://docs.rs/tokio-util/0.6.3/tokio_util/time/delay_queue/struct.DelayQueue.html) which can store blocks until it's time to import them.
- Add a new `DelayedImportBlock` variant to the `beacon_processor::WorkEvent` enum to handle this new event.
- In the `BeaconProcessor`, refactor a `tokio::select!` to a struct with an explicit `Stream` implementation. I experienced some issues with `tokio::select!` in the block delay queue and I also found it hard to debug. I think this explicit implementation is nicer and functionally equivalent (apart from the fact that `tokio::select!` randomly chooses futures to poll, whereas now we're deterministic).
- Add a testing framework to the `beacon_processor` module that tests this new block delay logic. I also tested a handful of other operations in the beacon processor (attns, slashings, exits) since it was super easy to copy-pasta the code from the `http_api` tester.
- To implement these tests I added the concept of an optional `work_journal_tx` to the `BeaconProcessor` which will spit out a log of events. I used this in the tests to ensure that things were happening as I expect.
- The tests are a little racey, but it's hard to avoid that when testing timing-based code. If we see CI failures I can revise. I haven't observed *any* failures due to races on my machine or on CI yet.
- To assist with testing I allowed for directly setting the time on the `ManualSlotClock`.
- I gave the `beacon_processor::Worker` a `Toolbox` for two reasons; (a) it avoids changing tons of function sigs when you want to pass a new object to the worker and (b) it seemed cute.
This commit is contained in:
@@ -179,7 +179,7 @@ pub struct SyncManager<T: BeaconChainTypes> {
|
||||
single_block_lookups: FnvHashMap<RequestId, SingleBlockRequest>,
|
||||
|
||||
/// A multi-threaded, non-blocking processor for applying messages to the beacon chain.
|
||||
beacon_processor_send: mpsc::Sender<BeaconWorkEvent<T::EthSpec>>,
|
||||
beacon_processor_send: mpsc::Sender<BeaconWorkEvent<T>>,
|
||||
|
||||
/// The logger for the import manager.
|
||||
log: Logger,
|
||||
@@ -210,7 +210,7 @@ pub fn spawn<T: BeaconChainTypes>(
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
network_globals: Arc<NetworkGlobals<T::EthSpec>>,
|
||||
network_send: mpsc::UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
beacon_processor_send: mpsc::Sender<BeaconWorkEvent<T::EthSpec>>,
|
||||
beacon_processor_send: mpsc::Sender<BeaconWorkEvent<T>>,
|
||||
log: slog::Logger,
|
||||
) -> mpsc::UnboundedSender<SyncMessage<T::EthSpec>> {
|
||||
assert!(
|
||||
|
||||
@@ -96,7 +96,7 @@ pub struct SyncingChain<T: BeaconChainTypes> {
|
||||
validated_batches: u64,
|
||||
|
||||
/// A multi-threaded, non-blocking processor for applying messages to the beacon chain.
|
||||
beacon_processor_send: Sender<BeaconWorkEvent<T::EthSpec>>,
|
||||
beacon_processor_send: Sender<BeaconWorkEvent<T>>,
|
||||
|
||||
/// The chain's log.
|
||||
log: slog::Logger,
|
||||
@@ -123,7 +123,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
target_head_slot: Slot,
|
||||
target_head_root: Hash256,
|
||||
peer_id: PeerId,
|
||||
beacon_processor_send: Sender<BeaconWorkEvent<T::EthSpec>>,
|
||||
beacon_processor_send: Sender<BeaconWorkEvent<T>>,
|
||||
log: &slog::Logger,
|
||||
) -> Self {
|
||||
let mut peers = FnvHashMap::default();
|
||||
|
||||
@@ -194,7 +194,7 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
|
||||
network: &mut SyncNetworkContext<T::EthSpec>,
|
||||
local: &SyncInfo,
|
||||
awaiting_head_peers: &mut HashMap<PeerId, SyncInfo>,
|
||||
beacon_processor_send: &mpsc::Sender<BeaconWorkEvent<T::EthSpec>>,
|
||||
beacon_processor_send: &mpsc::Sender<BeaconWorkEvent<T>>,
|
||||
) {
|
||||
// Remove any outdated finalized/head chains
|
||||
self.purge_outdated_chains(local, awaiting_head_peers);
|
||||
@@ -328,7 +328,7 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
|
||||
local_epoch: Epoch,
|
||||
local_head_epoch: Epoch,
|
||||
awaiting_head_peers: &mut HashMap<PeerId, SyncInfo>,
|
||||
beacon_processor_send: &mpsc::Sender<BeaconWorkEvent<T::EthSpec>>,
|
||||
beacon_processor_send: &mpsc::Sender<BeaconWorkEvent<T>>,
|
||||
) {
|
||||
// Include the awaiting head peers
|
||||
for (peer_id, peer_sync_info) in awaiting_head_peers.drain() {
|
||||
@@ -463,7 +463,7 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
|
||||
target_head_slot: Slot,
|
||||
peer: PeerId,
|
||||
sync_type: RangeSyncType,
|
||||
beacon_processor_send: &mpsc::Sender<BeaconWorkEvent<T::EthSpec>>,
|
||||
beacon_processor_send: &mpsc::Sender<BeaconWorkEvent<T>>,
|
||||
network: &mut SyncNetworkContext<T::EthSpec>,
|
||||
) {
|
||||
let id = SyncingChain::<T>::id(&target_head_root, &target_head_slot);
|
||||
|
||||
@@ -68,7 +68,7 @@ pub struct RangeSync<T: BeaconChainTypes> {
|
||||
/// that need to be downloaded.
|
||||
chains: ChainCollection<T>,
|
||||
/// A multi-threaded, non-blocking processor for applying messages to the beacon chain.
|
||||
beacon_processor_send: mpsc::Sender<BeaconWorkEvent<T::EthSpec>>,
|
||||
beacon_processor_send: mpsc::Sender<BeaconWorkEvent<T>>,
|
||||
/// The syncing logger.
|
||||
log: slog::Logger,
|
||||
}
|
||||
@@ -76,7 +76,7 @@ pub struct RangeSync<T: BeaconChainTypes> {
|
||||
impl<T: BeaconChainTypes> RangeSync<T> {
|
||||
pub fn new(
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
beacon_processor_send: mpsc::Sender<BeaconWorkEvent<T::EthSpec>>,
|
||||
beacon_processor_send: mpsc::Sender<BeaconWorkEvent<T>>,
|
||||
log: slog::Logger,
|
||||
) -> Self {
|
||||
RangeSync {
|
||||
|
||||
Reference in New Issue
Block a user