├── .clippy.toml ├── .dockerignore ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug.yml │ ├── config.yml │ ├── docs.yml │ └── feature.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── book.yml │ ├── conventional-commits.yml │ ├── core-rust.yml │ ├── optional-rust.yml │ └── riscv.yml ├── .gitignore ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── Dockerfile.cross ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── bin └── ream │ ├── Cargo.toml │ ├── assets │ ├── keystore_dir │ │ ├── Pbkdf2TestKeystore.json │ │ └── ScryptDecryptionTest.json │ └── test_password.txt │ └── src │ ├── cli │ ├── beacon_node.rs │ ├── constants.rs │ ├── import_keystores.rs │ ├── mod.rs │ └── validator_node.rs │ ├── lib.rs │ └── main.rs ├── book.toml ├── book ├── Changelog.md ├── SUMMARY.md ├── cli │ ├── SUMMARY.md │ ├── cli.md │ ├── help.rs │ ├── ream.md │ ├── ream │ │ ├── beacon_node.md │ │ ├── node.md │ │ └── validator_node.md │ └── update.sh ├── installation.md └── intro.md ├── crates ├── common │ ├── beacon_api_types │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── block.rs │ │ │ ├── committee.rs │ │ │ ├── duties.rs │ │ │ ├── error.rs │ │ │ ├── id.rs │ │ │ ├── lib.rs │ │ │ ├── query.rs │ │ │ ├── request.rs │ │ │ ├── responses.rs │ │ │ ├── sync.rs │ │ │ └── validator.rs │ ├── beacon_chain │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── beacon_chain.rs │ │ │ └── lib.rs │ ├── checkpoint_sync │ │ ├── Cargo.toml │ │ ├── resources │ │ │ └── checkpoint_sync_sources │ │ │ │ ├── holesky.yaml │ │ │ │ ├── hoodi.yaml │ │ │ │ ├── mainnet.yaml │ │ │ │ └── sepolia.yaml │ │ └── src │ │ │ ├── checkpoint.rs │ │ │ ├── lib.rs │ │ │ └── weak_subjectivity.rs │ ├── consensus │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── attestation.rs │ │ │ ├── attestation_data.rs │ │ │ ├── attester_slashing.rs │ │ │ ├── beacon_block_header.rs │ │ │ ├── blob_sidecar.rs │ │ │ ├── bls_to_execution_change.rs │ │ │ ├── checkpoint.rs │ │ │ ├── consolidation_request.rs │ │ │ ├── constants.rs │ │ │ ├── deposit.rs │ │ │ ├── deposit_data.rs │ │ │ ├── deposit_message.rs │ │ │ ├── deposit_request.rs │ │ │ ├── electra │ │ │ ├── beacon_block.rs │ │ │ ├── beacon_block_body.rs │ │ │ ├── beacon_state.rs │ │ │ ├── blinded_beacon_block.rs │ │ │ ├── blinded_beacon_block_body.rs │ │ │ ├── execution_payload.rs │ │ │ ├── execution_payload_header.rs │ │ │ └── mod.rs │ │ │ ├── eth_1_block.rs │ │ │ ├── eth_1_data.rs │ │ │ ├── execution_engine │ │ │ ├── engine_trait.rs │ │ │ ├── mock_engine.rs │ │ │ ├── mod.rs │ │ │ ├── new_payload_request.rs │ │ │ └── rpc_types │ │ │ │ ├── get_blobs.rs │ │ │ │ ├── mod.rs │ │ │ │ └── transaction.rs │ │ │ ├── execution_requests.rs │ │ │ ├── fork.rs │ │ │ ├── fork_choice │ │ │ ├── latest_message.rs │ │ │ └── mod.rs │ │ │ ├── fork_data.rs │ │ │ ├── genesis.rs │ │ │ ├── helpers.rs │ │ │ ├── historical_batch.rs │ │ │ ├── historical_summary.rs │ │ │ ├── indexed_attestation.rs │ │ │ ├── lib.rs │ │ │ ├── misc.rs │ │ │ ├── pending_attestation.rs │ │ │ ├── pending_consolidation.rs │ │ │ ├── pending_deposit.rs │ │ │ ├── pending_partial_withdrawal.rs │ │ │ ├── polynomial_commitments │ │ │ ├── kzg_commitment.rs │ │ │ ├── kzg_proof.rs │ │ │ └── mod.rs │ │ │ ├── predicates.rs │ │ │ ├── proposer_slashing.rs │ │ │ ├── signing_data.rs │ │ │ ├── single_attestation.rs │ │ │ ├── sync_aggregate.rs │ │ │ ├── sync_committee.rs │ │ │ ├── validator.rs │ │ │ ├── voluntary_exit.rs │ │ │ ├── withdrawal.rs │ │ │ └── withdrawal_request.rs │ ├── execution_engine │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ ├── rpc_types │ │ │ ├── eth_syncing.rs │ │ │ ├── execution_payload.rs │ │ │ ├── forkchoice_update.rs │ │ │ ├── get_payload.rs │ │ │ ├── mod.rs │ │ │ └── payload_status.rs │ │ │ └── utils.rs │ ├── executor │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── fork_choice │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── constants.rs │ │ │ ├── handlers.rs │ │ │ ├── lib.rs │ │ │ └── store.rs │ ├── light_client │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── bootstrap.rs │ │ │ ├── finality_update.rs │ │ │ ├── header.rs │ │ │ ├── lib.rs │ │ │ └── optimistic_update.rs │ ├── network_spec │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── b32_hex.rs │ │ │ ├── cli.rs │ │ │ ├── fork_schedule.rs │ │ │ ├── lib.rs │ │ │ └── networks.rs │ ├── node │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src │ │ │ ├── lib.rs │ │ │ └── version.rs │ ├── polynomial_commitments │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── error.rs │ │ │ ├── handlers.rs │ │ │ ├── lib.rs │ │ │ ├── trusted_setup.rs │ │ │ └── trusted_setup.txt │ └── validator │ │ ├── Cargo.toml │ │ └── src │ │ ├── aggregate_and_proof.rs │ │ ├── attestation.rs │ │ ├── beacon_api_client │ │ ├── event.rs │ │ ├── http_client.rs │ │ └── mod.rs │ │ ├── blob_sidecars.rs │ │ ├── block.rs │ │ ├── constants.rs │ │ ├── contribution_and_proof.rs │ │ ├── execution_requests.rs │ │ ├── lib.rs │ │ ├── state.rs │ │ ├── sync_committee.rs │ │ └── validator.rs ├── crypto │ ├── bls │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ ├── constants.rs │ │ │ ├── errors.rs │ │ │ ├── lib.rs │ │ │ ├── private_key.rs │ │ │ ├── pubkey.rs │ │ │ ├── signature.rs │ │ │ ├── supranational │ │ │ ├── errors.rs │ │ │ ├── mod.rs │ │ │ ├── private_key.rs │ │ │ ├── pubkey.rs │ │ │ └── signature.rs │ │ │ ├── traits.rs │ │ │ └── zkcrypto │ │ │ ├── mod.rs │ │ │ ├── private_key.rs │ │ │ ├── pubkey.rs │ │ │ └── signature.rs │ ├── keystore │ │ ├── Cargo.toml │ │ ├── assets │ │ │ ├── Pbkdf2TestKeystore.json │ │ │ ├── ScryptDecryptionTest.json │ │ │ └── ScryptKeystore.json │ │ └── src │ │ │ ├── decrypt.rs │ │ │ ├── hex_serde.rs │ │ │ ├── hmac.rs │ │ │ ├── keystore.rs │ │ │ ├── lib.rs │ │ │ ├── pbkdf2.rs │ │ │ ├── salsa.rs │ │ │ └── scrypt.rs │ └── merkle │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ ├── hash.rs │ │ ├── index.rs │ │ ├── lib.rs │ │ └── multiproof.rs ├── networking │ ├── discv5 │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── config.rs │ │ │ ├── discovery.rs │ │ │ ├── eth2.rs │ │ │ ├── lib.rs │ │ │ └── subnet.rs │ ├── manager │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── config.rs │ │ │ ├── lib.rs │ │ │ └── service.rs │ ├── p2p │ │ ├── Cargo.toml │ │ ├── resources │ │ │ ├── bootnodes_holesky.yaml │ │ │ ├── bootnodes_hoodi.yaml │ │ │ ├── bootnodes_mainnet.yaml │ │ │ └── bootnodes_sepolia.yaml │ │ └── src │ │ │ ├── bootnodes.rs │ │ │ ├── channel.rs │ │ │ ├── config.rs │ │ │ ├── constants.rs │ │ │ ├── gossipsub │ │ │ ├── configurations.rs │ │ │ ├── error.rs │ │ │ ├── message.rs │ │ │ ├── mod.rs │ │ │ ├── snappy.rs │ │ │ └── topics.rs │ │ │ ├── lib.rs │ │ │ ├── network.rs │ │ │ ├── network_state.rs │ │ │ ├── peer.rs │ │ │ ├── req_resp │ │ │ ├── configurations.rs │ │ │ ├── error.rs │ │ │ ├── handler.rs │ │ │ ├── inbound_protocol.rs │ │ │ ├── messages │ │ │ │ ├── beacon_blocks.rs │ │ │ │ ├── blob_sidecars.rs │ │ │ │ ├── goodbye.rs │ │ │ │ ├── meta_data.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── ping.rs │ │ │ │ └── status.rs │ │ │ ├── mod.rs │ │ │ ├── outbound_protocol.rs │ │ │ └── protocol_id.rs │ │ │ └── utils.rs │ └── syncer │ │ ├── Cargo.toml │ │ └── src │ │ ├── block_range │ │ └── mod.rs │ │ └── lib.rs ├── rpc │ ├── Cargo.toml │ └── src │ │ ├── config.rs │ │ ├── handlers │ │ ├── blob_sidecar.rs │ │ ├── block.rs │ │ ├── committee.rs │ │ ├── config.rs │ │ ├── duties.rs │ │ ├── header.rs │ │ ├── light_client.rs │ │ ├── mod.rs │ │ ├── peers.rs │ │ ├── state.rs │ │ ├── validator.rs │ │ └── version.rs │ │ ├── lib.rs │ │ └── routes │ │ ├── beacon.rs │ │ ├── config.rs │ │ ├── debug.rs │ │ ├── mod.rs │ │ ├── node.rs │ │ └── validator.rs ├── runtime │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── lib.rs └── storage │ ├── Cargo.toml │ └── src │ ├── db.rs │ ├── dir.rs │ ├── errors.rs │ ├── lib.rs │ └── tables │ ├── beacon_block.rs │ ├── beacon_state.rs │ ├── blobs_and_proofs.rs │ ├── block_timeliness.rs │ ├── checkpoint_states.rs │ ├── equivocating_indices.rs │ ├── finalized_checkpoint.rs │ ├── genesis_time.rs │ ├── justified_checkpoint.rs │ ├── latest_messages.rs │ ├── mod.rs │ ├── parent_root_index.rs │ ├── proposer_boost_root.rs │ ├── slot_index.rs │ ├── state_root_index.rs │ ├── time.rs │ ├── unrealized_finalized_checkpoint.rs │ ├── unrealized_justifications.rs │ └── unrealized_justified_checkpoint.rs ├── rustfmt.toml └── testing ├── beacon-api ├── Cargo.toml └── tests │ ├── assets │ ├── block.json │ └── state.json │ └── serialization.rs └── ef-tests ├── Cargo.toml ├── Makefile ├── README.md ├── src ├── lib.rs ├── macros │ ├── epoch_processing.rs │ ├── fork_choice.rs │ ├── merkle_proof.rs │ ├── mod.rs │ ├── operations.rs │ ├── rewards.rs │ ├── sanity_blocks.rs │ ├── sanity_slots.rs │ ├── shuffling.rs │ └── ssz_static.rs └── utils.rs └── tests └── tests.rs /.clippy.toml: -------------------------------------------------------------------------------- 1 | # This feature is in Rust nightly, as of v1.62. The line is added in anticipation. 2 | # Once the feature stabilizes, remove the #[allow(clippy::unwrap_used)] lines in front of every `mod test` 3 | allow-unwrap-in-tests = true 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | testing/ef_tests/ 2 | target/ 3 | *.data 4 | *.tar.gz 5 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Context on this file: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners 2 | # The basic gist is that this file is used to automatically request reviews from the people listed in the file when a PR is opened. 3 | * @KolbyML 4 | crates/common/beacon_chain/ @KolbyML @Kayden-ML 5 | crates/common/checkpoint_sync/ @KolbyML @varun-doshi 6 | crates/common/consensus/ @KolbyML @Kayden-ML @varun-doshi 7 | crates/common/execution_engine/ @KolbyML @Kayden-ML 8 | crates/common/fork_choice/ @KolbyML @Kayden-ML 9 | crates/common/network_spec/ @KolbyML @Kayden-ML 10 | crates/common/polynomial_commitments/ @KolbyML @Kayden-ML 11 | crates/crypto/ @KolbyML @syjn99 12 | crates/rpc/ @KolbyML @varun-doshi 13 | testing/ @KolbyML @Kayden-ML @syjn99 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: GitHub Discussions 4 | url: https://github.com/ReamLabs/ream/discussions 5 | about: Please ask and answer questions here to keep the issue tracker clean. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/docs.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | description: Suggest a change to our documentation 3 | labels: ["C-docs", "S-needs-triage"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | If you are unsure if the docs are relevant or needed, please open up a discussion first. 9 | - type: textarea 10 | attributes: 11 | label: Describe the change 12 | description: | 13 | Please describe the documentation you want to change or add, and if it is for end-users or contributors. 14 | validations: 15 | required: true 16 | - type: textarea 17 | attributes: 18 | label: Additional context 19 | description: Add any other context to the feature (like screenshots, resources) 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest a feature 3 | labels: ["C-enhancement", "S-needs-triage"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Please ensure that the feature has not already been requested in the issue tracker. 9 | - type: textarea 10 | attributes: 11 | label: Describe the feature 12 | description: | 13 | Please describe the feature and what it is aiming to solve, if relevant. 14 | 15 | If the feature is for a crate, please include a proposed API surface. 16 | validations: 17 | required: true 18 | - type: textarea 19 | attributes: 20 | label: Additional context 21 | description: Add any other context to the feature (like screenshots, resources) 22 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### What are you trying to achieve? 2 | 3 | 4 | 5 | ### How was it implemented/fixed? 6 | 7 | 8 | 9 | ### To-Do 10 | 11 | 12 | - [ ] I have read [CONTRIBUTING.md](https://github.com/ReamLabs/ream/blob/master/CONTRIBUTING.md). 13 | - [ ] This PR uses [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). 14 | -------------------------------------------------------------------------------- /.github/workflows/conventional-commits.yml: -------------------------------------------------------------------------------- 1 | name: Conventional Commits 2 | 3 | on: 4 | pull_request: 5 | branches: [ "master" ] 6 | merge_group: 7 | 8 | jobs: 9 | conventional-commits: 10 | name: Conventional Commits 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | 15 | - uses: webiny/action-conventional-commits@v1.3.0 16 | -------------------------------------------------------------------------------- /.github/workflows/optional-rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | cargo-udeps: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Install cargo udeps 20 | run: cargo install cargo-udeps --locked 21 | 22 | - name: Run cargo udeps 23 | run: cargo +nightly udeps --workspace --tests --all-targets --release --exclude ef-tests 24 | -------------------------------------------------------------------------------- /.github/workflows/riscv.yml: -------------------------------------------------------------------------------- 1 | name: RISC-V 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | merge_group: 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | build-sp1: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout Repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Install toolchain 22 | run: | 23 | curl -L https://sp1up.succinct.xyz | bash 24 | /home/runner/.sp1/bin/sp1up 25 | rustup default succinct 26 | 27 | - name: Build SP1 28 | run: cargo build --target=riscv32im-succinct-zkvm-elf -p ream-consensus -p ream-bls 29 | 30 | build-risc0: 31 | runs-on: ubuntu-latest 32 | 33 | steps: 34 | - uses: actions/checkout@v4 35 | 36 | - name: Install toolchain 37 | run: | 38 | curl -L https://risczero.com/install | bash 39 | /home/runner/.risc0/bin/rzup install 40 | /home/runner/.risc0/bin/rzup install rust 1.85.0 41 | rustup default risc0 42 | 43 | - name: Build Risc0 44 | run: cargo build --target=riscv32im-risc0-zkvm-elf -p ream-consensus -p ream-bls 45 | 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | ./debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # Testdata 10 | testing/ef-tests/mainnet/* 11 | 12 | # MSVC Windows builds of rustc generate these, which store debugging information 13 | *.pdb 14 | 15 | # VSCode 16 | .vscode 17 | 18 | # RustRover 19 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 20 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 21 | # and can be added to the global gitignore or merged into this file. For a more nuclear 22 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 23 | .idea/ 24 | 25 | # redb files: prevent accidental commits of database files 26 | *.redb 27 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker.io/docker/dockerfile:1.7-labs 2 | 3 | FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef 4 | WORKDIR /app 5 | 6 | LABEL org.opencontainers.image.source=https://github.com/reamlabs/ream 7 | LABEL org.opencontainers.image.description="Ream is a modular, open-source Ethereum beam chain client." 8 | LABEL org.opencontainers.image.licenses="MIT" 9 | 10 | # Install system dependencies 11 | RUN apt-get update && apt-get -y upgrade && apt-get install -y libclang-dev pkg-config 12 | 13 | # Builds a cargo-chef plan 14 | FROM chef AS planner 15 | COPY --exclude=.git --exclude=dist . . 16 | RUN cargo chef prepare --recipe-path recipe.json 17 | 18 | FROM chef AS builder 19 | COPY --from=planner /app/recipe.json recipe.json 20 | 21 | # Build profile, release by default 22 | ARG BUILD_PROFILE=release 23 | ENV BUILD_PROFILE=$BUILD_PROFILE 24 | 25 | # Extra Cargo flags 26 | ARG RUSTFLAGS="" 27 | ENV RUSTFLAGS="$RUSTFLAGS" 28 | 29 | # Extra Cargo features 30 | ARG FEATURES="" 31 | ENV FEATURES=$FEATURES 32 | 33 | # Build dependencies 34 | RUN cargo chef cook --profile $BUILD_PROFILE --features "$FEATURES" --recipe-path recipe.json 35 | 36 | # Build application 37 | COPY --exclude=.git --exclude=dist . . 38 | RUN cargo build --profile $BUILD_PROFILE --features "$FEATURES" --locked --bin ream 39 | 40 | # ARG is not resolved in COPY so we have to hack around it by copying the 41 | # binary to a temporary location 42 | RUN cp /app/target/$BUILD_PROFILE/ream /app/ream 43 | 44 | # Use Ubuntu as the release image 45 | FROM ubuntu AS runtime 46 | WORKDIR /app 47 | 48 | # Copy ream over from the build stage 49 | COPY --from=builder /app/ream /usr/local/bin 50 | 51 | # Copy licenses 52 | COPY LICENSE ./ 53 | 54 | EXPOSE 8545 8546 55 | ENTRYPOINT ["/usr/local/bin/ream"] 56 | -------------------------------------------------------------------------------- /Dockerfile.cross: -------------------------------------------------------------------------------- 1 | # This image is meant to enable cross-architecture builds. 2 | # It assumes the ream binary has already been 3 | # compiled for `$TARGETPLATFORM` and moved to `./dist/bin/$TARGETARCH`. 4 | FROM ubuntu:24.04 5 | 6 | LABEL org.opencontainers.image.source=https://github.com/reamlabs/ream 7 | LABEL org.opencontainers.image.description="Ream is a modular, open-source Ethereum Beam Chain client." 8 | LABEL org.opencontainers.image.licenses="MIT" 9 | 10 | # Filled by docker buildx 11 | ARG TARGETARCH 12 | 13 | COPY ./dist/bin/$TARGETARCH/ream /usr/local/bin/ream 14 | 15 | EXPOSE 8545 8546 16 | ENTRYPOINT ["/usr/local/bin/ream"] 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ream Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Heavily inspired by Reth: https://github.com/paradigmxyz/reth/blob/4c39b98b621c53524c6533a9c7b52fc42c25abd6/Makefile 2 | .DEFAULT_GOAL := help 3 | 4 | # Cargo features for builds. 5 | FEATURES ?= 6 | 7 | # Cargo profile for builds. 8 | PROFILE ?= release 9 | 10 | # Extra flags for Cargo. 11 | CARGO_INSTALL_EXTRA_FLAGS ?= 12 | 13 | CARGO_TARGET_DIR ?= target 14 | 15 | ##@ Help 16 | .PHONY: help 17 | help: # Display this help. 18 | @awk 'BEGIN {FS = ":.*#"; printf "Usage:\n make \033[34m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?#/ { printf " \033[34m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST) 19 | 20 | ##@ Build 21 | .PHONY: build 22 | build: # Build the Ream binary into `target` directory. 23 | cargo build --bin ream --features "$(FEATURES)" --profile "$(PROFILE)" 24 | 25 | .PHONY: install 26 | install: # Build and install the Ream binary under `~/.cargo/bin`. 27 | cargo install --path bin/ream --force --locked \ 28 | --features "$(FEATURES)" \ 29 | --profile "$(PROFILE)" \ 30 | $(CARGO_INSTALL_EXTRA_FLAGS) 31 | 32 | ##@ Others 33 | .PHONY: clean 34 | clean: # Run `cargo clean`. 35 | cargo clean 36 | 37 | .PHONY: lint 38 | lint: # Run `clippy` and `rustfmt`. 39 | cargo +nightly fmt --all 40 | cargo clippy --all --all-targets --features "$(FEATURES)" --no-deps -- --deny warnings 41 | 42 | # clippy for bls with supranational feature 43 | cargo clippy --package ream-bls --all-targets --features "supranational" --no-deps -- --deny warnings 44 | 45 | # cargo sort 46 | cargo sort --grouped --workspace 47 | 48 | .PHONY: build-debug 49 | build-debug: ## Build the ream binary into `target/debug` directory. 50 | cargo build --bin ream --features "$(FEATURES)" 51 | 52 | .PHONY: update-book-cli 53 | update-book-cli: build-debug ## Update book cli documentation. 54 | @echo "Updating book cli doc..." 55 | @./book/cli/update.sh $(CARGO_TARGET_DIR)/debug/ream 56 | 57 | .PHONY: test 58 | test: # Run all tests. 59 | cargo test --workspace -- --nocapture 60 | 61 | clean-deps: 62 | cargo +nightly udeps --workspace --tests --all-targets --release --exclude ef-tests 63 | 64 | pr: 65 | make lint && \ 66 | make update-book-cli && \ 67 | make test 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ream 2 | 3 | [![Telegram](https://img.shields.io/badge/Telegram-2CA5E0?style=for-the-badge&logo=telegram&logoColor=white)][tg-url] 4 | 5 | 6 | ## What is Ream 7 | 8 | Ream aims to be modular, contributor-friendly, and blazingly fast implementation of the Beam Chain specification. 9 | 10 | One of our goals is to build an ecosystem where developers can seamlessly build on Ream, saving time and avoiding the need to reinvent the wheel. 11 | 12 | ## What is the Beam Chain 13 | 14 | The Beam Chain is the next generation of Ethereum Consensus, incorporating all of the greatest and latest ideas from the Ethereum research roadmap. Its goal is to transition quickly and safely from the Beacon Chain to a consensus layer design much closer to the final design of Ethereum. 15 | 16 | [Video of the announcement of the Beam Chain with more information](https://youtu.be/lRqnFrqpq4k?si=YODLo0MBgkVMblmE) 17 | 18 | ## What are our goals? 19 | 20 | Building the first Beam Chain client which is 21 | - Modular 22 | - Contributor Friendly 23 | - Blazingly Fast 24 | - Extendible 25 | 26 | ## Getting Help 27 | 28 | - Join the [Telegram][tg-url] to get help, or 29 | - Open an issue with [a bug](https://github.com/ReamLabs/ream/issues/new) 30 | 31 | [tg-url]: https://t.me/ReamLabs 32 | 33 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Report a Vulnerability 4 | 5 | Contact [security@reamlabs.com](mailto:security@reamlabs.com). 6 | -------------------------------------------------------------------------------- /bin/ream/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream" 3 | description = "A Rust implementation of the Ethereum Beam Chain specification." 4 | authors.workspace = true 5 | edition.workspace = true 6 | keywords.workspace = true 7 | license.workspace = true 8 | readme.workspace = true 9 | repository.workspace = true 10 | rust-version.workspace = true 11 | version.workspace = true 12 | 13 | [[bin]] 14 | name = "ream" 15 | path = "src/main.rs" 16 | 17 | [dependencies] 18 | alloy-primitives.workspace = true 19 | anyhow.workspace = true 20 | clap = { workspace = true, features = ["derive", "env"] } 21 | discv5.workspace = true 22 | hashbrown.workspace = true 23 | tokio.workspace = true 24 | tracing = { workspace = true, features = ["log"] } 25 | tracing-subscriber = { workspace = true, features = ["env-filter"] } 26 | unicode-normalization.workspace = true 27 | url.workspace = true 28 | 29 | # ream dependencies 30 | ream-checkpoint-sync.workspace = true 31 | ream-consensus.workspace = true 32 | ream-discv5.workspace = true 33 | ream-executor.workspace = true 34 | ream-keystore.workspace = true 35 | ream-manager.workspace = true 36 | ream-network-spec.workspace = true 37 | ream-node.workspace = true 38 | ream-p2p.workspace = true 39 | ream-rpc.workspace = true 40 | ream-storage.workspace = true 41 | ream-validator.workspace = true 42 | -------------------------------------------------------------------------------- /bin/ream/assets/keystore_dir/Pbkdf2TestKeystore.json: -------------------------------------------------------------------------------- 1 | {"crypto":{"kdf":{"function":"pbkdf2","params":{"dklen":32,"c":262144,"prf":"hmac-sha256","salt":"d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"},"message":""},"checksum":{"function":"sha256","params":{},"message":"8a9f5d9912ed7e75ea794bc5a89bca5f193721d30868ade6f73043c6ea6febf1"},"cipher":{"function":"aes-128-ctr","params":{"iv":"264daa3f303d7259501c93d997d84fe6"},"message":"cee03fde2af33149775b7223e7845e4fb2c8ae1792e5f99fe9ecf474cc8c16ad"}},"description":"This is a test keystore that uses PBKDF2 to secure the secret.","pubkey":"9612d7a727c9d0a22e185a1c768478dfe919cada9266988cb32359c11f2b7b27f4ae4040902382ae2910c15e2b420d07","path":"m/12381/60/0/0","uuid":"64625def-3331-4eea-ab6f-782f3ed16a83","version":4} 2 | -------------------------------------------------------------------------------- /bin/ream/assets/keystore_dir/ScryptDecryptionTest.json: -------------------------------------------------------------------------------- 1 | {"crypto":{"kdf":{"function":"scrypt","params":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"},"message":""},"checksum":{"function":"sha256","params":{},"message":"d2217fe5f3e9a1e34581ef8a78f7c9928e436d36dacc5e846690a5581e8ea484"},"cipher":{"function":"aes-128-ctr","params":{"iv":"264daa3f303d7259501c93d997d84fe6"},"message":"06ae90d55fe0a6e9c5c3bc5b170827b2e5cce3929ed3f116c2811e6366dfe20f"}},"description":"This is a test keystore that uses scrypt to secure the secret.","pubkey":"9612d7a727c9d0a22e185a1c768478dfe919cada9266988cb32359c11f2b7b27f4ae4040902382ae2910c15e2b420d07","path":"m/12381/60/3141592653/589793238","uuid":"1d85ae20-35c5-4611-98e8-aa14a633906f","version":4} 2 | -------------------------------------------------------------------------------- /bin/ream/assets/test_password.txt: -------------------------------------------------------------------------------- 1 | 𝔱𝔢𝔰𝔱𝔭𝔞𝔰𝔰𝔴𝔬𝔯𝔡🔑 -------------------------------------------------------------------------------- /bin/ream/src/cli/constants.rs: -------------------------------------------------------------------------------- 1 | use std::net::{IpAddr, Ipv4Addr}; 2 | 3 | pub const DEFAULT_BEACON_API_ENDPOINT: &str = "http://localhost:5052"; 4 | pub const DEFAULT_DISABLE_DISCOVERY: bool = false; 5 | pub const DEFAULT_DISCOVERY_PORT: u16 = 9000; 6 | pub const DEFAULT_HTTP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); 7 | pub const DEFAULT_HTTP_ALLOW_ORIGIN: bool = false; 8 | pub const DEFAULT_HTTP_PORT: u16 = 5052; 9 | pub const DEFAULT_KEY_MANAGER_HTTP_PORT: u16 = 8008; 10 | pub const DEFAULT_NETWORK: &str = "mainnet"; 11 | pub const DEFAULT_REQUEST_TIMEOUT: &str = "60"; 12 | pub const DEFAULT_SOCKET_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED); 13 | pub const DEFAULT_SOCKET_PORT: u16 = 9000; 14 | -------------------------------------------------------------------------------- /bin/ream/src/cli/import_keystores.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::{read_dir, read_to_string}, 3 | path::PathBuf, 4 | }; 5 | 6 | use anyhow::anyhow; 7 | use ream_keystore::keystore::EncryptedKeystore; 8 | use unicode_normalization::UnicodeNormalization; 9 | 10 | pub fn load_password_file(path: &PathBuf) -> anyhow::Result { 11 | let contents = 12 | read_to_string(path).map_err(|err| anyhow!("Unable to load password file: {err:?}"))?; 13 | Ok(contents.trim_end_matches(&['\n', '\r'][..]).to_string()) 14 | } 15 | 16 | pub fn load_keystore_directory(config: &PathBuf) -> anyhow::Result> { 17 | Ok(read_dir(config) 18 | .map_err(|err| anyhow!("Failed to read directory {}: {err:?}", config.display()))? 19 | .filter_map(|entry| { 20 | let path = entry.ok()?.path(); 21 | if path.is_file() 22 | && path.extension().and_then(|extension| extension.to_str()) == Some("json") 23 | { 24 | Some(EncryptedKeystore::load_from_file(path).ok()?) 25 | } else { 26 | None 27 | } 28 | }) 29 | .collect::>()) 30 | } 31 | 32 | pub fn process_password(password: String) -> String { 33 | password 34 | .nfkd() 35 | .filter(|&character_unprocessed| { 36 | let character = character_unprocessed as u32; 37 | !((character == 0x7F) || (character <= 0x1F) || (0x80..=0x9F).contains(&character)) 38 | }) 39 | .collect() 40 | } 41 | 42 | #[cfg(test)] 43 | mod tests { 44 | use alloy_primitives::hex; 45 | 46 | use super::*; 47 | 48 | #[test] 49 | fn test_process_password() { 50 | let original = "𝔱𝔢𝔰𝔱𝔭𝔞𝔰𝔰𝔴𝔬𝔯𝔡🔑".to_string(); 51 | let processed = process_password(original); 52 | 53 | let expected = hex!("0x7465737470617373776f7264f09f9491"); 54 | assert_eq!(expected, processed.into_bytes().as_slice()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /bin/ream/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod cli; 2 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Ream Contributors"] 3 | language = "en" 4 | multilingual = false 5 | src = "book" 6 | title = "ream Book" 7 | 8 | [output.html] 9 | theme = "book/theme" 10 | git-repository-url = "https://github.com/ReamLabs/ream" 11 | default-theme = "ayu" 12 | no-section-label = true 13 | 14 | [output.html.fold] 15 | enable = true 16 | level = 1 17 | 18 | [build] 19 | build-dir = "target/book" 20 | 21 | [preprocessor.template] 22 | before = ["links"] 23 | 24 | [preprocessor.index] 25 | 26 | [preprocessor.links] 27 | -------------------------------------------------------------------------------- /book/Changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | To view all Changes made to the project, run this in your terminal: 4 | ```sh 5 | git log --pretty="-%s" 6 | ``` 7 | 8 | To add all Changes in a `Changelog.md` file, run this in your terminal: 9 | ```sh 10 | git log --pretty="- %s" > CHANGELOG.md 11 | ``` -------------------------------------------------------------------------------- /book/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Introduction](./intro.md) 4 | - [Installation](./installation.md) 5 | - [CLI Reference](./cli/cli.md) 6 | - [`ream`](./cli/ream.md) 7 | - [`ream beacon_node`](./cli/ream/beacon_node.md) 8 | - [`ream validator_node`](./cli/ream/validator_node.md) 9 | - [Changelog](./Changelog.md) 10 | 11 | -------------------------------------------------------------------------------- /book/cli/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [`ream`](./ream.md) 2 | - [`ream beacon_node`](./ream/beacon_node.md) 3 | - [`ream validator_node`](./ream/validator_node.md) 4 | 5 | -------------------------------------------------------------------------------- /book/cli/cli.md: -------------------------------------------------------------------------------- 1 | # CLI Reference 2 | 3 | The Ream node is operated via the CLI by running the `ream node` command. To stop it, press `ctrl-c`. You may need to wait a bit as Ream tears down existing p2p connections or other cleanup tasks. 4 | 5 | However, Ream has more commands: 6 | 7 | {{#include ./SUMMARY.md}} 8 | -------------------------------------------------------------------------------- /book/cli/ream.md: -------------------------------------------------------------------------------- 1 | # ream 2 | 3 | A Rust implementation of the Ethereum Beam Chain specification. 4 | 5 | ```bash 6 | $ ream --help 7 | ``` 8 | ```txt 9 | Usage: ream 10 | 11 | Commands: 12 | beacon_node Start the node 13 | validator_node 14 | help Print this message or the help of the given subcommand(s) 15 | 16 | Options: 17 | -h, --help Print help 18 | -V, --version Print version 19 | ``` 20 | -------------------------------------------------------------------------------- /book/cli/ream/node.md: -------------------------------------------------------------------------------- 1 | # ream node 2 | 3 | Start the node 4 | 5 | ```bash 6 | $ ream node --help 7 | ``` 8 | ```txt 9 | Usage: ream node [OPTIONS] 10 | 11 | Options: 12 | -v, --verbosity 13 | Verbosity level [default: 3] 14 | --network 15 | Choose mainnet, holesky, sepolia, hoodi, dev or provide a path to a YAML config file [default: mainnet] 16 | --http-address 17 | Set HTTP address [default: 127.0.0.1] 18 | --http-port 19 | Set HTTP Port [default: 5052] 20 | --http-allow-origin 21 | 22 | --socket-address 23 | Set P2P socket address [default: 0.0.0.0] 24 | --socket-port 25 | Set P2P socket port (TCP) [default: 9000] 26 | --discovery-port 27 | Discovery 5 listening port (UDP) [default: 9000] 28 | --disable-discovery 29 | Disable Discv5 30 | --data-dir 31 | The directory for storing application data. If used together with --ephemeral, new child directory will be created. 32 | -e, --ephemeral 33 | Use new data directory, located in OS temporary directory. If used together with --data-dir, new directory will be created there instead. 34 | --bootnodes 35 | One or more comma-delimited base64-encoded ENR's of peers to initially connect to. Use 'default' to use the default bootnodes for the network. Use 'none' to disable bootnodes. [default: default] 36 | --checkpoint-sync-url 37 | Trusted RPC URL to initiate Checkpoint Sync. 38 | --purge-db 39 | Purges the database. 40 | --execution-endpoint 41 | The URL of the execution endpoint. This is used to send requests to the engine api. 42 | --execution-jwt-secret 43 | The JWT secret used to authenticate with the execution endpoint. This is used to send requests to the engine api. 44 | -h, --help 45 | Print help 46 | ``` 47 | -------------------------------------------------------------------------------- /book/cli/ream/validator_node.md: -------------------------------------------------------------------------------- 1 | # ream validator_node 2 | 3 | 4 | 5 | ```bash 6 | $ ream validator_node --help 7 | ``` 8 | ```txt 9 | Usage: ream validator_node [OPTIONS] --import-keystores --suggested-fee-recipient 10 | 11 | Options: 12 | -v, --verbosity 13 | Verbosity level [default: 3] 14 | --beacon-api-endpoint 15 | Set HTTP url of the beacon api endpoint [default: http://localhost:5052] 16 | --request-timeout 17 | Set HTTP request timeout for beacon api calls [default: 60] 18 | --key-manager-http-address 19 | Set HTTP address of the key manager server [default: 127.0.0.1] 20 | --key-manager-http-port 21 | Set HTTP Port of the key manager server [default: 8008] 22 | --network 23 | Choose mainnet, holesky, sepolia, hoodi, dev or provide a path to a YAML config file [default: mainnet] 24 | --import-keystores 25 | The directory for importing keystores 26 | --suggested-fee-recipient 27 | The suggested fee recipient address where staking rewards would go to 28 | --password-file 29 | The plaintext password file to use for keystores 30 | --password 31 | The password to use for keystores. It's recommended to use password-file over this in order to prevent your keystore password from appearing in the shell history 32 | -h, --help 33 | Print help 34 | ``` 35 | -------------------------------------------------------------------------------- /book/cli/update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | BOOK_ROOT="$(dirname "$(dirname "$0")")" 5 | REAM=${1:-"$(dirname "$BOOK_ROOT")/target/debug/ream"} 6 | 7 | cmd=( 8 | "$(dirname "$0")/help.rs" 9 | --root-dir "$BOOK_ROOT/" 10 | --root-indentation 2 11 | --root-summary 12 | --out-dir "$BOOK_ROOT/cli/" 13 | "$REAM" 14 | ) 15 | echo "Running: $" "${cmd[*]}" 16 | "${cmd[@]}" 17 | -------------------------------------------------------------------------------- /book/installation.md: -------------------------------------------------------------------------------- 1 | # Build from Source 2 | 3 | You can build Ream on Linux. 4 | 5 | ## Dependencies 6 | 7 | First install Rust using rustup: 8 | 9 | ```bash 10 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 11 | ``` 12 | 13 | There are some other dependencies you need to install based on your operating system (OS): 14 | 15 | - **Ubuntu/Debian**: `apt-get install libclang-dev pkg-config libssl-dev build-essential` 16 | 17 | Install cargo-sort and cargo-udeps tools. 18 | 19 | ```bash 20 | cargo install cargo-udeps --locked 21 | cargo install --git https://github.com/DevinR528/cargo-sort.git --rev 25a60ad860ce7cd0055abf4b69c18285cb07ab41 cargo-sort 22 | ``` 23 | 24 | ## Build Ream 25 | 26 | Clone the repository and move to the directory: 27 | 28 | ```bash 29 | git clone git@github.com:reamlabs/ream.git 30 | cd ream 31 | ``` 32 | 33 | After everything is setup, you can start the build: 34 | 35 | ```bash 36 | make build 37 | ``` -------------------------------------------------------------------------------- /book/intro.md: -------------------------------------------------------------------------------- 1 | # Ream 2 | 3 | ## What is Ream 4 | 5 | Ream aims to be modular, contributor-friendly, and blazingly fast implementation of the Beam Chain specification. 6 | One of our goals is to build an ecosystem where developers can seamlessly build on Ream, saving time and avoiding the need to reinvent the wheel. 7 | 8 | ## Join 9 | 10 | [![Telegram](https://img.shields.io/badge/Telegram-2CA5E0?style=for-the-badge&logo=telegram&logoColor=white)][tg-url] 11 | 12 | [tg-url]: https://t.me/ReamLabs -------------------------------------------------------------------------------- /crates/common/beacon_api_types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-beacon-api-types" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | actix-web.workspace = true 14 | alloy-primitives.workspace = true 15 | anyhow.workspace = true 16 | ethereum_serde_utils.workspace = true 17 | ethereum_ssz.workspace = true 18 | ethereum_ssz_derive.workspace = true 19 | reqwest.workspace = true 20 | serde.workspace = true 21 | serde_json.workspace = true 22 | thiserror.workspace = true 23 | url.workspace = true 24 | 25 | # ream dependencies 26 | ream-bls.workspace = true 27 | ream-consensus.workspace = true 28 | -------------------------------------------------------------------------------- /crates/common/beacon_api_types/src/block.rs: -------------------------------------------------------------------------------- 1 | use ream_consensus::{ 2 | electra::{beacon_block::BeaconBlock, blinded_beacon_block::BlindedBeaconBlock}, 3 | execution_engine::rpc_types::get_blobs::Blob, 4 | polynomial_commitments::kzg_proof::KZGProof, 5 | }; 6 | use serde::{Deserialize, Serialize}; 7 | use ssz_derive::{Decode, Encode}; 8 | 9 | #[derive(Debug, Clone, Serialize, Deserialize)] 10 | pub struct ProduceBlockResponse { 11 | pub version: String, 12 | pub execution_payload_blinded: bool, 13 | #[serde(with = "serde_utils::quoted_u64")] 14 | pub execution_payload_value: u64, 15 | #[serde(with = "serde_utils::quoted_u64")] 16 | pub consensus_block_value: u64, 17 | pub data: ProduceBlockData, 18 | } 19 | 20 | #[derive(Debug, Clone, Serialize, Deserialize)] 21 | #[serde(untagged)] 22 | pub enum ProduceBlockData { 23 | Full(FullBlockData), 24 | Blinded(BlindedBeaconBlock), 25 | } 26 | 27 | #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] 28 | pub struct FullBlockData { 29 | pub block: BeaconBlock, 30 | pub kzg_proofs: Vec, 31 | pub blobs: Vec, 32 | } 33 | -------------------------------------------------------------------------------- /crates/common/beacon_api_types/src/committee.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Deserialize, Serialize)] 4 | pub struct BeaconCommitteeSubscription { 5 | #[serde(with = "serde_utils::quoted_u64")] 6 | pub validator_index: u64, 7 | #[serde(with = "serde_utils::quoted_u64")] 8 | pub committee_index: u64, 9 | #[serde(with = "serde_utils::quoted_u64")] 10 | pub committees_at_slot: u64, 11 | #[serde(with = "serde_utils::quoted_u64")] 12 | pub slot: u64, 13 | pub is_aggregator: bool, 14 | } 15 | -------------------------------------------------------------------------------- /crates/common/beacon_api_types/src/duties.rs: -------------------------------------------------------------------------------- 1 | use ream_bls::PubKey; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | 5 | #[derive(Debug, Deserialize, Serialize, Encode, Decode)] 6 | pub struct ProposerDuty { 7 | pub pubkey: PubKey, 8 | #[serde(with = "serde_utils::quoted_u64")] 9 | pub validator_index: u64, 10 | #[serde(with = "serde_utils::quoted_u64")] 11 | pub slot: u64, 12 | } 13 | 14 | #[derive(Debug, Deserialize, Serialize, Encode, Decode)] 15 | pub struct AttesterDuty { 16 | pub pubkey: PubKey, 17 | #[serde(with = "serde_utils::quoted_u64")] 18 | pub validator_index: u64, 19 | #[serde(with = "serde_utils::quoted_u64")] 20 | pub committee_index: u64, 21 | #[serde(with = "serde_utils::quoted_u64")] 22 | pub committees_at_slot: u64, 23 | #[serde(with = "serde_utils::quoted_u64")] 24 | pub validator_committee_index: u64, 25 | #[serde(with = "serde_utils::quoted_u64")] 26 | pub slot: u64, 27 | } 28 | 29 | #[derive(Debug, Deserialize, Serialize, Encode, Decode)] 30 | pub struct SyncCommitteeDuty { 31 | pub pubkey: PubKey, 32 | #[serde(with = "serde_utils::quoted_u64")] 33 | pub validator_index: u64, 34 | pub validator_sync_committee_indices: Vec, 35 | } 36 | -------------------------------------------------------------------------------- /crates/common/beacon_api_types/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod block; 2 | pub mod committee; 3 | pub mod duties; 4 | pub mod error; 5 | pub mod id; 6 | pub mod query; 7 | pub mod request; 8 | pub mod responses; 9 | pub mod sync; 10 | pub mod validator; 11 | -------------------------------------------------------------------------------- /crates/common/beacon_api_types/src/query.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use super::id::ValidatorID; 5 | use crate::validator::ValidatorStatus; 6 | 7 | #[derive(Debug, Serialize, Deserialize)] 8 | pub struct EpochQuery { 9 | pub epoch: Option, 10 | } 11 | 12 | #[derive(Debug, Deserialize)] 13 | pub struct SlotQuery { 14 | pub slot: Option, 15 | } 16 | 17 | #[derive(Debug, Deserialize)] 18 | pub struct IndexQuery { 19 | pub index: Option, 20 | } 21 | 22 | #[derive(Debug, Deserialize)] 23 | pub struct RootQuery { 24 | pub root: Option, 25 | } 26 | 27 | #[derive(Debug, Deserialize)] 28 | pub struct ParentRootQuery { 29 | pub parent_root: Option, 30 | } 31 | 32 | #[derive(Default, Debug, Deserialize)] 33 | pub struct IdQuery { 34 | pub id: Option>, 35 | } 36 | 37 | #[derive(Default, Debug, Deserialize)] 38 | pub struct BlobSidecarQuery { 39 | pub indices: Option>, 40 | } 41 | 42 | #[derive(Default, Debug, Deserialize)] 43 | pub struct StatusQuery { 44 | pub status: Option>, 45 | } 46 | 47 | impl StatusQuery { 48 | pub fn has_status(&self) -> bool { 49 | match &self.status { 50 | Some(statuses) => !statuses.is_empty(), 51 | None => false, 52 | } 53 | } 54 | 55 | pub fn contains_status(&self, status: &ValidatorStatus) -> bool { 56 | match &self.status { 57 | Some(statuses) => statuses.contains(status), 58 | None => true, // If no statuses specified, accept all 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /crates/common/beacon_api_types/src/request.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{id::ValidatorID, validator::ValidatorStatus}; 4 | 5 | #[derive(Debug, Deserialize, Serialize)] 6 | pub struct ValidatorsPostRequest { 7 | pub ids: Option>, 8 | pub statuses: Option>, 9 | } 10 | -------------------------------------------------------------------------------- /crates/common/beacon_api_types/src/sync.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use ssz_derive::{Decode, Encode}; 3 | 4 | #[derive(Debug, Deserialize, Serialize, Encode, Decode)] 5 | pub struct SyncStatus { 6 | #[serde(with = "serde_utils::quoted_u64")] 7 | pub head_slot: u64, 8 | #[serde(with = "serde_utils::quoted_u64")] 9 | pub sync_distance: u64, 10 | pub is_syncing: bool, 11 | pub is_optimistic: bool, 12 | pub el_offline: bool, 13 | } 14 | -------------------------------------------------------------------------------- /crates/common/beacon_api_types/src/validator.rs: -------------------------------------------------------------------------------- 1 | use ream_consensus::validator::Validator; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] 5 | #[serde(rename_all = "snake_case")] 6 | pub enum ValidatorStatus { 7 | Pending, 8 | PendingInitialized, 9 | PendingQueued, 10 | Active, 11 | ActiveOngoing, 12 | ActiveExiting, 13 | ActiveSlashed, 14 | Exited, 15 | ExitedUnslashed, 16 | ExitedSlashed, 17 | Withdrawal, 18 | WithdrawalPossible, 19 | WithdrawalDone, 20 | Offline, 21 | } 22 | 23 | #[derive(Debug, Serialize, Deserialize, Clone)] 24 | pub struct ValidatorData { 25 | #[serde(with = "serde_utils::quoted_u64")] 26 | pub index: u64, 27 | #[serde(with = "serde_utils::quoted_u64")] 28 | pub balance: u64, 29 | pub status: ValidatorStatus, 30 | pub validator: Validator, 31 | } 32 | 33 | impl ValidatorData { 34 | pub fn new(index: u64, balance: u64, status: ValidatorStatus, validator: Validator) -> Self { 35 | Self { 36 | index, 37 | balance, 38 | status, 39 | validator, 40 | } 41 | } 42 | } 43 | 44 | #[derive(Debug, Serialize)] 45 | pub struct ValidatorBalance { 46 | #[serde(with = "serde_utils::quoted_u64")] 47 | pub index: u64, 48 | #[serde(with = "serde_utils::quoted_u64")] 49 | pub balance: u64, 50 | } 51 | -------------------------------------------------------------------------------- /crates/common/beacon_chain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-beacon-chain" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | anyhow.workspace = true 14 | tokio.workspace = true 15 | 16 | # ream dependencies 17 | ream-consensus.workspace = true 18 | ream-execution-engine.workspace = true 19 | ream-fork-choice.workspace = true 20 | ream-storage.workspace = true 21 | -------------------------------------------------------------------------------- /crates/common/beacon_chain/src/beacon_chain.rs: -------------------------------------------------------------------------------- 1 | use ream_consensus::{ 2 | attestation::Attestation, attester_slashing::AttesterSlashing, 3 | electra::beacon_block::SignedBeaconBlock, 4 | }; 5 | use ream_execution_engine::ExecutionEngine; 6 | use ream_fork_choice::{ 7 | handlers::{on_attestation, on_attester_slashing, on_block, on_tick}, 8 | store::Store, 9 | }; 10 | use ream_storage::db::ReamDB; 11 | use tokio::sync::Mutex; 12 | 13 | /// BeaconChain is the main struct which manages the nodes local beacon chain. 14 | pub struct BeaconChain { 15 | pub store: Mutex, 16 | pub execution_engine: Option, 17 | } 18 | 19 | impl BeaconChain { 20 | /// Creates a new instance of `BeaconChain`. 21 | pub fn new(db: ReamDB, execution_engine: Option) -> Self { 22 | Self { 23 | store: Mutex::new(Store::new(db)), 24 | execution_engine, 25 | } 26 | } 27 | 28 | pub async fn process_block(&self, signed_block: SignedBeaconBlock) -> anyhow::Result<()> { 29 | let mut store = self.store.lock().await; 30 | on_block(&mut store, &signed_block, &self.execution_engine).await?; 31 | Ok(()) 32 | } 33 | 34 | pub async fn process_attester_slashing( 35 | &self, 36 | attester_slashing: AttesterSlashing, 37 | ) -> anyhow::Result<()> { 38 | let mut store = self.store.lock().await; 39 | on_attester_slashing(&mut store, attester_slashing)?; 40 | Ok(()) 41 | } 42 | 43 | pub async fn process_attestation( 44 | &self, 45 | attestation: Attestation, 46 | is_from_block: bool, 47 | ) -> anyhow::Result<()> { 48 | let mut store = self.store.lock().await; 49 | on_attestation(&mut store, attestation, is_from_block)?; 50 | Ok(()) 51 | } 52 | 53 | pub async fn process_tick(&self, time: u64) -> anyhow::Result<()> { 54 | let mut store = self.store.lock().await; 55 | on_tick(&mut store, time)?; 56 | Ok(()) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /crates/common/beacon_chain/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod beacon_chain; 2 | -------------------------------------------------------------------------------- /crates/common/checkpoint_sync/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-checkpoint-sync" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | alloy-primitives.workspace = true 14 | anyhow.workspace = true 15 | ethereum_ssz.workspace = true 16 | ethereum_ssz_derive.workspace = true 17 | reqwest.workspace = true 18 | serde.workspace = true 19 | serde_json.workspace = true 20 | serde_yaml.workspace = true 21 | tracing.workspace = true 22 | 23 | # ream dependencies 24 | ream-consensus.workspace = true 25 | ream-fork-choice.workspace = true 26 | ream-network-spec.workspace = true 27 | ream-storage.workspace = true 28 | -------------------------------------------------------------------------------- /crates/common/checkpoint_sync/resources/checkpoint_sync_sources/holesky.yaml: -------------------------------------------------------------------------------- 1 | - https://holesky.beaconstate.info 2 | - https://checkpoint-sync.holesky.ethpandaops.io 3 | -------------------------------------------------------------------------------- /crates/common/checkpoint_sync/resources/checkpoint_sync_sources/hoodi.yaml: -------------------------------------------------------------------------------- 1 | - https://hoodi.beaconstate.info 2 | - https://hoodi.beaconstate.ethstaker.cc 3 | -------------------------------------------------------------------------------- /crates/common/checkpoint_sync/resources/checkpoint_sync_sources/mainnet.yaml: -------------------------------------------------------------------------------- 1 | - http://testing.mainnet.beacon-api.nimbus.team 2 | - https://beaconstate.ethstaker.cc 3 | - https://sync-mainnet.beaconcha.in 4 | - https://mainnet.checkpoint.sigp.io 5 | -------------------------------------------------------------------------------- /crates/common/checkpoint_sync/resources/checkpoint_sync_sources/sepolia.yaml: -------------------------------------------------------------------------------- 1 | - https://beaconstate-sepolia.chainsafe.io 2 | - https://checkpoint-sync.sepolia.ethpandaops.io 3 | - https://sepolia.beaconstate.info 4 | -------------------------------------------------------------------------------- /crates/common/checkpoint_sync/src/checkpoint.rs: -------------------------------------------------------------------------------- 1 | use ream_network_spec::networks::{Network, network_spec}; 2 | use reqwest::Url; 3 | 4 | pub fn get_checkpoint_sync_sources(checkpoint_sync_url: Option) -> Vec { 5 | if let Some(checkpoint_sync_url) = checkpoint_sync_url { 6 | return vec![checkpoint_sync_url]; 7 | } 8 | let raw_urls: Vec = match network_spec().network { 9 | Network::Mainnet => serde_yaml::from_str(include_str!( 10 | "../resources/checkpoint_sync_sources/mainnet.yaml" 11 | )) 12 | .expect("should deserialize checkpoint sync sources"), 13 | Network::Holesky => serde_yaml::from_str(include_str!( 14 | "../resources/checkpoint_sync_sources/holesky.yaml" 15 | )) 16 | .expect("should deserialize checkpoint sync sources"), 17 | Network::Sepolia => serde_yaml::from_str(include_str!( 18 | "../resources/checkpoint_sync_sources/sepolia.yaml" 19 | )) 20 | .expect("should deserialize checkpoint sync sources"), 21 | Network::Hoodi => serde_yaml::from_str(include_str!( 22 | "../resources/checkpoint_sync_sources/hoodi.yaml" 23 | )) 24 | .expect("should deserialize checkpoint sync sources"), 25 | Network::Dev | Network::Custom(_) => vec![], 26 | }; 27 | 28 | raw_urls 29 | .into_iter() 30 | .map(|raw_url| Url::parse(&raw_url).expect("invalid URL in checkpoint sync YAML")) 31 | .collect() 32 | } 33 | -------------------------------------------------------------------------------- /crates/common/consensus/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-consensus" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | alloy-consensus.workspace = true 14 | alloy-primitives.workspace = true 15 | alloy-rlp.workspace = true 16 | anyhow.workspace = true 17 | async-trait.workspace = true 18 | ethereum_hashing.workspace = true 19 | ethereum_serde_utils.workspace = true 20 | ethereum_ssz.workspace = true 21 | ethereum_ssz_derive.workspace = true 22 | itertools.workspace = true 23 | serde.workspace = true 24 | serde_json.workspace = true 25 | serde_yaml.workspace = true 26 | sha2.workspace = true 27 | ssz_types.workspace = true 28 | thiserror.workspace = true 29 | tree_hash.workspace = true 30 | tree_hash_derive.workspace = true 31 | 32 | # ream dependencies 33 | ream-bls.workspace = true 34 | ream-merkle.workspace = true 35 | -------------------------------------------------------------------------------- /crates/common/consensus/src/attestation.rs: -------------------------------------------------------------------------------- 1 | use ream_bls::BLSSignature; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use ssz_types::{ 5 | BitList, BitVector, 6 | typenum::{U64, U131072}, 7 | }; 8 | use tree_hash_derive::TreeHash; 9 | 10 | use crate::attestation_data::AttestationData; 11 | 12 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 13 | pub struct Attestation { 14 | pub aggregation_bits: BitList, 15 | pub data: AttestationData, 16 | pub signature: BLSSignature, 17 | pub committee_bits: BitVector, 18 | } 19 | -------------------------------------------------------------------------------- /crates/common/consensus/src/attestation_data.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | use crate::checkpoint::Checkpoint; 7 | 8 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 9 | pub struct AttestationData { 10 | #[serde(with = "serde_utils::quoted_u64")] 11 | pub slot: u64, 12 | #[serde(with = "serde_utils::quoted_u64")] 13 | pub index: u64, 14 | 15 | /// LMD GHOST vote 16 | pub beacon_block_root: B256, 17 | 18 | /// FFG vote 19 | pub source: Checkpoint, 20 | pub target: Checkpoint, 21 | } 22 | -------------------------------------------------------------------------------- /crates/common/consensus/src/attester_slashing.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use ssz_derive::{Decode, Encode}; 3 | use tree_hash_derive::TreeHash; 4 | 5 | use crate::indexed_attestation::IndexedAttestation; 6 | 7 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 8 | pub struct AttesterSlashing { 9 | pub attestation_1: IndexedAttestation, 10 | pub attestation_2: IndexedAttestation, 11 | } 12 | -------------------------------------------------------------------------------- /crates/common/consensus/src/beacon_block_header.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use ream_bls::BLSSignature; 3 | use serde::{Deserialize, Serialize}; 4 | use ssz_derive::{Decode, Encode}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 8 | pub struct SignedBeaconBlockHeader { 9 | pub message: BeaconBlockHeader, 10 | pub signature: BLSSignature, 11 | } 12 | 13 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 14 | pub struct BeaconBlockHeader { 15 | #[serde(with = "serde_utils::quoted_u64")] 16 | pub slot: u64, 17 | #[serde(with = "serde_utils::quoted_u64")] 18 | pub proposer_index: u64, 19 | pub parent_root: B256, 20 | pub state_root: B256, 21 | pub body_root: B256, 22 | } 23 | -------------------------------------------------------------------------------- /crates/common/consensus/src/blob_sidecar.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use ssz_types::{FixedVector, typenum::U17}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | use crate::{ 8 | beacon_block_header::SignedBeaconBlockHeader, 9 | execution_engine::rpc_types::get_blobs::Blob, 10 | polynomial_commitments::{kzg_commitment::KZGCommitment, kzg_proof::KZGProof}, 11 | }; 12 | 13 | #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Encode, Decode, TreeHash)] 14 | pub struct BlobSidecar { 15 | #[serde(with = "serde_utils::quoted_u64")] 16 | pub index: u64, 17 | pub blob: Blob, 18 | pub kzg_commitment: KZGCommitment, 19 | pub kzg_proof: KZGProof, 20 | pub signed_block_header: SignedBeaconBlockHeader, 21 | pub kzg_commitment_inclusion_proof: FixedVector, 22 | } 23 | 24 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Encode, Decode, Ord, PartialOrd)] 25 | pub struct BlobIdentifier { 26 | pub block_root: B256, 27 | pub index: u64, 28 | } 29 | 30 | impl BlobIdentifier { 31 | pub fn new(block_root: B256, index: u64) -> Self { 32 | Self { block_root, index } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /crates/common/consensus/src/bls_to_execution_change.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::Address; 2 | use ream_bls::{BLSSignature, PubKey}; 3 | use serde::{Deserialize, Serialize}; 4 | use ssz_derive::{Decode, Encode}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | use crate::misc::checksummed_address; 8 | 9 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 10 | pub struct SignedBLSToExecutionChange { 11 | pub message: BLSToExecutionChange, 12 | pub signature: BLSSignature, 13 | } 14 | 15 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 16 | pub struct BLSToExecutionChange { 17 | #[serde(with = "serde_utils::quoted_u64")] 18 | pub validator_index: u64, 19 | pub from_bls_pubkey: PubKey, 20 | #[serde(with = "checksummed_address")] 21 | pub to_execution_address: Address, 22 | } 23 | -------------------------------------------------------------------------------- /crates/common/consensus/src/checkpoint.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use alloy_primitives::B256; 4 | use serde::{Deserialize, Serialize}; 5 | use ssz_derive::{Decode, Encode}; 6 | use tree_hash_derive::TreeHash; 7 | 8 | #[derive( 9 | Debug, 10 | Eq, 11 | Hash, 12 | PartialEq, 13 | Clone, 14 | Copy, 15 | Serialize, 16 | Deserialize, 17 | Encode, 18 | Decode, 19 | TreeHash, 20 | PartialOrd, 21 | Ord, 22 | Default, 23 | )] 24 | pub struct Checkpoint { 25 | #[serde(with = "serde_utils::quoted_u64")] 26 | pub epoch: u64, 27 | pub root: B256, 28 | } 29 | 30 | impl FromStr for Checkpoint { 31 | type Err = CheckpointParseError; 32 | 33 | fn from_str(s: &str) -> Result { 34 | let (root_str, epoch_str) = s 35 | .split_once(':') 36 | .ok_or(CheckpointParseError::InvalidFormat)?; 37 | 38 | let root = root_str 39 | .strip_prefix("0x") 40 | .ok_or(CheckpointParseError::MissingHexPrefix) 41 | .and_then(|hex| B256::from_str(hex).map_err(|_| CheckpointParseError::InvalidHex))?; 42 | 43 | let epoch = epoch_str 44 | .parse::() 45 | .map_err(|_| CheckpointParseError::InvalidEpoch)?; 46 | 47 | Ok(Self { epoch, root }) 48 | } 49 | } 50 | 51 | #[derive(thiserror::Error, Debug)] 52 | pub enum CheckpointParseError { 53 | #[error("Expected format: 0x:")] 54 | InvalidFormat, 55 | #[error("Missing '0x' prefix on block_root")] 56 | MissingHexPrefix, 57 | #[error("Invalid hex block_root (expected 32 bytes)")] 58 | InvalidHex, 59 | #[error("Epoch must be a valid u64 integer")] 60 | InvalidEpoch, 61 | } 62 | -------------------------------------------------------------------------------- /crates/common/consensus/src/consolidation_request.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::Address; 2 | use ream_bls::PubKey; 3 | use serde::{Deserialize, Serialize}; 4 | use ssz_derive::{Decode, Encode}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | use crate::misc::checksummed_address; 8 | 9 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 10 | pub struct ConsolidationRequest { 11 | #[serde(with = "checksummed_address")] 12 | pub source_address: Address, 13 | pub source_pubkey: PubKey, 14 | pub target_pubkey: PubKey, 15 | } 16 | -------------------------------------------------------------------------------- /crates/common/consensus/src/deposit.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use ssz_types::{FixedVector, typenum::U33}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | use crate::deposit_data::DepositData; 8 | 9 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 10 | pub struct Deposit { 11 | pub proof: FixedVector, 12 | pub data: DepositData, 13 | } 14 | -------------------------------------------------------------------------------- /crates/common/consensus/src/deposit_data.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use ream_bls::{BLSSignature, PubKey}; 3 | use serde::{Deserialize, Serialize}; 4 | use ssz_derive::{Decode, Encode}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 8 | pub struct DepositData { 9 | pub pubkey: PubKey, 10 | pub withdrawal_credentials: B256, 11 | #[serde(with = "serde_utils::quoted_u64")] 12 | pub amount: u64, 13 | 14 | /// BLS aggregate signature 15 | pub signature: BLSSignature, 16 | } 17 | -------------------------------------------------------------------------------- /crates/common/consensus/src/deposit_message.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use ream_bls::PubKey; 3 | use serde::{Deserialize, Serialize}; 4 | use ssz_derive::{Decode, Encode}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 8 | pub struct DepositMessage { 9 | pub pubkey: PubKey, 10 | pub withdrawal_credentials: B256, 11 | pub amount: u64, 12 | } 13 | -------------------------------------------------------------------------------- /crates/common/consensus/src/deposit_request.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use ream_bls::{BLSSignature, PubKey}; 3 | use serde::{Deserialize, Serialize}; 4 | use ssz_derive::{Decode, Encode}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 8 | pub struct DepositRequest { 9 | pub pubkey: PubKey, 10 | pub withdrawal_credentials: B256, 11 | #[serde(with = "serde_utils::quoted_u64")] 12 | pub amount: u64, 13 | pub signature: BLSSignature, 14 | #[serde(with = "serde_utils::quoted_u64")] 15 | pub index: u64, 16 | } 17 | -------------------------------------------------------------------------------- /crates/common/consensus/src/electra/blinded_beacon_block.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use tree_hash::TreeHash; 5 | use tree_hash_derive::TreeHash; 6 | 7 | use crate::electra::blinded_beacon_block_body::BlindedBeaconBlockBody; 8 | 9 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 10 | pub struct BlindedBeaconBlock { 11 | #[serde(with = "serde_utils::quoted_u64")] 12 | pub slot: u64, 13 | #[serde(with = "serde_utils::quoted_u64")] 14 | pub proposer_index: u64, 15 | pub parent_root: B256, 16 | pub state_root: B256, 17 | pub body: BlindedBeaconBlockBody, 18 | } 19 | 20 | impl BlindedBeaconBlock { 21 | pub fn block_root(&self) -> B256 { 22 | self.tree_hash_root() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /crates/common/consensus/src/electra/blinded_beacon_block_body.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use ream_bls::BLSSignature; 3 | use serde::{Deserialize, Serialize}; 4 | use ssz_derive::{Decode, Encode}; 5 | use ssz_types::{ 6 | VariableList, 7 | typenum::{U1, U8, U16, U4096}, 8 | }; 9 | use tree_hash_derive::TreeHash; 10 | 11 | use crate::{ 12 | attestation::Attestation, attester_slashing::AttesterSlashing, 13 | bls_to_execution_change::SignedBLSToExecutionChange, deposit::Deposit, 14 | electra::execution_payload_header::ExecutionPayloadHeader, eth_1_data::Eth1Data, 15 | execution_requests::ExecutionRequests, polynomial_commitments::kzg_commitment::KZGCommitment, 16 | proposer_slashing::ProposerSlashing, sync_aggregate::SyncAggregate, 17 | voluntary_exit::SignedVoluntaryExit, 18 | }; 19 | 20 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 21 | pub struct BlindedBeaconBlockBody { 22 | pub randao_reveal: BLSSignature, 23 | 24 | /// Eth1 data vote 25 | pub eth1_data: Eth1Data, 26 | 27 | /// Arbitrary data 28 | pub graffiti: B256, 29 | 30 | // Operations 31 | pub proposer_slashings: VariableList, 32 | pub attester_slashings: VariableList, 33 | pub attestations: VariableList, 34 | pub deposits: VariableList, 35 | pub voluntary_exits: VariableList, 36 | pub sync_aggregate: SyncAggregate, 37 | 38 | // Execution 39 | pub execution_payload_header: ExecutionPayloadHeader, 40 | pub bls_to_execution_changes: VariableList, 41 | pub blob_kzg_commitments: VariableList, 42 | pub execution_requests: ExecutionRequests, 43 | } 44 | -------------------------------------------------------------------------------- /crates/common/consensus/src/electra/execution_payload_header.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::{Address, B256, U256}; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use ssz_types::{ 5 | FixedVector, VariableList, 6 | serde_utils::{hex_fixed_vec, hex_var_list}, 7 | typenum::{self, U32}, 8 | }; 9 | use tree_hash_derive::TreeHash; 10 | 11 | use crate::misc::checksummed_address; 12 | 13 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 14 | pub struct ExecutionPayloadHeader { 15 | // Execution block header fields 16 | pub parent_hash: B256, 17 | #[serde(with = "checksummed_address")] 18 | pub fee_recipient: Address, 19 | pub state_root: B256, 20 | pub receipts_root: B256, 21 | #[serde(with = "hex_fixed_vec")] 22 | pub logs_bloom: FixedVector, 23 | pub prev_randao: B256, 24 | #[serde(with = "serde_utils::quoted_u64")] 25 | pub block_number: u64, 26 | #[serde(with = "serde_utils::quoted_u64")] 27 | pub gas_limit: u64, 28 | #[serde(with = "serde_utils::quoted_u64")] 29 | pub gas_used: u64, 30 | #[serde(with = "serde_utils::quoted_u64")] 31 | pub timestamp: u64, 32 | #[serde(with = "hex_var_list")] 33 | pub extra_data: VariableList, 34 | #[serde(with = "serde_utils::quoted_u256")] 35 | pub base_fee_per_gas: U256, 36 | 37 | // Extra payload fields 38 | pub block_hash: B256, 39 | pub transactions_root: B256, 40 | pub withdrawals_root: B256, 41 | #[serde(with = "serde_utils::quoted_u64")] 42 | pub blob_gas_used: u64, 43 | #[serde(with = "serde_utils::quoted_u64")] 44 | pub excess_blob_gas: u64, 45 | } 46 | -------------------------------------------------------------------------------- /crates/common/consensus/src/electra/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod beacon_block; 2 | pub mod beacon_block_body; 3 | pub mod beacon_state; 4 | pub mod blinded_beacon_block; 5 | pub mod blinded_beacon_block_body; 6 | pub mod execution_payload; 7 | pub mod execution_payload_header; 8 | -------------------------------------------------------------------------------- /crates/common/consensus/src/eth_1_block.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | 5 | use crate::{ 6 | constants::{ETH1_FOLLOW_DISTANCE, SECONDS_PER_ETH1_BLOCK}, 7 | eth_1_data::Eth1Data, 8 | }; 9 | 10 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode)] 11 | pub struct Eth1Block { 12 | pub number: u64, 13 | pub timestamp: u64, 14 | pub deposit_root: B256, 15 | #[serde(with = "serde_utils::quoted_u64")] 16 | pub deposit_count: u64, 17 | pub block_hash: B256, 18 | } 19 | 20 | impl Eth1Block { 21 | pub fn is_candidate_block(&self, period_start: u64) -> bool { 22 | self.timestamp + SECONDS_PER_ETH1_BLOCK * ETH1_FOLLOW_DISTANCE <= period_start 23 | && self.timestamp + SECONDS_PER_ETH1_BLOCK * ETH1_FOLLOW_DISTANCE * 2 >= period_start 24 | } 25 | 26 | pub fn eth1_data(&self) -> Eth1Data { 27 | Eth1Data { 28 | deposit_root: self.deposit_root, 29 | deposit_count: self.deposit_count, 30 | block_hash: self.block_hash, 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /crates/common/consensus/src/eth_1_data.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, Hash)] 7 | pub struct Eth1Data { 8 | pub deposit_root: B256, 9 | #[serde(with = "serde_utils::quoted_u64")] 10 | pub deposit_count: u64, 11 | pub block_hash: B256, 12 | } 13 | -------------------------------------------------------------------------------- /crates/common/consensus/src/execution_engine/engine_trait.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use async_trait::async_trait; 3 | 4 | use super::{new_payload_request::NewPayloadRequest, rpc_types::get_blobs::BlobAndProofV1}; 5 | 6 | #[async_trait] 7 | pub trait ExecutionApi { 8 | /// Return ``True`` if and only if ``new_payload_request`` is valid with respect to 9 | /// ``self.execution_state``. 10 | async fn verify_and_notify_new_payload( 11 | &self, 12 | new_payload_request: NewPayloadRequest, 13 | ) -> anyhow::Result; 14 | 15 | async fn engine_get_blobs_v1( 16 | &self, 17 | blob_version_hashes: Vec, 18 | ) -> anyhow::Result>>; 19 | } 20 | -------------------------------------------------------------------------------- /crates/common/consensus/src/execution_engine/mock_engine.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use alloy_primitives::B256; 4 | use anyhow::Ok; 5 | use async_trait::async_trait; 6 | use serde::Deserialize; 7 | 8 | use super::{ 9 | engine_trait::ExecutionApi, new_payload_request::NewPayloadRequest, 10 | rpc_types::get_blobs::BlobAndProofV1, 11 | }; 12 | 13 | #[derive(Deserialize, Debug, Default)] 14 | pub struct MockExecutionEngine { 15 | execution_valid: bool, 16 | } 17 | 18 | impl MockExecutionEngine { 19 | pub fn new() -> Self { 20 | Self { 21 | execution_valid: true, 22 | } 23 | } 24 | 25 | pub fn from_file(execution_yaml_path: &Path) -> anyhow::Result { 26 | let file = std::fs::File::open(execution_yaml_path)?; 27 | Ok(serde_yaml::from_reader(file)?) 28 | } 29 | 30 | pub fn set_payload_status(&mut self, payload_status: bool) { 31 | self.execution_valid = payload_status; 32 | } 33 | } 34 | 35 | #[async_trait] 36 | impl ExecutionApi for MockExecutionEngine { 37 | async fn verify_and_notify_new_payload( 38 | &self, 39 | _new_payload_request: NewPayloadRequest, 40 | ) -> anyhow::Result { 41 | Ok(self.execution_valid) 42 | } 43 | 44 | async fn engine_get_blobs_v1( 45 | &self, 46 | blob_version_hashes: Vec, 47 | ) -> anyhow::Result>> { 48 | Ok(blob_version_hashes.into_iter().map(|_| None).collect()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /crates/common/consensus/src/execution_engine/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod engine_trait; 2 | pub mod mock_engine; 3 | pub mod new_payload_request; 4 | pub mod rpc_types; 5 | -------------------------------------------------------------------------------- /crates/common/consensus/src/execution_engine/new_payload_request.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | 5 | use crate::{electra::execution_payload::ExecutionPayload, execution_requests::ExecutionRequests}; 6 | 7 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode)] 8 | pub struct NewPayloadRequest { 9 | pub execution_payload: ExecutionPayload, 10 | pub versioned_hashes: Vec, 11 | pub parent_beacon_block_root: B256, 12 | pub execution_requests: ExecutionRequests, 13 | } 14 | -------------------------------------------------------------------------------- /crates/common/consensus/src/execution_engine/rpc_types/get_blobs.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use ssz_derive::{Decode, Encode}; 3 | use ssz_types::{FixedVector, serde_utils::hex_fixed_vec, typenum::U131072}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | use crate::{constants::BYTES_PER_BLOB, polynomial_commitments::kzg_proof::KZGProof}; 7 | 8 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Decode, Encode, TreeHash)] 9 | #[serde(transparent)] 10 | pub struct Blob { 11 | #[serde(with = "hex_fixed_vec")] 12 | pub inner: FixedVector, 13 | } 14 | 15 | impl Blob { 16 | pub fn to_fixed_bytes(&self) -> [u8; BYTES_PER_BLOB] { 17 | let mut fixed_array = [0u8; BYTES_PER_BLOB]; 18 | fixed_array.copy_from_slice(&self.inner); 19 | fixed_array 20 | } 21 | } 22 | 23 | #[derive(Deserialize, Debug, Clone, PartialEq, Decode, Encode)] 24 | #[serde(rename_all = "camelCase")] 25 | pub struct BlobAndProofV1 { 26 | pub blob: Blob, 27 | pub proof: KZGProof, 28 | } 29 | -------------------------------------------------------------------------------- /crates/common/consensus/src/execution_engine/rpc_types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod get_blobs; 2 | pub mod transaction; 3 | -------------------------------------------------------------------------------- /crates/common/consensus/src/execution_requests.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use ssz_derive::{Decode, Encode}; 3 | use ssz_types::{ 4 | VariableList, 5 | typenum::{U2, U16, U8192}, 6 | }; 7 | use tree_hash_derive::TreeHash; 8 | 9 | use crate::{ 10 | consolidation_request::ConsolidationRequest, deposit_request::DepositRequest, 11 | withdrawal_request::WithdrawalRequest, 12 | }; 13 | 14 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 15 | pub struct ExecutionRequests { 16 | pub deposits: VariableList, 17 | pub withdrawals: VariableList, 18 | pub consolidations: VariableList, 19 | } 20 | -------------------------------------------------------------------------------- /crates/common/consensus/src/fork.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::aliases::B32; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Encode, Decode, TreeHash, Eq)] 7 | pub struct Fork { 8 | pub previous_version: B32, 9 | pub current_version: B32, 10 | #[serde(with = "serde_utils::quoted_u64")] 11 | pub epoch: u64, 12 | } 13 | 14 | impl Fork { 15 | pub const UNSCHEDULED_EPOCH: u64 = u64::MAX; 16 | } 17 | -------------------------------------------------------------------------------- /crates/common/consensus/src/fork_choice/latest_message.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 7 | pub struct LatestMessage { 8 | pub epoch: u64, 9 | pub root: B256, 10 | } 11 | -------------------------------------------------------------------------------- /crates/common/consensus/src/fork_choice/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod latest_message; 2 | -------------------------------------------------------------------------------- /crates/common/consensus/src/fork_data.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::{B256, aliases::B32}; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use tree_hash::TreeHash; 5 | use tree_hash_derive::TreeHash; 6 | 7 | #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Encode, Decode, TreeHash)] 8 | pub struct ForkData { 9 | pub current_version: B32, 10 | pub genesis_validators_root: B256, 11 | } 12 | 13 | impl ForkData { 14 | /// Return the 32-byte fork data root for the ``current_version`` and 15 | /// ``genesis_validators_root``. This is used primarily in signature domains to avoid 16 | /// collisions across forks/chains. 17 | pub fn compute_fork_data_root(&self) -> B256 { 18 | self.tree_hash_root() 19 | } 20 | 21 | /// Return the 4-byte fork digest for the ``current_version`` and ``genesis_validators_root``. 22 | /// This is a digest primarily used for domain separation on the p2p layer. 23 | /// 4-bytes suffices for practical separation of forks/chains. 24 | pub fn compute_fork_digest(&self) -> B32 { 25 | B32::from_slice(&self.compute_fork_data_root()[..4]) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /crates/common/consensus/src/genesis.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::{B256, aliases::B32}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | /// Genesis Config store. 5 | #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] 6 | pub struct Genesis { 7 | #[serde(with = "serde_utils::quoted_u64")] 8 | pub genesis_time: u64, 9 | pub genesis_validators_root: B256, 10 | pub genesis_fork_version: B32, 11 | } 12 | -------------------------------------------------------------------------------- /crates/common/consensus/src/helpers.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use alloy_primitives::B256; 4 | 5 | use crate::{ 6 | constants::{EFFECTIVE_BALANCE_INCREMENT, SLOTS_PER_EPOCH}, 7 | electra::beacon_state::BeaconState, 8 | }; 9 | 10 | pub fn get_total_balance(state: &BeaconState, indices: Vec) -> u64 { 11 | let sum = indices 12 | .iter() 13 | .map(|&index| { 14 | state 15 | .validators 16 | .get(index as usize) 17 | .expect("Couldn't find index invalidators") 18 | .effective_balance 19 | }) 20 | .sum(); 21 | cmp::max(EFFECTIVE_BALANCE_INCREMENT, sum) 22 | } 23 | 24 | pub fn get_total_active_balance(state: &BeaconState) -> u64 { 25 | get_total_balance( 26 | state, 27 | state.get_active_validator_indices(state.get_current_epoch()), 28 | ) 29 | } 30 | 31 | pub fn calculate_committee_fraction(state: &BeaconState, committee_percent: u64) -> u64 { 32 | let committee_weight = get_total_active_balance(state) / SLOTS_PER_EPOCH; 33 | (committee_weight * committee_percent) / 100 34 | } 35 | 36 | pub fn xor>(bytes_1: T, bytes_2: T) -> B256 { 37 | let mut result: B256 = B256::default(); 38 | for i in 0..32 { 39 | result[i] = bytes_1.as_ref()[i] ^ bytes_2.as_ref()[i]; 40 | } 41 | result 42 | } 43 | -------------------------------------------------------------------------------- /crates/common/consensus/src/historical_batch.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use ssz_types::{FixedVector, typenum::U8192}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | // todo: add tests 8 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 9 | pub struct HistoricalBatch { 10 | pub block_roots: FixedVector, 11 | pub state_roots: FixedVector, 12 | } 13 | -------------------------------------------------------------------------------- /crates/common/consensus/src/historical_summary.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 7 | pub struct HistoricalSummary { 8 | pub block_summary_root: B256, 9 | pub state_summary_root: B256, 10 | } 11 | -------------------------------------------------------------------------------- /crates/common/consensus/src/indexed_attestation.rs: -------------------------------------------------------------------------------- 1 | use ream_bls::BLSSignature; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use ssz_types::{VariableList, serde_utils::quoted_u64_var_list, typenum::U131072}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | use crate::attestation_data::AttestationData; 8 | 9 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 10 | pub struct IndexedAttestation { 11 | #[serde(with = "quoted_u64_var_list")] 12 | pub attesting_indices: VariableList, 13 | pub data: AttestationData, 14 | pub signature: BLSSignature, 15 | } 16 | -------------------------------------------------------------------------------- /crates/common/consensus/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::unwrap_used)] 2 | 3 | pub mod attestation; 4 | pub mod attestation_data; 5 | pub mod attester_slashing; 6 | pub mod beacon_block_header; 7 | pub mod blob_sidecar; 8 | pub mod bls_to_execution_change; 9 | pub mod checkpoint; 10 | pub mod consolidation_request; 11 | pub mod constants; 12 | pub mod deposit; 13 | pub mod deposit_data; 14 | pub mod deposit_message; 15 | pub mod deposit_request; 16 | pub mod electra; 17 | pub mod eth_1_block; 18 | pub mod eth_1_data; 19 | pub mod execution_engine; 20 | pub mod execution_requests; 21 | pub mod fork; 22 | pub mod fork_choice; 23 | pub mod fork_data; 24 | pub mod genesis; 25 | pub mod helpers; 26 | pub mod historical_batch; 27 | pub mod historical_summary; 28 | pub mod indexed_attestation; 29 | pub mod misc; 30 | pub mod pending_attestation; 31 | pub mod pending_consolidation; 32 | pub mod pending_deposit; 33 | pub mod pending_partial_withdrawal; 34 | pub mod polynomial_commitments; 35 | pub mod predicates; 36 | pub mod proposer_slashing; 37 | pub mod signing_data; 38 | pub mod single_attestation; 39 | pub mod sync_aggregate; 40 | pub mod sync_committee; 41 | pub mod validator; 42 | pub mod voluntary_exit; 43 | pub mod withdrawal; 44 | pub mod withdrawal_request; 45 | -------------------------------------------------------------------------------- /crates/common/consensus/src/pending_attestation.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use ssz_derive::{Decode, Encode}; 3 | use ssz_types::{BitList, typenum::U2048}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | use crate::attestation_data::AttestationData; 7 | 8 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 9 | pub struct PendingAttestation { 10 | pub aggregation_bits: BitList, 11 | pub data: AttestationData, 12 | #[serde(with = "serde_utils::quoted_u64")] 13 | pub proposer_index: u64, 14 | } 15 | -------------------------------------------------------------------------------- /crates/common/consensus/src/pending_consolidation.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use ssz_derive::{Decode, Encode}; 3 | use tree_hash_derive::TreeHash; 4 | 5 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 6 | pub struct PendingConsolidation { 7 | #[serde(with = "serde_utils::quoted_u64")] 8 | pub source_index: u64, 9 | #[serde(with = "serde_utils::quoted_u64")] 10 | pub target_index: u64, 11 | } 12 | -------------------------------------------------------------------------------- /crates/common/consensus/src/pending_deposit.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use ream_bls::{BLSSignature, PubKey}; 3 | use serde::{Deserialize, Serialize}; 4 | use ssz_derive::{Decode, Encode}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 8 | pub struct PendingDeposit { 9 | pub pubkey: PubKey, 10 | pub withdrawal_credentials: B256, 11 | #[serde(with = "serde_utils::quoted_u64")] 12 | pub amount: u64, 13 | pub signature: BLSSignature, 14 | #[serde(with = "serde_utils::quoted_u64")] 15 | pub slot: u64, 16 | } 17 | -------------------------------------------------------------------------------- /crates/common/consensus/src/pending_partial_withdrawal.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use ssz_derive::{Decode, Encode}; 3 | use tree_hash_derive::TreeHash; 4 | 5 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 6 | pub struct PendingPartialWithdrawal { 7 | #[serde(with = "serde_utils::quoted_u64")] 8 | pub validator_index: u64, 9 | #[serde(with = "serde_utils::quoted_u64")] 10 | pub amount: u64, 11 | #[serde(with = "serde_utils::quoted_u64")] 12 | pub withdrawable_epoch: u64, 13 | } 14 | -------------------------------------------------------------------------------- /crates/common/consensus/src/polynomial_commitments/kzg_proof.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::FixedBytes; 2 | 3 | use crate::constants::BYTES_PER_PROOF; 4 | 5 | pub type KZGProof = FixedBytes; 6 | -------------------------------------------------------------------------------- /crates/common/consensus/src/polynomial_commitments/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod kzg_commitment; 2 | pub mod kzg_proof; 3 | -------------------------------------------------------------------------------- /crates/common/consensus/src/predicates.rs: -------------------------------------------------------------------------------- 1 | use crate::attestation_data::AttestationData; 2 | 3 | /// Check if ``data_1`` and ``data_2`` are slashable according to Casper FFG rules. 4 | pub fn is_slashable_attestation_data(data_1: &AttestationData, data_2: &AttestationData) -> bool { 5 | (data_1 != data_2 && data_1.target.epoch == data_2.target.epoch) 6 | || (data_1.source.epoch < data_2.source.epoch && data_2.target.epoch < data_1.target.epoch) 7 | } 8 | -------------------------------------------------------------------------------- /crates/common/consensus/src/proposer_slashing.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use ssz_derive::{Decode, Encode}; 3 | use tree_hash_derive::TreeHash; 4 | 5 | use crate::beacon_block_header::SignedBeaconBlockHeader; 6 | 7 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 8 | pub struct ProposerSlashing { 9 | pub signed_header_1: SignedBeaconBlockHeader, 10 | pub signed_header_2: SignedBeaconBlockHeader, 11 | } 12 | -------------------------------------------------------------------------------- /crates/common/consensus/src/signing_data.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Encode, Decode, TreeHash)] 7 | pub struct SigningData { 8 | pub object_root: B256, 9 | pub domain: B256, 10 | } 11 | -------------------------------------------------------------------------------- /crates/common/consensus/src/single_attestation.rs: -------------------------------------------------------------------------------- 1 | use ream_bls::BLSSignature; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | use crate::attestation_data::AttestationData; 7 | 8 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 9 | pub struct SingleAttestation { 10 | pub committee_index: u64, 11 | pub attester_index: u64, 12 | pub data: AttestationData, 13 | pub signature: BLSSignature, 14 | } 15 | -------------------------------------------------------------------------------- /crates/common/consensus/src/sync_aggregate.rs: -------------------------------------------------------------------------------- 1 | use ream_bls::BLSSignature; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use ssz_types::{BitVector, typenum::U512}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 8 | pub struct SyncAggregate { 9 | pub sync_committee_bits: BitVector, 10 | pub sync_committee_signature: BLSSignature, 11 | } 12 | -------------------------------------------------------------------------------- /crates/common/consensus/src/sync_committee.rs: -------------------------------------------------------------------------------- 1 | use ream_bls::PubKey; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use ssz_types::{FixedVector, typenum::U512}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 8 | pub struct SyncCommittee { 9 | pub pubkeys: FixedVector, 10 | pub aggregate_pubkey: PubKey, 11 | } 12 | -------------------------------------------------------------------------------- /crates/common/consensus/src/voluntary_exit.rs: -------------------------------------------------------------------------------- 1 | use ream_bls::BLSSignature; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 7 | pub struct SignedVoluntaryExit { 8 | pub message: VoluntaryExit, 9 | pub signature: BLSSignature, 10 | } 11 | 12 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 13 | pub struct VoluntaryExit { 14 | #[serde(with = "serde_utils::quoted_u64")] 15 | pub epoch: u64, 16 | #[serde(with = "serde_utils::quoted_u64")] 17 | pub validator_index: u64, 18 | } 19 | -------------------------------------------------------------------------------- /crates/common/consensus/src/withdrawal.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::Address; 2 | use alloy_rlp::RlpEncodable; 3 | use serde::{Deserialize, Serialize}; 4 | use ssz_derive::{Decode, Encode}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | use crate::misc::checksummed_address; 8 | 9 | #[derive( 10 | Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, RlpEncodable, 11 | )] 12 | pub struct Withdrawal { 13 | #[serde(with = "serde_utils::quoted_u64")] 14 | pub index: u64, 15 | #[serde(with = "serde_utils::quoted_u64")] 16 | pub validator_index: u64, 17 | #[serde(with = "checksummed_address")] 18 | pub address: Address, 19 | #[serde(with = "serde_utils::quoted_u64")] 20 | pub amount: u64, 21 | } 22 | -------------------------------------------------------------------------------- /crates/common/consensus/src/withdrawal_request.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::Address; 2 | use ream_bls::PubKey; 3 | use serde::{Deserialize, Serialize}; 4 | use ssz_derive::{Decode, Encode}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | use crate::misc::checksummed_address; 8 | 9 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 10 | pub struct WithdrawalRequest { 11 | #[serde(with = "checksummed_address")] 12 | pub source_address: Address, 13 | pub validator_pubkey: PubKey, 14 | #[serde(with = "serde_utils::quoted_u64")] 15 | pub amount: u64, 16 | } 17 | -------------------------------------------------------------------------------- /crates/common/execution_engine/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-execution-engine" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | alloy-primitives.workspace = true 14 | alloy-rlp.workspace = true 15 | alloy-rpc-types-eth.workspace = true 16 | anyhow.workspace = true 17 | async-trait.workspace = true 18 | ethereum_serde_utils.workspace = true 19 | ethereum_ssz.workspace = true 20 | ethereum_ssz_derive.workspace = true 21 | jsonwebtoken.workspace = true 22 | reqwest.workspace = true 23 | serde.workspace = true 24 | serde_json.workspace = true 25 | ssz_types.workspace = true 26 | tokio.workspace = true 27 | tree_hash.workspace = true 28 | tree_hash_derive.workspace = true 29 | 30 | # ream dependencies 31 | ream-consensus.workspace = true 32 | -------------------------------------------------------------------------------- /crates/common/execution_engine/src/rpc_types/eth_syncing.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::U256; 2 | use serde::Deserialize; 3 | 4 | #[derive(Deserialize, Debug)] 5 | #[serde(rename_all = "camelCase")] 6 | pub struct SyncingInfo { 7 | pub starting_block: U256, 8 | pub current_block: U256, 9 | pub highest_block: U256, 10 | } 11 | 12 | #[derive(Deserialize, Debug)] 13 | #[serde(untagged)] 14 | pub enum EthSyncing { 15 | SyncingInfo(SyncingInfo), 16 | NotSyncing(bool), 17 | } 18 | -------------------------------------------------------------------------------- /crates/common/execution_engine/src/rpc_types/forkchoice_update.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::{Address, B64, B256}; 2 | use ream_consensus::withdrawal::Withdrawal; 3 | use serde::{Deserialize, Serialize}; 4 | use ssz_types::{VariableList, typenum::U16}; 5 | 6 | use super::payload_status::PayloadStatusV1; 7 | 8 | #[derive(Debug, Deserialize, Serialize)] 9 | #[serde(rename_all = "camelCase")] 10 | pub struct ForkchoiceStateV1 { 11 | pub head_block_hash: B256, 12 | pub safe_block_hash: B256, 13 | pub finalized_block_hash: B256, 14 | } 15 | 16 | #[derive(Debug, Deserialize, Serialize)] 17 | #[serde(rename_all = "camelCase")] 18 | pub struct PayloadAttributesV3 { 19 | #[serde(with = "serde_utils::u64_hex_be")] 20 | pub timestamp: u64, 21 | pub prev_randao: B256, 22 | pub suggested_fee_recipient: Address, 23 | pub withdrawals: VariableList, 24 | pub parent_beacon_block_root: B256, 25 | } 26 | 27 | #[derive(Debug, Deserialize)] 28 | #[serde(rename_all = "camelCase")] 29 | pub struct ForkchoiceUpdateResult { 30 | pub payload_status: PayloadStatusV1, 31 | pub payload_id: Option, 32 | } 33 | -------------------------------------------------------------------------------- /crates/common/execution_engine/src/rpc_types/get_payload.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::{B256, Bytes}; 2 | use ream_consensus::polynomial_commitments::kzg_commitment::KZGCommitment; 3 | use serde::{Deserialize, Serialize}; 4 | use ssz_derive::{Decode, Encode}; 5 | use ssz_types::{ 6 | VariableList, 7 | serde_utils::list_of_hex_var_list, 8 | typenum::{U96, U1024, U1048576}, 9 | }; 10 | use tree_hash_derive::TreeHash; 11 | 12 | use super::execution_payload::ExecutionPayloadV3; 13 | 14 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 15 | #[serde(rename_all = "camelCase")] 16 | pub struct BlobsBundleV1 { 17 | pub blobs: VariableList, 18 | #[serde(with = "list_of_hex_var_list")] 19 | pub commitments: VariableList, U1024>, 20 | #[serde(with = "list_of_hex_var_list")] 21 | pub proofs: VariableList, U1024>, 22 | } 23 | 24 | #[derive(Deserialize, Debug)] 25 | #[serde(rename_all = "camelCase")] 26 | pub struct PayloadV4 { 27 | pub execution_payload: ExecutionPayloadV3, 28 | pub block_value: B256, 29 | pub blobs_bundle: BlobsBundleV1, 30 | pub should_overide_builder: bool, 31 | pub execution_requests: Vec, 32 | } 33 | -------------------------------------------------------------------------------- /crates/common/execution_engine/src/rpc_types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod eth_syncing; 2 | pub mod execution_payload; 3 | pub mod forkchoice_update; 4 | pub mod get_payload; 5 | pub mod payload_status; 6 | -------------------------------------------------------------------------------- /crates/common/execution_engine/src/rpc_types/payload_status.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Deserialize, Debug, Serialize, PartialEq)] 5 | #[serde(rename_all = "SCREAMING_SNAKE_CASE")] 6 | pub enum PayloadStatus { 7 | Valid, 8 | Invalid, 9 | Syncing, 10 | Accepted, 11 | InvalidBlockHash, 12 | } 13 | 14 | #[derive(Deserialize, Debug)] 15 | #[serde(rename_all = "camelCase")] 16 | pub struct PayloadStatusV1 { 17 | pub status: PayloadStatus, 18 | pub latest_valid_hash: Option, 19 | pub validation_error: Option, 20 | } 21 | -------------------------------------------------------------------------------- /crates/common/executor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-executor" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | futures.workspace = true 14 | tokio.workspace = true 15 | -------------------------------------------------------------------------------- /crates/common/fork_choice/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-fork-choice" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | alloy-consensus.workspace = true 14 | alloy-primitives.workspace = true 15 | alloy-rlp.workspace = true 16 | anyhow.workspace = true 17 | async-trait.workspace = true 18 | ethereum_hashing.workspace = true 19 | ethereum_serde_utils.workspace = true 20 | ethereum_ssz.workspace = true 21 | ethereum_ssz_derive.workspace = true 22 | hashbrown.workspace = true 23 | itertools.workspace = true 24 | kzg.workspace = true 25 | rust-kzg-blst.workspace = true 26 | serde.workspace = true 27 | serde_json.workspace = true 28 | serde_yaml.workspace = true 29 | ssz_types.workspace = true 30 | thiserror.workspace = true 31 | tree_hash.workspace = true 32 | tree_hash_derive.workspace = true 33 | 34 | # ream dependencies 35 | ream-bls.workspace = true 36 | ream-consensus.workspace = true 37 | ream-polynomial-commitments.workspace = true 38 | ream-storage.workspace = true 39 | -------------------------------------------------------------------------------- /crates/common/fork_choice/src/constants.rs: -------------------------------------------------------------------------------- 1 | pub const PROPOSER_SCORE_BOOST: u64 = 40; 2 | pub const REORG_HEAD_WEIGHT_THRESHOLD: u64 = 20; 3 | pub const REORG_MAX_EPOCHS_SINCE_FINALIZATION: u64 = 2; 4 | pub const REORG_PARENT_WEIGHT_THRESHOLD: u64 = 160; 5 | -------------------------------------------------------------------------------- /crates/common/fork_choice/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod constants; 2 | pub mod handlers; 3 | pub mod store; 4 | -------------------------------------------------------------------------------- /crates/common/light_client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-light-client" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | alloy-primitives.workspace = true 14 | anyhow.workspace = true 15 | ethereum_serde_utils.workspace = true 16 | ethereum_ssz.workspace = true 17 | ethereum_ssz_derive.workspace = true 18 | serde.workspace = true 19 | ssz_types.workspace = true 20 | tree_hash.workspace = true 21 | tree_hash_derive.workspace = true 22 | 23 | # ream dependencies 24 | ream-consensus.workspace = true 25 | -------------------------------------------------------------------------------- /crates/common/light_client/src/bootstrap.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use anyhow::ensure; 3 | use ream_consensus::{ 4 | electra::{beacon_block::SignedBeaconBlock, beacon_state::BeaconState}, 5 | sync_committee::SyncCommittee, 6 | }; 7 | use serde::Serialize; 8 | use ssz_types::{FixedVector, typenum::U5}; 9 | use tree_hash::TreeHash; 10 | 11 | use crate::header::LightClientHeader; 12 | 13 | #[derive(Serialize)] 14 | pub struct LightClientBootstrap { 15 | pub header: LightClientHeader, 16 | pub current_sync_committee: SyncCommittee, 17 | pub current_sync_committee_branch: FixedVector, 18 | } 19 | 20 | impl LightClientBootstrap { 21 | pub fn new(state: &BeaconState, signed_block: &SignedBeaconBlock) -> anyhow::Result { 22 | ensure!( 23 | state.slot == state.latest_block_header.slot, 24 | "State slot must be equal to block slot" 25 | ); 26 | 27 | let mut header = state.latest_block_header.clone(); 28 | header.state_root = state.tree_hash_root(); 29 | 30 | ensure!( 31 | header.tree_hash_root() == signed_block.message.tree_hash_root(), 32 | "Header root must be equal to block root" 33 | ); 34 | 35 | Ok(LightClientBootstrap { 36 | header: LightClientHeader::new(signed_block)?, 37 | current_sync_committee: (*state.current_sync_committee).clone(), 38 | current_sync_committee_branch: state.current_sync_committee_inclusion_proof()?.into(), 39 | }) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /crates/common/light_client/src/finality_update.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use ream_consensus::sync_aggregate::SyncAggregate; 3 | use serde::{Deserialize, Serialize}; 4 | use ssz_derive::{Decode, Encode}; 5 | use ssz_types::{FixedVector, typenum::U6}; 6 | use tree_hash_derive::TreeHash; 7 | 8 | use crate::header::LightClientHeader; 9 | 10 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 11 | pub struct LightClientFinalityUpdate { 12 | pub attested_header: LightClientHeader, 13 | /// Finalized header corresponding to `attested_header.beacon.state_root` 14 | pub finalized_header: LightClientHeader, 15 | pub finality_branch: FixedVector, 16 | /// Sync committee aggregate signature 17 | pub sync_aggregate: SyncAggregate, 18 | /// Slot at which the aggregate signature was created (untrusted) 19 | #[serde(with = "serde_utils::quoted_u64")] 20 | pub signature_slot: u64, 21 | } 22 | -------------------------------------------------------------------------------- /crates/common/light_client/src/header.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use ream_consensus::{ 3 | beacon_block_header::BeaconBlockHeader, 4 | electra::{beacon_block::SignedBeaconBlock, execution_payload_header::ExecutionPayloadHeader}, 5 | }; 6 | use serde::{Deserialize, Serialize}; 7 | use ssz_derive::{Decode, Encode}; 8 | use ssz_types::{FixedVector, typenum::U3}; 9 | use tree_hash::TreeHash; 10 | use tree_hash_derive::TreeHash; 11 | 12 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 13 | pub struct LightClientHeader { 14 | pub beacon: BeaconBlockHeader, 15 | pub execution: ExecutionPayloadHeader, 16 | pub execution_branch: FixedVector, 17 | } 18 | 19 | impl LightClientHeader { 20 | pub fn new(signed_block: &SignedBeaconBlock) -> anyhow::Result { 21 | Ok(Self { 22 | beacon: BeaconBlockHeader { 23 | slot: signed_block.message.slot, 24 | proposer_index: signed_block.message.proposer_index, 25 | parent_root: signed_block.message.parent_root, 26 | state_root: signed_block.message.state_root, 27 | body_root: signed_block.message.body.tree_hash_root(), 28 | }, 29 | execution: signed_block 30 | .message 31 | .body 32 | .execution_payload 33 | .to_execution_payload_header(), 34 | execution_branch: signed_block 35 | .message 36 | .body 37 | .execution_payload_inclusion_proof()? 38 | .into(), 39 | }) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /crates/common/light_client/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod bootstrap; 2 | pub mod finality_update; 3 | pub mod header; 4 | pub mod optimistic_update; 5 | -------------------------------------------------------------------------------- /crates/common/light_client/src/optimistic_update.rs: -------------------------------------------------------------------------------- 1 | use ream_consensus::sync_aggregate::SyncAggregate; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | use crate::header::LightClientHeader; 7 | 8 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 9 | pub struct LightClientOptimisticUpdate { 10 | /// Header attested to by the sync committee 11 | pub attested_header: LightClientHeader, 12 | /// Sync committee aggregate signature 13 | pub sync_aggregate: SyncAggregate, 14 | /// Slot at which the aggregate signature was created (untrusted) 15 | #[serde(with = "serde_utils::quoted_u64")] 16 | pub signature_slot: u64, 17 | } 18 | -------------------------------------------------------------------------------- /crates/common/network_spec/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-network-spec" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | alloy-primitives.workspace = true 14 | anyhow.workspace = true 15 | ethereum_serde_utils.workspace = true 16 | serde.workspace = true 17 | serde_yaml.workspace = true 18 | 19 | # ream-dependencies 20 | ream-consensus.workspace = true 21 | -------------------------------------------------------------------------------- /crates/common/network_spec/src/b32_hex.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::aliases::B32; 2 | use serde::{Deserializer, Serializer}; 3 | use serde_utils::hex::{self, PrefixedHexVisitor}; 4 | 5 | pub fn serialize(hash: &B32, serializer: S) -> Result 6 | where 7 | S: Serializer, 8 | { 9 | serializer.serialize_str(&format!("0x{}", hex::encode(hash))) 10 | } 11 | 12 | pub fn deserialize<'de, D>(deserializer: D) -> Result 13 | where 14 | D: Deserializer<'de>, 15 | { 16 | let decoded = deserializer.deserialize_str(PrefixedHexVisitor)?; 17 | B32::try_from(decoded.as_slice()).map_err(serde::de::Error::custom) 18 | } 19 | -------------------------------------------------------------------------------- /crates/common/network_spec/src/cli.rs: -------------------------------------------------------------------------------- 1 | use std::{fs, sync::Arc}; 2 | 3 | use crate::networks::{DEV, HOLESKY, HOODI, MAINNET, NetworkSpec, SEPOLIA}; 4 | 5 | pub fn network_parser(network_string: &str) -> Result, String> { 6 | match network_string { 7 | "mainnet" => Ok(MAINNET.clone()), 8 | "holesky" => Ok(HOLESKY.clone()), 9 | "sepolia" => Ok(SEPOLIA.clone()), 10 | "hoodi" => Ok(HOODI.clone()), 11 | "dev" => Ok(DEV.clone()), 12 | _ => { 13 | let contents = fs::read_to_string(network_string) 14 | .map_err(|err| format!("Failed to read file: {err}"))?; 15 | Ok(Arc::new(serde_yaml::from_str(&contents).map_err( 16 | |err| format!("Failed to parse YAML from: {err}"), 17 | )?)) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /crates/common/network_spec/src/fork_schedule.rs: -------------------------------------------------------------------------------- 1 | use std::slice::Iter; 2 | 3 | use ream_consensus::fork::Fork; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 7 | pub struct ForkSchedule(pub [Fork; ForkSchedule::TOTAL]); 8 | 9 | impl ForkSchedule { 10 | pub const TOTAL: usize = 6; 11 | 12 | pub const fn new(forks: [Fork; ForkSchedule::TOTAL]) -> Self { 13 | Self(forks) 14 | } 15 | 16 | pub fn iter(&self) -> Iter<'_, Fork> { 17 | self.0.iter() 18 | } 19 | 20 | pub fn scheduled(&self) -> impl Iterator { 21 | self.iter() 22 | .filter(|fork| fork.epoch != Fork::UNSCHEDULED_EPOCH) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /crates/common/network_spec/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod b32_hex; 2 | pub mod cli; 3 | pub mod fork_schedule; 4 | pub mod networks; 5 | -------------------------------------------------------------------------------- /crates/common/node/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-node" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | 14 | [build-dependencies] 15 | vergen = { version = "9.0", features = ["build", "cargo", "emit_and_set", "rustc"] } 16 | vergen-git2 = "1.0" 17 | -------------------------------------------------------------------------------- /crates/common/node/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod version; 2 | -------------------------------------------------------------------------------- /crates/common/node/src/version.rs: -------------------------------------------------------------------------------- 1 | pub const APP_NAME: &str = "ream"; 2 | 3 | /// The latest git commit hash of the build. 4 | pub const REAM_FULL_COMMIT: &str = env!("VERGEN_GIT_SHA"); 5 | pub const REAM_SHORT_COMMIT: &str = env!("VERGEN_GIT_SHA_SHORT"); 6 | 7 | /// Ream's version is the same as the git tag. 8 | pub const REAM_VERSION: &str = env!("REAM_VERSION"); 9 | 10 | /// The operating system of the build, linux, macos, windows etc. 11 | pub const BUILD_OPERATING_SYSTEM: &str = env!("REAM_BUILD_OPERATING_SYSTEM"); 12 | 13 | /// The architecture of the build, x86_64, aarch64, etc. 14 | pub const BUILD_ARCHITECTURE: &str = env!("REAM_BUILD_ARCHITECTURE"); 15 | 16 | // /// The version of the programming language used to build the binary. 17 | pub const PROGRAMMING_LANGUAGE_VERSION: &str = env!("VERGEN_RUSTC_SEMVER"); 18 | 19 | pub const FULL_VERSION: &str = env!("REAM_FULL_VERSION"); 20 | 21 | /// Returns the ream version and git revision. 22 | pub const fn get_ream_version_short_commit() -> &'static str { 23 | REAM_SHORT_COMMIT 24 | } 25 | 26 | /// Information about the client. 27 | /// example: ream/v0.0.1-892ad575/linux-x86_64/rustc1.85.0 28 | pub fn ream_node_version() -> String { 29 | format!( 30 | "{APP_NAME}/{REAM_VERSION}-{REAM_SHORT_COMMIT}/{BUILD_OPERATING_SYSTEM}-{BUILD_ARCHITECTURE}/rustc{PROGRAMMING_LANGUAGE_VERSION}" 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /crates/common/polynomial_commitments/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-polynomial-commitments" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | alloy-consensus.workspace = true 14 | alloy-primitives.workspace = true 15 | alloy-rlp.workspace = true 16 | anyhow.workspace = true 17 | async-trait.workspace = true 18 | ethereum_hashing.workspace = true 19 | ethereum_serde_utils.workspace = true 20 | ethereum_ssz.workspace = true 21 | ethereum_ssz_derive.workspace = true 22 | itertools.workspace = true 23 | kzg.workspace = true 24 | rust-kzg-blst.workspace = true 25 | serde.workspace = true 26 | serde_json.workspace = true 27 | serde_yaml.workspace = true 28 | ssz_types.workspace = true 29 | thiserror.workspace = true 30 | tree_hash.workspace = true 31 | tree_hash_derive.workspace = true 32 | 33 | # ream dependencies 34 | ream-bls.workspace = true 35 | ream-consensus.workspace = true 36 | -------------------------------------------------------------------------------- /crates/common/polynomial_commitments/src/error.rs: -------------------------------------------------------------------------------- 1 | #![expect(clippy::module_name_repetitions)] 2 | 3 | use thiserror::Error; 4 | 5 | #[derive(Clone, PartialEq, Eq, Debug, Error)] 6 | pub enum KzgError { 7 | #[error("kzg error: {0}")] 8 | KzgError(String), 9 | } 10 | -------------------------------------------------------------------------------- /crates/common/polynomial_commitments/src/handlers.rs: -------------------------------------------------------------------------------- 1 | use kzg::eip_4844::verify_blob_kzg_proof_batch_raw; 2 | use ream_consensus::{ 3 | execution_engine::rpc_types::get_blobs::Blob, 4 | polynomial_commitments::{kzg_commitment::KZGCommitment, kzg_proof::KZGProof}, 5 | }; 6 | 7 | use super::{error::KzgError, trusted_setup}; 8 | 9 | /// Given a list of blobs and blob KZG proofs, verify that they correspond to the provided 10 | /// commitments. Will return True if there are zero blobs/commitments/proofs. 11 | /// Public method. 12 | pub fn verify_blob_kzg_proof_batch( 13 | blobs: &[Blob], 14 | commitments_bytes: &[KZGCommitment], 15 | proofs_bytes: &[KZGProof], 16 | ) -> anyhow::Result { 17 | let raw_blobs = blobs 18 | .iter() 19 | .map(|blob| blob.to_fixed_bytes()) 20 | .collect::>(); 21 | 22 | let raw_commitments = commitments_bytes 23 | .iter() 24 | .map(|commitment| commitment.0) 25 | .collect::>(); 26 | 27 | let raw_proofs = proofs_bytes.iter().map(|proof| proof.0).collect::>(); 28 | 29 | let result = verify_blob_kzg_proof_batch_raw( 30 | &raw_blobs, 31 | &raw_commitments, 32 | &raw_proofs, 33 | trusted_setup::blst_settings(), 34 | ); 35 | 36 | result.map_err(KzgError::KzgError).map_err(Into::into) 37 | } 38 | -------------------------------------------------------------------------------- /crates/common/polynomial_commitments/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod handlers; 3 | pub mod trusted_setup; 4 | -------------------------------------------------------------------------------- /crates/common/polynomial_commitments/src/trusted_setup.rs: -------------------------------------------------------------------------------- 1 | use std::sync::OnceLock; 2 | 3 | use anyhow::{Result, anyhow}; 4 | use kzg::eip_4844::{load_trusted_setup_rust, load_trusted_setup_string}; 5 | 6 | fn load_trusted_setup() -> Result<(Vec, Vec, Vec)> { 7 | static CONTENTS: &str = include_str!("trusted_setup.txt"); 8 | 9 | load_trusted_setup_string(CONTENTS).map_err(|error| anyhow!(error)) 10 | } 11 | 12 | macro_rules! impl_settings { 13 | ($backend:ident, $settings_type:ty) => { 14 | pub fn $backend() -> &'static $settings_type { 15 | static KZG_SETTINGS: OnceLock<$settings_type> = OnceLock::new(); 16 | 17 | KZG_SETTINGS.get_or_init(|| { 18 | let output = load_trusted_setup().and_then( 19 | |(g1_monomial_bytes, g1_lagrange_bytes, g2_monomial_bytes)| { 20 | load_trusted_setup_rust( 21 | g1_monomial_bytes.as_slice(), 22 | g1_lagrange_bytes.as_slice(), 23 | g2_monomial_bytes.as_slice(), 24 | ) 25 | .map_err(|err| anyhow!(err)) 26 | }, 27 | ); 28 | 29 | match output { 30 | Ok(settings) => settings, 31 | Err(error) => panic!("failed to load kzg trusted setup: {error}"), 32 | } 33 | }) 34 | } 35 | }; 36 | } 37 | 38 | impl_settings!( 39 | blst_settings, 40 | rust_kzg_blst::types::kzg_settings::FsKZGSettings 41 | ); 42 | -------------------------------------------------------------------------------- /crates/common/validator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-validator" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | alloy-primitives.workspace = true 14 | alloy-rpc-types-beacon.workspace = true 15 | anyhow.workspace = true 16 | ethereum_hashing.workspace = true 17 | ethereum_serde_utils.workspace = true 18 | ethereum_ssz.workspace = true 19 | ethereum_ssz_derive.workspace = true 20 | eventsource-client.workspace = true 21 | futures.workspace = true 22 | ream-beacon-api-types.workspace = true 23 | reqwest.workspace = true 24 | serde.workspace = true 25 | serde_json.workspace = true 26 | serde_yaml.workspace = true 27 | ssz_types.workspace = true 28 | tokio.workspace = true 29 | tracing.workspace = true 30 | tree_hash.workspace = true 31 | tree_hash_derive.workspace = true 32 | 33 | # ream dependencies 34 | ream-bls.workspace = true 35 | ream-consensus.workspace = true 36 | ream-execution-engine.workspace = true 37 | ream-executor.workspace = true 38 | ream-keystore.workspace = true 39 | ream-network-spec.workspace = true 40 | -------------------------------------------------------------------------------- /crates/common/validator/src/aggregate_and_proof.rs: -------------------------------------------------------------------------------- 1 | use ream_bls::{BLSSignature, PrivateKey, traits::Signable}; 2 | use ream_consensus::{ 3 | attestation::Attestation, 4 | constants::DOMAIN_AGGREGATE_AND_PROOF, 5 | electra::beacon_state::BeaconState, 6 | misc::{compute_epoch_at_slot, compute_signing_root}, 7 | }; 8 | use serde::{Deserialize, Serialize}; 9 | use ssz_derive::{Decode, Encode}; 10 | use tree_hash_derive::TreeHash; 11 | 12 | use crate::attestation::get_slot_signature; 13 | 14 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 15 | pub struct AggregateAndProof { 16 | pub aggregator_index: u64, 17 | pub aggregate: Attestation, 18 | pub selection_proof: BLSSignature, 19 | } 20 | 21 | #[derive(Serialize, Deserialize)] 22 | pub struct SignedAggregateAndProof { 23 | pub message: AggregateAndProof, 24 | pub signature: BLSSignature, 25 | } 26 | 27 | pub fn get_aggregate_and_proof( 28 | state: &BeaconState, 29 | aggregator_index: u64, 30 | aggregate: Attestation, 31 | private_key: PrivateKey, 32 | ) -> anyhow::Result { 33 | Ok(AggregateAndProof { 34 | selection_proof: get_slot_signature(state, aggregate.data.slot, private_key)?, 35 | aggregator_index, 36 | aggregate, 37 | }) 38 | } 39 | 40 | pub fn get_aggregate_and_proof_signature( 41 | state: &BeaconState, 42 | aggregate_and_proof: AggregateAndProof, 43 | private_key: PrivateKey, 44 | ) -> anyhow::Result { 45 | let domain = state.get_domain( 46 | DOMAIN_AGGREGATE_AND_PROOF, 47 | Some(compute_epoch_at_slot( 48 | aggregate_and_proof.aggregate.data.slot, 49 | )), 50 | ); 51 | let signing_root = compute_signing_root(aggregate_and_proof, domain); 52 | Ok(private_key.sign(signing_root.as_ref())?) 53 | } 54 | -------------------------------------------------------------------------------- /crates/common/validator/src/blob_sidecars.rs: -------------------------------------------------------------------------------- 1 | use ream_network_spec::networks::network_spec; 2 | 3 | pub fn compute_subnet_for_blob_sidecar(blob_index: u64) -> u64 { 4 | blob_index % network_spec().blob_sidecar_subnet_count_electra 5 | } 6 | -------------------------------------------------------------------------------- /crates/common/validator/src/block.rs: -------------------------------------------------------------------------------- 1 | use ream_bls::{BLSSignature, PrivateKey, traits::Signable}; 2 | use ream_consensus::{ 3 | constants::DOMAIN_BEACON_PROPOSER, 4 | electra::{beacon_block::BeaconBlock, beacon_state::BeaconState}, 5 | misc::{compute_epoch_at_slot, compute_signing_root}, 6 | }; 7 | pub fn get_block_signature( 8 | state: &BeaconState, 9 | block: &BeaconBlock, 10 | private_key: PrivateKey, 11 | ) -> anyhow::Result { 12 | let domain = state.get_domain( 13 | DOMAIN_BEACON_PROPOSER, 14 | Some(compute_epoch_at_slot(block.slot)), 15 | ); 16 | let signing_root = compute_signing_root(block, domain); 17 | Ok(private_key.sign(signing_root.as_ref())?) 18 | } 19 | -------------------------------------------------------------------------------- /crates/common/validator/src/constants.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::{aliases::B32, fixed_bytes}; 2 | 3 | pub const ATTESTATION_SUBNET_COUNT: u64 = 64; 4 | pub const DOMAIN_CONTRIBUTION_AND_PROOF: B32 = fixed_bytes!("0x09000000"); 5 | pub const DOMAIN_SELECTION_PROOF: B32 = fixed_bytes!("0x05000000"); 6 | pub const DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF: B32 = fixed_bytes!("0x08000000"); 7 | pub const SYNC_COMMITTEE_SUBNET_COUNT: u64 = 4; 8 | pub const TARGET_AGGREGATORS_PER_COMMITTEE: u64 = 16; 9 | -------------------------------------------------------------------------------- /crates/common/validator/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ethereum_hashing::hash; 2 | use ream_bls::BLSSignature; 3 | 4 | pub mod aggregate_and_proof; 5 | pub mod attestation; 6 | pub mod beacon_api_client; 7 | pub mod blob_sidecars; 8 | pub mod block; 9 | pub mod constants; 10 | pub mod contribution_and_proof; 11 | pub mod execution_requests; 12 | pub mod state; 13 | pub mod sync_committee; 14 | pub mod validator; 15 | 16 | pub fn hash_signature_prefix_to_u64(signature: BLSSignature) -> u64 { 17 | let mut hash_prefix_bytes = [0u8; 8]; 18 | hash_prefix_bytes.copy_from_slice(&hash(signature.to_slice())[..8]); 19 | u64::from_le_bytes(hash_prefix_bytes) 20 | } 21 | -------------------------------------------------------------------------------- /crates/common/validator/src/state.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use ream_bls::{BLSSignature, PrivateKey, traits::Signable}; 3 | use ream_consensus::{ 4 | constants::DOMAIN_RANDAO, 5 | electra::{ 6 | beacon_block::{BeaconBlock, SignedBeaconBlock}, 7 | beacon_state::BeaconState, 8 | }, 9 | execution_engine::engine_trait::ExecutionApi, 10 | misc::{compute_epoch_at_slot, compute_signing_root}, 11 | }; 12 | use tree_hash::TreeHash; 13 | 14 | pub fn get_epoch_signature( 15 | state: &BeaconState, 16 | block: &BeaconBlock, 17 | private_key: PrivateKey, 18 | ) -> anyhow::Result { 19 | let domain = state.get_domain(DOMAIN_RANDAO, Some(compute_epoch_at_slot(block.slot))); 20 | let signing_root = compute_signing_root(compute_epoch_at_slot(block.slot), domain); 21 | Ok(private_key.sign(signing_root.as_ref())?) 22 | } 23 | 24 | pub async fn compute_new_state_root( 25 | state: &BeaconState, 26 | block: &BeaconBlock, 27 | execution_engine: &Option, 28 | ) -> anyhow::Result { 29 | let mut temp_state = state.clone(); 30 | temp_state 31 | .state_transition( 32 | &SignedBeaconBlock { 33 | message: block.clone(), 34 | signature: BLSSignature::infinity(), 35 | }, 36 | false, 37 | execution_engine, 38 | ) 39 | .await?; 40 | Ok(temp_state.tree_hash_root()) 41 | } 42 | -------------------------------------------------------------------------------- /crates/crypto/bls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-bls" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [features] 13 | supranational = ["blst"] 14 | zkcrypto = ["bls12_381", "sha2"] 15 | 16 | [dependencies] 17 | alloy-primitives.workspace = true 18 | anyhow.workspace = true 19 | bls12_381 = { git = "https://github.com/zkcrypto/bls12_381", rev = "9ea427c0eb1a7e2ac16902a322aea156c496ddb0", optional = true, features = ["experimental"] } # latest commit on 2024-06-22, which is the base commit of sp1-patches 20 | blst = { version = "0.3", optional = true } 21 | ethereum_ssz.workspace = true 22 | ethereum_ssz_derive.workspace = true 23 | group = "0.13.0" 24 | serde.workspace = true 25 | sha2 = { workspace = true, optional = true } 26 | ssz_types.workspace = true 27 | thiserror.workspace = true 28 | tree_hash.workspace = true 29 | tree_hash_derive.workspace = true 30 | -------------------------------------------------------------------------------- /crates/crypto/bls/src/constants.rs: -------------------------------------------------------------------------------- 1 | pub const DST: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; 2 | -------------------------------------------------------------------------------- /crates/crypto/bls/src/errors.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | #[cfg(feature = "supranational")] 4 | use crate::supranational::errors::BlstError; 5 | 6 | #[derive(Error, PartialEq, Debug)] 7 | pub enum BLSError { 8 | #[cfg(feature = "supranational")] 9 | #[error("blst error: {0}")] 10 | BlstError(#[from] BlstError), 11 | #[error("invalid byte length")] 12 | InvalidByteLength, 13 | #[error("invalid private key")] 14 | InvalidPrivateKey, 15 | #[error("invalid public key")] 16 | InvalidPublicKey, 17 | #[error("invalid signature")] 18 | InvalidSignature, 19 | #[error("invalid hex string")] 20 | InvalidHexString, 21 | } 22 | -------------------------------------------------------------------------------- /crates/crypto/bls/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The BLS (Boneh-Lynn-Shacham) cryptographic backend implementation is determined 2 | //! at compile time via feature flags. Two implementations are supported: 3 | //! - "supranational": Uses the supranational/blst library, optimized for performance 4 | //! - "zkcrypto": Uses the zkcrypto/bls12_381 library implementation, optimized for zkVMs 5 | 6 | pub mod constants; 7 | pub mod errors; 8 | pub mod private_key; 9 | pub mod pubkey; 10 | pub mod signature; 11 | pub mod traits; 12 | 13 | pub use private_key::PrivateKey; 14 | pub use pubkey::PubKey; 15 | pub use signature::BLSSignature; 16 | 17 | #[cfg(feature = "supranational")] 18 | pub mod supranational; 19 | #[cfg(feature = "zkcrypto")] 20 | pub mod zkcrypto; 21 | -------------------------------------------------------------------------------- /crates/crypto/bls/src/private_key.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use ssz_derive::{Decode, Encode}; 3 | use tree_hash_derive::TreeHash; 4 | 5 | #[derive(Debug, PartialEq, Clone, Encode, Decode, TreeHash, Default, Eq, Hash)] 6 | pub struct PrivateKey { 7 | pub inner: B256, 8 | } 9 | -------------------------------------------------------------------------------- /crates/crypto/bls/src/pubkey.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use alloy_primitives::hex; 4 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 5 | use ssz::Encode; 6 | use ssz_derive::{Decode, Encode}; 7 | use ssz_types::{FixedVector, typenum::U48}; 8 | use tree_hash_derive::TreeHash; 9 | 10 | use crate::errors::BLSError; 11 | 12 | #[derive(Debug, PartialEq, Clone, Encode, Decode, TreeHash, Default, Eq, Hash)] 13 | pub struct PubKey { 14 | pub inner: FixedVector, 15 | } 16 | 17 | impl Serialize for PubKey { 18 | fn serialize(&self, serializer: S) -> Result 19 | where 20 | S: Serializer, 21 | { 22 | let val = format!("0x{}", hex::encode(self.inner.as_ssz_bytes())); 23 | serializer.serialize_str(&val) 24 | } 25 | } 26 | 27 | impl<'de> Deserialize<'de> for PubKey { 28 | fn deserialize(deserializer: D) -> Result 29 | where 30 | D: Deserializer<'de>, 31 | { 32 | let result: String = Deserialize::deserialize(deserializer)?; 33 | let result = hex::decode(&result).map_err(serde::de::Error::custom)?; 34 | let key = FixedVector::from(result); 35 | Ok(Self { inner: key }) 36 | } 37 | } 38 | 39 | impl PubKey { 40 | pub fn to_bytes(&self) -> &[u8] { 41 | self.inner.iter().as_slice() 42 | } 43 | } 44 | 45 | impl FromStr for PubKey { 46 | type Err = BLSError; 47 | fn from_str(s: &str) -> Result { 48 | let clean_str = s.strip_prefix("0x").unwrap_or(s); 49 | let bytes = hex::decode(clean_str).map_err(|_| BLSError::InvalidHexString)?; 50 | 51 | if bytes.len() != 48 { 52 | return Err(BLSError::InvalidByteLength); 53 | } 54 | 55 | Ok(PubKey { 56 | inner: FixedVector::from(bytes), 57 | }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /crates/crypto/bls/src/signature.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::hex; 2 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 3 | use ssz::Encode; 4 | use ssz_derive::{Decode, Encode}; 5 | use ssz_types::{FixedVector, typenum::U96}; 6 | use tree_hash_derive::TreeHash; 7 | 8 | #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode, TreeHash, Default)] 9 | pub struct BLSSignature { 10 | pub inner: FixedVector, 11 | } 12 | 13 | impl Serialize for BLSSignature { 14 | fn serialize(&self, serializer: S) -> Result 15 | where 16 | S: Serializer, 17 | { 18 | let val = format!("0x{}", hex::encode(self.inner.as_ssz_bytes())); 19 | serializer.serialize_str(&val) 20 | } 21 | } 22 | 23 | impl<'de> Deserialize<'de> for BLSSignature { 24 | fn deserialize(deserializer: D) -> Result 25 | where 26 | D: Deserializer<'de>, 27 | { 28 | let result: String = Deserialize::deserialize(deserializer)?; 29 | let result = hex::decode(&result).map_err(serde::de::Error::custom)?; 30 | let key = FixedVector::from(result); 31 | Ok(Self { inner: key }) 32 | } 33 | } 34 | 35 | impl BLSSignature { 36 | pub fn to_slice(&self) -> &[u8] { 37 | self.inner.iter().as_slice() 38 | } 39 | 40 | pub fn infinity() -> Self { 41 | Self { 42 | inner: FixedVector::from(vec![ 43 | 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 44 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 45 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 46 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 47 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 48 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 49 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 50 | ]), 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /crates/crypto/bls/src/supranational/errors.rs: -------------------------------------------------------------------------------- 1 | use blst::BLST_ERROR; 2 | use thiserror::Error; 3 | 4 | /// Wrapper for the errors returned by the blst library 5 | #[derive(Error, Debug, PartialEq)] 6 | pub enum BlstError { 7 | #[error("Invalid BLS public key encoding")] 8 | BadEncoding, 9 | #[error("BLS point is not on curve")] 10 | PointNotOnCurve, 11 | #[error("BLS point is not in the correct group")] 12 | PointNotInGroup, 13 | #[error("BLS aggregate type mismatch")] 14 | AggrTypeMismatch, 15 | #[error("BLS verification failed")] 16 | VerificationFailed, 17 | #[error("BLS public key is infinity")] 18 | PublicKeyIsInfinity, 19 | #[error("Invalid BLS scalar value")] 20 | BadScalar, 21 | #[error("Unknown BLS error: {0:?}")] 22 | Unknown(BLST_ERROR), 23 | } 24 | 25 | impl From for BlstError { 26 | fn from(error: BLST_ERROR) -> Self { 27 | match error { 28 | BLST_ERROR::BLST_BAD_ENCODING => BlstError::BadEncoding, 29 | BLST_ERROR::BLST_POINT_NOT_ON_CURVE => BlstError::PointNotOnCurve, 30 | BLST_ERROR::BLST_POINT_NOT_IN_GROUP => BlstError::PointNotInGroup, 31 | BLST_ERROR::BLST_AGGR_TYPE_MISMATCH => BlstError::AggrTypeMismatch, 32 | BLST_ERROR::BLST_VERIFY_FAIL => BlstError::VerificationFailed, 33 | BLST_ERROR::BLST_PK_IS_INFINITY => BlstError::PublicKeyIsInfinity, 34 | BLST_ERROR::BLST_BAD_SCALAR => BlstError::BadScalar, 35 | // SAFETY: BLST_SUCCESS should never be passed to this error conversion. 36 | // It is used only for comparison in verification methods and represents 37 | // a successful operation, not an error condition. 38 | BLST_ERROR::BLST_SUCCESS => unreachable!("Success is not an error"), 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /crates/crypto/bls/src/supranational/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod errors; 2 | pub mod private_key; 3 | pub mod pubkey; 4 | pub mod signature; 5 | -------------------------------------------------------------------------------- /crates/crypto/bls/src/supranational/private_key.rs: -------------------------------------------------------------------------------- 1 | use anyhow::anyhow; 2 | use blst::min_pk::SecretKey as BlstSecretKey; 3 | use ssz_types::FixedVector; 4 | 5 | use crate::{ 6 | PrivateKey, 7 | constants::DST, 8 | signature::BLSSignature, 9 | traits::{Signable, SupranationalSignable}, 10 | }; 11 | 12 | impl Signable for PrivateKey { 13 | type Error = anyhow::Error; 14 | 15 | fn sign(&self, message: &[u8]) -> Result { 16 | let private_key = BlstSecretKey::from_bytes(self.inner.as_slice()) 17 | .map_err(|err| anyhow!("Failed to convert to BlstSecretKey: {err:?}"))?; 18 | let signature = private_key.sign(message, DST, &[]); 19 | Ok(BLSSignature { 20 | inner: FixedVector::new(signature.serialize().to_vec()) 21 | .map_err(|err| anyhow!("Failed to create to BLSSignature: {err:?}"))?, 22 | }) 23 | } 24 | } 25 | 26 | impl SupranationalSignable for PrivateKey {} 27 | -------------------------------------------------------------------------------- /crates/crypto/bls/src/supranational/pubkey.rs: -------------------------------------------------------------------------------- 1 | use anyhow::anyhow; 2 | use blst::min_pk::{AggregatePublicKey as BlstAggregatePublicKey, PublicKey as BlstPublicKey}; 3 | use ssz_types::FixedVector; 4 | 5 | use crate::{ 6 | errors::BLSError, 7 | pubkey::PubKey, 8 | traits::{Aggregatable, SupranationalAggregatable}, 9 | }; 10 | 11 | impl TryFrom for PubKey { 12 | type Error = BLSError; 13 | 14 | fn try_from(value: BlstPublicKey) -> Result { 15 | Ok(PubKey { 16 | inner: FixedVector::new(value.to_bytes().to_vec()) 17 | .map_err(|_| BLSError::InvalidPublicKey)?, 18 | }) 19 | } 20 | } 21 | 22 | impl PubKey { 23 | pub fn to_blst_pubkey(&self) -> Result { 24 | BlstPublicKey::from_bytes(&self.inner).map_err(|err| BLSError::BlstError(err.into())) 25 | } 26 | } 27 | 28 | impl Aggregatable for PubKey { 29 | type Error = anyhow::Error; 30 | 31 | fn aggregate(public_keys: &[&PubKey]) -> anyhow::Result { 32 | let public_keys = public_keys 33 | .iter() 34 | .map(|public_key| public_key.to_blst_pubkey()) 35 | .collect::, _>>()?; 36 | let aggregate_public_key = 37 | BlstAggregatePublicKey::aggregate(&public_keys.iter().collect::>(), true) 38 | .map_err(|err| anyhow!("Failed to aggregate and validate public keys {err:?}"))?; 39 | Ok(PubKey::try_from(aggregate_public_key.to_public_key())?) 40 | } 41 | } 42 | 43 | impl SupranationalAggregatable for PubKey {} 44 | -------------------------------------------------------------------------------- /crates/crypto/bls/src/zkcrypto/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod private_key; 2 | pub mod pubkey; 3 | pub mod signature; 4 | -------------------------------------------------------------------------------- /crates/crypto/bls/src/zkcrypto/private_key.rs: -------------------------------------------------------------------------------- 1 | use bls12_381::{ 2 | G2Projective, Scalar, 3 | hash_to_curve::{ExpandMsgXmd, HashToCurve}, 4 | }; 5 | use group::Curve; 6 | use ssz_types::FixedVector; 7 | 8 | use crate::{ 9 | PrivateKey, 10 | constants::DST, 11 | errors::BLSError, 12 | signature::BLSSignature, 13 | traits::{Signable, ZkcryptoSignable}, 14 | }; 15 | 16 | impl Signable for PrivateKey { 17 | type Error = BLSError; 18 | 19 | fn sign(&self, message: &[u8]) -> Result { 20 | let hash_point = >>::hash_to_curve( 21 | [message], 22 | DST, 23 | ); 24 | 25 | let scalar = Scalar::from_bytes(self.inner.as_ref()) 26 | .into_option() 27 | .ok_or(BLSError::InvalidPrivateKey)?; 28 | let signature_point = hash_point * scalar; 29 | let signature_bytes = signature_point.to_affine().to_compressed(); 30 | 31 | Ok(BLSSignature { 32 | inner: FixedVector::new(signature_bytes.to_vec()) 33 | .map_err(|_| BLSError::InvalidPrivateKey)?, 34 | }) 35 | } 36 | } 37 | 38 | impl ZkcryptoSignable for PrivateKey {} 39 | -------------------------------------------------------------------------------- /crates/crypto/bls/src/zkcrypto/pubkey.rs: -------------------------------------------------------------------------------- 1 | use bls12_381::{G1Affine, G1Projective}; 2 | 3 | use crate::{ 4 | PubKey, 5 | errors::BLSError, 6 | traits::{Aggregatable, ZkcryptoAggregatable}, 7 | }; 8 | 9 | impl From for PubKey { 10 | fn from(value: G1Projective) -> Self { 11 | Self { 12 | inner: G1Affine::from(value).to_compressed().to_vec().into(), 13 | } 14 | } 15 | } 16 | 17 | impl TryFrom<&PubKey> for G1Affine { 18 | type Error = BLSError; 19 | 20 | fn try_from(value: &PubKey) -> Result { 21 | match G1Affine::from_compressed( 22 | &value 23 | .to_bytes() 24 | .try_into() 25 | .map_err(|_| BLSError::InvalidByteLength)?, 26 | ) 27 | .into_option() 28 | { 29 | Some(point) => Ok(point), 30 | None => Err(BLSError::InvalidPublicKey), 31 | } 32 | } 33 | } 34 | 35 | impl Aggregatable for PubKey { 36 | type Error = BLSError; 37 | 38 | fn aggregate(public_keys: &[&PubKey]) -> Result { 39 | let aggregate_point = 40 | public_keys 41 | .iter() 42 | .try_fold(G1Projective::identity(), |accumulator, public_key| { 43 | Ok(accumulator.add(&G1Projective::from(G1Affine::try_from(*public_key)?))) 44 | })?; 45 | 46 | Ok(PubKey::from(aggregate_point)) 47 | } 48 | } 49 | 50 | impl ZkcryptoAggregatable for PubKey {} 51 | -------------------------------------------------------------------------------- /crates/crypto/keystore/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-keystore" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | aes.workspace = true 14 | alloy-primitives.workspace = true 15 | anyhow.workspace = true 16 | serde.workspace = true 17 | serde_json.workspace = true 18 | sha2.workspace = true 19 | ssz_types.workspace = true 20 | 21 | # ream dependencies 22 | ream-bls.workspace = true 23 | -------------------------------------------------------------------------------- /crates/crypto/keystore/assets/Pbkdf2TestKeystore.json: -------------------------------------------------------------------------------- 1 | {"crypto":{"kdf":{"function":"pbkdf2","params":{"dklen":32,"c":262144,"prf":"hmac-sha256","salt":"d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"},"message":""},"checksum":{"function":"sha256","params":{},"message":"8a9f5d9912ed7e75ea794bc5a89bca5f193721d30868ade6f73043c6ea6febf1"},"cipher":{"function":"aes-128-ctr","params":{"iv":"264daa3f303d7259501c93d997d84fe6"},"message":"cee03fde2af33149775b7223e7845e4fb2c8ae1792e5f99fe9ecf474cc8c16ad"}},"description":"This is a test keystore that uses PBKDF2 to secure the secret.","pubkey":"9612d7a727c9d0a22e185a1c768478dfe919cada9266988cb32359c11f2b7b27f4ae4040902382ae2910c15e2b420d07","path":"m/12381/60/0/0","uuid":"64625def-3331-4eea-ab6f-782f3ed16a83","version":4} 2 | -------------------------------------------------------------------------------- /crates/crypto/keystore/assets/ScryptDecryptionTest.json: -------------------------------------------------------------------------------- 1 | {"crypto":{"kdf":{"function":"scrypt","params":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"},"message":""},"checksum":{"function":"sha256","params":{},"message":"d2217fe5f3e9a1e34581ef8a78f7c9928e436d36dacc5e846690a5581e8ea484"},"cipher":{"function":"aes-128-ctr","params":{"iv":"264daa3f303d7259501c93d997d84fe6"},"message":"06ae90d55fe0a6e9c5c3bc5b170827b2e5cce3929ed3f116c2811e6366dfe20f"}},"description":"This is a test keystore that uses scrypt to secure the secret.","pubkey":"9612d7a727c9d0a22e185a1c768478dfe919cada9266988cb32359c11f2b7b27f4ae4040902382ae2910c15e2b420d07","path":"m/12381/60/3141592653/589793238","uuid":"1d85ae20-35c5-4611-98e8-aa14a633906f","version":4} 2 | -------------------------------------------------------------------------------- /crates/crypto/keystore/assets/ScryptKeystore.json: -------------------------------------------------------------------------------- 1 | {"crypto": {"kdf": {"function": "scrypt", "params": {"dklen": 32, "n": 262144, "r": 8, "p": 1, "salt": "a8ba3c3981ec95d49c776f4959720dc04e7ac39a4d8aa26bccf419cb241efd6a"}, "message": ""}, "checksum": {"function": "sha256", "params": {}, "message": "7c6392e1b675ea50451ff356206ffe01be7b938eab3b7e2821fcfc0542d90032"}, "cipher": {"function": "aes-128-ctr", "params": {"iv": "180742384a64fedc51147799da529dd0"}, "message": "ae3a00597d61d570b767704edb925e2fe2dd474ea1145c62ac04a2484a322e3d"}}, "description": "", "pubkey": "b69dfa082ca75d4e50ed4da8fa07d550ba9ec4019815409f42a98b79861d7ad96633a2476594b94c8a6e3048e1b2623e", "path": "m/12381/3600/0/0/0", "uuid": "8f6774f8-3b29-448f-b407-499fb1e98a20", "version": 4} 2 | -------------------------------------------------------------------------------- /crates/crypto/keystore/src/decrypt.rs: -------------------------------------------------------------------------------- 1 | use aes::{ 2 | Aes128, 3 | cipher::{BlockEncrypt, KeyInit, generic_array::GenericArray}, 4 | }; 5 | 6 | pub fn aes128_ctr(buffer: &mut [u8], key: [u8; 16], initial_vector: &[u8; 16]) { 7 | let cipher = Aes128::new(&key.into()); 8 | let mut counter = u128::from_be_bytes(*initial_vector); 9 | 10 | for chunk in buffer.chunks_mut(16) { 11 | let mut block = GenericArray::from(counter.to_be_bytes()); 12 | cipher.encrypt_block(&mut block); 13 | for (chunk_byte, block_byte) in chunk.iter_mut().zip(block.iter()) { 14 | *chunk_byte ^= block_byte; 15 | } 16 | counter = counter.wrapping_add(1); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /crates/crypto/keystore/src/hex_serde.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::hex::{FromHex, ToHexExt}; 2 | use serde::{Deserialize, Deserializer, Serializer, de}; 3 | 4 | pub fn serialize(value: &T, serializer: S) -> Result 5 | where 6 | T: AsRef<[u8]>, 7 | S: Serializer, 8 | { 9 | serializer.serialize_str(&value.encode_hex()) 10 | } 11 | 12 | pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> 13 | where 14 | D: Deserializer<'de>, 15 | { 16 | let s = String::deserialize(deserializer)?; 17 | Vec::::from_hex(&s).map_err(|err| de::Error::custom(err.to_string())) 18 | } 19 | -------------------------------------------------------------------------------- /crates/crypto/keystore/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod decrypt; 2 | pub mod hex_serde; 3 | pub mod hmac; 4 | pub mod keystore; 5 | pub mod pbkdf2; 6 | pub mod salsa; 7 | pub mod scrypt; 8 | -------------------------------------------------------------------------------- /crates/crypto/merkle/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-merkle" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | alloy-primitives.workspace = true 14 | anyhow.workspace = true 15 | ethereum_hashing.workspace = true 16 | serde.workspace = true 17 | -------------------------------------------------------------------------------- /crates/crypto/merkle/README.md: -------------------------------------------------------------------------------- 1 | # ream-merkle 2 | 3 | This crate provides functionality for generating and verifying Merkle Tree proofs. 4 | -------------------------------------------------------------------------------- /crates/crypto/merkle/src/hash.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | 3 | /// Common hashing function for Merkle trees. 4 | pub fn hash_concat(h1: &[u8], h2: &[u8]) -> B256 { 5 | ethereum_hashing::hash32_concat(h1, h2).into() 6 | } 7 | -------------------------------------------------------------------------------- /crates/networking/discv5/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-discv5" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | alloy-primitives.workspace = true 14 | alloy-rlp.workspace = true 15 | anyhow.workspace = true 16 | discv5.workspace = true 17 | ethereum_ssz.workspace = true 18 | ethereum_ssz_derive.workspace = true 19 | futures.workspace = true 20 | libp2p.workspace = true 21 | rand = "0.8.5" 22 | ssz_types.workspace = true 23 | tokio.workspace = true 24 | tracing.workspace = true 25 | 26 | # ream dependencies 27 | ream-consensus.workspace = true 28 | ream-network-spec.workspace = true 29 | -------------------------------------------------------------------------------- /crates/networking/discv5/src/config.rs: -------------------------------------------------------------------------------- 1 | use std::net::{IpAddr, Ipv4Addr}; 2 | 3 | use discv5::{ConfigBuilder, Enr, ListenConfig}; 4 | 5 | use crate::subnet::{AttestationSubnets, SyncCommitteeSubnets}; 6 | 7 | pub struct DiscoveryConfig { 8 | pub discv5_config: discv5::Config, 9 | pub bootnodes: Vec, 10 | pub socket_address: IpAddr, 11 | pub socket_port: u16, 12 | pub discovery_port: u16, 13 | pub disable_discovery: bool, 14 | pub attestation_subnets: AttestationSubnets, 15 | pub sync_committee_subnets: SyncCommitteeSubnets, 16 | } 17 | 18 | impl Default for DiscoveryConfig { 19 | fn default() -> Self { 20 | let mut attestation_subnets = AttestationSubnets::new(); 21 | let sync_committee_subnets = SyncCommitteeSubnets::new(); 22 | 23 | // Enable attestation subnets 0 and 1 as a reasonable default 24 | attestation_subnets 25 | .enable_attestation_subnet(0) 26 | .expect("Failed to enable attestation subnet 0"); 27 | attestation_subnets 28 | .enable_attestation_subnet(1) 29 | .expect("Failed to enable attestation subnet 1"); 30 | 31 | let socket_address = Ipv4Addr::UNSPECIFIED; 32 | let socket_port = 9000; 33 | let discovery_port = 9000; 34 | let listen_config = ListenConfig::from_ip(socket_address.into(), discovery_port); 35 | 36 | let discv5_config = ConfigBuilder::new(listen_config).build(); 37 | 38 | Self { 39 | discv5_config, 40 | bootnodes: Vec::new(), 41 | socket_address: socket_address.into(), 42 | socket_port, 43 | discovery_port, 44 | disable_discovery: false, 45 | attestation_subnets, 46 | sync_committee_subnets, 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /crates/networking/discv5/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | pub mod discovery; 3 | pub mod eth2; 4 | pub mod subnet; 5 | -------------------------------------------------------------------------------- /crates/networking/manager/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-manager" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | anyhow.workspace = true 14 | discv5.workspace = true 15 | libp2p.workspace = true 16 | tokio.workspace = true 17 | tracing.workspace = true 18 | tree_hash.workspace = true 19 | url.workspace = true 20 | 21 | # ream dependencies 22 | ream-beacon-chain.workspace = true 23 | ream-consensus.workspace = true 24 | ream-discv5.workspace = true 25 | ream-execution-engine.workspace = true 26 | ream-executor.workspace = true 27 | ream-fork-choice.workspace = true 28 | ream-network-spec.workspace = true 29 | ream-p2p.workspace = true 30 | ream-storage.workspace = true 31 | ream-syncer.workspace = true 32 | -------------------------------------------------------------------------------- /crates/networking/manager/src/config.rs: -------------------------------------------------------------------------------- 1 | use std::{net::IpAddr, path::PathBuf}; 2 | 3 | use ream_p2p::bootnodes::Bootnodes; 4 | use url::Url; 5 | 6 | pub struct ManagerConfig { 7 | pub http_address: IpAddr, 8 | pub http_port: u16, 9 | pub http_allow_origin: bool, 10 | pub socket_address: IpAddr, 11 | pub socket_port: u16, 12 | pub discovery_port: u16, 13 | pub disable_discovery: bool, 14 | pub data_dir: Option, 15 | pub ephemeral: bool, 16 | pub bootnodes: Bootnodes, 17 | pub checkpoint_sync_url: Option, 18 | pub purge_db: bool, 19 | pub execution_endpoint: Option, 20 | pub execution_jwt_secret: Option, 21 | } 22 | -------------------------------------------------------------------------------- /crates/networking/manager/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | pub mod service; 3 | -------------------------------------------------------------------------------- /crates/networking/p2p/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-p2p" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | alloy-primitives.workspace = true 14 | anyhow.workspace = true 15 | asynchronous-codec = "0.7.0" 16 | delay_map.workspace = true 17 | discv5.workspace = true 18 | enr.workspace = true 19 | ethereum_ssz.workspace = true 20 | ethereum_ssz_derive.workspace = true 21 | futures.workspace = true 22 | k256 = { version = "0.13", default-features = false, features = ["ecdsa", "arithmetic"] } 23 | libp2p.workspace = true 24 | libp2p-identity.workspace = true 25 | libp2p-mplex.workspace = true 26 | parking_lot.workspace = true 27 | serde.workspace = true 28 | serde_yaml.workspace = true 29 | sha2.workspace = true 30 | snap.workspace = true 31 | ssz_types.workspace = true 32 | thiserror.workspace = true 33 | tokio.workspace = true 34 | tokio-io-timeout = "1" 35 | tokio-util.workspace = true 36 | tracing.workspace = true 37 | tree_hash.workspace = true 38 | unsigned-varint = { version = "0.8", features = ["codec"] } 39 | 40 | # ream dependencies 41 | ream-consensus.workspace = true 42 | ream-discv5.workspace = true 43 | ream-executor.workspace = true 44 | ream-light-client.workspace = true 45 | ream-network-spec.workspace = true 46 | ream-validator.workspace = true 47 | -------------------------------------------------------------------------------- /crates/networking/p2p/resources/bootnodes_holesky.yaml: -------------------------------------------------------------------------------- 1 | # Eth holesky consensus layer bootnodes 2 | # Taken from https://github.com/eth-clients/holesky/blob/main/metadata/bootstrap_nodes.yaml 3 | 4 | # EF 5 | - enr:-Ku4QFo-9q73SspYI8cac_4kTX7yF800VXqJW4Lj3HkIkb5CMqFLxciNHePmMt4XdJzHvhrCC5ADI4D_GkAsxGJRLnQBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAhnTT-AQFwAP__________gmlkgnY0gmlwhLKAiOmJc2VjcDI1NmsxoQORcM6e19T1T9gi7jxEZjk_sjVLGFscUNqAY9obgZaxbIN1ZHCCIyk 6 | - enr:-Ku4QPG7F72mbKx3gEQEx07wpYYusGDh-ni6SNkLvOS-hhN-BxIggN7tKlmalb0L5JPoAfqD-akTZ-gX06hFeBEz4WoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAhnTT-AQFwAP__________gmlkgnY0gmlwhJK-DYCJc2VjcDI1NmsxoQKLVXFOhp2uX6jeT0DvvDpPcU8FWMjQdR4wMuORMhpX24N1ZHCCIyk 7 | - enr:-LK4QPxe-mDiSOtEB_Y82ozvxn9aQM07Ui8A-vQHNgYGMMthfsfOabaaTHhhJHFCBQQVRjBww_A5bM1rf8MlkJU_l68Eh2F0dG5ldHOIAADAAAAAAACEZXRoMpBpt9l0BAFwAAABAAAAAAAAgmlkgnY0gmlwhLKAiOmJc2VjcDI1NmsxoQJu6T9pclPObAzEVQ53DpVQqjadmVxdTLL-J3h9NFoCeIN0Y3CCIyiDdWRwgiMo 8 | - enr:-Ly4QGbOw4xNel5EhmDsJJ-QhC9XycWtsetnWoZ0uRy381GHdHsNHJiCwDTOkb3S1Ade0SFQkWJX_pgb3g8Jfh93rvMBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpBpt9l0BAFwAAABAAAAAAAAgmlkgnY0gmlwhJK-DYCJc2VjcDI1NmsxoQOxKv9sv3zKF8GDewgFGGHKP5HCZZpPpTrwl9eXKAWGxIhzeW5jbmV0cwCDdGNwgiMog3VkcIIjKA 9 | 10 | # Teku 11 | - enr:-KO4QCi3ZY4TM5KL7bAG6laSYiYelDWu0crvUjCXlyc_cwEfUpMIuARuMJYGxWe-UYYpHEw_aBbZ1u-4tHQ8imyI5uaCAsGEZXRoMpBprg6ZBQFwAP__________gmlkgnY0gmlwhKyuI_mJc2VjcDI1NmsxoQLoFG5-vuNX6N49vnkTBaA3ZsBDF8B30DGqWOGtRGz5w4N0Y3CCIyiDdWRwgiMo 12 | 13 | # Sigma Prime 14 | - enr:-Le4QLoE1wFHSlGcm48a9ZESb_MRLqPPu6G0vHqu4MaUcQNDHS69tsy-zkN0K6pglyzX8m24mkb-LtBcbjAYdP1uxm4BhGV0aDKQabfZdAQBcAAAAQAAAAAAAIJpZIJ2NIJpcIQ5gR6Wg2lwNpAgAUHQBwEQAAAAAAAAADR-iXNlY3AyNTZrMaEDPMSNdcL92uNIyCsS177Z6KTXlbZakQqxv3aQcWawNXeDdWRwgiMohHVkcDaCI4I 15 | 16 | # Lodestar 17 | - enr:-KG4QC9Wm32mtzB5Fbj2ri2TEKglHmIWgvwTQCvNHBopuwpNAi1X6qOsBg_Z1-Bee-kfSrhzUQZSgDUyfH5outUprtoBgmlkgnY0gmlwhHEel3eDaXA2kP6AAAAAAAAAAlBW__4Srr-Jc2VjcDI1NmsxoQO7KE63Z4eSI55S1Yn7q9_xFkJ1Wt-a3LgiXuKGs19s0YN1ZHCCIyiEdWRwNoIjKA 18 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/bootnodes.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use anyhow::anyhow; 4 | use discv5::Enr; 5 | use ream_network_spec::networks::Network; 6 | 7 | #[derive(Clone, Debug, Default, Eq, PartialEq)] 8 | pub enum Bootnodes { 9 | #[default] 10 | Default, 11 | None, 12 | Custom(Vec), 13 | } 14 | 15 | impl FromStr for Bootnodes { 16 | type Err = anyhow::Error; 17 | 18 | fn from_str(s: &str) -> Result { 19 | match s { 20 | "default" => Ok(Bootnodes::Default), 21 | "none" => Ok(Bootnodes::None), 22 | _ => s 23 | .split(',') 24 | .map(Enr::from_str) 25 | .collect::, String>>() 26 | .map(Bootnodes::Custom) 27 | .map_err(|err| anyhow!("Failed to parse bootnodes: {err:?}")), 28 | } 29 | } 30 | } 31 | 32 | impl Bootnodes { 33 | pub fn to_enrs(self, network: Network) -> Vec { 34 | let bootnodes: Vec = match network { 35 | Network::Mainnet => { 36 | serde_yaml::from_str(include_str!("../resources/bootnodes_mainnet.yaml")) 37 | .expect("should deserialize bootnodes") 38 | } 39 | Network::Holesky => { 40 | serde_yaml::from_str(include_str!("../resources/bootnodes_holesky.yaml")) 41 | .expect("should deserialize bootnodes") 42 | } 43 | Network::Sepolia => { 44 | serde_yaml::from_str(include_str!("../resources/bootnodes_sepolia.yaml")) 45 | .expect("should deserialize bootnodes") 46 | } 47 | Network::Hoodi => { 48 | serde_yaml::from_str(include_str!("../resources/bootnodes_hoodi.yaml")) 49 | .expect("should deserialize bootnodes") 50 | } 51 | Network::Dev | Network::Custom(_) => vec![], 52 | }; 53 | 54 | match self { 55 | Bootnodes::Default => bootnodes, 56 | Bootnodes::None => vec![], 57 | Bootnodes::Custom(bootnodes) => bootnodes, 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/channel.rs: -------------------------------------------------------------------------------- 1 | use libp2p::{PeerId, swarm::ConnectionId}; 2 | use tokio::sync::mpsc; 3 | 4 | use crate::req_resp::{handler::RespMessage, messages::ResponseMessage}; 5 | 6 | pub enum P2PCallbackResponse { 7 | ResponseMessage(Box), 8 | EndOfStream, 9 | } 10 | 11 | pub enum P2PMessage { 12 | Request(P2PRequest), 13 | Response(P2PResponse), 14 | } 15 | 16 | pub enum P2PRequest { 17 | BlockRange { 18 | peer_id: PeerId, 19 | start: u64, 20 | count: u64, 21 | callback: mpsc::Sender>, 22 | }, 23 | } 24 | 25 | pub struct P2PResponse { 26 | pub peer_id: PeerId, 27 | pub connection_id: ConnectionId, 28 | pub stream_id: u64, 29 | pub message: RespMessage, 30 | } 31 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/config.rs: -------------------------------------------------------------------------------- 1 | use std::{net::IpAddr, path::PathBuf}; 2 | 3 | use ream_discv5::config::DiscoveryConfig; 4 | 5 | use crate::gossipsub::configurations::GossipsubConfig; 6 | 7 | pub struct NetworkConfig { 8 | pub socket_address: IpAddr, 9 | 10 | pub socket_port: u16, 11 | 12 | pub discv5_config: DiscoveryConfig, 13 | 14 | pub gossipsub_config: GossipsubConfig, 15 | 16 | pub data_dir: PathBuf, 17 | } 18 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/constants.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use alloy_primitives::{aliases::B32, fixed_bytes}; 4 | 5 | /// The maximum allowed size of uncompressed payload in gossipsub messages and RPC chunks 6 | pub const MAX_PAYLOAD_SIZE: u64 = 10485760; 7 | pub const MESSAGE_DOMAIN_VALID_SNAPPY: B32 = fixed_bytes!("0x01000000"); 8 | pub const MESSAGE_DOMAIN_INVALID_SNAPPY: B32 = fixed_bytes!("0x00000000"); 9 | 10 | pub const PING_INTERVAL_DURATION: Duration = Duration::from_secs(300); 11 | pub const TARGET_PEER_COUNT: usize = 50; 12 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/gossipsub/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(thiserror::Error, Debug)] 2 | pub enum GossipsubError { 3 | #[error("Gossipsub invalid data {0}")] 4 | InvalidData(String), 5 | #[error("Gossipsub invalid topic {0}")] 6 | InvalidTopic(String), 7 | } 8 | 9 | impl From for GossipsubError { 10 | fn from(err: ssz::DecodeError) -> Self { 11 | GossipsubError::InvalidData(format!("Failed to decode ssz: {err:?}")) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/gossipsub/mod.rs: -------------------------------------------------------------------------------- 1 | //! https://ethereum.github.io/consensus-specs/specs/phase0/p2p-interface/#the-gossip-domain-gossipsub 2 | 3 | pub mod configurations; 4 | pub mod error; 5 | pub mod message; 6 | pub mod snappy; 7 | pub mod topics; 8 | 9 | use libp2p::gossipsub::{AllowAllSubscriptionFilter, Behaviour}; 10 | use snappy::SnappyTransform; 11 | 12 | pub type GossipsubBehaviour = Behaviour; 13 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/gossipsub/snappy.rs: -------------------------------------------------------------------------------- 1 | use libp2p::gossipsub::{DataTransform, Message, RawMessage, TopicHash}; 2 | use snap::raw::{Decoder, Encoder, decompress_len}; 3 | 4 | pub struct SnappyTransform { 5 | max_size_per_message: usize, 6 | } 7 | 8 | impl SnappyTransform { 9 | pub fn new(max_size_per_message: usize) -> Self { 10 | SnappyTransform { 11 | max_size_per_message, 12 | } 13 | } 14 | } 15 | 16 | impl DataTransform for SnappyTransform { 17 | fn inbound_transform(&self, raw_message: RawMessage) -> Result { 18 | let len = decompress_len(&raw_message.data)?; 19 | 20 | if len > self.max_size_per_message { 21 | return Err(std::io::Error::new( 22 | std::io::ErrorKind::InvalidData, 23 | format!( 24 | "Message size ({}) exceeds max gossip size per message ({})", 25 | len, self.max_size_per_message 26 | ), 27 | )); 28 | } 29 | 30 | let mut decoder = Decoder::new(); 31 | let data = decoder.decompress_vec(&raw_message.data)?; 32 | 33 | Ok(Message { 34 | source: raw_message.source, 35 | data, 36 | sequence_number: raw_message.sequence_number, 37 | topic: raw_message.topic, 38 | }) 39 | } 40 | 41 | fn outbound_transform( 42 | &self, 43 | _topic: &TopicHash, 44 | data: Vec, 45 | ) -> Result, std::io::Error> { 46 | if data.len() > self.max_size_per_message { 47 | return Err(std::io::Error::new( 48 | std::io::ErrorKind::InvalidData, 49 | format!( 50 | "Message size ({}) exceeds max gossip size per message ({})", 51 | data.len(), 52 | self.max_size_per_message 53 | ), 54 | )); 55 | } 56 | 57 | let mut encoder = Encoder::new(); 58 | let raw_message = encoder.compress_vec(&data)?; 59 | 60 | Ok(raw_message) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod bootnodes; 2 | pub mod channel; 3 | pub mod config; 4 | pub mod constants; 5 | pub mod gossipsub; 6 | pub mod network; 7 | pub mod network_state; 8 | pub mod peer; 9 | pub mod req_resp; 10 | pub mod utils; 11 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/network_state.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, fs, path::PathBuf}; 2 | 3 | use anyhow::anyhow; 4 | use discv5::Enr; 5 | use libp2p::{Multiaddr, PeerId}; 6 | use parking_lot::RwLock; 7 | use ssz::Encode; 8 | 9 | use crate::{ 10 | peer::{CachedPeer, ConnectionState, Direction}, 11 | req_resp::messages::meta_data::GetMetaDataV2, 12 | utils::META_DATA_FILE_NAME, 13 | }; 14 | 15 | pub struct NetworkState { 16 | pub peer_table: RwLock>, 17 | pub meta_data: RwLock, 18 | pub data_dir: PathBuf, 19 | } 20 | 21 | impl NetworkState { 22 | pub fn upsert_peer( 23 | &self, 24 | peer_id: PeerId, 25 | address: Option, 26 | state: ConnectionState, 27 | direction: Direction, 28 | enr: Option, 29 | ) { 30 | let mut peer_table = self.peer_table.write(); 31 | peer_table 32 | .entry(peer_id) 33 | .and_modify(|cached_peer| { 34 | if let Some(address_ref) = &address { 35 | cached_peer.last_seen_p2p_address = Some(address_ref.clone()); 36 | } 37 | cached_peer.state = state; 38 | cached_peer.direction = direction; 39 | if let Some(enr_ref) = &enr { 40 | cached_peer.enr = Some(enr_ref.clone()); 41 | } 42 | }) 43 | .or_insert(CachedPeer { 44 | peer_id, 45 | last_seen_p2p_address: address, 46 | state, 47 | direction, 48 | enr, 49 | meta_data: None, 50 | }); 51 | } 52 | 53 | pub fn write_meta_data_to_disk(&self) -> anyhow::Result<()> { 54 | let meta_data_path = self.data_dir.join(META_DATA_FILE_NAME); 55 | fs::write(meta_data_path, self.meta_data.read().as_ssz_bytes()) 56 | .map_err(|err| anyhow!("Failed to write meta data to disk: {err:?}"))?; 57 | Ok(()) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/peer.rs: -------------------------------------------------------------------------------- 1 | use discv5::Enr; 2 | use libp2p::{Multiaddr, PeerId}; 3 | use serde::Serialize; 4 | 5 | use crate::req_resp::messages::meta_data::GetMetaDataV2; 6 | 7 | #[derive(Debug, Serialize, Clone, Copy, PartialEq, Eq)] 8 | #[serde(rename_all = "lowercase")] 9 | pub enum ConnectionState { 10 | Connected, 11 | Connecting, 12 | Disconnected, 13 | Disconnecting, 14 | } 15 | 16 | #[derive(Debug, Serialize, Clone, Copy, PartialEq, Eq)] 17 | #[serde(rename_all = "lowercase")] 18 | pub enum Direction { 19 | Inbound, 20 | Outbound, 21 | Unknown, 22 | } 23 | 24 | #[derive(Clone, Debug)] 25 | pub struct CachedPeer { 26 | /// libp2p peer ID 27 | pub peer_id: PeerId, 28 | 29 | /// Last known multiaddress observed for the peer 30 | pub last_seen_p2p_address: Option, 31 | 32 | /// Current known connection state 33 | pub state: ConnectionState, 34 | 35 | /// Direction of the most recent connection (inbound/outbound) 36 | pub direction: Direction, 37 | 38 | /// Ethereum Node Record (ENR), if known 39 | pub enr: Option, 40 | 41 | pub meta_data: Option, 42 | } 43 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/req_resp/configurations.rs: -------------------------------------------------------------------------------- 1 | use ssz_types::typenum::{U4, U64}; 2 | 3 | /// The number of attestation subnets used in the gossipsub protocol. 4 | pub type AttestationSubnetCount = U64; 5 | 6 | /// The number of sync committee subnets used in the gossipsub aggregation protocol. 7 | pub type SyncCommitteeSubnetCount = U4; 8 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/req_resp/error.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self}; 2 | 3 | use ssz_types::{VariableList, typenum::U256}; 4 | 5 | #[derive(thiserror::Error, Debug)] 6 | pub enum ReqRespError { 7 | #[error("IO error: {0}")] 8 | IoError(#[from] io::Error), 9 | 10 | #[error("Anyhow error: {0}")] 11 | Anyhow(#[from] anyhow::Error), 12 | 13 | #[error("Invalid data {0}")] 14 | InvalidData(String), 15 | 16 | #[error("Incomplete stream")] 17 | IncompleteStream, 18 | 19 | #[error("Stream timed out {0}")] 20 | StreamTimedOut(#[from] tokio::time::error::Elapsed), 21 | 22 | #[error("Disconnected")] 23 | Disconnected, 24 | 25 | #[error("Raw error message {0}")] 26 | RawError(String), 27 | } 28 | 29 | impl From for ReqRespError { 30 | fn from(err: ssz::DecodeError) -> Self { 31 | ReqRespError::InvalidData(format!("Failed to decode ssz: {err:?}")) 32 | } 33 | } 34 | 35 | impl From> for ReqRespError { 36 | fn from(err: VariableList) -> Self { 37 | let err = String::from_utf8(Vec::from(err)).unwrap_or("Invalid UTF-8".to_string()); 38 | ReqRespError::InvalidData(format!("ReqResp error message from peer: {err:?}")) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/req_resp/messages/beacon_blocks.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::B256; 2 | use ssz_derive::{Decode, Encode}; 3 | use ssz_types::{VariableList, typenum::U1024}; 4 | 5 | #[derive(Debug, Default, Clone, PartialEq, Eq, Encode, Decode)] 6 | pub struct BeaconBlocksByRangeV2Request { 7 | pub start_slot: u64, 8 | pub count: u64, 9 | /// Deprecated, must be set to 1 10 | step: u64, 11 | } 12 | 13 | impl BeaconBlocksByRangeV2Request { 14 | pub fn new(start_slot: u64, count: u64) -> Self { 15 | Self { 16 | start_slot, 17 | count, 18 | step: 1, 19 | } 20 | } 21 | } 22 | 23 | #[derive(Debug, Default, Clone, PartialEq, Eq, Encode, Decode)] 24 | #[ssz(struct_behaviour = "transparent")] 25 | pub struct BeaconBlocksByRootV2Request { 26 | pub inner: VariableList, 27 | } 28 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/req_resp/messages/blob_sidecars.rs: -------------------------------------------------------------------------------- 1 | use ream_consensus::blob_sidecar::BlobIdentifier; 2 | use ssz_derive::{Decode, Encode}; 3 | use ssz_types::{ 4 | VariableList, 5 | typenum::{B0, B1, UInt, UTerm}, 6 | }; 7 | 8 | pub type MaxRequestBlobSidecarsElectra = UInt< 9 | UInt< 10 | UInt< 11 | UInt, B0>, B0>, B1>, B0>, B0>, B0>, B0>, 12 | B0, 13 | >, 14 | B0, 15 | >, 16 | B0, 17 | >; 18 | 19 | #[derive(Debug, Default, Clone, PartialEq, Eq, Encode, Decode)] 20 | pub struct BlobSidecarsByRangeV1Request { 21 | pub start_slot: u64, 22 | pub count: u64, 23 | } 24 | 25 | #[derive(Debug, Default, Clone, PartialEq, Eq, Encode, Decode)] 26 | #[ssz(struct_behaviour = "transparent")] 27 | pub struct BlobSidecarsByRootV1Request { 28 | pub inner: VariableList, 29 | } 30 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/req_resp/messages/meta_data.rs: -------------------------------------------------------------------------------- 1 | use ssz_derive::{Decode, Encode}; 2 | use ssz_types::BitVector; 3 | 4 | use crate::req_resp::configurations::{AttestationSubnetCount, SyncCommitteeSubnetCount}; 5 | 6 | #[derive(Debug, Default, Clone, PartialEq, Eq, Encode, Decode)] 7 | pub struct GetMetaDataV2 { 8 | pub seq_number: u64, 9 | pub attnets: BitVector, 10 | pub syncnets: BitVector, 11 | } 12 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/req_resp/messages/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod beacon_blocks; 2 | pub mod blob_sidecars; 3 | pub mod goodbye; 4 | pub mod meta_data; 5 | pub mod ping; 6 | pub mod status; 7 | 8 | use std::sync::Arc; 9 | 10 | use beacon_blocks::{BeaconBlocksByRangeV2Request, BeaconBlocksByRootV2Request}; 11 | use blob_sidecars::{BlobSidecarsByRangeV1Request, BlobSidecarsByRootV1Request}; 12 | use goodbye::Goodbye; 13 | use meta_data::GetMetaDataV2; 14 | use ping::Ping; 15 | use ream_consensus::{blob_sidecar::BlobSidecar, electra::beacon_block::SignedBeaconBlock}; 16 | use ssz_derive::{Decode, Encode}; 17 | use status::Status; 18 | 19 | #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] 20 | #[ssz(enum_behaviour = "transparent")] 21 | pub enum RequestMessage { 22 | MetaData(Arc), 23 | Goodbye(Goodbye), 24 | Status(Status), 25 | Ping(Ping), 26 | BeaconBlocksByRange(BeaconBlocksByRangeV2Request), 27 | BeaconBlocksByRoot(BeaconBlocksByRootV2Request), 28 | BlobSidecarsByRange(BlobSidecarsByRangeV1Request), 29 | BlobSidecarsByRoot(BlobSidecarsByRootV1Request), 30 | } 31 | 32 | #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] 33 | #[ssz(enum_behaviour = "transparent")] 34 | pub enum ResponseMessage { 35 | MetaData(Arc), 36 | Goodbye(Goodbye), 37 | Status(Status), 38 | Ping(Ping), 39 | BeaconBlocksByRange(SignedBeaconBlock), 40 | BeaconBlocksByRoot(SignedBeaconBlock), 41 | BlobSidecarsByRange(BlobSidecar), 42 | BlobSidecarsByRoot(BlobSidecar), 43 | } 44 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/req_resp/messages/ping.rs: -------------------------------------------------------------------------------- 1 | use ssz_derive::{Decode, Encode}; 2 | 3 | #[derive(Debug, Default, Clone, PartialEq, Eq, Encode, Decode)] 4 | #[ssz(struct_behaviour = "transparent")] 5 | pub struct Ping { 6 | pub sequence_number: u64, 7 | } 8 | 9 | impl Ping { 10 | pub fn new(sequence_number: u64) -> Self { 11 | Self { sequence_number } 12 | } 13 | } 14 | 15 | #[cfg(test)] 16 | mod tests { 17 | use ssz::{Decode, Encode}; 18 | 19 | use super::*; 20 | 21 | #[test] 22 | fn test_ping_encode_decode() { 23 | let ping = Ping { 24 | sequence_number: 42, 25 | }; 26 | let encoded = ping.as_ssz_bytes(); 27 | let decoded = Ping::from_ssz_bytes(&encoded).unwrap(); 28 | assert_eq!(ping, decoded); 29 | let sequence_number = ping.sequence_number; 30 | assert_eq!(sequence_number.as_ssz_bytes(), ping.as_ssz_bytes()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/req_resp/messages/status.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::{B256, aliases::B32}; 2 | use ssz_derive::{Decode, Encode}; 3 | 4 | #[derive(Debug, Default, Clone, PartialEq, Eq, Encode, Decode)] 5 | pub struct Status { 6 | pub fork_digest: B32, 7 | pub finalized_root: B256, 8 | pub finalized_epoch: u64, 9 | pub head_root: B256, 10 | pub head_slot: u64, 11 | } 12 | -------------------------------------------------------------------------------- /crates/networking/p2p/src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::{cmp::max, fs, path::PathBuf}; 2 | 3 | use anyhow::anyhow; 4 | use ssz::Decode; 5 | 6 | use crate::{constants::MAX_PAYLOAD_SIZE, req_resp::messages::meta_data::GetMetaDataV2}; 7 | 8 | pub const META_DATA_FILE_NAME: &str = "meta_data.ssz"; 9 | 10 | /// Worst-case compressed length for a given payload of size n when using snappy: 11 | /// https://github.com/google/snappy/blob/32ded457c0b1fe78ceb8397632c416568d6714a0/snappy.cc#L218C1-L218C47 12 | pub fn max_compressed_len(n: u64) -> u64 { 13 | 32 + n + n / 6 14 | } 15 | 16 | /// Allow 1024 bytes for framing and encoding overhead but at least 1MiB in case MAX_PAYLOAD_SIZE is 17 | /// small. 18 | pub fn max_message_size() -> u64 { 19 | max(max_compressed_len(MAX_PAYLOAD_SIZE) + 1024, 1024 * 1024) 20 | } 21 | 22 | pub fn read_meta_data_from_disk(path: PathBuf) -> anyhow::Result { 23 | let meta_data_path = path.join(META_DATA_FILE_NAME); 24 | if !meta_data_path.exists() { 25 | return Ok(GetMetaDataV2::default()); 26 | } 27 | 28 | GetMetaDataV2::from_ssz_bytes(&fs::read(meta_data_path)?) 29 | .map_err(|err| anyhow!("Failed to decode meta data: {err:?}")) 30 | } 31 | -------------------------------------------------------------------------------- /crates/networking/syncer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-syncer" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | anyhow.workspace = true 14 | tokio.workspace = true 15 | 16 | # ream dependencies 17 | ream-beacon-chain.workspace = true 18 | ream-p2p.workspace = true 19 | -------------------------------------------------------------------------------- /crates/networking/syncer/src/block_range/mod.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ream_beacon_chain::beacon_chain::BeaconChain; 4 | use ream_p2p::channel::P2PMessage; 5 | use tokio::sync::mpsc::UnboundedSender; 6 | 7 | pub struct BlockRangeSyncer { 8 | pub beacon_chain: Arc, 9 | pub p2p_sender: UnboundedSender, 10 | } 11 | 12 | impl BlockRangeSyncer { 13 | pub fn new(beacon_chain: Arc, p2p_sender: UnboundedSender) -> Self { 14 | Self { 15 | beacon_chain, 16 | p2p_sender, 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /crates/networking/syncer/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod block_range; 2 | -------------------------------------------------------------------------------- /crates/rpc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-rpc" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | actix-web.workspace = true 14 | actix-web-lab.workspace = true 15 | alloy-primitives.workspace = true 16 | discv5.workspace = true 17 | ethereum_serde_utils.workspace = true 18 | ethereum_ssz.workspace = true 19 | ethereum_ssz_derive.workspace = true 20 | hashbrown.workspace = true 21 | libp2p.workspace = true 22 | parking_lot.workspace = true 23 | serde.workspace = true 24 | serde_json.workspace = true 25 | ssz_types.workspace = true 26 | thiserror.workspace = true 27 | tracing.workspace = true 28 | tree_hash.workspace = true 29 | 30 | #ream-dependencies 31 | ream-beacon-api-types.workspace = true 32 | ream-bls.workspace = true 33 | ream-consensus.workspace = true 34 | ream-fork-choice.workspace = true 35 | ream-light-client.workspace = true 36 | ream-network-spec.workspace = true 37 | ream-node.workspace = true 38 | ream-p2p.workspace = true 39 | ream-storage.workspace = true 40 | -------------------------------------------------------------------------------- /crates/rpc/src/config.rs: -------------------------------------------------------------------------------- 1 | use std::net::{IpAddr, SocketAddr}; 2 | 3 | #[derive(Debug, Clone)] 4 | pub struct RpcServerConfig { 5 | pub http_socket_address: SocketAddr, 6 | pub http_allow_origin: bool, 7 | } 8 | 9 | impl RpcServerConfig { 10 | /// Creates a new instance from CLI arguments 11 | pub fn new(http_address: IpAddr, http_port: u16, http_allow_origin: bool) -> Self { 12 | Self { 13 | http_socket_address: SocketAddr::new(http_address, http_port), 14 | http_allow_origin, 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /crates/rpc/src/handlers/light_client.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{ 2 | HttpResponse, Responder, get, 3 | web::{Data, Path}, 4 | }; 5 | use alloy_primitives::B256; 6 | use ream_beacon_api_types::{error::ApiError, responses::DataVersionedResponse}; 7 | use ream_light_client::bootstrap::LightClientBootstrap; 8 | use ream_storage::{db::ReamDB, tables::Table}; 9 | use tracing::error; 10 | 11 | #[get("/beacon/light_client/bootstrap/{block_root}")] 12 | pub async fn get_light_client_bootstrap( 13 | db: Data, 14 | block_root: Path, 15 | ) -> Result { 16 | let block_root = block_root.into_inner(); 17 | let beacon_block = db 18 | .beacon_block_provider() 19 | .get(block_root) 20 | .map_err(|err| { 21 | error!("Failed to get block by block_root, error: {err:?}"); 22 | ApiError::InternalError 23 | })? 24 | .ok_or_else(|| { 25 | ApiError::NotFound(format!("Failed to find `beacon block` from {block_root:?}")) 26 | })?; 27 | 28 | let beacon_state = db 29 | .beacon_state_provider() 30 | .get(block_root) 31 | .map_err(|_| ApiError::InternalError)? 32 | .ok_or(ApiError::NotFound(format!( 33 | "Failed to find `beacon_state` from {block_root:?}" 34 | )))?; 35 | 36 | let light_client_bootstrap = 37 | LightClientBootstrap::new(&beacon_state, &beacon_block).map_err(|err| { 38 | error!("Failed to create light client bootstrap, error: {err:?}"); 39 | ApiError::InternalError 40 | })?; 41 | 42 | Ok(HttpResponse::Ok().json(DataVersionedResponse::new(light_client_bootstrap))) 43 | } 44 | -------------------------------------------------------------------------------- /crates/rpc/src/handlers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod blob_sidecar; 2 | pub mod block; 3 | pub mod committee; 4 | pub mod config; 5 | pub mod duties; 6 | pub mod header; 7 | pub mod light_client; 8 | pub mod peers; 9 | pub mod state; 10 | pub mod validator; 11 | pub mod version; 12 | -------------------------------------------------------------------------------- /crates/rpc/src/handlers/peers.rs: -------------------------------------------------------------------------------- 1 | use std::{str::FromStr, sync::Arc}; 2 | 3 | use actix_web::{ 4 | HttpResponse, Responder, get, 5 | web::{Data, Path}, 6 | }; 7 | use discv5::Enr; 8 | use libp2p::{Multiaddr, PeerId}; 9 | use ream_beacon_api_types::{error::ApiError, responses::DataResponse}; 10 | use ream_p2p::{ 11 | network_state::NetworkState, 12 | peer::{ConnectionState, Direction}, 13 | }; 14 | use serde::Serialize; 15 | 16 | /// GET /eth/v1/node/peers/{peer_id} 17 | #[get("/node/peers/{peer_id}")] 18 | pub async fn get_peer( 19 | network_state: Data>, 20 | peer_id: Path, 21 | ) -> Result { 22 | let peer_id = peer_id.into_inner(); 23 | let peer_id = PeerId::from_str(&peer_id).map_err(|err| { 24 | ApiError::BadRequest(format!("Invalid PeerId format: {peer_id}, {err:?}")) 25 | })?; 26 | 27 | let cached_peer = network_state 28 | .peer_table 29 | .read() 30 | .get(&peer_id) 31 | .cloned() 32 | .ok_or_else(|| ApiError::NotFound(format!("Peer not found: {peer_id}")))?; 33 | 34 | Ok(HttpResponse::Ok().json(DataResponse::new(&Peer { 35 | peer_id: cached_peer.peer_id, 36 | last_seen_p2p_address: cached_peer.last_seen_p2p_address, 37 | state: cached_peer.state, 38 | direction: cached_peer.direction, 39 | enr: cached_peer.enr, 40 | }))) 41 | } 42 | 43 | #[derive(Clone, Debug, Serialize)] 44 | pub struct Peer { 45 | /// libp2p peer ID 46 | pub peer_id: PeerId, 47 | 48 | /// Last known multiaddress observed for the peer 49 | #[serde(skip_serializing_if = "Option::is_none")] 50 | pub last_seen_p2p_address: Option, 51 | 52 | /// Current known connection state 53 | pub state: ConnectionState, 54 | 55 | /// Direction of the most recent connection (inbound/outbound) 56 | pub direction: Direction, 57 | 58 | /// Ethereum Node Record (ENR), if known 59 | #[serde(skip_serializing_if = "Option::is_none")] 60 | pub enr: Option, 61 | } 62 | -------------------------------------------------------------------------------- /crates/rpc/src/handlers/version.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{HttpResponse, Responder, get}; 2 | use ream_beacon_api_types::{error::ApiError, responses::DataResponse}; 3 | use ream_node::version::ream_node_version; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Serialize, Deserialize, Default)] 7 | pub struct Version { 8 | version: String, 9 | } 10 | 11 | impl Version { 12 | pub fn new() -> Self { 13 | Self { 14 | version: ream_node_version(), 15 | } 16 | } 17 | } 18 | 19 | /// Called by `eth/v1/node/version` to get the Node Version. 20 | #[get("/node/version")] 21 | pub async fn get_version() -> Result { 22 | Ok(HttpResponse::Ok().json(DataResponse::new(Version::new()))) 23 | } 24 | -------------------------------------------------------------------------------- /crates/rpc/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use actix_web::{App, HttpServer, dev::ServerHandle, middleware, web::Data}; 4 | use config::RpcServerConfig; 5 | use ream_p2p::network_state::NetworkState; 6 | use ream_storage::db::ReamDB; 7 | use tracing::info; 8 | 9 | use crate::routes::register_routers; 10 | 11 | pub mod config; 12 | pub mod handlers; 13 | pub mod routes; 14 | 15 | /// Start the Beacon API server. 16 | pub async fn start_server( 17 | server_config: RpcServerConfig, 18 | db: ReamDB, 19 | network_state: Arc, 20 | ) -> std::io::Result<()> { 21 | info!( 22 | "starting HTTP server on {:?}", 23 | server_config.http_socket_address 24 | ); 25 | // create the stop handle container 26 | let stop_handle = Data::new(StopHandle::default()); 27 | 28 | let server = HttpServer::new(move || { 29 | let stop_handle = stop_handle.clone(); 30 | App::new() 31 | .wrap(middleware::Logger::default()) 32 | .app_data(stop_handle) 33 | .app_data(Data::new(db.clone())) 34 | .app_data(Data::new(network_state.clone())) 35 | .configure(register_routers) 36 | }) 37 | .bind(server_config.http_socket_address)? 38 | .run(); 39 | 40 | server.await 41 | } 42 | 43 | #[derive(Default)] 44 | struct StopHandle { 45 | inner: parking_lot::Mutex>, 46 | } 47 | 48 | #[allow(dead_code)] 49 | impl StopHandle { 50 | /// Sets the server handle to stop. 51 | pub(crate) fn register(&self, handle: ServerHandle) { 52 | *self.inner.lock() = Some(handle); 53 | } 54 | 55 | /// Sends stop signal through contained server handle. 56 | pub(crate) fn stop(&self, graceful: bool) { 57 | #[allow(clippy::let_underscore_future)] 58 | let _ = self.inner.lock().as_ref().unwrap().stop(graceful); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /crates/rpc/src/routes/beacon.rs: -------------------------------------------------------------------------------- 1 | use actix_web::web::ServiceConfig; 2 | 3 | use crate::handlers::{ 4 | blob_sidecar::get_blob_sidecars, 5 | block::{ 6 | get_block_attestations, get_block_from_id, get_block_rewards, get_block_root, get_genesis, 7 | }, 8 | committee::get_committees, 9 | header::{get_headers, get_headers_from_block}, 10 | state::{ 11 | get_pending_consolidations, get_pending_deposits, get_pending_partial_withdrawals, 12 | get_state_finality_checkpoint, get_state_fork, get_state_randao, get_state_root, 13 | get_sync_committees, 14 | }, 15 | validator::{ 16 | get_validator_balances_from_state, get_validator_from_state, get_validators_from_state, 17 | post_validator_balances_from_state, post_validator_identities_from_state, 18 | post_validators_from_state, 19 | }, 20 | }; 21 | 22 | /// Creates and returns all `/beacon` routes. 23 | pub fn register_beacon_routes(cfg: &mut ServiceConfig) { 24 | cfg.service(get_blob_sidecars) 25 | .service(get_block_rewards) 26 | .service(get_block_root) 27 | .service(get_committees) 28 | .service(get_genesis) 29 | .service(get_headers) 30 | .service(get_headers_from_block) 31 | .service(get_pending_consolidations) 32 | .service(get_pending_deposits) 33 | .service(get_pending_partial_withdrawals) 34 | .service(get_sync_committees) 35 | .service(get_state_finality_checkpoint) 36 | .service(get_state_fork) 37 | .service(get_state_randao) 38 | .service(get_state_root) 39 | .service(get_validator_from_state) 40 | .service(get_validators_from_state) 41 | .service(post_validator_identities_from_state) 42 | .service(post_validators_from_state) 43 | .service(get_validator_balances_from_state) 44 | .service(post_validator_balances_from_state); 45 | } 46 | 47 | pub fn register_beacon_routes_v2(cfg: &mut ServiceConfig) { 48 | cfg.service(get_block_attestations) 49 | .service(get_block_from_id); 50 | } 51 | -------------------------------------------------------------------------------- /crates/rpc/src/routes/config.rs: -------------------------------------------------------------------------------- 1 | use actix_web::web::ServiceConfig; 2 | 3 | use crate::handlers::config::{get_config_deposit_contract, get_config_spec, get_fork_schedule}; 4 | 5 | pub fn register_config_routes(cfg: &mut ServiceConfig) { 6 | cfg.service(get_config_spec) 7 | .service(get_config_deposit_contract) 8 | .service(get_fork_schedule); 9 | } 10 | -------------------------------------------------------------------------------- /crates/rpc/src/routes/debug.rs: -------------------------------------------------------------------------------- 1 | use actix_web::web::{ServiceConfig, scope}; 2 | 3 | use crate::handlers::{block::get_beacon_heads, state::get_beacon_state}; 4 | 5 | pub fn register_debug_routes(cfg: &mut ServiceConfig) { 6 | cfg.service(scope("/debug").service(get_beacon_heads)); 7 | } 8 | 9 | pub fn register_debug_routes_v2(cfg: &mut ServiceConfig) { 10 | cfg.service(scope("/debug").service(get_beacon_state)); 11 | } 12 | -------------------------------------------------------------------------------- /crates/rpc/src/routes/mod.rs: -------------------------------------------------------------------------------- 1 | use actix_web::web::{ServiceConfig, scope}; 2 | 3 | pub mod beacon; 4 | pub mod config; 5 | pub mod debug; 6 | pub mod node; 7 | pub mod validator; 8 | 9 | pub fn get_v1_routes(config: &mut ServiceConfig) { 10 | config.service( 11 | scope("/eth/v1") 12 | .configure(beacon::register_beacon_routes) 13 | .configure(debug::register_debug_routes) 14 | .configure(node::register_node_routes) 15 | .configure(config::register_config_routes) 16 | .configure(validator::register_validator_routes), 17 | ); 18 | } 19 | 20 | pub fn get_v2_routes(config: &mut ServiceConfig) { 21 | config.service( 22 | scope("/eth/v2") 23 | .configure(debug::register_debug_routes_v2) 24 | .configure(beacon::register_beacon_routes_v2), 25 | ); 26 | } 27 | 28 | pub fn register_routers(config: &mut ServiceConfig) { 29 | config.configure(get_v1_routes).configure(get_v2_routes); 30 | } 31 | -------------------------------------------------------------------------------- /crates/rpc/src/routes/node.rs: -------------------------------------------------------------------------------- 1 | use actix_web::web::ServiceConfig; 2 | 3 | use crate::handlers::{peers::get_peer, version::get_version}; 4 | 5 | pub fn register_node_routes(cfg: &mut ServiceConfig) { 6 | cfg.service(get_version).service(get_peer); 7 | } 8 | -------------------------------------------------------------------------------- /crates/rpc/src/routes/validator.rs: -------------------------------------------------------------------------------- 1 | use actix_web::web::ServiceConfig; 2 | 3 | use crate::handlers::duties::{get_attester_duties, get_proposer_duties}; 4 | 5 | pub fn register_validator_routes(config: &mut ServiceConfig) { 6 | config.service(get_proposer_duties); 7 | config.service(get_attester_duties); 8 | } 9 | -------------------------------------------------------------------------------- /crates/runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-runtime" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | -------------------------------------------------------------------------------- /crates/runtime/README.md: -------------------------------------------------------------------------------- 1 | # ream-runtime 2 | 3 | An abstraction for using different zkVMs -------------------------------------------------------------------------------- /crates/runtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn add(left: u64, right: u64) -> u64 { 2 | left + right 3 | } 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | use super::*; 8 | 9 | #[test] 10 | fn it_works() { 11 | let result = add(2, 2); 12 | assert_eq!(result, 4); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /crates/storage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ream-storage" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | alloy-primitives.workspace = true 14 | anyhow.workspace = true 15 | directories.workspace = true 16 | ethereum_ssz.workspace = true 17 | redb.workspace = true 18 | tempfile.workspace = true 19 | thiserror.workspace = true 20 | tracing.workspace = true 21 | tree_hash.workspace = true 22 | 23 | # ream dependencies 24 | ream-consensus.workspace = true 25 | -------------------------------------------------------------------------------- /crates/storage/src/dir.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs, io, path::PathBuf}; 2 | 3 | use directories::ProjectDirs; 4 | use tempfile::TempDir; 5 | use tracing::debug; 6 | 7 | /// Setup applications data directory. 8 | /// 9 | /// - If `ephemeral` is set, it will create temporary directory, either in `data_dir` (if provided) 10 | /// or in operating system temp directory. 11 | /// - Otherwise, it uses `data_dir` if set. 12 | /// - Lastly, if neither are set, it will use operating system default application local data 13 | /// directory. 14 | pub fn setup_data_dir( 15 | app_name: &str, 16 | data_dir: Option, 17 | ephemeral: bool, 18 | ) -> io::Result { 19 | if ephemeral { 20 | return create_temp_dir(app_name, data_dir).map(TempDir::into_path); 21 | } 22 | let data_dir = match data_dir { 23 | Some(data_dir) => data_dir, 24 | None => ProjectDirs::from("", "", app_name) 25 | .map(|proj_dirs| proj_dirs.data_local_dir().to_path_buf()) 26 | .ok_or_else(|| io::Error::other("No valid default directory."))?, 27 | }; 28 | fs::create_dir_all(&data_dir)?; 29 | Ok(data_dir) 30 | } 31 | 32 | /// Create a random named directory that is deleted once it goes out of scope. 33 | /// 34 | /// The location of the directory can be controlled by `dir` param: 35 | /// 36 | /// - if `None`, it will be located under OS's temporary directory, e.g. on Linux: 37 | /// `/tmp/{app_name}/{random_name}` 38 | /// - if `Some(root)`, it will be `{root}/{app_name}/{random_name}` 39 | pub fn create_temp_dir(app_name: &str, root: Option) -> io::Result { 40 | let temp_dir = root.unwrap_or_else(env::temp_dir).join(app_name); 41 | debug!("Creating temp dir: {temp_dir:?}"); 42 | fs::create_dir_all(&temp_dir)?; 43 | TempDir::new_in(&temp_dir) 44 | } 45 | -------------------------------------------------------------------------------- /crates/storage/src/errors.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | #[derive(Error, Debug)] 4 | pub enum StoreError { 5 | #[error("Redb error: {0}")] 6 | Redb(#[from] Box), 7 | 8 | #[error("Io error in creating DB file: {0}")] 9 | Io(#[from] std::io::Error), 10 | 11 | #[error("Field not initilized")] 12 | FieldNotInitilized, 13 | } 14 | 15 | impl From for StoreError { 16 | fn from(err: redb::Error) -> Self { 17 | StoreError::Redb(Box::new(err)) 18 | } 19 | } 20 | 21 | impl From for StoreError { 22 | fn from(err: redb::TransactionError) -> Self { 23 | StoreError::Redb(Box::new(err.into())) 24 | } 25 | } 26 | 27 | impl From for StoreError { 28 | fn from(err: redb::TableError) -> Self { 29 | StoreError::Redb(Box::new(err.into())) 30 | } 31 | } 32 | 33 | impl From for StoreError { 34 | fn from(err: redb::CommitError) -> Self { 35 | StoreError::Redb(Box::new(err.into())) 36 | } 37 | } 38 | 39 | impl From for StoreError { 40 | fn from(err: redb::StorageError) -> Self { 41 | StoreError::Redb(Box::new(err.into())) 42 | } 43 | } 44 | 45 | impl From for StoreError { 46 | fn from(err: redb::DatabaseError) -> Self { 47 | StoreError::Redb(Box::new(err.into())) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /crates/storage/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod db; 2 | pub mod dir; 3 | pub mod errors; 4 | pub mod tables; 5 | -------------------------------------------------------------------------------- /crates/storage/src/tables/beacon_state.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use alloy_primitives::B256; 4 | use ream_consensus::electra::beacon_state::BeaconState; 5 | use redb::{Database, Durability, ReadableTable, TableDefinition}; 6 | 7 | use super::{SSZEncoding, Table}; 8 | use crate::errors::StoreError; 9 | 10 | /// Table definition for the Beacon State table 11 | /// 12 | /// Key: block_root 13 | /// Value: BeaconState 14 | pub const BEACON_STATE_TABLE: TableDefinition, SSZEncoding> = 15 | TableDefinition::new("beacon_state"); 16 | 17 | pub struct BeaconStateTable { 18 | pub db: Arc, 19 | } 20 | 21 | impl Table for BeaconStateTable { 22 | type Key = B256; 23 | 24 | type Value = BeaconState; 25 | 26 | fn get(&self, key: Self::Key) -> Result, StoreError> { 27 | let read_txn = self.db.begin_read()?; 28 | 29 | let table = read_txn.open_table(BEACON_STATE_TABLE)?; 30 | let result = table.get(key)?; 31 | Ok(result.map(|res| res.value())) 32 | } 33 | 34 | fn insert(&self, key: Self::Key, value: Self::Value) -> Result<(), StoreError> { 35 | let mut write_txn = self.db.begin_write()?; 36 | write_txn.set_durability(Durability::Immediate); 37 | let mut table = write_txn.open_table(BEACON_STATE_TABLE)?; 38 | table.insert(key, value)?; 39 | drop(table); 40 | write_txn.commit()?; 41 | Ok(()) 42 | } 43 | } 44 | 45 | impl BeaconStateTable { 46 | pub fn first(&self) -> Result, StoreError> { 47 | let read_txn = self.db.begin_read()?; 48 | 49 | let table = read_txn.open_table(BEACON_STATE_TABLE)?; 50 | let result = table.first()?; 51 | Ok(result.map(|res| res.1.value())) 52 | } 53 | 54 | pub fn last(&self) -> Result, StoreError> { 55 | let read_txn = self.db.begin_read()?; 56 | 57 | let table = read_txn.open_table(BEACON_STATE_TABLE)?; 58 | let result = table.last()?; 59 | Ok(result.map(|res| res.1.value())) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /crates/storage/src/tables/blobs_and_proofs.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ream_consensus::{ 4 | blob_sidecar::BlobIdentifier, execution_engine::rpc_types::get_blobs::BlobAndProofV1, 5 | }; 6 | use redb::{Database, Durability, TableDefinition}; 7 | 8 | use super::{SSZEncoding, Table}; 9 | use crate::errors::StoreError; 10 | 11 | /// Table definition for the Blobs And Proofs table 12 | /// 13 | /// Key: BlobIdentifier 14 | /// Value: BlobAndProofV1 15 | pub const BLOBS_AND_PROOFS_TABLE: TableDefinition< 16 | SSZEncoding, 17 | SSZEncoding, 18 | > = TableDefinition::new("blobs_and_proofs"); 19 | 20 | pub struct BlobsAndProofsTable { 21 | pub db: Arc, 22 | } 23 | 24 | impl Table for BlobsAndProofsTable { 25 | type Key = BlobIdentifier; 26 | 27 | type Value = BlobAndProofV1; 28 | 29 | fn get(&self, key: Self::Key) -> Result, StoreError> { 30 | let read_txn = self.db.begin_read()?; 31 | 32 | let table = read_txn.open_table(BLOBS_AND_PROOFS_TABLE)?; 33 | let result = table.get(key)?; 34 | Ok(result.map(|res| res.value())) 35 | } 36 | 37 | fn insert(&self, key: Self::Key, value: Self::Value) -> Result<(), StoreError> { 38 | let mut write_txn = self.db.begin_write()?; 39 | write_txn.set_durability(Durability::Immediate); 40 | let mut table = write_txn.open_table(BLOBS_AND_PROOFS_TABLE)?; 41 | table.insert(key, value)?; 42 | drop(table); 43 | write_txn.commit()?; 44 | Ok(()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /crates/storage/src/tables/block_timeliness.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use alloy_primitives::B256; 4 | use redb::{Database, Durability, TableDefinition}; 5 | 6 | use super::{SSZEncoding, Table}; 7 | use crate::errors::StoreError; 8 | 9 | /// Table definition for the Block Timeliness table 10 | /// 11 | /// Key: block_timeliness 12 | /// Value: bool 13 | pub const BLOCK_TIMELINESS_TABLE: TableDefinition, SSZEncoding> = 14 | TableDefinition::new("block_timeliness"); 15 | 16 | pub struct BlockTimelinessTable { 17 | pub db: Arc, 18 | } 19 | 20 | impl Table for BlockTimelinessTable { 21 | type Key = B256; 22 | 23 | type Value = bool; 24 | 25 | fn get(&self, key: Self::Key) -> Result, StoreError> { 26 | let read_txn = self.db.begin_read()?; 27 | 28 | let table = read_txn.open_table(BLOCK_TIMELINESS_TABLE)?; 29 | let result = table.get(key)?; 30 | Ok(result.map(|res| res.value())) 31 | } 32 | 33 | fn insert(&self, key: Self::Key, value: Self::Value) -> Result<(), StoreError> { 34 | let mut write_txn = self.db.begin_write()?; 35 | write_txn.set_durability(Durability::Immediate); 36 | let mut table = write_txn.open_table(BLOCK_TIMELINESS_TABLE)?; 37 | table.insert(key, value)?; 38 | drop(table); 39 | write_txn.commit()?; 40 | Ok(()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/storage/src/tables/checkpoint_states.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ream_consensus::{checkpoint::Checkpoint, electra::beacon_state::BeaconState}; 4 | use redb::{Database, Durability, TableDefinition}; 5 | 6 | use super::{SSZEncoding, Table}; 7 | use crate::errors::StoreError; 8 | 9 | /// Table definition for the Checkpoint States table 10 | /// 11 | /// Key: checkpoint_states 12 | /// Value: BeaconState 13 | pub const CHECKPOINT_STATES_TABLE: TableDefinition< 14 | SSZEncoding, 15 | SSZEncoding, 16 | > = TableDefinition::new("checkpoint_states"); 17 | 18 | pub struct CheckpointStatesTable { 19 | pub db: Arc, 20 | } 21 | 22 | impl Table for CheckpointStatesTable { 23 | type Key = Checkpoint; 24 | 25 | type Value = BeaconState; 26 | 27 | fn get(&self, key: Self::Key) -> Result, StoreError> { 28 | let read_txn = self.db.begin_read()?; 29 | 30 | let table = read_txn.open_table(CHECKPOINT_STATES_TABLE)?; 31 | let result = table.get(key)?; 32 | Ok(result.map(|res| res.value())) 33 | } 34 | 35 | fn insert(&self, key: Self::Key, value: Self::Value) -> Result<(), StoreError> { 36 | let mut write_txn = self.db.begin_write()?; 37 | write_txn.set_durability(Durability::Immediate); 38 | let mut table = write_txn.open_table(CHECKPOINT_STATES_TABLE)?; 39 | table.insert(key, value)?; 40 | drop(table); 41 | write_txn.commit()?; 42 | Ok(()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /crates/storage/src/tables/equivocating_indices.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use alloy_primitives::map::HashSet; 4 | use redb::{Database, Durability, TableDefinition}; 5 | 6 | use super::Field; 7 | use crate::errors::StoreError; 8 | 9 | /// Table definition for the Equivocating_Indices table 10 | /// 11 | /// Value: Vec 12 | pub const EQUIVOCATING_INDICES_FIELD: TableDefinition<&str, Vec> = 13 | TableDefinition::new("equivocating_indices"); 14 | 15 | pub const EQUIVOCATING_INDICES_KEY: &str = "equivocating_indices_key"; 16 | 17 | pub struct EquivocatingIndicesField { 18 | pub db: Arc, 19 | } 20 | 21 | impl Field for EquivocatingIndicesField { 22 | type Value = HashSet; 23 | 24 | fn get(&self) -> Result { 25 | let read_txn = self.db.begin_read()?; 26 | 27 | let table = read_txn.open_table(EQUIVOCATING_INDICES_FIELD)?; 28 | let result = table 29 | .get(EQUIVOCATING_INDICES_KEY)? 30 | .ok_or(StoreError::FieldNotInitilized)?; 31 | Ok(result.value().into_iter().collect()) 32 | } 33 | 34 | fn insert(&self, value: Self::Value) -> Result<(), StoreError> { 35 | let mut write_txn = self.db.begin_write()?; 36 | write_txn.set_durability(Durability::Immediate); 37 | let mut table = write_txn.open_table(EQUIVOCATING_INDICES_FIELD)?; 38 | table.insert( 39 | EQUIVOCATING_INDICES_KEY, 40 | value.into_iter().collect::>(), 41 | )?; 42 | drop(table); 43 | write_txn.commit()?; 44 | Ok(()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /crates/storage/src/tables/finalized_checkpoint.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ream_consensus::checkpoint::Checkpoint; 4 | use redb::{Database, Durability, TableDefinition}; 5 | 6 | use super::{Field, SSZEncoding}; 7 | use crate::errors::StoreError; 8 | 9 | /// Table definition for the Finalized_Checkpoint table 10 | /// 11 | /// Value: Checkpoint 12 | pub const FINALIZED_CHECKPOINT_FIELD: TableDefinition<&str, SSZEncoding> = 13 | TableDefinition::new("finalized_checkpoint"); 14 | 15 | pub const FINALIZED_CHECKPOINT_FIELD_KEY: &str = "finalized_checkpoint_key"; 16 | 17 | pub struct FinalizedCheckpointField { 18 | pub db: Arc, 19 | } 20 | 21 | impl Field for FinalizedCheckpointField { 22 | type Value = Checkpoint; 23 | 24 | fn get(&self) -> Result { 25 | let read_txn = self.db.begin_read()?; 26 | 27 | let table = read_txn.open_table(FINALIZED_CHECKPOINT_FIELD)?; 28 | let result = table 29 | .get(FINALIZED_CHECKPOINT_FIELD_KEY)? 30 | .ok_or(StoreError::FieldNotInitilized)?; 31 | Ok(result.value()) 32 | } 33 | 34 | fn insert(&self, value: Self::Value) -> Result<(), StoreError> { 35 | let mut write_txn = self.db.begin_write()?; 36 | write_txn.set_durability(Durability::Immediate); 37 | let mut table = write_txn.open_table(FINALIZED_CHECKPOINT_FIELD)?; 38 | table.insert(FINALIZED_CHECKPOINT_FIELD_KEY, value)?; 39 | drop(table); 40 | write_txn.commit()?; 41 | Ok(()) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/storage/src/tables/genesis_time.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use redb::{Database, Durability, TableDefinition}; 4 | 5 | use super::Field; 6 | use crate::errors::StoreError; 7 | 8 | /// Table definition for the Genesis_Time table 9 | /// 10 | /// Value: u64 11 | pub const GENESIS_TIME_FIELD: TableDefinition<&str, u64> = TableDefinition::new("genesis_time"); 12 | 13 | pub const GENESIS_TIME_KEY: &str = "genesis_time_key"; 14 | 15 | pub struct GenesisTimeField { 16 | pub db: Arc, 17 | } 18 | 19 | impl Field for GenesisTimeField { 20 | type Value = u64; 21 | 22 | fn get(&self) -> Result { 23 | let read_txn = self.db.begin_read()?; 24 | 25 | let table = read_txn.open_table(GENESIS_TIME_FIELD)?; 26 | let result = table 27 | .get(GENESIS_TIME_KEY)? 28 | .ok_or(StoreError::FieldNotInitilized)?; 29 | Ok(result.value()) 30 | } 31 | 32 | fn insert(&self, value: Self::Value) -> Result<(), StoreError> { 33 | let mut write_txn = self.db.begin_write()?; 34 | write_txn.set_durability(Durability::Immediate); 35 | let mut table = write_txn.open_table(GENESIS_TIME_FIELD)?; 36 | table.insert(GENESIS_TIME_KEY, value)?; 37 | drop(table); 38 | write_txn.commit()?; 39 | Ok(()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /crates/storage/src/tables/justified_checkpoint.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ream_consensus::checkpoint::Checkpoint; 4 | use redb::{Database, Durability, TableDefinition}; 5 | 6 | use super::{Field, SSZEncoding}; 7 | use crate::errors::StoreError; 8 | 9 | /// Table definition for the Justified_Checkpoint table 10 | /// 11 | /// Value: Checkpoint 12 | pub const JUSTIFIED_CHECKPOINT_FIELD: TableDefinition<&str, SSZEncoding> = 13 | TableDefinition::new("justified_checkpoint"); 14 | 15 | pub const JUSTIFIED_CHECKPOINT_KEY: &str = "justified_checkpoint_key"; 16 | 17 | pub struct JustifiedCheckpointField { 18 | pub db: Arc, 19 | } 20 | 21 | impl Field for JustifiedCheckpointField { 22 | type Value = Checkpoint; 23 | 24 | fn get(&self) -> Result { 25 | let read_txn = self.db.begin_read()?; 26 | 27 | let table = read_txn.open_table(JUSTIFIED_CHECKPOINT_FIELD)?; 28 | let result = table 29 | .get(JUSTIFIED_CHECKPOINT_KEY)? 30 | .ok_or(StoreError::FieldNotInitilized)?; 31 | Ok(result.value()) 32 | } 33 | 34 | fn insert(&self, value: Self::Value) -> Result<(), StoreError> { 35 | let mut write_txn = self.db.begin_write()?; 36 | write_txn.set_durability(Durability::Immediate); 37 | let mut table = write_txn.open_table(JUSTIFIED_CHECKPOINT_FIELD)?; 38 | table.insert(JUSTIFIED_CHECKPOINT_KEY, value)?; 39 | drop(table); 40 | write_txn.commit()?; 41 | Ok(()) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/storage/src/tables/latest_messages.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ream_consensus::fork_choice::latest_message::LatestMessage; 4 | use redb::{Database, Durability, TableDefinition}; 5 | 6 | use super::{SSZEncoding, Table}; 7 | use crate::errors::StoreError; 8 | 9 | /// Table definition for the Latest Message table 10 | /// 11 | /// Key: latest_messages 12 | /// Value: LatestMessage 13 | pub const LATEST_MESSAGES_TABLE: TableDefinition> = 14 | TableDefinition::new("latest_messages"); 15 | 16 | pub struct LatestMessagesTable { 17 | pub db: Arc, 18 | } 19 | 20 | impl Table for LatestMessagesTable { 21 | type Key = u64; 22 | 23 | type Value = LatestMessage; 24 | 25 | fn get(&self, key: Self::Key) -> Result, StoreError> { 26 | let read_txn = self.db.begin_read()?; 27 | 28 | let table = read_txn.open_table(LATEST_MESSAGES_TABLE)?; 29 | let result = table.get(key)?; 30 | Ok(result.map(|res| res.value())) 31 | } 32 | 33 | fn insert(&self, key: Self::Key, value: Self::Value) -> Result<(), StoreError> { 34 | let mut write_txn = self.db.begin_write()?; 35 | write_txn.set_durability(Durability::Immediate); 36 | let mut table = write_txn.open_table(LATEST_MESSAGES_TABLE)?; 37 | table.insert(key, value)?; 38 | drop(table); 39 | write_txn.commit()?; 40 | Ok(()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/storage/src/tables/parent_root_index.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use alloy_primitives::B256; 4 | use redb::{Database, Durability, MultimapTableDefinition}; 5 | 6 | use super::{MultimapTable, SSZEncoding}; 7 | use crate::errors::StoreError; 8 | 9 | /// Table definition for the Parent Root Index Multimap table 10 | /// 11 | /// Key: ParentRoot 12 | /// Value: BlockRoot's 13 | pub const PARENT_ROOT_INDEX_MULTIMAP_TABLE: MultimapTableDefinition< 14 | SSZEncoding, 15 | SSZEncoding, 16 | > = MultimapTableDefinition::new("parent_root_index_multimap"); 17 | 18 | pub struct ParentRootIndexMultimapTable { 19 | pub db: Arc, 20 | } 21 | 22 | impl MultimapTable for ParentRootIndexMultimapTable { 23 | type Key = B256; 24 | 25 | type GetValue = Vec; 26 | 27 | type InsertValue = B256; 28 | 29 | fn get(&self, key: Self::Key) -> Result, StoreError> { 30 | let read_txn = self.db.begin_read()?; 31 | 32 | let table = read_txn.open_multimap_table(PARENT_ROOT_INDEX_MULTIMAP_TABLE)?; 33 | let result = table.get(key)?; 34 | let mut values = vec![]; 35 | for value in result { 36 | values.push(value?.value()); 37 | } 38 | Ok(Some(values)) 39 | } 40 | 41 | fn insert(&self, key: Self::Key, value: Self::InsertValue) -> Result<(), StoreError> { 42 | let mut write_txn = self.db.begin_write()?; 43 | write_txn.set_durability(Durability::Immediate); 44 | let mut table = write_txn.open_multimap_table(PARENT_ROOT_INDEX_MULTIMAP_TABLE)?; 45 | table.insert(key, value)?; 46 | drop(table); 47 | write_txn.commit()?; 48 | Ok(()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /crates/storage/src/tables/proposer_boost_root.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use alloy_primitives::{B256, FixedBytes}; 4 | use redb::{Database, Durability, TableDefinition}; 5 | 6 | use super::{Field, SSZEncoding}; 7 | use crate::errors::StoreError; 8 | 9 | /// Table definition for the Proposer_Boost_Root table 10 | /// 11 | /// Value: Root 12 | pub const PROPOSER_BOOST_ROOT_FIELD: TableDefinition<&str, SSZEncoding> = 13 | TableDefinition::new("proposer_boost_root"); 14 | 15 | pub const PROPOSER_BOOST_ROOT_KEY: &str = "proposer_boost_root_key"; 16 | 17 | pub struct ProposerBoostRootField { 18 | pub db: Arc, 19 | } 20 | 21 | impl Field for ProposerBoostRootField { 22 | type Value = B256; 23 | 24 | fn get(&self) -> Result, StoreError> { 25 | let read_txn = self.db.begin_read()?; 26 | 27 | let table = read_txn.open_table(PROPOSER_BOOST_ROOT_FIELD)?; 28 | let result = table 29 | .get(PROPOSER_BOOST_ROOT_KEY)? 30 | .ok_or(StoreError::FieldNotInitilized)?; 31 | Ok(result.value()) 32 | } 33 | 34 | fn insert(&self, value: Self::Value) -> Result<(), StoreError> { 35 | let mut write_txn = self.db.begin_write()?; 36 | write_txn.set_durability(Durability::Immediate); 37 | let mut table = write_txn.open_table(PROPOSER_BOOST_ROOT_FIELD)?; 38 | table.insert(PROPOSER_BOOST_ROOT_KEY, value)?; 39 | drop(table); 40 | write_txn.commit()?; 41 | Ok(()) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/storage/src/tables/slot_index.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use alloy_primitives::B256; 4 | use redb::{Database, Durability, ReadableTable, TableDefinition}; 5 | 6 | use super::{SSZEncoding, Table}; 7 | use crate::errors::StoreError; 8 | 9 | /// Table definition for the Slot Index table 10 | /// 11 | /// Key: slot number 12 | /// Value: block_root 13 | pub const SLOT_INDEX_TABLE: TableDefinition> = 14 | TableDefinition::new("slot_index"); 15 | 16 | pub struct SlotIndexTable { 17 | pub db: Arc, 18 | } 19 | 20 | impl Table for SlotIndexTable { 21 | type Key = u64; 22 | 23 | type Value = B256; 24 | 25 | fn get(&self, key: Self::Key) -> Result, StoreError> { 26 | let read_txn = self.db.begin_read()?; 27 | 28 | let table = read_txn.open_table(SLOT_INDEX_TABLE)?; 29 | let result = table.get(key)?; 30 | Ok(result.map(|res| res.value())) 31 | } 32 | 33 | fn insert(&self, key: Self::Key, value: Self::Value) -> Result<(), StoreError> { 34 | let mut write_txn = self.db.begin_write()?; 35 | write_txn.set_durability(Durability::Immediate); 36 | let mut table = write_txn.open_table(SLOT_INDEX_TABLE)?; 37 | table.insert(key, value)?; 38 | drop(table); 39 | write_txn.commit()?; 40 | Ok(()) 41 | } 42 | } 43 | 44 | impl SlotIndexTable { 45 | pub fn get_highest_slot(&self) -> Result, StoreError> { 46 | let read_txn = self.db.begin_read()?; 47 | let table = read_txn.open_table(SLOT_INDEX_TABLE)?; 48 | Ok(table.last()?.map(|result| result.0.value())) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /crates/storage/src/tables/state_root_index.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use alloy_primitives::B256; 4 | use redb::{Database, Durability, TableDefinition}; 5 | 6 | use super::{SSZEncoding, Table}; 7 | use crate::errors::StoreError; 8 | 9 | /// Table definition for the State Root Index table 10 | /// 11 | /// Key: state_root 12 | /// Value: block_root 13 | pub const STATE_ROOT_INDEX_TABLE: TableDefinition, SSZEncoding> = 14 | TableDefinition::new("state_root_index"); 15 | 16 | pub struct StateRootIndexTable { 17 | pub db: Arc, 18 | } 19 | 20 | impl Table for StateRootIndexTable { 21 | type Key = B256; 22 | 23 | type Value = B256; 24 | 25 | fn get(&self, key: Self::Key) -> Result, StoreError> { 26 | let read_txn = self.db.begin_read()?; 27 | 28 | let table = read_txn.open_table(STATE_ROOT_INDEX_TABLE)?; 29 | let result = table.get(key)?; 30 | Ok(result.map(|res| res.value())) 31 | } 32 | 33 | fn insert(&self, key: Self::Key, value: Self::Value) -> Result<(), StoreError> { 34 | let mut write_txn = self.db.begin_write()?; 35 | write_txn.set_durability(Durability::Immediate); 36 | let mut table = write_txn.open_table(STATE_ROOT_INDEX_TABLE)?; 37 | table.insert(key, value)?; 38 | drop(table); 39 | write_txn.commit()?; 40 | Ok(()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/storage/src/tables/time.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use redb::{Database, Durability, TableDefinition}; 4 | 5 | use super::Field; 6 | use crate::errors::StoreError; 7 | 8 | /// Table definition for the Time table 9 | /// 10 | /// Value: u64 11 | pub const TIME_FIELD: TableDefinition<&str, u64> = TableDefinition::new("time"); 12 | 13 | pub const TIME_KEY: &str = "time_key"; 14 | 15 | pub struct TimeField { 16 | pub db: Arc, 17 | } 18 | 19 | impl Field for TimeField { 20 | type Value = u64; 21 | 22 | fn get(&self) -> Result { 23 | let read_txn = self.db.begin_read()?; 24 | 25 | let table = read_txn.open_table(TIME_FIELD)?; 26 | let result = table.get(TIME_KEY)?.ok_or(StoreError::FieldNotInitilized)?; 27 | Ok(result.value()) 28 | } 29 | 30 | fn insert(&self, value: Self::Value) -> Result<(), StoreError> { 31 | let mut write_txn = self.db.begin_write()?; 32 | write_txn.set_durability(Durability::Immediate); 33 | let mut table = write_txn.open_table(TIME_FIELD)?; 34 | table.insert(TIME_KEY, value)?; 35 | drop(table); 36 | write_txn.commit()?; 37 | Ok(()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /crates/storage/src/tables/unrealized_finalized_checkpoint.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ream_consensus::checkpoint::Checkpoint; 4 | use redb::{Database, Durability, TableDefinition}; 5 | 6 | use super::{Field, SSZEncoding}; 7 | use crate::errors::StoreError; 8 | 9 | /// Table definition for the Unrealized_Finalized_Checkpoint table 10 | /// 11 | /// Value: Checkpoint 12 | pub const UNREALIZED_FINALIZED_CHECKPOINT_FIELD: TableDefinition<&str, SSZEncoding> = 13 | TableDefinition::new("unrealized_finalized_checkpoint"); 14 | 15 | pub const UNREALIZED_FINALIZED_CHECKPOINT_FIELD_KEY: &str = "unrealized_finalized_checkpoint_key"; 16 | 17 | pub struct UnrealizedFinalizedCheckpointField { 18 | pub db: Arc, 19 | } 20 | 21 | impl Field for UnrealizedFinalizedCheckpointField { 22 | type Value = Checkpoint; 23 | 24 | fn get(&self) -> Result { 25 | let read_txn = self.db.begin_read()?; 26 | 27 | let table = read_txn.open_table(UNREALIZED_FINALIZED_CHECKPOINT_FIELD)?; 28 | let result = table 29 | .get(UNREALIZED_FINALIZED_CHECKPOINT_FIELD_KEY)? 30 | .ok_or(StoreError::FieldNotInitilized)?; 31 | Ok(result.value()) 32 | } 33 | 34 | fn insert(&self, value: Self::Value) -> Result<(), StoreError> { 35 | let mut write_txn = self.db.begin_write()?; 36 | write_txn.set_durability(Durability::Immediate); 37 | let mut table = write_txn.open_table(UNREALIZED_FINALIZED_CHECKPOINT_FIELD)?; 38 | table.insert(UNREALIZED_FINALIZED_CHECKPOINT_FIELD_KEY, value)?; 39 | drop(table); 40 | write_txn.commit()?; 41 | Ok(()) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/storage/src/tables/unrealized_justifications.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use alloy_primitives::B256; 4 | use ream_consensus::checkpoint::Checkpoint; 5 | use redb::{Database, Durability, TableDefinition}; 6 | 7 | use super::{SSZEncoding, Table}; 8 | use crate::errors::StoreError; 9 | 10 | /// Table definition for the Unrealized Justifications table 11 | /// 12 | /// Key: unrealized_justifications 13 | /// Value: Checkpoint 14 | pub const UNREALIZED_JUSTIFICATIONS_TABLE: TableDefinition< 15 | SSZEncoding, 16 | SSZEncoding, 17 | > = TableDefinition::new("unrealized_justifications"); 18 | 19 | pub struct UnrealizedJustificationsTable { 20 | pub db: Arc, 21 | } 22 | 23 | impl Table for UnrealizedJustificationsTable { 24 | type Key = B256; 25 | 26 | type Value = Checkpoint; 27 | 28 | fn get(&self, key: Self::Key) -> Result, StoreError> { 29 | let read_txn = self.db.begin_read()?; 30 | 31 | let table = read_txn.open_table(UNREALIZED_JUSTIFICATIONS_TABLE)?; 32 | let result = table.get(key)?; 33 | Ok(result.map(|res| res.value())) 34 | } 35 | 36 | fn insert(&self, key: Self::Key, value: Self::Value) -> Result<(), StoreError> { 37 | let mut write_txn = self.db.begin_write()?; 38 | write_txn.set_durability(Durability::Immediate); 39 | let mut table = write_txn.open_table(UNREALIZED_JUSTIFICATIONS_TABLE)?; 40 | table.insert(key, value)?; 41 | drop(table); 42 | write_txn.commit()?; 43 | Ok(()) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /crates/storage/src/tables/unrealized_justified_checkpoint.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ream_consensus::checkpoint::Checkpoint; 4 | use redb::{Database, Durability, TableDefinition}; 5 | 6 | use super::{Field, SSZEncoding}; 7 | use crate::errors::StoreError; 8 | 9 | /// Table definition for the Unrealized_Justified_Checkpoint table 10 | /// 11 | /// Value: Checkpoint 12 | pub const UNREALIZED_JUSTIFED_CHECKPOINT_FIELD: TableDefinition<&str, SSZEncoding> = 13 | TableDefinition::new("unrealized_justified_checkpoint"); 14 | 15 | pub const UNREALIZED_JUSTIFED_CHECKPOINT_KEY: &str = "unrealized_justified_checkpoint_key"; 16 | 17 | pub struct UnrealizedJustifiedCheckpointField { 18 | pub db: Arc, 19 | } 20 | 21 | impl Field for UnrealizedJustifiedCheckpointField { 22 | type Value = Checkpoint; 23 | 24 | fn get(&self) -> Result { 25 | let read_txn = self.db.begin_read()?; 26 | 27 | let table = read_txn.open_table(UNREALIZED_JUSTIFED_CHECKPOINT_FIELD)?; 28 | let result = table 29 | .get(UNREALIZED_JUSTIFED_CHECKPOINT_KEY)? 30 | .ok_or(StoreError::FieldNotInitilized)?; 31 | Ok(result.value()) 32 | } 33 | 34 | fn insert(&self, value: Self::Value) -> Result<(), StoreError> { 35 | let mut write_txn = self.db.begin_write()?; 36 | write_txn.set_durability(Durability::Immediate); 37 | let mut table = write_txn.open_table(UNREALIZED_JUSTIFED_CHECKPOINT_FIELD)?; 38 | table.insert(UNREALIZED_JUSTIFED_CHECKPOINT_KEY, value)?; 39 | drop(table); 40 | write_txn.commit()?; 41 | Ok(()) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | binop_separator = "Front" 2 | comment_width = 100 3 | format_code_in_doc_comments = true 4 | group_imports = "StdExternalCrate" 5 | imports_granularity = "Crate" 6 | normalize_comments = true 7 | reorder_imports = true 8 | wrap_comments = true 9 | -------------------------------------------------------------------------------- /testing/beacon-api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "beacon-api-tests" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | alloy-primitives.workspace = true 14 | anyhow.workspace = true 15 | serde.workspace = true 16 | serde_json.workspace = true 17 | tokio.workspace = true 18 | 19 | # ream 20 | ream-beacon-api-types.workspace = true 21 | ream-consensus.workspace = true 22 | -------------------------------------------------------------------------------- /testing/beacon-api/tests/serialization.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs, 3 | path::{Path, PathBuf}, 4 | }; 5 | 6 | use alloy_primitives::b256; 7 | use ream_beacon_api_types::responses::BeaconVersionedResponse; 8 | use ream_consensus::electra::{beacon_block::SignedBeaconBlock, beacon_state::BeaconState}; 9 | use serde_json::Value; 10 | 11 | const PATH_TO_TEST_DATA_FOLDER: &str = "./tests/assets"; 12 | 13 | #[tokio::test] 14 | async fn test_beacon_state_serialization() -> anyhow::Result<()> { 15 | let original_json = read_json_file("state.json")?; 16 | 17 | let beacon_state: BeaconVersionedResponse = 18 | serde_json::from_value(original_json.clone())?; 19 | 20 | assert_eq!(beacon_state.version, "electra"); 21 | assert_eq!(beacon_state.data.latest_block_header.slot, 1); 22 | assert_eq!( 23 | beacon_state.data.latest_block_header.parent_root, 24 | b256!("0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2") 25 | ); 26 | 27 | let serialized_json: Value = serde_json::to_value(&beacon_state)?; 28 | 29 | assert_eq!( 30 | original_json, serialized_json, 31 | "Original JSON and re-serialized JSON do not match!" 32 | ); 33 | 34 | Ok(()) 35 | } 36 | 37 | #[tokio::test] 38 | async fn test_beacon_block_serialization() -> anyhow::Result<()> { 39 | let original_json = read_json_file("block.json")?; 40 | 41 | let beacon_block: BeaconVersionedResponse = 42 | serde_json::from_value(original_json.clone())?; 43 | 44 | assert_eq!(beacon_block.version, "electra"); 45 | assert_eq!(beacon_block.data.message.slot, 1); 46 | 47 | let serialized_json: Value = serde_json::to_value(&beacon_block)?; 48 | 49 | assert_eq!( 50 | serialized_json, original_json, 51 | "Re-encoded block doesn't match original JSON!" 52 | ); 53 | 54 | Ok(()) 55 | } 56 | 57 | pub fn read_json_file>(file_name: P) -> anyhow::Result { 58 | let file_contents = 59 | fs::read_to_string(PathBuf::from(PATH_TO_TEST_DATA_FOLDER).join(file_name))?; 60 | Ok(serde_json::from_str(&file_contents)?) 61 | } 62 | -------------------------------------------------------------------------------- /testing/ef-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ef-tests" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [features] 13 | ef-tests = [] 14 | 15 | [dependencies] 16 | alloy-consensus.workspace = true 17 | alloy-primitives.workspace = true 18 | anyhow.workspace = true 19 | ethereum_ssz.workspace = true 20 | ethereum_ssz_derive.workspace = true 21 | paste = "1.0.15" 22 | rstest.workspace = true 23 | serde.workspace = true 24 | serde_yaml.workspace = true 25 | snap.workspace = true 26 | ssz_types.workspace = true 27 | tokio.workspace = true 28 | tree_hash.workspace = true 29 | 30 | # ream dependencies 31 | ream-bls.workspace = true 32 | ream-consensus.workspace = true 33 | ream-fork-choice.workspace = true 34 | ream-merkle.workspace = true 35 | ream-storage.workspace = true 36 | -------------------------------------------------------------------------------- /testing/ef-tests/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = mainnet.tar.gz 2 | EXTRACT_DIR = mainnet 3 | LATEST_RELEASE_URL = https://api.github.com/repos/ethereum/consensus-spec-tests/releases 4 | 5 | .PHONY: all clean 6 | 7 | all: test 8 | 9 | $(EXTRACT_DIR): $(TARGET) 10 | @if [ -d $(EXTRACT_DIR) ]; then \ 11 | echo "$(EXTRACT_DIR) already exists. Skipping extraction."; \ 12 | else \ 13 | echo "Extracting $(TARGET) into $(EXTRACT_DIR)..."; \ 14 | mkdir -p $(EXTRACT_DIR); \ 15 | tar -xzf $(TARGET) -C $(EXTRACT_DIR); \ 16 | rm -f $(TARGET); \ 17 | echo "Extraction complete."; \ 18 | fi 19 | 20 | $(TARGET): 21 | @if [ -d $(EXTRACT_DIR) ]; then \ 22 | echo "$(EXTRACT_DIR) already downloaded. Skipping download."; \ 23 | else \ 24 | echo "Fetching the latest release (including pre-releases) for $(TARGET)..."; \ 25 | curl -s $(LATEST_RELEASE_URL) \ 26 | | grep "browser_download_url.*$(TARGET)" \ 27 | | head -n 1 \ 28 | | cut -d : -f 2,3 \ 29 | | tr -d \" \ 30 | | wget -qi -; \ 31 | echo "$(TARGET) downloaded successfully."; \ 32 | fi 33 | 34 | test: $(EXTRACT_DIR) 35 | @echo "Running tests..." 36 | @cargo test --release --features ef-tests 37 | @echo "Tests complete." 38 | 39 | clean: 40 | @echo "Cleaning up downloaded and extracted files..." 41 | @rm -f $(TARGET) 42 | @rm -rf $(EXTRACT_DIR) 43 | @echo "Clean up complete." 44 | -------------------------------------------------------------------------------- /testing/ef-tests/README.md: -------------------------------------------------------------------------------- 1 | ## Run [ethereum/consensus-spec-tests](https://github.com/ethereum/consensus-spec-tests) 2 | 3 | 4 | Run Tests this will automatically download test data 5 | ```bash 6 | make test 7 | ``` 8 | 9 | Clean test files 10 | ```bash 11 | make clean 12 | ``` 13 | -------------------------------------------------------------------------------- /testing/ef-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod macros; 2 | pub mod utils; 3 | -------------------------------------------------------------------------------- /testing/ef-tests/src/macros/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod epoch_processing; 2 | pub mod fork_choice; 3 | pub mod merkle_proof; 4 | pub mod operations; 5 | pub mod rewards; 6 | pub mod sanity_blocks; 7 | pub mod sanity_slots; 8 | pub mod shuffling; 9 | pub mod ssz_static; 10 | -------------------------------------------------------------------------------- /testing/ef-tests/src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use anyhow::anyhow; 4 | use snap::raw::Decoder; 5 | 6 | pub fn read_ssz_snappy(path: &Path) -> anyhow::Result { 7 | let ssz_snappy = std::fs::read(path)?; 8 | let mut decoder = Decoder::new(); 9 | let ssz = decoder.decompress_vec(&ssz_snappy)?; 10 | T::from_ssz_bytes(&ssz).map_err(|err| anyhow!("Failed to decode SSZ: {:?}", err)) 11 | } 12 | --------------------------------------------------------------------------------