Compare commits

..

6 Commits

Author SHA1 Message Date
Dhruv Bodani
38eabd1037 Add all the fixes from pre-commit (#14846)
* precommit fixes

* fix remaining
2025-03-24 09:46:45 -05:00
Dhruv Bodani
85aa47c42a fix golangci.yml location in contributing.md 2025-03-06 12:28:02 +05:30
Dhruv Bodani
949129143d update contributing.md and add githook for golangci-lint 2025-03-06 12:23:04 +05:30
Dhruv Bodani
7b86dc5526 Merge branch 'develop' into precommit 2025-03-06 10:27:07 +05:30
Dhruv Bodani
61c4a10dfa add changelog 2025-01-29 19:34:26 +05:30
Dhruv Bodani
6e6e71a75f add precommit hooks 2025-01-29 14:16:54 +05:30
521 changed files with 5084 additions and 10488 deletions

2
.github/CODEOWNERS vendored
View File

@@ -5,4 +5,4 @@
*.bzl @prestonvanloon
# Anyone on prylabs team can approve dependency updates.
deps.bzl @prysmaticlabs/core-team
deps.bzl @prysmaticlabs/core-team

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
uses: actions/checkout@v3
- name: Download unclog binary
uses: dsaltares/fetch-gh-release-asset@aa2ab1243d6e0d5b405b973c89fa4d06a2d0fff7 # 1.1.2
@@ -23,7 +23,7 @@ jobs:
- name: Get new changelog files
id: new-changelog-files
uses: OffchainLabs/gh-action-changed-files@9200e69727eb73eb060652b19946b8a2fdfb654b # v4.0.8
uses: tj-actions/changed-files@v45
with:
files: |
changelog/**.md

View File

@@ -19,4 +19,4 @@ jobs:
- name: Running Security Scan
run: |
curl -fsSL https://raw.githubusercontent.com/ZupIT/horusec/main/deployments/scripts/install.sh | bash -s latest
horusec start -t="10000" -p="./" -e="true" -i="**/crypto/bls/herumi/**, **/**/*_test.go, **/third_party/afl/**, **/crypto/keystore/key.go"
horusec start -t="10000" -p="./" -e="true" -i="**/crypto/bls/herumi/**, **/**/*_test.go, **/third_party/afl/**, **/crypto/keystore/key.go"

34
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,34 @@
repos:
# First run code formatters
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
hooks:
- id: end-of-file-fixer # ensures that a file is either empty, or ends with one newline
exclude_types: ["proto"]
- id: mixed-line-ending # ensures that a file doesn't contain a mix of LF and CRLF
- id: no-commit-to-branch # Protect specific branches (default: main/master) from direct checkins
- repo: local
hooks:
- id: gci
name: goimports
entry: bash -c 'command -v gci >/dev/null 2>&1 || go install github.com/daixiang0/gci@latest; gci write --skip-generated -s standard -s default "$@"; goimports -w "$@"' --
language: golang
files: \.go$
- repo: https://github.com/dnephin/pre-commit-golang
rev: v0.4.0
hooks:
- id: go-fmt
args: [ -w, -s ] # simplify code and write result to (source) file instead of stdout
- id: go-mod-tidy
files: go.mod
- id: golangci-lint
# Fix bazel build files
- repo: local
hooks:
- id: gazelle
name: bazel-fix
entry: .pre-commit/fixbazel.sh
language: system

3
.pre-commit/fixbazel.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env bash
bazel run //:gazelle -- fix

View File

@@ -197,7 +197,6 @@ nogo(
"//tools/analyzers/logruswitherror:go_default_library",
"//tools/analyzers/maligned:go_default_library",
"//tools/analyzers/nop:go_default_library",
"//tools/analyzers/nopanic:go_default_library",
"//tools/analyzers/properpermissions:go_default_library",
"//tools/analyzers/recursivelock:go_default_library",
"//tools/analyzers/shadowpredecl:go_default_library",

View File

@@ -4,139 +4,6 @@ All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
## [v5.3.2](https://github.com/prysmaticlabs/prysm/compare/v5.3.1...v5.3.2) - 2025-03-25
This release introduces support for the `Hoodi` testnet.
Release highlights:
- Ability to run the node on the `Hoodi` tesnet. See https://blog.ethereum.org/2025/03/18/hoodi-holesky for more information about `Hoodi`.
- A new feature that allows treat certain blocks as invalid. This is especially useful when the network is split, allowing the node to discontinue following unwanted forks.
Testnet operators are required to update to this release. Without this release you will be unable to run the node on the `Hoodi` testnet.
Mainnet operators are recommended to update to this release at their regular cadence.
### Added
- enable SSZ for builder API calls. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14976)
- Add Hoodi testnet flag `--hoodi` to specify Hoodi testnet config and bootnodes. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15057)
- block_gossip topic support to the beacon api event stream. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15038)
- Added a static analyzer to discourage use of panic() in Prysm. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15075)
- Add a feature flag `--blacklist-roots` to allow the node to specify blocks that will be treated as invalid. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15030)
### Changed
- changed request object for `POST /eth/v1/beacon/states/head/validators` to omit the field if empty for satisfying other clients. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15031)
- Update spec test to v1.5.0-beta.3. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15050)
- Update Gossip and RPC message limits to comply with the spec. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14799)
- Return 404 instead of 500 from API when when a blob for a requested index is not found. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14845)
- Save Electra orphaned attestations into attestations pool's block attestations. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15060)
- Removed redundant string conversion in `BeaconDbStater.State` to improve code clarity and maintainability. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15081)
### Fixed
- Update seen unaggregated att cache to properly handle Electra attestations. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15034)
- cosmetic fix for calling `/prysm/validators/performance` when connecting to non prysm beacon nodes and removing the 404 error log. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15062)
- Tracked validator cache: Make sure no to loose the reference. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15077)
- Fixed proposing at genesis when starting post Bellatrix. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15084)
## [v5.3.1](https://github.com/prysmaticlabs/prysm/compare/v5.3.0...v5.3.1) - 2025-03-13
This release is packed with critical fixes for **Electra** and some important fixes for mainnet too.
The release highlights include:
- Ensure that deleting a block from the database clears its entry in the slot->root db index. This issue was causing some operators to have a bricked database, requiring a full resync. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15011)
- Updated go to go1.24.0. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14969)
- Added a feature flag to sync from an arbitrary beacon block root at startup. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15000)
- Updated default gas limit from 30M to 36M. Override this with `--suggested-gas-limit=` in the validator client. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14858)
Known issues in **Electra**:
- Duplicate attestations are needlessly processed. This is being addressed in [[PR]](https://github.com/prysmaticlabs/prysm/pull/15034).
Testnet operators are strongly encouraged to update to this release. There are many fixes and improvements from the Holesky upgrade incident.
Mainnet operators are recommended to update to this release at their regular cadence.
### Added
- enable E2E for minimal and mainnet tests. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14842)
- enable web3signer E2E for electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14936)
- Enable multiclient E2E for electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14946)
- Enable Scenario E2E tests with electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14946)
- Add endpoint for getting pending deposits. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14941)
- Add request hash to header for builder: executable data to block. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14955)
- Log execution requests in each block. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14956)
- Add endpoint for getting pending partial withdrawals. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14949)
- Tracked validators cache: Added the `ItemCount` method. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14957)
- Tracked validators cache: Added the `Indices` method. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14957)
- Added deposit request testing for electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14964)
- Added support for otel tracing transport in HTTP clients in Prysm. This allows for tracing headers to be sent with http requests such that spans between the validator and beacon chain can be connected in the tracing graph. This change does nothing without `--enable-tracing`. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14972)
- Add SSZ support to light client finality and optimistic APIs. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14836)
- add log to committee index when committeebits are not the expected length of 1. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14993)
- Add acceptable address types for static peers. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14886)
- Added a feature flag to sync from an arbitrary beacon block root at startup. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15000)
### Changed
- updates geth to 1.15.0. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14842)
- Updates blst to v3.14.0 and fixes the references in our deps.bzl file. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14921)
- Updated tracing exporter from jaeger to otelhttp. This should not be a breaking change. Jaeger supports otel format, however you may need to update your URL as the default otel-collector port is 4318. See the [OpenTelemtry Protocol Exporter docs](https://opentelemetry.io/docs/specs/otel/protocol/exporter/) for more details. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14928)
- Don't use MaxCover for Electra on-chain attestations. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14925)
- Tracked validators cache: Remove validators from the cache if not seen after 1 hour. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14957)
- execution requests errors on ssz length have been improved. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14962)
- deprecate beacon api endpoints based on [3.0.0 release](https://github.com/ethereum/beacon-APIs/pull/506) for electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14967)
- Use go-cmp for printing better diffs for assertions.DeepEqual. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14978)
- Reorganized beacon chain flags in `--help` text into logical sections. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14959)
- `--validators-registration-batch-size`: Change default value from `0` to `200`. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14981)
- Updated go to go1.24.0. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14969)
- Updated gosec to v2.22.1 and golangci to v1.64.5. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14969)
- Updated github.com/trailofbits/go-mutexasserts. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14969)
- Updated rules_go to cf3c3af34bd869b864f5f2b98e2f41c2b220d6c9 to support go1.24.0. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14969)
- Validate blob sidecar re-order signature and bad parent block. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15013)
- Updated default gas limit from 30M to 36M. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14858)
- Ignore errors from `hasSeenBit` and don't pack unaggregated attestations. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15018)
### Removed
- Remove Fulu state and block. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14905)
- Removed the log summarizing all started services. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14958)
### Fixed
- fixed max and target blob per block from static to dynamic values. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14911)
- refactored publish block and block ssz functions to fix gocognit. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14913)
- refactored publish blinded block and blinded block ssz to correctly deal with version headers and sent blocks. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14913)
- Only check for electra related engine methods if electra is active. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14924)
- Fixed bug that breaks new blob storage layout code on Windows, caused by accidental use of platform-dependent path parsing package. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14931)
- Fix E2E Process Deposit Evaluator for Electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14933)
- Fixed the `bazel run //:gazelle` command in `DEPENDENCIES.md`. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14934)
- Fix E2E Deposit Activation Evaluator for Electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14938)
- Dedicated processing of `SingleAttestation` in the monitor service. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14965)
- adding in content type and accept headers for builder API call on registration. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14961)
- fixed gocognit in block conversions between json and proto types. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14953)
- Lint: Fix violations of S1009: should omit nil check; len() for nil slices is defined as zero. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14973)
- Lint: Fix violations of non-constant format string in call. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14974)
- Fixed violations of gosec G301. This is a check that created files and directories have file permissions 0750 and 0600 respectively. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14980)
- Check for the correct attester slashing type during gossip validation. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14985)
- cosmetic fix for post electra validator logs displaying attestation committee information correctly. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14992)
- fix inserting the wrong committee index into the seen cache for electra attestations. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14998)
- Allow any block type to be unmarshaled rather than only phase0 blocks in `slotByBlockRoot`. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15008)
- Fixed pruner to not block while pruning large database by introducing batchSize. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14929)
- Decompose Electra block attestations to prevent redundant packing. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14896)
- Fixed use of deprecated rand.Seed. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14969)
- Fixed build issue with SszGen where the go binary was not present in the $PATH. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14969)
- fixed /eth/v1/config/spec displays BLOB_SIDECAR_SUBNET_COUNT,BLOB_SIDECAR_SUBNET_COUNT_ELECTRA. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15016)
- Ensure that deleting a block from the database clears its entry in the slot->root db index. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15011)
- Broadcasting BLS to execution changes should not use the request context in a go routine. Use context.Background() for the broadcasting go routine. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15019)
- /eth/v1/validator/sync_committee_contribution should check for optimistic status and return a 503 if it's optimistic. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15022)
- Fixes printing superfluous response.WriteHeader call from error in builder. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15025)
- Fixes e2e run with builder having wrong gaslimit header due to not being set on eth1 nodes. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15025)
- Fixed a bug in the event stream handler when processing payload attribute events where the timestamp and slot of the event would be based on the head rather than the current slot. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14963)
- Handle unaggregated attestations when decomposing Electra block attestations. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15027)
## [v5.3.0](https://github.com/prysmaticlabs/prysm/compare/v5.2.0...v5.3.0) - 2025-02-12
This release includes support for Pectra activation in the [Holesky](https://github.com/eth-clients/holesky) and [Sepolia](https://github.com/eth-clients/sepolia) testnets! The release contains many fixes for Electra that have been found in rigorous testing through devnets in the last few months.
@@ -3255,4 +3122,4 @@ There are no security updates in this release.
# Older than v2.0.0
For changelog history for releases older than v2.0.0, please refer to https://github.com/prysmaticlabs/prysm/releases
For changelog history for releases older than v2.0.0, please refer to https://github.com/prysmaticlabs/prysm/releases

View File

@@ -175,6 +175,23 @@ $ git push myrepo feature-in-progress-branch -f
**22. Finally, again leave a comment to the Core Contributors on the pull request to let them know that the pull request has been updated.**
### Git hooks and linters.
Prysm is configured with [pre-commit](https://pre-commit.com) **githooks** that ensures pull
requests adhere to a minimum standard and are consistent. It is highly recommended running the githooks locally while developing for faster feedback.
To install githooks:
- Follow installation instructions [here](https://pre-commit.com/#installation) to install the `pre-commit` tool.
- Once installed, run `pre-commit install` in the project's root directory. This will set up the hooks.
- Note you can skip the hooks by committing with `-n`: `git commit -n -m "look mom no githooks"`
To update githooks:
```sh
pre-commit clean
```
The **linter** used is [golangci-lint](https://golangci-lint.run/). It runs as part of the githooks and is configured in [.golangci.yml](.golangci.yml)
## Maintaining CHANGELOG.md
This project follows the changelog guidelines from [keepachangelog.com](https://keepachangelog.com/en/1.1.0/). In order to minimize conflicts and workflow headaches, we chose to implement a changelog management

View File

@@ -672,4 +672,3 @@ may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@@ -1,7 +1,5 @@
<h1 align="left">Prysm: An Ethereum Consensus Implementation Written in Go</h1>
# Prysm: An Ethereum Consensus Implementation Written in Go
<div align="left">
[![Build status](https://badge.buildkite.com/b555891daf3614bae4284dcf365b2340cefc0089839526f096.svg?branch=master)](https://buildkite.com/prysmatic-labs/prysm)
[![Go Report Card](https://goreportcard.com/badge/github.com/prysmaticlabs/prysm)](https://goreportcard.com/report/github.com/prysmaticlabs/prysm)
[![Consensus_Spec_Version 1.4.0](https://img.shields.io/badge/Consensus%20Spec%20Version-v1.4.0-blue.svg)](https://github.com/ethereum/consensus-specs/tree/v1.4.0)
@@ -9,60 +7,31 @@
[![Discord](https://user-images.githubusercontent.com/7288322/34471967-1df7808a-efbb-11e7-9088-ed0b04151291.png)](https://discord.gg/prysmaticlabs)
[![GitPOAP Badge](https://public-api.gitpoap.io/v1/repo/prysmaticlabs/prysm/badge)](https://www.gitpoap.io/gh/prysmaticlabs/prysm)
</div>
This is the core repository for Prysm, a [Golang](https://golang.org/) implementation of the [Ethereum Consensus](https://ethereum.org/en/developers/docs/consensus-mechanisms/#proof-of-stake) [specification](https://github.com/ethereum/consensus-specs), developed by [Offchain Labs](https://www.offchainlabs.com). See the [Changelog](https://github.com/prysmaticlabs/prysm/releases) for details of the latest releases and upcoming breaking changes.
---
### Getting Started
## 📖 Overview
A detailed set of installation and usage instructions as well as breakdowns of each individual component are available in the [official documentation portal](https://docs.prylabs.network). If you still have questions, feel free to stop by our [Discord](https://discord.gg/prysmaticlabs).
This is the core repository for Prysm, a [Golang](https://golang.org/) implementation of the [Ethereum Consensus](https://ethereum.org/en/developers/docs/consensus-mechanisms/#proof-of-stake) [specification](https://github.com/ethereum/consensus-specs), developed by [Offchain Labs](https://www.offchainlabs.com).
### Staking on Mainnet
See the [Changelog](https://github.com/prysmaticlabs/prysm/releases) for details of the latest releases and upcoming breaking changes.
To participate in staking, you can join the [official eth2 launchpad](https://launchpad.ethereum.org). The launchpad is the only recommended way to become a validator on mainnet. You can explore validator rewards/penalties via Bitfly's block explorer: [beaconcha.in](https://beaconcha.in), and follow the latest blocks added to the chain on [beaconscan](https://beaconscan.com).
---
## 🚀 Getting Started
A detailed set of installation and usage instructions as well as breakdowns of each individual component are available in the **[official documentation portal](https://docs.prylabs.network)**.
💬 **Need help?** Join our **[Discord Community](https://discord.gg/prysmaticlabs)** for support.
---
## 🏆 Staking on Mainnet
To participate in staking, you can join the **[official Ethereum launchpad](https://launchpad.ethereum.org)**. The launchpad is the **only recommended** way to become a validator on mainnet.
🔍 Explore validator rewards/penalties:
- **[beaconcha.in](https://beaconcha.in)**
- **[beaconscan](https://beaconscan.com)**
---
## 🤝 Contributing
### 🔥 Branches
## Contributing
### Branches
Prysm maintains two permanent branches:
- **[`master`](https://github.com/prysmaticlabs/prysm/tree/master)** - This points to the latest stable release. It is ideal for most users.
- **[`develop`](https://github.com/prysmaticlabs/prysm/tree/develop)** - This is used for development and contains the latest PRs. Developers should base their PRs on this branch.
* [master](https://github.com/prysmaticlabs/prysm/tree/master): This points to the latest stable release. It is ideal for most users.
* [develop](https://github.com/prysmaticlabs/prysm/tree/develop): This is used for development, it contains the latest PRs. Developers should base their PRs on this branch.
### 🛠 Contribution Guide
### Guide
Want to get involved? Check out our [Contribution Guide](https://docs.prylabs.network/docs/contribute/contribution-guidelines/) to learn more!
Want to get involved? Check out our **[Contribution Guide](https://docs.prylabs.network/docs/contribute/contribution-guidelines/)** to learn more!
## License
---
[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html)
## 📜 License
## Legal Disclaimer
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0.en.html)
This project is licensed under the **GNU General Public License v3.0**.
---
## ⚖️ Legal Disclaimer
📜 [Terms of Use](/TERMS_OF_SERVICE.md)
[Terms of Use](/TERMS_OF_SERVICE.md)

View File

@@ -50,4 +50,3 @@ We reserve the right to revise these Terms, and your rights and obligations are
These Terms constitute the entire agreement between you and Offchain Labs regarding use of Prysm and will supersede all prior agreements whether, written or oral. No usage of trade or other regular practice or method of dealing between the parties will be used to modify, interpret, supplement, or alter the terms of these Terms.
If any portion of these Terms is held invalid or unenforceable, such invalidity or enforceability will not affect the other provisions of these Terms, which will remain in full force and effect, and the invalid or unenforceable portion will be given effect to the greatest extent possible. The failure of a party to require performance of any provision will not affect that partys right to require performance at any time thereafter, nor will a waiver of any breach or default of these Terms or any provision of these Terms constitute a waiver of any subsequent breach or default or a waiver of the provision itself.

View File

@@ -255,7 +255,7 @@ filegroup(
url = "https://github.com/ethereum/EIPs/archive/5480440fe51742ed23342b68cf106cefd427e39d.tar.gz",
)
consensus_spec_version = "v1.5.0-beta.4"
consensus_spec_version = "v1.5.0-beta.2"
bls_test_version = "v0.1.1"
@@ -271,7 +271,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
integrity = "sha256-QG0NUqaCvP5lKaKKwF/fmeICZVjONMlb7EE+MtYl0C0=",
integrity = "sha256-X/bMxbKg1clo2aFEjBoeuFq/U+BF1eQopgRP/7nI3Qg=",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/general.tar.gz" % consensus_spec_version,
)
@@ -287,7 +287,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
integrity = "sha256-8NQngTSSqzW/j3tOUi3r5h+94ChRbLNWTt7BOGqr4+E=",
integrity = "sha256-WSxdri5OJGuNApW+odKle5UzToDyEOx+F3lMiqamJAg=",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/minimal.tar.gz" % consensus_spec_version,
)
@@ -303,7 +303,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
integrity = "sha256-gFqxbaBnJ7dtdoj0zFbVrtlHv/bLNuWjrTHkyCAjFjI=",
integrity = "sha256-LYE8l3y/zSt4YVrehrJ3ralqtgeYNildiIp+HR6+xAI=",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/mainnet.tar.gz" % consensus_spec_version,
)
@@ -318,7 +318,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
integrity = "sha256-9paalF0POULpP2ga+4ouHSETKYrWNCUCZoJHPuFw06E=",
integrity = "sha256-jvZQ90qcJMTOqMsPO7sgeEVQmewZTHcz7LVDkNqwTFQ=",
strip_prefix = "consensus-specs-" + consensus_spec_version[1:],
url = "https://github.com/ethereum/consensus-specs/archive/refs/tags/%s.tar.gz" % consensus_spec_version,
)
@@ -365,9 +365,9 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
integrity = "sha256-YVFFrCmjoGZ3fXMWpsCpSsYbANy1grnqYwOLKIg2SsA=",
strip_prefix = "holesky-32a72e21c6e53c262f27d50dd540cb654517d03a",
url = "https://github.com/eth-clients/holesky/archive/32a72e21c6e53c262f27d50dd540cb654517d03a.tar.gz", # 2025-03-17
integrity = "sha256-b7ZTT+olF+VXEJYNTV5jggNtCkt9dOejm1i2VE+zy+0=",
strip_prefix = "holesky-874c199423ccd180607320c38cbaca05d9a1573a",
url = "https://github.com/eth-clients/holesky/archive/874c199423ccd180607320c38cbaca05d9a1573a.tar.gz", # 2024-06-18
)
http_archive(
@@ -381,25 +381,9 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
integrity = "sha256-b5F7Wg9LLMqGRIpP2uqb/YsSFVn2ynzlV7g/Nb1EFLk=",
strip_prefix = "sepolia-562d9938f08675e9ba490a1dfba21fb05843f39f",
url = "https://github.com/eth-clients/sepolia/archive/562d9938f08675e9ba490a1dfba21fb05843f39f.tar.gz", # 2025-03-17
)
http_archive(
name = "hoodi_testnet",
build_file_content = """
filegroup(
name = "configs",
srcs = [
"metadata/config.yaml",
],
visibility = ["//visibility:public"],
)
""",
integrity = "sha256-dPiEWUd8QvbYGwGtIm0QtCekitVLOLsW5rpQIGzz8PU=",
strip_prefix = "hoodi-828c2c940e1141092bd4bb979cef547ea926d272",
url = "https://github.com/eth-clients/hoodi/archive/828c2c940e1141092bd4bb979cef547ea926d272.tar.gz",
integrity = "sha256-cY/UgpCcYEhQf7JefD65FI8tn/A+rAvKhcm2/qiVdqY=",
strip_prefix = "sepolia-f2c219a93c4491cee3d90c18f2f8e82aed850eab",
url = "https://github.com/eth-clients/sepolia/archive/f2c219a93c4491cee3d90c18f2f8e82aed850eab.tar.gz", # 2024-09-19
)
http_archive(

View File

@@ -1,19 +0,0 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["common.go"],
importpath = "github.com/prysmaticlabs/prysm/v5/api/apiutil",
visibility = ["//visibility:public"],
deps = ["//consensus-types/primitives:go_default_library"],
)
go_test(
name = "go_default_test",
srcs = ["common_test.go"],
embed = [":go_default_library"],
deps = [
"//consensus-types/primitives:go_default_library",
"//testing/assert:go_default_library",
],
)

View File

@@ -1,23 +0,0 @@
package apiutil
import (
"fmt"
neturl "net/url"
"strconv"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
)
// Uint64ToString is a util function that will convert uints to string
func Uint64ToString[T uint64 | primitives.Slot | primitives.ValidatorIndex | primitives.CommitteeIndex | primitives.Epoch](val T) string {
return strconv.FormatUint(uint64(val), 10)
}
// BuildURL is a util function that assists with adding query parameters to the url
func BuildURL(path string, queryParams ...neturl.Values) string {
if len(queryParams) == 0 {
return path
}
return fmt.Sprintf("%s?%s", path, queryParams[0].Encode())
}

View File

@@ -1,37 +0,0 @@
package apiutil
import (
"net/url"
"testing"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/testing/assert"
)
func TestBeaconApiHelpers_TestUint64ToString(t *testing.T) {
const expectedResult = "1234"
const val = uint64(1234)
assert.Equal(t, expectedResult, Uint64ToString(val))
assert.Equal(t, expectedResult, Uint64ToString(primitives.Slot(val)))
assert.Equal(t, expectedResult, Uint64ToString(primitives.ValidatorIndex(val)))
assert.Equal(t, expectedResult, Uint64ToString(primitives.CommitteeIndex(val)))
assert.Equal(t, expectedResult, Uint64ToString(primitives.Epoch(val)))
}
func TestBuildURL_NoParams(t *testing.T) {
wanted := "/aaa/bbb/ccc"
actual := BuildURL("/aaa/bbb/ccc")
assert.Equal(t, wanted, actual)
}
func TestBuildURL_WithParams(t *testing.T) {
params := url.Values{}
params.Add("xxxx", "1")
params.Add("yyyy", "2")
params.Add("zzzz", "3")
wanted := "/aaa/bbb/ccc?xxxx=1&yyyy=2&zzzz=3"
actual := BuildURL("/aaa/bbb/ccc", params)
assert.Equal(t, wanted, actual)
}

View File

@@ -5,13 +5,14 @@ go_library(
srcs = [
"client.go",
"doc.go",
"health.go",
"log.go",
"template.go",
],
importpath = "github.com/prysmaticlabs/prysm/v5/api/client/beacon",
visibility = ["//visibility:public"],
deps = [
"//api/client:go_default_library",
"//api/client/beacon/iface:go_default_library",
"//api/server:go_default_library",
"//api/server/structs:go_default_library",
"//consensus-types/primitives:go_default_library",
@@ -26,10 +27,15 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["client_test.go"],
srcs = [
"client_test.go",
"health_test.go",
],
embed = [":go_default_library"],
deps = [
"//api/client:go_default_library",
"//api/client/beacon/testing:go_default_library",
"//testing/require:go_default_library",
"@org_uber_go_mock//gomock:go_default_library",
],
)

View File

@@ -11,6 +11,7 @@ import (
"regexp"
"sort"
"strconv"
"text/template"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
@@ -63,6 +64,23 @@ func IdFromSlot(s primitives.Slot) StateOrBlockId {
return StateOrBlockId(strconv.FormatUint(uint64(s), 10))
}
// idTemplate is used to create template functions that can interpolate StateOrBlockId values.
func idTemplate(ts string) func(StateOrBlockId) string {
t := template.Must(template.New("").Parse(ts))
f := func(id StateOrBlockId) string {
b := bytes.NewBuffer(nil)
err := t.Execute(b, struct{ Id string }{Id: string(id)})
if err != nil {
panic(fmt.Sprintf("invalid idTemplate: %s", ts))
}
return b.String()
}
// run the template to ensure that it is valid
// this should happen load time (using package scoped vars) to ensure runtime errors aren't possible
_ = f(IdGenesis)
return f
}
// RenderGetBlockPath formats a block id into a path for the GetBlock API endpoint.
func RenderGetBlockPath(id StateOrBlockId) string {
return path.Join(getSignedBlockPath, string(id))
@@ -96,6 +114,8 @@ func (c *Client) GetBlock(ctx context.Context, blockId StateOrBlockId) ([]byte,
return b, nil
}
var getBlockRootTpl = idTemplate(getBlockRootPath)
// GetBlockRoot retrieves the hash_tree_root of the BeaconBlock for the given block id.
// Block identifier can be one of: "head" (canonical head in node's view), "genesis", "finalized",
// <slot>, <hex encoded blockRoot with 0x prefix>. Variables of type StateOrBlockId are exported by this package
@@ -118,6 +138,8 @@ func (c *Client) GetBlockRoot(ctx context.Context, blockId StateOrBlockId) ([32]
return bytesutil.ToBytes32(rs), nil
}
var getForkTpl = idTemplate(getForkForStatePath)
// GetFork queries the Beacon Node API for the Fork from the state identified by stateId.
// Block identifier can be one of: "head" (canonical head in node's view), "genesis", "finalized",
// <slot>, <hex encoded blockRoot with 0x prefix>. Variables of type StateOrBlockId are exported by this package

View File

@@ -1,18 +1,20 @@
package health
package beacon
import (
"context"
"sync"
"github.com/prysmaticlabs/prysm/v5/api/client/beacon/iface"
)
type NodeHealthTracker struct {
isHealthy *bool
healthChan chan bool
node Node
node iface.HealthNode
sync.RWMutex
}
func NewTracker(node Node) Tracker {
func NewNodeHealthTracker(node iface.HealthNode) *NodeHealthTracker {
return &NodeHealthTracker{
node: node,
healthChan: make(chan bool, 1),
@@ -24,7 +26,7 @@ func (n *NodeHealthTracker) HealthUpdates() <-chan bool {
return n.healthChan
}
func (n *NodeHealthTracker) IsHealthy(_ context.Context) bool {
func (n *NodeHealthTracker) IsHealthy() bool {
n.RLock()
defer n.RUnlock()
if n.isHealthy == nil {

View File

@@ -1,20 +0,0 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"health.go",
"interfaces.go",
"mock.go",
],
importpath = "github.com/prysmaticlabs/prysm/v5/api/client/beacon/health",
visibility = ["//visibility:public"],
deps = ["@org_uber_go_mock//gomock:go_default_library"],
)
go_test(
name = "go_default_test",
srcs = ["health_test.go"],
embed = [":go_default_library"],
deps = ["@org_uber_go_mock//gomock:go_default_library"],
)

View File

@@ -1,10 +1,11 @@
package health
package beacon
import (
"context"
"sync"
"testing"
healthTesting "github.com/prysmaticlabs/prysm/v5/api/client/beacon/testing"
"go.uber.org/mock/gomock"
)
@@ -23,7 +24,7 @@ func TestNodeHealth_IsHealthy(t *testing.T) {
isHealthy: &tt.isHealthy,
healthChan: make(chan bool, 1),
}
if got := n.IsHealthy(context.Background()); got != tt.want {
if got := n.IsHealthy(); got != tt.want {
t.Errorf("IsHealthy() = %v, want %v", got, tt.want)
}
})
@@ -46,7 +47,7 @@ func TestNodeHealth_UpdateNodeHealth(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := NewMockHealthClient(ctrl)
client := healthTesting.NewMockHealthClient(ctrl)
client.EXPECT().IsHealthy(gomock.Any()).Return(tt.newStatus)
n := &NodeHealthTracker{
isHealthy: &tt.initial,
@@ -79,8 +80,8 @@ func TestNodeHealth_UpdateNodeHealth(t *testing.T) {
func TestNodeHealth_Concurrency(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := NewMockHealthClient(ctrl)
n := NewTracker(client)
client := healthTesting.NewMockHealthClient(ctrl)
n := NewNodeHealthTracker(client)
var wg sync.WaitGroup
// Number of goroutines to spawn for both reading and writing
@@ -103,7 +104,7 @@ func TestNodeHealth_Concurrency(t *testing.T) {
for i := 0; i < numGoroutines; i++ {
go func() {
defer wg.Done()
_ = n.IsHealthy(context.Background()) // Just read the value
_ = n.IsHealthy() // Just read the value
}()
}

View File

@@ -0,0 +1,8 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["health.go"],
importpath = "github.com/prysmaticlabs/prysm/v5/api/client/beacon/iface",
visibility = ["//visibility:public"],
)

View File

@@ -1,13 +1,13 @@
package health
package iface
import "context"
type Tracker interface {
type HealthTracker interface {
HealthUpdates() <-chan bool
IsHealthy() bool
CheckHealth(ctx context.Context) bool
Node
}
type Node interface {
type HealthNode interface {
IsHealthy(ctx context.Context) bool
}

View File

@@ -1,34 +0,0 @@
package beacon
import (
"bytes"
"fmt"
"text/template"
)
type templateFn func(StateOrBlockId) string
var getBlockRootTpl templateFn
var getForkTpl templateFn
func init() {
// idTemplate is used to create template functions that can interpolate StateOrBlockId values.
idTemplate := func(ts string) func(StateOrBlockId) string {
t := template.Must(template.New("").Parse(ts))
f := func(id StateOrBlockId) string {
b := bytes.NewBuffer(nil)
err := t.Execute(b, struct{ Id string }{Id: string(id)})
if err != nil {
panic(fmt.Sprintf("invalid idTemplate: %s", ts))
}
return b.String()
}
// run the template to ensure that it is valid
// this should happen load time (using package scoped vars) to ensure runtime errors aren't possible
_ = f(IdGenesis)
return f
}
getBlockRootTpl = idTemplate(getBlockRootPath)
getForkTpl = idTemplate(getForkForStatePath)
}

View File

@@ -0,0 +1,12 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["mock.go"],
importpath = "github.com/prysmaticlabs/prysm/v5/api/client/beacon/testing",
visibility = ["//visibility:public"],
deps = [
"//api/client/beacon/iface:go_default_library",
"@org_uber_go_mock//gomock:go_default_library",
],
)

View File

@@ -1,15 +1,16 @@
package health
package testing
import (
"context"
"reflect"
"sync"
"github.com/prysmaticlabs/prysm/v5/api/client/beacon/iface"
"go.uber.org/mock/gomock"
)
var (
_ = Node(&MockHealthClient{})
_ = iface.HealthNode(&MockHealthClient{})
)
// MockHealthClient is a mock of HealthClient interface.

View File

@@ -16,7 +16,6 @@ import (
"github.com/prysmaticlabs/prysm/v5/api"
"github.com/prysmaticlabs/prysm/v5/api/client"
"github.com/prysmaticlabs/prysm/v5/api/server/structs"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
@@ -36,14 +35,9 @@ const (
postRegisterValidatorPath = "/eth/v1/builder/validators"
)
var (
vrExample = &ethpb.SignedValidatorRegistrationV1{}
vrSize = vrExample.SizeSSZ()
errMalformedHostname = errors.New("hostname must include port, separated by one colon, like example.com:3500")
errMalformedRequest = errors.New("required request data are missing")
errNotBlinded = errors.New("submitted block is not blinded")
errVersionUnsupported = errors.New("version is not supported")
)
var errMalformedHostname = errors.New("hostname must include port, separated by one colon, like example.com:3500")
var errMalformedRequest = errors.New("required request data are missing")
var errNotBlinded = errors.New("submitted block is not blinded")
// ClientOpt is a functional option for the Client type (http.Client wrapper)
type ClientOpt func(*Client)
@@ -58,12 +52,6 @@ func WithObserver(m observer) ClientOpt {
}
}
func WithSSZ() ClientOpt {
return func(c *Client) {
c.sszEnabled = true
}
}
type requestLogger struct{}
func (*requestLogger) observe(r *http.Request) (e error) {
@@ -107,10 +95,9 @@ type BuilderClient interface {
// Client provides a collection of helper methods for calling Builder API endpoints.
type Client struct {
hc *http.Client
baseURL *url.URL
obvs []observer
sszEnabled bool
hc *http.Client
baseURL *url.URL
obvs []observer
}
// NewClient constructs a new client with the provided options (ex WithTimeout).
@@ -152,7 +139,7 @@ func (c *Client) NodeURL() string {
type reqOption func(*http.Request)
// do is a generic, opinionated request function to reduce boilerplate amongst the methods in this package api/client/builder.
func (c *Client) do(ctx context.Context, method string, path string, body io.Reader, opts ...reqOption) (res []byte, header http.Header, err error) {
func (c *Client) do(ctx context.Context, method string, path string, body io.Reader, opts ...reqOption) (res []byte, err error) {
ctx, span := trace.StartSpan(ctx, "builder.client.do")
defer func() {
tracing.AnnotateError(span, err)
@@ -168,6 +155,10 @@ func (c *Client) do(ctx context.Context, method string, path string, body io.Rea
if err != nil {
return
}
if method == http.MethodPost {
req.Header.Set("Content-Type", api.JsonMediaType)
}
req.Header.Set("Accept", api.JsonMediaType)
req.Header.Add("User-Agent", version.BuildData())
for _, o := range opts {
o(req)
@@ -196,7 +187,6 @@ func (c *Client) do(ctx context.Context, method string, path string, body io.Rea
err = errors.Wrap(err, "error reading http response body from builder server")
return
}
header = r.Header
return
}
@@ -226,145 +216,64 @@ func (c *Client) GetHeader(ctx context.Context, slot primitives.Slot, parentHash
if err != nil {
return nil, err
}
var getOpts reqOption
if c.sszEnabled {
getOpts = func(r *http.Request) {
r.Header.Set("Accept", api.OctetStreamMediaType)
}
} else {
getOpts = func(r *http.Request) {
r.Header.Set("Accept", api.JsonMediaType)
}
}
data, header, err := c.do(ctx, http.MethodGet, path, nil, getOpts)
hb, err := c.do(ctx, http.MethodGet, path, nil)
if err != nil {
return nil, errors.Wrap(err, "error getting header from builder server")
return nil, err
}
v := &VersionResponse{}
if err := json.Unmarshal(hb, v); err != nil {
return nil, errors.Wrapf(err, "error unmarshaling the builder GetHeader response, using slot=%d, parentHash=%#x, pubkey=%#x", slot, parentHash, pubkey)
}
bid, err := c.parseHeaderResponse(data, header)
ver, err := version.FromString(strings.ToLower(v.Version))
if err != nil {
return nil, errors.Wrapf(
err,
"error rendering exec header template with slot=%d, parentHash=%#x, pubkey=%#x",
slot,
parentHash,
pubkey,
)
return nil, errors.Wrap(err, fmt.Sprintf("unsupported header version %s", strings.ToLower(v.Version)))
}
return bid, nil
}
func (c *Client) parseHeaderResponse(data []byte, header http.Header) (SignedBid, error) {
var versionHeader string
if c.sszEnabled || header.Get(api.VersionHeader) != "" {
versionHeader = header.Get(api.VersionHeader)
} else {
// If we don't have a version header, attempt to parse JSON for version
v := &VersionResponse{}
if err := json.Unmarshal(data, v); err != nil {
return nil, errors.Wrap(
err,
"error unmarshaling builder GetHeader response",
)
}
versionHeader = strings.ToLower(v.Version)
}
ver, err := version.FromString(versionHeader)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("unsupported header version %s", versionHeader))
}
if ver >= version.Electra {
return c.parseHeaderElectra(data)
hr := &ExecHeaderResponseElectra{}
if err := json.Unmarshal(hb, hr); err != nil {
return nil, errors.Wrapf(err, "error unmarshaling the builder GetHeader response, using slot=%d, parentHash=%#x, pubkey=%#x", slot, parentHash, pubkey)
}
p, err := hr.ToProto()
if err != nil {
return nil, errors.Wrapf(err, "could not extract proto message from header")
}
return WrappedSignedBuilderBidElectra(p)
}
if ver >= version.Deneb {
return c.parseHeaderDeneb(data)
hr := &ExecHeaderResponseDeneb{}
if err := json.Unmarshal(hb, hr); err != nil {
return nil, errors.Wrapf(err, "error unmarshaling the builder GetHeader response, using slot=%d, parentHash=%#x, pubkey=%#x", slot, parentHash, pubkey)
}
p, err := hr.ToProto()
if err != nil {
return nil, errors.Wrapf(err, "could not extract proto message from header")
}
return WrappedSignedBuilderBidDeneb(p)
}
if ver >= version.Capella {
return c.parseHeaderCapella(data)
hr := &ExecHeaderResponseCapella{}
if err := json.Unmarshal(hb, hr); err != nil {
return nil, errors.Wrapf(err, "error unmarshaling the builder GetHeader response, using slot=%d, parentHash=%#x, pubkey=%#x", slot, parentHash, pubkey)
}
p, err := hr.ToProto()
if err != nil {
return nil, errors.Wrapf(err, "could not extract proto message from header")
}
return WrappedSignedBuilderBidCapella(p)
}
if ver >= version.Bellatrix {
return c.parseHeaderBellatrix(data)
}
return nil, fmt.Errorf("unsupported header version %s", versionHeader)
}
func (c *Client) parseHeaderElectra(data []byte) (SignedBid, error) {
if c.sszEnabled {
sb := &ethpb.SignedBuilderBidElectra{}
if err := sb.UnmarshalSSZ(data); err != nil {
return nil, errors.Wrap(err, "could not unmarshal SignedBuilderBidElectra SSZ")
hr := &ExecHeaderResponse{}
if err := json.Unmarshal(hb, hr); err != nil {
return nil, errors.Wrapf(err, "error unmarshaling the builder GetHeader response, using slot=%d, parentHash=%#x, pubkey=%#x", slot, parentHash, pubkey)
}
return WrappedSignedBuilderBidElectra(sb)
}
hr := &ExecHeaderResponseElectra{}
if err := json.Unmarshal(data, hr); err != nil {
return nil, errors.Wrap(err, "could not unmarshal ExecHeaderResponseElectra JSON")
}
p, err := hr.ToProto()
if err != nil {
return nil, errors.Wrap(err, "could not convert ExecHeaderResponseElectra to proto")
}
return WrappedSignedBuilderBidElectra(p)
}
func (c *Client) parseHeaderDeneb(data []byte) (SignedBid, error) {
if c.sszEnabled {
sb := &ethpb.SignedBuilderBidDeneb{}
if err := sb.UnmarshalSSZ(data); err != nil {
return nil, errors.Wrap(err, "could not unmarshal SignedBuilderBidDeneb SSZ")
p, err := hr.ToProto()
if err != nil {
return nil, errors.Wrap(err, "could not extract proto message from header")
}
return WrappedSignedBuilderBidDeneb(sb)
return WrappedSignedBuilderBid(p)
}
hr := &ExecHeaderResponseDeneb{}
if err := json.Unmarshal(data, hr); err != nil {
return nil, errors.Wrap(err, "could not unmarshal ExecHeaderResponseDeneb JSON")
}
p, err := hr.ToProto()
if err != nil {
return nil, errors.Wrap(err, "could not convert ExecHeaderResponseDeneb to proto")
}
return WrappedSignedBuilderBidDeneb(p)
}
func (c *Client) parseHeaderCapella(data []byte) (SignedBid, error) {
if c.sszEnabled {
sb := &ethpb.SignedBuilderBidCapella{}
if err := sb.UnmarshalSSZ(data); err != nil {
return nil, errors.Wrap(err, "could not unmarshal SignedBuilderBidCapella SSZ")
}
return WrappedSignedBuilderBidCapella(sb)
}
hr := &ExecHeaderResponseCapella{}
if err := json.Unmarshal(data, hr); err != nil {
return nil, errors.Wrap(err, "could not unmarshal ExecHeaderResponseCapella JSON")
}
p, err := hr.ToProto()
if err != nil {
return nil, errors.Wrap(err, "could not convert ExecHeaderResponseCapella to proto")
}
return WrappedSignedBuilderBidCapella(p)
}
func (c *Client) parseHeaderBellatrix(data []byte) (SignedBid, error) {
if c.sszEnabled {
sb := &ethpb.SignedBuilderBid{}
if err := sb.UnmarshalSSZ(data); err != nil {
return nil, errors.Wrap(err, "could not unmarshal SignedBuilderBid SSZ")
}
return WrappedSignedBuilderBid(sb)
}
hr := &ExecHeaderResponse{}
if err := json.Unmarshal(data, hr); err != nil {
return nil, errors.Wrap(err, "could not unmarshal ExecHeaderResponse JSON")
}
p, err := hr.ToProto()
if err != nil {
return nil, errors.Wrap(err, "could not convert ExecHeaderResponse to proto")
}
return WrappedSignedBuilderBid(p)
return nil, fmt.Errorf("unsupported header version %s", strings.ToLower(v.Version))
}
// RegisterValidator encodes the SignedValidatorRegistrationV1 message to json (including hex-encoding the byte
@@ -379,243 +288,70 @@ func (c *Client) RegisterValidator(ctx context.Context, svr []*ethpb.SignedValid
tracing.AnnotateError(span, err)
return err
}
var (
body []byte
err error
postOpts reqOption
)
if c.sszEnabled {
postOpts = func(r *http.Request) {
r.Header.Set("Content-Type", api.OctetStreamMediaType)
r.Header.Set("Accept", api.OctetStreamMediaType)
}
body, err = sszValidatorRegisterRequest(svr)
if err != nil {
err := errors.Wrap(err, "error ssz encoding the SignedValidatorRegistration value body in RegisterValidator")
tracing.AnnotateError(span, err)
return err
}
} else {
postOpts = func(r *http.Request) {
r.Header.Set("Content-Type", api.JsonMediaType)
r.Header.Set("Accept", api.JsonMediaType)
}
body, err = jsonValidatorRegisterRequest(svr)
if err != nil {
err := errors.Wrap(err, "error json encoding the SignedValidatorRegistration value body in RegisterValidator")
tracing.AnnotateError(span, err)
return err
}
}
if _, _, err = c.do(ctx, http.MethodPost, postRegisterValidatorPath, bytes.NewBuffer(body), postOpts); err != nil {
return errors.Wrap(err, "do")
}
log.WithField("registrationCount", len(svr)).Debug("Successfully registered validator(s) on builder")
return nil
}
func jsonValidatorRegisterRequest(svr []*ethpb.SignedValidatorRegistrationV1) ([]byte, error) {
vs := make([]*structs.SignedValidatorRegistration, len(svr))
for i := 0; i < len(svr); i++ {
vs[i] = structs.SignedValidatorRegistrationFromConsensus(svr[i])
}
body, err := json.Marshal(vs)
if err != nil {
return nil, err
err := errors.Wrap(err, "error encoding the SignedValidatorRegistration value body in RegisterValidator")
tracing.AnnotateError(span, err)
return err
}
return body, nil
}
func sszValidatorRegisterRequest(svr []*ethpb.SignedValidatorRegistrationV1) ([]byte, error) {
if uint64(len(svr)) > params.BeaconConfig().ValidatorRegistryLimit {
return nil, errors.Wrap(errMalformedRequest, "validator registry limit exceeded")
_, err = c.do(ctx, http.MethodPost, postRegisterValidatorPath, bytes.NewBuffer(body))
if err != nil {
return err
}
ssz := make([]byte, vrSize*len(svr))
for i, vr := range svr {
sszrep, err := vr.MarshalSSZ()
if err != nil {
return nil, errors.Wrap(err, "failed to marshal validator registry ssz")
}
copy(ssz[i*vrSize:(i+1)*vrSize], sszrep)
}
return ssz, nil
log.WithField("registrationCount", len(svr)).Debug("Successfully registered validator(s) on builder")
return nil
}
var errResponseVersionMismatch = errors.New("builder API response uses a different version than requested in " + api.VersionHeader + " header")
func getVersionsBlockToPayload(blockVersion int) (int, error) {
if blockVersion >= version.Deneb {
return version.Deneb, nil
}
if blockVersion == version.Capella {
return version.Capella, nil
}
if blockVersion == version.Bellatrix {
return version.Bellatrix, nil
}
return 0, errors.Wrapf(errVersionUnsupported, "block version %d", blockVersion)
}
// SubmitBlindedBlock calls the builder API endpoint that binds the validator to the builder and submits the block.
// The response is the full execution payload used to create the blinded block.
func (c *Client) SubmitBlindedBlock(ctx context.Context, sb interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, *v1.BlobsBundle, error) {
body, postOpts, err := c.buildBlindedBlockRequest(sb)
if err != nil {
return nil, nil, err
}
// post the blinded block - the execution payload response should contain the unblinded payload, along with the
// blobs bundle if it is post deneb.
data, header, err := c.do(ctx, http.MethodPost, postBlindedBeaconBlockPath, bytes.NewBuffer(body), postOpts)
if err != nil {
return nil, nil, errors.Wrap(err, "error posting the blinded block to the builder api")
}
ver, err := c.checkBlockVersion(data, header)
if err != nil {
return nil, nil, err
}
expectedPayloadVer, err := getVersionsBlockToPayload(sb.Version())
if err != nil {
return nil, nil, err
}
gotPayloadVer, err := getVersionsBlockToPayload(ver)
if err != nil {
return nil, nil, err
}
if expectedPayloadVer != gotPayloadVer {
return nil, nil, errors.Wrapf(errResponseVersionMismatch, "expected payload version %d, got %d", expectedPayloadVer, gotPayloadVer)
}
ed, blobs, err := c.parseBlindedBlockResponse(data, ver)
if err != nil {
return nil, nil, err
}
return ed, blobs, nil
}
func (c *Client) checkBlockVersion(respBytes []byte, header http.Header) (int, error) {
var versionHeader string
if c.sszEnabled {
versionHeader = strings.ToLower(header.Get(api.VersionHeader))
} else {
// fallback to JSON-based version extraction
v := &VersionResponse{}
if err := json.Unmarshal(respBytes, v); err != nil {
return 0, errors.Wrapf(err, "error unmarshaling JSON version fallback")
}
versionHeader = strings.ToLower(v.Version)
}
ver, err := version.FromString(versionHeader)
if err != nil {
return 0, errors.Wrapf(err, "unsupported header version %s", versionHeader)
}
return ver, nil
}
// Helper: build request body for SubmitBlindedBlock
func (c *Client) buildBlindedBlockRequest(sb interfaces.ReadOnlySignedBeaconBlock) ([]byte, reqOption, error) {
if !sb.IsBlinded() {
return nil, nil, errNotBlinded
}
if c.sszEnabled {
body, err := sb.MarshalSSZ()
if err != nil {
return nil, nil, errors.Wrap(err, "could not marshal SSZ for blinded block")
}
opt := func(r *http.Request) {
r.Header.Set(api.VersionHeader, version.String(sb.Version()))
r.Header.Set("Content-Type", api.OctetStreamMediaType)
r.Header.Set("Accept", api.OctetStreamMediaType)
}
return body, opt, nil
}
// massage the proto struct type data into the api response type.
mj, err := structs.SignedBeaconBlockMessageJsoner(sb)
if err != nil {
return nil, nil, errors.Wrap(err, "error generating blinded beacon block post request")
}
body, err := json.Marshal(mj)
if err != nil {
return nil, nil, errors.Wrap(err, "error marshaling blinded block to JSON")
return nil, nil, errors.Wrap(err, "error marshaling blinded block post request to json")
}
opt := func(r *http.Request) {
r.Header.Set(api.VersionHeader, version.String(sb.Version()))
postOpts := func(r *http.Request) {
r.Header.Add("Eth-Consensus-Version", version.String(sb.Version()))
r.Header.Set("Content-Type", api.JsonMediaType)
r.Header.Set("Accept", api.JsonMediaType)
}
return body, opt, nil
}
// Helper: parse the response returned by SubmitBlindedBlock
func (c *Client) parseBlindedBlockResponse(
respBytes []byte,
forkVersion int,
) (interfaces.ExecutionData, *v1.BlobsBundle, error) {
if c.sszEnabled {
return c.parseBlindedBlockResponseSSZ(respBytes, forkVersion)
// post the blinded block - the execution payload response should contain the unblinded payload, along with the
// blobs bundle if it is post deneb.
rb, err := c.do(ctx, http.MethodPost, postBlindedBeaconBlockPath, bytes.NewBuffer(body), postOpts)
if err != nil {
return nil, nil, errors.Wrap(err, "error posting the blinded block to the builder api")
}
return c.parseBlindedBlockResponseJSON(respBytes, forkVersion)
}
func (c *Client) parseBlindedBlockResponseSSZ(
respBytes []byte,
forkVersion int,
) (interfaces.ExecutionData, *v1.BlobsBundle, error) {
if forkVersion >= version.Deneb {
payloadAndBlobs := &v1.ExecutionPayloadDenebAndBlobsBundle{}
if err := payloadAndBlobs.UnmarshalSSZ(respBytes); err != nil {
return nil, nil, errors.Wrap(err, "unable to unmarshal ExecutionPayloadDenebAndBlobsBundle SSZ")
}
ed, err := blocks.NewWrappedExecutionData(payloadAndBlobs.Payload)
if err != nil {
return nil, nil, errors.Wrapf(err, "unable to wrap execution data for %s", version.String(forkVersion))
}
return ed, payloadAndBlobs.BlobsBundle, nil
} else if forkVersion >= version.Capella {
payload := &v1.ExecutionPayloadCapella{}
if err := payload.UnmarshalSSZ(respBytes); err != nil {
return nil, nil, errors.Wrap(err, "unable to unmarshal ExecutionPayloadCapella SSZ")
}
ed, err := blocks.NewWrappedExecutionData(payload)
if err != nil {
return nil, nil, errors.Wrapf(err, "unable to wrap execution data for %s", version.String(forkVersion))
}
return ed, nil, nil
} else if forkVersion >= version.Bellatrix {
payload := &v1.ExecutionPayload{}
if err := payload.UnmarshalSSZ(respBytes); err != nil {
return nil, nil, errors.Wrap(err, "unable to unmarshal ExecutionPayload SSZ")
}
ed, err := blocks.NewWrappedExecutionData(payload)
if err != nil {
return nil, nil, errors.Wrapf(err, "unable to wrap execution data for %s", version.String(forkVersion))
}
return ed, nil, nil
} else {
return nil, nil, fmt.Errorf("unsupported header version %s", version.String(forkVersion))
}
}
func (c *Client) parseBlindedBlockResponseJSON(
respBytes []byte,
forkVersion int,
) (interfaces.ExecutionData, *v1.BlobsBundle, error) {
// ExecutionPayloadResponse parses just the outer container and the Value key, enabling it to use the .Value
// key to determine which underlying data type to use to finish the unmarshaling.
ep := &ExecutionPayloadResponse{}
if err := json.Unmarshal(respBytes, ep); err != nil {
return nil, nil, errors.Wrap(err, "error unmarshaling ExecutionPayloadResponse")
if err := json.Unmarshal(rb, ep); err != nil {
return nil, nil, errors.Wrap(err, "error unmarshaling the builder ExecutionPayloadResponse")
}
if strings.ToLower(ep.Version) != version.String(sb.Version()) {
return nil, nil, errors.Wrapf(errResponseVersionMismatch, "req=%s, recv=%s", strings.ToLower(ep.Version), version.String(sb.Version()))
}
// This parses the rest of the response and returns the inner data field.
pp, err := ep.ParsePayload()
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to parse payload with version=%s", ep.Version)
return nil, nil, errors.Wrapf(err, "failed to parse execution payload from builder with version=%s", ep.Version)
}
// Get the payload as a proto.Message so it can be wrapped as an execution payload interface.
pb, err := pp.PayloadProto()
if err != nil {
return nil, nil, err
@@ -624,13 +360,11 @@ func (c *Client) parseBlindedBlockResponseJSON(
if err != nil {
return nil, nil, err
}
// Check if it contains blobs
bb, ok := pp.(BlobBundler)
if ok {
bbpb, err := bb.BundleProto()
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to extract blobs bundle from version=%s", ep.Version)
return nil, nil, errors.Wrapf(err, "failed to extract blobs bundle from builder response with version=%s", ep.Version)
}
return ed, bbpb, nil
}
@@ -641,10 +375,7 @@ func (c *Client) parseBlindedBlockResponseJSON(
// response, and an error response may have an error message. This method will return a nil value for error in the
// happy path, and an error with information about the server response body for a non-200 response.
func (c *Client) Status(ctx context.Context) error {
getOpts := func(r *http.Request) {
r.Header.Set("Accept", api.JsonMediaType)
}
_, _, err := c.do(ctx, http.MethodGet, getStatus, nil, getOpts)
_, err := c.do(ctx, http.MethodGet, getStatus, nil)
return err
}
@@ -659,18 +390,6 @@ func non200Err(response *http.Response) error {
}
msg := fmt.Sprintf("code=%d, url=%s, body=%s", response.StatusCode, response.Request.URL, body)
switch response.StatusCode {
case http.StatusUnsupportedMediaType:
log.WithError(ErrUnsupportedMediaType).Debug(msg)
if jsonErr := json.Unmarshal(bodyBytes, &errMessage); jsonErr != nil {
return errors.Wrap(jsonErr, "unable to read response body")
}
return errors.Wrap(ErrUnsupportedMediaType, errMessage.Message)
case http.StatusNotAcceptable:
log.WithError(ErrNotAcceptable).Debug(msg)
if jsonErr := json.Unmarshal(bodyBytes, &errMessage); jsonErr != nil {
return errors.Wrap(jsonErr, "unable to read response body")
}
return errors.Wrap(ErrNotAcceptable, errMessage.Message)
case http.StatusNoContent:
log.WithError(ErrNoContent).Debug(msg)
return ErrNoContent

View File

@@ -8,6 +8,7 @@ import (
"io"
"net/http"
"net/url"
"strconv"
"testing"
"github.com/prysmaticlabs/go-bitfield"
@@ -87,84 +88,39 @@ func TestClient_RegisterValidator(t *testing.T) {
ctx := context.Background()
expectedBody := `[{"message":{"fee_recipient":"0x0000000000000000000000000000000000000000","gas_limit":"23","timestamp":"42","pubkey":"0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"},"signature":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"}]`
expectedPath := "/eth/v1/builder/validators"
t.Run("JSON success", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, api.JsonMediaType, r.Header.Get("Content-Type"))
require.Equal(t, api.JsonMediaType, r.Header.Get("Accept"))
body, err := io.ReadAll(r.Body)
defer func() {
require.NoError(t, r.Body.Close())
}()
require.NoError(t, err)
require.Equal(t, expectedBody, string(body))
require.Equal(t, expectedPath, r.URL.Path)
require.Equal(t, http.MethodPost, r.Method)
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewBuffer(nil)),
Request: r.Clone(ctx),
}, nil
}),
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
}
reg := &eth.SignedValidatorRegistrationV1{
Message: &eth.ValidatorRegistrationV1{
FeeRecipient: ezDecode(t, params.BeaconConfig().EthBurnAddressHex),
GasLimit: 23,
Timestamp: 42,
Pubkey: ezDecode(t, "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"),
},
Signature: ezDecode(t, "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"),
}
require.NoError(t, c.RegisterValidator(ctx, []*eth.SignedValidatorRegistrationV1{reg}))
})
t.Run("SSZ success", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, api.OctetStreamMediaType, r.Header.Get("Content-Type"))
require.Equal(t, api.OctetStreamMediaType, r.Header.Get("Accept"))
body, err := io.ReadAll(r.Body)
defer func() {
require.NoError(t, r.Body.Close())
}()
require.NoError(t, err)
request := &eth.SignedValidatorRegistrationV1{}
itemBytes := body[:request.SizeSSZ()]
require.NoError(t, request.UnmarshalSSZ(itemBytes))
jsRequest := structs.SignedValidatorRegistrationFromConsensus(request)
js, err := json.Marshal([]*structs.SignedValidatorRegistration{jsRequest})
require.NoError(t, err)
require.Equal(t, expectedBody, string(js))
require.Equal(t, expectedPath, r.URL.Path)
require.Equal(t, http.MethodPost, r.Method)
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewBuffer(nil)),
Request: r.Clone(ctx),
}, nil
}),
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
sszEnabled: true,
}
reg := &eth.SignedValidatorRegistrationV1{
Message: &eth.ValidatorRegistrationV1{
FeeRecipient: ezDecode(t, params.BeaconConfig().EthBurnAddressHex),
GasLimit: 23,
Timestamp: 42,
Pubkey: ezDecode(t, "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"),
},
Signature: ezDecode(t, "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"),
}
require.NoError(t, c.RegisterValidator(ctx, []*eth.SignedValidatorRegistrationV1{reg}))
})
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, api.JsonMediaType, r.Header.Get("Content-Type"))
require.Equal(t, api.JsonMediaType, r.Header.Get("Accept"))
body, err := io.ReadAll(r.Body)
defer func() {
require.NoError(t, r.Body.Close())
}()
require.NoError(t, err)
require.Equal(t, expectedBody, string(body))
require.Equal(t, expectedPath, r.URL.Path)
require.Equal(t, http.MethodPost, r.Method)
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewBuffer(nil)),
Request: r.Clone(ctx),
}, nil
}),
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
}
reg := &eth.SignedValidatorRegistrationV1{
Message: &eth.ValidatorRegistrationV1{
FeeRecipient: ezDecode(t, params.BeaconConfig().EthBurnAddressHex),
GasLimit: 23,
Timestamp: 42,
Pubkey: ezDecode(t, "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"),
},
Signature: ezDecode(t, "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"),
}
require.NoError(t, c.RegisterValidator(ctx, []*eth.SignedValidatorRegistrationV1{reg}))
}
func TestClient_GetHeader(t *testing.T) {
@@ -220,7 +176,6 @@ func TestClient_GetHeader(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, expectedPath, r.URL.Path)
require.Equal(t, api.JsonMediaType, r.Header.Get("Accept"))
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewBufferString(testExampleHeaderResponse)),
@@ -252,56 +207,9 @@ func TestClient_GetHeader(t *testing.T) {
require.Equal(t, 0, value.Int.Cmp(primitives.WeiToBigInt(bid.Value())))
require.Equal(t, bidStr, primitives.WeiToBigInt(bid.Value()).String())
})
t.Run("bellatrix ssz", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, api.OctetStreamMediaType, r.Header.Get("Accept"))
require.Equal(t, expectedPath, r.URL.Path)
epr := &ExecHeaderResponse{}
require.NoError(t, json.Unmarshal([]byte(testExampleHeaderResponse), epr))
pro, err := epr.ToProto()
require.NoError(t, err)
ssz, err := pro.MarshalSSZ()
require.NoError(t, err)
header := http.Header{}
header.Set(api.VersionHeader, "bellatrix")
return &http.Response{
StatusCode: http.StatusOK,
Header: header,
Body: io.NopCloser(bytes.NewBuffer(ssz)),
Request: r.Clone(ctx),
}, nil
}),
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
sszEnabled: true,
}
h, err := c.GetHeader(ctx, slot, bytesutil.ToBytes32(parentHash), bytesutil.ToBytes48(pubkey))
require.NoError(t, err)
expectedSig := ezDecode(t, "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505")
require.Equal(t, true, bytes.Equal(expectedSig, h.Signature()))
expectedTxRoot := ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2")
bid, err := h.Message()
require.NoError(t, err)
bidHeader, err := bid.Header()
require.NoError(t, err)
withdrawalsRoot, err := bidHeader.TransactionsRoot()
require.NoError(t, err)
require.Equal(t, true, bytes.Equal(expectedTxRoot, withdrawalsRoot))
require.Equal(t, uint64(1), bidHeader.GasUsed())
// this matches the value in the testExampleHeaderResponse
bidStr := "652312848583266388373324160190187140051835877600158453279131187530910662656"
value, err := stringToUint256(bidStr)
require.NoError(t, err)
require.Equal(t, 0, value.Int.Cmp(primitives.WeiToBigInt(bid.Value())))
require.Equal(t, bidStr, primitives.WeiToBigInt(bid.Value()).String())
})
t.Run("capella", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, api.JsonMediaType, r.Header.Get("Accept"))
require.Equal(t, expectedPath, r.URL.Path)
return &http.Response{
StatusCode: http.StatusOK,
@@ -330,52 +238,9 @@ func TestClient_GetHeader(t *testing.T) {
require.Equal(t, 0, value.Int.Cmp(primitives.WeiToBigInt(bid.Value())))
require.Equal(t, bidStr, primitives.WeiToBigInt(bid.Value()).String())
})
t.Run("capella ssz", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, api.OctetStreamMediaType, r.Header.Get("Accept"))
require.Equal(t, expectedPath, r.URL.Path)
epr := &ExecHeaderResponseCapella{}
require.NoError(t, json.Unmarshal([]byte(testExampleHeaderResponseCapella), epr))
pro, err := epr.ToProto()
require.NoError(t, err)
ssz, err := pro.MarshalSSZ()
require.NoError(t, err)
header := http.Header{}
header.Set(api.VersionHeader, "capella")
return &http.Response{
StatusCode: http.StatusOK,
Header: header,
Body: io.NopCloser(bytes.NewBuffer(ssz)),
Request: r.Clone(ctx),
}, nil
}),
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
sszEnabled: true,
}
h, err := c.GetHeader(ctx, slot, bytesutil.ToBytes32(parentHash), bytesutil.ToBytes48(pubkey))
require.NoError(t, err)
expectedWithdrawalsRoot := ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2")
bid, err := h.Message()
require.NoError(t, err)
bidHeader, err := bid.Header()
require.NoError(t, err)
withdrawalsRoot, err := bidHeader.WithdrawalsRoot()
require.NoError(t, err)
require.Equal(t, true, bytes.Equal(expectedWithdrawalsRoot, withdrawalsRoot))
bidStr := "652312848583266388373324160190187140051835877600158453279131187530910662656"
value, err := stringToUint256(bidStr)
require.NoError(t, err)
require.Equal(t, 0, value.Int.Cmp(primitives.WeiToBigInt(bid.Value())))
require.Equal(t, bidStr, primitives.WeiToBigInt(bid.Value()).String())
})
t.Run("deneb", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, api.JsonMediaType, r.Header.Get("Accept"))
require.Equal(t, expectedPath, r.URL.Path)
return &http.Response{
StatusCode: http.StatusOK,
@@ -412,56 +277,6 @@ func TestClient_GetHeader(t *testing.T) {
require.Equal(t, len(kcgCommitments[i]) == 48, true)
}
})
t.Run("deneb ssz", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, api.OctetStreamMediaType, r.Header.Get("Accept"))
require.Equal(t, expectedPath, r.URL.Path)
epr := &ExecHeaderResponseDeneb{}
require.NoError(t, json.Unmarshal([]byte(testExampleHeaderResponseDeneb), epr))
pro, err := epr.ToProto()
require.NoError(t, err)
ssz, err := pro.MarshalSSZ()
require.NoError(t, err)
header := http.Header{}
header.Set(api.VersionHeader, "deneb")
return &http.Response{
StatusCode: http.StatusOK,
Header: header,
Body: io.NopCloser(bytes.NewBuffer(ssz)),
Request: r.Clone(ctx),
}, nil
}),
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
sszEnabled: true,
}
h, err := c.GetHeader(ctx, slot, bytesutil.ToBytes32(parentHash), bytesutil.ToBytes48(pubkey))
require.NoError(t, err)
expectedWithdrawalsRoot := ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2")
bid, err := h.Message()
require.NoError(t, err)
bidHeader, err := bid.Header()
require.NoError(t, err)
withdrawalsRoot, err := bidHeader.WithdrawalsRoot()
require.NoError(t, err)
require.Equal(t, true, bytes.Equal(expectedWithdrawalsRoot, withdrawalsRoot))
bidStr := "652312848583266388373324160190187140051835877600158453279131187530910662656"
value, err := stringToUint256(bidStr)
require.NoError(t, err)
require.Equal(t, 0, value.Int.Cmp(primitives.WeiToBigInt(bid.Value())))
require.Equal(t, bidStr, primitives.WeiToBigInt(bid.Value()).String())
dbid, ok := bid.(builderBidDeneb)
require.Equal(t, true, ok)
kcgCommitments := dbid.BlobKzgCommitments()
require.Equal(t, len(kcgCommitments) > 0, true)
for i := range kcgCommitments {
require.Equal(t, len(kcgCommitments[i]) == 48, true)
}
})
t.Run("deneb, too many kzg commitments", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
@@ -478,12 +293,11 @@ func TestClient_GetHeader(t *testing.T) {
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
}
_, err := c.GetHeader(ctx, slot, bytesutil.ToBytes32(parentHash), bytesutil.ToBytes48(pubkey))
require.ErrorContains(t, "could not convert ExecHeaderResponseDeneb to proto: too many blob commitments: 7", err)
require.ErrorContains(t, "could not extract proto message from header: too many blob commitments: 7", err)
})
t.Run("electra", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, api.JsonMediaType, r.Header.Get("Accept"))
require.Equal(t, expectedPath, r.URL.Path)
return &http.Response{
StatusCode: http.StatusOK,
@@ -524,61 +338,6 @@ func TestClient_GetHeader(t *testing.T) {
require.Equal(t, 1, len(requests.Withdrawals))
require.Equal(t, 1, len(requests.Consolidations))
})
t.Run("electra ssz", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, api.OctetStreamMediaType, r.Header.Get("Accept"))
require.Equal(t, expectedPath, r.URL.Path)
epr := &ExecHeaderResponseElectra{}
require.NoError(t, json.Unmarshal([]byte(testExampleHeaderResponseElectra), epr))
pro, err := epr.ToProto()
require.NoError(t, err)
ssz, err := pro.MarshalSSZ()
require.NoError(t, err)
header := http.Header{}
header.Set(api.VersionHeader, "electra")
return &http.Response{
StatusCode: http.StatusOK,
Header: header,
Body: io.NopCloser(bytes.NewBuffer(ssz)),
Request: r.Clone(ctx),
}, nil
}),
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
sszEnabled: true,
}
h, err := c.GetHeader(ctx, slot, bytesutil.ToBytes32(parentHash), bytesutil.ToBytes48(pubkey))
require.NoError(t, err)
expectedWithdrawalsRoot := ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2")
bid, err := h.Message()
require.NoError(t, err)
bidHeader, err := bid.Header()
require.NoError(t, err)
withdrawalsRoot, err := bidHeader.WithdrawalsRoot()
require.NoError(t, err)
require.Equal(t, true, bytes.Equal(expectedWithdrawalsRoot, withdrawalsRoot))
bidStr := "652312848583266388373324160190187140051835877600158453279131187530910662656"
value, err := stringToUint256(bidStr)
require.NoError(t, err)
require.Equal(t, 0, value.Int.Cmp(primitives.WeiToBigInt(bid.Value())))
require.Equal(t, bidStr, primitives.WeiToBigInt(bid.Value()).String())
ebid, ok := bid.(builderBidElectra)
require.Equal(t, true, ok)
kcgCommitments := ebid.BlobKzgCommitments()
require.Equal(t, len(kcgCommitments) > 0, true)
for i := range kcgCommitments {
require.Equal(t, len(kcgCommitments[i]) == 48, true)
}
requests := ebid.ExecutionRequests()
require.Equal(t, 1, len(requests.Deposits))
require.Equal(t, 1, len(requests.Withdrawals))
require.Equal(t, 1, len(requests.Consolidations))
})
t.Run("unsupported version", func(t *testing.T) {
hc := &http.Client{
@@ -631,51 +390,11 @@ func TestSubmitBlindedBlock(t *testing.T) {
require.Equal(t, fmt.Sprintf("%#x", bfpg.SSZBytes()), fmt.Sprintf("%#x", ep.BaseFeePerGas()))
require.Equal(t, uint64(1), ep.GasLimit())
})
t.Run("bellatrix ssz", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, postBlindedBeaconBlockPath, r.URL.Path)
require.Equal(t, "bellatrix", r.Header.Get(api.VersionHeader))
require.Equal(t, api.OctetStreamMediaType, r.Header.Get("Content-Type"))
require.Equal(t, api.OctetStreamMediaType, r.Header.Get("Accept"))
epr := &ExecutionPayloadResponse{}
require.NoError(t, json.Unmarshal([]byte(testExampleExecutionPayload), epr))
ep := &ExecutionPayload{}
require.NoError(t, json.Unmarshal(epr.Data, ep))
pro, err := ep.ToProto()
require.NoError(t, err)
ssz, err := pro.MarshalSSZ()
require.NoError(t, err)
header := http.Header{}
header.Set(api.VersionHeader, "bellatrix")
return &http.Response{
StatusCode: http.StatusOK,
Header: header,
Body: io.NopCloser(bytes.NewBuffer(ssz)),
Request: r.Clone(ctx),
}, nil
}),
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
sszEnabled: true,
}
sbbb, err := blocks.NewSignedBeaconBlock(testSignedBlindedBeaconBlockBellatrix(t))
require.NoError(t, err)
ep, _, err := c.SubmitBlindedBlock(ctx, sbbb)
require.NoError(t, err)
require.Equal(t, true, bytes.Equal(ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"), ep.ParentHash()))
bfpg, err := stringToUint256("452312848583266388373324160190187140051835877600158453279131187530910662656")
require.NoError(t, err)
require.Equal(t, fmt.Sprintf("%#x", bfpg.SSZBytes()), fmt.Sprintf("%#x", ep.BaseFeePerGas()))
require.Equal(t, uint64(1), ep.GasLimit())
})
t.Run("capella", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, postBlindedBeaconBlockPath, r.URL.Path)
require.Equal(t, "capella", r.Header.Get(api.VersionHeader))
require.Equal(t, "capella", r.Header.Get("Eth-Consensus-Version"))
require.Equal(t, api.JsonMediaType, r.Header.Get("Content-Type"))
require.Equal(t, api.JsonMediaType, r.Header.Get("Accept"))
return &http.Response{
@@ -701,54 +420,12 @@ func TestSubmitBlindedBlock(t *testing.T) {
assert.DeepEqual(t, ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943"), withdrawals[0].Address)
assert.Equal(t, uint64(1), withdrawals[0].Amount)
})
t.Run("capella ssz", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, postBlindedBeaconBlockPath, r.URL.Path)
require.Equal(t, "capella", r.Header.Get(api.VersionHeader))
require.Equal(t, api.OctetStreamMediaType, r.Header.Get("Content-Type"))
require.Equal(t, api.OctetStreamMediaType, r.Header.Get("Accept"))
epr := &ExecutionPayloadResponse{}
require.NoError(t, json.Unmarshal([]byte(testExampleExecutionPayloadCapella), epr))
ep := &ExecutionPayloadCapella{}
require.NoError(t, json.Unmarshal(epr.Data, ep))
pro, err := ep.ToProto()
require.NoError(t, err)
ssz, err := pro.MarshalSSZ()
require.NoError(t, err)
header := http.Header{}
header.Set(api.VersionHeader, "capella")
return &http.Response{
StatusCode: http.StatusOK,
Header: header,
Body: io.NopCloser(bytes.NewBuffer(ssz)),
Request: r.Clone(ctx),
}, nil
}),
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
sszEnabled: true,
}
sbb, err := blocks.NewSignedBeaconBlock(testSignedBlindedBeaconBlockCapella(t))
require.NoError(t, err)
ep, _, err := c.SubmitBlindedBlock(ctx, sbb)
require.NoError(t, err)
withdrawals, err := ep.Withdrawals()
require.NoError(t, err)
require.Equal(t, 1, len(withdrawals))
assert.Equal(t, uint64(1), withdrawals[0].Index)
assert.Equal(t, primitives.ValidatorIndex(1), withdrawals[0].ValidatorIndex)
assert.DeepEqual(t, ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943"), withdrawals[0].Address)
assert.Equal(t, uint64(1), withdrawals[0].Amount)
})
t.Run("deneb", func(t *testing.T) {
test := testSignedBlindedBeaconBlockDeneb(t)
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, postBlindedBeaconBlockPath, r.URL.Path)
require.Equal(t, "deneb", r.Header.Get(api.VersionHeader))
require.Equal(t, "deneb", r.Header.Get("Eth-Consensus-Version"))
require.Equal(t, api.JsonMediaType, r.Header.Get("Content-Type"))
require.Equal(t, api.JsonMediaType, r.Header.Get("Accept"))
var req structs.SignedBlindedBeaconBlockDeneb
@@ -783,140 +460,6 @@ func TestSubmitBlindedBlock(t *testing.T) {
assert.Equal(t, uint64(1), withdrawals[0].Amount)
require.NotNil(t, blobBundle)
})
t.Run("deneb ssz", func(t *testing.T) {
test := testSignedBlindedBeaconBlockDeneb(t)
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, postBlindedBeaconBlockPath, r.URL.Path)
require.Equal(t, "deneb", r.Header.Get(api.VersionHeader))
require.Equal(t, api.OctetStreamMediaType, r.Header.Get("Content-Type"))
require.Equal(t, api.OctetStreamMediaType, r.Header.Get("Accept"))
epr := &ExecPayloadResponseDeneb{}
require.NoError(t, json.Unmarshal([]byte(testExampleExecutionPayloadDeneb), epr))
pro, blob, err := epr.ToProto()
require.NoError(t, err)
combined := &v1.ExecutionPayloadDenebAndBlobsBundle{
Payload: pro,
BlobsBundle: blob,
}
ssz, err := combined.MarshalSSZ()
require.NoError(t, err)
header := http.Header{}
header.Set(api.VersionHeader, "deneb")
return &http.Response{
StatusCode: http.StatusOK,
Header: header,
Body: io.NopCloser(bytes.NewBuffer(ssz)),
Request: r.Clone(ctx),
}, nil
}),
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
sszEnabled: true,
}
sbb, err := blocks.NewSignedBeaconBlock(test)
require.NoError(t, err)
ep, blobBundle, err := c.SubmitBlindedBlock(ctx, sbb)
require.NoError(t, err)
withdrawals, err := ep.Withdrawals()
require.NoError(t, err)
require.Equal(t, 1, len(withdrawals))
assert.Equal(t, uint64(1), withdrawals[0].Index)
assert.Equal(t, primitives.ValidatorIndex(1), withdrawals[0].ValidatorIndex)
assert.DeepEqual(t, ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943"), withdrawals[0].Address)
assert.Equal(t, uint64(1), withdrawals[0].Amount)
require.NotNil(t, blobBundle)
})
t.Run("electra", func(t *testing.T) {
test := testSignedBlindedBeaconBlockElectra(t)
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, postBlindedBeaconBlockPath, r.URL.Path)
require.Equal(t, "electra", r.Header.Get(api.VersionHeader))
require.Equal(t, api.JsonMediaType, r.Header.Get("Content-Type"))
require.Equal(t, api.JsonMediaType, r.Header.Get("Accept"))
var req structs.SignedBlindedBeaconBlockElectra
err := json.NewDecoder(r.Body).Decode(&req)
require.NoError(t, err)
block, err := req.ToConsensus()
require.NoError(t, err)
require.DeepEqual(t, block, test)
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewBufferString(testExampleExecutionPayloadDeneb)),
Request: r.Clone(ctx),
}, nil
}),
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
}
sbb, err := blocks.NewSignedBeaconBlock(test)
require.NoError(t, err)
ep, blobBundle, err := c.SubmitBlindedBlock(ctx, sbb)
require.NoError(t, err)
withdrawals, err := ep.Withdrawals()
require.NoError(t, err)
require.Equal(t, 1, len(withdrawals))
assert.Equal(t, uint64(1), withdrawals[0].Index)
assert.Equal(t, primitives.ValidatorIndex(1), withdrawals[0].ValidatorIndex)
assert.DeepEqual(t, ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943"), withdrawals[0].Address)
assert.Equal(t, uint64(1), withdrawals[0].Amount)
require.NotNil(t, blobBundle)
})
t.Run("electra ssz", func(t *testing.T) {
test := testSignedBlindedBeaconBlockElectra(t)
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, postBlindedBeaconBlockPath, r.URL.Path)
require.Equal(t, "electra", r.Header.Get(api.VersionHeader))
require.Equal(t, api.OctetStreamMediaType, r.Header.Get("Content-Type"))
require.Equal(t, api.OctetStreamMediaType, r.Header.Get("Accept"))
epr := &ExecPayloadResponseDeneb{}
require.NoError(t, json.Unmarshal([]byte(testExampleExecutionPayloadDeneb), epr))
pro, blob, err := epr.ToProto()
require.NoError(t, err)
combined := &v1.ExecutionPayloadDenebAndBlobsBundle{
Payload: pro,
BlobsBundle: blob,
}
ssz, err := combined.MarshalSSZ()
require.NoError(t, err)
header := http.Header{}
header.Set(api.VersionHeader, "electra")
return &http.Response{
StatusCode: http.StatusOK,
Header: header,
Body: io.NopCloser(bytes.NewBuffer(ssz)),
Request: r.Clone(ctx),
}, nil
}),
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
sszEnabled: true,
}
sbb, err := blocks.NewSignedBeaconBlock(test)
require.NoError(t, err)
ep, blobBundle, err := c.SubmitBlindedBlock(ctx, sbb)
require.NoError(t, err)
withdrawals, err := ep.Withdrawals()
require.NoError(t, err)
require.Equal(t, 1, len(withdrawals))
assert.Equal(t, uint64(1), withdrawals[0].Index)
assert.Equal(t, primitives.ValidatorIndex(1), withdrawals[0].ValidatorIndex)
assert.DeepEqual(t, ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943"), withdrawals[0].Address)
assert.Equal(t, uint64(1), withdrawals[0].Amount)
require.NotNil(t, blobBundle)
})
t.Run("mismatched versions, expected bellatrix got capella", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
@@ -1043,13 +586,7 @@ func testSignedBlindedBeaconBlockBellatrix(t *testing.T) *eth.SignedBlindedBeaco
},
Deposits: []*eth.Deposit{
{
Proof: func() [][]byte {
b := make([][]byte, 33)
for i := range b {
b[i] = ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2")
}
return b
}(),
Proof: [][]byte{ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2")},
Data: &eth.Deposit_Data{
PublicKey: ezDecode(t, "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"),
WithdrawalCredentials: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
@@ -1068,8 +605,8 @@ func testSignedBlindedBeaconBlockBellatrix(t *testing.T) *eth.SignedBlindedBeaco
},
},
SyncAggregate: &eth.SyncAggregate{
SyncCommitteeSignature: make([]byte, 96),
SyncCommitteeBits: make(bitfield.Bitvector512, 64),
SyncCommitteeSignature: make([]byte, 48),
SyncCommitteeBits: bitfield.Bitvector512{0x01},
},
ExecutionPayloadHeader: &v1.ExecutionPayloadHeader{
ParentHash: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
@@ -1083,7 +620,7 @@ func testSignedBlindedBeaconBlockBellatrix(t *testing.T) *eth.SignedBlindedBeaco
GasUsed: 1,
Timestamp: 1,
ExtraData: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
BaseFeePerGas: ezDecode(t, "0x4523128485832663883733241601901871400518358776001584532791311875"),
BaseFeePerGas: []byte(strconv.FormatUint(1, 10)),
BlockHash: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
TransactionsRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
},
@@ -1191,13 +728,7 @@ func testSignedBlindedBeaconBlockCapella(t *testing.T) *eth.SignedBlindedBeaconB
},
Deposits: []*eth.Deposit{
{
Proof: func() [][]byte {
b := make([][]byte, 33)
for i := range b {
b[i] = ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2")
}
return b
}(),
Proof: [][]byte{ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2")},
Data: &eth.Deposit_Data{
PublicKey: ezDecode(t, "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"),
WithdrawalCredentials: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
@@ -1216,8 +747,8 @@ func testSignedBlindedBeaconBlockCapella(t *testing.T) *eth.SignedBlindedBeaconB
},
},
SyncAggregate: &eth.SyncAggregate{
SyncCommitteeSignature: make([]byte, 96),
SyncCommitteeBits: make(bitfield.Bitvector512, 64),
SyncCommitteeSignature: make([]byte, 48),
SyncCommitteeBits: bitfield.Bitvector512{0x01},
},
ExecutionPayloadHeader: &v1.ExecutionPayloadHeaderCapella{
ParentHash: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
@@ -1231,7 +762,7 @@ func testSignedBlindedBeaconBlockCapella(t *testing.T) *eth.SignedBlindedBeaconB
GasUsed: 1,
Timestamp: 1,
ExtraData: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
BaseFeePerGas: ezDecode(t, "0x4523128485832663883733241601901871400518358776001584532791311875"),
BaseFeePerGas: []byte(strconv.FormatUint(1, 10)),
BlockHash: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
TransactionsRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
WithdrawalsRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
@@ -1344,13 +875,7 @@ func testSignedBlindedBeaconBlockDeneb(t *testing.T) *eth.SignedBlindedBeaconBlo
},
Deposits: []*eth.Deposit{
{
Proof: func() [][]byte {
b := make([][]byte, 33)
for i := range b {
b[i] = ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2")
}
return b
}(),
Proof: [][]byte{ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2")},
Data: &eth.Deposit_Data{
PublicKey: ezDecode(t, "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"),
WithdrawalCredentials: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
@@ -1397,163 +922,6 @@ func testSignedBlindedBeaconBlockDeneb(t *testing.T) *eth.SignedBlindedBeaconBlo
}
}
func testSignedBlindedBeaconBlockElectra(t *testing.T) *eth.SignedBlindedBeaconBlockElectra {
basebytes, err := bytesutil.Uint256ToSSZBytes("14074904626401341155369551180448584754667373453244490859944217516317499064576")
if err != nil {
log.Error(err)
}
return &eth.SignedBlindedBeaconBlockElectra{
Message: &eth.BlindedBeaconBlockElectra{
Slot: 1,
ProposerIndex: 1,
ParentRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
StateRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
Body: &eth.BlindedBeaconBlockBodyElectra{
RandaoReveal: ezDecode(t, "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"),
Eth1Data: &eth.Eth1Data{
DepositRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
DepositCount: 1,
BlockHash: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
},
Graffiti: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
ProposerSlashings: []*eth.ProposerSlashing{
{
Header_1: &eth.SignedBeaconBlockHeader{
Header: &eth.BeaconBlockHeader{
Slot: 1,
ProposerIndex: 1,
ParentRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
StateRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
BodyRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
},
Signature: ezDecode(t, "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"),
},
Header_2: &eth.SignedBeaconBlockHeader{
Header: &eth.BeaconBlockHeader{
Slot: 1,
ProposerIndex: 1,
ParentRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
StateRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
BodyRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
},
Signature: ezDecode(t, "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"),
},
},
},
AttesterSlashings: []*eth.AttesterSlashingElectra{
{
Attestation_1: &eth.IndexedAttestationElectra{
AttestingIndices: []uint64{1},
Data: &eth.AttestationData{
Slot: 1,
CommitteeIndex: 1,
BeaconBlockRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
Source: &eth.Checkpoint{
Epoch: 1,
Root: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
},
Target: &eth.Checkpoint{
Epoch: 1,
Root: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
},
},
Signature: ezDecode(t, "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"),
},
Attestation_2: &eth.IndexedAttestationElectra{
AttestingIndices: []uint64{1},
Data: &eth.AttestationData{
Slot: 1,
CommitteeIndex: 1,
BeaconBlockRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
Source: &eth.Checkpoint{
Epoch: 1,
Root: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
},
Target: &eth.Checkpoint{
Epoch: 1,
Root: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
},
},
Signature: ezDecode(t, "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"),
},
},
},
Attestations: []*eth.AttestationElectra{
{
AggregationBits: bitfield.Bitlist{0x01},
Data: &eth.AttestationData{
Slot: 1,
CommitteeIndex: 1,
BeaconBlockRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
Source: &eth.Checkpoint{
Epoch: 1,
Root: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
},
Target: &eth.Checkpoint{
Epoch: 1,
Root: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
},
},
CommitteeBits: make(bitfield.Bitvector64, 8),
Signature: ezDecode(t, "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"),
},
},
Deposits: []*eth.Deposit{
{
Proof: func() [][]byte {
b := make([][]byte, 33)
for i := range b {
b[i] = ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2")
}
return b
}(),
Data: &eth.Deposit_Data{
PublicKey: ezDecode(t, "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"),
WithdrawalCredentials: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
Amount: 1,
Signature: ezDecode(t, "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"),
},
},
},
VoluntaryExits: []*eth.SignedVoluntaryExit{
{
Exit: &eth.VoluntaryExit{
Epoch: 1,
ValidatorIndex: 1,
},
Signature: ezDecode(t, "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"),
},
},
SyncAggregate: &eth.SyncAggregate{
SyncCommitteeSignature: make([]byte, 96),
SyncCommitteeBits: ezDecode(t, "0x6451e9f951ebf05edc01de67e593484b672877054f055903ff0df1a1a945cf30ca26bb4d4b154f94a1bc776bcf5d0efb3603e1f9b8ee2499ccdcfe2a18cef458"),
},
ExecutionRequests: &v1.ExecutionRequests{},
ExecutionPayloadHeader: &v1.ExecutionPayloadHeaderDeneb{
ParentHash: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
FeeRecipient: ezDecode(t, "0xabcf8e0d4e9587369b2301d0790347320302cc09"),
StateRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
ReceiptsRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
LogsBloom: ezDecode(t, "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
PrevRandao: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
BlockNumber: 1,
GasLimit: 1,
GasUsed: 1,
Timestamp: 1,
ExtraData: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
BaseFeePerGas: basebytes,
BlockHash: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
TransactionsRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
WithdrawalsRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
BlobGasUsed: 1,
ExcessBlobGas: 2,
},
},
},
Signature: ezDecode(t, "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"),
}
}
func TestRequestLogger(t *testing.T) {
wo := WithObserver(&requestLogger{})
c, err := NewClient("localhost:3500", wo)

View File

@@ -15,9 +15,3 @@ var ErrBadRequest = errors.Wrap(ErrNotOK, "recv 400 BadRequest response from API
// ErrNoContent specifically means that a '204 - No Content' response was received from the API.
// Typically, a 204 is a success but in this case for the Header API means No header is available
var ErrNoContent = errors.New("recv 204 no content response from API, No header is available")
// ErrUnsupportedMediaType specifically means that a '415 - Unsupported Media Type' was received from the API.
var ErrUnsupportedMediaType = errors.Wrap(ErrNotOK, "The media type in \"Content-Type\" header is unsupported, and the request has been rejected. This occurs when a HTTP request supplies a payload in a content-type that the server is not able to handle.")
// ErrNotAcceptable specifically means that a '406 - Not Acceptable' was received from the API.
var ErrNotAcceptable = errors.Wrap(ErrNotOK, "The accept header value is not acceptable")

View File

@@ -1,9 +1,8 @@
package httprest
import (
"time"
"net/http"
"time"
"github.com/prysmaticlabs/prysm/v5/api/server/middleware"
)

View File

@@ -78,8 +78,8 @@ type GetBlockHeaderResponse struct {
}
type GetValidatorsRequest struct {
Ids []string `json:"ids,omitempty"`
Statuses []string `json:"statuses,omitempty"`
Ids []string `json:"ids"`
Statuses []string `json:"statuses"`
}
type GetValidatorsResponse struct {
@@ -100,12 +100,6 @@ type GetValidatorBalancesResponse struct {
Data []*ValidatorBalance `json:"data"`
}
type GetValidatorIdentitiesResponse struct {
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
Data []*ValidatorIdentity `json:"data"`
}
type ValidatorContainer struct {
Index string `json:"index"`
Balance string `json:"balance"`
@@ -118,12 +112,6 @@ type ValidatorBalance struct {
Balance string `json:"balance"`
}
type ValidatorIdentity struct {
Index string `json:"index"`
Pubkey string `json:"pubkey"`
ActivationEpoch string `json:"activation_epoch"`
}
type GetBlockResponse struct {
Data *SignedBlock `json:"data"`
}

View File

@@ -20,11 +20,6 @@ type BlockEvent struct {
ExecutionOptimistic bool `json:"execution_optimistic"`
}
type BlockGossipEvent struct {
Slot string `json:"slot"`
Block string `json:"block"`
}
type AggregatedAttEventSource struct {
Aggregate *Attestation `json:"aggregate"`
}

View File

@@ -154,7 +154,7 @@ retry:
continue retry
}
if sub == nil {
panic("event: ResubscribeFunc returned nil subscription and no error") // lint:nopanic -- This should never happen.
panic("event: ResubscribeFunc returned nil subscription and no error")
}
return sub
case <-s.unsub:

View File

@@ -128,7 +128,6 @@ go_test(
"receive_block_test.go",
"service_norace_test.go",
"service_test.go",
"setup_forkchoice_test.go",
"setup_test.go",
"weak_subjectivity_checks_test.go",
],

View File

@@ -51,7 +51,6 @@ type ForkchoiceFetcher interface {
ProposerBoost() [32]byte
RecentBlockSlot(root [32]byte) (primitives.Slot, error)
IsCanonical(ctx context.Context, blockRoot [32]byte) (bool, error)
DependentRoot(primitives.Epoch) ([32]byte, error)
}
// TimeFetcher retrieves the Ethereum consensus data that's related to time.

View File

@@ -3,13 +3,10 @@ package blockchain
import (
"context"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
consensus_blocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/forkchoice"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)
// CachedHeadRoot returns the corresponding value from Forkchoice
@@ -103,33 +100,3 @@ func (s *Service) ParentRoot(root [32]byte) ([32]byte, error) {
defer s.cfg.ForkChoiceStore.RUnlock()
return s.cfg.ForkChoiceStore.ParentRoot(root)
}
// hashForGenesisBlock returns the right hash for the genesis block
func (s *Service) hashForGenesisBlock(ctx context.Context, root [32]byte) ([]byte, error) {
genRoot, err := s.cfg.BeaconDB.GenesisBlockRoot(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not get genesis block root")
}
if root != genRoot {
return nil, errNotGenesisRoot
}
st, err := s.cfg.BeaconDB.GenesisState(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not get genesis state")
}
if st.Version() < version.Bellatrix {
return nil, nil
}
header, err := st.LatestExecutionPayloadHeader()
if err != nil {
return nil, errors.Wrap(err, "could not get latest execution payload header")
}
return bytesutil.SafeCopyBytes(header.BlockHash()), nil
}
// DependentRoot wraps the corresponding method in forkchoice
func (s *Service) DependentRoot(epoch primitives.Epoch) ([32]byte, error) {
s.cfg.ForkChoiceStore.RLock()
defer s.cfg.ForkChoiceStore.RUnlock()
return s.cfg.ForkChoiceStore.DependentRoot(epoch)
}

View File

@@ -582,7 +582,6 @@ func TestService_IsOptimisticForRoot_StateSummaryRecovered(t *testing.T) {
br, err := b.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, context.Background(), beaconDB, b)
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, [32]byte{}))
_, err = c.IsOptimisticForRoot(ctx, br)
assert.NoError(t, err)
summ, err := beaconDB.StateSummary(ctx, br)
@@ -613,20 +612,3 @@ func TestService_IsFinalized(t *testing.T) {
require.Equal(t, true, c.IsFinalized(ctx, br))
require.Equal(t, false, c.IsFinalized(ctx, [32]byte{'c'}))
}
func Test_hashForGenesisRoot(t *testing.T) {
beaconDB := testDB.SetupDB(t)
ctx := context.Background()
c := setupBeaconChain(t, beaconDB)
st, _ := util.DeterministicGenesisStateElectra(t, 10)
require.NoError(t, c.cfg.BeaconDB.SaveGenesisData(ctx, st))
root, err := beaconDB.GenesisBlockRoot(ctx)
require.NoError(t, err)
genRoot, err := c.hashForGenesisBlock(ctx, [32]byte{'a'})
require.ErrorIs(t, err, errNotGenesisRoot)
require.IsNil(t, genRoot)
genRoot, err = c.hashForGenesisBlock(ctx, root)
require.NoError(t, err)
require.Equal(t, [32]byte{}, [32]byte(genRoot))
}

View File

@@ -30,10 +30,6 @@ var (
ErrNotCheckpoint = errors.New("not a checkpoint in forkchoice")
// ErrNilHead is returned when no head is present in the blockchain service.
ErrNilHead = errors.New("nil head")
// errNotGenesisRoot is returned when the root is not the genesis block root.
errNotGenesisRoot = errors.New("root is not the genesis block root")
// errBlacklistedBlock is returned when a block is blacklisted as invalid.
errBlacklistedRoot = errors.New("block root is blacklisted")
)
var errMaxBlobsExceeded = errors.New("Expected commitments in block exceeds MAX_BLOBS_PER_BLOCK")

View File

@@ -69,21 +69,10 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *fcuConfig) (*
SafeBlockHash: justifiedHash[:],
FinalizedBlockHash: finalizedHash[:],
}
if len(fcs.HeadBlockHash) != 32 || [32]byte(fcs.HeadBlockHash) == [32]byte{} {
// check if we are sending FCU at genesis
hash, err := s.hashForGenesisBlock(ctx, arg.headRoot)
if errors.Is(err, errNotGenesisRoot) {
log.Error("Sending nil head block hash to execution engine")
return nil, nil
}
if err != nil {
return nil, errors.Wrap(err, "could not get head block hash")
}
fcs.HeadBlockHash = hash
}
if arg.attributes == nil {
arg.attributes = payloadattribute.EmptyWithVersion(headBlk.Version())
}
go firePayloadAttributesEvent(ctx, s.cfg.StateNotifier.StateFeed(), arg)
payloadID, lastValidHash, err := s.cfg.ExecutionEngineCaller.ForkchoiceUpdated(ctx, fcs, arg.attributes)
if err != nil {
switch {
@@ -170,7 +159,6 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *fcuConfig) (*
log.WithFields(logrus.Fields{
"blockRoot": fmt.Sprintf("%#x", bytesutil.Trunc(arg.headRoot[:])),
"headSlot": headBlk.Slot(),
"nextSlot": nextSlot,
"payloadID": fmt.Sprintf("%#x", bytesutil.Trunc(payloadID[:])),
}).Info("Forkchoice updated with payload attributes for proposal")
s.cfg.PayloadIDCache.Set(nextSlot, arg.headRoot, pId)
@@ -178,19 +166,40 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *fcuConfig) (*
log.WithFields(logrus.Fields{
"blockHash": fmt.Sprintf("%#x", headPayload.BlockHash()),
"slot": headBlk.Slot(),
"nextSlot": nextSlot,
}).Error("Received nil payload ID on VALID engine response")
}
return payloadID, nil
}
func firePayloadAttributesEvent(_ context.Context, f event.SubscriberSender, nextSlot primitives.Slot) {
// the fcu args have differing amounts of completeness based on the code path,
// and there is work we only want to do if a client is actually listening to the events beacon api endpoint.
// temporary solution: just fire a blank event and fill in the details in the api handler.
func firePayloadAttributesEvent(ctx context.Context, f event.SubscriberSender, cfg *fcuConfig) {
pidx, err := helpers.BeaconProposerIndex(ctx, cfg.headState)
if err != nil {
log.WithError(err).
WithField("head_root", cfg.headRoot[:]).
Error("Could not get proposer index for PayloadAttributes event")
return
}
evd := payloadattribute.EventData{
ProposerIndex: pidx,
ProposalSlot: cfg.headState.Slot(),
ParentBlockRoot: cfg.headRoot[:],
Attributer: cfg.attributes,
HeadRoot: cfg.headRoot,
HeadState: cfg.headState,
HeadBlock: cfg.headBlock,
}
if cfg.headBlock != nil && !cfg.headBlock.IsNil() {
headPayload, err := cfg.headBlock.Block().Body().Execution()
if err != nil {
log.WithError(err).Error("Could not get execution payload for head block")
return
}
evd.ParentBlockHash = headPayload.BlockHash()
evd.ParentBlockNumber = headPayload.BlockNumber()
}
f.Send(&feed.Event{
Type: statefeed.PayloadAttributes,
Data: payloadattribute.EventData{ProposalSlot: nextSlot},
Data: evd,
})
}

View File

@@ -84,7 +84,7 @@ func Test_NotifyForkchoiceUpdate_GetPayloadAttrErrorCanContinue(t *testing.T) {
service.cfg.PayloadIDCache.Set(1, [32]byte{}, [8]byte{})
got, err := service.notifyForkchoiceUpdate(ctx, arg)
require.NoError(t, err)
require.IsNil(t, got)
require.DeepEqual(t, got, pid) // We still get a payload ID even though the state is bad. This means it returns until the end.
}
func Test_NotifyForkchoiceUpdate(t *testing.T) {
@@ -113,7 +113,6 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
state, blkRoot, err = prepareForkchoiceState(ctx, 2, bellatrixBlkRoot, altairBlkRoot, params.BeaconConfig().ZeroHash, ojc, ofc)
require.NoError(t, err)
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
badHash := [32]byte{'h'}
tests := []struct {
name string
@@ -211,7 +210,7 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
blk: func() interfaces.ReadOnlySignedBeaconBlock {
b, err := consensusblocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockBellatrix{Block: &ethpb.BeaconBlockBellatrix{
Body: &ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: &v1.ExecutionPayload{BlockHash: badHash[:]},
ExecutionPayload: &v1.ExecutionPayload{},
},
}})
require.NoError(t, err)

View File

@@ -102,10 +102,10 @@ func (s *Service) forkchoiceUpdateWithExecution(ctx context.Context, args *fcuCo
log.WithError(err).Error("could not save head")
}
go firePayloadAttributesEvent(ctx, s.cfg.StateNotifier.StateFeed(), s.CurrentSlot()+1)
// Only need to prune attestations from pool if the head has changed.
s.pruneAttsFromPool(s.ctx, args.headState, args.headBlock)
if err := s.pruneAttsFromPool(s.ctx, args.headState, args.headBlock); err != nil {
log.WithError(err).Error("could not prune attestations from pool")
}
return nil
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed"
statefeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/state"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/features"
@@ -327,22 +328,34 @@ func (s *Service) notifyNewHeadEvent(
newHeadStateRoot,
newHeadRoot []byte,
) error {
currEpoch := slots.ToEpoch(newHeadSlot)
currentDutyDependentRoot, err := s.DependentRoot(currEpoch)
previousDutyDependentRoot := s.originBlockRoot[:]
currentDutyDependentRoot := s.originBlockRoot[:]
var previousDutyEpoch primitives.Epoch
currentDutyEpoch := slots.ToEpoch(newHeadSlot)
if currentDutyEpoch > 0 {
previousDutyEpoch = currentDutyEpoch.Sub(1)
}
currentDutySlot, err := slots.EpochStart(currentDutyEpoch)
if err != nil {
return errors.Wrap(err, "could not get duty dependent root")
return errors.Wrap(err, "could not get duty slot")
}
if currentDutyDependentRoot == [32]byte{} {
currentDutyDependentRoot = s.originBlockRoot
previousDutySlot, err := slots.EpochStart(previousDutyEpoch)
if err != nil {
return errors.Wrap(err, "could not get duty slot")
}
previousDutyDependentRoot := currentDutyDependentRoot
if currEpoch > 0 {
previousDutyDependentRoot, err = s.DependentRoot(currEpoch.Sub(1))
if currentDutySlot > 0 {
currentDutyDependentRoot, err = helpers.BlockRootAtSlot(newHeadState, currentDutySlot-1)
if err != nil {
return errors.Wrap(err, "could not get duty dependent root")
}
}
if previousDutySlot > 0 {
previousDutyDependentRoot, err = helpers.BlockRootAtSlot(newHeadState, previousDutySlot-1)
if err != nil {
return errors.Wrap(err, "could not get duty dependent root")
}
}
isOptimistic, err := s.IsOptimistic(ctx)
if err != nil {
return errors.Wrap(err, "could not check if node is optimistically synced")
@@ -354,8 +367,8 @@ func (s *Service) notifyNewHeadEvent(
Block: newHeadRoot,
State: newHeadStateRoot,
EpochTransition: slots.IsEpochStart(newHeadSlot),
PreviousDutyDependentRoot: previousDutyDependentRoot[:],
CurrentDutyDependentRoot: currentDutyDependentRoot[:],
PreviousDutyDependentRoot: previousDutyDependentRoot,
CurrentDutyDependentRoot: currentDutyDependentRoot,
ExecutionOptimistic: isOptimistic,
},
})
@@ -397,11 +410,7 @@ func (s *Service) saveOrphanedOperations(ctx context.Context, orphanedRoot [32]b
return err
}
} else {
if orphanedBlk.Version() >= version.Electra {
if err = s.cfg.AttPool.SaveBlockAttestation(a); err != nil {
return err
}
} else if a.IsAggregated() {
if a.IsAggregated() {
if err = s.cfg.AttPool.SaveAggregatedAttestation(a); err != nil {
return err
}

View File

@@ -9,7 +9,6 @@ import (
mock "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing"
testDB "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree"
forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/blstoexec"
"github.com/prysmaticlabs/prysm/v5/config/params"
@@ -157,17 +156,14 @@ func Test_notifyNewHeadEvent(t *testing.T) {
notifier := &mock.MockStateNotifier{RecordEvents: true}
srv := &Service{
cfg: &config{
StateNotifier: notifier,
ForkChoiceStore: doublylinkedtree.New(),
StateNotifier: notifier,
},
originBlockRoot: [32]byte{1},
}
st, blk, err := prepareForkchoiceState(context.Background(), 0, [32]byte{}, [32]byte{}, [32]byte{}, &ethpb.Checkpoint{}, &ethpb.Checkpoint{})
require.NoError(t, err)
require.NoError(t, srv.cfg.ForkChoiceStore.InsertNode(context.Background(), st, blk))
newHeadStateRoot := [32]byte{2}
newHeadRoot := [32]byte{3}
require.NoError(t, srv.notifyNewHeadEvent(context.Background(), 1, bState, newHeadStateRoot[:], newHeadRoot[:]))
err := srv.notifyNewHeadEvent(context.Background(), 1, bState, newHeadStateRoot[:], newHeadRoot[:])
require.NoError(t, err)
events := notifier.ReceivedEvents()
require.Equal(t, 1, len(events))
@@ -189,14 +185,10 @@ func Test_notifyNewHeadEvent(t *testing.T) {
genesisRoot := [32]byte{1}
srv := &Service{
cfg: &config{
StateNotifier: notifier,
ForkChoiceStore: doublylinkedtree.New(),
StateNotifier: notifier,
},
originBlockRoot: genesisRoot,
}
st, blk, err := prepareForkchoiceState(context.Background(), 0, [32]byte{}, [32]byte{}, [32]byte{}, &ethpb.Checkpoint{}, &ethpb.Checkpoint{})
require.NoError(t, err)
require.NoError(t, srv.cfg.ForkChoiceStore.InsertNode(context.Background(), st, blk))
epoch1Start, err := slots.EpochStart(1)
require.NoError(t, err)
epoch2Start, err := slots.EpochStart(1)
@@ -217,8 +209,8 @@ func Test_notifyNewHeadEvent(t *testing.T) {
Block: newHeadRoot[:],
State: newHeadStateRoot[:],
EpochTransition: true,
PreviousDutyDependentRoot: make([]byte, 32),
CurrentDutyDependentRoot: srv.originBlockRoot[:],
PreviousDutyDependentRoot: genesisRoot[:],
CurrentDutyDependentRoot: make([]byte, 32),
}
require.DeepSSZEqual(t, wanted, eventHead)
})
@@ -332,72 +324,6 @@ func TestSaveOrphanedAtts(t *testing.T) {
require.DeepEqual(t, wantAtts, atts)
}
func TestSaveOrphanedAttsElectra(t *testing.T) {
ctx := context.Background()
beaconDB := testDB.SetupDB(t)
service := setupBeaconChain(t, beaconDB)
service.genesisTime = time.Now().Add(time.Duration(-10*int64(1)*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second)
// Chain setup
// 0 -- 1 -- 2 -- 3
// \-4
st, keys := util.DeterministicGenesisStateElectra(t, 64)
blkG, err := util.GenerateFullBlockElectra(st, keys, util.DefaultBlockGenConfig(), 1)
assert.NoError(t, err)
util.SaveBlock(t, ctx, service.cfg.BeaconDB, blkG)
rG, err := blkG.Block.HashTreeRoot()
require.NoError(t, err)
blk1, err := util.GenerateFullBlockElectra(st, keys, util.DefaultBlockGenConfig(), 2)
assert.NoError(t, err)
blk1.Block.ParentRoot = rG[:]
r1, err := blk1.Block.HashTreeRoot()
require.NoError(t, err)
blk2, err := util.GenerateFullBlockElectra(st, keys, util.DefaultBlockGenConfig(), 3)
assert.NoError(t, err)
blk2.Block.ParentRoot = r1[:]
r2, err := blk2.Block.HashTreeRoot()
require.NoError(t, err)
blk3, err := util.GenerateFullBlockElectra(st, keys, util.DefaultBlockGenConfig(), 4)
assert.NoError(t, err)
blk3.Block.ParentRoot = r2[:]
r3, err := blk3.Block.HashTreeRoot()
require.NoError(t, err)
blk4 := util.NewBeaconBlockElectra()
blk4.Block.Slot = 4
blk4.Block.ParentRoot = rG[:]
r4, err := blk4.Block.HashTreeRoot()
require.NoError(t, err)
ojc := &ethpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
ofc := &ethpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
for _, blk := range []*ethpb.SignedBeaconBlockElectra{blkG, blk1, blk2, blk3, blk4} {
r, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
state, blkRoot, err := prepareForkchoiceState(ctx, blk.Block.Slot, r, bytesutil.ToBytes32(blk.Block.ParentRoot), [32]byte{}, ojc, ofc)
require.NoError(t, err)
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
util.SaveBlock(t, ctx, beaconDB, blk)
}
require.NoError(t, service.saveOrphanedOperations(ctx, r3, r4))
require.Equal(t, 3, len(service.cfg.AttPool.BlockAttestations()))
wantAtts := []ethpb.Att{
blk3.Block.Body.Attestations[0],
blk2.Block.Body.Attestations[0],
blk1.Block.Body.Attestations[0],
}
atts := service.cfg.AttPool.BlockAttestations()
sort.Slice(atts, func(i, j int) bool {
return atts[i].GetData().Slot > atts[j].GetData().Slot
})
require.DeepEqual(t, wantAtts, atts)
}
func TestSaveOrphanedOps(t *testing.T) {
params.SetupTestConfigCleanup(t)
config := params.BeaconConfig()

View File

@@ -4164,4 +4164,4 @@
"0xa92039a08b5502d5b211a7744099c9f93fa8c90cedcb1d05e92f01886219dd464eb5fb0337496ad96ed09c987da4e5f019035c5b01cc09b2a18b8a8dd419bc5895388a07e26958f6bd26751929c25f89b8eb4a299d822e2d26fec9ef350e0d3c",
"0x92dcc5a1c8c3e1b28b1524e3dd6dbecd63017c9201da9dbe077f1b82adc08c50169f56fc7b5a3b28ec6b89254de3e2fd12838a761053437883c3e01ba616670cea843754548ef84bcc397de2369adcca2ab54cd73c55dc68d87aec3fc2fe4f10"
]
}
}

View File

@@ -3,7 +3,6 @@ package blockchain
import (
"testing"
mock "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing"
testDB "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/startup"
@@ -19,7 +18,6 @@ func testServiceOptsWithDB(t *testing.T) []Option {
WithStateGen(stategen.New(beaconDB, fcs)),
WithForkChoiceStore(fcs),
WithClockSynchronizer(cs),
WithStateNotifier(&mock.MockStateNotifier{RecordEvents: true}),
}
}

View File

@@ -18,7 +18,6 @@ import (
"github.com/prysmaticlabs/prysm/v5/time/slots"
)
// The caller of this function must have a lock on forkchoice.
func (s *Service) getRecentPreState(ctx context.Context, c *ethpb.Checkpoint) state.ReadOnlyBeaconState {
headEpoch := slots.ToEpoch(s.HeadSlot())
if c.Epoch < headEpoch {
@@ -28,6 +27,13 @@ func (s *Service) getRecentPreState(ctx context.Context, c *ethpb.Checkpoint) st
return nil
}
if c.Epoch == headEpoch {
targetSlot, err := s.cfg.ForkChoiceStore.Slot([32]byte(c.Root))
if err != nil {
return nil
}
if slots.ToEpoch(targetSlot)+1 < headEpoch {
return nil
}
st, err := s.HeadStateReadOnly(ctx)
if err != nil {
return nil
@@ -59,13 +65,12 @@ func (s *Service) getRecentPreState(ctx context.Context, c *ethpb.Checkpoint) st
return nil
}
if err := s.checkpointStateCache.AddCheckpointState(c, st); err != nil {
log.WithError(err).Error("Could not save checkpoint state to cache")
return nil
}
return st
}
// getAttPreState retrieves the att pre state by either from the cache or the DB.
// The caller of this function must have a lock on forkchoice.
func (s *Service) getAttPreState(ctx context.Context, c *ethpb.Checkpoint) (state.ReadOnlyBeaconState, error) {
// If the attestation is recent and canonical we can use the head state to compute the shuffling.
if st := s.getRecentPreState(ctx, c); st != nil {

View File

@@ -175,9 +175,6 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []consensusblocks.ROBlo
var set *bls.SignatureBatch
boundaries := make(map[[32]byte]state.BeaconState)
for i, b := range blks {
if features.BlacklistedBlock(b.Root()) {
return errBlacklistedRoot
}
v, h, err := getStateVersionAndPayload(preState)
if err != nil {
return err
@@ -426,12 +423,13 @@ func (s *Service) savePostStateInfo(ctx context.Context, r [32]byte, b interface
// pruneAttsFromPool removes these attestations from the attestation pool
// which are covered by attestations from the received block.
func (s *Service) pruneAttsFromPool(ctx context.Context, headState state.BeaconState, headBlock interfaces.ReadOnlySignedBeaconBlock) {
func (s *Service) pruneAttsFromPool(ctx context.Context, headState state.BeaconState, headBlock interfaces.ReadOnlySignedBeaconBlock) error {
for _, att := range headBlock.Block().Body().Attestations() {
if err := s.pruneCoveredAttsFromPool(ctx, headState, att); err != nil {
log.WithError(err).Warn("Could not prune attestations covered by a received block's attestation")
}
}
return nil
}
func (s *Service) pruneCoveredAttsFromPool(ctx context.Context, headState state.BeaconState, att ethpb.Att) error {
@@ -505,10 +503,6 @@ func (s *Service) pruneCoveredElectraAttsFromPool(ctx context.Context, headState
if err = s.cfg.AttestationCache.DeleteCovered(a); err != nil {
return errors.Wrap(err, "could not delete covered attestation")
}
} else if !a.IsAggregated() {
if err = s.cfg.AttPool.DeleteUnaggregatedAttestation(a); err != nil {
return errors.Wrap(err, "could not delete unaggregated attestation")
}
} else if err = s.cfg.AttPool.DeleteAggregatedAttestation(a); err != nil {
return errors.Wrap(err, "could not delete aggregated attestation")
}
@@ -729,9 +723,13 @@ func (s *Service) lateBlockTasks(ctx context.Context) {
attribute := s.getPayloadAttribute(ctx, headState, s.CurrentSlot()+1, headRoot[:])
// return early if we are not proposing next slot
if attribute.IsEmpty() {
// notifyForkchoiceUpdate fires the payload attribute event. But in this case, we won't
// call notifyForkchoiceUpdate, so the event is fired here.
go firePayloadAttributesEvent(ctx, s.cfg.StateNotifier.StateFeed(), s.CurrentSlot()+1)
fcuArgs := &fcuConfig{
headState: headState,
headRoot: headRoot,
headBlock: nil,
attributes: attribute,
}
go firePayloadAttributesEvent(ctx, s.cfg.StateNotifier.StateFeed(), fcuArgs)
return
}

View File

@@ -7,13 +7,12 @@ import (
"strings"
"time"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
lightclient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client"
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed"
statefeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/state"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
lightclient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree"
forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types"

View File

@@ -50,7 +50,6 @@ import (
func Test_pruneAttsFromPool_Electra(t *testing.T) {
ctx := context.Background()
logHook := logTest.NewGlobal()
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
@@ -72,7 +71,7 @@ func Test_pruneAttsFromPool_Electra(t *testing.T) {
cb := primitives.NewAttestationCommitteeBits()
cb.SetBitAt(0, true)
att1 := &ethpb.AttestationElectra{
AggregationBits: bitfield.Bitlist{0b10000000, 0b00000001},
AggregationBits: bitfield.Bitlist{0b11110111, 0b00000001},
Data: data,
Signature: make([]byte, 96),
CommitteeBits: cb,
@@ -96,15 +95,16 @@ func Test_pruneAttsFromPool_Electra(t *testing.T) {
CommitteeBits: cb,
}
require.NoError(t, s.cfg.AttPool.SaveUnaggregatedAttestation(att1))
require.NoError(t, s.cfg.AttPool.SaveAggregatedAttestation(att1))
require.NoError(t, s.cfg.AttPool.SaveAggregatedAttestation(att2))
require.NoError(t, s.cfg.AttPool.SaveAggregatedAttestation(att3))
require.Equal(t, 3, len(s.cfg.AttPool.AggregatedAttestations()))
cb = primitives.NewAttestationCommitteeBits()
cb.SetBitAt(0, true)
cb.SetBitAt(1, true)
onChainAtt := &ethpb.AttestationElectra{
AggregationBits: bitfield.Bitlist{0b10000000, 0b11110111, 0b00000001},
AggregationBits: bitfield.Bitlist{0b11110111, 0b11110111, 0b00000001},
Data: data,
Signature: make([]byte, 96),
CommitteeBits: cb,
@@ -126,12 +126,8 @@ func Test_pruneAttsFromPool_Electra(t *testing.T) {
// into the correct number of aggregates.
require.Equal(t, 4, len(committees))
s.pruneAttsFromPool(ctx, st, rob)
require.LogsDoNotContain(t, logHook, "Could not prune attestations")
attsInPool := s.cfg.AttPool.UnaggregatedAttestations()
assert.Equal(t, 0, len(attsInPool))
attsInPool = s.cfg.AttPool.AggregatedAttestations()
require.NoError(t, s.pruneAttsFromPool(ctx, st, rob))
attsInPool := s.cfg.AttPool.AggregatedAttestations()
require.Equal(t, 1, len(attsInPool))
assert.DeepEqual(t, att3, attsInPool[0])
}
@@ -912,8 +908,6 @@ func TestInsertFinalizedDeposits_MultipleFinalizedRoutines(t *testing.T) {
}
func TestRemoveBlockAttestationsInPool(t *testing.T) {
logHook := logTest.NewGlobal()
genesis, keys := util.DeterministicGenesisState(t, 64)
b, err := util.GenerateFullBlock(genesis, keys, util.DefaultBlockGenConfig(), 1)
assert.NoError(t, err)
@@ -933,8 +927,7 @@ func TestRemoveBlockAttestationsInPool(t *testing.T) {
require.NoError(t, service.cfg.AttPool.SaveAggregatedAttestations(atts))
wsb, err := consensusblocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
service.pruneAttsFromPool(context.Background(), nil /* state not needed pre-Electra */, wsb)
require.LogsDoNotContain(t, logHook, "Could not prune attestations")
require.NoError(t, service.pruneAttsFromPool(context.Background(), nil /* state not needed pre-Electra */, wsb))
require.Equal(t, 0, service.cfg.AttPool.AggregatedAttestationCount())
}
@@ -1990,7 +1983,6 @@ func TestNoViableHead_Reboot(t *testing.T) {
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, genesisState, genesisRoot), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, genesisRoot), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveGenesisBlockRoot(ctx, genesisRoot), "Could not save genesis state")
for i := 1; i < 6; i++ {
driftGenesisTime(service, int64(i), 0)
@@ -2125,7 +2117,6 @@ func TestNoViableHead_Reboot(t *testing.T) {
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, genesisState, jroot))
service.cfg.ForkChoiceStore.SetBalancesByRooter(service.cfg.StateGen.ActiveNonSlashedBalancesByRoot)
require.NoError(t, service.StartFromSavedState(genesisState))
require.NoError(t, service.cfg.BeaconDB.SaveGenesisBlockRoot(ctx, genesisRoot))
// Forkchoice has the genesisRoot loaded at startup
require.Equal(t, genesisRoot, service.ensureRootNotZeros(service.cfg.ForkChoiceStore.CachedHeadRoot()))
@@ -2135,7 +2126,7 @@ func TestNoViableHead_Reboot(t *testing.T) {
require.Equal(t, genesisRoot, bytesutil.ToBytes32(headRoot))
optimistic, err := service.IsOptimistic(ctx)
require.NoError(t, err)
require.Equal(t, false, optimistic)
require.Equal(t, true, optimistic)
// Check that the node's justified checkpoint does not agree with the
// last valid state's justified checkpoint
@@ -2653,7 +2644,7 @@ func TestSaveLightClientUpdate(t *testing.T) {
t.Run("Altair", func(t *testing.T) {
t.Run("No old update", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair(0, true)
l := util.NewTestLightClient(t).SetupTestAltair()
s.genesisTime = time.Unix(time.Now().Unix()-(int64(params.BeaconConfig().AltairForkEpoch)*int64(params.BeaconConfig().SlotsPerEpoch)*int64(params.BeaconConfig().SecondsPerSlot)), 0)
@@ -2699,7 +2690,7 @@ func TestSaveLightClientUpdate(t *testing.T) {
})
t.Run("New update is better", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair(0, true)
l := util.NewTestLightClient(t).SetupTestAltair()
s.genesisTime = time.Unix(time.Now().Unix()-(int64(params.BeaconConfig().AltairForkEpoch)*int64(params.BeaconConfig().SlotsPerEpoch)*int64(params.BeaconConfig().SecondsPerSlot)), 0)
@@ -2751,7 +2742,7 @@ func TestSaveLightClientUpdate(t *testing.T) {
})
t.Run("Old update is better", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair(0, false)
l := util.NewTestLightClient(t).SetupTestAltair()
s.genesisTime = time.Unix(time.Now().Unix()-(int64(params.BeaconConfig().AltairForkEpoch)*int64(params.BeaconConfig().SlotsPerEpoch)*int64(params.BeaconConfig().SecondsPerSlot)), 0)
@@ -2812,7 +2803,7 @@ func TestSaveLightClientUpdate(t *testing.T) {
t.Run("Capella", func(t *testing.T) {
t.Run("No old update", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(false, 0, true)
l := util.NewTestLightClient(t).SetupTestCapella(false)
s.genesisTime = time.Unix(time.Now().Unix()-(int64(params.BeaconConfig().CapellaForkEpoch)*int64(params.BeaconConfig().SlotsPerEpoch)*int64(params.BeaconConfig().SecondsPerSlot)), 0)
@@ -2857,7 +2848,7 @@ func TestSaveLightClientUpdate(t *testing.T) {
})
t.Run("New update is better", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(false, 0, true)
l := util.NewTestLightClient(t).SetupTestCapella(false)
s.genesisTime = time.Unix(time.Now().Unix()-(int64(params.BeaconConfig().CapellaForkEpoch)*int64(params.BeaconConfig().SlotsPerEpoch)*int64(params.BeaconConfig().SecondsPerSlot)), 0)
@@ -2909,7 +2900,7 @@ func TestSaveLightClientUpdate(t *testing.T) {
})
t.Run("Old update is better", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(false, 0, false)
l := util.NewTestLightClient(t).SetupTestCapella(false)
s.genesisTime = time.Unix(time.Now().Unix()-(int64(params.BeaconConfig().CapellaForkEpoch)*int64(params.BeaconConfig().SlotsPerEpoch)*int64(params.BeaconConfig().SecondsPerSlot)), 0)
@@ -2970,7 +2961,7 @@ func TestSaveLightClientUpdate(t *testing.T) {
t.Run("Deneb", func(t *testing.T) {
t.Run("No old update", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb(false, 0, true)
l := util.NewTestLightClient(t).SetupTestDeneb(false)
s.genesisTime = time.Unix(time.Now().Unix()-(int64(params.BeaconConfig().DenebForkEpoch)*int64(params.BeaconConfig().SlotsPerEpoch)*int64(params.BeaconConfig().SecondsPerSlot)), 0)
@@ -3015,7 +3006,7 @@ func TestSaveLightClientUpdate(t *testing.T) {
})
t.Run("New update is better", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb(false, 0, true)
l := util.NewTestLightClient(t).SetupTestDeneb(false)
s.genesisTime = time.Unix(time.Now().Unix()-(int64(params.BeaconConfig().DenebForkEpoch)*int64(params.BeaconConfig().SlotsPerEpoch)*int64(params.BeaconConfig().SecondsPerSlot)), 0)
@@ -3067,7 +3058,7 @@ func TestSaveLightClientUpdate(t *testing.T) {
})
t.Run("Old update is better", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb(false, 0, false)
l := util.NewTestLightClient(t).SetupTestDeneb(false)
s.genesisTime = time.Unix(time.Now().Unix()-(int64(params.BeaconConfig().DenebForkEpoch)*int64(params.BeaconConfig().SlotsPerEpoch)*int64(params.BeaconConfig().SecondsPerSlot)), 0)
@@ -3138,7 +3129,7 @@ func TestSaveLightClientBootstrap(t *testing.T) {
ctx := tr.ctx
t.Run("Altair", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair(0, true)
l := util.NewTestLightClient(t).SetupTestAltair()
s.genesisTime = time.Unix(time.Now().Unix()-(int64(params.BeaconConfig().AltairForkEpoch)*int64(params.BeaconConfig().SlotsPerEpoch)*int64(params.BeaconConfig().SecondsPerSlot)), 0)
@@ -3173,7 +3164,7 @@ func TestSaveLightClientBootstrap(t *testing.T) {
})
t.Run("Capella", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(false, 0, true)
l := util.NewTestLightClient(t).SetupTestCapella(false)
s.genesisTime = time.Unix(time.Now().Unix()-(int64(params.BeaconConfig().CapellaForkEpoch)*int64(params.BeaconConfig().SlotsPerEpoch)*int64(params.BeaconConfig().SecondsPerSlot)), 0)
@@ -3208,7 +3199,7 @@ func TestSaveLightClientBootstrap(t *testing.T) {
})
t.Run("Deneb", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb(false, 0, true)
l := util.NewTestLightClient(t).SetupTestDeneb(false)
s.genesisTime = time.Unix(time.Now().Unix()-(int64(params.BeaconConfig().DenebForkEpoch)*int64(params.BeaconConfig().SlotsPerEpoch)*int64(params.BeaconConfig().SecondsPerSlot)), 0)

View File

@@ -16,7 +16,6 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/das"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/slasher/types"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/features"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
@@ -65,10 +64,6 @@ type SlashingReceiver interface {
func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock, blockRoot [32]byte, avs das.AvailabilityStore) error {
ctx, span := trace.StartSpan(ctx, "blockChain.ReceiveBlock")
defer span.End()
// Return early if the block is blacklisted
if features.BlacklistedBlock(blockRoot) {
return errBlacklistedRoot
}
// Return early if the block has been synced
if s.InForkchoice(blockRoot) {
log.WithField("blockRoot", fmt.Sprintf("%#x", blockRoot)).Debug("Ignoring already synced block")

View File

@@ -12,7 +12,6 @@ import (
statefeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/state"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/das"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/voluntaryexits"
"github.com/prysmaticlabs/prysm/v5/config/features"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
@@ -42,16 +41,6 @@ func TestService_ReceiveBlock(t *testing.T) {
bc.ShardCommitteePeriod = 0 // Required for voluntary exits test in reasonable time.
params.OverrideBeaconConfig(bc)
badBlock := genFullBlock(t, util.DefaultBlockGenConfig(), 101)
badRoot, err := badBlock.Block.HashTreeRoot()
require.NoError(t, err)
badRoots := make(map[[32]byte]struct{})
badRoots[badRoot] = struct{}{}
resetCfg := features.InitWithReset(&features.Flags{
BlacklistedRoots: badRoots,
})
defer resetCfg()
type args struct {
block *ethpb.SignedBeaconBlock
}
@@ -135,14 +124,8 @@ func TestService_ReceiveBlock(t *testing.T) {
}
},
},
{
name: "The block is blacklisted",
args: args{
block: badBlock,
},
wantedErr: errBlacklistedRoot.Error(),
},
}
wg := new(sync.WaitGroup)
for _, tt := range tests {
wg.Add(1)

View File

@@ -39,7 +39,6 @@ import (
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
prysmTime "github.com/prysmaticlabs/prysm/v5/time"
"github.com/prysmaticlabs/prysm/v5/time/slots"
"github.com/sirupsen/logrus"
)
// Service represents a service that handles the internal
@@ -317,36 +316,32 @@ func (s *Service) originRootFromSavedState(ctx context.Context) ([32]byte, error
return genesisBlkRoot, nil
}
// initializeHeadFromDB uses the finalized checkpoint and head block root from forkchoice to set the current head.
// initializeHeadFromDB uses the finalized checkpoint and head block found in the database to set the current head.
// Note that this may block until stategen replays blocks between the finalized and head blocks
// if the head sync flag was specified and the gap between the finalized and head blocks is at least 128 epochs long.
func (s *Service) initializeHead(ctx context.Context, st state.BeaconState) error {
func (s *Service) initializeHeadFromDB(ctx context.Context, finalizedState state.BeaconState) error {
cp := s.FinalizedCheckpt()
fRoot := s.ensureRootNotZeros([32]byte(cp.Root))
if st == nil || st.IsNil() {
fRoot := [32]byte(cp.Root)
finalizedRoot := s.ensureRootNotZeros(fRoot)
if finalizedState == nil || finalizedState.IsNil() {
return errors.New("finalized state can't be nil")
}
s.cfg.ForkChoiceStore.RLock()
root := s.cfg.ForkChoiceStore.HighestReceivedBlockRoot()
s.cfg.ForkChoiceStore.RUnlock()
blk, err := s.cfg.BeaconDB.Block(ctx, root)
finalizedBlock, err := s.getBlock(ctx, finalizedRoot)
if err != nil {
return errors.Wrap(err, "could not get head block")
return errors.Wrap(err, "could not get finalized block")
}
if root != fRoot {
st, err = s.cfg.StateGen.StateByRoot(ctx, root)
if err != nil {
return errors.Wrap(err, "could not get head state")
}
}
if err := s.setHead(&head{root, blk, st, blk.Block().Slot(), false}); err != nil {
if err := s.setHead(&head{
finalizedRoot,
finalizedBlock,
finalizedState,
finalizedBlock.Block().Slot(),
false,
}); err != nil {
return errors.Wrap(err, "could not set head")
}
log.WithFields(logrus.Fields{
"root": fmt.Sprintf("%#x", root),
"slot": blk.Block().Slot(),
}).Info("Initialized head block from DB")
return nil
}

View File

@@ -2,117 +2,26 @@ package blockchain
import (
"bytes"
"context"
"fmt"
"github.com/pkg/errors"
forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/features"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/time/slots"
)
func (s *Service) setupForkchoice(st state.BeaconState) error {
if err := s.setupForkchoiceCheckpoints(); err != nil {
return errors.Wrap(err, "could not set up forkchoice checkpoints")
}
if err := s.setupForkchoiceTree(st); err != nil {
return errors.Wrap(err, "could not set up forkchoice root")
}
if err := s.initializeHead(s.ctx, st); err != nil {
return errors.Wrap(err, "could not initialize head from db")
}
return nil
}
func (s *Service) startupHeadRoot() [32]byte {
headStr := features.Get().ForceHead
cp := s.FinalizedCheckpt()
fRoot := s.ensureRootNotZeros([32]byte(cp.Root))
if headStr == "" {
return fRoot
}
if headStr == "head" {
root, err := s.cfg.BeaconDB.HeadBlockRoot()
if err != nil {
log.WithError(err).Error("could not get head block root, starting with finalized block as head")
return fRoot
}
log.Infof("Using Head root of %#x", root)
return root
}
root, err := bytesutil.DecodeHexWithLength(headStr, 32)
if err != nil {
log.WithError(err).Error("could not parse head root, starting with finalized block as head")
return fRoot
}
return [32]byte(root)
}
func (s *Service) setupForkchoiceTree(st state.BeaconState) error {
headRoot := s.startupHeadRoot()
cp := s.FinalizedCheckpt()
fRoot := s.ensureRootNotZeros([32]byte(cp.Root))
if err := s.setupForkchoiceRoot(st); err != nil {
return errors.Wrap(err, "could not set up forkchoice root")
}
if headRoot == fRoot {
return nil
if err := s.initializeHeadFromDB(s.ctx, st); err != nil {
return errors.Wrap(err, "could not initialize head from db")
}
blk, err := s.cfg.BeaconDB.Block(s.ctx, headRoot)
if err != nil {
log.WithError(err).Error("could not get head block, starting with finalized block as head")
return nil
}
if slots.ToEpoch(blk.Block().Slot()) < cp.Epoch {
log.WithField("headRoot", fmt.Sprintf("%#x", headRoot)).Error("head block is older than finalized block, starting with finalized block as head")
return nil
}
chain, err := s.buildForkchoiceChain(s.ctx, blk)
if err != nil {
log.WithError(err).Error("could not build forkchoice chain, starting with finalized block as head")
return nil
}
s.cfg.ForkChoiceStore.Lock()
defer s.cfg.ForkChoiceStore.Unlock()
return s.cfg.ForkChoiceStore.InsertChain(s.ctx, chain)
}
func (s *Service) buildForkchoiceChain(ctx context.Context, head interfaces.ReadOnlySignedBeaconBlock) ([]*forkchoicetypes.BlockAndCheckpoints, error) {
chain := []*forkchoicetypes.BlockAndCheckpoints{}
cp := s.FinalizedCheckpt()
fRoot := s.ensureRootNotZeros([32]byte(cp.Root))
jp := s.CurrentJustifiedCheckpt()
root, err := head.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "could not get head block root")
}
for {
roblock, err := blocks.NewROBlockWithRoot(head, root)
if err != nil {
return nil, err
}
// This chain sets the justified checkpoint for every block, including some that are older than jp.
// This should be however safe for forkchoice at startup. An alternative would be to hook during the
// block processing pipeline when setting the head state, to compute the right states for the justified
// checkpoint.
chain = append(chain, &forkchoicetypes.BlockAndCheckpoints{Block: roblock, JustifiedCheckpoint: jp, FinalizedCheckpoint: cp})
root = head.Block().ParentRoot()
if root == fRoot {
break
}
head, err = s.cfg.BeaconDB.Block(s.ctx, root)
if err != nil {
return nil, errors.Wrap(err, "could not get block")
}
if slots.ToEpoch(head.Block().Slot()) < cp.Epoch {
return nil, errors.New("head block is not a descendant of the finalized checkpoint")
}
}
return chain, nil
return nil
}
func (s *Service) setupForkchoiceRoot(st state.BeaconState) error {

View File

@@ -1,128 +0,0 @@
package blockchain
import (
"testing"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/config/features"
"github.com/prysmaticlabs/prysm/v5/config/params"
consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
logTest "github.com/sirupsen/logrus/hooks/test"
)
func Test_startupHeadRoot(t *testing.T) {
service, tr := minimalTestService(t)
ctx := tr.ctx
hook := logTest.NewGlobal()
cp := service.FinalizedCheckpt()
require.DeepEqual(t, cp.Root, params.BeaconConfig().ZeroHash[:])
gr := [32]byte{'r', 'o', 'o', 't'}
service.originBlockRoot = gr
require.NoError(t, service.cfg.BeaconDB.SaveGenesisBlockRoot(ctx, gr))
t.Run("start from finalized", func(t *testing.T) {
require.Equal(t, service.startupHeadRoot(), gr)
})
t.Run("head requested, error path", func(t *testing.T) {
resetCfg := features.InitWithReset(&features.Flags{
ForceHead: "head",
})
defer resetCfg()
require.Equal(t, service.startupHeadRoot(), gr)
require.LogsContain(t, hook, "could not get head block root, starting with finalized block as head")
})
st, _ := util.DeterministicGenesisState(t, 64)
hr := [32]byte{'h', 'e', 'a', 'd'}
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, st, hr), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, hr), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, hr))
t.Run("start from head", func(t *testing.T) {
resetCfg := features.InitWithReset(&features.Flags{
ForceHead: "head",
})
defer resetCfg()
require.Equal(t, service.startupHeadRoot(), hr)
})
}
func Test_setupForkchoiceTree_Finalized(t *testing.T) {
service, tr := minimalTestService(t)
ctx := tr.ctx
st, _ := util.DeterministicGenesisState(t, 64)
stateRoot, err := st.HashTreeRoot(ctx)
require.NoError(t, err, "Could not hash genesis state")
require.NoError(t, service.saveGenesisData(ctx, st))
genesis := blocks.NewGenesisBlock(stateRoot[:])
wsb, err := consensusblocks.NewSignedBeaconBlock(genesis)
require.NoError(t, err)
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wsb), "Could not save genesis block")
parentRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err, "Could not get signing root")
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, st, parentRoot), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, parentRoot), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveJustifiedCheckpoint(ctx, &ethpb.Checkpoint{Root: parentRoot[:]}))
require.NoError(t, service.cfg.BeaconDB.SaveFinalizedCheckpoint(ctx, &ethpb.Checkpoint{Root: parentRoot[:]}))
require.NoError(t, service.setupForkchoiceTree(st))
require.Equal(t, 1, service.cfg.ForkChoiceStore.NodeCount())
}
func Test_setupForkchoiceTree_Head(t *testing.T) {
service, tr := minimalTestService(t)
ctx := tr.ctx
resetCfg := features.InitWithReset(&features.Flags{
ForceHead: "head",
})
defer resetCfg()
genesisState, keys := util.DeterministicGenesisState(t, 64)
stateRoot, err := genesisState.HashTreeRoot(ctx)
require.NoError(t, err, "Could not hash genesis state")
genesis := blocks.NewGenesisBlock(stateRoot[:])
wsb, err := consensusblocks.NewSignedBeaconBlock(genesis)
require.NoError(t, err)
genesisRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err, "Could not get signing root")
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wsb), "Could not save genesis block")
require.NoError(t, service.saveGenesisData(ctx, genesisState))
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, genesisState, genesisRoot), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, genesisRoot), "Could not save genesis state")
st, err := service.HeadState(ctx)
require.NoError(t, err)
b, err := util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), primitives.Slot(1))
require.NoError(t, err)
wsb, err = consensusblocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
root, err := b.Block.HashTreeRoot()
require.NoError(t, err)
preState, err := service.getBlockPreState(ctx, wsb.Block())
require.NoError(t, err)
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
b, err = util.GenerateFullBlock(postState, keys, util.DefaultBlockGenConfig(), primitives.Slot(2))
require.NoError(t, err)
wsb, err = consensusblocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
root, err = b.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, preState))
require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, root))
cp := service.FinalizedCheckpt()
fRoot := service.ensureRootNotZeros([32]byte(cp.Root))
require.NotEqual(t, fRoot, root)
require.Equal(t, root, service.startupHeadRoot())
require.NoError(t, service.setupForkchoiceTree(st))
require.Equal(t, 2, service.cfg.ForkChoiceStore.NodeCount())
}

View File

@@ -75,7 +75,6 @@ type ChainService struct {
SyncingRoot [32]byte
Blobs []blocks.VerifiedROBlob
TargetRoot [32]byte
MockHeadSlot *primitives.Slot
}
func (s *ChainService) Ancestor(ctx context.Context, root []byte, slot primitives.Slot) ([]byte, error) {
@@ -335,9 +334,6 @@ func (s *ChainService) ReceiveBlock(ctx context.Context, block interfaces.ReadOn
// HeadSlot mocks HeadSlot method in chain service.
func (s *ChainService) HeadSlot() primitives.Slot {
if s.MockHeadSlot != nil {
return *s.MockHeadSlot
}
if s.State == nil {
return 0
}
@@ -448,11 +444,6 @@ func (s *ChainService) IsCanonical(_ context.Context, r [32]byte) (bool, error)
return true, nil
}
// DependentRoot mocks the base method in the chain service.
func (*ChainService) DependentRoot(_ primitives.Epoch) ([32]byte, error) {
return [32]byte{}, nil
}
// HasBlock mocks the same method in the chain service.
func (s *ChainService) HasBlock(ctx context.Context, rt [32]byte) bool {
if s.DB == nil {

View File

@@ -3,7 +3,6 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"log.go",
"metric.go",
"option.go",
"service.go",

View File

@@ -1,5 +0,0 @@
package builder
import "github.com/sirupsen/logrus"
var log = logrus.WithField("prefix", "builder")

View File

@@ -14,16 +14,10 @@ type Option func(s *Service) error
// FlagOptions for builder service flag configurations.
func FlagOptions(c *cli.Context) ([]Option, error) {
endpoint := c.String(flags.MevRelayEndpoint.Name)
sszEnabled := c.Bool(flags.EnableBuilderSSZ.Name)
var client *builder.Client
if endpoint != "" {
var opts []builder.ClientOpt
if sszEnabled {
log.Info("Using APIs with SSZ enabled")
opts = append(opts, builder.WithSSZ())
}
var err error
client, err = builder.NewClient(endpoint, opts...)
client, err = builder.NewClient(endpoint)
if err != nil {
return nil, err
}

View File

@@ -17,6 +17,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
v1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
log "github.com/sirupsen/logrus"
)
// ErrNoBuilder is used when builder endpoint is not configured.

View File

@@ -25,7 +25,7 @@ type (
}
TrackedValidatorsCache struct {
trackedValidators *cache.Cache
trackedValidators cache.Cache
}
)
@@ -50,7 +50,7 @@ var (
// NewTrackedValidatorsCache creates a new cache for tracking validators.
func NewTrackedValidatorsCache() *TrackedValidatorsCache {
return &TrackedValidatorsCache{
trackedValidators: cache.New(defaultExpiration, cleanupInterval),
trackedValidators: *cache.New(defaultExpiration, cleanupInterval),
}
}

View File

@@ -165,7 +165,7 @@ func AddValidatorFlag(flag, flagPosition uint8) (uint8, error) {
// if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index):
// epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
// proposer_reward_numerator += get_base_reward(state, index) * weight
func EpochParticipation(beaconState state.ReadOnlyBeaconState, indices []uint64, epochParticipation []byte, participatedFlags map[uint8]bool, totalBalance uint64) (uint64, []byte, error) {
func EpochParticipation(beaconState state.BeaconState, indices []uint64, epochParticipation []byte, participatedFlags map[uint8]bool, totalBalance uint64) (uint64, []byte, error) {
cfg := params.BeaconConfig()
sourceFlagIndex := cfg.TimelySourceFlagIndex
targetFlagIndex := cfg.TimelyTargetFlagIndex
@@ -267,7 +267,7 @@ func RewardProposer(ctx context.Context, beaconState state.BeaconState, proposer
// participation_flag_indices.append(TIMELY_HEAD_FLAG_INDEX)
//
// return participation_flag_indices
func AttestationParticipationFlagIndices(beaconState state.ReadOnlyBeaconState, data *ethpb.AttestationData, delay primitives.Slot) (map[uint8]bool, error) {
func AttestationParticipationFlagIndices(beaconState state.BeaconState, data *ethpb.AttestationData, delay primitives.Slot) (map[uint8]bool, error) {
currEpoch := time.CurrentEpoch(beaconState)
var justifiedCheckpt *ethpb.Checkpoint
if data.Target.Epoch == currEpoch {
@@ -312,7 +312,7 @@ func AttestationParticipationFlagIndices(beaconState state.ReadOnlyBeaconState,
// is_matching_source = data.source == justified_checkpoint
// is_matching_target = is_matching_source and data.target.root == get_block_root(state, data.target.epoch)
// is_matching_head = is_matching_target and data.beacon_block_root == get_block_root_at_slot(state, data.slot)
func MatchingStatus(beaconState state.ReadOnlyBeaconState, data *ethpb.AttestationData, cp *ethpb.Checkpoint) (matchedSrc, matchedTgt, matchedHead bool, err error) {
func MatchingStatus(beaconState state.BeaconState, data *ethpb.AttestationData, cp *ethpb.Checkpoint) (matchedSrc, matchedTgt, matchedHead bool, err error) {
matchedSrc = attestation.CheckPointIsEqual(data.Source, cp)
r, err := helpers.BlockRoot(beaconState, data.Target.Epoch)

View File

@@ -29,7 +29,6 @@ go_library(
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
"//beacon-chain/state/state-native/custom-types:go_default_library",
"//config/params:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
@@ -40,7 +39,6 @@ go_library(
"//monitoring/tracing/trace:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/attestation:go_default_library",
"//runtime/version:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
@@ -81,6 +79,7 @@ go_test(
"//consensus-types/blocks:go_default_library",
"//consensus-types/primitives:go_default_library",
"//crypto/bls:go_default_library",
"//crypto/bls/common:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",

View File

@@ -1,94 +1,7 @@
package electra
import (
"context"
"fmt"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
customtypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native/custom-types"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/attestation"
)
import "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
var (
ProcessAttestationsNoVerifySignature = altair.ProcessAttestationsNoVerifySignature
)
// GetProposerRewardNumerator returns the numerator of the proposer reward for an attestation.
func GetProposerRewardNumerator(
ctx context.Context,
st state.ReadOnlyBeaconState,
att ethpb.Att,
totalBalance uint64,
) (uint64, error) {
data := att.GetData()
delay, err := st.Slot().SafeSubSlot(data.Slot)
if err != nil {
return 0, fmt.Errorf("attestation slot %d exceeds state slot %d", data.Slot, st.Slot())
}
flags, err := altair.AttestationParticipationFlagIndices(st, data, delay)
if err != nil {
return 0, err
}
committees, err := helpers.AttestationCommitteesFromState(ctx, st, att)
if err != nil {
return 0, err
}
indices, err := attestation.AttestingIndices(att, committees...)
if err != nil {
return 0, err
}
var participation customtypes.ReadOnlyParticipation
if data.Target.Epoch == time.CurrentEpoch(st) {
participation, err = st.CurrentEpochParticipationReadOnly()
} else {
participation, err = st.PreviousEpochParticipationReadOnly()
}
if err != nil {
return 0, err
}
cfg := params.BeaconConfig()
var rewardNumerator uint64
for _, index := range indices {
if index >= uint64(participation.Len()) {
return 0, fmt.Errorf("index %d exceeds participation length %d", index, participation.Len())
}
br, err := altair.BaseRewardWithTotalBalance(st, primitives.ValidatorIndex(index), totalBalance)
if err != nil {
return 0, err
}
for _, entry := range []struct {
flagIndex uint8
weight uint64
}{
{cfg.TimelySourceFlagIndex, cfg.TimelySourceWeight},
{cfg.TimelyTargetFlagIndex, cfg.TimelyTargetWeight},
{cfg.TimelyHeadFlagIndex, cfg.TimelyHeadWeight},
} {
if flags[entry.flagIndex] { // If set, the validator voted correctly for the attestation given flag index.
hasVoted, err := altair.HasValidatorFlag(participation.At(index), entry.flagIndex)
if err != nil {
return 0, err
}
if !hasVoted { // If set, the validator has already voted in the beacon state so we don't double count.
rewardNumerator += br * entry.weight
}
}
}
}
return rewardNumerator, nil
}

View File

@@ -211,7 +211,7 @@ func ProcessConsolidationRequests(ctx context.Context, st state.BeaconState, req
if npc, err := st.NumPendingConsolidations(); err != nil {
return fmt.Errorf("failed to fetch number of pending consolidations: %w", err) // This should never happen.
} else if npc >= pcLimit {
continue
return nil
}
activeBal, err := helpers.TotalActiveBalance(st)
@@ -220,7 +220,7 @@ func ProcessConsolidationRequests(ctx context.Context, st state.BeaconState, req
}
churnLimit := helpers.ConsolidationChurnLimit(primitives.Gwei(activeBal))
if churnLimit <= primitives.Gwei(params.BeaconConfig().MinActivationBalance) {
continue
return nil
}
srcIdx, ok := st.ValidatorIndexByPubkey(sourcePubkey)

View File

@@ -415,44 +415,6 @@ func TestProcessConsolidationRequests(t *testing.T) {
require.Equal(t, params.BeaconConfig().FarFutureEpoch, src.WithdrawableEpoch, "source validator withdrawable epoch should not be updated")
},
},
{
name: "pending consolidations limit reached and compounded consolidation after",
state: func() state.BeaconState {
st := &eth.BeaconStateElectra{
Slot: params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().ShardCommitteePeriod)),
Validators: createValidatorsWithTotalActiveBalance(32000000000000000), // 32M ETH
PendingConsolidations: make([]*eth.PendingConsolidation, params.BeaconConfig().PendingConsolidationsLimit),
}
// To allow compounding consolidation requests.
st.Validators[3].WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s, err := state_native.InitializeFromProtoElectra(st)
require.NoError(t, err)
return s
}(),
reqs: []*enginev1.ConsolidationRequest{
{
SourceAddress: append(bytesutil.PadTo(nil, 19), byte(1)),
SourcePubkey: []byte("val_1"),
TargetPubkey: []byte("val_2"),
},
{
SourceAddress: append(bytesutil.PadTo(nil, 19), byte(3)),
SourcePubkey: []byte("val_3"),
TargetPubkey: []byte("val_3"),
},
},
validate: func(t *testing.T, st state.BeaconState) {
// Verify a pending consolidation is created.
numPC, err := st.NumPendingConsolidations()
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().PendingConsolidationsLimit, numPC)
// Verify that the last consolidation was included
src, err := st.ValidatorAtIndex(3)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().CompoundingWithdrawalPrefixByte, src.WithdrawalCredentials[0], "source validator was not compounded")
},
},
}
for _, tt := range tests {

View File

@@ -374,38 +374,47 @@ func ProcessPendingDeposits(ctx context.Context, st state.BeaconState, activeBal
// batchProcessNewPendingDeposits should only be used to process new deposits that require validator registration
func batchProcessNewPendingDeposits(ctx context.Context, state state.BeaconState, pendingDeposits []*ethpb.PendingDeposit) error {
// Return early if there are no deposits to process
if len(pendingDeposits) == 0 {
return nil
}
// Try batch verification of all deposit signatures
allSignaturesVerified, err := blocks.BatchVerifyPendingDepositsSignatures(ctx, pendingDeposits)
if err != nil {
return errors.Wrap(err, "batch signature verification failed")
}
for _, pd := range pendingDeposits {
validSig := allSignaturesVerified
// Process each deposit individually
for _, pendingDeposit := range pendingDeposits {
validSignature := allSignaturesVerified
// If batch verification failed, check the individual deposit signature
if !allSignaturesVerified {
validSig, err = blocks.IsValidDepositSignature(&ethpb.Deposit_Data{
PublicKey: bytesutil.SafeCopyBytes(pd.PublicKey),
WithdrawalCredentials: bytesutil.SafeCopyBytes(pd.WithdrawalCredentials),
Amount: pd.Amount,
Signature: bytesutil.SafeCopyBytes(pd.Signature),
validSignature, err = blocks.IsValidDepositSignature(&ethpb.Deposit_Data{
PublicKey: bytesutil.SafeCopyBytes(pendingDeposit.PublicKey),
WithdrawalCredentials: bytesutil.SafeCopyBytes(pendingDeposit.WithdrawalCredentials),
Amount: pendingDeposit.Amount,
Signature: bytesutil.SafeCopyBytes(pendingDeposit.Signature),
})
if err != nil {
return errors.Wrap(err, "individual deposit signature verification failed")
}
}
pubkey := bytesutil.ToBytes48(pd.PublicKey)
if index, exists := state.ValidatorIndexByPubkey(pubkey); exists {
if err := helpers.IncreaseBalance(state, index, pd.Amount); err != nil {
return errors.Wrap(err, "could not increase balance")
}
} else if validSig {
if err := AddValidatorToRegistry(state, pd.PublicKey, pd.WithdrawalCredentials, pd.Amount); err != nil {
return errors.Wrap(err, "failed to add validator to registry")
// Add validator to the registry if the signature is valid
if validSignature {
_, has := state.ValidatorIndexByPubkey(bytesutil.ToBytes48(pendingDeposit.PublicKey))
if has {
index, _ := state.ValidatorIndexByPubkey(bytesutil.ToBytes48(pendingDeposit.PublicKey))
if err := helpers.IncreaseBalance(state, index, pendingDeposit.Amount); err != nil {
return errors.Wrap(err, "could not increase balance")
}
} else {
err = AddValidatorToRegistry(state, pendingDeposit.PublicKey, pendingDeposit.WithdrawalCredentials, pendingDeposit.Amount)
if err != nil {
return errors.Wrap(err, "failed to add validator to registry")
}
}
}
}

View File

@@ -14,6 +14,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
"github.com/prysmaticlabs/prysm/v5/crypto/bls/common"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
@@ -195,7 +196,7 @@ func TestProcessPendingDeposits(t *testing.T) {
},
},
{
name: "process excess balance as a topup",
name: "process excess balance that uses a point to infinity signature, processed as a topup",
state: func() state.BeaconState {
excessBalance := uint64(100)
st := stateWithActiveBalanceETH(t, 32)
@@ -208,6 +209,7 @@ func TestProcessPendingDeposits(t *testing.T) {
validators[0].PublicKey = sk.PublicKey().Marshal()
validators[0].WithdrawalCredentials = wc
dep := stateTesting.GeneratePendingDeposit(t, sk, excessBalance, bytesutil.ToBytes32(wc), 0)
dep.Signature = common.InfiniteSignature[:]
require.NoError(t, st.SetValidators(validators))
require.NoError(t, st.SetPendingDeposits([]*eth.PendingDeposit{dep}))
return st
@@ -307,7 +309,7 @@ func TestProcessPendingDeposits(t *testing.T) {
}
func TestBatchProcessNewPendingDeposits(t *testing.T) {
t.Run("one valid deposit one garbage deposit", func(t *testing.T) {
t.Run("invalid batch initiates correct individual validation", func(t *testing.T) {
st := stateWithActiveBalanceETH(t, 0)
require.Equal(t, 0, len(st.Validators()))
require.Equal(t, 0, len(st.Balances()))
@@ -318,47 +320,13 @@ func TestBatchProcessNewPendingDeposits(t *testing.T) {
wc[31] = byte(0)
validDep := stateTesting.GeneratePendingDeposit(t, sk, params.BeaconConfig().MinActivationBalance, bytesutil.ToBytes32(wc), 0)
invalidDep := &eth.PendingDeposit{PublicKey: make([]byte, 48)}
// have a combination of valid and invalid deposits
deps := []*eth.PendingDeposit{validDep, invalidDep}
require.NoError(t, electra.BatchProcessNewPendingDeposits(context.Background(), st, deps))
// successfully added to register
require.Equal(t, 1, len(st.Validators()))
require.Equal(t, 1, len(st.Balances()))
})
t.Run("two valid deposits from same key", func(t *testing.T) {
st := stateWithActiveBalanceETH(t, 0)
require.Equal(t, 0, len(st.Validators()))
require.Equal(t, 0, len(st.Balances()))
sk, err := bls.RandKey()
require.NoError(t, err)
wc := make([]byte, 32)
wc[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
wc[31] = byte(0)
validDep := stateTesting.GeneratePendingDeposit(t, sk, params.BeaconConfig().MinActivationBalance, bytesutil.ToBytes32(wc), 0)
deps := []*eth.PendingDeposit{validDep, validDep}
require.NoError(t, electra.BatchProcessNewPendingDeposits(context.Background(), st, deps))
require.Equal(t, 1, len(st.Validators()))
require.Equal(t, 1, len(st.Balances()))
require.Equal(t, params.BeaconConfig().MinActivationBalance*2, st.Balances()[0])
})
t.Run("one valid one with invalid signature deposit", func(t *testing.T) {
st := stateWithActiveBalanceETH(t, 0)
require.Equal(t, 0, len(st.Validators()))
require.Equal(t, 0, len(st.Balances()))
sk, err := bls.RandKey()
require.NoError(t, err)
wc := make([]byte, 32)
wc[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
wc[31] = byte(0)
validDep := stateTesting.GeneratePendingDeposit(t, sk, params.BeaconConfig().MinActivationBalance, bytesutil.ToBytes32(wc), 0)
invalidSigDep := stateTesting.GeneratePendingDeposit(t, sk, params.BeaconConfig().MinActivationBalance, bytesutil.ToBytes32(wc), 0)
invalidSigDep.Signature = make([]byte, 96)
deps := []*eth.PendingDeposit{validDep, invalidSigDep}
require.NoError(t, electra.BatchProcessNewPendingDeposits(context.Background(), st, deps))
require.Equal(t, 1, len(st.Validators()))
require.Equal(t, 1, len(st.Balances()))
require.Equal(t, 2*params.BeaconConfig().MinActivationBalance, st.Balances()[0])
})
}
func TestProcessDepositRequests(t *testing.T) {
@@ -590,6 +558,7 @@ func TestApplyPendingDeposit_TopUp(t *testing.T) {
validators[0].PublicKey = sk.PublicKey().Marshal()
validators[0].WithdrawalCredentials = wc
dep := stateTesting.GeneratePendingDeposit(t, sk, excessBalance, bytesutil.ToBytes32(wc), 0)
dep.Signature = common.InfiniteSignature[:]
require.NoError(t, st.SetValidators(validators))
require.NoError(t, electra.ApplyPendingDeposit(context.Background(), st, dep))

View File

@@ -11,7 +11,6 @@ go_library(
deps = [
"//async/event:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
],
)

View File

@@ -3,7 +3,6 @@ package operation
import (
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
)
@@ -36,9 +35,6 @@ const (
// SingleAttReceived is sent after a single attestation object is received from gossip or rpc
SingleAttReceived = 9
// BlockGossipReceived is sent after a block has been received from gossip or API that passes validation rules.
BlockGossipReceived = 10
)
// UnAggregatedAttReceivedData is the data sent with UnaggregatedAttReceived events.
@@ -89,9 +85,3 @@ type AttesterSlashingReceivedData struct {
type SingleAttReceivedData struct {
Attestation ethpb.Att
}
// BlockGossipReceivedData is the data sent with BlockGossipReceived events.
type BlockGossipReceivedData struct {
// SignedBlock is the block that was received.
SignedBlock interfaces.ReadOnlySignedBeaconBlock
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/crypto/hash"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
prysmTime "github.com/prysmaticlabs/prysm/v5/time"
"github.com/prysmaticlabs/prysm/v5/time/slots"
)
@@ -85,7 +86,15 @@ func IsAggregator(committeeCount uint64, slotSig []byte) (bool, error) {
//
// return uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT)
func ComputeSubnetForAttestation(activeValCount uint64, att ethpb.Att) uint64 {
return ComputeSubnetFromCommitteeAndSlot(activeValCount, att.GetCommitteeIndex(), att.GetData().Slot)
if att.Version() >= version.Electra {
committeeIndex := 0
committeeIndices := att.CommitteeBitsVal().BitIndices()
if len(committeeIndices) > 0 {
committeeIndex = committeeIndices[0]
}
return ComputeSubnetFromCommitteeAndSlot(activeValCount, primitives.CommitteeIndex(committeeIndex), att.GetData().Slot)
}
return ComputeSubnetFromCommitteeAndSlot(activeValCount, att.GetData().CommitteeIndex, att.GetData().Slot)
}
// ComputeSubnetFromCommitteeAndSlot is a flattened version of ComputeSubnetForAttestation where we only pass in

View File

@@ -2,10 +2,7 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"lightclient.go",
"store.go",
],
srcs = ["lightclient.go"],
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client",
visibility = ["//visibility:public"],
deps = [
@@ -30,10 +27,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = [
"lightclient_test.go",
"store_test.go",
],
srcs = ["lightclient_test.go"],
deps = [
":go_default_library",
"//config/fieldparams:go_default_library",

View File

@@ -5,15 +5,14 @@ import (
"strings"
"testing"
"github.com/prysmaticlabs/prysm/v5/config/params"
light_client "github.com/prysmaticlabs/prysm/v5/consensus-types/light-client"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/pkg/errors"
lightClient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
consensustypes "github.com/prysmaticlabs/prysm/v5/consensus-types"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
light_client "github.com/prysmaticlabs/prysm/v5/consensus-types/light-client"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/ssz"
v11 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
pb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
@@ -32,7 +31,7 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T)
params.OverrideBeaconConfig(cfg)
t.Run("Altair", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair(0, true)
l := util.NewTestLightClient(t).SetupTestAltair()
update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock)
require.NoError(t, err)
@@ -44,7 +43,7 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T)
})
t.Run("Capella", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(false, 0, true)
l := util.NewTestLightClient(t).SetupTestCapella(false)
update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock)
require.NoError(t, err)
@@ -57,7 +56,7 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T)
})
t.Run("Deneb", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb(false, 0, true)
l := util.NewTestLightClient(t).SetupTestDeneb(false)
update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock)
require.NoError(t, err)
@@ -70,7 +69,7 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T)
})
t.Run("Electra", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestElectra(false, 0, true)
l := util.NewTestLightClient(t).SetupTestElectra(false)
update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock)
require.NoError(t, err)
@@ -94,7 +93,7 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) {
params.OverrideBeaconConfig(cfg)
t.Run("Altair", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair(0, true)
l := util.NewTestLightClient(t).SetupTestAltair()
t.Run("FinalizedBlock Not Nil", func(t *testing.T) {
update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock)
@@ -131,7 +130,7 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) {
t.Run("Capella", func(t *testing.T) {
t.Run("FinalizedBlock Not Nil", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(false, 0, true)
l := util.NewTestLightClient(t).SetupTestCapella(false)
update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock)
require.NoError(t, err)
require.NotNil(t, update, "update is nil")
@@ -238,7 +237,7 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) {
t.Run("Deneb", func(t *testing.T) {
t.Run("FinalizedBlock Not Nil", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb(false, 0, true)
l := util.NewTestLightClient(t).SetupTestDeneb(false)
update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock)
require.NoError(t, err)
@@ -390,7 +389,7 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) {
t.Run("Electra", func(t *testing.T) {
t.Run("FinalizedBlock Not Nil", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestElectra(false, 0, true)
l := util.NewTestLightClient(t).SetupTestElectra(false)
update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock)
require.NoError(t, err)
@@ -542,7 +541,7 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) {
func TestLightClient_BlockToLightClientHeader(t *testing.T) {
t.Run("Altair", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair(0, true)
l := util.NewTestLightClient(t).SetupTestAltair()
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
@@ -565,7 +564,7 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
})
t.Run("Bellatrix", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestBellatrix(0, true)
l := util.NewTestLightClient(t).SetupTestBellatrix()
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
@@ -589,7 +588,7 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
t.Run("Capella", func(t *testing.T) {
t.Run("Non-Blinded Beacon Block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(false, 0, true)
l := util.NewTestLightClient(t).SetupTestCapella(false)
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
@@ -650,7 +649,7 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
})
t.Run("Blinded Beacon Block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(true, 0, true)
l := util.NewTestLightClient(t).SetupTestCapella(true)
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
@@ -713,7 +712,7 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
t.Run("Deneb", func(t *testing.T) {
t.Run("Non-Blinded Beacon Block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb(false, 0, true)
l := util.NewTestLightClient(t).SetupTestDeneb(false)
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
@@ -782,7 +781,7 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
})
t.Run("Blinded Beacon Block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb(true, 0, true)
l := util.NewTestLightClient(t).SetupTestDeneb(true)
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
@@ -853,7 +852,7 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
t.Run("Electra", func(t *testing.T) {
t.Run("Non-Blinded Beacon Block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestElectra(false, 0, true)
l := util.NewTestLightClient(t).SetupTestElectra(false)
header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block)
require.NoError(t, err)
@@ -918,7 +917,7 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
})
t.Run("Blinded Beacon Block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestElectra(true, 0, true)
l := util.NewTestLightClient(t).SetupTestElectra(true)
header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block)
require.NoError(t, err)
@@ -984,7 +983,7 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
})
t.Run("Capella fork with Altair block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair(0, true)
l := util.NewTestLightClient(t).SetupTestAltair()
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
@@ -1006,7 +1005,7 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
})
t.Run("Deneb fork with Altair block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair(0, true)
l := util.NewTestLightClient(t).SetupTestAltair()
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
@@ -1029,7 +1028,7 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
t.Run("Deneb fork with Capella block", func(t *testing.T) {
t.Run("Non-Blinded Beacon Block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(false, 0, true)
l := util.NewTestLightClient(t).SetupTestCapella(false)
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
@@ -1089,7 +1088,7 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
})
t.Run("Blinded Beacon Block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(true, 0, true)
l := util.NewTestLightClient(t).SetupTestCapella(true)
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,

View File

@@ -1,38 +0,0 @@
package light_client
import (
"sync"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
)
type Store struct {
mu sync.RWMutex
lastFinalityUpdate interfaces.LightClientFinalityUpdate
lastOptimisticUpdate interfaces.LightClientOptimisticUpdate
}
func (s *Store) SetLastFinalityUpdate(update interfaces.LightClientFinalityUpdate) {
s.mu.Lock()
defer s.mu.Unlock()
s.lastFinalityUpdate = update
}
func (s *Store) LastFinalityUpdate() interfaces.LightClientFinalityUpdate {
s.mu.RLock()
defer s.mu.RUnlock()
return s.lastFinalityUpdate
}
func (s *Store) SetLastOptimisticUpdate(update interfaces.LightClientOptimisticUpdate) {
s.mu.Lock()
defer s.mu.Unlock()
s.lastOptimisticUpdate = update
}
func (s *Store) LastOptimisticUpdate() interfaces.LightClientOptimisticUpdate {
s.mu.RLock()
defer s.mu.RUnlock()
return s.lastOptimisticUpdate
}

View File

@@ -1,67 +0,0 @@
package light_client_test
import (
"testing"
lightClient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func TestLightClientStore(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig()
cfg.AltairForkEpoch = 1
cfg.BellatrixForkEpoch = 2
cfg.CapellaForkEpoch = 3
cfg.DenebForkEpoch = 4
cfg.ElectraForkEpoch = 5
params.OverrideBeaconConfig(cfg)
// Initialize the light client store
lcStore := &lightClient.Store{}
// Create test light client updates for Capella and Deneb
lCapella := util.NewTestLightClient(t).SetupTestCapella(false, 0, true)
opUpdateCapella, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(lCapella.Ctx, lCapella.State.Slot(), lCapella.State, lCapella.Block, lCapella.AttestedState, lCapella.AttestedBlock)
require.NoError(t, err)
require.NotNil(t, opUpdateCapella, "OptimisticUpdateCapella is nil")
finUpdateCapella, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(lCapella.Ctx, lCapella.State.Slot(), lCapella.State, lCapella.Block, lCapella.AttestedState, lCapella.AttestedBlock, lCapella.FinalizedBlock)
require.NoError(t, err)
require.NotNil(t, finUpdateCapella, "FinalityUpdateCapella is nil")
lDeneb := util.NewTestLightClient(t).SetupTestDeneb(false, 0, true)
opUpdateDeneb, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(lDeneb.Ctx, lDeneb.State.Slot(), lDeneb.State, lDeneb.Block, lDeneb.AttestedState, lDeneb.AttestedBlock)
require.NoError(t, err)
require.NotNil(t, opUpdateDeneb, "OptimisticUpdateDeneb is nil")
finUpdateDeneb, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(lDeneb.Ctx, lDeneb.State.Slot(), lDeneb.State, lDeneb.Block, lDeneb.AttestedState, lDeneb.AttestedBlock, lDeneb.FinalizedBlock)
require.NoError(t, err)
require.NotNil(t, finUpdateDeneb, "FinalityUpdateDeneb is nil")
// Initially the store should have nil values for both updates
require.IsNil(t, lcStore.LastFinalityUpdate(), "lastFinalityUpdate should be nil")
require.IsNil(t, lcStore.LastOptimisticUpdate(), "lastOptimisticUpdate should be nil")
// Set and get finality with Capella update. Optimistic update should be nil
lcStore.SetLastFinalityUpdate(finUpdateCapella)
require.Equal(t, finUpdateCapella, lcStore.LastFinalityUpdate(), "lastFinalityUpdate is wrong")
require.IsNil(t, lcStore.LastOptimisticUpdate(), "lastOptimisticUpdate should be nil")
// Set and get optimistic with Capella update. Finality update should be Capella
lcStore.SetLastOptimisticUpdate(opUpdateCapella)
require.Equal(t, opUpdateCapella, lcStore.LastOptimisticUpdate(), "lastOptimisticUpdate is wrong")
require.Equal(t, finUpdateCapella, lcStore.LastFinalityUpdate(), "lastFinalityUpdate is wrong")
// Set and get finality and optimistic with Deneb update
lcStore.SetLastFinalityUpdate(finUpdateDeneb)
lcStore.SetLastOptimisticUpdate(opUpdateDeneb)
require.Equal(t, finUpdateDeneb, lcStore.LastFinalityUpdate(), "lastFinalityUpdate is wrong")
require.Equal(t, opUpdateDeneb, lcStore.LastOptimisticUpdate(), "lastOptimisticUpdate is wrong")
// Set and get finality and optimistic with nil update
lcStore.SetLastFinalityUpdate(nil)
lcStore.SetLastOptimisticUpdate(nil)
require.IsNil(t, lcStore.LastFinalityUpdate(), "lastFinalityUpdate should be nil")
require.IsNil(t, lcStore.LastOptimisticUpdate(), "lastOptimisticUpdate should be nil")
}

View File

@@ -415,15 +415,11 @@ func VerifyOperationLengths(_ context.Context, state state.BeaconState, b interf
)
}
maxAttestations := params.BeaconConfig().MaxAttestations
if body.Version() >= version.Electra {
maxAttestations = params.BeaconConfig().MaxAttestationsElectra
}
if uint64(len(body.Attestations())) > maxAttestations {
if uint64(len(body.Attestations())) > params.BeaconConfig().MaxAttestations {
return nil, fmt.Errorf(
"number of attestations (%d) in block body exceeds allowed threshold of %d",
len(body.Attestations()),
maxAttestations,
params.BeaconConfig().MaxAttestations,
)
}

View File

@@ -474,24 +474,6 @@ func TestProcessBlock_OverMaxAttestations(t *testing.T) {
assert.ErrorContains(t, want, err)
}
func TestProcessBlock_OverMaxAttestationsElectra(t *testing.T) {
b := &ethpb.SignedBeaconBlockElectra{
Block: &ethpb.BeaconBlockElectra{
Body: &ethpb.BeaconBlockBodyElectra{
Attestations: make([]*ethpb.AttestationElectra, params.BeaconConfig().MaxAttestationsElectra+1),
},
},
}
want := fmt.Sprintf("number of attestations (%d) in block body exceeds allowed threshold of %d",
len(b.Block.Body.Attestations), params.BeaconConfig().MaxAttestationsElectra)
s, err := state_native.InitializeFromProtoUnsafeElectra(&ethpb.BeaconStateElectra{})
require.NoError(t, err)
wsb, err := consensusblocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
_, err = transition.VerifyOperationLengths(context.Background(), s, wsb.Block())
assert.ErrorContains(t, want, err)
}
func TestProcessBlock_OverMaxVoluntaryExits(t *testing.T) {
maxExits := params.BeaconConfig().MaxVoluntaryExits
b := &ethpb.SignedBeaconBlock{

View File

@@ -2,19 +2,13 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"errors.go",
"filter.go",
],
srcs = ["filter.go"],
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/filters",
visibility = [
"//beacon-chain:__subpackages__",
"//tools:__subpackages__",
],
deps = [
"//consensus-types/primitives:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],
deps = ["//consensus-types/primitives:go_default_library"],
)
go_test(
@@ -24,6 +18,5 @@ go_test(
deps = [
"//consensus-types/primitives:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
],
)

View File

@@ -1,9 +0,0 @@
package filters
import "errors"
var (
ErrIncompatibleFilters = errors.New("combination of filters is not valid")
ErrNotSet = errors.New("filter was not set")
ErrInvalidQuery = errors.New("invalid query")
)

View File

@@ -14,10 +14,7 @@
// }
package filters
import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
)
import primitives "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
// FilterType defines an enum which is used as the keys in a map that tracks
// set attribute filters for data as part of the `FilterQuery` struct type.
@@ -48,34 +45,10 @@ const (
SlotStep
)
// SlotRoot is the slot and root of a single block.
type SlotRoot struct {
Slot primitives.Slot
Root [32]byte
}
// AncestryQuery is a special query that describes a chain of blocks that satisfies the invariant of:
// blocks[n].parent_root == blocks[n-1].root.
type AncestryQuery struct {
// Slot of oldest to return.
Earliest primitives.Slot
// Descendent that all ancestors in chain must descend from.
Descendent SlotRoot
set bool
}
func (aq AncestryQuery) Span() primitives.Slot {
if aq.Earliest > aq.Descendent.Slot {
return 0
}
return (aq.Descendent.Slot - aq.Earliest) + 1 // +1 to include upper bound
}
// QueryFilter defines a generic interface for type-asserting
// specific filters to use in querying DB objects.
type QueryFilter struct {
queries map[FilterType]interface{}
ancestry AncestryQuery
queries map[FilterType]interface{}
}
// NewFilter instantiates a new QueryFilter type used to build filters for
@@ -159,42 +132,3 @@ func (q *QueryFilter) SetSlotStep(val uint64) *QueryFilter {
q.queries[SlotStep] = val
return q
}
// SimpleSlotRange returns the start and end slot of a query filter if it is a simple slot range query.
// A simple slot range query is one where the filter only contains a start slot and an end slot.
// If the query is not a simple slot range query, the bool return value will be false.
func (q *QueryFilter) SimpleSlotRange() (primitives.Slot, primitives.Slot, bool) {
if len(q.queries) != 2 || q.queries[StartSlot] == nil || q.queries[EndSlot] == nil {
return 0, 0, false
}
start, ok := q.queries[StartSlot].(primitives.Slot)
if !ok {
return 0, 0, false
}
end, ok := q.queries[EndSlot].(primitives.Slot)
if !ok {
return 0, 0, false
}
return start, end, true
}
// SetAncestryQuery sets the filter to be an ancestryQuery. Note that this filter type is exclusive with
// other filters, so call ing GetAncestryQuery will return an error if other values are set.
func (q *QueryFilter) SetAncestryQuery(aq AncestryQuery) *QueryFilter {
aq.set = true
q.ancestry = aq
return q
}
func (q *QueryFilter) GetAncestryQuery() (AncestryQuery, error) {
if !q.ancestry.set {
return q.ancestry, ErrNotSet
}
if len(q.queries) > 0 {
return q.ancestry, errors.Wrap(ErrIncompatibleFilters, "AncestryQuery cannot be combined with other filters")
}
if q.ancestry.Earliest > q.ancestry.Descendent.Slot {
return q.ancestry, errors.Wrap(ErrInvalidQuery, "descendent slot must come after earliest slot")
}
return q.ancestry, nil
}

View File

@@ -5,7 +5,6 @@ import (
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/testing/assert"
"github.com/prysmaticlabs/prysm/v5/testing/require"
)
func TestQueryFilter_ChainsCorrectly(t *testing.T) {
@@ -29,64 +28,3 @@ func TestQueryFilter_ChainsCorrectly(t *testing.T) {
}
}
}
func TestSimpleSlotRange(t *testing.T) {
type tc struct {
name string
applFilters []func(*QueryFilter) *QueryFilter
isSimple bool
start primitives.Slot
end primitives.Slot
}
cases := []tc{
{
name: "no filters",
applFilters: []func(*QueryFilter) *QueryFilter{},
isSimple: false,
},
{
name: "start slot",
applFilters: []func(*QueryFilter) *QueryFilter{
func(f *QueryFilter) *QueryFilter {
return f.SetStartSlot(3)
},
},
isSimple: false,
},
{
name: "end slot",
applFilters: []func(*QueryFilter) *QueryFilter{
func(f *QueryFilter) *QueryFilter {
return f.SetEndSlot(3)
},
},
isSimple: false,
},
{
name: "end slot",
applFilters: []func(*QueryFilter) *QueryFilter{
func(f *QueryFilter) *QueryFilter {
return f.SetStartSlot(3)
},
func(f *QueryFilter) *QueryFilter {
return f.SetEndSlot(7)
},
},
start: 3,
end: 7,
isSimple: true,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
f := NewFilter()
for _, filt := range c.applFilters {
f = filt(f)
}
start, end, isSimple := f.SimpleSlotRange()
require.Equal(t, c.isSimple, isSimple)
require.Equal(t, c.start, start)
require.Equal(t, c.end, end)
})
}
}

View File

@@ -110,7 +110,6 @@ type HeadAccessDatabase interface {
// Block related methods.
HeadBlock(ctx context.Context) (interfaces.ReadOnlySignedBeaconBlock, error)
HeadBlockRoot() ([32]byte, error)
SaveHeadBlockRoot(ctx context.Context, blockRoot [32]byte) error
// Genesis operations.

View File

@@ -35,7 +35,7 @@ func (s *Store) LastArchivedRoot(ctx context.Context) [32]byte {
_, blockRoot = bkt.Cursor().Last()
return nil
}); err != nil { // This view never returns an error, but we'll handle anyway for sanity.
panic(err) // lint:nopanic -- View never returns an error.
panic(err)
}
return bytesutil.ToBytes32(blockRoot)
@@ -53,7 +53,7 @@ func (s *Store) ArchivedPointRoot(ctx context.Context, slot primitives.Slot) [32
blockRoot = bucket.Get(bytesutil.SlotToBytesBigEndian(slot))
return nil
}); err != nil { // This view never returns an error, but we'll handle anyway for sanity.
panic(err) // lint:nopanic -- View never returns an error.
panic(err)
}
return bytesutil.ToBytes32(blockRoot)
@@ -69,7 +69,7 @@ func (s *Store) HasArchivedPoint(ctx context.Context, slot primitives.Slot) bool
exists = iBucket.Get(bytesutil.SlotToBytesBigEndian(slot)) != nil
return nil
}); err != nil { // This view never returns an error, but we'll handle anyway for sanity.
panic(err) // lint:nopanic -- View never returns an error.
panic(err)
}
return exists
}

View File

@@ -4,7 +4,6 @@ import (
"bytes"
"context"
"fmt"
"slices"
"github.com/ethereum/go-ethereum/common"
"github.com/golang/snappy"
@@ -31,32 +30,22 @@ var errInvalidSlotRange = errors.New("invalid end slot and start slot provided")
func (s *Store) Block(ctx context.Context, blockRoot [32]byte) (interfaces.ReadOnlySignedBeaconBlock, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.Block")
defer span.End()
blk, err := s.getBlock(ctx, blockRoot, nil)
if errors.Is(err, ErrNotFound) {
return nil, nil
}
return blk, err
}
func (s *Store) getBlock(ctx context.Context, blockRoot [32]byte, tx *bolt.Tx) (interfaces.ReadOnlySignedBeaconBlock, error) {
// Return block from cache if it exists.
if v, ok := s.blockCache.Get(string(blockRoot[:])); v != nil && ok {
return v.(interfaces.ReadOnlySignedBeaconBlock), nil
}
// This method allows the caller to pass in its tx if one is already open.
// Or if a nil value is used, a transaction will be managed intenally.
if tx == nil {
var err error
tx, err = s.db.Begin(false)
if err != nil {
return nil, err
var blk interfaces.ReadOnlySignedBeaconBlock
err := s.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket(blocksBucket)
enc := bkt.Get(blockRoot[:])
if enc == nil {
return nil
}
defer func() {
if err := tx.Rollback(); err != nil {
log.WithError(err).Error("could not rollback read-only getBlock transaction")
}
}()
}
return unmarshalBlock(ctx, tx.Bucket(blocksBucket).Get(blockRoot[:]))
var err error
blk, err = unmarshalBlock(ctx, enc)
return err
})
return blk, err
}
// OriginCheckpointBlockRoot returns the value written to the db in SaveOriginCheckpointBlockRoot
@@ -81,21 +70,6 @@ func (s *Store) OriginCheckpointBlockRoot(ctx context.Context) ([32]byte, error)
return root, err
}
// HeadBlockRoot returns the latest canonical block root in the Ethereum Beacon Chain.
func (s *Store) HeadBlockRoot() ([32]byte, error) {
var root [32]byte
err := s.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket(blocksBucket)
headRoot := bkt.Get(headBlockRootKey)
if len(headRoot) == 0 {
return errors.New("no head block root found")
}
copy(root[:], headRoot)
return nil
})
return root, err
}
// HeadBlock returns the latest canonical block in the Ethereum Beacon Chain.
func (s *Store) HeadBlock(ctx context.Context) (interfaces.ReadOnlySignedBeaconBlock, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.HeadBlock")
@@ -118,95 +92,13 @@ func (s *Store) HeadBlock(ctx context.Context) (interfaces.ReadOnlySignedBeaconB
return headBlock, err
}
// blocksAncestryQuery returns all blocks *before* the descendent block;
// that is: inclusive of q.Earliest, exclusive of q.Descendent.Slot.
func (s *Store) blocksAncestryQuery(ctx context.Context, q filters.AncestryQuery) ([]interfaces.ReadOnlySignedBeaconBlock, [][32]byte, error) {
// Save resources if no blocks will be found by the query.
if q.Span() < 1 {
return nil, nil, filters.ErrInvalidQuery
}
blocks := make([]interfaces.ReadOnlySignedBeaconBlock, 0, q.Span())
roots := make([][32]byte, 0, q.Span())
// Handle edge case where start and end are equal; slotRootsInRange would see end < start and err.
// So, just grab the descendent in its own tx and stop there.
if q.Span() == 1 {
err := s.db.View(func(tx *bolt.Tx) error {
descendent, err := s.getBlock(ctx, q.Descendent.Root, tx)
if err != nil {
return errors.Wrap(err, "descendent block not in db")
}
blocks = append(blocks, descendent)
roots = append(roots, q.Descendent.Root)
return nil
})
return blocks, roots, err
}
// stop before the descendent slot since it is determined by the query
sr, err := s.slotRootsInRange(ctx, q.Earliest, q.Descendent.Slot-1, -1)
if err != nil {
return nil, nil, err
}
err = s.db.View(func(tx *bolt.Tx) error {
descendent, err := s.getBlock(ctx, q.Descendent.Root, tx)
if err != nil {
return errors.Wrap(err, "descendent block not in db")
}
proot := descendent.Block().ParentRoot()
lowest := descendent.Block().Slot()
blocks = append(blocks, descendent)
roots = append(roots, q.Descendent.Root)
// slotRootsInRange returns the roots in descending order
for _, prev := range sr {
if prev.slot < q.Earliest {
return nil
}
if prev.slot >= lowest {
continue
}
if prev.root == proot {
p, err := s.getBlock(ctx, prev.root, tx)
if err != nil {
return err
}
roots = append(roots, prev.root)
blocks = append(blocks, p)
proot = p.Block().ParentRoot()
lowest = p.Block().Slot()
}
}
return nil
})
if err != nil {
return nil, nil, err
}
slices.Reverse(roots)
slices.Reverse(blocks)
return blocks, roots, err
}
// Blocks retrieves a list of beacon blocks and its respective roots by filter criteria.
func (s *Store) Blocks(ctx context.Context, f *filters.QueryFilter) ([]interfaces.ReadOnlySignedBeaconBlock, [][32]byte, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.Blocks")
defer span.End()
if q, err := f.GetAncestryQuery(); err == nil {
return s.blocksAncestryQuery(ctx, q)
} else {
if !errors.Is(err, filters.ErrNotSet) {
return nil, nil, err
}
}
blocks := make([]interfaces.ReadOnlySignedBeaconBlock, 0)
blockRoots := make([][32]byte, 0)
if start, end, isSimple := f.SimpleSlotRange(); isSimple {
return s.blocksForSlotRange(ctx, start, end)
}
err := s.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket(blocksBucket)
@@ -229,69 +121,6 @@ func (s *Store) Blocks(ctx context.Context, f *filters.QueryFilter) ([]interface
return blocks, blockRoots, err
}
// cleanupMissingBlockIndices cleans up the slot->root mapping, and the parent root index pointing
// from each of these blocks to each of their children. Since we don't have the blocks themselves,
// we don't know their parent root to efficiently clean the index going the other direction.
func (s *Store) cleanupMissingBlockIndices(ctx context.Context, badBlocks []slotRoot) {
errs := make([]error, 0)
err := s.db.Update(func(tx *bolt.Tx) error {
for _, sr := range badBlocks {
log.WithField("root", fmt.Sprintf("%#x", sr.root)).WithField("slot", sr.slot).Warn("Cleaning up indices for missing block")
if err := s.deleteSlotIndexEntry(tx, sr.slot, sr.root); err != nil {
errs = append(errs, errors.Wrapf(err, "failed to clean up slot index entry for root %#x and slot %d", sr.root, sr.slot))
}
if err := tx.Bucket(blockParentRootIndicesBucket).Delete(sr.root[:]); err != nil {
errs = append(errs, errors.Wrapf(err, "failed to clean up block parent index for root %#x", sr.root))
}
}
return nil
})
if err != nil {
errs = append(errs, err)
}
for _, err := range errs {
log.WithError(err).Error("Failed to clean up indices for missing block")
}
}
// blocksForSlotRange gets all blocks and roots for a given slot range.
// This function uses the slot->root index, which can contain multiple entries for the same slot
// in case of forks. It will return all blocks for the given slot range, and the roots of those blocks.
// The [i]th element of the blocks slice corresponds to the [i]th element of the roots slice.
// If a block is not found, it will be added to a slice of missing blocks, which will have their indices cleaned
// in a separate Update transaction before the method returns. This is done to compensate for a previous bug where
// block deletions left danging index entries.
func (s *Store) blocksForSlotRange(ctx context.Context, startSlot, endSlot primitives.Slot) ([]interfaces.ReadOnlySignedBeaconBlock, [][32]byte, error) {
slotRootPairs, err := s.slotRootsInRange(ctx, startSlot, endSlot, -1) // set batch size to zero to retrieve all
if err != nil {
return nil, nil, err
}
slices.Reverse(slotRootPairs)
badBlocks := make([]slotRoot, 0)
defer func() { s.cleanupMissingBlockIndices(ctx, badBlocks) }()
roots := make([][32]byte, 0, len(slotRootPairs))
blks := make([]interfaces.ReadOnlySignedBeaconBlock, 0, len(slotRootPairs))
err = s.db.View(func(tx *bolt.Tx) error {
for _, sr := range slotRootPairs {
blk, err := s.getBlock(ctx, sr.root, tx)
if err != nil {
if errors.Is(err, ErrNotFound) {
badBlocks = append(badBlocks, sr)
continue
}
return err
}
roots = append(roots, sr.root)
blks = append(blks, blk)
}
return nil
})
if err != nil {
return nil, nil, err
}
return blks, roots, nil
}
// BlockRoots retrieves a list of beacon block roots by filter criteria. If the caller
// requires both the blocks and the block roots for a certain filter they should instead
// use the Blocks function rather than use BlockRoots. During periods of non finality
@@ -331,7 +160,7 @@ func (s *Store) HasBlock(ctx context.Context, blockRoot [32]byte) bool {
exists = bkt.Get(blockRoot[:]) != nil
return nil
}); err != nil { // This view never returns an error, but we'll handle anyway for sanity.
panic(err) // lint:nopanic -- View never returns an error.
panic(err)
}
return exists
}
@@ -398,21 +227,6 @@ func (s *Store) DeleteBlock(ctx context.Context, root [32]byte) error {
return ErrDeleteJustifiedAndFinalized
}
// Look up the block to find its slot; needed to remove the slot index entry.
blk, err := s.getBlock(ctx, root, tx)
if err != nil {
// getBlock can return ErrNotFound, in which case we won't even try to delete it.
if errors.Is(err, ErrNotFound) {
return nil
}
return err
}
if err := s.deleteSlotIndexEntry(tx, blk.Block().Slot(), root); err != nil {
return err
}
if err := s.deleteMatchingParentIndex(tx, blk.Block().ParentRoot(), root); err != nil {
return err
}
if err := s.deleteBlock(tx, root[:]); err != nil {
return err
}
@@ -866,7 +680,6 @@ type slotRoot struct {
}
// slotRootsInRange returns slot and block root pairs of length min(batchSize, end-slot)
// If batchSize < 0, the limit check will be skipped entirely.
func (s *Store) slotRootsInRange(ctx context.Context, start, end primitives.Slot, batchSize int) ([]slotRoot, error) {
_, span := trace.StartSpan(ctx, "BeaconDB.slotRootsInRange")
defer span.End()
@@ -876,26 +689,10 @@ func (s *Store) slotRootsInRange(ctx context.Context, start, end primitives.Slot
var pairs []slotRoot
key := bytesutil.SlotToBytesBigEndian(end)
edge := false // used to detect whether we are at the very beginning or end of the index
err := s.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket(blockSlotIndicesBucket)
c := bkt.Cursor()
for k, v := c.Seek(key); ; /* rely on internal checks to exit */ k, v = c.Prev() {
if len(k) == 0 && len(v) == 0 {
// The `edge`` variable and this `if` deal with 2 edge cases:
// - Seeking past the end of the bucket (the `end` param is higher than the highest slot).
// - Seeking before the beginning of the bucket (the `start` param is lower than the lowest slot).
// In both of these cases k,v will be nil and we can handle the same way using `edge` to
// - continue to the next iteration. If the following Prev() key/value is nil, Prev has gone past the beginning.
// - Otherwise, iterate as usual.
if edge {
return nil
}
edge = true
continue
}
edge = false
for k, v := c.Seek(key); k != nil; k, v = c.Prev() {
slot := bytesutil.BytesToSlotBigEndian(k)
if slot > end {
continue // Seek will seek to the next key *after* the given one if not present
@@ -910,13 +707,11 @@ func (s *Store) slotRootsInRange(ctx context.Context, start, end primitives.Slot
for _, r := range roots {
pairs = append(pairs, slotRoot{slot: slot, root: r})
}
if batchSize < 0 {
continue
}
if len(pairs) >= batchSize {
return nil // allows code to easily cap the number of items pruned
}
}
return nil
})
return pairs, err
@@ -1104,9 +899,6 @@ func createBlockIndicesFromFilters(ctx context.Context, f *filters.QueryFilter)
// unmarshal block from marshaled proto beacon block bytes to versioned beacon block struct type.
func unmarshalBlock(_ context.Context, enc []byte) (interfaces.ReadOnlySignedBeaconBlock, error) {
if len(enc) == 0 {
return nil, errors.Wrap(ErrNotFound, "empty block bytes in db")
}
var err error
enc, err = snappy.Decode(nil, enc)
if err != nil {
@@ -1258,47 +1050,6 @@ func (s *Store) deleteBlock(tx *bolt.Tx, root []byte) error {
return nil
}
func (s *Store) deleteMatchingParentIndex(tx *bolt.Tx, parent, child [32]byte) error {
bkt := tx.Bucket(blockParentRootIndicesBucket)
if err := deleteRootIndexEntry(bkt, parent[:], child); err != nil {
return errors.Wrap(err, "could not delete parent root index entry")
}
return nil
}
func (s *Store) deleteSlotIndexEntry(tx *bolt.Tx, slot primitives.Slot, root [32]byte) error {
key := bytesutil.SlotToBytesBigEndian(slot)
bkt := tx.Bucket(blockSlotIndicesBucket)
if err := deleteRootIndexEntry(bkt, key, root); err != nil {
return errors.Wrap(err, "could not delete slot index entry")
}
return nil
}
func deleteRootIndexEntry(bkt *bolt.Bucket, key []byte, root [32]byte) error {
packed := bkt.Get(key)
if len(packed) == 0 {
return nil
}
updated, err := removeRoot(packed, root)
if err != nil {
return err
}
// Don't update the value if the root was not found.
if bytes.Equal(updated, packed) {
return nil
}
// If there are no other roots in the key, just delete it.
if len(updated) == 0 {
if err := bkt.Delete(key); err != nil {
return err
}
return nil
}
// Update the key with the root removed.
return bkt.Put(key, updated)
}
func (s *Store) deleteValidatorHashes(tx *bolt.Tx, root []byte) error {
ok, err := s.isStateValidatorMigrationOver()
if err != nil {

View File

@@ -24,11 +24,9 @@ import (
"google.golang.org/protobuf/proto"
)
type testNewBlockFunc func(primitives.Slot, []byte) (interfaces.ReadOnlySignedBeaconBlock, error)
var blockTests = []struct {
name string
newBlock testNewBlockFunc
newBlock func(primitives.Slot, []byte) (interfaces.ReadOnlySignedBeaconBlock, error)
}{
{
name: "phase0",
@@ -198,13 +196,9 @@ func TestStore_BlocksCRUD(t *testing.T) {
blockRoot, err := blk.Block().HashTreeRoot()
require.NoError(t, err)
_, err = db.getBlock(ctx, blockRoot, nil)
require.ErrorIs(t, err, ErrNotFound)
retrievedBlock, err := db.Block(ctx, blockRoot)
require.NoError(t, err)
assert.DeepEqual(t, nil, retrievedBlock, "Expected nil block")
_, err = db.getBlock(ctx, blockRoot, nil)
require.ErrorIs(t, err, ErrNotFound)
require.NoError(t, db.SaveBlock(ctx, blk))
assert.Equal(t, true, db.HasBlock(ctx, blockRoot), "Expected block to exist in the db")
@@ -220,34 +214,10 @@ func TestStore_BlocksCRUD(t *testing.T) {
retrievedPb, err := retrievedBlock.Proto()
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(wantedPb, retrievedPb), "Wanted: %v, received: %v", wanted, retrievedBlock)
// Check that the block is in the slot->block index
found, roots, err := db.BlockRootsBySlot(ctx, blk.Block().Slot())
require.NoError(t, err)
require.Equal(t, true, found)
require.Equal(t, 1, len(roots))
require.Equal(t, blockRoot, roots[0])
// Delete the block, then check that it is no longer in the index.
parent := blk.Block().ParentRoot()
testCheckParentIndices(t, db.db, parent, true)
require.NoError(t, db.DeleteBlock(ctx, blockRoot))
require.NoError(t, err)
testCheckParentIndices(t, db.db, parent, false)
found, roots, err = db.BlockRootsBySlot(ctx, blk.Block().Slot())
require.NoError(t, err)
require.Equal(t, false, found)
require.Equal(t, 0, len(roots))
})
}
}
func testCheckParentIndices(t *testing.T, db *bolt.DB, parent [32]byte, expected bool) {
require.NoError(t, db.View(func(tx *bolt.Tx) error {
require.Equal(t, expected, tx.Bucket(blockParentRootIndicesBucket).Get(parent[:]) != nil)
return nil
}))
}
func TestStore_BlocksHandleZeroCase(t *testing.T) {
for _, tt := range blockTests {
t.Run(tt.name, func(t *testing.T) {
@@ -744,120 +714,6 @@ func TestStore_Blocks_FiltersCorrectly(t *testing.T) {
}
}
func testBlockChain(t *testing.T, nb testNewBlockFunc, slots []primitives.Slot, parent []byte) []interfaces.ReadOnlySignedBeaconBlock {
if len(parent) < 32 {
var zero [32]byte
copy(parent, zero[:])
}
chain := make([]interfaces.ReadOnlySignedBeaconBlock, 0, len(slots))
for _, slot := range slots {
pr := make([]byte, 32)
copy(pr, parent)
b, err := nb(slot, pr)
require.NoError(t, err)
chain = append(chain, b)
npr, err := b.Block().HashTreeRoot()
parent = npr[:]
require.NoError(t, err)
}
return chain
}
func testSlotSlice(start, end primitives.Slot) []primitives.Slot {
end += 1 // add 1 to make the range inclusive
slots := make([]primitives.Slot, 0, end-start)
for ; start < end; start++ {
slots = append(slots, start)
}
return slots
}
func TestCleanupMissingBlockIndices(t *testing.T) {
for _, tt := range blockTests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
db := setupDB(t)
chain := testBlockChain(t, tt.newBlock, testSlotSlice(1, 10), nil)
require.NoError(t, db.SaveBlocks(ctx, chain))
corrupt, err := blocks.NewROBlock(chain[5])
require.NoError(t, err)
cr := corrupt.Root()
require.NoError(t, db.db.Update(func(tx *bolt.Tx) error {
return tx.Bucket(blocksBucket).Delete(cr[:])
}))
// Need to also delete it from the cache!!
db.blockCache.Del(string(cr[:]))
res, roots, err := db.Blocks(ctx, filters.NewFilter().SetEndSlot(10).SetStartSlot(1))
require.NoError(t, err)
require.Equal(t, 9, len(roots))
require.Equal(t, len(res), len(roots))
require.NoError(t, db.db.View(func(tx *bolt.Tx) error {
encSlot := bytesutil.SlotToBytesBigEndian(corrupt.Block().Slot())
// make sure slot->root index is cleaned up
require.Equal(t, 0, len(tx.Bucket(blockSlotIndicesBucket).Get(encSlot)))
require.Equal(t, 0, len(tx.Bucket(blockParentRootIndicesBucket).Get(cr[:])))
return nil
}))
})
}
}
func TestCleanupMissingForkedBlockIndices(t *testing.T) {
for _, tt := range blockTests[0:1] {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
db := setupDB(t)
chain := testBlockChain(t, tt.newBlock, testSlotSlice(1, 10), nil)
require.NoError(t, db.SaveBlocks(ctx, chain))
// forkChain should skip the slot at skipBlock, and have the same parent
skipBlockParent := chain[4].Block().ParentRoot()
// It should start at the same slot as missingBlock, which comes one slot after the skip slot,
// so there are 2 blocks in that slot
missingBlock, err := blocks.NewROBlock(chain[5])
require.NoError(t, err)
// missingBlock will be deleted in the main chain, but there will be a block at that slot in the fork chain
forkChain := testBlockChain(t, tt.newBlock, testSlotSlice(missingBlock.Block().Slot(), 10), skipBlockParent[:])
require.NoError(t, db.SaveBlocks(ctx, forkChain))
forkChainStart, err := blocks.NewROBlock(forkChain[0])
require.NoError(t, err)
encMissingSlot := bytesutil.SlotToBytesBigEndian(missingBlock.Block().Slot())
require.NoError(t, db.db.View(func(tx *bolt.Tx) error {
require.Equal(t, 32, len(tx.Bucket(blockParentRootIndicesBucket).Get(missingBlock.RootSlice())))
// There are 2 block roots packed in this slot, so it is 64 bytes long
require.Equal(t, 64, len(tx.Bucket(blockSlotIndicesBucket).Get(encMissingSlot)))
// skipBlockParent should also have 2 entries and be 64 bytes, since the forkChain is based on the same parent as the skip block
childRoots := tx.Bucket(blockParentRootIndicesBucket).Get(skipBlockParent[:])
require.Equal(t, 64, len(childRoots))
return nil
}))
require.NoError(t, db.db.Update(func(tx *bolt.Tx) error {
return tx.Bucket(blocksBucket).Delete(missingBlock.RootSlice())
}))
// Need to also delete it from the cache!!
db.blockCache.Del(string(missingBlock.RootSlice()))
// Blocks should give us blocks from all chains.
res, roots, err := db.Blocks(ctx, filters.NewFilter().SetEndSlot(10).SetStartSlot(1))
require.NoError(t, err)
require.Equal(t, (len(chain)-1)+len(forkChain), len(roots))
require.Equal(t, len(res), len(roots))
require.NoError(t, db.db.View(func(tx *bolt.Tx) error {
// There should now be 32 bytes in this index - one root from the forked chain
slotIdxVal := tx.Bucket(blockSlotIndicesBucket).Get(encMissingSlot)
require.Equal(t, forkChainStart.Root(), [32]byte(slotIdxVal))
require.Equal(t, 0, len(tx.Bucket(blockParentRootIndicesBucket).Get(missingBlock.RootSlice())))
forkChildRoot := tx.Bucket(blockParentRootIndicesBucket).Get(skipBlockParent[:])
require.Equal(t, 64, len(forkChildRoot))
return nil
}))
})
}
}
func TestStore_Blocks_VerifyBlockRoots(t *testing.T) {
for _, tt := range blockTests {
t.Run(tt.name, func(t *testing.T) {

View File

@@ -20,7 +20,7 @@ func (s *Store) DepositContractAddress(ctx context.Context) ([]byte, error) {
addr = chainInfo.Get(depositContractAddressKey)
return nil
}); err != nil { // This view never returns an error, but we'll handle anyway for sanity.
panic(err) // lint:nopanic -- View never returns an error.
panic(err)
}
return addr, nil
}

View File

@@ -407,7 +407,7 @@ func (s *Store) HasState(ctx context.Context, blockRoot [32]byte) bool {
return nil
})
if err != nil {
panic(err) // lint:nopanic -- View never returns an error.
panic(err)
}
return hasState
}

View File

@@ -114,27 +114,3 @@ func splitRoots(b []byte) ([][32]byte, error) {
}
return rl, nil
}
func removeRoot(roots []byte, root [32]byte) ([]byte, error) {
if len(roots) == 0 {
return []byte{}, nil
}
if len(roots) == 32 && bytes.Equal(roots, root[:]) {
return []byte{}, nil
}
if len(roots)%32 != 0 {
return nil, errors.Wrapf(errMisalignedRootList, "root list len=%d", len(roots))
}
search := root[:]
for i := 0; i <= len(roots)-32; i += 32 {
if bytes.Equal(roots[i:i+32], search) {
result := make([]byte, len(roots)-32)
copy(result, roots[:i])
copy(result[i:], roots[i+32:])
return result, nil
}
}
return roots, nil
}

View File

@@ -1,7 +1,6 @@
package kv
import (
"bytes"
"context"
"crypto/rand"
"testing"
@@ -196,85 +195,3 @@ func TestSplitRoots(t *testing.T) {
})
}
}
func tPad(p ...[]byte) []byte {
r := make([]byte, 32*len(p))
for i, b := range p {
copy(r[i*32:], b)
}
return r
}
func TestRemoveRoot(t *testing.T) {
cases := []struct {
name string
roots []byte
root [32]byte
expect []byte
err error
}{
{
name: "empty",
roots: []byte{},
root: [32]byte{0xde, 0xad, 0xbe, 0xef},
expect: []byte{},
},
{
name: "single",
roots: tPad([]byte{0xde, 0xad, 0xbe, 0xef}),
root: [32]byte{0xde, 0xad, 0xbe, 0xef},
expect: []byte{},
},
{
name: "single, different",
roots: tPad([]byte{0xde, 0xad, 0xbe, 0xef}),
root: [32]byte{0xde, 0xad, 0xbe, 0xee},
expect: tPad([]byte{0xde, 0xad, 0xbe, 0xef}),
},
{
name: "multi",
roots: tPad([]byte{0xde, 0xad, 0xbe, 0xef}, []byte{0xac, 0x1d, 0xfa, 0xce}),
root: [32]byte{0xac, 0x1d, 0xfa, 0xce},
expect: tPad([]byte{0xde, 0xad, 0xbe, 0xef}),
},
{
name: "multi, reordered",
roots: tPad([]byte{0xac, 0x1d, 0xfa, 0xce}, []byte{0xde, 0xad, 0xbe, 0xef}),
root: [32]byte{0xac, 0x1d, 0xfa, 0xce},
expect: tPad([]byte{0xde, 0xad, 0xbe, 0xef}),
},
{
name: "multi, 3",
roots: tPad([]byte{0xac, 0x1d, 0xfa, 0xce}, []byte{0xbe, 0xef, 0xca, 0xb5}, []byte{0xde, 0xad, 0xbe, 0xef}),
root: [32]byte{0xac, 0x1d, 0xfa, 0xce},
expect: tPad([]byte{0xbe, 0xef, 0xca, 0xb5}, []byte{0xde, 0xad, 0xbe, 0xef}),
},
{
name: "multi, different",
roots: tPad([]byte{0xde, 0xad, 0xbe, 0xef}, []byte{0xac, 0x1d, 0xfa, 0xce}),
root: [32]byte{0xac, 0x1d, 0xbe, 0xa7},
expect: tPad([]byte{0xde, 0xad, 0xbe, 0xef}, []byte{0xac, 0x1d, 0xfa, 0xce}),
},
{
name: "misaligned",
roots: make([]byte, 61),
root: [32]byte{0xac, 0x1d, 0xbe, 0xa7},
err: errMisalignedRootList,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
before := make([]byte, len(c.roots))
copy(before, c.roots)
r, err := removeRoot(c.roots, c.root)
if c.err != nil {
require.ErrorIs(t, err, c.err)
return
}
require.NoError(t, err)
require.Equal(t, len(c.expect), len(r))
require.Equal(t, true, bytes.Equal(c.expect, r))
require.Equal(t, true, bytes.Equal(before, c.roots))
})
}
}

View File

@@ -1,10 +1,8 @@
package kv
import (
"bytes"
"context"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
bolt "go.etcd.io/bbolt"
@@ -21,17 +19,7 @@ func (s *Store) LastValidatedCheckpoint(ctx context.Context) (*ethpb.Checkpoint,
if enc == nil {
var finErr error
checkpoint, finErr = s.FinalizedCheckpoint(ctx)
if finErr != nil {
return finErr
}
if bytes.Equal(checkpoint.Root, params.BeaconConfig().ZeroHash[:]) {
bkt = tx.Bucket(blocksBucket)
r := bkt.Get(genesisBlockRootKey)
if r != nil {
checkpoint.Root = r
}
}
return nil
return finErr
}
checkpoint = &ethpb.Checkpoint{}
return decode(ctx, enc, checkpoint)

View File

@@ -5,17 +5,15 @@ import (
"testing"
"time"
dbtest "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
slottest "github.com/prysmaticlabs/prysm/v5/time/slots/testing"
"github.com/sirupsen/logrus"
dbtest "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/testing/require"
logTest "github.com/sirupsen/logrus/hooks/test"
)

View File

@@ -587,7 +587,7 @@ func BenchmarkHighestAttestations(b *testing.B) {
beaconDB := setupDB(b)
require.NoError(b, beaconDB.SaveAttestationRecordsForValidators(ctx, atts))
allIndices := make([]primitives.ValidatorIndex, 0, valsPerAtt*count)
allIndices := make([]primitives.ValidatorIndex, valsPerAtt*count)
for i := 0; i < count; i++ {
indicesForAtt := make([]primitives.ValidatorIndex, valsPerAtt)
for r := 0; r < valsPerAtt; r++ {

View File

@@ -158,7 +158,7 @@ func trim(queue *cache.FIFO, maxSize uint64) {
for s := uint64(len(queue.ListKeys())); s > maxSize; s-- {
// #nosec G104 popProcessNoopFunc never returns an error
if _, err := queue.Pop(popProcessNoopFunc); err != nil { // This never returns an error, but we'll handle anyway for sanity.
panic(err) // lint:nopanic -- popProcessNoopFunc never returns an error.
panic(err)
}
}
}

View File

@@ -1,2 +1,2 @@
go test fuzz v1
[]byte("{\"baseFeePerGas\":\"0x7fffffffffffffff\",\"difficulty\":\"0x7fffffffffffffff\",\"extraData\":\"0x\",\"gasLimit\":\"0xffffffffffffffff\",\"gasUsed\":\"0xffffffffffffffff\",\"hash\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"logsBloom\":\"0x6a756e6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"nonce\":\"0x0000000000000000\",\"number\":\"0x7fffffffffffffff\",\"parentHash\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"receiptsRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"sha3Uncles\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"stateRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"timestamp\":\"0x64\",\"totalDifficulty\":\"999999999999999999999999999999999999999\",\"transactions\":[{\"type\":\"0x2\",\"nonce\":\"0xffffffffffffffff\",\"gasPrice\":null,\"maxPriorityFeePerGas\":\"0x7fffffffffffffff\",\"maxFeePerGas\":\"0x7fffffffffffffff\",\"gas\":\"0xffffffffffffffff\",\"value\":\"0x7fffffffffffffff\",\"input\":\"0x72616e646f6d\",\"v\":\"0x0\",\"r\":\"0x7fffffffffffffff\",\"s\":\"0x7fffffffffffffff\",\"to\":\"0x095e7baea6a6c7c4c2dfeb977efac326af552d87\",\"chainId\":\"0x7fffffffffffffff\",\"accessList\":[],\"hash\":\"0x26db3ef2c0e5945b24088d6a4165d0bb2959abd848b57891aa041b72518548ab\"},{\"type\":\"0x2\",\"nonce\":\"0xffffffffffffffff\",\"gasPrice\":null,\"maxPriorityFeePerGas\":\"0x7fffffffffffffff\",\"maxFeePerGas\":\"0x7fffffffffffffff\",\"gas\":\"0xfffffffffffaffff\",\"value\":\"0x7fffffffffffffff\",\"input\":\"0x72616e646f6d\",\"v\":\"0x0\",\"r\":\"0x7fffffffffffffff\",\"s\":\"0x7fffffffffffffff\",\"to\":\"0x095e7baea6a6c7c4c2dfeb977efac326af552d87\",\"chainId\":\"0x7fffffffffffffff\",\"accessList\":[],\"hash\":\"0x26db3ef2c0e5945b24088d6a4165d0bb2959abd848b57891aa041b72518548ab\"}],\"transactionsRoot\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}")
[]byte("{\"baseFeePerGas\":\"0x7fffffffffffffff\",\"difficulty\":\"0x7fffffffffffffff\",\"extraData\":\"0x\",\"gasLimit\":\"0xffffffffffffffff\",\"gasUsed\":\"0xffffffffffffffff\",\"hash\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"logsBloom\":\"0x6a756e6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"nonce\":\"0x0000000000000000\",\"number\":\"0x7fffffffffffffff\",\"parentHash\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"receiptsRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"sha3Uncles\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"stateRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"timestamp\":\"0x64\",\"totalDifficulty\":\"999999999999999999999999999999999999999\",\"transactions\":[{\"type\":\"0x2\",\"nonce\":\"0xffffffffffffffff\",\"gasPrice\":null,\"maxPriorityFeePerGas\":\"0x7fffffffffffffff\",\"maxFeePerGas\":\"0x7fffffffffffffff\",\"gas\":\"0xffffffffffffffff\",\"value\":\"0x7fffffffffffffff\",\"input\":\"0x72616e646f6d\",\"v\":\"0x0\",\"r\":\"0x7fffffffffffffff\",\"s\":\"0x7fffffffffffffff\",\"to\":\"0x095e7baea6a6c7c4c2dfeb977efac326af552d87\",\"chainId\":\"0x7fffffffffffffff\",\"accessList\":[],\"hash\":\"0x26db3ef2c0e5945b24088d6a4165d0bb2959abd848b57891aa041b72518548ab\"},{\"type\":\"0x2\",\"nonce\":\"0xffffffffffffffff\",\"gasPrice\":null,\"maxPriorityFeePerGas\":\"0x7fffffffffffffff\",\"maxFeePerGas\":\"0x7fffffffffffffff\",\"gas\":\"0xfffffffffffaffff\",\"value\":\"0x7fffffffffffffff\",\"input\":\"0x72616e646f6d\",\"v\":\"0x0\",\"r\":\"0x7fffffffffffffff\",\"s\":\"0x7fffffffffffffff\",\"to\":\"0x095e7baea6a6c7c4c2dfeb977efac326af552d87\",\"chainId\":\"0x7fffffffffffffff\",\"accessList\":[],\"hash\":\"0x26db3ef2c0e5945b24088d6a4165d0bb2959abd848b57891aa041b72518548ab\"}],\"transactionsRoot\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}")

View File

@@ -1,2 +1,2 @@
go test fuzz v1
[]byte("{\"0000000000000\":{},\"pAYloAdId\":\"\"}")
[]byte("{\"0000000000000\":{},\"pAYloAdId\":\"\"}")

View File

@@ -57,7 +57,7 @@ func (*FaultyExecutionChain) ChainStartEth1Data() *ethpb.Eth1Data {
func (*FaultyExecutionChain) PreGenesisState() state.BeaconState {
s, err := state_native.InitializeFromProtoUnsafePhase0(&ethpb.BeaconState{})
if err != nil {
panic("could not initialize state") // lint:nopanic -- test code.
panic("could not initialize state")
}
return s
}

View File

@@ -622,25 +622,6 @@ func (f *ForkChoice) Slot(root [32]byte) (primitives.Slot, error) {
return n.slot, nil
}
// DependentRoot returns the last root of the epoch prior to the requested ecoch in the canonical chain.
func (f *ForkChoice) DependentRoot(epoch primitives.Epoch) ([32]byte, error) {
tr, err := f.TargetRootForEpoch(f.CachedHeadRoot(), epoch)
if err != nil {
return [32]byte{}, err
}
if tr == [32]byte{} {
return [32]byte{}, nil
}
n, ok := f.store.nodeByRoot[tr]
if !ok || n == nil {
return [32]byte{}, ErrNilNode
}
if slots.ToEpoch(n.slot) == epoch && n.parent != nil {
n = n.parent
}
return n.root, nil
}
// TargetRootForEpoch returns the root of the target block for a given epoch.
// The epoch parameter is crucial to identify the correct target root. For example:
// When inserting a block at slot 63 with block root 0xA and target root 0xB (pointing to the block at slot 32),

View File

@@ -30,7 +30,7 @@ func TestLastRoot(t *testing.T) {
st, root, err = prepareForkchoiceState(ctx, 34, [32]byte{'6'}, [32]byte{'5'}, [32]byte{'6'}, 0, 0)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, st, root))
headNode := f.store.nodeByRoot[[32]byte{'6'}]
headNode, _ := f.store.nodeByRoot[[32]byte{'6'}]
f.store.headNode = headNode
require.Equal(t, [32]byte{'6'}, f.store.headNode.root)
require.Equal(t, [32]byte{'2'}, f.LastRoot(0))

View File

@@ -252,13 +252,6 @@ func (s *Store) tips() ([][32]byte, []primitives.Slot) {
return roots, slots
}
func (f *ForkChoice) HighestReceivedBlockRoot() [32]byte {
if f.store.highestReceivedNode == nil {
return [32]byte{}
}
return f.store.highestReceivedNode.root
}
// HighestReceivedBlockSlot returns the highest slot received by the forkchoice
func (f *ForkChoice) HighestReceivedBlockSlot() primitives.Slot {
if f.store.highestReceivedNode == nil {

View File

@@ -471,101 +471,53 @@ func TestStore_TargetRootForEpoch(t *testing.T) {
target, err := f.TargetRootForEpoch(blk.Root(), 1)
require.NoError(t, err)
require.Equal(t, target, blk.Root())
dependent, err := f.DependentRoot(1)
require.NoError(t, err)
require.Equal(t, dependent, [32]byte{})
state, blk1, err := prepareForkchoiceState(ctx, params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'b'}, blk.Root(), params.BeaconConfig().ZeroHash, 1, 1)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, state, blk1))
headRoot, err := f.Head(ctx) // To cache the head root
require.NoError(t, err)
require.Equal(t, headRoot, blk1.Root())
target, err = f.TargetRootForEpoch(blk1.Root(), 1)
require.NoError(t, err)
require.Equal(t, target, blk.Root())
dependent, err = f.DependentRoot(1)
require.NoError(t, err)
require.Equal(t, dependent, [32]byte{})
// Insert a block for the next epoch (missed slot 0)
state, blk2, err := prepareForkchoiceState(ctx, 2*params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'c'}, blk1.Root(), params.BeaconConfig().ZeroHash, 1, 1)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, state, blk2))
headRoot, err = f.Head(ctx)
require.NoError(t, err)
require.Equal(t, headRoot, blk2.Root())
target, err = f.TargetRootForEpoch(blk2.Root(), 2)
require.NoError(t, err)
require.Equal(t, target, blk1.Root())
dependent, err = f.DependentRoot(1)
require.NoError(t, err)
require.Equal(t, dependent, [32]byte{})
headRoot, err = f.Head(ctx)
require.NoError(t, err)
require.Equal(t, headRoot, blk2.Root())
dependent, err = f.DependentRoot(2)
require.NoError(t, err)
require.Equal(t, dependent, blk1.Root())
state, blk3, err := prepareForkchoiceState(ctx, 2*params.BeaconConfig().SlotsPerEpoch+2, [32]byte{'d'}, blk2.Root(), params.BeaconConfig().ZeroHash, 1, 1)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, state, blk3))
headRoot, err = f.Head(ctx)
require.NoError(t, err)
require.Equal(t, headRoot, blk3.Root())
target, err = f.TargetRootForEpoch(blk2.Root(), 2)
require.NoError(t, err)
require.Equal(t, target, blk1.Root())
dependent, err = f.DependentRoot(2)
require.NoError(t, err)
require.Equal(t, dependent, blk1.Root())
// Prune finalization
s := f.store
s.finalizedCheckpoint.Root = blk1.Root()
s.justifiedCheckpoint.Root = blk1.Root()
require.NoError(t, s.prune(ctx))
target, err = f.TargetRootForEpoch(blk1.Root(), 1)
require.NoError(t, err)
require.Equal(t, [32]byte{}, target)
dependent, err = f.DependentRoot(1)
require.NoError(t, err)
require.Equal(t, [32]byte{}, dependent)
// Insert a block for next epoch (slot 0 present)
state, blk4, err := prepareForkchoiceState(ctx, 3*params.BeaconConfig().SlotsPerEpoch, [32]byte{'e'}, blk1.Root(), params.BeaconConfig().ZeroHash, 1, 1)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, state, blk4))
headRoot, err = f.Head(ctx)
require.NoError(t, err)
require.Equal(t, headRoot, blk4.Root())
target, err = f.TargetRootForEpoch(blk4.Root(), 3)
require.NoError(t, err)
require.Equal(t, target, blk4.Root())
dependent, err = f.DependentRoot(3)
require.NoError(t, err)
require.Equal(t, dependent, blk1.Root())
dependent, err = f.DependentRoot(2)
require.NoError(t, err)
require.Equal(t, dependent, blk1.Root())
state, blk5, err := prepareForkchoiceState(ctx, 3*params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'f'}, blk4.Root(), params.BeaconConfig().ZeroHash, 1, 1)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, state, blk5))
headRoot, err = f.Head(ctx)
require.NoError(t, err)
require.Equal(t, headRoot, blk5.Root())
target, err = f.TargetRootForEpoch(blk5.Root(), 3)
require.NoError(t, err)
require.Equal(t, target, blk4.Root())
dependent, err = f.DependentRoot(3)
require.NoError(t, err)
require.Equal(t, dependent, blk1.Root())
dependent, err = f.DependentRoot(2)
require.NoError(t, err)
require.Equal(t, dependent, blk1.Root())
// Target root where the target epoch is same or ahead of the block slot
target, err = f.TargetRootForEpoch(blk5.Root(), 4)
@@ -581,21 +533,9 @@ func TestStore_TargetRootForEpoch(t *testing.T) {
state, blk6, err := prepareForkchoiceState(ctx, 4*params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'g'}, blk5.Root(), params.BeaconConfig().ZeroHash, 1, 1)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, state, blk6))
headRoot, err = f.Head(ctx)
require.NoError(t, err)
require.Equal(t, headRoot, blk6.Root())
target, err = f.TargetRootForEpoch(blk6.Root(), 4)
require.NoError(t, err)
require.Equal(t, target, blk5.Root())
dependent, err = f.DependentRoot(4)
require.NoError(t, err)
require.Equal(t, dependent, blk5.Root())
dependent, err = f.DependentRoot(3)
require.NoError(t, err)
require.Equal(t, dependent, blk1.Root())
dependent, err = f.DependentRoot(1)
require.NoError(t, err)
require.Equal(t, dependent, [32]byte{})
target, err = f.TargetRootForEpoch(blk6.Root(), 2)
require.NoError(t, err)
require.Equal(t, target, blk1.Root())

View File

@@ -65,7 +65,6 @@ type FastGetter interface {
FinalizedPayloadBlockHash() [32]byte
HasNode([32]byte) bool
HighestReceivedBlockSlot() primitives.Slot
HighestReceivedBlockRoot() [32]byte
HighestReceivedBlockDelay() primitives.Slot
IsCanonical(root [32]byte) bool
IsOptimistic(root [32]byte) (bool, error)
@@ -79,7 +78,6 @@ type FastGetter interface {
ReceivedBlocksLastEpoch() (uint64, error)
ShouldOverrideFCU() bool
Slot([32]byte) (primitives.Slot, error)
DependentRoot(primitives.Epoch) ([32]byte, error)
TargetRootForEpoch([32]byte, primitives.Epoch) ([32]byte, error)
UnrealizedJustifiedPayloadBlockHash() [32]byte
Weight(root [32]byte) (uint64, error)

View File

@@ -114,13 +114,6 @@ func (ro *ROForkChoice) HighestReceivedBlockSlot() primitives.Slot {
return ro.getter.HighestReceivedBlockSlot()
}
// HighestReceivedBlockRoot delegates to the underlying forkchoice call, under a lock.
func (ro *ROForkChoice) HighestReceivedBlockRoot() [32]byte {
ro.l.RLock()
defer ro.l.RUnlock()
return ro.getter.HighestReceivedBlockRoot()
}
// HighestReceivedBlockDelay delegates to the underlying forkchoice call, under a lock.
func (ro *ROForkChoice) HighestReceivedBlockDelay() primitives.Slot {
ro.l.RLock()
@@ -170,13 +163,6 @@ func (ro *ROForkChoice) LastRoot(e primitives.Epoch) [32]byte {
return ro.getter.LastRoot(e)
}
// DependentRoot delegates to the underlying forkchoice call, under a lock.
func (ro *ROForkChoice) DependentRoot(epoch primitives.Epoch) ([32]byte, error) {
ro.l.RLock()
defer ro.l.RUnlock()
return ro.getter.DependentRoot(epoch)
}
// TargetRootForEpoch delegates to the underlying forkchoice call, under a lock.
func (ro *ROForkChoice) TargetRootForEpoch(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
ro.l.RLock()

Some files were not shown because too many files have changed in this diff Show More