Ethereum consensus client in Rust
Go to file
Paul Hauner be4e261e74 Use async code when interacting with EL (#3244)
## Overview

This rather extensive PR achieves two primary goals:

1. Uses the finalized/justified checkpoints of fork choice (FC), rather than that of the head state.
2. Refactors fork choice, block production and block processing to `async` functions.

Additionally, it achieves:

- Concurrent forkchoice updates to the EL and cache pruning after a new head is selected.
- Concurrent "block packing" (attestations, etc) and execution payload retrieval during block production.
- Concurrent per-block-processing and execution payload verification during block processing.
- The `Arc`-ification of `SignedBeaconBlock` during block processing (it's never mutated, so why not?):
    - I had to do this to deal with sending blocks into spawned tasks.
    - Previously we were cloning the beacon block at least 2 times during each block processing, these clones are either removed or turned into cheaper `Arc` clones.
    - We were also `Box`-ing and un-`Box`-ing beacon blocks as they moved throughout the networking crate. This is not a big deal, but it's nice to avoid shifting things between the stack and heap.
    - Avoids cloning *all the blocks* in *every chain segment* during sync.
    - It also has the potential to clean up our code where we need to pass an *owned* block around so we can send it back in the case of an error (I didn't do much of this, my PR is already big enough 😅)
- The `BeaconChain::HeadSafetyStatus` struct was removed. It was an old relic from prior merge specs.

For motivation for this change, see https://github.com/sigp/lighthouse/pull/3244#issuecomment-1160963273

## Changes to `canonical_head` and `fork_choice`

Previously, the `BeaconChain` had two separate fields:

```
canonical_head: RwLock<Snapshot>,
fork_choice: RwLock<BeaconForkChoice>
```

Now, we have grouped these values under a single struct:

```
canonical_head: CanonicalHead {
  cached_head: RwLock<Arc<Snapshot>>,
  fork_choice: RwLock<BeaconForkChoice>
} 
```

Apart from ergonomics, the only *actual* change here is wrapping the canonical head snapshot in an `Arc`. This means that we no longer need to hold the `cached_head` (`canonical_head`, in old terms) lock when we want to pull some values from it. This was done to avoid deadlock risks by preventing functions from acquiring (and holding) the `cached_head` and `fork_choice` locks simultaneously.

## Breaking Changes

### The `state` (root) field in the `finalized_checkpoint` SSE event

Consider the scenario where epoch `n` is just finalized, but `start_slot(n)` is skipped. There are two state roots we might in the `finalized_checkpoint` SSE event:

1. The state root of the finalized block, which is `get_block(finalized_checkpoint.root).state_root`.
4. The state root at slot of `start_slot(n)`, which would be the state from (1), but "skipped forward" through any skip slots.

Previously, Lighthouse would choose (2). However, we can see that when [Teku generates that event](de2b2801c8/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/events/EventSubscriptionManager.java (L171-L182)) it uses [`getStateRootFromBlockRoot`](de2b2801c8/data/provider/src/main/java/tech/pegasys/teku/api/ChainDataProvider.java (L336-L341)) which uses (1).

I have switched Lighthouse from (2) to (1). I think it's a somewhat arbitrary choice between the two, where (1) is easier to compute and is consistent with Teku.

## Notes for Reviewers

I've renamed `BeaconChain::fork_choice` to `BeaconChain::recompute_head`. Doing this helped ensure I broke all previous uses of fork choice and I also find it more descriptive. It describes an action and can't be confused with trying to get a reference to the `ForkChoice` struct.

I've changed the ordering of SSE events when a block is received. It used to be `[block, finalized, head]` and now it's `[block, head, finalized]`. It was easier this way and I don't think we were making any promises about SSE event ordering so it's not "breaking".

I've made it so fork choice will run when it's first constructed. I did this because I wanted to have a cached version of the last call to `get_head`. Ensuring `get_head` has been run *at least once* means that the cached values doesn't need to wrapped in an `Option`. This was fairly simple, it just involved passing a `slot` to the constructor so it knows *when* it's being run. When loading a fork choice from the store and a slot clock isn't handy I've just used the `slot` that was saved in the `fork_choice_store`. That seems like it would be a faithful representation of the slot when we saved it.

I added the `genesis_time: u64` to the `BeaconChain`. It's small, constant and nice to have around.

Since we're using FC for the fin/just checkpoints, we no longer get the `0x00..00` roots at genesis. You can see I had to remove a work-around in `ef-tests` here: b56be3bc2. I can't find any reason why this would be an issue, if anything I think it'll be better since the genesis-alias has caught us out a few times (0x00..00 isn't actually a real root). Edit: I did find a case where the `network` expected the 0x00..00 alias and patched it here: 3f26ac3e2.

You'll notice a lot of changes in tests. Generally, tests should be functionally equivalent. Here are the things creating the most diff-noise in tests:
- Changing tests to be `tokio::async` tests.
- Adding `.await` to fork choice, block processing and block production functions.
- Refactor of the `canonical_head` "API" provided by the `BeaconChain`. E.g., `chain.canonical_head.cached_head()` instead of `chain.canonical_head.read()`.
- Wrapping `SignedBeaconBlock` in an `Arc`.
- In the `beacon_chain/tests/block_verification`, we can't use the `lazy_static` `CHAIN_SEGMENT` variable anymore since it's generated with an async function. We just generate it in each test, not so efficient but hopefully insignificant.

I had to disable `rayon` concurrent tests in the `fork_choice` tests. This is because the use of `rayon` and `block_on` was causing a panic.

Co-authored-by: Mac L <mjladson@pm.me>
2022-07-03 05:36:50 +00:00
.github Lint against panicky calls in async functions (#3250) 2022-06-10 04:29:27 +00:00
account_manager Improve slashing import log (#3122) 2022-03-28 07:14:16 +00:00
beacon_node Use async code when interacting with EL (#3244) 2022-07-03 05:36:50 +00:00
book Register validator api (#3194) 2022-06-30 00:49:21 +00:00
boot_node Fix clippy lints for rust 1.62 (#3300) 2022-06-30 22:51:49 +00:00
common Use async code when interacting with EL (#3244) 2022-07-03 05:36:50 +00:00
consensus Use async code when interacting with EL (#3244) 2022-07-03 05:36:50 +00:00
crypto Update and consolidate dependencies (#3136) 2022-04-04 00:26:16 +00:00
database_manager Use async code when interacting with EL (#3244) 2022-07-03 05:36:50 +00:00
lcli Unify execution layer endpoints (#3214) 2022-06-29 09:07:09 +00:00
lighthouse Remove builder redundancy (#3294) 2022-07-01 01:15:19 +00:00
scripts Unify execution layer endpoints (#3214) 2022-06-29 09:07:09 +00:00
slasher Use async code when interacting with EL (#3244) 2022-07-03 05:36:50 +00:00
testing Use async code when interacting with EL (#3244) 2022-07-03 05:36:50 +00:00
validator_client Register validator api (#3194) 2022-06-30 00:49:21 +00:00
.dockerignore Exclude EE build dirs from Docker context (#3174) 2022-05-09 23:43:31 +00:00
.editorconfig Add editorconfig template 2019-03-11 15:09:57 +11:00
.gitignore Lint against panicky calls in async functions (#3250) 2022-06-10 04:29:27 +00:00
.gitmodules Replace EF tests submodule with a makefile 2019-09-08 04:19:54 +10:00
bors.toml Add minimum supported Rust version (#3082) 2022-03-17 03:33:29 +00:00
Cargo.lock Use async code when interacting with EL (#3244) 2022-07-03 05:36:50 +00:00
Cargo.toml Remove builder redundancy (#3294) 2022-07-01 01:15:19 +00:00
CONTRIBUTING.md Update CONTRIBUTING.md (#751) 2020-01-03 10:45:53 +11:00
Cross.toml Update Cross config for v0.2.2 (#3286) 2022-06-29 04:50:36 +00:00
Dockerfile Use a stable tag for ubuntu in dockerfile (#3231) 2022-05-31 06:09:12 +00:00
Dockerfile.cross Use a stable tag for ubuntu in dockerfile (#3231) 2022-05-31 06:09:12 +00:00
LICENSE Update License to Apache 2.0 2019-04-15 16:47:35 +10:00
Makefile Lint against panicky calls in async functions (#3250) 2022-06-10 04:29:27 +00:00
README.md Remove build status badge from README (#3195) 2022-05-20 05:02:14 +00:00
SECURITY.md Add how users should report security vulnerabilities for this repository (#2562) 2021-09-07 01:54:05 +00:00

Lighthouse: Ethereum consensus client

An open-source Ethereum consensus client, written in Rust and maintained by Sigma Prime.

Book Status Chat Badge

Documentation

Banner

Overview

Lighthouse is:

  • Ready for use on Ethereum consensus mainnet.
  • Fully open-source, licensed under Apache 2.0.
  • Security-focused. Fuzzing techniques have been continuously applied and several external security reviews have been performed.
  • Built in Rust, a modern language providing unique safety guarantees and excellent performance (comparable to C++).
  • Funded by various organisations, including Sigma Prime, the Ethereum Foundation, ConsenSys, the Decentralization Foundation and private individuals.
  • Actively involved in the specification and security analysis of the Ethereum proof-of-stake consensus specification.

Staking Deposit Contract

The Lighthouse team acknowledges 0x00000000219ab540356cBB839Cbe05303d7705Fa as the canonical staking deposit contract address.

Documentation

The Lighthouse Book contains information for users and developers.

The Lighthouse team maintains a blog at lighthouse-blog.sigmaprime.io which contains periodical progress updates, roadmap insights and interesting findings.

Branches

Lighthouse maintains two permanent branches:

  • stable: Always points to the latest stable release.
    • This is ideal for most users.
  • unstable: Used for development, contains the latest PRs.
    • Developers should base their PRs on this branch.

Contributing

Lighthouse welcomes contributors.

If you are looking to contribute, please head to the Contributing section of the Lighthouse book.

Contact

The best place for discussion is the Lighthouse Discord server.

Sign up to the Lighthouse Development Updates mailing list for email notifications about releases, network status and other important information.

Encrypt sensitive messages using our PGP key.

Donations

Lighthouse is an open-source project and a public good. Funding public goods is hard and we're grateful for the donations we receive from the community via:

  • Gitcoin Grants.
  • Ethereum address: 0x25c4a76E7d118705e7Ea2e9b7d8C59930d8aCD3b (donation.sigmaprime.eth).