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:
Paul Hauner
2021-02-24 03:08:52 +00:00
parent 399d073ab4
commit a764c3b247
19 changed files with 1161 additions and 115 deletions

53
Cargo.lock generated
View File

@@ -1607,6 +1607,38 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
[[package]]
name = "discv5"
version = "0.1.0-beta.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f52d2228d51e8f868a37d5b5b25b82c13552b635d5b47c3a5d53855a6fc4f0"
dependencies = [
"aes-ctr",
"aes-gcm 0.8.0",
"arrayvec",
"digest 0.9.0",
"enr",
"fnv",
"futures 0.3.12",
"hex",
"hkdf",
"k256",
"lazy_static",
"lru_time_cache",
"parking_lot",
"rand 0.7.3",
"rlp 0.5.0",
"sha2 0.9.2",
"smallvec",
"tokio 1.1.0",
"tokio-stream",
"tokio-util 0.6.3",
"tracing",
"tracing-subscriber",
"uint",
"zeroize",
]
[[package]]
name = "discv5"
version = "0.1.0-beta.3"
@@ -1775,9 +1807,9 @@ dependencies = [
[[package]]
name = "env_logger"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e"
checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f"
dependencies = [
"atty",
"humantime",
@@ -1966,7 +1998,7 @@ dependencies = [
"base64 0.13.0",
"directory",
"dirs 3.0.1",
"discv5",
"discv5 0.1.0-beta.3 (git+https://github.com/sigp/discv5?rev=02d2c896c66f8dc2b848c3996fedcd98e1dfec69)",
"error-chain",
"eth2_ssz",
"eth2_ssz_derive",
@@ -2797,7 +2829,7 @@ version = "0.1.0"
dependencies = [
"beacon_chain",
"bs58 0.4.0",
"discv5",
"discv5 0.1.0-beta.3 (git+https://github.com/sigp/discv5?rev=02d2c896c66f8dc2b848c3996fedcd98e1dfec69)",
"environment",
"eth1",
"eth2",
@@ -3657,7 +3689,7 @@ dependencies = [
"clap",
"clap_utils",
"directory",
"env_logger 0.8.2",
"env_logger 0.8.3",
"environment",
"eth2_network_config",
"futures 0.3.12",
@@ -4069,6 +4101,8 @@ name = "network"
version = "0.2.0"
dependencies = [
"beacon_chain",
"discv5 0.1.0-beta.3 (registry+https://github.com/rust-lang/crates.io-index)",
"environment",
"error-chain",
"eth2_libp2p",
"eth2_ssz",
@@ -4104,6 +4138,7 @@ dependencies = [
"tempfile",
"tokio 1.1.0",
"tokio-stream",
"tokio-util 0.6.3",
"tree_hash",
"types",
]
@@ -5651,7 +5686,7 @@ name = "simulator"
version = "0.2.0"
dependencies = [
"clap",
"env_logger 0.8.2",
"env_logger 0.8.3",
"eth1",
"eth1_test_rig",
"futures 0.3.12",
@@ -5942,7 +5977,7 @@ dependencies = [
"arbitrary",
"bls",
"criterion",
"env_logger 0.8.2",
"env_logger 0.8.3",
"eth2_hashing",
"eth2_ssz",
"eth2_ssz_types",
@@ -6509,9 +6544,9 @@ dependencies = [
[[package]]
name = "tokio-stream"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76066865172052eb8796c686f0b441a93df8b08d40a950b062ffb9a426f00edd"
checksum = "1981ad97df782ab506a1f43bf82c967326960d278acf3bf8279809648c3ff3ea"
dependencies = [
"futures-core",
"pin-project-lite 0.2.4",