mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Compare commits
63 Commits
v5.3.0
...
fix-optimi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0e74f0ae9 | ||
|
|
f40b4f16c2 | ||
|
|
7fd4f746d6 | ||
|
|
2362d9f3c2 | ||
|
|
db5d03eeb1 | ||
|
|
fff8f7276f | ||
|
|
5c01cd3915 | ||
|
|
2cdbfeceff | ||
|
|
6b84f8c6b1 | ||
|
|
997a9112d1 | ||
|
|
d46ca97680 | ||
|
|
417bbf8a9e | ||
|
|
a7b016c954 | ||
|
|
6015493de9 | ||
|
|
c718bdbe2b | ||
|
|
0a8f947169 | ||
|
|
d7efccf6a5 | ||
|
|
334920bc9e | ||
|
|
6e00db433c | ||
|
|
c6344e7c3e | ||
|
|
2131254722 | ||
|
|
b6d1866deb | ||
|
|
e56f489d06 | ||
|
|
bf62afb27c | ||
|
|
8369056027 | ||
|
|
09499a732f | ||
|
|
2ee015452c | ||
|
|
ffc1bf8bbe | ||
|
|
014dbd5c3a | ||
|
|
9bceaa59d2 | ||
|
|
832ebb3f39 | ||
|
|
8345c271cc | ||
|
|
56208aa84d | ||
|
|
b866a2c744 | ||
|
|
a77234e637 | ||
|
|
e0e7354708 | ||
|
|
0f86a16915 | ||
|
|
972c22b02f | ||
|
|
93c27340e4 | ||
|
|
c3edb32558 | ||
|
|
3baaa732df | ||
|
|
8ceb7e76ea | ||
|
|
4d5dddd302 | ||
|
|
55efccb07f | ||
|
|
961d8e1481 | ||
|
|
d396a9931e | ||
|
|
e3f8f121f4 | ||
|
|
80f29e9eda | ||
|
|
8995d8133a | ||
|
|
31044206b8 | ||
|
|
3a1702e56f | ||
|
|
501ec74a48 | ||
|
|
c248fe0bb3 | ||
|
|
215fbcb2e4 | ||
|
|
e39f44b529 | ||
|
|
9eff6ae476 | ||
|
|
3eec5a5cb6 | ||
|
|
66878deb2c | ||
|
|
0b6e1711e4 | ||
|
|
15025837bb | ||
|
|
0229a2055e | ||
|
|
eb9af15c7a | ||
|
|
0584746815 |
2
.github/actions/gomodtidy/Dockerfile
vendored
2
.github/actions/gomodtidy/Dockerfile
vendored
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.23-alpine
|
||||
FROM golang:1.24-alpine
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
|
||||
16
.github/workflows/go.yml
vendored
16
.github/workflows/go.yml
vendored
@@ -28,15 +28,15 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Go 1.23
|
||||
- name: Set up Go 1.24
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.23.5'
|
||||
go-version: '1.24.0'
|
||||
- name: Run Gosec Security Scanner
|
||||
run: | # https://github.com/securego/gosec/issues/469
|
||||
export PATH=$PATH:$(go env GOPATH)/bin
|
||||
go install github.com/securego/gosec/v2/cmd/gosec@v2.19.0
|
||||
gosec -exclude-generated -exclude=G307 -exclude-dir=crypto/bls/herumi ./...
|
||||
go install github.com/securego/gosec/v2/cmd/gosec@v2.22.1
|
||||
gosec -exclude-generated -exclude=G307,G115 -exclude-dir=crypto/bls/herumi ./...
|
||||
|
||||
lint:
|
||||
name: Lint
|
||||
@@ -45,16 +45,16 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go 1.23
|
||||
- name: Set up Go 1.24
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.23.5'
|
||||
go-version: '1.24.0'
|
||||
id: go
|
||||
|
||||
- name: Golangci-lint
|
||||
uses: golangci/golangci-lint-action@v5
|
||||
with:
|
||||
version: v1.63.4
|
||||
version: v1.64.5
|
||||
args: --config=.golangci.yml --out-${NO_FUTURE}format colored-line-number
|
||||
|
||||
build:
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.23.5'
|
||||
go-version: '1.24.0'
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
|
||||
@@ -75,6 +75,7 @@ linters:
|
||||
- tagliatelle
|
||||
- thelper
|
||||
- unparam
|
||||
- usetesting
|
||||
- varnamelen
|
||||
- wrapcheck
|
||||
- wsl
|
||||
|
||||
@@ -165,7 +165,7 @@ STATICCHECK_ANALYZERS = [
|
||||
"sa6006",
|
||||
"sa9001",
|
||||
"sa9002",
|
||||
#"sa9003", # Doesn't build. See https://github.com/dominikh/go-tools/pull/1483
|
||||
"sa9003",
|
||||
"sa9004",
|
||||
"sa9005",
|
||||
"sa9006",
|
||||
|
||||
137
CHANGELOG.md
137
CHANGELOG.md
@@ -4,6 +4,141 @@ 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.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.
|
||||
|
||||
For mainnet, we have a few nice features for you to try:
|
||||
|
||||
- [PR #14023](https://github.com/prysmaticlabs/prysm/pull/14023) introduces a new file layout structure for storing blobs. Rather than storing all blob root directories in one parent directory, blob root directories are organized in subdirectories by epoch. This should vastly decrease the blob cache warmup time when Prysm is starting. Try this feature with `--blob-storage-layout=by-epoch`.
|
||||
|
||||
Updating to this release is **required** for Holesky and Sepolia operators and it is **recommended** for mainnet users as there are a few bug fixes that apply to deneb logic.
|
||||
|
||||
### Added
|
||||
|
||||
- Added an error field to log `Finished building block`. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14696)
|
||||
- Implemented a new `EmptyExecutionPayloadHeader` function. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14713)
|
||||
- Added proper gas limit check for header from the builder. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14707)
|
||||
- `Finished building block`: Display error only if not nil. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14722)
|
||||
- Added light client feature flag check to RPC handlers. [PR](https://github.com/prysmaticlabs/prysm/pull/14736). [[PR]](https://github.com/prysmaticlabs/prysm/pull/14782)
|
||||
- Added support to update target and max blob count to different values per hard fork config. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14678)
|
||||
- Log before blob filesystem cache warm-up. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14735)
|
||||
- New design for the attestation pool. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14324)
|
||||
- Add field param placeholder for Electra blob target and max to pass spec tests. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14733)
|
||||
- Light client: Add better error handling. [PR](https://github.com/prysmaticlabs/prysm/pull/14749). [[PR]](https://github.com/prysmaticlabs/prysm/pull/14782)
|
||||
- Add EIP-7691: Blob throughput increase. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14750)
|
||||
- Trace IDONTWANT Messages in Pubsub. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14778)
|
||||
- Add Fulu fork boilerplate. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14771)
|
||||
- DB optimization for saving light client bootstraps (save unique sync committees only). [[PR]](https://github.com/prysmaticlabs/prysm/pull/14782)
|
||||
- Separate type for unaggregated network attestations. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14659)
|
||||
- Remote signer electra fork support. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14477)
|
||||
- Add Electra test case to rewards API. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14816)
|
||||
- Update `proto_test.go` to Electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14817)
|
||||
- Update slasher service to Electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14812)
|
||||
- Builder API endpoint to support Electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14344)
|
||||
- Added protoc toolchains with a version of v25.3. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14818)
|
||||
- Add test cases for the eth_lightclient_bootstrap API SSZ support. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14824)
|
||||
- Handle `AttesterSlashingElectra` everywhere in the codebase. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14823)
|
||||
- Add Beacon DB pruning service to prune historical data older than MIN_EPOCHS_FOR_BLOCK_REQUESTS (roughly equivalent to the weak subjectivity period). [[PR]](https://github.com/prysmaticlabs/prysm/pull/14687)
|
||||
- Nil consolidation request check for core processing. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14851)
|
||||
- Updated blob sidecar api endpoint for Electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14852)
|
||||
- Slashing pool service to convert slashings from Phase0 to Electra at the fork. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14844)
|
||||
- check to stop eth1 voting after electra and eth1 deposits stop. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14835)
|
||||
- WARN log message on node startup advising of the upcoming deprecation of the --enable-historical-state-representation feature flag. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14856)
|
||||
- Beacon API event support for `SingleAttestation` and `SignedAggregateAttestationAndProofElectra`. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14855)
|
||||
- Added Electra tests for `TestLightClient_NewLightClientOptimisticUpdateFromBeaconState` and `TestLightClient_NewLightClientFinalityUpdateFromBeaconState`. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14783)
|
||||
- New option to select an alternate blob storage layout. Rather than a flat directory with a subdir for each block root, a multi-level scheme is used to organize blobs by epoch/slot/root, enabling leaner syscalls, indexing and pruning. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14023)
|
||||
- Send pending att queue's attestations through the notification feed. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14862)
|
||||
- Prune all pending deposits and proofs in post-Electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14829)
|
||||
- Add Pectra testnet dates. (Sepolia and Holesky). [[PR]](https://github.com/prysmaticlabs/prysm/pull/14884)
|
||||
|
||||
### Changed
|
||||
|
||||
- Process light client finality updates only for new finalized epochs instead of doing it for every block. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14713)
|
||||
- Refactor subnets subscriptions. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14711)
|
||||
- Refactor RPC handlers subscriptions. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14732)
|
||||
- Go deps upgrade, from `ioutil` to `io`. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14737)
|
||||
- Move successfully registered validator(s) on builder log to debug. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14735)
|
||||
- Update some test files to use `crypto/rand` instead of `math/rand`. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14747)
|
||||
- Re-organize the content of the `*.proto` files (No functional change). [[PR]](https://github.com/prysmaticlabs/prysm/pull/14755)
|
||||
- SSZ files generation: Remove the `// Hash: ...` header.[[PR]](https://github.com/prysmaticlabs/prysm/pull/14760)
|
||||
- Updated Electra spec definition for `process_epoch`. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14768)
|
||||
- Update our `go-libp2p-pubsub` dependency. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14770)
|
||||
- Re-organize the content of files to ease the creation of a new fork boilerplate. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14761)
|
||||
- Updated spec definition electra `process_registry_updates`. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14767)
|
||||
- Fixed Metadata errors for peers connected via QUIC. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14776)
|
||||
- Updated spec definitions for `process_slashings` in godocs. Simplified `ProcessSlashings` API. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14766)
|
||||
- Update spec tests to v1.5.0-beta.0. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14788)
|
||||
- Process light client finality updates only for new finalized epochs instead of doing it for every block. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14718)
|
||||
- Update blobs by rpc topics from V2 to V1. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14785)
|
||||
- Updated geth to 1.14~. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14351)
|
||||
- E2e tests start from bellatrix. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14351)
|
||||
- Version pinning unclog after making some ux improvements. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14802)
|
||||
- Remove helpers to check for execution/compounding withdrawal credentials and expose them as methods. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14808)
|
||||
- Refactor `2006-01-02 15:04:05` to `time.DateTime`. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14792)
|
||||
- Updated Prysm to Go v1.23.5. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14818)
|
||||
- Updated Bazel version to v7.4.1. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14818)
|
||||
- Updated rules_go to v0.46.0. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14818)
|
||||
- Updated golang.org/x/tools to be compatible with v1.23.5. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14818)
|
||||
- CI now requires proto files to be properly formatted with clang-format. [[PR](https://github.com/prysmaticlabs/prysm/pull/14831)]. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14831)
|
||||
- Improved test coverage of beacon-chain/core/electra/churn.go. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14837)
|
||||
- Update electra spec test to beta1. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14841)
|
||||
- Move deposit request nil check to apply all. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14849)
|
||||
- Do not mark blocks as invalid on context deadlines during state transition. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14838)
|
||||
- Update electra core processing to not mark block bad if execution request error. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14826)
|
||||
- Dependency: Updated go-ethereum to v1.14.13. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14872)
|
||||
- improving readability on proposer settings loader. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14868)
|
||||
- Removes existing validator.processSlot span and adds validator.processSlot span to slotCtx. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14874)
|
||||
- DownloadFinalizedData has moved from the api/client package to beacon-chain/sync/checkpoint. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14871)
|
||||
- Updated Blob-Batch-Limit to increase to 192 for electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14883)
|
||||
- Updated Blob-Batch-Limit-Burst-Factor to increase to 3. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14883)
|
||||
- Changed the derived batch limit when serving blobs. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14883)
|
||||
- Updated go-libp2p-pubsub to v0.13.0. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14890)
|
||||
- Rename light client flag from `enable-lightclient` to `enable-light-client`. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14887)
|
||||
- Update electra spec test to beta2. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14901)
|
||||
|
||||
### Removed
|
||||
|
||||
- Cleanup ProcessSlashings method to remove unnecessary argument. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14762)
|
||||
- Remove `/proto/eth/v2` directory. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14765)
|
||||
- Remove `/memsize/` pprof endpoint as it will no longer be supported in go 1.23. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14351)
|
||||
- Clean `TestCanUpgrade*` tests. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14791)
|
||||
- Remove `Copy()` from the `ReadOnlyBeaconBlock` interface. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14811)
|
||||
- Removed a tracing span on signature requests. These requests usually took less than 5 nanoseconds and are generally not worth tracing. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14864)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Added check to prevent nil pointer deference or out of bounds array access when validating the BLSToExecutionChange on an impossibly nil validator. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14705)
|
||||
- EIP-7691: Ensure new blobs subnets are subscribed on epoch in advance. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14759)
|
||||
- Fix kzg commitment inclusion proof depth minimal value. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14787)
|
||||
- Replace exampleIP to `96.7.129.13`. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14795)
|
||||
- Fixed a p2p test to reliably return a static IP through DNS resolution. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14800)
|
||||
- `ToBlinded`: Use Fulu struct for Fulu (instead of Electra). [[PR]](https://github.com/prysmaticlabs/prysm/pull/14797)
|
||||
- fix panic with type cast on pbgenericblock(). [[PR]](https://github.com/prysmaticlabs/prysm/pull/14801)
|
||||
- Prysmctl generate genesis state: fix truncation of ExtraData to 32 bytes to satisfy SSZ marshaling. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14803)
|
||||
- added conditional evaluators to fix scenario e2e tests. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14798)
|
||||
- Use `SingleAttestation` for Fulu in p2p attestation map. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14809)
|
||||
- `UpgradeToFulu`: Respect the specification. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14821)
|
||||
- `nodeFilter`: Implement `filterPeerForBlobSubnet` to avoid error logs. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14822)
|
||||
- Fixed deposit packing for post-Electra: early return if EIP-6110 is applied. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14697)
|
||||
- Fix batch process new pending deposits by getting validators from state. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14827)
|
||||
- Fix handling unfound block at slot. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14852)
|
||||
- Fixed incorrect attester slashing length check. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14833)
|
||||
- Fix monitor service for Electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14853)
|
||||
- add more nil checks on ToConsensus functions for added safety. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14867)
|
||||
- Fix electra state to safe share references on pending fields when append. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14895)
|
||||
- Add missing config values from the spec. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14903)
|
||||
- We remove the unused `rebuildTrie` assignments for fields which do not use them. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14906)
|
||||
- fix block api endpoint to handle blocks with the same structure but on different forks (i.e. fulu and electra). [[PR]](https://github.com/prysmaticlabs/prysm/pull/14897)
|
||||
- We change how we track blob indexes during their reconstruction from the EL to prevent. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14909)
|
||||
- We now use the correct maximum value when serving blobs for electra blocks. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14910)
|
||||
|
||||
### Security
|
||||
|
||||
- go version upgrade to 1.22.10 for CVE CVE-2024-34156. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14729)
|
||||
- Update golang.org/x/crypto to v0.31.0 to address CVE-2024-45337. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14777)
|
||||
- Update golang.org/x/net to v0.33.0 to address CVE-2024-45338. [[PR]](https://github.com/prysmaticlabs/prysm/pull/14780)
|
||||
|
||||
## [v5.2.0](https://github.com/prysmaticlabs/prysm/compare/v5.1.2...v5.2.0)
|
||||
|
||||
Updating to this release is highly recommended, especially for users running v5.1.1 or v5.1.2.
|
||||
@@ -2987,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
|
||||
|
||||
@@ -55,7 +55,7 @@ bazel build //beacon-chain --config=release
|
||||
## Adding / updating dependencies
|
||||
|
||||
1. Add your dependency as you would with go modules. I.e. `go get ...`
|
||||
1. Run `bazel run //:gazelle -- update-repos -from_file=go.mod` to update the bazel managed dependencies.
|
||||
1. Run `bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro=deps.bzl%prysm_deps -prune=true` to update the bazel managed dependencies.
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
10
WORKSPACE
10
WORKSPACE
@@ -160,15 +160,15 @@ oci_register_toolchains(
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_go",
|
||||
integrity = "sha256-JD8o94crTb2DFiJJR8nMAGdBAW95zIENB4cbI+JnrI4=",
|
||||
patch_args = ["-p1"],
|
||||
patches = [
|
||||
# Expose internals of go_test for custom build transitions.
|
||||
"//third_party:io_bazel_rules_go_test.patch",
|
||||
],
|
||||
sha256 = "b2038e2de2cace18f032249cb4bb0048abf583a36369fa98f687af1b3f880b26",
|
||||
strip_prefix = "rules_go-cf3c3af34bd869b864f5f2b98e2f41c2b220d6c9",
|
||||
urls = [
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.48.1/rules_go-v0.48.1.zip",
|
||||
"https://github.com/bazelbuild/rules_go/releases/download/v0.48.1/rules_go-v0.48.1.zip",
|
||||
"https://github.com/bazel-contrib/rules_go/archive/cf3c3af34bd869b864f5f2b98e2f41c2b220d6c9.tar.gz",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -210,7 +210,7 @@ load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_depe
|
||||
go_rules_dependencies()
|
||||
|
||||
go_register_toolchains(
|
||||
go_version = "1.23.5",
|
||||
go_version = "1.24.0",
|
||||
nogo = "@//:nogo",
|
||||
)
|
||||
|
||||
@@ -431,7 +431,7 @@ gometalinter_dependencies()
|
||||
|
||||
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies")
|
||||
|
||||
gazelle_dependencies()
|
||||
gazelle_dependencies(go_sdk = "go_sdk")
|
||||
|
||||
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ go_library(
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_fastssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_opentelemetry_go_contrib_instrumentation_net_http_otelhttp//:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -46,6 +47,7 @@ go_test(
|
||||
data = glob(["testdata/**"]),
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//api:go_default_library",
|
||||
"//api/server/structs:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -108,7 +109,7 @@ func NewClient(host string, opts ...ClientOpt) (*Client, error) {
|
||||
return nil, err
|
||||
}
|
||||
c := &Client{
|
||||
hc: &http.Client{},
|
||||
hc: &http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)},
|
||||
baseURL: u,
|
||||
}
|
||||
for _, o := range opts {
|
||||
@@ -154,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)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/v5/api"
|
||||
"github.com/prysmaticlabs/prysm/v5/api/server/structs"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
||||
@@ -89,6 +90,8 @@ func TestClient_RegisterValidator(t *testing.T) {
|
||||
expectedPath := "/eth/v1/builder/validators"
|
||||
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())
|
||||
@@ -364,8 +367,8 @@ func TestSubmitBlindedBlock(t *testing.T) {
|
||||
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
|
||||
require.Equal(t, postBlindedBeaconBlockPath, r.URL.Path)
|
||||
require.Equal(t, "bellatrix", r.Header.Get("Eth-Consensus-Version"))
|
||||
require.Equal(t, "application/json", r.Header.Get("Content-Type"))
|
||||
require.Equal(t, "application/json", r.Header.Get("Accept"))
|
||||
require.Equal(t, api.JsonMediaType, r.Header.Get("Content-Type"))
|
||||
require.Equal(t, api.JsonMediaType, r.Header.Get("Accept"))
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewBufferString(testExampleExecutionPayload)),
|
||||
@@ -392,8 +395,8 @@ func TestSubmitBlindedBlock(t *testing.T) {
|
||||
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
|
||||
require.Equal(t, postBlindedBeaconBlockPath, r.URL.Path)
|
||||
require.Equal(t, "capella", r.Header.Get("Eth-Consensus-Version"))
|
||||
require.Equal(t, "application/json", r.Header.Get("Content-Type"))
|
||||
require.Equal(t, "application/json", r.Header.Get("Accept"))
|
||||
require.Equal(t, api.JsonMediaType, r.Header.Get("Content-Type"))
|
||||
require.Equal(t, api.JsonMediaType, r.Header.Get("Accept"))
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewBufferString(testExampleExecutionPayloadCapella)),
|
||||
@@ -423,8 +426,8 @@ func TestSubmitBlindedBlock(t *testing.T) {
|
||||
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
|
||||
require.Equal(t, postBlindedBeaconBlockPath, r.URL.Path)
|
||||
require.Equal(t, "deneb", r.Header.Get("Eth-Consensus-Version"))
|
||||
require.Equal(t, "application/json", r.Header.Get("Content-Type"))
|
||||
require.Equal(t, "application/json", r.Header.Get("Accept"))
|
||||
require.Equal(t, api.JsonMediaType, r.Header.Get("Content-Type"))
|
||||
require.Equal(t, api.JsonMediaType, r.Header.Get("Accept"))
|
||||
var req structs.SignedBlindedBeaconBlockDeneb
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -4,9 +4,11 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"block.go",
|
||||
"block_execution.go",
|
||||
"conversions.go",
|
||||
"conversions_blob.go",
|
||||
"conversions_block.go",
|
||||
"conversions_block_execution.go",
|
||||
"conversions_lightclient.go",
|
||||
"conversions_state.go",
|
||||
"endpoints_beacon.go",
|
||||
@@ -47,10 +49,16 @@ go_library(
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["conversions_test.go"],
|
||||
srcs = [
|
||||
"conversions_block_execution_test.go",
|
||||
"conversions_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -186,40 +186,6 @@ type BlindedBeaconBlockBodyBellatrix struct {
|
||||
ExecutionPayloadHeader *ExecutionPayloadHeader `json:"execution_payload_header"`
|
||||
}
|
||||
|
||||
type ExecutionPayload struct {
|
||||
ParentHash string `json:"parent_hash"`
|
||||
FeeRecipient string `json:"fee_recipient"`
|
||||
StateRoot string `json:"state_root"`
|
||||
ReceiptsRoot string `json:"receipts_root"`
|
||||
LogsBloom string `json:"logs_bloom"`
|
||||
PrevRandao string `json:"prev_randao"`
|
||||
BlockNumber string `json:"block_number"`
|
||||
GasLimit string `json:"gas_limit"`
|
||||
GasUsed string `json:"gas_used"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ExtraData string `json:"extra_data"`
|
||||
BaseFeePerGas string `json:"base_fee_per_gas"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
Transactions []string `json:"transactions"`
|
||||
}
|
||||
|
||||
type ExecutionPayloadHeader struct {
|
||||
ParentHash string `json:"parent_hash"`
|
||||
FeeRecipient string `json:"fee_recipient"`
|
||||
StateRoot string `json:"state_root"`
|
||||
ReceiptsRoot string `json:"receipts_root"`
|
||||
LogsBloom string `json:"logs_bloom"`
|
||||
PrevRandao string `json:"prev_randao"`
|
||||
BlockNumber string `json:"block_number"`
|
||||
GasLimit string `json:"gas_limit"`
|
||||
GasUsed string `json:"gas_used"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ExtraData string `json:"extra_data"`
|
||||
BaseFeePerGas string `json:"base_fee_per_gas"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
TransactionsRoot string `json:"transactions_root"`
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Capella
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -298,42 +264,6 @@ type BlindedBeaconBlockBodyCapella struct {
|
||||
BLSToExecutionChanges []*SignedBLSToExecutionChange `json:"bls_to_execution_changes"`
|
||||
}
|
||||
|
||||
type ExecutionPayloadCapella struct {
|
||||
ParentHash string `json:"parent_hash"`
|
||||
FeeRecipient string `json:"fee_recipient"`
|
||||
StateRoot string `json:"state_root"`
|
||||
ReceiptsRoot string `json:"receipts_root"`
|
||||
LogsBloom string `json:"logs_bloom"`
|
||||
PrevRandao string `json:"prev_randao"`
|
||||
BlockNumber string `json:"block_number"`
|
||||
GasLimit string `json:"gas_limit"`
|
||||
GasUsed string `json:"gas_used"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ExtraData string `json:"extra_data"`
|
||||
BaseFeePerGas string `json:"base_fee_per_gas"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
Transactions []string `json:"transactions"`
|
||||
Withdrawals []*Withdrawal `json:"withdrawals"`
|
||||
}
|
||||
|
||||
type ExecutionPayloadHeaderCapella struct {
|
||||
ParentHash string `json:"parent_hash"`
|
||||
FeeRecipient string `json:"fee_recipient"`
|
||||
StateRoot string `json:"state_root"`
|
||||
ReceiptsRoot string `json:"receipts_root"`
|
||||
LogsBloom string `json:"logs_bloom"`
|
||||
PrevRandao string `json:"prev_randao"`
|
||||
BlockNumber string `json:"block_number"`
|
||||
GasLimit string `json:"gas_limit"`
|
||||
GasUsed string `json:"gas_used"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ExtraData string `json:"extra_data"`
|
||||
BaseFeePerGas string `json:"base_fee_per_gas"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
TransactionsRoot string `json:"transactions_root"`
|
||||
WithdrawalsRoot string `json:"withdrawals_root"`
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Deneb
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -426,46 +356,6 @@ type BlindedBeaconBlockBodyDeneb struct {
|
||||
BlobKzgCommitments []string `json:"blob_kzg_commitments"`
|
||||
}
|
||||
|
||||
type ExecutionPayloadDeneb struct {
|
||||
ParentHash string `json:"parent_hash"`
|
||||
FeeRecipient string `json:"fee_recipient"`
|
||||
StateRoot string `json:"state_root"`
|
||||
ReceiptsRoot string `json:"receipts_root"`
|
||||
LogsBloom string `json:"logs_bloom"`
|
||||
PrevRandao string `json:"prev_randao"`
|
||||
BlockNumber string `json:"block_number"`
|
||||
GasLimit string `json:"gas_limit"`
|
||||
GasUsed string `json:"gas_used"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ExtraData string `json:"extra_data"`
|
||||
BaseFeePerGas string `json:"base_fee_per_gas"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
Transactions []string `json:"transactions"`
|
||||
Withdrawals []*Withdrawal `json:"withdrawals"`
|
||||
BlobGasUsed string `json:"blob_gas_used"`
|
||||
ExcessBlobGas string `json:"excess_blob_gas"`
|
||||
}
|
||||
|
||||
type ExecutionPayloadHeaderDeneb struct {
|
||||
ParentHash string `json:"parent_hash"`
|
||||
FeeRecipient string `json:"fee_recipient"`
|
||||
StateRoot string `json:"state_root"`
|
||||
ReceiptsRoot string `json:"receipts_root"`
|
||||
LogsBloom string `json:"logs_bloom"`
|
||||
PrevRandao string `json:"prev_randao"`
|
||||
BlockNumber string `json:"block_number"`
|
||||
GasLimit string `json:"gas_limit"`
|
||||
GasUsed string `json:"gas_used"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ExtraData string `json:"extra_data"`
|
||||
BaseFeePerGas string `json:"base_fee_per_gas"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
TransactionsRoot string `json:"transactions_root"`
|
||||
WithdrawalsRoot string `json:"withdrawals_root"`
|
||||
BlobGasUsed string `json:"blob_gas_used"`
|
||||
ExcessBlobGas string `json:"excess_blob_gas"`
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Electra
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -560,14 +450,6 @@ type BlindedBeaconBlockBodyElectra struct {
|
||||
ExecutionRequests *ExecutionRequests `json:"execution_requests"`
|
||||
}
|
||||
|
||||
type (
|
||||
ExecutionRequests struct {
|
||||
Deposits []*DepositRequest `json:"deposits"`
|
||||
Withdrawals []*WithdrawalRequest `json:"withdrawals"`
|
||||
Consolidations []*ConsolidationRequest `json:"consolidations"`
|
||||
}
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Fulu
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -579,14 +461,14 @@ type SignedBeaconBlockContentsFulu struct {
|
||||
}
|
||||
|
||||
type BeaconBlockContentsFulu struct {
|
||||
Block *BeaconBlockFulu `json:"block"`
|
||||
KzgProofs []string `json:"kzg_proofs"`
|
||||
Blobs []string `json:"blobs"`
|
||||
Block *BeaconBlockElectra `json:"block"`
|
||||
KzgProofs []string `json:"kzg_proofs"`
|
||||
Blobs []string `json:"blobs"`
|
||||
}
|
||||
|
||||
type SignedBeaconBlockFulu struct {
|
||||
Message *BeaconBlockFulu `json:"message"`
|
||||
Signature string `json:"signature"`
|
||||
Message *BeaconBlockElectra `json:"message"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
var _ SignedMessageJsoner = &SignedBeaconBlockFulu{}
|
||||
@@ -599,36 +481,12 @@ func (s *SignedBeaconBlockFulu) SigString() string {
|
||||
return s.Signature
|
||||
}
|
||||
|
||||
type BeaconBlockFulu struct {
|
||||
Slot string `json:"slot"`
|
||||
ProposerIndex string `json:"proposer_index"`
|
||||
ParentRoot string `json:"parent_root"`
|
||||
StateRoot string `json:"state_root"`
|
||||
Body *BeaconBlockBodyFulu `json:"body"`
|
||||
}
|
||||
|
||||
type BeaconBlockBodyFulu struct {
|
||||
RandaoReveal string `json:"randao_reveal"`
|
||||
Eth1Data *Eth1Data `json:"eth1_data"`
|
||||
Graffiti string `json:"graffiti"`
|
||||
ProposerSlashings []*ProposerSlashing `json:"proposer_slashings"`
|
||||
AttesterSlashings []*AttesterSlashingElectra `json:"attester_slashings"`
|
||||
Attestations []*AttestationElectra `json:"attestations"`
|
||||
Deposits []*Deposit `json:"deposits"`
|
||||
VoluntaryExits []*SignedVoluntaryExit `json:"voluntary_exits"`
|
||||
SyncAggregate *SyncAggregate `json:"sync_aggregate"`
|
||||
ExecutionPayload *ExecutionPayloadDeneb `json:"execution_payload"`
|
||||
BLSToExecutionChanges []*SignedBLSToExecutionChange `json:"bls_to_execution_changes"`
|
||||
BlobKzgCommitments []string `json:"blob_kzg_commitments"`
|
||||
ExecutionRequests *ExecutionRequests `json:"execution_requests"`
|
||||
}
|
||||
|
||||
type BlindedBeaconBlockFulu struct {
|
||||
Slot string `json:"slot"`
|
||||
ProposerIndex string `json:"proposer_index"`
|
||||
ParentRoot string `json:"parent_root"`
|
||||
StateRoot string `json:"state_root"`
|
||||
Body *BlindedBeaconBlockBodyFulu `json:"body"`
|
||||
Slot string `json:"slot"`
|
||||
ProposerIndex string `json:"proposer_index"`
|
||||
ParentRoot string `json:"parent_root"`
|
||||
StateRoot string `json:"state_root"`
|
||||
Body *BlindedBeaconBlockBodyElectra `json:"body"`
|
||||
}
|
||||
|
||||
type SignedBlindedBeaconBlockFulu struct {
|
||||
@@ -645,19 +503,3 @@ func (s *SignedBlindedBeaconBlockFulu) MessageRawJson() ([]byte, error) {
|
||||
func (s *SignedBlindedBeaconBlockFulu) SigString() string {
|
||||
return s.Signature
|
||||
}
|
||||
|
||||
type BlindedBeaconBlockBodyFulu struct {
|
||||
RandaoReveal string `json:"randao_reveal"`
|
||||
Eth1Data *Eth1Data `json:"eth1_data"`
|
||||
Graffiti string `json:"graffiti"`
|
||||
ProposerSlashings []*ProposerSlashing `json:"proposer_slashings"`
|
||||
AttesterSlashings []*AttesterSlashingElectra `json:"attester_slashings"`
|
||||
Attestations []*AttestationElectra `json:"attestations"`
|
||||
Deposits []*Deposit `json:"deposits"`
|
||||
VoluntaryExits []*SignedVoluntaryExit `json:"voluntary_exits"`
|
||||
SyncAggregate *SyncAggregate `json:"sync_aggregate"`
|
||||
ExecutionPayloadHeader *ExecutionPayloadHeaderDeneb `json:"execution_payload_header"`
|
||||
BLSToExecutionChanges []*SignedBLSToExecutionChange `json:"bls_to_execution_changes"`
|
||||
BlobKzgCommitments []string `json:"blob_kzg_commitments"`
|
||||
ExecutionRequests *ExecutionRequests `json:"execution_requests"`
|
||||
}
|
||||
|
||||
157
api/server/structs/block_execution.go
Normal file
157
api/server/structs/block_execution.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package structs
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Bellatrix
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type ExecutionPayload struct {
|
||||
ParentHash string `json:"parent_hash"`
|
||||
FeeRecipient string `json:"fee_recipient"`
|
||||
StateRoot string `json:"state_root"`
|
||||
ReceiptsRoot string `json:"receipts_root"`
|
||||
LogsBloom string `json:"logs_bloom"`
|
||||
PrevRandao string `json:"prev_randao"`
|
||||
BlockNumber string `json:"block_number"`
|
||||
GasLimit string `json:"gas_limit"`
|
||||
GasUsed string `json:"gas_used"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ExtraData string `json:"extra_data"`
|
||||
BaseFeePerGas string `json:"base_fee_per_gas"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
Transactions []string `json:"transactions"`
|
||||
}
|
||||
|
||||
type ExecutionPayloadHeader struct {
|
||||
ParentHash string `json:"parent_hash"`
|
||||
FeeRecipient string `json:"fee_recipient"`
|
||||
StateRoot string `json:"state_root"`
|
||||
ReceiptsRoot string `json:"receipts_root"`
|
||||
LogsBloom string `json:"logs_bloom"`
|
||||
PrevRandao string `json:"prev_randao"`
|
||||
BlockNumber string `json:"block_number"`
|
||||
GasLimit string `json:"gas_limit"`
|
||||
GasUsed string `json:"gas_used"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ExtraData string `json:"extra_data"`
|
||||
BaseFeePerGas string `json:"base_fee_per_gas"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
TransactionsRoot string `json:"transactions_root"`
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Capella
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type ExecutionPayloadCapella struct {
|
||||
ParentHash string `json:"parent_hash"`
|
||||
FeeRecipient string `json:"fee_recipient"`
|
||||
StateRoot string `json:"state_root"`
|
||||
ReceiptsRoot string `json:"receipts_root"`
|
||||
LogsBloom string `json:"logs_bloom"`
|
||||
PrevRandao string `json:"prev_randao"`
|
||||
BlockNumber string `json:"block_number"`
|
||||
GasLimit string `json:"gas_limit"`
|
||||
GasUsed string `json:"gas_used"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ExtraData string `json:"extra_data"`
|
||||
BaseFeePerGas string `json:"base_fee_per_gas"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
Transactions []string `json:"transactions"`
|
||||
Withdrawals []*Withdrawal `json:"withdrawals"`
|
||||
}
|
||||
|
||||
type ExecutionPayloadHeaderCapella struct {
|
||||
ParentHash string `json:"parent_hash"`
|
||||
FeeRecipient string `json:"fee_recipient"`
|
||||
StateRoot string `json:"state_root"`
|
||||
ReceiptsRoot string `json:"receipts_root"`
|
||||
LogsBloom string `json:"logs_bloom"`
|
||||
PrevRandao string `json:"prev_randao"`
|
||||
BlockNumber string `json:"block_number"`
|
||||
GasLimit string `json:"gas_limit"`
|
||||
GasUsed string `json:"gas_used"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ExtraData string `json:"extra_data"`
|
||||
BaseFeePerGas string `json:"base_fee_per_gas"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
TransactionsRoot string `json:"transactions_root"`
|
||||
WithdrawalsRoot string `json:"withdrawals_root"`
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Deneb
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type ExecutionPayloadDeneb struct {
|
||||
ParentHash string `json:"parent_hash"`
|
||||
FeeRecipient string `json:"fee_recipient"`
|
||||
StateRoot string `json:"state_root"`
|
||||
ReceiptsRoot string `json:"receipts_root"`
|
||||
LogsBloom string `json:"logs_bloom"`
|
||||
PrevRandao string `json:"prev_randao"`
|
||||
BlockNumber string `json:"block_number"`
|
||||
GasLimit string `json:"gas_limit"`
|
||||
GasUsed string `json:"gas_used"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ExtraData string `json:"extra_data"`
|
||||
BaseFeePerGas string `json:"base_fee_per_gas"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
Transactions []string `json:"transactions"`
|
||||
Withdrawals []*Withdrawal `json:"withdrawals"`
|
||||
BlobGasUsed string `json:"blob_gas_used"`
|
||||
ExcessBlobGas string `json:"excess_blob_gas"`
|
||||
}
|
||||
|
||||
type ExecutionPayloadHeaderDeneb struct {
|
||||
ParentHash string `json:"parent_hash"`
|
||||
FeeRecipient string `json:"fee_recipient"`
|
||||
StateRoot string `json:"state_root"`
|
||||
ReceiptsRoot string `json:"receipts_root"`
|
||||
LogsBloom string `json:"logs_bloom"`
|
||||
PrevRandao string `json:"prev_randao"`
|
||||
BlockNumber string `json:"block_number"`
|
||||
GasLimit string `json:"gas_limit"`
|
||||
GasUsed string `json:"gas_used"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ExtraData string `json:"extra_data"`
|
||||
BaseFeePerGas string `json:"base_fee_per_gas"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
TransactionsRoot string `json:"transactions_root"`
|
||||
WithdrawalsRoot string `json:"withdrawals_root"`
|
||||
BlobGasUsed string `json:"blob_gas_used"`
|
||||
ExcessBlobGas string `json:"excess_blob_gas"`
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Electra
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type ExecutionRequests struct {
|
||||
Deposits []*DepositRequest `json:"deposits"`
|
||||
Withdrawals []*WithdrawalRequest `json:"withdrawals"`
|
||||
Consolidations []*ConsolidationRequest `json:"consolidations"`
|
||||
}
|
||||
|
||||
type DepositRequest struct {
|
||||
Pubkey string `json:"pubkey"`
|
||||
WithdrawalCredentials string `json:"withdrawal_credentials"`
|
||||
Amount string `json:"amount"`
|
||||
Signature string `json:"signature"`
|
||||
Index string `json:"index"`
|
||||
}
|
||||
|
||||
type WithdrawalRequest struct {
|
||||
SourceAddress string `json:"source_address"`
|
||||
ValidatorPubkey string `json:"validator_pubkey"`
|
||||
Amount string `json:"amount"`
|
||||
}
|
||||
|
||||
type ConsolidationRequest struct {
|
||||
SourceAddress string `json:"source_address"`
|
||||
SourcePubkey string `json:"source_pubkey"`
|
||||
TargetPubkey string `json:"target_pubkey"`
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Fulu
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -889,126 +889,6 @@ func WithdrawalFromConsensus(w *enginev1.Withdrawal) *Withdrawal {
|
||||
}
|
||||
}
|
||||
|
||||
func WithdrawalRequestsFromConsensus(ws []*enginev1.WithdrawalRequest) []*WithdrawalRequest {
|
||||
result := make([]*WithdrawalRequest, len(ws))
|
||||
for i, w := range ws {
|
||||
result[i] = WithdrawalRequestFromConsensus(w)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func WithdrawalRequestFromConsensus(w *enginev1.WithdrawalRequest) *WithdrawalRequest {
|
||||
return &WithdrawalRequest{
|
||||
SourceAddress: hexutil.Encode(w.SourceAddress),
|
||||
ValidatorPubkey: hexutil.Encode(w.ValidatorPubkey),
|
||||
Amount: fmt.Sprintf("%d", w.Amount),
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WithdrawalRequest) ToConsensus() (*enginev1.WithdrawalRequest, error) {
|
||||
src, err := bytesutil.DecodeHexWithLength(w.SourceAddress, common.AddressLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "SourceAddress")
|
||||
}
|
||||
pubkey, err := bytesutil.DecodeHexWithLength(w.ValidatorPubkey, fieldparams.BLSPubkeyLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ValidatorPubkey")
|
||||
}
|
||||
amount, err := strconv.ParseUint(w.Amount, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "Amount")
|
||||
}
|
||||
return &enginev1.WithdrawalRequest{
|
||||
SourceAddress: src,
|
||||
ValidatorPubkey: pubkey,
|
||||
Amount: amount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ConsolidationRequestsFromConsensus(cs []*enginev1.ConsolidationRequest) []*ConsolidationRequest {
|
||||
result := make([]*ConsolidationRequest, len(cs))
|
||||
for i, c := range cs {
|
||||
result[i] = ConsolidationRequestFromConsensus(c)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func ConsolidationRequestFromConsensus(c *enginev1.ConsolidationRequest) *ConsolidationRequest {
|
||||
return &ConsolidationRequest{
|
||||
SourceAddress: hexutil.Encode(c.SourceAddress),
|
||||
SourcePubkey: hexutil.Encode(c.SourcePubkey),
|
||||
TargetPubkey: hexutil.Encode(c.TargetPubkey),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConsolidationRequest) ToConsensus() (*enginev1.ConsolidationRequest, error) {
|
||||
srcAddress, err := bytesutil.DecodeHexWithLength(c.SourceAddress, common.AddressLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "SourceAddress")
|
||||
}
|
||||
srcPubkey, err := bytesutil.DecodeHexWithLength(c.SourcePubkey, fieldparams.BLSPubkeyLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "SourcePubkey")
|
||||
}
|
||||
targetPubkey, err := bytesutil.DecodeHexWithLength(c.TargetPubkey, fieldparams.BLSPubkeyLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "TargetPubkey")
|
||||
}
|
||||
return &enginev1.ConsolidationRequest{
|
||||
SourceAddress: srcAddress,
|
||||
SourcePubkey: srcPubkey,
|
||||
TargetPubkey: targetPubkey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DepositRequestsFromConsensus(ds []*enginev1.DepositRequest) []*DepositRequest {
|
||||
result := make([]*DepositRequest, len(ds))
|
||||
for i, d := range ds {
|
||||
result[i] = DepositRequestFromConsensus(d)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func DepositRequestFromConsensus(d *enginev1.DepositRequest) *DepositRequest {
|
||||
return &DepositRequest{
|
||||
Pubkey: hexutil.Encode(d.Pubkey),
|
||||
WithdrawalCredentials: hexutil.Encode(d.WithdrawalCredentials),
|
||||
Amount: fmt.Sprintf("%d", d.Amount),
|
||||
Signature: hexutil.Encode(d.Signature),
|
||||
Index: fmt.Sprintf("%d", d.Index),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DepositRequest) ToConsensus() (*enginev1.DepositRequest, error) {
|
||||
pubkey, err := bytesutil.DecodeHexWithLength(d.Pubkey, fieldparams.BLSPubkeyLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "Pubkey")
|
||||
}
|
||||
withdrawalCredentials, err := bytesutil.DecodeHexWithLength(d.WithdrawalCredentials, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "WithdrawalCredentials")
|
||||
}
|
||||
amount, err := strconv.ParseUint(d.Amount, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "Amount")
|
||||
}
|
||||
sig, err := bytesutil.DecodeHexWithLength(d.Signature, fieldparams.BLSSignatureLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "Signature")
|
||||
}
|
||||
index, err := strconv.ParseUint(d.Index, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "Index")
|
||||
}
|
||||
return &enginev1.DepositRequest{
|
||||
Pubkey: pubkey,
|
||||
WithdrawalCredentials: withdrawalCredentials,
|
||||
Amount: amount,
|
||||
Signature: sig,
|
||||
Index: index,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ProposerSlashingsToConsensus(src []*ProposerSlashing) ([]*eth.ProposerSlashing, error) {
|
||||
if src == nil {
|
||||
return nil, server.NewDecodeError(errNilValue, "ProposerSlashings")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
973
api/server/structs/conversions_block_execution.go
Normal file
973
api/server/structs/conversions_block_execution.go
Normal file
@@ -0,0 +1,973 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/prysmaticlabs/prysm/v5/api/server"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/container/slice"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Bellatrix
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
func ExecutionPayloadFromConsensus(payload *enginev1.ExecutionPayload) (*ExecutionPayload, error) {
|
||||
baseFeePerGas, err := sszBytesToUint256String(payload.BaseFeePerGas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactions := make([]string, len(payload.Transactions))
|
||||
for i, tx := range payload.Transactions {
|
||||
transactions[i] = hexutil.Encode(tx)
|
||||
}
|
||||
|
||||
return &ExecutionPayload{
|
||||
ParentHash: hexutil.Encode(payload.ParentHash),
|
||||
FeeRecipient: hexutil.Encode(payload.FeeRecipient),
|
||||
StateRoot: hexutil.Encode(payload.StateRoot),
|
||||
ReceiptsRoot: hexutil.Encode(payload.ReceiptsRoot),
|
||||
LogsBloom: hexutil.Encode(payload.LogsBloom),
|
||||
PrevRandao: hexutil.Encode(payload.PrevRandao),
|
||||
BlockNumber: fmt.Sprintf("%d", payload.BlockNumber),
|
||||
GasLimit: fmt.Sprintf("%d", payload.GasLimit),
|
||||
GasUsed: fmt.Sprintf("%d", payload.GasUsed),
|
||||
Timestamp: fmt.Sprintf("%d", payload.Timestamp),
|
||||
ExtraData: hexutil.Encode(payload.ExtraData),
|
||||
BaseFeePerGas: baseFeePerGas,
|
||||
BlockHash: hexutil.Encode(payload.BlockHash),
|
||||
Transactions: transactions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *ExecutionPayload) ToConsensus() (*enginev1.ExecutionPayload, error) {
|
||||
if e == nil {
|
||||
return nil, server.NewDecodeError(errNilValue, "ExecutionPayload")
|
||||
}
|
||||
payloadParentHash, err := bytesutil.DecodeHexWithLength(e.ParentHash, common.HashLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.ParentHash")
|
||||
}
|
||||
payloadFeeRecipient, err := bytesutil.DecodeHexWithLength(e.FeeRecipient, fieldparams.FeeRecipientLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.FeeRecipient")
|
||||
}
|
||||
payloadStateRoot, err := bytesutil.DecodeHexWithLength(e.StateRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.StateRoot")
|
||||
}
|
||||
payloadReceiptsRoot, err := bytesutil.DecodeHexWithLength(e.ReceiptsRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.ReceiptsRoot")
|
||||
}
|
||||
payloadLogsBloom, err := bytesutil.DecodeHexWithLength(e.LogsBloom, fieldparams.LogsBloomLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.LogsBloom")
|
||||
}
|
||||
payloadPrevRandao, err := bytesutil.DecodeHexWithLength(e.PrevRandao, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.PrevRandao")
|
||||
}
|
||||
payloadBlockNumber, err := strconv.ParseUint(e.BlockNumber, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.BlockNumber")
|
||||
}
|
||||
payloadGasLimit, err := strconv.ParseUint(e.GasLimit, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.GasLimit")
|
||||
}
|
||||
payloadGasUsed, err := strconv.ParseUint(e.GasUsed, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.GasUsed")
|
||||
}
|
||||
payloadTimestamp, err := strconv.ParseUint(e.Timestamp, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.Timestamp")
|
||||
}
|
||||
payloadExtraData, err := bytesutil.DecodeHexWithMaxLength(e.ExtraData, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.ExtraData")
|
||||
}
|
||||
payloadBaseFeePerGas, err := bytesutil.Uint256ToSSZBytes(e.BaseFeePerGas)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.BaseFeePerGas")
|
||||
}
|
||||
payloadBlockHash, err := bytesutil.DecodeHexWithLength(e.BlockHash, common.HashLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.BlockHash")
|
||||
}
|
||||
err = slice.VerifyMaxLength(e.Transactions, fieldparams.MaxTxsPerPayloadLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.Transactions")
|
||||
}
|
||||
payloadTxs := make([][]byte, len(e.Transactions))
|
||||
for i, tx := range e.Transactions {
|
||||
payloadTxs[i], err = bytesutil.DecodeHexWithMaxLength(tx, fieldparams.MaxBytesPerTxLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, fmt.Sprintf("ExecutionPayload.Transactions[%d]", i))
|
||||
}
|
||||
}
|
||||
return &enginev1.ExecutionPayload{
|
||||
ParentHash: payloadParentHash,
|
||||
FeeRecipient: payloadFeeRecipient,
|
||||
StateRoot: payloadStateRoot,
|
||||
ReceiptsRoot: payloadReceiptsRoot,
|
||||
LogsBloom: payloadLogsBloom,
|
||||
PrevRandao: payloadPrevRandao,
|
||||
BlockNumber: payloadBlockNumber,
|
||||
GasLimit: payloadGasLimit,
|
||||
GasUsed: payloadGasUsed,
|
||||
Timestamp: payloadTimestamp,
|
||||
ExtraData: payloadExtraData,
|
||||
BaseFeePerGas: payloadBaseFeePerGas,
|
||||
BlockHash: payloadBlockHash,
|
||||
Transactions: payloadTxs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ExecutionPayloadHeaderFromConsensus(payload *enginev1.ExecutionPayloadHeader) (*ExecutionPayloadHeader, error) {
|
||||
baseFeePerGas, err := sszBytesToUint256String(payload.BaseFeePerGas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ExecutionPayloadHeader{
|
||||
ParentHash: hexutil.Encode(payload.ParentHash),
|
||||
FeeRecipient: hexutil.Encode(payload.FeeRecipient),
|
||||
StateRoot: hexutil.Encode(payload.StateRoot),
|
||||
ReceiptsRoot: hexutil.Encode(payload.ReceiptsRoot),
|
||||
LogsBloom: hexutil.Encode(payload.LogsBloom),
|
||||
PrevRandao: hexutil.Encode(payload.PrevRandao),
|
||||
BlockNumber: fmt.Sprintf("%d", payload.BlockNumber),
|
||||
GasLimit: fmt.Sprintf("%d", payload.GasLimit),
|
||||
GasUsed: fmt.Sprintf("%d", payload.GasUsed),
|
||||
Timestamp: fmt.Sprintf("%d", payload.Timestamp),
|
||||
ExtraData: hexutil.Encode(payload.ExtraData),
|
||||
BaseFeePerGas: baseFeePerGas,
|
||||
BlockHash: hexutil.Encode(payload.BlockHash),
|
||||
TransactionsRoot: hexutil.Encode(payload.TransactionsRoot),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *ExecutionPayloadHeader) ToConsensus() (*enginev1.ExecutionPayloadHeader, error) {
|
||||
if e == nil {
|
||||
return nil, server.NewDecodeError(errNilValue, "ExecutionPayloadHeader")
|
||||
}
|
||||
payloadParentHash, err := bytesutil.DecodeHexWithLength(e.ParentHash, common.HashLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.ParentHash")
|
||||
}
|
||||
payloadFeeRecipient, err := bytesutil.DecodeHexWithLength(e.FeeRecipient, fieldparams.FeeRecipientLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.FeeRecipient")
|
||||
}
|
||||
payloadStateRoot, err := bytesutil.DecodeHexWithLength(e.StateRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.StateRoot")
|
||||
}
|
||||
payloadReceiptsRoot, err := bytesutil.DecodeHexWithLength(e.ReceiptsRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.ReceiptsRoot")
|
||||
}
|
||||
payloadLogsBloom, err := bytesutil.DecodeHexWithLength(e.LogsBloom, fieldparams.LogsBloomLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.LogsBloom")
|
||||
}
|
||||
payloadPrevRandao, err := bytesutil.DecodeHexWithLength(e.PrevRandao, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.PrevRandao")
|
||||
}
|
||||
payloadBlockNumber, err := strconv.ParseUint(e.BlockNumber, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.BlockNumber")
|
||||
}
|
||||
payloadGasLimit, err := strconv.ParseUint(e.GasLimit, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.GasLimit")
|
||||
}
|
||||
payloadGasUsed, err := strconv.ParseUint(e.GasUsed, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.GasUsed")
|
||||
}
|
||||
payloadTimestamp, err := strconv.ParseUint(e.Timestamp, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.Timestamp")
|
||||
}
|
||||
payloadExtraData, err := bytesutil.DecodeHexWithMaxLength(e.ExtraData, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.ExtraData")
|
||||
}
|
||||
payloadBaseFeePerGas, err := bytesutil.Uint256ToSSZBytes(e.BaseFeePerGas)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.BaseFeePerGas")
|
||||
}
|
||||
payloadBlockHash, err := bytesutil.DecodeHexWithLength(e.BlockHash, common.HashLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.BlockHash")
|
||||
}
|
||||
payloadTxsRoot, err := bytesutil.DecodeHexWithLength(e.TransactionsRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.TransactionsRoot")
|
||||
}
|
||||
|
||||
return &enginev1.ExecutionPayloadHeader{
|
||||
ParentHash: payloadParentHash,
|
||||
FeeRecipient: payloadFeeRecipient,
|
||||
StateRoot: payloadStateRoot,
|
||||
ReceiptsRoot: payloadReceiptsRoot,
|
||||
LogsBloom: payloadLogsBloom,
|
||||
PrevRandao: payloadPrevRandao,
|
||||
BlockNumber: payloadBlockNumber,
|
||||
GasLimit: payloadGasLimit,
|
||||
GasUsed: payloadGasUsed,
|
||||
Timestamp: payloadTimestamp,
|
||||
ExtraData: payloadExtraData,
|
||||
BaseFeePerGas: payloadBaseFeePerGas,
|
||||
BlockHash: payloadBlockHash,
|
||||
TransactionsRoot: payloadTxsRoot,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Capella
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
func ExecutionPayloadCapellaFromConsensus(payload *enginev1.ExecutionPayloadCapella) (*ExecutionPayloadCapella, error) {
|
||||
baseFeePerGas, err := sszBytesToUint256String(payload.BaseFeePerGas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactions := make([]string, len(payload.Transactions))
|
||||
for i, tx := range payload.Transactions {
|
||||
transactions[i] = hexutil.Encode(tx)
|
||||
}
|
||||
|
||||
return &ExecutionPayloadCapella{
|
||||
ParentHash: hexutil.Encode(payload.ParentHash),
|
||||
FeeRecipient: hexutil.Encode(payload.FeeRecipient),
|
||||
StateRoot: hexutil.Encode(payload.StateRoot),
|
||||
ReceiptsRoot: hexutil.Encode(payload.ReceiptsRoot),
|
||||
LogsBloom: hexutil.Encode(payload.LogsBloom),
|
||||
PrevRandao: hexutil.Encode(payload.PrevRandao),
|
||||
BlockNumber: fmt.Sprintf("%d", payload.BlockNumber),
|
||||
GasLimit: fmt.Sprintf("%d", payload.GasLimit),
|
||||
GasUsed: fmt.Sprintf("%d", payload.GasUsed),
|
||||
Timestamp: fmt.Sprintf("%d", payload.Timestamp),
|
||||
ExtraData: hexutil.Encode(payload.ExtraData),
|
||||
BaseFeePerGas: baseFeePerGas,
|
||||
BlockHash: hexutil.Encode(payload.BlockHash),
|
||||
Transactions: transactions,
|
||||
Withdrawals: WithdrawalsFromConsensus(payload.Withdrawals),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *ExecutionPayloadCapella) ToConsensus() (*enginev1.ExecutionPayloadCapella, error) {
|
||||
if e == nil {
|
||||
return nil, server.NewDecodeError(errNilValue, "ExecutionPayload")
|
||||
}
|
||||
payloadParentHash, err := bytesutil.DecodeHexWithLength(e.ParentHash, common.HashLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.ParentHash")
|
||||
}
|
||||
payloadFeeRecipient, err := bytesutil.DecodeHexWithLength(e.FeeRecipient, fieldparams.FeeRecipientLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.FeeRecipient")
|
||||
}
|
||||
payloadStateRoot, err := bytesutil.DecodeHexWithLength(e.StateRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.StateRoot")
|
||||
}
|
||||
payloadReceiptsRoot, err := bytesutil.DecodeHexWithLength(e.ReceiptsRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.ReceiptsRoot")
|
||||
}
|
||||
payloadLogsBloom, err := bytesutil.DecodeHexWithLength(e.LogsBloom, fieldparams.LogsBloomLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.LogsBloom")
|
||||
}
|
||||
payloadPrevRandao, err := bytesutil.DecodeHexWithLength(e.PrevRandao, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.PrevRandao")
|
||||
}
|
||||
payloadBlockNumber, err := strconv.ParseUint(e.BlockNumber, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.BlockNumber")
|
||||
}
|
||||
payloadGasLimit, err := strconv.ParseUint(e.GasLimit, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.GasLimit")
|
||||
}
|
||||
payloadGasUsed, err := strconv.ParseUint(e.GasUsed, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.GasUsed")
|
||||
}
|
||||
payloadTimestamp, err := strconv.ParseUint(e.Timestamp, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.Timestamp")
|
||||
}
|
||||
payloadExtraData, err := bytesutil.DecodeHexWithMaxLength(e.ExtraData, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.ExtraData")
|
||||
}
|
||||
payloadBaseFeePerGas, err := bytesutil.Uint256ToSSZBytes(e.BaseFeePerGas)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.BaseFeePerGas")
|
||||
}
|
||||
payloadBlockHash, err := bytesutil.DecodeHexWithLength(e.BlockHash, common.HashLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.BlockHash")
|
||||
}
|
||||
err = slice.VerifyMaxLength(e.Transactions, fieldparams.MaxTxsPerPayloadLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.Transactions")
|
||||
}
|
||||
payloadTxs := make([][]byte, len(e.Transactions))
|
||||
for i, tx := range e.Transactions {
|
||||
payloadTxs[i], err = bytesutil.DecodeHexWithMaxLength(tx, fieldparams.MaxBytesPerTxLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, fmt.Sprintf("ExecutionPayload.Transactions[%d]", i))
|
||||
}
|
||||
}
|
||||
err = slice.VerifyMaxLength(e.Withdrawals, fieldparams.MaxWithdrawalsPerPayload)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.Withdrawals")
|
||||
}
|
||||
withdrawals := make([]*enginev1.Withdrawal, len(e.Withdrawals))
|
||||
for i, w := range e.Withdrawals {
|
||||
withdrawalIndex, err := strconv.ParseUint(w.WithdrawalIndex, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, fmt.Sprintf("ExecutionPayload.Withdrawals[%d].WithdrawalIndex", i))
|
||||
}
|
||||
validatorIndex, err := strconv.ParseUint(w.ValidatorIndex, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, fmt.Sprintf("ExecutionPayload.Withdrawals[%d].ValidatorIndex", i))
|
||||
}
|
||||
address, err := bytesutil.DecodeHexWithLength(w.ExecutionAddress, common.AddressLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, fmt.Sprintf("ExecutionPayload.Withdrawals[%d].ExecutionAddress", i))
|
||||
}
|
||||
amount, err := strconv.ParseUint(w.Amount, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, fmt.Sprintf("ExecutionPayload.Withdrawals[%d].Amount", i))
|
||||
}
|
||||
withdrawals[i] = &enginev1.Withdrawal{
|
||||
Index: withdrawalIndex,
|
||||
ValidatorIndex: primitives.ValidatorIndex(validatorIndex),
|
||||
Address: address,
|
||||
Amount: amount,
|
||||
}
|
||||
}
|
||||
return &enginev1.ExecutionPayloadCapella{
|
||||
ParentHash: payloadParentHash,
|
||||
FeeRecipient: payloadFeeRecipient,
|
||||
StateRoot: payloadStateRoot,
|
||||
ReceiptsRoot: payloadReceiptsRoot,
|
||||
LogsBloom: payloadLogsBloom,
|
||||
PrevRandao: payloadPrevRandao,
|
||||
BlockNumber: payloadBlockNumber,
|
||||
GasLimit: payloadGasLimit,
|
||||
GasUsed: payloadGasUsed,
|
||||
Timestamp: payloadTimestamp,
|
||||
ExtraData: payloadExtraData,
|
||||
BaseFeePerGas: payloadBaseFeePerGas,
|
||||
BlockHash: payloadBlockHash,
|
||||
Transactions: payloadTxs,
|
||||
Withdrawals: withdrawals,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ExecutionPayloadHeaderCapellaFromConsensus(payload *enginev1.ExecutionPayloadHeaderCapella) (*ExecutionPayloadHeaderCapella, error) {
|
||||
baseFeePerGas, err := sszBytesToUint256String(payload.BaseFeePerGas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ExecutionPayloadHeaderCapella{
|
||||
ParentHash: hexutil.Encode(payload.ParentHash),
|
||||
FeeRecipient: hexutil.Encode(payload.FeeRecipient),
|
||||
StateRoot: hexutil.Encode(payload.StateRoot),
|
||||
ReceiptsRoot: hexutil.Encode(payload.ReceiptsRoot),
|
||||
LogsBloom: hexutil.Encode(payload.LogsBloom),
|
||||
PrevRandao: hexutil.Encode(payload.PrevRandao),
|
||||
BlockNumber: fmt.Sprintf("%d", payload.BlockNumber),
|
||||
GasLimit: fmt.Sprintf("%d", payload.GasLimit),
|
||||
GasUsed: fmt.Sprintf("%d", payload.GasUsed),
|
||||
Timestamp: fmt.Sprintf("%d", payload.Timestamp),
|
||||
ExtraData: hexutil.Encode(payload.ExtraData),
|
||||
BaseFeePerGas: baseFeePerGas,
|
||||
BlockHash: hexutil.Encode(payload.BlockHash),
|
||||
TransactionsRoot: hexutil.Encode(payload.TransactionsRoot),
|
||||
WithdrawalsRoot: hexutil.Encode(payload.WithdrawalsRoot),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *ExecutionPayloadHeaderCapella) ToConsensus() (*enginev1.ExecutionPayloadHeaderCapella, error) {
|
||||
if e == nil {
|
||||
return nil, server.NewDecodeError(errNilValue, "ExecutionPayloadHeader")
|
||||
}
|
||||
payloadParentHash, err := bytesutil.DecodeHexWithLength(e.ParentHash, common.HashLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.ParentHash")
|
||||
}
|
||||
payloadFeeRecipient, err := bytesutil.DecodeHexWithLength(e.FeeRecipient, fieldparams.FeeRecipientLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.FeeRecipient")
|
||||
}
|
||||
payloadStateRoot, err := bytesutil.DecodeHexWithLength(e.StateRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.StateRoot")
|
||||
}
|
||||
payloadReceiptsRoot, err := bytesutil.DecodeHexWithLength(e.ReceiptsRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.ReceiptsRoot")
|
||||
}
|
||||
payloadLogsBloom, err := bytesutil.DecodeHexWithLength(e.LogsBloom, fieldparams.LogsBloomLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.LogsBloom")
|
||||
}
|
||||
payloadPrevRandao, err := bytesutil.DecodeHexWithLength(e.PrevRandao, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.PrevRandao")
|
||||
}
|
||||
payloadBlockNumber, err := strconv.ParseUint(e.BlockNumber, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.BlockNumber")
|
||||
}
|
||||
payloadGasLimit, err := strconv.ParseUint(e.GasLimit, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.GasLimit")
|
||||
}
|
||||
payloadGasUsed, err := strconv.ParseUint(e.GasUsed, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.GasUsed")
|
||||
}
|
||||
payloadTimestamp, err := strconv.ParseUint(e.Timestamp, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.Timestamp")
|
||||
}
|
||||
payloadExtraData, err := bytesutil.DecodeHexWithMaxLength(e.ExtraData, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.ExtraData")
|
||||
}
|
||||
payloadBaseFeePerGas, err := bytesutil.Uint256ToSSZBytes(e.BaseFeePerGas)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.BaseFeePerGas")
|
||||
}
|
||||
payloadBlockHash, err := bytesutil.DecodeHexWithMaxLength(e.BlockHash, common.HashLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.BlockHash")
|
||||
}
|
||||
payloadTxsRoot, err := bytesutil.DecodeHexWithMaxLength(e.TransactionsRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.TransactionsRoot")
|
||||
}
|
||||
payloadWithdrawalsRoot, err := bytesutil.DecodeHexWithMaxLength(e.WithdrawalsRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.WithdrawalsRoot")
|
||||
}
|
||||
return &enginev1.ExecutionPayloadHeaderCapella{
|
||||
ParentHash: payloadParentHash,
|
||||
FeeRecipient: payloadFeeRecipient,
|
||||
StateRoot: payloadStateRoot,
|
||||
ReceiptsRoot: payloadReceiptsRoot,
|
||||
LogsBloom: payloadLogsBloom,
|
||||
PrevRandao: payloadPrevRandao,
|
||||
BlockNumber: payloadBlockNumber,
|
||||
GasLimit: payloadGasLimit,
|
||||
GasUsed: payloadGasUsed,
|
||||
Timestamp: payloadTimestamp,
|
||||
ExtraData: payloadExtraData,
|
||||
BaseFeePerGas: payloadBaseFeePerGas,
|
||||
BlockHash: payloadBlockHash,
|
||||
TransactionsRoot: payloadTxsRoot,
|
||||
WithdrawalsRoot: payloadWithdrawalsRoot,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Deneb
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
func ExecutionPayloadDenebFromConsensus(payload *enginev1.ExecutionPayloadDeneb) (*ExecutionPayloadDeneb, error) {
|
||||
baseFeePerGas, err := sszBytesToUint256String(payload.BaseFeePerGas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactions := make([]string, len(payload.Transactions))
|
||||
for i, tx := range payload.Transactions {
|
||||
transactions[i] = hexutil.Encode(tx)
|
||||
}
|
||||
|
||||
return &ExecutionPayloadDeneb{
|
||||
ParentHash: hexutil.Encode(payload.ParentHash),
|
||||
FeeRecipient: hexutil.Encode(payload.FeeRecipient),
|
||||
StateRoot: hexutil.Encode(payload.StateRoot),
|
||||
ReceiptsRoot: hexutil.Encode(payload.ReceiptsRoot),
|
||||
LogsBloom: hexutil.Encode(payload.LogsBloom),
|
||||
PrevRandao: hexutil.Encode(payload.PrevRandao),
|
||||
BlockNumber: fmt.Sprintf("%d", payload.BlockNumber),
|
||||
GasLimit: fmt.Sprintf("%d", payload.GasLimit),
|
||||
GasUsed: fmt.Sprintf("%d", payload.GasUsed),
|
||||
Timestamp: fmt.Sprintf("%d", payload.Timestamp),
|
||||
ExtraData: hexutil.Encode(payload.ExtraData),
|
||||
BaseFeePerGas: baseFeePerGas,
|
||||
BlockHash: hexutil.Encode(payload.BlockHash),
|
||||
Transactions: transactions,
|
||||
Withdrawals: WithdrawalsFromConsensus(payload.Withdrawals),
|
||||
BlobGasUsed: fmt.Sprintf("%d", payload.BlobGasUsed),
|
||||
ExcessBlobGas: fmt.Sprintf("%d", payload.ExcessBlobGas),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *ExecutionPayloadDeneb) ToConsensus() (*enginev1.ExecutionPayloadDeneb, error) {
|
||||
if e == nil {
|
||||
return nil, server.NewDecodeError(errNilValue, "ExecutionPayload")
|
||||
}
|
||||
payloadParentHash, err := bytesutil.DecodeHexWithLength(e.ParentHash, common.HashLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.ParentHash")
|
||||
}
|
||||
payloadFeeRecipient, err := bytesutil.DecodeHexWithLength(e.FeeRecipient, fieldparams.FeeRecipientLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.FeeRecipient")
|
||||
}
|
||||
payloadStateRoot, err := bytesutil.DecodeHexWithLength(e.StateRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.StateRoot")
|
||||
}
|
||||
payloadReceiptsRoot, err := bytesutil.DecodeHexWithLength(e.ReceiptsRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.ReceiptsRoot")
|
||||
}
|
||||
payloadLogsBloom, err := bytesutil.DecodeHexWithLength(e.LogsBloom, fieldparams.LogsBloomLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.LogsBloom")
|
||||
}
|
||||
payloadPrevRandao, err := bytesutil.DecodeHexWithLength(e.PrevRandao, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.PrevRandao")
|
||||
}
|
||||
payloadBlockNumber, err := strconv.ParseUint(e.BlockNumber, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.BlockNumber")
|
||||
}
|
||||
payloadGasLimit, err := strconv.ParseUint(e.GasLimit, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.GasLimit")
|
||||
}
|
||||
payloadGasUsed, err := strconv.ParseUint(e.GasUsed, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.GasUsed")
|
||||
}
|
||||
payloadTimestamp, err := strconv.ParseUint(e.Timestamp, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.Timestamp")
|
||||
}
|
||||
payloadExtraData, err := bytesutil.DecodeHexWithMaxLength(e.ExtraData, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.ExtraData")
|
||||
}
|
||||
payloadBaseFeePerGas, err := bytesutil.Uint256ToSSZBytes(e.BaseFeePerGas)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.BaseFeePerGas")
|
||||
}
|
||||
payloadBlockHash, err := bytesutil.DecodeHexWithLength(e.BlockHash, common.HashLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.BlockHash")
|
||||
}
|
||||
err = slice.VerifyMaxLength(e.Transactions, fieldparams.MaxTxsPerPayloadLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.Transactions")
|
||||
}
|
||||
txs := make([][]byte, len(e.Transactions))
|
||||
for i, tx := range e.Transactions {
|
||||
txs[i], err = bytesutil.DecodeHexWithMaxLength(tx, fieldparams.MaxBytesPerTxLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, fmt.Sprintf("ExecutionPayload.Transactions[%d]", i))
|
||||
}
|
||||
}
|
||||
err = slice.VerifyMaxLength(e.Withdrawals, fieldparams.MaxWithdrawalsPerPayload)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.Withdrawals")
|
||||
}
|
||||
withdrawals := make([]*enginev1.Withdrawal, len(e.Withdrawals))
|
||||
for i, w := range e.Withdrawals {
|
||||
withdrawalIndex, err := strconv.ParseUint(w.WithdrawalIndex, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, fmt.Sprintf("ExecutionPayload.Withdrawals[%d].WithdrawalIndex", i))
|
||||
}
|
||||
validatorIndex, err := strconv.ParseUint(w.ValidatorIndex, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, fmt.Sprintf("ExecutionPayload.Withdrawals[%d].ValidatorIndex", i))
|
||||
}
|
||||
address, err := bytesutil.DecodeHexWithLength(w.ExecutionAddress, common.AddressLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, fmt.Sprintf("ExecutionPayload.Withdrawals[%d].ExecutionAddress", i))
|
||||
}
|
||||
amount, err := strconv.ParseUint(w.Amount, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, fmt.Sprintf("ExecutionPayload.Withdrawals[%d].Amount", i))
|
||||
}
|
||||
withdrawals[i] = &enginev1.Withdrawal{
|
||||
Index: withdrawalIndex,
|
||||
ValidatorIndex: primitives.ValidatorIndex(validatorIndex),
|
||||
Address: address,
|
||||
Amount: amount,
|
||||
}
|
||||
}
|
||||
|
||||
payloadBlobGasUsed, err := strconv.ParseUint(e.BlobGasUsed, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.BlobGasUsed")
|
||||
}
|
||||
payloadExcessBlobGas, err := strconv.ParseUint(e.ExcessBlobGas, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.ExcessBlobGas")
|
||||
}
|
||||
return &enginev1.ExecutionPayloadDeneb{
|
||||
ParentHash: payloadParentHash,
|
||||
FeeRecipient: payloadFeeRecipient,
|
||||
StateRoot: payloadStateRoot,
|
||||
ReceiptsRoot: payloadReceiptsRoot,
|
||||
LogsBloom: payloadLogsBloom,
|
||||
PrevRandao: payloadPrevRandao,
|
||||
BlockNumber: payloadBlockNumber,
|
||||
GasLimit: payloadGasLimit,
|
||||
GasUsed: payloadGasUsed,
|
||||
Timestamp: payloadTimestamp,
|
||||
ExtraData: payloadExtraData,
|
||||
BaseFeePerGas: payloadBaseFeePerGas,
|
||||
BlockHash: payloadBlockHash,
|
||||
Transactions: txs,
|
||||
Withdrawals: withdrawals,
|
||||
BlobGasUsed: payloadBlobGasUsed,
|
||||
ExcessBlobGas: payloadExcessBlobGas,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ExecutionPayloadHeaderDenebFromConsensus(payload *enginev1.ExecutionPayloadHeaderDeneb) (*ExecutionPayloadHeaderDeneb, error) {
|
||||
baseFeePerGas, err := sszBytesToUint256String(payload.BaseFeePerGas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ExecutionPayloadHeaderDeneb{
|
||||
ParentHash: hexutil.Encode(payload.ParentHash),
|
||||
FeeRecipient: hexutil.Encode(payload.FeeRecipient),
|
||||
StateRoot: hexutil.Encode(payload.StateRoot),
|
||||
ReceiptsRoot: hexutil.Encode(payload.ReceiptsRoot),
|
||||
LogsBloom: hexutil.Encode(payload.LogsBloom),
|
||||
PrevRandao: hexutil.Encode(payload.PrevRandao),
|
||||
BlockNumber: fmt.Sprintf("%d", payload.BlockNumber),
|
||||
GasLimit: fmt.Sprintf("%d", payload.GasLimit),
|
||||
GasUsed: fmt.Sprintf("%d", payload.GasUsed),
|
||||
Timestamp: fmt.Sprintf("%d", payload.Timestamp),
|
||||
ExtraData: hexutil.Encode(payload.ExtraData),
|
||||
BaseFeePerGas: baseFeePerGas,
|
||||
BlockHash: hexutil.Encode(payload.BlockHash),
|
||||
TransactionsRoot: hexutil.Encode(payload.TransactionsRoot),
|
||||
WithdrawalsRoot: hexutil.Encode(payload.WithdrawalsRoot),
|
||||
BlobGasUsed: fmt.Sprintf("%d", payload.BlobGasUsed),
|
||||
ExcessBlobGas: fmt.Sprintf("%d", payload.ExcessBlobGas),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *ExecutionPayloadHeaderDeneb) ToConsensus() (*enginev1.ExecutionPayloadHeaderDeneb, error) {
|
||||
if e == nil {
|
||||
return nil, server.NewDecodeError(errNilValue, "ExecutionPayloadHeader")
|
||||
}
|
||||
payloadParentHash, err := bytesutil.DecodeHexWithLength(e.ParentHash, common.HashLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.ParentHash")
|
||||
}
|
||||
payloadFeeRecipient, err := bytesutil.DecodeHexWithLength(e.FeeRecipient, fieldparams.FeeRecipientLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.FeeRecipient")
|
||||
}
|
||||
payloadStateRoot, err := bytesutil.DecodeHexWithLength(e.StateRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.StateRoot")
|
||||
}
|
||||
payloadReceiptsRoot, err := bytesutil.DecodeHexWithLength(e.ReceiptsRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.ReceiptsRoot")
|
||||
}
|
||||
payloadLogsBloom, err := bytesutil.DecodeHexWithLength(e.LogsBloom, fieldparams.LogsBloomLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.LogsBloom")
|
||||
}
|
||||
payloadPrevRandao, err := bytesutil.DecodeHexWithLength(e.PrevRandao, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.PrevRandao")
|
||||
}
|
||||
payloadBlockNumber, err := strconv.ParseUint(e.BlockNumber, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.BlockNumber")
|
||||
}
|
||||
payloadGasLimit, err := strconv.ParseUint(e.GasLimit, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.GasLimit")
|
||||
}
|
||||
payloadGasUsed, err := strconv.ParseUint(e.GasUsed, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.GasUsed")
|
||||
}
|
||||
payloadTimestamp, err := strconv.ParseUint(e.Timestamp, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.Timestamp")
|
||||
}
|
||||
payloadExtraData, err := bytesutil.DecodeHexWithMaxLength(e.ExtraData, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.ExtraData")
|
||||
}
|
||||
payloadBaseFeePerGas, err := bytesutil.Uint256ToSSZBytes(e.BaseFeePerGas)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.BaseFeePerGas")
|
||||
}
|
||||
payloadBlockHash, err := bytesutil.DecodeHexWithLength(e.BlockHash, common.HashLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.BlockHash")
|
||||
}
|
||||
payloadTxsRoot, err := bytesutil.DecodeHexWithLength(e.TransactionsRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.TransactionsRoot")
|
||||
}
|
||||
payloadWithdrawalsRoot, err := bytesutil.DecodeHexWithLength(e.WithdrawalsRoot, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayloadHeader.WithdrawalsRoot")
|
||||
}
|
||||
payloadBlobGasUsed, err := strconv.ParseUint(e.BlobGasUsed, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.BlobGasUsed")
|
||||
}
|
||||
payloadExcessBlobGas, err := strconv.ParseUint(e.ExcessBlobGas, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ExecutionPayload.ExcessBlobGas")
|
||||
}
|
||||
return &enginev1.ExecutionPayloadHeaderDeneb{
|
||||
ParentHash: payloadParentHash,
|
||||
FeeRecipient: payloadFeeRecipient,
|
||||
StateRoot: payloadStateRoot,
|
||||
ReceiptsRoot: payloadReceiptsRoot,
|
||||
LogsBloom: payloadLogsBloom,
|
||||
PrevRandao: payloadPrevRandao,
|
||||
BlockNumber: payloadBlockNumber,
|
||||
GasLimit: payloadGasLimit,
|
||||
GasUsed: payloadGasUsed,
|
||||
Timestamp: payloadTimestamp,
|
||||
ExtraData: payloadExtraData,
|
||||
BaseFeePerGas: payloadBaseFeePerGas,
|
||||
BlockHash: payloadBlockHash,
|
||||
TransactionsRoot: payloadTxsRoot,
|
||||
WithdrawalsRoot: payloadWithdrawalsRoot,
|
||||
BlobGasUsed: payloadBlobGasUsed,
|
||||
ExcessBlobGas: payloadExcessBlobGas,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Electra
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
var (
|
||||
ExecutionPayloadElectraFromConsensus = ExecutionPayloadDenebFromConsensus
|
||||
ExecutionPayloadHeaderElectraFromConsensus = ExecutionPayloadHeaderDenebFromConsensus
|
||||
)
|
||||
|
||||
func WithdrawalRequestsFromConsensus(ws []*enginev1.WithdrawalRequest) []*WithdrawalRequest {
|
||||
result := make([]*WithdrawalRequest, len(ws))
|
||||
for i, w := range ws {
|
||||
result[i] = WithdrawalRequestFromConsensus(w)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func WithdrawalRequestFromConsensus(w *enginev1.WithdrawalRequest) *WithdrawalRequest {
|
||||
return &WithdrawalRequest{
|
||||
SourceAddress: hexutil.Encode(w.SourceAddress),
|
||||
ValidatorPubkey: hexutil.Encode(w.ValidatorPubkey),
|
||||
Amount: fmt.Sprintf("%d", w.Amount),
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WithdrawalRequest) ToConsensus() (*enginev1.WithdrawalRequest, error) {
|
||||
if w == nil {
|
||||
return nil, server.NewDecodeError(errNilValue, "WithdrawalRequest")
|
||||
}
|
||||
src, err := bytesutil.DecodeHexWithLength(w.SourceAddress, common.AddressLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "SourceAddress")
|
||||
}
|
||||
pubkey, err := bytesutil.DecodeHexWithLength(w.ValidatorPubkey, fieldparams.BLSPubkeyLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ValidatorPubkey")
|
||||
}
|
||||
amount, err := strconv.ParseUint(w.Amount, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "Amount")
|
||||
}
|
||||
return &enginev1.WithdrawalRequest{
|
||||
SourceAddress: src,
|
||||
ValidatorPubkey: pubkey,
|
||||
Amount: amount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ConsolidationRequestsFromConsensus(cs []*enginev1.ConsolidationRequest) []*ConsolidationRequest {
|
||||
result := make([]*ConsolidationRequest, len(cs))
|
||||
for i, c := range cs {
|
||||
result[i] = ConsolidationRequestFromConsensus(c)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func ConsolidationRequestFromConsensus(c *enginev1.ConsolidationRequest) *ConsolidationRequest {
|
||||
return &ConsolidationRequest{
|
||||
SourceAddress: hexutil.Encode(c.SourceAddress),
|
||||
SourcePubkey: hexutil.Encode(c.SourcePubkey),
|
||||
TargetPubkey: hexutil.Encode(c.TargetPubkey),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConsolidationRequest) ToConsensus() (*enginev1.ConsolidationRequest, error) {
|
||||
if c == nil {
|
||||
return nil, server.NewDecodeError(errNilValue, "ConsolidationRequest")
|
||||
}
|
||||
srcAddress, err := bytesutil.DecodeHexWithLength(c.SourceAddress, common.AddressLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "SourceAddress")
|
||||
}
|
||||
srcPubkey, err := bytesutil.DecodeHexWithLength(c.SourcePubkey, fieldparams.BLSPubkeyLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "SourcePubkey")
|
||||
}
|
||||
targetPubkey, err := bytesutil.DecodeHexWithLength(c.TargetPubkey, fieldparams.BLSPubkeyLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "TargetPubkey")
|
||||
}
|
||||
return &enginev1.ConsolidationRequest{
|
||||
SourceAddress: srcAddress,
|
||||
SourcePubkey: srcPubkey,
|
||||
TargetPubkey: targetPubkey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DepositRequestsFromConsensus(ds []*enginev1.DepositRequest) []*DepositRequest {
|
||||
result := make([]*DepositRequest, len(ds))
|
||||
for i, d := range ds {
|
||||
result[i] = DepositRequestFromConsensus(d)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func DepositRequestFromConsensus(d *enginev1.DepositRequest) *DepositRequest {
|
||||
return &DepositRequest{
|
||||
Pubkey: hexutil.Encode(d.Pubkey),
|
||||
WithdrawalCredentials: hexutil.Encode(d.WithdrawalCredentials),
|
||||
Amount: fmt.Sprintf("%d", d.Amount),
|
||||
Signature: hexutil.Encode(d.Signature),
|
||||
Index: fmt.Sprintf("%d", d.Index),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DepositRequest) ToConsensus() (*enginev1.DepositRequest, error) {
|
||||
if d == nil {
|
||||
return nil, server.NewDecodeError(errNilValue, "DepositRequest")
|
||||
}
|
||||
pubkey, err := bytesutil.DecodeHexWithLength(d.Pubkey, fieldparams.BLSPubkeyLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "Pubkey")
|
||||
}
|
||||
withdrawalCredentials, err := bytesutil.DecodeHexWithLength(d.WithdrawalCredentials, fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "WithdrawalCredentials")
|
||||
}
|
||||
amount, err := strconv.ParseUint(d.Amount, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "Amount")
|
||||
}
|
||||
sig, err := bytesutil.DecodeHexWithLength(d.Signature, fieldparams.BLSSignatureLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "Signature")
|
||||
}
|
||||
index, err := strconv.ParseUint(d.Index, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "Index")
|
||||
}
|
||||
return &enginev1.DepositRequest{
|
||||
Pubkey: pubkey,
|
||||
WithdrawalCredentials: withdrawalCredentials,
|
||||
Amount: amount,
|
||||
Signature: sig,
|
||||
Index: index,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ExecutionRequestsFromConsensus(er *enginev1.ExecutionRequests) *ExecutionRequests {
|
||||
return &ExecutionRequests{
|
||||
Deposits: DepositRequestsFromConsensus(er.Deposits),
|
||||
Withdrawals: WithdrawalRequestsFromConsensus(er.Withdrawals),
|
||||
Consolidations: ConsolidationRequestsFromConsensus(er.Consolidations),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ExecutionRequests) ToConsensus() (*enginev1.ExecutionRequests, error) {
|
||||
if e == nil {
|
||||
return nil, server.NewDecodeError(errNilValue, "ExecutionRequests")
|
||||
}
|
||||
var err error
|
||||
if err = slice.VerifyMaxLength(e.Deposits, params.BeaconConfig().MaxDepositRequestsPerPayload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
depositRequests := make([]*enginev1.DepositRequest, len(e.Deposits))
|
||||
for i, d := range e.Deposits {
|
||||
depositRequests[i], err = d.ToConsensus()
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, fmt.Sprintf("ExecutionRequests.Deposits[%d]", i))
|
||||
}
|
||||
}
|
||||
|
||||
if err = slice.VerifyMaxLength(e.Withdrawals, params.BeaconConfig().MaxWithdrawalRequestsPerPayload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withdrawalRequests := make([]*enginev1.WithdrawalRequest, len(e.Withdrawals))
|
||||
for i, w := range e.Withdrawals {
|
||||
withdrawalRequests[i], err = w.ToConsensus()
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, fmt.Sprintf("ExecutionRequests.Withdrawals[%d]", i))
|
||||
}
|
||||
}
|
||||
|
||||
if err = slice.VerifyMaxLength(e.Consolidations, params.BeaconConfig().MaxConsolidationsRequestsPerPayload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
consolidationRequests := make([]*enginev1.ConsolidationRequest, len(e.Consolidations))
|
||||
for i, c := range e.Consolidations {
|
||||
consolidationRequests[i], err = c.ToConsensus()
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, fmt.Sprintf("ExecutionRequests.Consolidations[%d]", i))
|
||||
}
|
||||
}
|
||||
return &enginev1.ExecutionRequests{
|
||||
Deposits: depositRequests,
|
||||
Withdrawals: withdrawalRequests,
|
||||
Consolidations: consolidationRequests,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Fulu
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
var (
|
||||
ExecutionPayloadFuluFromConsensus = ExecutionPayloadDenebFromConsensus
|
||||
ExecutionPayloadHeaderFuluFromConsensus = ExecutionPayloadHeaderDenebFromConsensus
|
||||
BeaconBlockFuluFromConsensus = BeaconBlockElectraFromConsensus
|
||||
)
|
||||
563
api/server/structs/conversions_block_execution_test.go
Normal file
563
api/server/structs/conversions_block_execution_test.go
Normal file
@@ -0,0 +1,563 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
)
|
||||
|
||||
func fillByteSlice(sliceLength int, value byte) []byte {
|
||||
bytes := make([]byte, sliceLength)
|
||||
|
||||
for index := range bytes {
|
||||
bytes[index] = value
|
||||
}
|
||||
|
||||
return bytes
|
||||
}
|
||||
|
||||
// TestExecutionPayloadFromConsensus_HappyPath checks the
|
||||
// ExecutionPayloadFromConsensus function under normal conditions.
|
||||
func TestExecutionPayloadFromConsensus_HappyPath(t *testing.T) {
|
||||
consensusPayload := &enginev1.ExecutionPayload{
|
||||
ParentHash: fillByteSlice(common.HashLength, 0xaa),
|
||||
FeeRecipient: fillByteSlice(20, 0xbb),
|
||||
StateRoot: fillByteSlice(32, 0xcc),
|
||||
ReceiptsRoot: fillByteSlice(32, 0xdd),
|
||||
LogsBloom: fillByteSlice(256, 0xee),
|
||||
PrevRandao: fillByteSlice(32, 0xff),
|
||||
BlockNumber: 12345,
|
||||
GasLimit: 15000000,
|
||||
GasUsed: 8000000,
|
||||
Timestamp: 1680000000,
|
||||
ExtraData: fillByteSlice(8, 0x11),
|
||||
BaseFeePerGas: fillByteSlice(32, 0x01),
|
||||
BlockHash: fillByteSlice(common.HashLength, 0x22),
|
||||
Transactions: [][]byte{
|
||||
fillByteSlice(10, 0x33),
|
||||
fillByteSlice(10, 0x44),
|
||||
},
|
||||
}
|
||||
|
||||
result, err := ExecutionPayloadFromConsensus(consensusPayload)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
require.Equal(t, hexutil.Encode(consensusPayload.ParentHash), result.ParentHash)
|
||||
require.Equal(t, hexutil.Encode(consensusPayload.FeeRecipient), result.FeeRecipient)
|
||||
require.Equal(t, hexutil.Encode(consensusPayload.StateRoot), result.StateRoot)
|
||||
require.Equal(t, hexutil.Encode(consensusPayload.ReceiptsRoot), result.ReceiptsRoot)
|
||||
require.Equal(t, fmt.Sprintf("%d", consensusPayload.BlockNumber), result.BlockNumber)
|
||||
}
|
||||
|
||||
// TestExecutionPayload_ToConsensus_HappyPath checks the
|
||||
// (*ExecutionPayload).ToConsensus function under normal conditions.
|
||||
func TestExecutionPayload_ToConsensus_HappyPath(t *testing.T) {
|
||||
payload := &ExecutionPayload{
|
||||
ParentHash: hexutil.Encode(fillByteSlice(common.HashLength, 0xaa)),
|
||||
FeeRecipient: hexutil.Encode(fillByteSlice(20, 0xbb)),
|
||||
StateRoot: hexutil.Encode(fillByteSlice(32, 0xcc)),
|
||||
ReceiptsRoot: hexutil.Encode(fillByteSlice(32, 0xdd)),
|
||||
LogsBloom: hexutil.Encode(fillByteSlice(256, 0xee)),
|
||||
PrevRandao: hexutil.Encode(fillByteSlice(32, 0xff)),
|
||||
BlockNumber: "12345",
|
||||
GasLimit: "15000000",
|
||||
GasUsed: "8000000",
|
||||
Timestamp: "1680000000",
|
||||
ExtraData: "0x11111111",
|
||||
BaseFeePerGas: "1234",
|
||||
BlockHash: hexutil.Encode(fillByteSlice(common.HashLength, 0x22)),
|
||||
Transactions: []string{
|
||||
hexutil.Encode(fillByteSlice(10, 0x33)),
|
||||
hexutil.Encode(fillByteSlice(10, 0x44)),
|
||||
},
|
||||
}
|
||||
|
||||
result, err := payload.ToConsensus()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, result.ParentHash, fillByteSlice(common.HashLength, 0xaa))
|
||||
require.DeepEqual(t, result.FeeRecipient, fillByteSlice(20, 0xbb))
|
||||
require.DeepEqual(t, result.StateRoot, fillByteSlice(32, 0xcc))
|
||||
}
|
||||
|
||||
// TestExecutionPayloadHeaderFromConsensus_HappyPath checks the
|
||||
// ExecutionPayloadHeaderFromConsensus function under normal conditions.
|
||||
func TestExecutionPayloadHeaderFromConsensus_HappyPath(t *testing.T) {
|
||||
consensusHeader := &enginev1.ExecutionPayloadHeader{
|
||||
ParentHash: fillByteSlice(common.HashLength, 0xaa),
|
||||
FeeRecipient: fillByteSlice(20, 0xbb),
|
||||
StateRoot: fillByteSlice(32, 0xcc),
|
||||
ReceiptsRoot: fillByteSlice(32, 0xdd),
|
||||
LogsBloom: fillByteSlice(256, 0xee),
|
||||
PrevRandao: fillByteSlice(32, 0xff),
|
||||
BlockNumber: 9999,
|
||||
GasLimit: 5000000,
|
||||
GasUsed: 2500000,
|
||||
Timestamp: 1111111111,
|
||||
ExtraData: fillByteSlice(4, 0x12),
|
||||
BaseFeePerGas: fillByteSlice(32, 0x34),
|
||||
BlockHash: fillByteSlice(common.HashLength, 0x56),
|
||||
TransactionsRoot: fillByteSlice(32, 0x78),
|
||||
}
|
||||
|
||||
result, err := ExecutionPayloadHeaderFromConsensus(consensusHeader)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
require.Equal(t, hexutil.Encode(consensusHeader.ParentHash), result.ParentHash)
|
||||
require.Equal(t, fmt.Sprintf("%d", consensusHeader.BlockNumber), result.BlockNumber)
|
||||
}
|
||||
|
||||
// TestExecutionPayloadHeader_ToConsensus_HappyPath checks the
|
||||
// (*ExecutionPayloadHeader).ToConsensus function under normal conditions.
|
||||
func TestExecutionPayloadHeader_ToConsensus_HappyPath(t *testing.T) {
|
||||
header := &ExecutionPayloadHeader{
|
||||
ParentHash: hexutil.Encode(fillByteSlice(common.HashLength, 0xaa)),
|
||||
FeeRecipient: hexutil.Encode(fillByteSlice(20, 0xbb)),
|
||||
StateRoot: hexutil.Encode(fillByteSlice(32, 0xcc)),
|
||||
ReceiptsRoot: hexutil.Encode(fillByteSlice(32, 0xdd)),
|
||||
LogsBloom: hexutil.Encode(fillByteSlice(256, 0xee)),
|
||||
PrevRandao: hexutil.Encode(fillByteSlice(32, 0xff)),
|
||||
BlockNumber: "9999",
|
||||
GasLimit: "5000000",
|
||||
GasUsed: "2500000",
|
||||
Timestamp: "1111111111",
|
||||
ExtraData: "0x1234abcd",
|
||||
BaseFeePerGas: "1234",
|
||||
BlockHash: hexutil.Encode(fillByteSlice(common.HashLength, 0x56)),
|
||||
TransactionsRoot: hexutil.Encode(fillByteSlice(32, 0x78)),
|
||||
}
|
||||
|
||||
result, err := header.ToConsensus()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, hexutil.Encode(result.ParentHash), header.ParentHash)
|
||||
require.DeepEqual(t, hexutil.Encode(result.FeeRecipient), header.FeeRecipient)
|
||||
require.DeepEqual(t, hexutil.Encode(result.StateRoot), header.StateRoot)
|
||||
}
|
||||
|
||||
// TestExecutionPayloadCapellaFromConsensus_HappyPath checks the
|
||||
// ExecutionPayloadCapellaFromConsensus function under normal conditions.
|
||||
func TestExecutionPayloadCapellaFromConsensus_HappyPath(t *testing.T) {
|
||||
capellaPayload := &enginev1.ExecutionPayloadCapella{
|
||||
ParentHash: fillByteSlice(common.HashLength, 0xaa),
|
||||
FeeRecipient: fillByteSlice(20, 0xbb),
|
||||
StateRoot: fillByteSlice(32, 0xcc),
|
||||
ReceiptsRoot: fillByteSlice(32, 0xdd),
|
||||
LogsBloom: fillByteSlice(256, 0xee),
|
||||
PrevRandao: fillByteSlice(32, 0xff),
|
||||
BlockNumber: 123,
|
||||
GasLimit: 9876543,
|
||||
GasUsed: 1234567,
|
||||
Timestamp: 5555555,
|
||||
ExtraData: fillByteSlice(6, 0x11),
|
||||
BaseFeePerGas: fillByteSlice(32, 0x22),
|
||||
BlockHash: fillByteSlice(common.HashLength, 0x33),
|
||||
Transactions: [][]byte{
|
||||
fillByteSlice(5, 0x44),
|
||||
},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
{
|
||||
Index: 1,
|
||||
ValidatorIndex: 2,
|
||||
Address: fillByteSlice(20, 0xaa),
|
||||
Amount: 100,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result, err := ExecutionPayloadCapellaFromConsensus(capellaPayload)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
require.Equal(t, hexutil.Encode(capellaPayload.ParentHash), result.ParentHash)
|
||||
require.Equal(t, len(capellaPayload.Transactions), len(result.Transactions))
|
||||
require.Equal(t, len(capellaPayload.Withdrawals), len(result.Withdrawals))
|
||||
}
|
||||
|
||||
// TestExecutionPayloadCapella_ToConsensus_HappyPath checks the
|
||||
// (*ExecutionPayloadCapella).ToConsensus function under normal conditions.
|
||||
func TestExecutionPayloadCapella_ToConsensus_HappyPath(t *testing.T) {
|
||||
capella := &ExecutionPayloadCapella{
|
||||
ParentHash: hexutil.Encode(fillByteSlice(common.HashLength, 0xaa)),
|
||||
FeeRecipient: hexutil.Encode(fillByteSlice(20, 0xbb)),
|
||||
StateRoot: hexutil.Encode(fillByteSlice(32, 0xcc)),
|
||||
ReceiptsRoot: hexutil.Encode(fillByteSlice(32, 0xdd)),
|
||||
LogsBloom: hexutil.Encode(fillByteSlice(256, 0xee)),
|
||||
PrevRandao: hexutil.Encode(fillByteSlice(32, 0xff)),
|
||||
BlockNumber: "123",
|
||||
GasLimit: "9876543",
|
||||
GasUsed: "1234567",
|
||||
Timestamp: "5555555",
|
||||
ExtraData: hexutil.Encode(fillByteSlice(6, 0x11)),
|
||||
BaseFeePerGas: "1234",
|
||||
BlockHash: hexutil.Encode(fillByteSlice(common.HashLength, 0x33)),
|
||||
Transactions: []string{
|
||||
hexutil.Encode(fillByteSlice(5, 0x44)),
|
||||
},
|
||||
Withdrawals: []*Withdrawal{
|
||||
{
|
||||
WithdrawalIndex: "1",
|
||||
ValidatorIndex: "2",
|
||||
ExecutionAddress: hexutil.Encode(fillByteSlice(20, 0xaa)),
|
||||
Amount: "100",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result, err := capella.ToConsensus()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, hexutil.Encode(result.ParentHash), capella.ParentHash)
|
||||
require.DeepEqual(t, hexutil.Encode(result.FeeRecipient), capella.FeeRecipient)
|
||||
require.DeepEqual(t, hexutil.Encode(result.StateRoot), capella.StateRoot)
|
||||
}
|
||||
|
||||
// TestExecutionPayloadDenebFromConsensus_HappyPath checks the
|
||||
// ExecutionPayloadDenebFromConsensus function under normal conditions.
|
||||
func TestExecutionPayloadDenebFromConsensus_HappyPath(t *testing.T) {
|
||||
denebPayload := &enginev1.ExecutionPayloadDeneb{
|
||||
ParentHash: fillByteSlice(common.HashLength, 0xaa),
|
||||
FeeRecipient: fillByteSlice(20, 0xbb),
|
||||
StateRoot: fillByteSlice(32, 0xcc),
|
||||
ReceiptsRoot: fillByteSlice(32, 0xdd),
|
||||
LogsBloom: fillByteSlice(256, 0xee),
|
||||
PrevRandao: fillByteSlice(32, 0xff),
|
||||
BlockNumber: 999,
|
||||
GasLimit: 2222222,
|
||||
GasUsed: 1111111,
|
||||
Timestamp: 666666,
|
||||
ExtraData: fillByteSlice(6, 0x11),
|
||||
BaseFeePerGas: fillByteSlice(32, 0x22),
|
||||
BlockHash: fillByteSlice(common.HashLength, 0x33),
|
||||
Transactions: [][]byte{
|
||||
fillByteSlice(5, 0x44),
|
||||
},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
{
|
||||
Index: 1,
|
||||
ValidatorIndex: 2,
|
||||
Address: fillByteSlice(20, 0xaa),
|
||||
Amount: 100,
|
||||
},
|
||||
},
|
||||
BlobGasUsed: 1234,
|
||||
ExcessBlobGas: 5678,
|
||||
}
|
||||
|
||||
result, err := ExecutionPayloadDenebFromConsensus(denebPayload)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, hexutil.Encode(denebPayload.ParentHash), result.ParentHash)
|
||||
require.Equal(t, len(denebPayload.Transactions), len(result.Transactions))
|
||||
require.Equal(t, len(denebPayload.Withdrawals), len(result.Withdrawals))
|
||||
require.Equal(t, "1234", result.BlobGasUsed)
|
||||
require.Equal(t, fmt.Sprintf("%d", denebPayload.BlockNumber), result.BlockNumber)
|
||||
}
|
||||
|
||||
// TestExecutionPayloadDeneb_ToConsensus_HappyPath checks the
|
||||
// (*ExecutionPayloadDeneb).ToConsensus function under normal conditions.
|
||||
func TestExecutionPayloadDeneb_ToConsensus_HappyPath(t *testing.T) {
|
||||
deneb := &ExecutionPayloadDeneb{
|
||||
ParentHash: hexutil.Encode(fillByteSlice(common.HashLength, 0xaa)),
|
||||
FeeRecipient: hexutil.Encode(fillByteSlice(20, 0xbb)),
|
||||
StateRoot: hexutil.Encode(fillByteSlice(32, 0xcc)),
|
||||
ReceiptsRoot: hexutil.Encode(fillByteSlice(32, 0xdd)),
|
||||
LogsBloom: hexutil.Encode(fillByteSlice(256, 0xee)),
|
||||
PrevRandao: hexutil.Encode(fillByteSlice(32, 0xff)),
|
||||
BlockNumber: "999",
|
||||
GasLimit: "2222222",
|
||||
GasUsed: "1111111",
|
||||
Timestamp: "666666",
|
||||
ExtraData: hexutil.Encode(fillByteSlice(6, 0x11)),
|
||||
BaseFeePerGas: "1234",
|
||||
BlockHash: hexutil.Encode(fillByteSlice(common.HashLength, 0x33)),
|
||||
Transactions: []string{
|
||||
hexutil.Encode(fillByteSlice(5, 0x44)),
|
||||
},
|
||||
Withdrawals: []*Withdrawal{
|
||||
{
|
||||
WithdrawalIndex: "1",
|
||||
ValidatorIndex: "2",
|
||||
ExecutionAddress: hexutil.Encode(fillByteSlice(20, 0xaa)),
|
||||
Amount: "100",
|
||||
},
|
||||
},
|
||||
BlobGasUsed: "1234",
|
||||
ExcessBlobGas: "5678",
|
||||
}
|
||||
|
||||
result, err := deneb.ToConsensus()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, hexutil.Encode(result.ParentHash), deneb.ParentHash)
|
||||
require.DeepEqual(t, hexutil.Encode(result.FeeRecipient), deneb.FeeRecipient)
|
||||
require.Equal(t, result.BlockNumber, uint64(999))
|
||||
}
|
||||
|
||||
func TestExecutionPayloadHeaderCapellaFromConsensus_HappyPath(t *testing.T) {
|
||||
capellaHeader := &enginev1.ExecutionPayloadHeaderCapella{
|
||||
ParentHash: fillByteSlice(common.HashLength, 0xaa),
|
||||
FeeRecipient: fillByteSlice(20, 0xbb),
|
||||
StateRoot: fillByteSlice(32, 0xcc),
|
||||
ReceiptsRoot: fillByteSlice(32, 0xdd),
|
||||
LogsBloom: fillByteSlice(256, 0xee),
|
||||
PrevRandao: fillByteSlice(32, 0xff),
|
||||
BlockNumber: 555,
|
||||
GasLimit: 1111111,
|
||||
GasUsed: 222222,
|
||||
Timestamp: 3333333333,
|
||||
ExtraData: fillByteSlice(4, 0x12),
|
||||
BaseFeePerGas: fillByteSlice(32, 0x34),
|
||||
BlockHash: fillByteSlice(common.HashLength, 0x56),
|
||||
TransactionsRoot: fillByteSlice(32, 0x78),
|
||||
WithdrawalsRoot: fillByteSlice(32, 0x99),
|
||||
}
|
||||
|
||||
result, err := ExecutionPayloadHeaderCapellaFromConsensus(capellaHeader)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, hexutil.Encode(capellaHeader.ParentHash), result.ParentHash)
|
||||
require.DeepEqual(t, hexutil.Encode(capellaHeader.WithdrawalsRoot), result.WithdrawalsRoot)
|
||||
}
|
||||
|
||||
func TestExecutionPayloadHeaderCapella_ToConsensus_HappyPath(t *testing.T) {
|
||||
header := &ExecutionPayloadHeaderCapella{
|
||||
ParentHash: hexutil.Encode(fillByteSlice(common.HashLength, 0xaa)),
|
||||
FeeRecipient: hexutil.Encode(fillByteSlice(20, 0xbb)),
|
||||
StateRoot: hexutil.Encode(fillByteSlice(32, 0xcc)),
|
||||
ReceiptsRoot: hexutil.Encode(fillByteSlice(32, 0xdd)),
|
||||
LogsBloom: hexutil.Encode(fillByteSlice(256, 0xee)),
|
||||
PrevRandao: hexutil.Encode(fillByteSlice(32, 0xff)),
|
||||
BlockNumber: "555",
|
||||
GasLimit: "1111111",
|
||||
GasUsed: "222222",
|
||||
Timestamp: "3333333333",
|
||||
ExtraData: "0x1234abcd",
|
||||
BaseFeePerGas: "1234",
|
||||
BlockHash: hexutil.Encode(fillByteSlice(common.HashLength, 0x56)),
|
||||
TransactionsRoot: hexutil.Encode(fillByteSlice(32, 0x78)),
|
||||
WithdrawalsRoot: hexutil.Encode(fillByteSlice(32, 0x99)),
|
||||
}
|
||||
|
||||
result, err := header.ToConsensus()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, hexutil.Encode(result.ParentHash), header.ParentHash)
|
||||
require.DeepEqual(t, hexutil.Encode(result.FeeRecipient), header.FeeRecipient)
|
||||
require.DeepEqual(t, hexutil.Encode(result.StateRoot), header.StateRoot)
|
||||
require.DeepEqual(t, hexutil.Encode(result.ReceiptsRoot), header.ReceiptsRoot)
|
||||
require.DeepEqual(t, hexutil.Encode(result.WithdrawalsRoot), header.WithdrawalsRoot)
|
||||
}
|
||||
|
||||
func TestExecutionPayloadHeaderDenebFromConsensus_HappyPath(t *testing.T) {
|
||||
denebHeader := &enginev1.ExecutionPayloadHeaderDeneb{
|
||||
ParentHash: fillByteSlice(common.HashLength, 0xaa),
|
||||
FeeRecipient: fillByteSlice(20, 0xbb),
|
||||
StateRoot: fillByteSlice(32, 0xcc),
|
||||
ReceiptsRoot: fillByteSlice(32, 0xdd),
|
||||
LogsBloom: fillByteSlice(256, 0xee),
|
||||
PrevRandao: fillByteSlice(32, 0xff),
|
||||
BlockNumber: 999,
|
||||
GasLimit: 5000000,
|
||||
GasUsed: 2500000,
|
||||
Timestamp: 4444444444,
|
||||
ExtraData: fillByteSlice(4, 0x12),
|
||||
BaseFeePerGas: fillByteSlice(32, 0x34),
|
||||
BlockHash: fillByteSlice(common.HashLength, 0x56),
|
||||
TransactionsRoot: fillByteSlice(32, 0x78),
|
||||
WithdrawalsRoot: fillByteSlice(32, 0x99),
|
||||
BlobGasUsed: 1234,
|
||||
ExcessBlobGas: 5678,
|
||||
}
|
||||
|
||||
result, err := ExecutionPayloadHeaderDenebFromConsensus(denebHeader)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, hexutil.Encode(denebHeader.ParentHash), result.ParentHash)
|
||||
require.DeepEqual(t, hexutil.Encode(denebHeader.FeeRecipient), result.FeeRecipient)
|
||||
require.DeepEqual(t, hexutil.Encode(denebHeader.StateRoot), result.StateRoot)
|
||||
require.DeepEqual(t, fmt.Sprintf("%d", denebHeader.BlobGasUsed), result.BlobGasUsed)
|
||||
}
|
||||
|
||||
func TestExecutionPayloadHeaderDeneb_ToConsensus_HappyPath(t *testing.T) {
|
||||
header := &ExecutionPayloadHeaderDeneb{
|
||||
ParentHash: hexutil.Encode(fillByteSlice(common.HashLength, 0xaa)),
|
||||
FeeRecipient: hexutil.Encode(fillByteSlice(20, 0xbb)),
|
||||
StateRoot: hexutil.Encode(fillByteSlice(32, 0xcc)),
|
||||
ReceiptsRoot: hexutil.Encode(fillByteSlice(32, 0xdd)),
|
||||
LogsBloom: hexutil.Encode(fillByteSlice(256, 0xee)),
|
||||
PrevRandao: hexutil.Encode(fillByteSlice(32, 0xff)),
|
||||
BlockNumber: "999",
|
||||
GasLimit: "5000000",
|
||||
GasUsed: "2500000",
|
||||
Timestamp: "4444444444",
|
||||
ExtraData: "0x1234abcd",
|
||||
BaseFeePerGas: "1234",
|
||||
BlockHash: hexutil.Encode(fillByteSlice(common.HashLength, 0x56)),
|
||||
TransactionsRoot: hexutil.Encode(fillByteSlice(32, 0x78)),
|
||||
WithdrawalsRoot: hexutil.Encode(fillByteSlice(32, 0x99)),
|
||||
BlobGasUsed: "1234",
|
||||
ExcessBlobGas: "5678",
|
||||
}
|
||||
|
||||
result, err := header.ToConsensus()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, hexutil.Encode(result.ParentHash), header.ParentHash)
|
||||
require.DeepEqual(t, result.BlobGasUsed, uint64(1234))
|
||||
require.DeepEqual(t, result.ExcessBlobGas, uint64(5678))
|
||||
require.DeepEqual(t, result.BlockNumber, uint64(999))
|
||||
}
|
||||
|
||||
func TestWithdrawalRequestsFromConsensus_HappyPath(t *testing.T) {
|
||||
consensusRequests := []*enginev1.WithdrawalRequest{
|
||||
{
|
||||
SourceAddress: fillByteSlice(20, 0xbb),
|
||||
ValidatorPubkey: fillByteSlice(48, 0xbb),
|
||||
Amount: 12345,
|
||||
},
|
||||
{
|
||||
SourceAddress: fillByteSlice(20, 0xcc),
|
||||
ValidatorPubkey: fillByteSlice(48, 0xcc),
|
||||
Amount: 54321,
|
||||
},
|
||||
}
|
||||
|
||||
result := WithdrawalRequestsFromConsensus(consensusRequests)
|
||||
require.DeepEqual(t, len(result), len(consensusRequests))
|
||||
require.DeepEqual(t, result[0].Amount, fmt.Sprintf("%d", consensusRequests[0].Amount))
|
||||
}
|
||||
|
||||
func TestWithdrawalRequestFromConsensus_HappyPath(t *testing.T) {
|
||||
req := &enginev1.WithdrawalRequest{
|
||||
SourceAddress: fillByteSlice(20, 0xbb),
|
||||
ValidatorPubkey: fillByteSlice(48, 0xbb),
|
||||
Amount: 42,
|
||||
}
|
||||
result := WithdrawalRequestFromConsensus(req)
|
||||
require.NotNil(t, result)
|
||||
require.DeepEqual(t, result.SourceAddress, hexutil.Encode(fillByteSlice(20, 0xbb)))
|
||||
}
|
||||
|
||||
func TestWithdrawalRequest_ToConsensus_HappyPath(t *testing.T) {
|
||||
withdrawalReq := &WithdrawalRequest{
|
||||
SourceAddress: hexutil.Encode(fillByteSlice(20, 111)),
|
||||
ValidatorPubkey: hexutil.Encode(fillByteSlice(48, 123)),
|
||||
Amount: "12345",
|
||||
}
|
||||
result, err := withdrawalReq.ToConsensus()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, result.Amount, uint64(12345))
|
||||
}
|
||||
|
||||
func TestConsolidationRequestsFromConsensus_HappyPath(t *testing.T) {
|
||||
consensusRequests := []*enginev1.ConsolidationRequest{
|
||||
{
|
||||
SourceAddress: fillByteSlice(20, 111),
|
||||
SourcePubkey: fillByteSlice(48, 112),
|
||||
TargetPubkey: fillByteSlice(48, 113),
|
||||
},
|
||||
}
|
||||
result := ConsolidationRequestsFromConsensus(consensusRequests)
|
||||
require.DeepEqual(t, len(result), len(consensusRequests))
|
||||
require.DeepEqual(t, result[0].SourceAddress, "0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f")
|
||||
}
|
||||
|
||||
func TestDepositRequestsFromConsensus_HappyPath(t *testing.T) {
|
||||
ds := []*enginev1.DepositRequest{
|
||||
{
|
||||
Pubkey: fillByteSlice(48, 0xbb),
|
||||
WithdrawalCredentials: fillByteSlice(32, 0xdd),
|
||||
Amount: 98765,
|
||||
Signature: fillByteSlice(96, 0xff),
|
||||
Index: 111,
|
||||
},
|
||||
}
|
||||
result := DepositRequestsFromConsensus(ds)
|
||||
require.DeepEqual(t, len(result), len(ds))
|
||||
require.DeepEqual(t, result[0].Amount, "98765")
|
||||
}
|
||||
|
||||
func TestDepositRequest_ToConsensus_HappyPath(t *testing.T) {
|
||||
req := &DepositRequest{
|
||||
Pubkey: hexutil.Encode(fillByteSlice(48, 0xbb)),
|
||||
WithdrawalCredentials: hexutil.Encode(fillByteSlice(32, 0xaa)),
|
||||
Amount: "123",
|
||||
Signature: hexutil.Encode(fillByteSlice(96, 0xdd)),
|
||||
Index: "456",
|
||||
}
|
||||
|
||||
result, err := req.ToConsensus()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, result.Amount, uint64(123))
|
||||
require.DeepEqual(t, result.Signature, fillByteSlice(96, 0xdd))
|
||||
}
|
||||
|
||||
func TestExecutionRequestsFromConsensus_HappyPath(t *testing.T) {
|
||||
er := &enginev1.ExecutionRequests{
|
||||
Deposits: []*enginev1.DepositRequest{
|
||||
{
|
||||
Pubkey: fillByteSlice(48, 0xba),
|
||||
WithdrawalCredentials: fillByteSlice(32, 0xaa),
|
||||
Amount: 33,
|
||||
Signature: fillByteSlice(96, 0xff),
|
||||
Index: 44,
|
||||
},
|
||||
},
|
||||
Withdrawals: []*enginev1.WithdrawalRequest{
|
||||
{
|
||||
SourceAddress: fillByteSlice(20, 0xaa),
|
||||
ValidatorPubkey: fillByteSlice(48, 0xba),
|
||||
Amount: 555,
|
||||
},
|
||||
},
|
||||
Consolidations: []*enginev1.ConsolidationRequest{
|
||||
{
|
||||
SourceAddress: fillByteSlice(20, 0xdd),
|
||||
SourcePubkey: fillByteSlice(48, 0xdd),
|
||||
TargetPubkey: fillByteSlice(48, 0xcc),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result := ExecutionRequestsFromConsensus(er)
|
||||
require.NotNil(t, result)
|
||||
require.Equal(t, 1, len(result.Deposits))
|
||||
require.Equal(t, "33", result.Deposits[0].Amount)
|
||||
require.Equal(t, 1, len(result.Withdrawals))
|
||||
require.Equal(t, "555", result.Withdrawals[0].Amount)
|
||||
require.Equal(t, 1, len(result.Consolidations))
|
||||
require.Equal(t, "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", result.Consolidations[0].TargetPubkey)
|
||||
}
|
||||
|
||||
func TestExecutionRequests_ToConsensus_HappyPath(t *testing.T) {
|
||||
execReq := &ExecutionRequests{
|
||||
Deposits: []*DepositRequest{
|
||||
{
|
||||
Pubkey: hexutil.Encode(fillByteSlice(48, 0xbb)),
|
||||
WithdrawalCredentials: hexutil.Encode(fillByteSlice(32, 0xaa)),
|
||||
Amount: "33",
|
||||
Signature: hexutil.Encode(fillByteSlice(96, 0xff)),
|
||||
Index: "44",
|
||||
},
|
||||
},
|
||||
Withdrawals: []*WithdrawalRequest{
|
||||
{
|
||||
SourceAddress: hexutil.Encode(fillByteSlice(20, 0xdd)),
|
||||
ValidatorPubkey: hexutil.Encode(fillByteSlice(48, 0xbb)),
|
||||
Amount: "555",
|
||||
},
|
||||
},
|
||||
Consolidations: []*ConsolidationRequest{
|
||||
{
|
||||
SourceAddress: hexutil.Encode(fillByteSlice(20, 0xcc)),
|
||||
SourcePubkey: hexutil.Encode(fillByteSlice(48, 0xbb)),
|
||||
TargetPubkey: hexutil.Encode(fillByteSlice(48, 0xcc)),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result, err := execReq.ToConsensus()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(result.Deposits))
|
||||
require.Equal(t, uint64(33), result.Deposits[0].Amount)
|
||||
require.Equal(t, 1, len(result.Withdrawals))
|
||||
require.Equal(t, uint64(555), result.Withdrawals[0].Amount)
|
||||
require.Equal(t, 1, len(result.Consolidations))
|
||||
require.DeepEqual(t, fillByteSlice(48, 0xcc), result.Consolidations[0].TargetPubkey)
|
||||
}
|
||||
@@ -250,3 +250,17 @@ type ChainHead struct {
|
||||
PreviousJustifiedBlockRoot string `json:"previous_justified_block_root"`
|
||||
OptimisticStatus bool `json:"optimistic_status"`
|
||||
}
|
||||
|
||||
type GetPendingDepositsResponse struct {
|
||||
Version string `json:"version"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
Data []*PendingDeposit `json:"data"`
|
||||
}
|
||||
|
||||
type GetPendingPartialWithdrawalsResponse struct {
|
||||
Version string `json:"version"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
Data []*PendingPartialWithdrawal `json:"data"`
|
||||
}
|
||||
|
||||
@@ -54,4 +54,5 @@ type ForkChoiceNodeExtraData struct {
|
||||
Balance string `json:"balance"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
TimeStamp string `json:"timestamp"`
|
||||
Target string `json:"target"`
|
||||
}
|
||||
|
||||
@@ -244,26 +244,6 @@ type Withdrawal struct {
|
||||
Amount string `json:"amount"`
|
||||
}
|
||||
|
||||
type DepositRequest struct {
|
||||
Pubkey string `json:"pubkey"`
|
||||
WithdrawalCredentials string `json:"withdrawal_credentials"`
|
||||
Amount string `json:"amount"`
|
||||
Signature string `json:"signature"`
|
||||
Index string `json:"index"`
|
||||
}
|
||||
|
||||
type WithdrawalRequest struct {
|
||||
SourceAddress string `json:"source_address"`
|
||||
ValidatorPubkey string `json:"validator_pubkey"`
|
||||
Amount string `json:"amount"`
|
||||
}
|
||||
|
||||
type ConsolidationRequest struct {
|
||||
SourceAddress string `json:"source_address"`
|
||||
SourcePubkey string `json:"source_pubkey"`
|
||||
TargetPubkey string `json:"target_pubkey"`
|
||||
}
|
||||
|
||||
type PendingDeposit struct {
|
||||
Pubkey string `json:"pubkey"`
|
||||
WithdrawalCredentials string `json:"withdrawal_credentials"`
|
||||
|
||||
@@ -125,7 +125,7 @@ func getChan(key string) chan byte {
|
||||
|
||||
// Return a new string with unique elements.
|
||||
func unique(arr []string) []string {
|
||||
if arr == nil || len(arr) <= 1 {
|
||||
if len(arr) <= 1 {
|
||||
return arr
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ go_library(
|
||||
"receive_blob.go",
|
||||
"receive_block.go",
|
||||
"service.go",
|
||||
"setup_forchoice.go",
|
||||
"tracked_proposer.go",
|
||||
"weak_subjectivity_checks.go",
|
||||
],
|
||||
@@ -96,6 +97,7 @@ go_library(
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@org_golang_x_sync//errgroup:go_default_library",
|
||||
],
|
||||
@@ -154,6 +156,7 @@ go_test(
|
||||
"//beacon-chain/forkchoice/doubly-linked-tree:go_default_library",
|
||||
"//beacon-chain/forkchoice/types:go_default_library",
|
||||
"//beacon-chain/operations/attestations:go_default_library",
|
||||
"//beacon-chain/operations/attestations/kv:go_default_library",
|
||||
"//beacon-chain/operations/blstoexec:go_default_library",
|
||||
"//beacon-chain/operations/slashings:go_default_library",
|
||||
"//beacon-chain/operations/voluntaryexits:go_default_library",
|
||||
@@ -185,6 +188,7 @@ go_test(
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_holiman_uint256//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
|
||||
@@ -103,7 +103,7 @@ func (s *Service) forkchoiceUpdateWithExecution(ctx context.Context, args *fcuCo
|
||||
}
|
||||
|
||||
// Only need to prune attestations from pool if the head has changed.
|
||||
if err := s.pruneAttsFromPool(args.headBlock); err != nil {
|
||||
if err := s.pruneAttsFromPool(s.ctx, args.headState, args.headBlock); err != nil {
|
||||
log.WithError(err).Error("could not prune attestations from pool")
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -69,6 +69,22 @@ func logStateTransitionData(b interfaces.ReadOnlyBeaconBlock) error {
|
||||
log = log.WithField("kzgCommitmentCount", len(kzgs))
|
||||
}
|
||||
}
|
||||
if b.Version() >= version.Electra {
|
||||
eReqs, err := b.Body().ExecutionRequests()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get execution requests")
|
||||
} else {
|
||||
if len(eReqs.Deposits) > 0 {
|
||||
log = log.WithField("depositRequestCount", len(eReqs.Deposits))
|
||||
}
|
||||
if len(eReqs.Consolidations) > 0 {
|
||||
log = log.WithField("consolidationRequestCount", len(eReqs.Consolidations))
|
||||
}
|
||||
if len(eReqs.Withdrawals) > 0 {
|
||||
log = log.WithField("withdrawalRequestCount", len(eReqs.Withdrawals))
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Info("Finished applying state transition")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -213,3 +213,10 @@ func WithSyncChecker(checker Checker) Option {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithSlasherEnabled(enabled bool) Option {
|
||||
return func(s *Service) error {
|
||||
s.slasherEnabled = enabled
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func (s *Service) OnAttestation(ctx context.Context, a ethpb.Att, disparity time
|
||||
}
|
||||
|
||||
// Use the target state to verify attesting indices are valid.
|
||||
committees, err := helpers.AttestationCommittees(ctx, baseState, a)
|
||||
committees, err := helpers.AttestationCommitteesFromState(ctx, baseState, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
|
||||
coreTime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
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"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||
@@ -368,7 +370,7 @@ func (s *Service) handleEpochBoundary(ctx context.Context, slot primitives.Slot,
|
||||
func (s *Service) handleBlockAttestations(ctx context.Context, blk interfaces.ReadOnlyBeaconBlock, st state.BeaconState) error {
|
||||
// Feed in block's attestations to fork choice store.
|
||||
for _, a := range blk.Body().Attestations() {
|
||||
committees, err := helpers.AttestationCommittees(ctx, st, a)
|
||||
committees, err := helpers.AttestationCommitteesFromState(ctx, st, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -419,27 +421,98 @@ func (s *Service) savePostStateInfo(ctx context.Context, r [32]byte, b interface
|
||||
return nil
|
||||
}
|
||||
|
||||
// This removes the attestations in block `b` from the attestation mem pool.
|
||||
func (s *Service) pruneAttsFromPool(headBlock interfaces.ReadOnlySignedBeaconBlock) error {
|
||||
atts := headBlock.Block().Body().Attestations()
|
||||
for _, att := range atts {
|
||||
if features.Get().EnableExperimentalAttestationPool {
|
||||
if err := s.cfg.AttestationCache.DeleteCovered(att); err != nil {
|
||||
return errors.Wrap(err, "could not delete attestation")
|
||||
}
|
||||
} else if att.IsAggregated() {
|
||||
if err := s.cfg.AttPool.DeleteAggregatedAttestation(att); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := s.cfg.AttPool.DeleteUnaggregatedAttestation(att); err != nil {
|
||||
return err
|
||||
}
|
||||
// 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) 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 {
|
||||
switch {
|
||||
case !att.IsAggregated():
|
||||
return s.cfg.AttPool.DeleteUnaggregatedAttestation(att)
|
||||
case att.Version() == version.Phase0:
|
||||
if features.Get().EnableExperimentalAttestationPool {
|
||||
return errors.Wrap(s.cfg.AttestationCache.DeleteCovered(att), "could not delete covered attestation")
|
||||
}
|
||||
return errors.Wrap(s.cfg.AttPool.DeleteAggregatedAttestation(att), "could not delete aggregated attestation")
|
||||
default:
|
||||
return s.pruneCoveredElectraAttsFromPool(ctx, headState, att)
|
||||
}
|
||||
}
|
||||
|
||||
// pruneCoveredElectraAttsFromPool handles removing aggregated Electra attestations from the pool after receiving a block.
|
||||
// Because in Electra block attestations can combine aggregates for multiple committees, comparing attestation bits
|
||||
// of a block attestation with attestations bits of an aggregate can cause unexpected results, leading to covered
|
||||
// aggregates not being removed from the pool.
|
||||
//
|
||||
// To make sure aggregates are removed, we decompose the block attestation into dummy aggregates, with each
|
||||
// aggregate accounting for one committee. This allows us to compare aggregates in the same way it's done for
|
||||
// Phase0. Even though we can't provide a valid signature for the dummy aggregate, it does not matter because
|
||||
// signatures play no part in pruning attestations.
|
||||
func (s *Service) pruneCoveredElectraAttsFromPool(ctx context.Context, headState state.BeaconState, att ethpb.Att) error {
|
||||
if att.Version() == version.Phase0 {
|
||||
log.Error("Called pruneCoveredElectraAttsFromPool with a Phase0 attestation")
|
||||
return nil
|
||||
}
|
||||
|
||||
// We don't want to recompute committees. If they are not cached already,
|
||||
// we allow attestations to stay in the pool. If these attestations are
|
||||
// included in a later block, they will be redundant. But given that
|
||||
// they were not cached in the first place, it's unlikely that they
|
||||
// will be chosen into a block.
|
||||
ok, committees, err := helpers.AttestationCommitteesFromCache(ctx, headState, att)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get attestation committees")
|
||||
}
|
||||
if !ok {
|
||||
log.Debug("Attestation committees are not cached. Skipping attestation pruning.")
|
||||
return nil
|
||||
}
|
||||
|
||||
committeeIndices := att.CommitteeBitsVal().BitIndices()
|
||||
offset := uint64(0)
|
||||
|
||||
// Sanity check as this should never happen
|
||||
if len(committeeIndices) != len(committees) {
|
||||
return errors.New("committee indices and committees have different lengths")
|
||||
}
|
||||
|
||||
for i, c := range committees {
|
||||
ab := bitfield.NewBitlist(uint64(len(c)))
|
||||
for j := uint64(0); j < uint64(len(c)); j++ {
|
||||
ab.SetBitAt(j, att.GetAggregationBits().BitAt(j+offset))
|
||||
}
|
||||
|
||||
cb := primitives.NewAttestationCommitteeBits()
|
||||
cb.SetBitAt(uint64(committeeIndices[i]), true)
|
||||
|
||||
a := ðpb.AttestationElectra{
|
||||
AggregationBits: ab,
|
||||
Data: att.GetData(),
|
||||
CommitteeBits: cb,
|
||||
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
}
|
||||
|
||||
if features.Get().EnableExperimentalAttestationPool {
|
||||
if err = s.cfg.AttestationCache.DeleteCovered(a); err != nil {
|
||||
return errors.Wrap(err, "could not delete covered attestation")
|
||||
}
|
||||
} else if err = s.cfg.AttPool.DeleteAggregatedAttestation(a); err != nil {
|
||||
return errors.Wrap(err, "could not delete aggregated attestation")
|
||||
}
|
||||
|
||||
offset += uint64(len(c))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateMergeTransitionBlock validates the merge transition block.
|
||||
func (s *Service) validateMergeTransitionBlock(ctx context.Context, stateVersion int, stateHeader interfaces.ExecutionData, blk interfaces.ReadOnlySignedBeaconBlock) error {
|
||||
// Skip validation if block is older than Bellatrix.
|
||||
|
||||
@@ -12,8 +12,10 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
gethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
|
||||
"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/signing"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
|
||||
@@ -25,6 +27,7 @@ import (
|
||||
mockExecution "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/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/attestations/kv"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/features"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
@@ -45,6 +48,90 @@ import (
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
func Test_pruneAttsFromPool_Electra(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.TargetCommitteeSize = 8
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
s := Service{
|
||||
cfg: &config{
|
||||
AttPool: kv.NewAttCaches(),
|
||||
},
|
||||
}
|
||||
|
||||
data := ðpb.AttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Source: ðpb.Checkpoint{Root: make([]byte, 32)},
|
||||
Target: ðpb.Checkpoint{Root: make([]byte, 32)},
|
||||
}
|
||||
|
||||
cb := primitives.NewAttestationCommitteeBits()
|
||||
cb.SetBitAt(0, true)
|
||||
att1 := ðpb.AttestationElectra{
|
||||
AggregationBits: bitfield.Bitlist{0b11110111, 0b00000001},
|
||||
Data: data,
|
||||
Signature: make([]byte, 96),
|
||||
CommitteeBits: cb,
|
||||
}
|
||||
|
||||
cb = primitives.NewAttestationCommitteeBits()
|
||||
cb.SetBitAt(1, true)
|
||||
att2 := ðpb.AttestationElectra{
|
||||
AggregationBits: bitfield.Bitlist{0b11110111, 0b00000001},
|
||||
Data: data,
|
||||
Signature: make([]byte, 96),
|
||||
CommitteeBits: cb,
|
||||
}
|
||||
|
||||
cb = primitives.NewAttestationCommitteeBits()
|
||||
cb.SetBitAt(3, true)
|
||||
att3 := ðpb.AttestationElectra{
|
||||
AggregationBits: bitfield.Bitlist{0b11110111, 0b00000001},
|
||||
Data: data,
|
||||
Signature: make([]byte, 96),
|
||||
CommitteeBits: cb,
|
||||
}
|
||||
|
||||
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 := ðpb.AttestationElectra{
|
||||
AggregationBits: bitfield.Bitlist{0b11110111, 0b11110111, 0b00000001},
|
||||
Data: data,
|
||||
Signature: make([]byte, 96),
|
||||
CommitteeBits: cb,
|
||||
}
|
||||
bl := ðpb.SignedBeaconBlockElectra{
|
||||
Block: ðpb.BeaconBlockElectra{
|
||||
Body: ðpb.BeaconBlockBodyElectra{
|
||||
Attestations: []*ethpb.AttestationElectra{onChainAtt},
|
||||
},
|
||||
},
|
||||
Signature: make([]byte, 96),
|
||||
}
|
||||
rob, err := consensusblocks.NewSignedBeaconBlock(bl)
|
||||
require.NoError(t, err)
|
||||
st, _ := util.DeterministicGenesisStateElectra(t, 1024)
|
||||
committees, err := helpers.BeaconCommittees(ctx, st, 0)
|
||||
require.NoError(t, err)
|
||||
// Sanity check to make sure the on-chain att will be decomposed
|
||||
// into the correct number of aggregates.
|
||||
require.Equal(t, 4, len(committees))
|
||||
|
||||
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])
|
||||
}
|
||||
|
||||
func TestStore_OnBlockBatch(t *testing.T) {
|
||||
service, tr := minimalTestService(t)
|
||||
ctx := tr.ctx
|
||||
@@ -840,7 +927,7 @@ func TestRemoveBlockAttestationsInPool(t *testing.T) {
|
||||
require.NoError(t, service.cfg.AttPool.SaveAggregatedAttestations(atts))
|
||||
wsb, err := consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.pruneAttsFromPool(wsb))
|
||||
require.NoError(t, service.pruneAttsFromPool(context.Background(), nil /* state not needed pre-Electra */, wsb))
|
||||
require.Equal(t, 0, service.cfg.AttPool.AggregatedAttestationCount())
|
||||
}
|
||||
|
||||
@@ -2039,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
|
||||
|
||||
@@ -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"
|
||||
@@ -121,7 +120,7 @@ func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySig
|
||||
return err
|
||||
}
|
||||
// If slasher is configured, forward the attestations in the block via an event feed for processing.
|
||||
if features.Get().EnableSlasher {
|
||||
if s.slasherEnabled {
|
||||
go s.sendBlockAttestationsToSlasher(blockCopy, preState)
|
||||
}
|
||||
|
||||
@@ -548,7 +547,7 @@ func (s *Service) sendBlockAttestationsToSlasher(signed interfaces.ReadOnlySigne
|
||||
// is done in the background to avoid adding more load to this critical code path.
|
||||
ctx := context.TODO()
|
||||
for _, att := range signed.Block().Body().Attestations() {
|
||||
committees, err := helpers.AttestationCommittees(ctx, preState, att)
|
||||
committees, err := helpers.AttestationCommitteesFromState(ctx, preState, att)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get attestation committees")
|
||||
return
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
@@ -23,7 +22,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db/filesystem"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/execution"
|
||||
f "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice"
|
||||
forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/attestations"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/blstoexec"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/slashings"
|
||||
@@ -32,7 +30,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/startup"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||
@@ -65,6 +62,7 @@ type Service struct {
|
||||
blobNotifiers *blobNotifierMap
|
||||
blockBeingSynced *currentlySyncingBlock
|
||||
blobStorage *filesystem.BlobStorage
|
||||
slasherEnabled bool
|
||||
}
|
||||
|
||||
// config options for the service.
|
||||
@@ -269,69 +267,18 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error {
|
||||
return err
|
||||
}
|
||||
s.originBlockRoot = originRoot
|
||||
|
||||
if err := s.initializeHeadFromDB(s.ctx); err != nil {
|
||||
return errors.Wrap(err, "could not set up chain info")
|
||||
st, err := s.cfg.StateGen.Resume(s.ctx, s.cfg.FinalizedStateAtStartUp)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized state from db")
|
||||
}
|
||||
spawnCountdownIfPreGenesis(s.ctx, s.genesisTime, s.cfg.BeaconDB)
|
||||
|
||||
justified, err := s.cfg.BeaconDB.JustifiedCheckpoint(s.ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get justified checkpoint")
|
||||
}
|
||||
if justified == nil {
|
||||
return errNilJustifiedCheckpoint
|
||||
}
|
||||
finalized, err := s.cfg.BeaconDB.FinalizedCheckpoint(s.ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized checkpoint")
|
||||
}
|
||||
if finalized == nil {
|
||||
return errNilFinalizedCheckpoint
|
||||
}
|
||||
|
||||
fRoot := s.ensureRootNotZeros(bytesutil.ToBytes32(finalized.Root))
|
||||
s.cfg.ForkChoiceStore.Lock()
|
||||
defer s.cfg.ForkChoiceStore.Unlock()
|
||||
if err := s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(s.ctx, &forkchoicetypes.Checkpoint{Epoch: justified.Epoch,
|
||||
Root: bytesutil.ToBytes32(justified.Root)}); err != nil {
|
||||
return errors.Wrap(err, "could not update forkchoice's justified checkpoint")
|
||||
}
|
||||
if err := s.cfg.ForkChoiceStore.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: finalized.Epoch,
|
||||
Root: bytesutil.ToBytes32(finalized.Root)}); err != nil {
|
||||
return errors.Wrap(err, "could not update forkchoice's finalized checkpoint")
|
||||
}
|
||||
s.cfg.ForkChoiceStore.SetGenesisTime(uint64(s.genesisTime.Unix()))
|
||||
|
||||
st, err := s.cfg.StateGen.StateByRoot(s.ctx, fRoot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized checkpoint state")
|
||||
}
|
||||
finalizedBlock, err := s.cfg.BeaconDB.Block(s.ctx, fRoot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized checkpoint block")
|
||||
}
|
||||
roblock, err := blocks.NewROBlockWithRoot(finalizedBlock, fRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.cfg.ForkChoiceStore.InsertNode(s.ctx, st, roblock); err != nil {
|
||||
return errors.Wrap(err, "could not insert finalized block to forkchoice")
|
||||
}
|
||||
if !features.Get().EnableStartOptimistic {
|
||||
lastValidatedCheckpoint, err := s.cfg.BeaconDB.LastValidatedCheckpoint(s.ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get last validated checkpoint")
|
||||
}
|
||||
if bytes.Equal(finalized.Root, lastValidatedCheckpoint.Root) {
|
||||
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(s.ctx, fRoot); err != nil {
|
||||
return errors.Wrap(err, "could not set finalized block as validated")
|
||||
}
|
||||
}
|
||||
if err := s.setupForkchoice(st); err != nil {
|
||||
return errors.Wrap(err, "could not set up forkchoice")
|
||||
}
|
||||
// not attempting to save initial sync blocks here, because there shouldn't be any until
|
||||
// after the statefeed.Initialized event is fired (below)
|
||||
if err := s.wsVerifier.VerifyWeakSubjectivity(s.ctx, finalized.Epoch); err != nil {
|
||||
cp := s.FinalizedCheckpt()
|
||||
if err := s.wsVerifier.VerifyWeakSubjectivity(s.ctx, cp.Epoch); err != nil {
|
||||
// Exit run time if the node failed to verify weak subjectivity checkpoint.
|
||||
return errors.Wrap(err, "could not verify initial checkpoint provided for chain sync")
|
||||
}
|
||||
@@ -340,7 +287,6 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error {
|
||||
if err := s.clockSetter.SetClock(startup.NewClock(s.genesisTime, vr)); err != nil {
|
||||
return errors.Wrap(err, "failed to initialize blockchain service")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -373,23 +319,10 @@ func (s *Service) originRootFromSavedState(ctx context.Context) ([32]byte, error
|
||||
// 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) initializeHeadFromDB(ctx context.Context) error {
|
||||
finalized, err := s.cfg.BeaconDB.FinalizedCheckpoint(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized checkpoint from db")
|
||||
}
|
||||
if finalized == nil {
|
||||
// This should never happen. At chain start, the finalized checkpoint
|
||||
// would be the genesis state and block.
|
||||
return errors.New("no finalized epoch in the database")
|
||||
}
|
||||
finalizedRoot := s.ensureRootNotZeros(bytesutil.ToBytes32(finalized.Root))
|
||||
var finalizedState state.BeaconState
|
||||
|
||||
finalizedState, err = s.cfg.StateGen.Resume(ctx, s.cfg.FinalizedStateAtStartUp)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized state from db")
|
||||
}
|
||||
func (s *Service) initializeHeadFromDB(ctx context.Context, finalizedState state.BeaconState) error {
|
||||
cp := s.FinalizedCheckpt()
|
||||
fRoot := [32]byte(cp.Root)
|
||||
finalizedRoot := s.ensureRootNotZeros(fRoot)
|
||||
|
||||
if finalizedState == nil || finalizedState.IsNil() {
|
||||
return errors.New("finalized state can't be nil")
|
||||
|
||||
84
beacon-chain/blockchain/setup_forchoice.go
Normal file
84
beacon-chain/blockchain/setup_forchoice.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"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/encoding/bytesutil"
|
||||
)
|
||||
|
||||
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.setupForkchoiceRoot(st); err != nil {
|
||||
return errors.Wrap(err, "could not set up forkchoice root")
|
||||
}
|
||||
if err := s.initializeHeadFromDB(s.ctx, st); err != nil {
|
||||
return errors.Wrap(err, "could not initialize head from db")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) setupForkchoiceRoot(st state.BeaconState) error {
|
||||
cp := s.FinalizedCheckpt()
|
||||
fRoot := s.ensureRootNotZeros([32]byte(cp.Root))
|
||||
finalizedBlock, err := s.cfg.BeaconDB.Block(s.ctx, fRoot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized checkpoint block")
|
||||
}
|
||||
roblock, err := blocks.NewROBlockWithRoot(finalizedBlock, fRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.cfg.ForkChoiceStore.InsertNode(s.ctx, st, roblock); err != nil {
|
||||
return errors.Wrap(err, "could not insert finalized block to forkchoice")
|
||||
}
|
||||
if !features.Get().EnableStartOptimistic {
|
||||
lastValidatedCheckpoint, err := s.cfg.BeaconDB.LastValidatedCheckpoint(s.ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get last validated checkpoint")
|
||||
}
|
||||
if bytes.Equal(fRoot[:], lastValidatedCheckpoint.Root) {
|
||||
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(s.ctx, fRoot); err != nil {
|
||||
return errors.Wrap(err, "could not set finalized block as validated")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) setupForkchoiceCheckpoints() error {
|
||||
justified, err := s.cfg.BeaconDB.JustifiedCheckpoint(s.ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get justified checkpoint")
|
||||
}
|
||||
if justified == nil {
|
||||
return errNilJustifiedCheckpoint
|
||||
}
|
||||
finalized, err := s.cfg.BeaconDB.FinalizedCheckpoint(s.ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized checkpoint")
|
||||
}
|
||||
if finalized == nil {
|
||||
return errNilFinalizedCheckpoint
|
||||
}
|
||||
|
||||
fRoot := s.ensureRootNotZeros(bytesutil.ToBytes32(finalized.Root))
|
||||
s.cfg.ForkChoiceStore.Lock()
|
||||
defer s.cfg.ForkChoiceStore.Unlock()
|
||||
if err := s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(s.ctx, &forkchoicetypes.Checkpoint{Epoch: justified.Epoch,
|
||||
Root: bytesutil.ToBytes32(justified.Root)}); err != nil {
|
||||
return errors.Wrap(err, "could not update forkchoice's justified checkpoint")
|
||||
}
|
||||
if err := s.cfg.ForkChoiceStore.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: finalized.Epoch,
|
||||
Root: fRoot}); err != nil {
|
||||
return errors.Wrap(err, "could not update forkchoice's finalized checkpoint")
|
||||
}
|
||||
s.cfg.ForkChoiceStore.SetGenesisTime(uint64(s.genesisTime.Unix()))
|
||||
return nil
|
||||
}
|
||||
1
beacon-chain/cache/BUILD.bazel
vendored
1
beacon-chain/cache/BUILD.bazel
vendored
@@ -84,6 +84,7 @@ go_test(
|
||||
"sync_committee_head_state_test.go",
|
||||
"sync_committee_test.go",
|
||||
"sync_subnet_ids_test.go",
|
||||
"tracked_validators_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
|
||||
138
beacon-chain/cache/tracked_validators.go
vendored
138
beacon-chain/cache/tracked_validators.go
vendored
@@ -1,49 +1,139 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type TrackedValidator struct {
|
||||
Active bool
|
||||
FeeRecipient primitives.ExecutionAddress
|
||||
Index primitives.ValidatorIndex
|
||||
}
|
||||
const (
|
||||
defaultExpiration = 1 * time.Hour
|
||||
cleanupInterval = 15 * time.Minute
|
||||
)
|
||||
|
||||
type TrackedValidatorsCache struct {
|
||||
sync.Mutex
|
||||
trackedValidators map[primitives.ValidatorIndex]TrackedValidator
|
||||
}
|
||||
type (
|
||||
TrackedValidator struct {
|
||||
Active bool
|
||||
FeeRecipient primitives.ExecutionAddress
|
||||
Index primitives.ValidatorIndex
|
||||
}
|
||||
|
||||
TrackedValidatorsCache struct {
|
||||
trackedValidators cache.Cache
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// Metrics.
|
||||
trackedValidatorsCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "tracked_validators_cache_miss",
|
||||
Help: "The number of tracked validators requests that are not present in the cache.",
|
||||
})
|
||||
|
||||
trackedValidatorsCacheTotal = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "tracked_validators_cache_total",
|
||||
Help: "The total number of tracked validators requests in the cache.",
|
||||
})
|
||||
|
||||
trackedValidatorsCacheCount = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "tracked_validators_cache_count",
|
||||
Help: "The number of tracked validators in the cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// NewTrackedValidatorsCache creates a new cache for tracking validators.
|
||||
func NewTrackedValidatorsCache() *TrackedValidatorsCache {
|
||||
return &TrackedValidatorsCache{
|
||||
trackedValidators: make(map[primitives.ValidatorIndex]TrackedValidator),
|
||||
trackedValidators: *cache.New(defaultExpiration, cleanupInterval),
|
||||
}
|
||||
}
|
||||
|
||||
// Validator retrieves a tracked validator from the cache (if present).
|
||||
func (t *TrackedValidatorsCache) Validator(index primitives.ValidatorIndex) (TrackedValidator, bool) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
val, ok := t.trackedValidators[index]
|
||||
return val, ok
|
||||
trackedValidatorsCacheTotal.Inc()
|
||||
|
||||
key := toCacheKey(index)
|
||||
item, ok := t.trackedValidators.Get(key)
|
||||
if !ok {
|
||||
trackedValidatorsCacheMiss.Inc()
|
||||
return TrackedValidator{}, false
|
||||
}
|
||||
|
||||
val, ok := item.(TrackedValidator)
|
||||
if !ok {
|
||||
logrus.Errorf("Failed to cast tracked validator from cache, got unexpected item type %T", item)
|
||||
return TrackedValidator{}, false
|
||||
}
|
||||
|
||||
return val, true
|
||||
}
|
||||
|
||||
// Set adds a tracked validator to the cache.
|
||||
func (t *TrackedValidatorsCache) Set(val TrackedValidator) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
t.trackedValidators[val.Index] = val
|
||||
key := toCacheKey(val.Index)
|
||||
t.trackedValidators.Set(key, val, cache.DefaultExpiration)
|
||||
}
|
||||
|
||||
// Delete removes a tracked validator from the cache.
|
||||
func (t *TrackedValidatorsCache) Prune() {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
t.trackedValidators = make(map[primitives.ValidatorIndex]TrackedValidator)
|
||||
t.trackedValidators.Flush()
|
||||
trackedValidatorsCacheCount.Set(0)
|
||||
}
|
||||
|
||||
// Validating returns true if there are at least one tracked validators in the cache.
|
||||
func (t *TrackedValidatorsCache) Validating() bool {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
return len(t.trackedValidators) > 0
|
||||
count := t.trackedValidators.ItemCount()
|
||||
trackedValidatorsCacheCount.Set(float64(count))
|
||||
|
||||
return count > 0
|
||||
}
|
||||
|
||||
// ItemCount returns the number of tracked validators in the cache.
|
||||
func (t *TrackedValidatorsCache) ItemCount() int {
|
||||
count := t.trackedValidators.ItemCount()
|
||||
trackedValidatorsCacheCount.Set(float64(count))
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
// Indices returns a map of validator indices that are being tracked.
|
||||
func (t *TrackedValidatorsCache) Indices() map[primitives.ValidatorIndex]bool {
|
||||
items := t.trackedValidators.Items()
|
||||
count := len(items)
|
||||
trackedValidatorsCacheCount.Set(float64(count))
|
||||
|
||||
indices := make(map[primitives.ValidatorIndex]bool, count)
|
||||
|
||||
for cacheKey := range items {
|
||||
index, err := fromCacheKey(cacheKey)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("Failed to get validator index from cache key")
|
||||
continue
|
||||
}
|
||||
|
||||
indices[index] = true
|
||||
}
|
||||
|
||||
return indices
|
||||
}
|
||||
|
||||
// toCacheKey creates a cache key from the validator index.
|
||||
func toCacheKey(validatorIndex primitives.ValidatorIndex) string {
|
||||
return strconv.FormatUint(uint64(validatorIndex), 10)
|
||||
}
|
||||
|
||||
// fromCacheKey gets the validator index from the cache key.
|
||||
func fromCacheKey(key string) (primitives.ValidatorIndex, error) {
|
||||
validatorIndex, err := strconv.ParseUint(key, 10, 64)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "parse Uint: %s", key)
|
||||
}
|
||||
|
||||
return primitives.ValidatorIndex(validatorIndex), nil
|
||||
}
|
||||
|
||||
79
beacon-chain/cache/tracked_validators_test.go
vendored
Normal file
79
beacon-chain/cache/tracked_validators_test.go
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
)
|
||||
|
||||
func mapEqual(a, b map[primitives.ValidatorIndex]bool) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for k, v := range a {
|
||||
if b[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func TestTrackedValidatorsCache(t *testing.T) {
|
||||
vc := NewTrackedValidatorsCache()
|
||||
|
||||
// No validators in cache.
|
||||
require.Equal(t, 0, vc.ItemCount())
|
||||
require.Equal(t, false, vc.Validating())
|
||||
require.Equal(t, 0, len(vc.Indices()))
|
||||
|
||||
_, ok := vc.Validator(41)
|
||||
require.Equal(t, false, ok)
|
||||
|
||||
// Add some validators (one twice).
|
||||
v42Expected := TrackedValidator{Active: true, FeeRecipient: [20]byte{1}, Index: 42}
|
||||
v43Expected := TrackedValidator{Active: false, FeeRecipient: [20]byte{2}, Index: 43}
|
||||
|
||||
vc.Set(v42Expected)
|
||||
vc.Set(v43Expected)
|
||||
vc.Set(v42Expected)
|
||||
|
||||
// Check if they are in the cache.
|
||||
v42Actual, ok := vc.Validator(42)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, v42Expected, v42Actual)
|
||||
|
||||
v43Actual, ok := vc.Validator(43)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, v43Expected, v43Actual)
|
||||
|
||||
expected := map[primitives.ValidatorIndex]bool{42: true, 43: true}
|
||||
actual := vc.Indices()
|
||||
require.Equal(t, true, mapEqual(expected, actual))
|
||||
|
||||
// Check the item count and if the cache is validating.
|
||||
require.Equal(t, 2, vc.ItemCount())
|
||||
require.Equal(t, true, vc.Validating())
|
||||
|
||||
// Check if a non-existing validator is in the cache.
|
||||
_, ok = vc.Validator(41)
|
||||
require.Equal(t, false, ok)
|
||||
|
||||
// Prune the cache and test it.
|
||||
vc.Prune()
|
||||
|
||||
_, ok = vc.Validator(41)
|
||||
require.Equal(t, false, ok)
|
||||
|
||||
_, ok = vc.Validator(42)
|
||||
require.Equal(t, false, ok)
|
||||
|
||||
_, ok = vc.Validator(43)
|
||||
require.Equal(t, false, ok)
|
||||
|
||||
require.Equal(t, 0, vc.ItemCount())
|
||||
require.Equal(t, false, vc.Validating())
|
||||
require.Equal(t, 0, len(vc.Indices()))
|
||||
}
|
||||
@@ -66,7 +66,7 @@ func ProcessAttestationNoVerifySignature(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
committees, err := helpers.AttestationCommittees(ctx, beaconState, att)
|
||||
committees, err := helpers.AttestationCommitteesFromState(ctx, beaconState, att)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ func createAttestationSignatureBatch(
|
||||
descs := make([]string, len(atts))
|
||||
for i, a := range atts {
|
||||
sigs[i] = a.GetSignature()
|
||||
committees, err := helpers.AttestationCommittees(ctx, beaconState, a)
|
||||
committees, err := helpers.AttestationCommitteesFromState(ctx, beaconState, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ func UpgradeToFulu(beaconState state.BeaconState) (state.BeaconState, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := ðpb.BeaconStateFulu{
|
||||
s := ðpb.BeaconStateElectra{
|
||||
GenesisTime: beaconState.GenesisTime(),
|
||||
GenesisValidatorsRoot: beaconState.GenesisValidatorsRoot(),
|
||||
Slot: beaconState.Slot(),
|
||||
|
||||
@@ -30,6 +30,13 @@ var (
|
||||
proposerIndicesCache = cache.NewProposerIndicesCache()
|
||||
)
|
||||
|
||||
type beaconCommitteeFunc = func(
|
||||
ctx context.Context,
|
||||
state state.ReadOnlyBeaconState,
|
||||
slot primitives.Slot,
|
||||
committeeIndex primitives.CommitteeIndex,
|
||||
) ([]primitives.ValidatorIndex, error)
|
||||
|
||||
// SlotCommitteeCount returns the number of beacon committees of a slot. The
|
||||
// active validator count is provided as an argument rather than an imported implementation
|
||||
// from the spec definition. Having the active validator count as an argument allows for
|
||||
@@ -59,21 +66,48 @@ func SlotCommitteeCount(activeValidatorCount uint64) uint64 {
|
||||
return committeesPerSlot
|
||||
}
|
||||
|
||||
// AttestationCommittees returns beacon state committees that reflect attestation's committee indices.
|
||||
func AttestationCommittees(ctx context.Context, st state.ReadOnlyBeaconState, att ethpb.Att) ([][]primitives.ValidatorIndex, error) {
|
||||
// AttestationCommitteesFromState returns beacon state committees that reflect attestation's committee indices.
|
||||
func AttestationCommitteesFromState(ctx context.Context, st state.ReadOnlyBeaconState, att ethpb.Att) ([][]primitives.ValidatorIndex, error) {
|
||||
return attestationCommittees(ctx, st, att, BeaconCommitteeFromState)
|
||||
}
|
||||
|
||||
// AttestationCommitteesFromCache has the same functionality as AttestationCommitteesFromState, but only returns a value
|
||||
// when all attestation committees are already cached.
|
||||
func AttestationCommitteesFromCache(ctx context.Context, st state.ReadOnlyBeaconState, att ethpb.Att) (bool, [][]primitives.ValidatorIndex, error) {
|
||||
committees, err := attestationCommittees(ctx, st, att, BeaconCommitteeFromCache)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if len(committees) == 0 {
|
||||
return false, nil, nil
|
||||
}
|
||||
for _, c := range committees {
|
||||
if len(c) == 0 {
|
||||
return false, nil, nil
|
||||
}
|
||||
}
|
||||
return true, committees, nil
|
||||
}
|
||||
|
||||
func attestationCommittees(
|
||||
ctx context.Context,
|
||||
st state.ReadOnlyBeaconState,
|
||||
att ethpb.Att,
|
||||
committeeFunc beaconCommitteeFunc,
|
||||
) ([][]primitives.ValidatorIndex, error) {
|
||||
var committees [][]primitives.ValidatorIndex
|
||||
if att.Version() >= version.Electra {
|
||||
committeeIndices := att.CommitteeBitsVal().BitIndices()
|
||||
committees = make([][]primitives.ValidatorIndex, len(committeeIndices))
|
||||
for i, ci := range committeeIndices {
|
||||
committee, err := BeaconCommitteeFromState(ctx, st, att.GetData().Slot, primitives.CommitteeIndex(ci))
|
||||
committee, err := committeeFunc(ctx, st, att.GetData().Slot, primitives.CommitteeIndex(ci))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
committees[i] = committee
|
||||
}
|
||||
} else {
|
||||
committee, err := BeaconCommitteeFromState(ctx, st, att.GetData().Slot, att.GetData().CommitteeIndex)
|
||||
committee, err := committeeFunc(ctx, st, att.GetData().Slot, att.GetData().CommitteeIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -164,6 +198,27 @@ func BeaconCommitteeFromState(ctx context.Context, state state.ReadOnlyBeaconSta
|
||||
return BeaconCommittee(ctx, activeIndices, seed, slot, committeeIndex)
|
||||
}
|
||||
|
||||
// BeaconCommitteeFromCache has the same functionality as BeaconCommitteeFromState, but only returns a value
|
||||
// when the committee is already cached.
|
||||
func BeaconCommitteeFromCache(
|
||||
ctx context.Context,
|
||||
state state.ReadOnlyBeaconState,
|
||||
slot primitives.Slot,
|
||||
committeeIndex primitives.CommitteeIndex,
|
||||
) ([]primitives.ValidatorIndex, error) {
|
||||
epoch := slots.ToEpoch(slot)
|
||||
seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get seed")
|
||||
}
|
||||
|
||||
committee, err := committeeCache.Committee(ctx, slot, seed, committeeIndex)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not interface with committee cache")
|
||||
}
|
||||
return committee, nil
|
||||
}
|
||||
|
||||
// BeaconCommittee returns the beacon committee of a given slot and committee index. The
|
||||
// validator indices and seed are provided as an argument rather than an imported implementation
|
||||
// from the spec definition. Having them as an argument allows for cheaper computation run time.
|
||||
|
||||
@@ -729,7 +729,9 @@ func TestCommitteeIndices(t *testing.T) {
|
||||
assert.DeepEqual(t, []primitives.CommitteeIndex{0, 1, 3}, indices)
|
||||
}
|
||||
|
||||
func TestAttestationCommittees(t *testing.T) {
|
||||
func TestAttestationCommitteesFromState(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().TargetCommitteeSize))
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
@@ -745,7 +747,7 @@ func TestAttestationCommittees(t *testing.T) {
|
||||
|
||||
t.Run("pre-Electra", func(t *testing.T) {
|
||||
att := ðpb.Attestation{Data: ðpb.AttestationData{CommitteeIndex: 0}}
|
||||
committees, err := helpers.AttestationCommittees(context.Background(), state, att)
|
||||
committees, err := helpers.AttestationCommitteesFromState(ctx, state, att)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(committees))
|
||||
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[0])))
|
||||
@@ -755,7 +757,7 @@ func TestAttestationCommittees(t *testing.T) {
|
||||
bits.SetBitAt(0, true)
|
||||
bits.SetBitAt(1, true)
|
||||
att := ðpb.AttestationElectra{CommitteeBits: bits, Data: ðpb.AttestationData{}}
|
||||
committees, err := helpers.AttestationCommittees(context.Background(), state, att)
|
||||
committees, err := helpers.AttestationCommitteesFromState(ctx, state, att)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(committees))
|
||||
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[0])))
|
||||
@@ -763,9 +765,58 @@ func TestAttestationCommittees(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconCommittees(t *testing.T) {
|
||||
prevConfig := params.BeaconConfig().Copy()
|
||||
defer params.OverrideBeaconConfig(prevConfig)
|
||||
func TestAttestationCommitteesFromCache(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().TargetCommitteeSize))
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
|
||||
state, err := state_native.InitializeFromProtoPhase0(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("pre-Electra", func(t *testing.T) {
|
||||
helpers.ClearCache()
|
||||
att := ðpb.Attestation{Data: ðpb.AttestationData{CommitteeIndex: 0}}
|
||||
ok, _, err := helpers.AttestationCommitteesFromCache(ctx, state, att)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, ok)
|
||||
require.NoError(t, helpers.UpdateCommitteeCache(ctx, state, 0))
|
||||
ok, committees, err := helpers.AttestationCommitteesFromCache(ctx, state, att)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, 1, len(committees))
|
||||
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[0])))
|
||||
})
|
||||
t.Run("post-Electra", func(t *testing.T) {
|
||||
helpers.ClearCache()
|
||||
bits := primitives.NewAttestationCommitteeBits()
|
||||
bits.SetBitAt(0, true)
|
||||
bits.SetBitAt(1, true)
|
||||
att := ðpb.AttestationElectra{CommitteeBits: bits, Data: ðpb.AttestationData{}}
|
||||
ok, _, err := helpers.AttestationCommitteesFromCache(ctx, state, att)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, ok)
|
||||
require.NoError(t, helpers.UpdateCommitteeCache(ctx, state, 0))
|
||||
ok, committees, err := helpers.AttestationCommitteesFromCache(ctx, state, att)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, 2, len(committees))
|
||||
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[0])))
|
||||
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[1])))
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconCommitteesFromState(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
params.SetupTestConfigCleanup(t)
|
||||
c := params.BeaconConfig().Copy()
|
||||
c.MinGenesisActiveValidatorCount = 128
|
||||
c.SlotsPerEpoch = 4
|
||||
@@ -774,15 +825,49 @@ func TestBeaconCommittees(t *testing.T) {
|
||||
|
||||
state, _ := util.DeterministicGenesisState(t, 256)
|
||||
|
||||
activeCount, err := helpers.ActiveValidatorCount(context.Background(), state, 0)
|
||||
activeCount, err := helpers.ActiveValidatorCount(ctx, state, 0)
|
||||
require.NoError(t, err)
|
||||
committeesPerSlot := helpers.SlotCommitteeCount(activeCount)
|
||||
committees, err := helpers.BeaconCommittees(context.Background(), state, 0)
|
||||
committees, err := helpers.BeaconCommittees(ctx, state, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, committeesPerSlot, uint64(len(committees)))
|
||||
for idx := primitives.CommitteeIndex(0); idx < primitives.CommitteeIndex(len(committees)); idx++ {
|
||||
committee, err := helpers.BeaconCommitteeFromState(context.Background(), state, 0, idx)
|
||||
committee, err := helpers.BeaconCommitteeFromState(ctx, state, 0, idx)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, committees[idx], committee)
|
||||
assert.DeepEqual(t, committees[idx], committee)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeaconCommitteesFromCache(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
params.SetupTestConfigCleanup(t)
|
||||
c := params.BeaconConfig().Copy()
|
||||
c.MinGenesisActiveValidatorCount = 128
|
||||
c.SlotsPerEpoch = 4
|
||||
c.TargetCommitteeSize = 16
|
||||
params.OverrideBeaconConfig(c)
|
||||
|
||||
state, _ := util.DeterministicGenesisState(t, 256)
|
||||
|
||||
activeCount, err := helpers.ActiveValidatorCount(ctx, state, 0)
|
||||
require.NoError(t, err)
|
||||
committeesPerSlot := helpers.SlotCommitteeCount(activeCount)
|
||||
committees, err := helpers.BeaconCommittees(ctx, state, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, committeesPerSlot, uint64(len(committees)))
|
||||
|
||||
helpers.ClearCache()
|
||||
for idx := primitives.CommitteeIndex(0); idx < primitives.CommitteeIndex(len(committees)); idx++ {
|
||||
committee, err := helpers.BeaconCommitteeFromCache(ctx, state, 0, idx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, len(committee))
|
||||
}
|
||||
|
||||
require.NoError(t, helpers.UpdateCommitteeCache(ctx, state, 0))
|
||||
for idx := primitives.CommitteeIndex(0); idx < primitives.CommitteeIndex(len(committees)); idx++ {
|
||||
committee, err := helpers.BeaconCommitteeFromCache(ctx, state, 0, idx)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, committees[idx], committee)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package filesystem
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -193,7 +192,7 @@ func rootFromPath(p string) ([32]byte, error) {
|
||||
}
|
||||
|
||||
func idxFromPath(p string) (uint64, error) {
|
||||
p = path.Base(p)
|
||||
p = filepath.Base(p)
|
||||
|
||||
if !isSszFile(p) {
|
||||
return 0, errors.Wrap(errNotBlobSSZ, "does not have .ssz extension")
|
||||
|
||||
@@ -101,7 +101,7 @@ type NoHeadAccessDatabase interface {
|
||||
SaveLightClientBootstrap(ctx context.Context, blockRoot []byte, bootstrap interfaces.LightClientBootstrap) error
|
||||
|
||||
CleanUpDirtyStates(ctx context.Context, slotsPerArchivedPoint primitives.Slot) error
|
||||
DeleteHistoricalDataBeforeSlot(ctx context.Context, slot primitives.Slot) error
|
||||
DeleteHistoricalDataBeforeSlot(ctx context.Context, slot primitives.Slot, batchSize int) (int, error)
|
||||
}
|
||||
|
||||
// HeadAccessDatabase defines a struct with access to reading chain head data.
|
||||
|
||||
@@ -30,22 +30,32 @@ 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()
|
||||
// Return block from cache if it exists.
|
||||
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) {
|
||||
if v, ok := s.blockCache.Get(string(blockRoot[:])); v != nil && ok {
|
||||
return v.(interfaces.ReadOnlySignedBeaconBlock), nil
|
||||
}
|
||||
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
|
||||
}
|
||||
// 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
|
||||
blk, err = unmarshalBlock(ctx, enc)
|
||||
return err
|
||||
})
|
||||
return blk, err
|
||||
tx, err = s.db.Begin(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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[:]))
|
||||
}
|
||||
|
||||
// OriginCheckpointBlockRoot returns the value written to the db in SaveOriginCheckpointBlockRoot
|
||||
@@ -227,6 +237,21 @@ 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
|
||||
}
|
||||
@@ -245,77 +270,82 @@ func (s *Store) DeleteBlock(ctx context.Context, root [32]byte) error {
|
||||
// - blockRootValidatorHashesBucket
|
||||
// - blockSlotIndicesBucket
|
||||
// - stateSlotIndicesBucket
|
||||
func (s *Store) DeleteHistoricalDataBeforeSlot(ctx context.Context, cutoffSlot primitives.Slot) error {
|
||||
func (s *Store) DeleteHistoricalDataBeforeSlot(ctx context.Context, cutoffSlot primitives.Slot, batchSize int) (int, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.DeleteHistoricalDataBeforeSlot")
|
||||
defer span.End()
|
||||
|
||||
// Collect slot/root pairs to perform deletions in a separate read only transaction.
|
||||
var (
|
||||
roots [][]byte
|
||||
slts []primitives.Slot
|
||||
)
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
var err error
|
||||
roots, slts, err = blockRootsBySlotRange(ctx, tx.Bucket(blockSlotIndicesBucket), primitives.Slot(0), cutoffSlot, nil, nil, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not retrieve block roots")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
slotRoots, err := s.slotRootsInRange(ctx, primitives.Slot(0), cutoffSlot, batchSize)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not retrieve block roots and slots")
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Return early if there's nothing to delete.
|
||||
if len(slotRoots) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Perform all deletions in a single transaction for atomicity
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
for _, root := range roots {
|
||||
var numSlotsDeleted int
|
||||
err = s.db.Update(func(tx *bolt.Tx) error {
|
||||
for _, sr := range slotRoots {
|
||||
// Return if context is cancelled or deadline is exceeded.
|
||||
if ctx.Err() != nil {
|
||||
//nolint:nilerr
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete block
|
||||
if err = s.deleteBlock(tx, root); err != nil {
|
||||
if err = s.deleteBlock(tx, sr.root[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete finalized block roots index
|
||||
if err = tx.Bucket(finalizedBlockRootsIndexBucket).Delete(root); err != nil {
|
||||
if err = tx.Bucket(finalizedBlockRootsIndexBucket).Delete(sr.root[:]); err != nil {
|
||||
return errors.Wrap(err, "could not delete finalized block root index")
|
||||
}
|
||||
|
||||
// Delete state
|
||||
if err = tx.Bucket(stateBucket).Delete(root); err != nil {
|
||||
if err = tx.Bucket(stateBucket).Delete(sr.root[:]); err != nil {
|
||||
return errors.Wrap(err, "could not delete state")
|
||||
}
|
||||
|
||||
// Delete state summary
|
||||
if err = tx.Bucket(stateSummaryBucket).Delete(root); err != nil {
|
||||
if err = tx.Bucket(stateSummaryBucket).Delete(sr.root[:]); err != nil {
|
||||
return errors.Wrap(err, "could not delete state summary")
|
||||
}
|
||||
|
||||
// Delete validator entries
|
||||
if err = s.deleteValidatorHashes(tx, root); err != nil {
|
||||
if err = s.deleteValidatorHashes(tx, sr.root[:]); err != nil {
|
||||
return errors.Wrap(err, "could not delete validators")
|
||||
}
|
||||
|
||||
numSlotsDeleted++
|
||||
}
|
||||
|
||||
for _, slot := range slts {
|
||||
for _, sr := range slotRoots {
|
||||
// Delete slot indices
|
||||
if err = tx.Bucket(blockSlotIndicesBucket).Delete(bytesutil.SlotToBytesBigEndian(slot)); err != nil {
|
||||
if err = tx.Bucket(blockSlotIndicesBucket).Delete(bytesutil.SlotToBytesBigEndian(sr.slot)); err != nil {
|
||||
return errors.Wrap(err, "could not delete block slot index")
|
||||
}
|
||||
if err = tx.Bucket(stateSlotIndicesBucket).Delete(bytesutil.SlotToBytesBigEndian(slot)); err != nil {
|
||||
if err = tx.Bucket(stateSlotIndicesBucket).Delete(bytesutil.SlotToBytesBigEndian(sr.slot)); err != nil {
|
||||
return errors.Wrap(err, "could not delete state slot index")
|
||||
}
|
||||
}
|
||||
|
||||
// Delete all caches after we have deleted everything from buckets.
|
||||
// This is done after the buckets are deleted to avoid any issues in case of transaction rollback.
|
||||
for _, root := range roots {
|
||||
for _, sr := range slotRoots {
|
||||
// Delete block from cache
|
||||
s.blockCache.Del(string(root))
|
||||
s.blockCache.Del(string(sr.root[:]))
|
||||
// Delete state summary from cache
|
||||
s.stateSummaryCache.delete([32]byte(root))
|
||||
s.stateSummaryCache.delete(sr.root)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return numSlotsDeleted, err
|
||||
}
|
||||
|
||||
// SaveBlock to the db.
|
||||
@@ -336,7 +366,7 @@ func (s *Store) SaveBlock(ctx context.Context, signed interfaces.ReadOnlySignedB
|
||||
// if a `saveBlindedBeaconBlocks` key exists in the database. Otherwise, we check if the last
|
||||
// blocked stored to check if it is blinded, and then write that `saveBlindedBeaconBlocks` key
|
||||
// to the DB for future checks.
|
||||
func (s *Store) shouldSaveBlinded(ctx context.Context) (bool, error) {
|
||||
func (s *Store) shouldSaveBlinded() (bool, error) {
|
||||
var saveBlinded bool
|
||||
if err := s.db.View(func(tx *bolt.Tx) error {
|
||||
metadataBkt := tx.Bucket(chainMetadataBucket)
|
||||
@@ -398,7 +428,7 @@ func prepareBlockBatch(blks []blocks.ROBlock, shouldBlind bool) ([]blockBatchEnt
|
||||
}
|
||||
|
||||
func (s *Store) SaveROBlocks(ctx context.Context, blks []blocks.ROBlock, cache bool) error {
|
||||
shouldBlind, err := s.shouldSaveBlinded(ctx)
|
||||
shouldBlind, err := s.shouldSaveBlinded()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -669,6 +699,49 @@ func (s *Store) SaveRegistrationsByValidatorIDs(ctx context.Context, ids []primi
|
||||
})
|
||||
}
|
||||
|
||||
type slotRoot struct {
|
||||
slot primitives.Slot
|
||||
root [32]byte
|
||||
}
|
||||
|
||||
// slotRootsInRange returns slot and block root pairs of length min(batchSize, end-slot)
|
||||
func (s *Store) slotRootsInRange(ctx context.Context, start, end primitives.Slot, batchSize int) ([]slotRoot, error) {
|
||||
_, span := trace.StartSpan(ctx, "BeaconDB.slotRootsInRange")
|
||||
defer span.End()
|
||||
if end < start {
|
||||
return nil, errInvalidSlotRange
|
||||
}
|
||||
|
||||
var pairs []slotRoot
|
||||
key := bytesutil.SlotToBytesBigEndian(end)
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(blockSlotIndicesBucket)
|
||||
c := bkt.Cursor()
|
||||
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
|
||||
}
|
||||
if slot < start {
|
||||
return nil
|
||||
}
|
||||
roots, err := splitRoots(v)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "corrupt value %v in block slot index for slot=%d", v, slot)
|
||||
}
|
||||
for _, r := range roots {
|
||||
pairs = append(pairs, slotRoot{slot: slot, root: r})
|
||||
}
|
||||
if len(pairs) >= batchSize {
|
||||
return nil // allows code to easily cap the number of items pruned
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return pairs, err
|
||||
}
|
||||
|
||||
// blockRootsByFilter retrieves the block roots given the filter criteria.
|
||||
func blockRootsByFilter(ctx context.Context, tx *bolt.Tx, f *filters.QueryFilter) ([][]byte, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.blockRootsByFilter")
|
||||
@@ -689,7 +762,7 @@ func blockRootsByFilter(ctx context.Context, tx *bolt.Tx, f *filters.QueryFilter
|
||||
|
||||
// We retrieve block roots that match a filter criteria of slot ranges, if specified.
|
||||
filtersMap := f.Filters()
|
||||
rootsBySlotRange, _, err := blockRootsBySlotRange(
|
||||
rootsBySlotRange, err := blockRootsBySlotRange(
|
||||
ctx,
|
||||
tx.Bucket(blockSlotIndicesBucket),
|
||||
filtersMap[filters.StartSlot],
|
||||
@@ -734,13 +807,13 @@ func blockRootsBySlotRange(
|
||||
ctx context.Context,
|
||||
bkt *bolt.Bucket,
|
||||
startSlotEncoded, endSlotEncoded, startEpochEncoded, endEpochEncoded, slotStepEncoded interface{},
|
||||
) ([][]byte, []primitives.Slot, error) {
|
||||
) ([][]byte, error) {
|
||||
_, span := trace.StartSpan(ctx, "BeaconDB.blockRootsBySlotRange")
|
||||
defer span.End()
|
||||
|
||||
// Return nothing when all slot parameters are missing
|
||||
if startSlotEncoded == nil && endSlotEncoded == nil && startEpochEncoded == nil && endEpochEncoded == nil {
|
||||
return [][]byte{}, nil, nil
|
||||
return [][]byte{}, nil
|
||||
}
|
||||
|
||||
var startSlot, endSlot primitives.Slot
|
||||
@@ -761,11 +834,11 @@ func blockRootsBySlotRange(
|
||||
if startEpochOk && endEpochOk {
|
||||
startSlot, err = slots.EpochStart(startEpoch)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
endSlot, err = slots.EpochStart(endEpoch)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
endSlot = endSlot + params.BeaconConfig().SlotsPerEpoch - 1
|
||||
}
|
||||
@@ -776,11 +849,10 @@ func blockRootsBySlotRange(
|
||||
return key != nil && bytes.Compare(key, max) <= 0
|
||||
}
|
||||
if endSlot < startSlot {
|
||||
return nil, nil, errInvalidSlotRange
|
||||
return nil, errInvalidSlotRange
|
||||
}
|
||||
rootsRange := endSlot.SubSlot(startSlot).Div(step)
|
||||
roots := make([][]byte, 0, rootsRange)
|
||||
var slts []primitives.Slot
|
||||
c := bkt.Cursor()
|
||||
for k, v := c.Seek(min); conditional(k, max); k, v = c.Next() {
|
||||
slot := bytesutil.BytesToSlotBigEndian(k)
|
||||
@@ -795,9 +867,8 @@ func blockRootsBySlotRange(
|
||||
splitRoots = append(splitRoots, v[i:i+32])
|
||||
}
|
||||
roots = append(roots, splitRoots...)
|
||||
slts = append(slts, slot)
|
||||
}
|
||||
return roots, slts, nil
|
||||
return roots, nil
|
||||
}
|
||||
|
||||
// blockRootsBySlot retrieves the block roots by slot
|
||||
@@ -853,6 +924,9 @@ 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 {
|
||||
@@ -1004,6 +1078,47 @@ 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 {
|
||||
|
||||
@@ -196,9 +196,13 @@ 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")
|
||||
@@ -214,10 +218,34 @@ 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) {
|
||||
@@ -359,184 +387,221 @@ func TestStore_DeleteFinalizedBlock(t *testing.T) {
|
||||
|
||||
func TestStore_HistoricalDataBeforeSlot(t *testing.T) {
|
||||
slotsPerEpoch := uint64(params.BeaconConfig().SlotsPerEpoch)
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// Save genesis block root
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesisBlockRoot))
|
||||
tests := []struct {
|
||||
name string
|
||||
batchSize int
|
||||
numOfEpochs uint64
|
||||
deleteBeforeSlot uint64
|
||||
}{
|
||||
{
|
||||
name: "batchSize less than delete range",
|
||||
batchSize: 10,
|
||||
numOfEpochs: 4,
|
||||
deleteBeforeSlot: 25,
|
||||
},
|
||||
{
|
||||
name: "batchSize greater than delete range",
|
||||
batchSize: 30,
|
||||
numOfEpochs: 4,
|
||||
deleteBeforeSlot: 15,
|
||||
},
|
||||
}
|
||||
|
||||
// Create and save blocks for 4 epochs
|
||||
blks := makeBlocks(t, 0, slotsPerEpoch*4, genesisBlockRoot)
|
||||
require.NoError(t, db.SaveBlocks(ctx, blks))
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
// Save genesis block root
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesisBlockRoot))
|
||||
|
||||
// Mark state validator migration as complete
|
||||
err := db.db.Update(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(migrationsBucket).Put(migrationStateValidatorsKey, migrationCompleted)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// Create and save blocks for given epochs
|
||||
blks := makeBlocks(t, 0, slotsPerEpoch*tt.numOfEpochs, genesisBlockRoot)
|
||||
require.NoError(t, db.SaveBlocks(ctx, blks))
|
||||
|
||||
migrated, err := db.isStateValidatorMigrationOver()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, migrated)
|
||||
// Mark state validator migration as complete
|
||||
err := db.db.Update(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(migrationsBucket).Put(migrationStateValidatorsKey, migrationCompleted)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create state summaries and states for each block
|
||||
ss := make([]*ethpb.StateSummary, len(blks))
|
||||
states := make([]state.BeaconState, len(blks))
|
||||
migrated, err := db.isStateValidatorMigrationOver()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, migrated)
|
||||
|
||||
for i, blk := range blks {
|
||||
slot := blk.Block().Slot()
|
||||
r, err := blk.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
// Create state summaries and states for each block
|
||||
ss := make([]*ethpb.StateSummary, len(blks))
|
||||
states := make([]state.BeaconState, len(blks))
|
||||
|
||||
// Create and save state summary
|
||||
ss[i] = ðpb.StateSummary{
|
||||
Slot: slot,
|
||||
Root: r[:],
|
||||
}
|
||||
for i, blk := range blks {
|
||||
slot := blk.Block().Slot()
|
||||
r, err := blk.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create and save state with validator entries
|
||||
vals := make([]*ethpb.Validator, 2)
|
||||
for j := range vals {
|
||||
vals[j] = ðpb.Validator{
|
||||
PublicKey: bytesutil.PadTo([]byte{byte(i*j + 1)}, 48),
|
||||
WithdrawalCredentials: bytesutil.PadTo([]byte{byte(i*j + 2)}, 32),
|
||||
// Create and save state summary
|
||||
ss[i] = ðpb.StateSummary{
|
||||
Slot: slot,
|
||||
Root: r[:],
|
||||
}
|
||||
|
||||
// Create and save state with validator entries
|
||||
vals := make([]*ethpb.Validator, 2)
|
||||
for j := range vals {
|
||||
vals[j] = ðpb.Validator{
|
||||
PublicKey: bytesutil.PadTo([]byte{byte(i*j + 1)}, 48),
|
||||
WithdrawalCredentials: bytesutil.PadTo([]byte{byte(i*j + 2)}, 32),
|
||||
}
|
||||
}
|
||||
|
||||
st, err := util.NewBeaconState(func(state *ethpb.BeaconState) error {
|
||||
state.Validators = vals
|
||||
state.Slot = slot
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveState(ctx, st, r))
|
||||
states[i] = st
|
||||
|
||||
// Verify validator entries are saved to db
|
||||
valsActual, err := db.validatorEntries(ctx, r)
|
||||
require.NoError(t, err)
|
||||
for j, val := range valsActual {
|
||||
require.DeepEqual(t, vals[j], val)
|
||||
}
|
||||
}
|
||||
}
|
||||
require.NoError(t, db.SaveStateSummaries(ctx, ss))
|
||||
|
||||
st, err := util.NewBeaconState(func(state *ethpb.BeaconState) error {
|
||||
state.Validators = vals
|
||||
state.Slot = slot
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveState(ctx, st, r))
|
||||
states[i] = st
|
||||
|
||||
// Verify validator entries are saved to db
|
||||
valsActual, err := db.validatorEntries(ctx, r)
|
||||
require.NoError(t, err)
|
||||
for j, val := range valsActual {
|
||||
require.DeepEqual(t, vals[j], val)
|
||||
}
|
||||
}
|
||||
require.NoError(t, db.SaveStateSummaries(ctx, ss))
|
||||
|
||||
// Verify slot indices exist before deletion
|
||||
err = db.db.View(func(tx *bolt.Tx) error {
|
||||
blockSlotBkt := tx.Bucket(blockSlotIndicesBucket)
|
||||
stateSlotBkt := tx.Bucket(stateSlotIndicesBucket)
|
||||
|
||||
for i := uint64(0); i < slotsPerEpoch; i++ {
|
||||
slot := bytesutil.SlotToBytesBigEndian(primitives.Slot(i + 1))
|
||||
assert.NotNil(t, blockSlotBkt.Get(slot), "Expected block slot index to exist")
|
||||
assert.NotNil(t, stateSlotBkt.Get(slot), "Expected state slot index to exist", i)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Delete data before slot at epoch 1
|
||||
require.NoError(t, db.DeleteHistoricalDataBeforeSlot(ctx, primitives.Slot(slotsPerEpoch)))
|
||||
|
||||
// Verify blocks from epoch 0 are deleted
|
||||
for i := uint64(0); i < slotsPerEpoch; i++ {
|
||||
root, err := blks[i].Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check block is deleted
|
||||
retrievedBlocks, err := db.BlocksBySlot(ctx, primitives.Slot(i))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, len(retrievedBlocks))
|
||||
|
||||
// Verify block does not exist
|
||||
assert.Equal(t, false, db.HasBlock(ctx, root))
|
||||
|
||||
// Verify block parent root does not exist
|
||||
err = db.db.View(func(tx *bolt.Tx) error {
|
||||
require.Equal(t, 0, len(tx.Bucket(blockParentRootIndicesBucket).Get(root[:])))
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify state is deleted
|
||||
hasState := db.HasState(ctx, root)
|
||||
assert.Equal(t, false, hasState)
|
||||
|
||||
// Verify state summary is deleted
|
||||
hasSummary := db.HasStateSummary(ctx, root)
|
||||
assert.Equal(t, false, hasSummary)
|
||||
|
||||
// Verify validator hashes for block roots are deleted
|
||||
err = db.db.View(func(tx *bolt.Tx) error {
|
||||
assert.Equal(t, 0, len(tx.Bucket(blockRootValidatorHashesBucket).Get(root[:])))
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Verify slot indices are deleted
|
||||
err = db.db.View(func(tx *bolt.Tx) error {
|
||||
blockSlotBkt := tx.Bucket(blockSlotIndicesBucket)
|
||||
stateSlotBkt := tx.Bucket(stateSlotIndicesBucket)
|
||||
|
||||
for i := uint64(0); i < slotsPerEpoch; i++ {
|
||||
slot := bytesutil.SlotToBytesBigEndian(primitives.Slot(i + 1))
|
||||
assert.Equal(t, 0, len(blockSlotBkt.Get(slot)), fmt.Sprintf("Expected block slot index to be deleted, slot: %d", slot))
|
||||
assert.Equal(t, 0, len(stateSlotBkt.Get(slot)), fmt.Sprintf("Expected state slot index to be deleted, slot: %d", slot))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify blocks from epochs 1-3 still exist
|
||||
for i := slotsPerEpoch; i < slotsPerEpoch*4; i++ {
|
||||
root, err := blks[i].Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify block exists
|
||||
assert.Equal(t, true, db.HasBlock(ctx, root))
|
||||
|
||||
// Verify remaining block parent root exists, except last slot since we store parent roots of each block.
|
||||
if i < slotsPerEpoch*4-1 {
|
||||
// Verify slot indices exist before deletion
|
||||
err = db.db.View(func(tx *bolt.Tx) error {
|
||||
require.NotNil(t, tx.Bucket(blockParentRootIndicesBucket).Get(root[:]), fmt.Sprintf("Expected block parent index to be deleted, slot: %d", i))
|
||||
blockSlotBkt := tx.Bucket(blockSlotIndicesBucket)
|
||||
stateSlotBkt := tx.Bucket(stateSlotIndicesBucket)
|
||||
|
||||
for i := uint64(0); i < uint64(tt.deleteBeforeSlot); i++ {
|
||||
slot := bytesutil.SlotToBytesBigEndian(primitives.Slot(i + 1))
|
||||
assert.NotNil(t, blockSlotBkt.Get(slot), "Expected block slot index to exist")
|
||||
assert.NotNil(t, stateSlotBkt.Get(slot), "Expected state slot index to exist", i)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Verify state exists
|
||||
hasState := db.HasState(ctx, root)
|
||||
assert.Equal(t, true, hasState)
|
||||
// Delete data before slot
|
||||
slotsDeleted, err := db.DeleteHistoricalDataBeforeSlot(ctx, primitives.Slot(tt.deleteBeforeSlot), tt.batchSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify state summary exists
|
||||
hasSummary := db.HasStateSummary(ctx, root)
|
||||
assert.Equal(t, true, hasSummary)
|
||||
var startSlotDeleted, endSlotDeleted uint64
|
||||
if tt.batchSize >= int(tt.deleteBeforeSlot) {
|
||||
startSlotDeleted = 1
|
||||
endSlotDeleted = tt.deleteBeforeSlot
|
||||
} else {
|
||||
startSlotDeleted = tt.deleteBeforeSlot - uint64(tt.batchSize) + 1
|
||||
endSlotDeleted = tt.deleteBeforeSlot
|
||||
}
|
||||
|
||||
// Verify slot indices still exist
|
||||
err = db.db.View(func(tx *bolt.Tx) error {
|
||||
blockSlotBkt := tx.Bucket(blockSlotIndicesBucket)
|
||||
stateSlotBkt := tx.Bucket(stateSlotIndicesBucket)
|
||||
require.Equal(t, endSlotDeleted-startSlotDeleted+1, uint64(slotsDeleted))
|
||||
|
||||
slot := bytesutil.SlotToBytesBigEndian(primitives.Slot(i + 1))
|
||||
assert.NotNil(t, blockSlotBkt.Get(slot), "Expected block slot index to exist")
|
||||
assert.NotNil(t, stateSlotBkt.Get(slot), "Expected state slot index to exist")
|
||||
return nil
|
||||
// Verify blocks before given slot/batch are deleted
|
||||
for i := startSlotDeleted; i < endSlotDeleted; i++ {
|
||||
root, err := blks[i].Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check block is deleted
|
||||
retrievedBlocks, err := db.BlocksBySlot(ctx, primitives.Slot(i))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, len(retrievedBlocks), fmt.Sprintf("Expected %d blocks, got %d for slot %d", 0, len(retrievedBlocks), i))
|
||||
|
||||
// Verify block does not exist
|
||||
assert.Equal(t, false, db.HasBlock(ctx, root), fmt.Sprintf("Expected block index to not exist for slot %d", i))
|
||||
|
||||
// Verify block parent root does not exist
|
||||
err = db.db.View(func(tx *bolt.Tx) error {
|
||||
require.Equal(t, 0, len(tx.Bucket(blockParentRootIndicesBucket).Get(root[:])))
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify state is deleted
|
||||
hasState := db.HasState(ctx, root)
|
||||
assert.Equal(t, false, hasState)
|
||||
|
||||
// Verify state summary is deleted
|
||||
hasSummary := db.HasStateSummary(ctx, root)
|
||||
assert.Equal(t, false, hasSummary)
|
||||
|
||||
// Verify validator hashes for block roots are deleted
|
||||
err = db.db.View(func(tx *bolt.Tx) error {
|
||||
assert.Equal(t, 0, len(tx.Bucket(blockRootValidatorHashesBucket).Get(root[:])))
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Verify slot indices are deleted
|
||||
err = db.db.View(func(tx *bolt.Tx) error {
|
||||
blockSlotBkt := tx.Bucket(blockSlotIndicesBucket)
|
||||
stateSlotBkt := tx.Bucket(stateSlotIndicesBucket)
|
||||
|
||||
for i := startSlotDeleted; i < endSlotDeleted; i++ {
|
||||
slot := bytesutil.SlotToBytesBigEndian(primitives.Slot(i + 1))
|
||||
assert.Equal(t, 0, len(blockSlotBkt.Get(slot)), fmt.Sprintf("Expected block slot index to be deleted, slot: %d", slot))
|
||||
assert.Equal(t, 0, len(stateSlotBkt.Get(slot)), fmt.Sprintf("Expected state slot index to be deleted, slot: %d", slot))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify blocks from expectedLastDeletedSlot till numEpochs still exist
|
||||
for i := endSlotDeleted; i < slotsPerEpoch*tt.numOfEpochs; i++ {
|
||||
root, err := blks[i].Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify block exists
|
||||
assert.Equal(t, true, db.HasBlock(ctx, root))
|
||||
|
||||
// Verify remaining block parent root exists, except last slot since we store parent roots of each block.
|
||||
if i < slotsPerEpoch*tt.numOfEpochs-1 {
|
||||
err = db.db.View(func(tx *bolt.Tx) error {
|
||||
require.NotNil(t, tx.Bucket(blockParentRootIndicesBucket).Get(root[:]), fmt.Sprintf("Expected block parent index to be deleted, slot: %d", i))
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Verify state exists
|
||||
hasState := db.HasState(ctx, root)
|
||||
assert.Equal(t, true, hasState)
|
||||
|
||||
// Verify state summary exists
|
||||
hasSummary := db.HasStateSummary(ctx, root)
|
||||
assert.Equal(t, true, hasSummary)
|
||||
|
||||
// Verify slot indices still exist
|
||||
err = db.db.View(func(tx *bolt.Tx) error {
|
||||
blockSlotBkt := tx.Bucket(blockSlotIndicesBucket)
|
||||
stateSlotBkt := tx.Bucket(stateSlotIndicesBucket)
|
||||
|
||||
slot := bytesutil.SlotToBytesBigEndian(primitives.Slot(i + 1))
|
||||
assert.NotNil(t, blockSlotBkt.Get(slot), "Expected block slot index to exist")
|
||||
assert.NotNil(t, stateSlotBkt.Get(slot), "Expected state slot index to exist")
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify validator entries still exist
|
||||
valsActual, err := db.validatorEntries(ctx, root)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, valsActual)
|
||||
|
||||
// Verify remaining validator hashes for block roots exists
|
||||
err = db.db.View(func(tx *bolt.Tx) error {
|
||||
assert.NotNil(t, tx.Bucket(blockRootValidatorHashesBucket).Get(root[:]))
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify validator entries still exist
|
||||
valsActual, err := db.validatorEntries(ctx, root)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, valsActual)
|
||||
|
||||
// Verify remaining validator hashes for block roots exists
|
||||
err = db.db.View(func(tx *bolt.Tx) error {
|
||||
assert.NotNil(t, tx.Bucket(blockRootValidatorHashesBucket).Get(root[:]))
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestStore_GenesisBlock(t *testing.T) {
|
||||
|
||||
@@ -215,7 +215,7 @@ func (s *Store) LightClientUpdates(ctx context.Context, startPeriod, endPeriod u
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return updates, err
|
||||
return updates, nil
|
||||
}
|
||||
|
||||
func (s *Store) LightClientUpdate(ctx context.Context, period uint64) (interfaces.LightClientUpdate, error) {
|
||||
|
||||
@@ -518,9 +518,9 @@ func (s *Store) unmarshalState(_ context.Context, enc []byte, validatorEntries [
|
||||
|
||||
switch {
|
||||
case hasFuluKey(enc):
|
||||
protoState := ðpb.BeaconStateFulu{}
|
||||
protoState := ðpb.BeaconStateElectra{}
|
||||
if err := protoState.UnmarshalSSZ(enc[len(fuluKey):]); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal encoding for Electra")
|
||||
return nil, errors.Wrap(err, "failed to unmarshal encoding for Fulu")
|
||||
}
|
||||
ok, err := s.isStateValidatorMigrationOver()
|
||||
if err != nil {
|
||||
@@ -690,7 +690,7 @@ func marshalState(ctx context.Context, st state.ReadOnlyBeaconState) ([]byte, er
|
||||
}
|
||||
return snappy.Encode(nil, append(ElectraKey, rawObj...)), nil
|
||||
case version.Fulu:
|
||||
rState, ok := st.ToProtoUnsafe().(*ethpb.BeaconStateFulu)
|
||||
rState, ok := st.ToProtoUnsafe().(*ethpb.BeaconStateElectra)
|
||||
if !ok {
|
||||
return nil, errors.New("non valid inner state")
|
||||
}
|
||||
@@ -820,30 +820,25 @@ func (s *Store) slotByBlockRoot(ctx context.Context, tx *bolt.Tx, blockRoot []by
|
||||
// no need to construct the validator entries as it is not used here.
|
||||
s, err := s.unmarshalState(ctx, enc, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, errors.Wrap(err, "could not unmarshal state")
|
||||
}
|
||||
if s == nil || s.IsNil() {
|
||||
return 0, errors.New("state can't be nil")
|
||||
}
|
||||
return s.Slot(), nil
|
||||
}
|
||||
b := ðpb.SignedBeaconBlock{}
|
||||
err := decode(ctx, enc, b)
|
||||
b, err := unmarshalBlock(ctx, enc)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not unmarshal block")
|
||||
}
|
||||
if err := blocks.BeaconBlockIsNil(b); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
wsb, err := blocks.NewSignedBeaconBlock(b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := blocks.BeaconBlockIsNil(wsb); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return b.Block.Slot, nil
|
||||
return b.Block().Slot(), nil
|
||||
}
|
||||
stateSummary := ðpb.StateSummary{}
|
||||
if err := decode(ctx, enc, stateSummary); err != nil {
|
||||
return 0, err
|
||||
return 0, errors.Wrap(err, "could not unmarshal state summary")
|
||||
}
|
||||
return stateSummary.Slot, nil
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
mathRand "math/rand"
|
||||
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -1070,6 +1069,31 @@ func TestBellatrixState_CanDelete(t *testing.T) {
|
||||
require.Equal(t, state.ReadOnlyBeaconState(nil), savedS, "Unsaved state should've been nil")
|
||||
}
|
||||
|
||||
func TestBellatrixState_CanDeleteWithBlock(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
|
||||
b := util.NewBeaconBlockBellatrix()
|
||||
b.Block.Slot = 100
|
||||
r, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
wsb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(context.Background(), wsb))
|
||||
|
||||
require.Equal(t, false, db.HasState(context.Background(), r))
|
||||
|
||||
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
|
||||
require.NoError(t, st.SetSlot(100))
|
||||
|
||||
require.NoError(t, db.SaveState(context.Background(), st, r))
|
||||
require.Equal(t, true, db.HasState(context.Background(), r))
|
||||
|
||||
require.NoError(t, db.DeleteState(context.Background(), r))
|
||||
savedS, err := db.State(context.Background(), r)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, state.ReadOnlyBeaconState(nil), savedS, "Unsaved state should've been nil")
|
||||
}
|
||||
|
||||
func TestDenebState_CanSaveRetrieve(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
|
||||
|
||||
@@ -114,3 +114,27 @@ 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
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
@@ -195,3 +196,85 @@ 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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,15 @@ import (
|
||||
|
||||
var log = logrus.WithField("prefix", "db-pruner")
|
||||
|
||||
const (
|
||||
// defaultPrunableBatchSize is the number of slots that can be pruned at once.
|
||||
defaultPrunableBatchSize = 32
|
||||
// defaultPruningWindow is the duration of one pruning window.
|
||||
defaultPruningWindow = time.Second * 3
|
||||
// defaultNumBatchesToPrune is the number of batches to prune in one pruning window.
|
||||
defaultNumBatchesToPrune = 15
|
||||
)
|
||||
|
||||
type ServiceOption func(*Service)
|
||||
|
||||
// WithRetentionPeriod allows the user to specify a different data retention period than the spec default.
|
||||
@@ -143,14 +152,17 @@ func (p *Service) prune(slot primitives.Slot) error {
|
||||
}).Debug("Pruning chain data")
|
||||
|
||||
tt := time.Now()
|
||||
if err := p.db.DeleteHistoricalDataBeforeSlot(p.ctx, pruneUpto); err != nil {
|
||||
return errors.Wrapf(err, "could not delete upto slot %d", pruneUpto)
|
||||
numBatches, err := p.pruneBatches(pruneUpto)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to prune batches")
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"prunedUpto": pruneUpto,
|
||||
"duration": time.Since(tt),
|
||||
"currentSlot": slot,
|
||||
"batchSize": defaultPrunableBatchSize,
|
||||
"numBatches": numBatches,
|
||||
}).Debug("Successfully pruned chain data")
|
||||
|
||||
// Update pruning checkpoint.
|
||||
@@ -159,6 +171,33 @@ func (p *Service) prune(slot primitives.Slot) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Service) pruneBatches(pruneUpto primitives.Slot) (int, error) {
|
||||
ctx, cancel := context.WithTimeout(p.ctx, defaultPruningWindow)
|
||||
defer cancel()
|
||||
|
||||
numBatches := 0
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return numBatches, nil
|
||||
default:
|
||||
for i := 0; i < defaultNumBatchesToPrune; i++ {
|
||||
slotsDeleted, err := p.db.DeleteHistoricalDataBeforeSlot(ctx, pruneUpto, defaultPrunableBatchSize)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "could not delete upto slot %d", pruneUpto)
|
||||
}
|
||||
|
||||
// Return if there's nothing to delete.
|
||||
if slotsDeleted == 0 {
|
||||
return numBatches, nil
|
||||
}
|
||||
|
||||
numBatches++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pruneStartSlotFunc returns the function to determine the start slot to start pruning.
|
||||
func pruneStartSlotFunc(retentionEpochs primitives.Epoch) func(primitives.Slot) primitives.Slot {
|
||||
return func(current primitives.Slot) primitives.Slot {
|
||||
|
||||
@@ -36,17 +36,19 @@ var (
|
||||
NewPayloadMethod,
|
||||
NewPayloadMethodV2,
|
||||
NewPayloadMethodV3,
|
||||
NewPayloadMethodV4,
|
||||
ForkchoiceUpdatedMethod,
|
||||
ForkchoiceUpdatedMethodV2,
|
||||
ForkchoiceUpdatedMethodV3,
|
||||
GetPayloadMethod,
|
||||
GetPayloadMethodV2,
|
||||
GetPayloadMethodV3,
|
||||
GetPayloadMethodV4,
|
||||
GetPayloadBodiesByHashV1,
|
||||
GetPayloadBodiesByRangeV1,
|
||||
}
|
||||
electraEngineEndpoints = []string{
|
||||
NewPayloadMethodV4,
|
||||
GetPayloadMethodV4,
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -296,6 +298,10 @@ func (s *Service) ExchangeCapabilities(ctx context.Context) ([]string, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ExchangeCapabilities")
|
||||
defer span.End()
|
||||
|
||||
// Only check for electra related engine methods if it has been activated.
|
||||
if params.ElectraEnabled() {
|
||||
supportedEngineEndpoints = append(supportedEngineEndpoints, electraEngineEndpoints...)
|
||||
}
|
||||
var result []string
|
||||
err := s.rpcClient.CallContext(ctx, &result, ExchangeCapabilities, supportedEngineEndpoints)
|
||||
if err != nil {
|
||||
|
||||
@@ -156,6 +156,10 @@ func (n *Node) nodeTreeDump(ctx context.Context, nodes []*forkchoice2.Node) ([]*
|
||||
if n.parent != nil {
|
||||
parentRoot = n.parent.root
|
||||
}
|
||||
target := [32]byte{}
|
||||
if n.target != nil {
|
||||
target = n.target.root
|
||||
}
|
||||
thisNode := &forkchoice2.Node{
|
||||
Slot: n.slot,
|
||||
BlockRoot: n.root[:],
|
||||
@@ -169,6 +173,7 @@ func (n *Node) nodeTreeDump(ctx context.Context, nodes []*forkchoice2.Node) ([]*
|
||||
ExecutionOptimistic: n.optimistic,
|
||||
ExecutionBlockHash: n.payloadHash[:],
|
||||
Timestamp: n.timestamp,
|
||||
Target: target[:],
|
||||
}
|
||||
if n.optimistic {
|
||||
thisNode.Validity = forkchoice2.Optimistic
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package doublylinkedtree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
)
|
||||
@@ -15,7 +13,7 @@ func (f *ForkChoice) applyProposerBoostScore() error {
|
||||
if s.previousProposerBoostRoot != params.BeaconConfig().ZeroHash {
|
||||
previousNode, ok := s.nodeByRoot[s.previousProposerBoostRoot]
|
||||
if !ok || previousNode == nil {
|
||||
log.WithError(errInvalidProposerBoostRoot).Errorf(fmt.Sprintf("invalid prev root %#x", s.previousProposerBoostRoot))
|
||||
log.WithError(errInvalidProposerBoostRoot).Errorf("invalid prev root %#x", s.previousProposerBoostRoot)
|
||||
} else {
|
||||
previousNode.balance -= s.previousProposerBoostScore
|
||||
}
|
||||
@@ -24,7 +22,7 @@ func (f *ForkChoice) applyProposerBoostScore() error {
|
||||
if s.proposerBoostRoot != params.BeaconConfig().ZeroHash {
|
||||
currentNode, ok := s.nodeByRoot[s.proposerBoostRoot]
|
||||
if !ok || currentNode == nil {
|
||||
log.WithError(errInvalidProposerBoostRoot).Errorf(fmt.Sprintf("invalid current root %#x", s.proposerBoostRoot))
|
||||
log.WithError(errInvalidProposerBoostRoot).Errorf("invalid current root %#x", s.proposerBoostRoot)
|
||||
} else {
|
||||
proposerScore = (s.committeeWeight * params.BeaconConfig().ProposerScoreBoost) / 100
|
||||
currentNode.balance += proposerScore
|
||||
|
||||
@@ -166,6 +166,23 @@ func (s *Service) processIncludedAttestation(ctx context.Context, state state.Be
|
||||
}
|
||||
}
|
||||
|
||||
// processSingleAttestation logs when the beacon node observes a single attestation from tracked validator.
|
||||
func (s *Service) processSingleAttestation(att ethpb.Att) {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
|
||||
single, ok := att.(*ethpb.SingleAttestation)
|
||||
if !ok {
|
||||
log.Errorf("Wrong attestation type (expected %T, got %T)", ðpb.SingleAttestation{}, att)
|
||||
return
|
||||
}
|
||||
|
||||
if s.canUpdateAttestedValidator(single.AttesterIndex, single.GetData().Slot) {
|
||||
logFields := logMessageTimelyFlagsForIndex(single.AttesterIndex, att.GetData())
|
||||
log.WithFields(logFields).Info("Processed unaggregated attestation")
|
||||
}
|
||||
}
|
||||
|
||||
// processUnaggregatedAttestation logs when the beacon node observes an unaggregated attestation from tracked validator.
|
||||
func (s *Service) processUnaggregatedAttestation(ctx context.Context, att ethpb.Att) {
|
||||
s.RLock()
|
||||
|
||||
@@ -241,7 +241,7 @@ func (s *Service) monitorRoutine(stateChannel chan *feed.Event, stateSub event.S
|
||||
if !ok {
|
||||
log.Error("Event feed data is not of type *operation.SingleAttReceivedData")
|
||||
} else {
|
||||
s.processUnaggregatedAttestation(s.ctx, data.Attestation)
|
||||
s.processSingleAttestation(data.Attestation)
|
||||
}
|
||||
case operation.ExitReceived:
|
||||
data, ok := e.Data.(*operation.ExitReceivedData)
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
func configureTracing(cliCtx *cli.Context) error {
|
||||
return tracing.Setup(
|
||||
cliCtx.Context,
|
||||
"beacon-chain", // service name
|
||||
cliCtx.String(cmd.TracingProcessNameFlag.Name),
|
||||
cliCtx.String(cmd.TracingEndpointFlag.Name),
|
||||
@@ -165,7 +166,7 @@ func configureExecutionSetting(cliCtx *cli.Context) error {
|
||||
}
|
||||
|
||||
if !cliCtx.IsSet(flags.SuggestedFeeRecipient.Name) {
|
||||
log.Warnf("In order to receive transaction fees from proposing blocks, " +
|
||||
log.Warn("In order to receive transaction fees from proposing blocks, " +
|
||||
"you must provide flag --" + flags.SuggestedFeeRecipient.Name + " with a valid ethereum address when starting your beacon node. " +
|
||||
"Please see our documentation for more information on this requirement (https://docs.prylabs.network/docs/execution-node/fee-recipient).")
|
||||
return nil
|
||||
|
||||
@@ -122,6 +122,7 @@ type BeaconNode struct {
|
||||
BlobStorageOptions []filesystem.BlobStorageOption
|
||||
verifyInitWaiter *verification.InitializerWaiter
|
||||
syncChecker *initialsync.SyncChecker
|
||||
slasherEnabled bool
|
||||
}
|
||||
|
||||
// New creates a new node instance, sets up configuration options, and registers
|
||||
@@ -159,6 +160,7 @@ func New(cliCtx *cli.Context, cancel context.CancelFunc, opts ...Option) (*Beaco
|
||||
serviceFlagOpts: &serviceFlagOpts{},
|
||||
initialSyncComplete: make(chan struct{}),
|
||||
syncChecker: &initialsync.SyncChecker{},
|
||||
slasherEnabled: cliCtx.Bool(flags.SlasherFlag.Name),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
@@ -342,7 +344,7 @@ func registerServices(cliCtx *cli.Context, beacon *BeaconNode, synchronizer *sta
|
||||
return errors.Wrap(err, "could not register slashing pool service")
|
||||
}
|
||||
|
||||
log.Debugln("Registering Slasher Service")
|
||||
log.WithField("enabled", beacon.slasherEnabled).Debugln("Registering Slasher Service")
|
||||
if err := beacon.registerSlasherService(); err != nil {
|
||||
return errors.Wrap(err, "could not register slasher service")
|
||||
}
|
||||
@@ -587,7 +589,7 @@ func (b *BeaconNode) startDB(cliCtx *cli.Context, depositAddress string) error {
|
||||
}
|
||||
|
||||
func (b *BeaconNode) startSlasherDB(cliCtx *cli.Context) error {
|
||||
if !features.Get().EnableSlasher {
|
||||
if !b.slasherEnabled {
|
||||
return nil
|
||||
}
|
||||
baseDir := cliCtx.String(cmd.DataDirFlag.Name)
|
||||
@@ -775,6 +777,7 @@ func (b *BeaconNode) registerBlockchainService(fc forkchoice.ForkChoicer, gs *st
|
||||
blockchain.WithTrackedValidatorsCache(b.trackedValidatorsCache),
|
||||
blockchain.WithPayloadIDCache(b.payloadIDCache),
|
||||
blockchain.WithSyncChecker(b.syncChecker),
|
||||
blockchain.WithSlasherEnabled(b.slasherEnabled),
|
||||
)
|
||||
|
||||
blockchainService, err := blockchain.NewService(b.ctx, opts...)
|
||||
@@ -859,6 +862,7 @@ func (b *BeaconNode) registerSyncService(initialSyncComplete chan struct{}, bFil
|
||||
regularsync.WithBlobStorage(b.BlobStorage),
|
||||
regularsync.WithVerifierWaiter(b.verifyInitWaiter),
|
||||
regularsync.WithAvailableBlocker(bFillStore),
|
||||
regularsync.WithSlasherEnabled(b.slasherEnabled),
|
||||
)
|
||||
return b.services.RegisterService(rs)
|
||||
}
|
||||
@@ -887,7 +891,7 @@ func (b *BeaconNode) registerInitialSyncService(complete chan struct{}) error {
|
||||
}
|
||||
|
||||
func (b *BeaconNode) registerSlasherService() error {
|
||||
if !features.Get().EnableSlasher {
|
||||
if !b.slasherEnabled {
|
||||
return nil
|
||||
}
|
||||
var chainService *blockchain.Service
|
||||
@@ -934,7 +938,7 @@ func (b *BeaconNode) registerRPCService(router *http.ServeMux) error {
|
||||
}
|
||||
|
||||
var slasherService *slasher.Service
|
||||
if features.Get().EnableSlasher {
|
||||
if b.slasherEnabled {
|
||||
if err := b.services.FetchService(&slasherService); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ go_library(
|
||||
"doc.go",
|
||||
"log.go",
|
||||
"metrics.go",
|
||||
"pool.go",
|
||||
"service.go",
|
||||
"service_new.go",
|
||||
"types.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/slashings",
|
||||
|
||||
324
beacon-chain/operations/slashings/pool.go
Normal file
324
beacon-chain/operations/slashings/pool.go
Normal file
@@ -0,0 +1,324 @@
|
||||
package slashings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
|
||||
coretime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/container/slice"
|
||||
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"github.com/trailofbits/go-mutexasserts"
|
||||
)
|
||||
|
||||
// NewPool returns an initialized attester slashing and proposer slashing pool.
|
||||
func NewPool() *Pool {
|
||||
return &Pool{
|
||||
pendingProposerSlashing: make([]*ethpb.ProposerSlashing, 0),
|
||||
pendingAttesterSlashing: make([]*PendingAttesterSlashing, 0),
|
||||
included: make(map[primitives.ValidatorIndex]bool),
|
||||
}
|
||||
}
|
||||
|
||||
// PendingAttesterSlashings returns attester slashings that are able to be included into a block.
|
||||
// This method will return the amount of pending attester slashings for a block transition unless parameter `noLimit` is true
|
||||
// to indicate the request is for noLimit pending items.
|
||||
func (p *Pool) PendingAttesterSlashings(ctx context.Context, state state.ReadOnlyBeaconState, noLimit bool) []ethpb.AttSlashing {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
_, span := trace.StartSpan(ctx, "operations.PendingAttesterSlashing")
|
||||
defer span.End()
|
||||
|
||||
// Update prom metric.
|
||||
numPendingAttesterSlashings.Set(float64(len(p.pendingAttesterSlashing)))
|
||||
|
||||
included := make(map[primitives.ValidatorIndex]bool)
|
||||
|
||||
// Allocate pending slice with a capacity of maxAttesterSlashings or len(p.pendingAttesterSlashing)) depending on the request.
|
||||
maxSlashings := params.BeaconConfig().MaxAttesterSlashings
|
||||
|
||||
// EIP-7549: Beginning from Electra, the max attester slashings is reduced to 1.
|
||||
if state.Version() >= version.Electra {
|
||||
maxSlashings = params.BeaconConfig().MaxAttesterSlashingsElectra
|
||||
}
|
||||
if noLimit {
|
||||
maxSlashings = uint64(len(p.pendingAttesterSlashing))
|
||||
}
|
||||
pending := make([]ethpb.AttSlashing, 0, maxSlashings)
|
||||
for i := 0; i < len(p.pendingAttesterSlashing); i++ {
|
||||
if uint64(len(pending)) >= maxSlashings {
|
||||
break
|
||||
}
|
||||
slashing := p.pendingAttesterSlashing[i]
|
||||
valid, err := p.validatorSlashingPreconditionCheck(state, slashing.validatorToSlash)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not validate attester slashing")
|
||||
continue
|
||||
}
|
||||
if included[slashing.validatorToSlash] || !valid {
|
||||
p.pendingAttesterSlashing = append(p.pendingAttesterSlashing[:i], p.pendingAttesterSlashing[i+1:]...)
|
||||
i--
|
||||
continue
|
||||
}
|
||||
attSlashing := slashing.attesterSlashing
|
||||
slashedVal := slice.IntersectionUint64(
|
||||
attSlashing.FirstAttestation().GetAttestingIndices(),
|
||||
attSlashing.SecondAttestation().GetAttestingIndices(),
|
||||
)
|
||||
for _, idx := range slashedVal {
|
||||
included[primitives.ValidatorIndex(idx)] = true
|
||||
}
|
||||
|
||||
pending = append(pending, attSlashing)
|
||||
}
|
||||
|
||||
return pending
|
||||
}
|
||||
|
||||
// PendingProposerSlashings returns proposer slashings that are able to be included into a block.
|
||||
// This method will return the amount of pending proposer slashings for a block transition unless the `noLimit` parameter
|
||||
// is set to true to indicate the request is for noLimit pending items.
|
||||
func (p *Pool) PendingProposerSlashings(ctx context.Context, state state.ReadOnlyBeaconState, noLimit bool) []*ethpb.ProposerSlashing {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
_, span := trace.StartSpan(ctx, "operations.PendingProposerSlashing")
|
||||
defer span.End()
|
||||
|
||||
// Update prom metric.
|
||||
numPendingProposerSlashings.Set(float64(len(p.pendingProposerSlashing)))
|
||||
|
||||
// Allocate pending slice with a capacity of len(p.pendingProposerSlashing) or maxProposerSlashings depending on the request.
|
||||
maxSlashings := params.BeaconConfig().MaxProposerSlashings
|
||||
if noLimit {
|
||||
maxSlashings = uint64(len(p.pendingProposerSlashing))
|
||||
}
|
||||
pending := make([]*ethpb.ProposerSlashing, 0, maxSlashings)
|
||||
for i := 0; i < len(p.pendingProposerSlashing); i++ {
|
||||
if uint64(len(pending)) >= maxSlashings {
|
||||
break
|
||||
}
|
||||
slashing := p.pendingProposerSlashing[i]
|
||||
valid, err := p.validatorSlashingPreconditionCheck(state, slashing.Header_1.Header.ProposerIndex)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not validate proposer slashing")
|
||||
continue
|
||||
}
|
||||
if !valid {
|
||||
p.pendingProposerSlashing = append(p.pendingProposerSlashing[:i], p.pendingProposerSlashing[i+1:]...)
|
||||
i--
|
||||
continue
|
||||
}
|
||||
|
||||
pending = append(pending, slashing)
|
||||
}
|
||||
return pending
|
||||
}
|
||||
|
||||
// InsertAttesterSlashing into the pool. This method is a no-op if the attester slashing already exists in the pool,
|
||||
// has been included into a block recently, or the validator is already exited.
|
||||
func (p *Pool) InsertAttesterSlashing(
|
||||
ctx context.Context,
|
||||
state state.ReadOnlyBeaconState,
|
||||
slashing ethpb.AttSlashing,
|
||||
) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
ctx, span := trace.StartSpan(ctx, "operations.InsertAttesterSlashing")
|
||||
defer span.End()
|
||||
|
||||
if err := blocks.VerifyAttesterSlashing(ctx, state, slashing); err != nil {
|
||||
return errors.Wrap(err, "could not verify attester slashing")
|
||||
}
|
||||
|
||||
slashedVal := slice.IntersectionUint64(slashing.FirstAttestation().GetAttestingIndices(), slashing.SecondAttestation().GetAttestingIndices())
|
||||
cantSlash := make([]uint64, 0, len(slashedVal))
|
||||
slashingReason := ""
|
||||
for _, val := range slashedVal {
|
||||
// Has this validator index been included recently?
|
||||
ok, err := p.validatorSlashingPreconditionCheck(state, primitives.ValidatorIndex(val))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If the validator has already exited, has already been slashed, or if its index
|
||||
// has been recently included in the pool of slashings, skip including this indice.
|
||||
if !ok {
|
||||
slashingReason = "validator already exited/slashed or already recently included in slashings pool"
|
||||
cantSlash = append(cantSlash, val)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if the validator already exists in the list of slashings.
|
||||
// Use binary search to find the answer.
|
||||
found := sort.Search(len(p.pendingAttesterSlashing), func(i int) bool {
|
||||
return uint64(p.pendingAttesterSlashing[i].validatorToSlash) >= val
|
||||
})
|
||||
if found != len(p.pendingAttesterSlashing) && uint64(p.pendingAttesterSlashing[found].validatorToSlash) == val {
|
||||
slashingReason = "validator already exist in list of pending slashings, no need to attempt to slash again"
|
||||
cantSlash = append(cantSlash, val)
|
||||
continue
|
||||
}
|
||||
|
||||
pendingSlashing := &PendingAttesterSlashing{
|
||||
attesterSlashing: slashing,
|
||||
validatorToSlash: primitives.ValidatorIndex(val),
|
||||
}
|
||||
// Insert into pending list and sort again.
|
||||
p.pendingAttesterSlashing = append(p.pendingAttesterSlashing, pendingSlashing)
|
||||
sort.Slice(p.pendingAttesterSlashing, func(i, j int) bool {
|
||||
return p.pendingAttesterSlashing[i].validatorToSlash < p.pendingAttesterSlashing[j].validatorToSlash
|
||||
})
|
||||
numPendingAttesterSlashings.Set(float64(len(p.pendingAttesterSlashing)))
|
||||
}
|
||||
if len(cantSlash) == len(slashedVal) {
|
||||
return fmt.Errorf(
|
||||
"could not slash any of %d validators in submitted slashing: %s",
|
||||
len(slashedVal),
|
||||
slashingReason,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertProposerSlashing into the pool. This method is a no-op if the pending slashing already exists,
|
||||
// has been included recently, the validator is already exited, or the validator was already slashed.
|
||||
func (p *Pool) InsertProposerSlashing(
|
||||
ctx context.Context,
|
||||
state state.ReadOnlyBeaconState,
|
||||
slashing *ethpb.ProposerSlashing,
|
||||
) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
_, span := trace.StartSpan(ctx, "operations.InsertProposerSlashing")
|
||||
defer span.End()
|
||||
|
||||
if err := blocks.VerifyProposerSlashing(state, slashing); err != nil {
|
||||
return errors.Wrap(err, "could not verify proposer slashing")
|
||||
}
|
||||
|
||||
idx := slashing.Header_1.Header.ProposerIndex
|
||||
ok, err := p.validatorSlashingPreconditionCheck(state, idx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If the validator has already exited, has already been slashed, or if its index
|
||||
// has been recently included in the pool of slashings, do not process this new
|
||||
// slashing.
|
||||
if !ok {
|
||||
return fmt.Errorf("validator at index %d cannot be slashed", idx)
|
||||
}
|
||||
|
||||
// Check if the validator already exists in the list of slashings.
|
||||
// Use binary search to find the answer.
|
||||
found := sort.Search(len(p.pendingProposerSlashing), func(i int) bool {
|
||||
return p.pendingProposerSlashing[i].Header_1.Header.ProposerIndex >= slashing.Header_1.Header.ProposerIndex
|
||||
})
|
||||
if found != len(p.pendingProposerSlashing) && p.pendingProposerSlashing[found].Header_1.Header.ProposerIndex ==
|
||||
slashing.Header_1.Header.ProposerIndex {
|
||||
return errors.New("slashing object already exists in pending proposer slashings")
|
||||
}
|
||||
|
||||
// Insert into pending list and sort again.
|
||||
p.pendingProposerSlashing = append(p.pendingProposerSlashing, slashing)
|
||||
sort.Slice(p.pendingProposerSlashing, func(i, j int) bool {
|
||||
return p.pendingProposerSlashing[i].Header_1.Header.ProposerIndex < p.pendingProposerSlashing[j].Header_1.Header.ProposerIndex
|
||||
})
|
||||
numPendingProposerSlashings.Set(float64(len(p.pendingProposerSlashing)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarkIncludedAttesterSlashing is used when an attester slashing has been included in a beacon block.
|
||||
// Every block seen by this node that contains proposer slashings should call this method to include
|
||||
// the proposer slashings.
|
||||
func (p *Pool) MarkIncludedAttesterSlashing(as ethpb.AttSlashing) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
slashedVal := slice.IntersectionUint64(as.FirstAttestation().GetAttestingIndices(), as.SecondAttestation().GetAttestingIndices())
|
||||
for _, val := range slashedVal {
|
||||
i := sort.Search(len(p.pendingAttesterSlashing), func(i int) bool {
|
||||
return uint64(p.pendingAttesterSlashing[i].validatorToSlash) >= val
|
||||
})
|
||||
if i != len(p.pendingAttesterSlashing) && uint64(p.pendingAttesterSlashing[i].validatorToSlash) == val {
|
||||
p.pendingAttesterSlashing = append(p.pendingAttesterSlashing[:i], p.pendingAttesterSlashing[i+1:]...)
|
||||
}
|
||||
p.included[primitives.ValidatorIndex(val)] = true
|
||||
numAttesterSlashingsIncluded.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
// MarkIncludedProposerSlashing is used when an proposer slashing has been included in a beacon block.
|
||||
// Every block seen by this node that contains proposer slashings should call this method to include
|
||||
// the proposer slashings.
|
||||
func (p *Pool) MarkIncludedProposerSlashing(ps *ethpb.ProposerSlashing) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
i := sort.Search(len(p.pendingProposerSlashing), func(i int) bool {
|
||||
return p.pendingProposerSlashing[i].Header_1.Header.ProposerIndex >= ps.Header_1.Header.ProposerIndex
|
||||
})
|
||||
if i != len(p.pendingProposerSlashing) && p.pendingProposerSlashing[i].Header_1.Header.ProposerIndex == ps.Header_1.Header.ProposerIndex {
|
||||
p.pendingProposerSlashing = append(p.pendingProposerSlashing[:i], p.pendingProposerSlashing[i+1:]...)
|
||||
}
|
||||
p.included[ps.Header_1.Header.ProposerIndex] = true
|
||||
numProposerSlashingsIncluded.Inc()
|
||||
}
|
||||
|
||||
// ConvertToElectra converts all Phase0 attester slashings to Electra attester slashings.
|
||||
// This functionality is needed at the time of the Electra fork.
|
||||
func (p *Pool) ConvertToElectra() {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
for _, pas := range p.pendingAttesterSlashing {
|
||||
if pas.attesterSlashing.Version() == version.Phase0 {
|
||||
first := pas.attesterSlashing.FirstAttestation()
|
||||
second := pas.attesterSlashing.SecondAttestation()
|
||||
pas.attesterSlashing = ðpb.AttesterSlashingElectra{
|
||||
Attestation_1: ðpb.IndexedAttestationElectra{
|
||||
AttestingIndices: first.GetAttestingIndices(),
|
||||
Data: first.GetData(),
|
||||
Signature: first.GetSignature(),
|
||||
},
|
||||
Attestation_2: ðpb.IndexedAttestationElectra{
|
||||
AttestingIndices: second.GetAttestingIndices(),
|
||||
Data: second.GetData(),
|
||||
Signature: second.GetSignature(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this function checks a few items about a validator before proceeding with inserting
|
||||
// a proposer/attester slashing into the pool. First, it checks if the validator
|
||||
// has been recently included in the pool, then it checks if the validator is slashable.
|
||||
// Note: this method requires caller to hold the lock.
|
||||
func (p *Pool) validatorSlashingPreconditionCheck(
|
||||
state state.ReadOnlyBeaconState,
|
||||
valIdx primitives.ValidatorIndex,
|
||||
) (bool, error) {
|
||||
if !mutexasserts.RWMutexLocked(&p.lock) && !mutexasserts.RWMutexRLocked(&p.lock) {
|
||||
return false, errors.New("pool.validatorSlashingPreconditionCheck: caller must hold read/write lock")
|
||||
}
|
||||
|
||||
// Check if the validator index has been included recently.
|
||||
if p.included[valIdx] {
|
||||
return false, nil
|
||||
}
|
||||
validator, err := state.ValidatorAtIndexReadOnly(valIdx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// Checking if the validator is slashable.
|
||||
if !helpers.IsSlashableValidatorUsingTrie(validator, coretime.CurrentEpoch(state)) {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -2,323 +2,102 @@ package slashings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
|
||||
coretime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/startup"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/container/slice"
|
||||
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"github.com/trailofbits/go-mutexasserts"
|
||||
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||
)
|
||||
|
||||
// NewPool returns an initialized attester slashing and proposer slashing pool.
|
||||
func NewPool() *Pool {
|
||||
return &Pool{
|
||||
pendingProposerSlashing: make([]*ethpb.ProposerSlashing, 0),
|
||||
pendingAttesterSlashing: make([]*PendingAttesterSlashing, 0),
|
||||
included: make(map[primitives.ValidatorIndex]bool),
|
||||
// WithElectraTimer includes functional options for the blockchain service related to CLI flags.
|
||||
func WithElectraTimer(cw startup.ClockWaiter, currentSlotFn func() primitives.Slot) Option {
|
||||
return func(p *PoolService) error {
|
||||
p.runElectraTimer = true
|
||||
p.cw = cw
|
||||
p.currentSlotFn = currentSlotFn
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// PendingAttesterSlashings returns attester slashings that are able to be included into a block.
|
||||
// This method will return the amount of pending attester slashings for a block transition unless parameter `noLimit` is true
|
||||
// to indicate the request is for noLimit pending items.
|
||||
func (p *Pool) PendingAttesterSlashings(ctx context.Context, state state.ReadOnlyBeaconState, noLimit bool) []ethpb.AttSlashing {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
_, span := trace.StartSpan(ctx, "operations.PendingAttesterSlashing")
|
||||
defer span.End()
|
||||
|
||||
// Update prom metric.
|
||||
numPendingAttesterSlashings.Set(float64(len(p.pendingAttesterSlashing)))
|
||||
|
||||
included := make(map[primitives.ValidatorIndex]bool)
|
||||
|
||||
// Allocate pending slice with a capacity of maxAttesterSlashings or len(p.pendingAttesterSlashing)) depending on the request.
|
||||
maxSlashings := params.BeaconConfig().MaxAttesterSlashings
|
||||
|
||||
// EIP-7549: Beginning from Electra, the max attester slashings is reduced to 1.
|
||||
if state.Version() >= version.Electra {
|
||||
maxSlashings = params.BeaconConfig().MaxAttesterSlashingsElectra
|
||||
}
|
||||
if noLimit {
|
||||
maxSlashings = uint64(len(p.pendingAttesterSlashing))
|
||||
}
|
||||
pending := make([]ethpb.AttSlashing, 0, maxSlashings)
|
||||
for i := 0; i < len(p.pendingAttesterSlashing); i++ {
|
||||
if uint64(len(pending)) >= maxSlashings {
|
||||
break
|
||||
}
|
||||
slashing := p.pendingAttesterSlashing[i]
|
||||
valid, err := p.validatorSlashingPreconditionCheck(state, slashing.validatorToSlash)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not validate attester slashing")
|
||||
continue
|
||||
}
|
||||
if included[slashing.validatorToSlash] || !valid {
|
||||
p.pendingAttesterSlashing = append(p.pendingAttesterSlashing[:i], p.pendingAttesterSlashing[i+1:]...)
|
||||
i--
|
||||
continue
|
||||
}
|
||||
attSlashing := slashing.attesterSlashing
|
||||
slashedVal := slice.IntersectionUint64(
|
||||
attSlashing.FirstAttestation().GetAttestingIndices(),
|
||||
attSlashing.SecondAttestation().GetAttestingIndices(),
|
||||
)
|
||||
for _, idx := range slashedVal {
|
||||
included[primitives.ValidatorIndex(idx)] = true
|
||||
}
|
||||
|
||||
pending = append(pending, attSlashing)
|
||||
// NewPoolService returns a service that manages the Pool.
|
||||
func NewPoolService(ctx context.Context, pool PoolManager, opts ...Option) *PoolService {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
p := &PoolService{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
poolManager: pool,
|
||||
}
|
||||
|
||||
return pending
|
||||
for _, opt := range opts {
|
||||
if err := opt(p); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// PendingProposerSlashings returns proposer slashings that are able to be included into a block.
|
||||
// This method will return the amount of pending proposer slashings for a block transition unless the `noLimit` parameter
|
||||
// is set to true to indicate the request is for noLimit pending items.
|
||||
func (p *Pool) PendingProposerSlashings(ctx context.Context, state state.ReadOnlyBeaconState, noLimit bool) []*ethpb.ProposerSlashing {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
_, span := trace.StartSpan(ctx, "operations.PendingProposerSlashing")
|
||||
defer span.End()
|
||||
|
||||
// Update prom metric.
|
||||
numPendingProposerSlashings.Set(float64(len(p.pendingProposerSlashing)))
|
||||
|
||||
// Allocate pending slice with a capacity of len(p.pendingProposerSlashing) or maxProposerSlashings depending on the request.
|
||||
maxSlashings := params.BeaconConfig().MaxProposerSlashings
|
||||
if noLimit {
|
||||
maxSlashings = uint64(len(p.pendingProposerSlashing))
|
||||
}
|
||||
pending := make([]*ethpb.ProposerSlashing, 0, maxSlashings)
|
||||
for i := 0; i < len(p.pendingProposerSlashing); i++ {
|
||||
if uint64(len(pending)) >= maxSlashings {
|
||||
break
|
||||
}
|
||||
slashing := p.pendingProposerSlashing[i]
|
||||
valid, err := p.validatorSlashingPreconditionCheck(state, slashing.Header_1.Header.ProposerIndex)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not validate proposer slashing")
|
||||
continue
|
||||
}
|
||||
if !valid {
|
||||
p.pendingProposerSlashing = append(p.pendingProposerSlashing[:i], p.pendingProposerSlashing[i+1:]...)
|
||||
i--
|
||||
continue
|
||||
}
|
||||
|
||||
pending = append(pending, slashing)
|
||||
}
|
||||
return pending
|
||||
// Start the slashing pool service.
|
||||
func (p *PoolService) Start() {
|
||||
go p.run()
|
||||
}
|
||||
|
||||
// InsertAttesterSlashing into the pool. This method is a no-op if the attester slashing already exists in the pool,
|
||||
// has been included into a block recently, or the validator is already exited.
|
||||
func (p *Pool) InsertAttesterSlashing(
|
||||
ctx context.Context,
|
||||
state state.ReadOnlyBeaconState,
|
||||
slashing ethpb.AttSlashing,
|
||||
) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
ctx, span := trace.StartSpan(ctx, "operations.InsertAttesterSlashing")
|
||||
defer span.End()
|
||||
|
||||
if err := blocks.VerifyAttesterSlashing(ctx, state, slashing); err != nil {
|
||||
return errors.Wrap(err, "could not verify attester slashing")
|
||||
func (p *PoolService) run() {
|
||||
if !p.runElectraTimer {
|
||||
return
|
||||
}
|
||||
|
||||
slashedVal := slice.IntersectionUint64(slashing.FirstAttestation().GetAttestingIndices(), slashing.SecondAttestation().GetAttestingIndices())
|
||||
cantSlash := make([]uint64, 0, len(slashedVal))
|
||||
slashingReason := ""
|
||||
for _, val := range slashedVal {
|
||||
// Has this validator index been included recently?
|
||||
ok, err := p.validatorSlashingPreconditionCheck(state, primitives.ValidatorIndex(val))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If the validator has already exited, has already been slashed, or if its index
|
||||
// has been recently included in the pool of slashings, skip including this indice.
|
||||
if !ok {
|
||||
slashingReason = "validator already exited/slashed or already recently included in slashings pool"
|
||||
cantSlash = append(cantSlash, val)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if the validator already exists in the list of slashings.
|
||||
// Use binary search to find the answer.
|
||||
found := sort.Search(len(p.pendingAttesterSlashing), func(i int) bool {
|
||||
return uint64(p.pendingAttesterSlashing[i].validatorToSlash) >= val
|
||||
})
|
||||
if found != len(p.pendingAttesterSlashing) && uint64(p.pendingAttesterSlashing[found].validatorToSlash) == val {
|
||||
slashingReason = "validator already exist in list of pending slashings, no need to attempt to slash again"
|
||||
cantSlash = append(cantSlash, val)
|
||||
continue
|
||||
}
|
||||
|
||||
pendingSlashing := &PendingAttesterSlashing{
|
||||
attesterSlashing: slashing,
|
||||
validatorToSlash: primitives.ValidatorIndex(val),
|
||||
}
|
||||
// Insert into pending list and sort again.
|
||||
p.pendingAttesterSlashing = append(p.pendingAttesterSlashing, pendingSlashing)
|
||||
sort.Slice(p.pendingAttesterSlashing, func(i, j int) bool {
|
||||
return p.pendingAttesterSlashing[i].validatorToSlash < p.pendingAttesterSlashing[j].validatorToSlash
|
||||
})
|
||||
numPendingAttesterSlashings.Set(float64(len(p.pendingAttesterSlashing)))
|
||||
electraSlot, err := slots.EpochStart(params.BeaconConfig().ElectraForkEpoch)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(cantSlash) == len(slashedVal) {
|
||||
return fmt.Errorf(
|
||||
"could not slash any of %d validators in submitted slashing: %s",
|
||||
len(slashedVal),
|
||||
slashingReason,
|
||||
)
|
||||
|
||||
// If run() is executed after the transition to Electra has already happened,
|
||||
// there is nothing to convert because the slashing pool is empty at startup.
|
||||
if p.currentSlotFn() >= electraSlot {
|
||||
return
|
||||
}
|
||||
|
||||
p.waitForChainInitialization()
|
||||
|
||||
electraTime, err := slots.ToTime(uint64(p.clock.GenesisTime().Unix()), electraSlot)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
t := time.NewTimer(electraTime.Sub(p.clock.Now()))
|
||||
defer t.Stop()
|
||||
|
||||
select {
|
||||
case <-t.C:
|
||||
log.Info("Converting Phase0 slashings to Electra slashings")
|
||||
p.poolManager.ConvertToElectra()
|
||||
return
|
||||
case <-p.ctx.Done():
|
||||
log.Warn("Context cancelled, ConvertToElectra timer will not execute")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PoolService) waitForChainInitialization() {
|
||||
clock, err := p.cw.WaitForClock(p.ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not receive chain start notification")
|
||||
}
|
||||
p.clock = clock
|
||||
log.WithField("genesisTime", clock.GenesisTime()).Info(
|
||||
"Slashing pool service received chain initialization event",
|
||||
)
|
||||
}
|
||||
|
||||
// Stop the slashing pool service.
|
||||
func (p *PoolService) Stop() error {
|
||||
p.cancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertProposerSlashing into the pool. This method is a no-op if the pending slashing already exists,
|
||||
// has been included recently, the validator is already exited, or the validator was already slashed.
|
||||
func (p *Pool) InsertProposerSlashing(
|
||||
ctx context.Context,
|
||||
state state.ReadOnlyBeaconState,
|
||||
slashing *ethpb.ProposerSlashing,
|
||||
) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
_, span := trace.StartSpan(ctx, "operations.InsertProposerSlashing")
|
||||
defer span.End()
|
||||
|
||||
if err := blocks.VerifyProposerSlashing(state, slashing); err != nil {
|
||||
return errors.Wrap(err, "could not verify proposer slashing")
|
||||
}
|
||||
|
||||
idx := slashing.Header_1.Header.ProposerIndex
|
||||
ok, err := p.validatorSlashingPreconditionCheck(state, idx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If the validator has already exited, has already been slashed, or if its index
|
||||
// has been recently included in the pool of slashings, do not process this new
|
||||
// slashing.
|
||||
if !ok {
|
||||
return fmt.Errorf("validator at index %d cannot be slashed", idx)
|
||||
}
|
||||
|
||||
// Check if the validator already exists in the list of slashings.
|
||||
// Use binary search to find the answer.
|
||||
found := sort.Search(len(p.pendingProposerSlashing), func(i int) bool {
|
||||
return p.pendingProposerSlashing[i].Header_1.Header.ProposerIndex >= slashing.Header_1.Header.ProposerIndex
|
||||
})
|
||||
if found != len(p.pendingProposerSlashing) && p.pendingProposerSlashing[found].Header_1.Header.ProposerIndex ==
|
||||
slashing.Header_1.Header.ProposerIndex {
|
||||
return errors.New("slashing object already exists in pending proposer slashings")
|
||||
}
|
||||
|
||||
// Insert into pending list and sort again.
|
||||
p.pendingProposerSlashing = append(p.pendingProposerSlashing, slashing)
|
||||
sort.Slice(p.pendingProposerSlashing, func(i, j int) bool {
|
||||
return p.pendingProposerSlashing[i].Header_1.Header.ProposerIndex < p.pendingProposerSlashing[j].Header_1.Header.ProposerIndex
|
||||
})
|
||||
numPendingProposerSlashings.Set(float64(len(p.pendingProposerSlashing)))
|
||||
|
||||
// Status of the slashing pool service.
|
||||
func (p *PoolService) Status() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarkIncludedAttesterSlashing is used when an attester slashing has been included in a beacon block.
|
||||
// Every block seen by this node that contains proposer slashings should call this method to include
|
||||
// the proposer slashings.
|
||||
func (p *Pool) MarkIncludedAttesterSlashing(as ethpb.AttSlashing) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
slashedVal := slice.IntersectionUint64(as.FirstAttestation().GetAttestingIndices(), as.SecondAttestation().GetAttestingIndices())
|
||||
for _, val := range slashedVal {
|
||||
i := sort.Search(len(p.pendingAttesterSlashing), func(i int) bool {
|
||||
return uint64(p.pendingAttesterSlashing[i].validatorToSlash) >= val
|
||||
})
|
||||
if i != len(p.pendingAttesterSlashing) && uint64(p.pendingAttesterSlashing[i].validatorToSlash) == val {
|
||||
p.pendingAttesterSlashing = append(p.pendingAttesterSlashing[:i], p.pendingAttesterSlashing[i+1:]...)
|
||||
}
|
||||
p.included[primitives.ValidatorIndex(val)] = true
|
||||
numAttesterSlashingsIncluded.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
// MarkIncludedProposerSlashing is used when an proposer slashing has been included in a beacon block.
|
||||
// Every block seen by this node that contains proposer slashings should call this method to include
|
||||
// the proposer slashings.
|
||||
func (p *Pool) MarkIncludedProposerSlashing(ps *ethpb.ProposerSlashing) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
i := sort.Search(len(p.pendingProposerSlashing), func(i int) bool {
|
||||
return p.pendingProposerSlashing[i].Header_1.Header.ProposerIndex >= ps.Header_1.Header.ProposerIndex
|
||||
})
|
||||
if i != len(p.pendingProposerSlashing) && p.pendingProposerSlashing[i].Header_1.Header.ProposerIndex == ps.Header_1.Header.ProposerIndex {
|
||||
p.pendingProposerSlashing = append(p.pendingProposerSlashing[:i], p.pendingProposerSlashing[i+1:]...)
|
||||
}
|
||||
p.included[ps.Header_1.Header.ProposerIndex] = true
|
||||
numProposerSlashingsIncluded.Inc()
|
||||
}
|
||||
|
||||
// ConvertToElectra converts all Phase0 attester slashings to Electra attester slashings.
|
||||
// This functionality is needed at the time of the Electra fork.
|
||||
func (p *Pool) ConvertToElectra() {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
for _, pas := range p.pendingAttesterSlashing {
|
||||
if pas.attesterSlashing.Version() == version.Phase0 {
|
||||
first := pas.attesterSlashing.FirstAttestation()
|
||||
second := pas.attesterSlashing.SecondAttestation()
|
||||
pas.attesterSlashing = ðpb.AttesterSlashingElectra{
|
||||
Attestation_1: ðpb.IndexedAttestationElectra{
|
||||
AttestingIndices: first.GetAttestingIndices(),
|
||||
Data: first.GetData(),
|
||||
Signature: first.GetSignature(),
|
||||
},
|
||||
Attestation_2: ðpb.IndexedAttestationElectra{
|
||||
AttestingIndices: second.GetAttestingIndices(),
|
||||
Data: second.GetData(),
|
||||
Signature: second.GetSignature(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this function checks a few items about a validator before proceeding with inserting
|
||||
// a proposer/attester slashing into the pool. First, it checks if the validator
|
||||
// has been recently included in the pool, then it checks if the validator is slashable.
|
||||
// Note: this method requires caller to hold the lock.
|
||||
func (p *Pool) validatorSlashingPreconditionCheck(
|
||||
state state.ReadOnlyBeaconState,
|
||||
valIdx primitives.ValidatorIndex,
|
||||
) (bool, error) {
|
||||
if !mutexasserts.RWMutexLocked(&p.lock) && !mutexasserts.RWMutexRLocked(&p.lock) {
|
||||
return false, errors.New("pool.validatorSlashingPreconditionCheck: caller must hold read/write lock")
|
||||
}
|
||||
|
||||
// Check if the validator index has been included recently.
|
||||
if p.included[valIdx] {
|
||||
return false, nil
|
||||
}
|
||||
validator, err := state.ValidatorAtIndexReadOnly(valIdx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// Checking if the validator is slashable.
|
||||
if !helpers.IsSlashableValidatorUsingTrie(validator, coretime.CurrentEpoch(state)) {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
package slashings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/startup"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||
)
|
||||
|
||||
// WithElectraTimer includes functional options for the blockchain service related to CLI flags.
|
||||
func WithElectraTimer(cw startup.ClockWaiter, currentSlotFn func() primitives.Slot) Option {
|
||||
return func(p *PoolService) error {
|
||||
p.runElectraTimer = true
|
||||
p.cw = cw
|
||||
p.currentSlotFn = currentSlotFn
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewPoolService returns a service that manages the Pool.
|
||||
func NewPoolService(ctx context.Context, pool PoolManager, opts ...Option) *PoolService {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
p := &PoolService{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
poolManager: pool,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
if err := opt(p); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// Start the slashing pool service.
|
||||
func (p *PoolService) Start() {
|
||||
go p.run()
|
||||
}
|
||||
|
||||
func (p *PoolService) run() {
|
||||
if !p.runElectraTimer {
|
||||
return
|
||||
}
|
||||
|
||||
electraSlot, err := slots.EpochStart(params.BeaconConfig().ElectraForkEpoch)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If run() is executed after the transition to Electra has already happened,
|
||||
// there is nothing to convert because the slashing pool is empty at startup.
|
||||
if p.currentSlotFn() >= electraSlot {
|
||||
return
|
||||
}
|
||||
|
||||
p.waitForChainInitialization()
|
||||
|
||||
electraTime, err := slots.ToTime(uint64(p.clock.GenesisTime().Unix()), electraSlot)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
t := time.NewTimer(electraTime.Sub(p.clock.Now()))
|
||||
defer t.Stop()
|
||||
|
||||
select {
|
||||
case <-t.C:
|
||||
log.Info("Converting Phase0 slashings to Electra slashings")
|
||||
p.poolManager.ConvertToElectra()
|
||||
return
|
||||
case <-p.ctx.Done():
|
||||
log.Warn("Context cancelled, ConvertToElectra timer will not execute")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PoolService) waitForChainInitialization() {
|
||||
clock, err := p.cw.WaitForClock(p.ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not receive chain start notification")
|
||||
}
|
||||
p.clock = clock
|
||||
log.WithField("genesisTime", clock.GenesisTime()).Info(
|
||||
"Slashing pool service received chain initialization event",
|
||||
)
|
||||
}
|
||||
|
||||
// Stop the slashing pool service.
|
||||
func (p *PoolService) Stop() error {
|
||||
p.cancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Status of the slashing pool service.
|
||||
func (p *PoolService) Status() error {
|
||||
return nil
|
||||
}
|
||||
@@ -33,6 +33,9 @@ var (
|
||||
// AggregateAttestationMap maps the fork-version to the underlying data type for that
|
||||
// particular fork period.
|
||||
AggregateAttestationMap map[[4]byte]func() (ethpb.SignedAggregateAttAndProof, error)
|
||||
// AttesterSlashingMap maps the fork-version to the underlying data type for that particular
|
||||
// fork period.
|
||||
AttesterSlashingMap map[[4]byte]func() (ethpb.AttSlashing, error)
|
||||
)
|
||||
|
||||
// InitializeDataMaps initializes all the relevant object maps. This function is called to
|
||||
@@ -72,7 +75,7 @@ func InitializeDataMaps() {
|
||||
},
|
||||
bytesutil.ToBytes4(params.BeaconConfig().FuluForkVersion): func() (interfaces.ReadOnlySignedBeaconBlock, error) {
|
||||
return blocks.NewSignedBeaconBlock(
|
||||
ðpb.SignedBeaconBlockFulu{Block: ðpb.BeaconBlockFulu{Body: ðpb.BeaconBlockBodyFulu{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}}}},
|
||||
ðpb.SignedBeaconBlockFulu{Block: ðpb.BeaconBlockElectra{Body: ðpb.BeaconBlockBodyElectra{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}}}},
|
||||
)
|
||||
},
|
||||
}
|
||||
@@ -151,4 +154,29 @@ func InitializeDataMaps() {
|
||||
return ðpb.SignedAggregateAttestationAndProofElectra{}, nil
|
||||
},
|
||||
}
|
||||
|
||||
// Reset our aggregate attestation map.
|
||||
AttesterSlashingMap = map[[4]byte]func() (ethpb.AttSlashing, error){
|
||||
bytesutil.ToBytes4(params.BeaconConfig().GenesisForkVersion): func() (ethpb.AttSlashing, error) {
|
||||
return ðpb.AttesterSlashing{}, nil
|
||||
},
|
||||
bytesutil.ToBytes4(params.BeaconConfig().AltairForkVersion): func() (ethpb.AttSlashing, error) {
|
||||
return ðpb.AttesterSlashing{}, nil
|
||||
},
|
||||
bytesutil.ToBytes4(params.BeaconConfig().BellatrixForkVersion): func() (ethpb.AttSlashing, error) {
|
||||
return ðpb.AttesterSlashing{}, nil
|
||||
},
|
||||
bytesutil.ToBytes4(params.BeaconConfig().CapellaForkVersion): func() (ethpb.AttSlashing, error) {
|
||||
return ðpb.AttesterSlashing{}, nil
|
||||
},
|
||||
bytesutil.ToBytes4(params.BeaconConfig().DenebForkVersion): func() (ethpb.AttSlashing, error) {
|
||||
return ðpb.AttesterSlashing{}, nil
|
||||
},
|
||||
bytesutil.ToBytes4(params.BeaconConfig().ElectraForkVersion): func() (ethpb.AttSlashing, error) {
|
||||
return ðpb.AttesterSlashingElectra{}, nil
|
||||
},
|
||||
bytesutil.ToBytes4(params.BeaconConfig().FuluForkVersion): func() (ethpb.AttSlashing, error) {
|
||||
return ðpb.AttesterSlashingElectra{}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,13 @@ func TestInitializeDataMaps(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, version.Phase0, agg.Version())
|
||||
}
|
||||
attSlashFunc, ok := AttesterSlashingMap[bytesutil.ToBytes4(params.BeaconConfig().GenesisForkVersion)]
|
||||
assert.Equal(t, tt.exists, ok)
|
||||
if tt.exists {
|
||||
attSlash, err := attSlashFunc()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, version.Phase0, attSlash.Version())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@@ -28,7 +27,7 @@ func TestVerifyConnectivity(t *testing.T) {
|
||||
{"123.123.123.123", 19000, false, "Dialing an unreachable IP: 123.123.123.123:19000"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(fmt.Sprintf(tc.name),
|
||||
t.Run(tc.name,
|
||||
func(t *testing.T) {
|
||||
verifyConnectivity(tc.address, tc.port, "tcp")
|
||||
logMessage := "IP address is not accessible"
|
||||
|
||||
@@ -161,6 +161,7 @@ func (s *Service) builderEndpoints(stater lookup.Stater) []endpoint {
|
||||
const namespace = "builder"
|
||||
return []endpoint{
|
||||
{
|
||||
// Deprecated: use SSE from /eth/v1/events for `Payload Attributes` instead
|
||||
template: "/eth/v1/builder/states/{state_id}/expected_withdrawals",
|
||||
name: namespace + ".ExpectedWithdrawals",
|
||||
middleware: []middleware.Middleware{
|
||||
@@ -225,6 +226,7 @@ func (s *Service) validatorEndpoints(
|
||||
const namespace = "validator"
|
||||
return []endpoint{
|
||||
{
|
||||
// Deprecated: use /eth/v2/validator/aggregate_attestation instead
|
||||
template: "/eth/v1/validator/aggregate_attestation",
|
||||
name: namespace + ".GetAggregateAttestation",
|
||||
middleware: []middleware.Middleware{
|
||||
@@ -253,6 +255,7 @@ func (s *Service) validatorEndpoints(
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
// Deprecated: use /eth/v2/validator/aggregate_and_proofs instead
|
||||
template: "/eth/v1/validator/aggregate_and_proofs",
|
||||
name: namespace + ".SubmitAggregateAndProofs",
|
||||
middleware: []middleware.Middleware{
|
||||
@@ -583,6 +586,7 @@ func (s *Service) beaconEndpoints(
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
// Deprecated: use /eth/v2/beacon/blocks instead
|
||||
template: "/eth/v1/beacon/blocks",
|
||||
name: namespace + ".PublishBlock",
|
||||
middleware: []middleware.Middleware{
|
||||
@@ -593,6 +597,7 @@ func (s *Service) beaconEndpoints(
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
// Deprecated: use /eth/v2/beacon/blinded_blocks instead
|
||||
template: "/eth/v1/beacon/blinded_blocks",
|
||||
name: namespace + ".PublishBlindedBlock",
|
||||
middleware: []middleware.Middleware{
|
||||
@@ -632,6 +637,7 @@ func (s *Service) beaconEndpoints(
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
// Deprecated: use /eth/v2/beacon/blocks/{block_id}/attestations instead
|
||||
template: "/eth/v1/beacon/blocks/{block_id}/attestations",
|
||||
name: namespace + ".GetBlockAttestations",
|
||||
middleware: []middleware.Middleware{
|
||||
@@ -668,6 +674,7 @@ func (s *Service) beaconEndpoints(
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
// Deprecated: use /eth/v2/beacon/pool/attestations instead
|
||||
template: "/eth/v1/beacon/pool/attestations",
|
||||
name: namespace + ".ListAttestations",
|
||||
middleware: []middleware.Middleware{
|
||||
@@ -754,6 +761,7 @@ func (s *Service) beaconEndpoints(
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
// Deprecated: use /eth/v2/beacon/pool/attester_slashings instead
|
||||
template: "/eth/v1/beacon/pool/attester_slashings",
|
||||
name: namespace + ".GetAttesterSlashings",
|
||||
middleware: []middleware.Middleware{
|
||||
@@ -876,6 +884,7 @@ func (s *Service) beaconEndpoints(
|
||||
methods: []string{http.MethodGet, http.MethodPost},
|
||||
},
|
||||
{
|
||||
// Deprecated: no longer needed post Electra
|
||||
template: "/eth/v1/beacon/deposit_snapshot",
|
||||
name: namespace + ".GetDepositSnapshot",
|
||||
middleware: []middleware.Middleware{
|
||||
@@ -884,6 +893,24 @@ func (s *Service) beaconEndpoints(
|
||||
handler: server.GetDepositSnapshot,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/states/{state_id}/pending_deposits",
|
||||
name: namespace + ".GetPendingDeposits",
|
||||
middleware: []middleware.Middleware{
|
||||
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
|
||||
},
|
||||
handler: server.GetPendingDeposits,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/states/{state_id}/pending_partial_withdrawals",
|
||||
name: namespace + ".GetPendingPartialWithdrawals",
|
||||
middleware: []middleware.Middleware{
|
||||
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
|
||||
},
|
||||
handler: server.GetPendingPartialWithdrawals,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,38 +17,40 @@ func Test_endpoints(t *testing.T) {
|
||||
}
|
||||
|
||||
beaconRoutes := map[string][]string{
|
||||
"/eth/v1/beacon/genesis": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/root": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/fork": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/finality_checkpoints": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/validators": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/states/{state_id}/validators/{validator_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/validator_balances": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/states/{state_id}/committees": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/sync_committees": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/randao": {http.MethodGet},
|
||||
"/eth/v1/beacon/headers": {http.MethodGet},
|
||||
"/eth/v1/beacon/headers/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/blinded_blocks": {http.MethodPost},
|
||||
"/eth/v2/beacon/blinded_blocks": {http.MethodPost},
|
||||
"/eth/v1/beacon/blocks": {http.MethodPost},
|
||||
"/eth/v2/beacon/blocks": {http.MethodPost},
|
||||
"/eth/v2/beacon/blocks/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/blocks/{block_id}/root": {http.MethodGet},
|
||||
"/eth/v1/beacon/blocks/{block_id}/attestations": {http.MethodGet},
|
||||
"/eth/v2/beacon/blocks/{block_id}/attestations": {http.MethodGet},
|
||||
"/eth/v1/beacon/blob_sidecars/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/deposit_snapshot": {http.MethodGet},
|
||||
"/eth/v1/beacon/blinded_blocks/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/pool/attestations": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v2/beacon/pool/attestations": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v2/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/proposer_slashings": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/sync_committees": {http.MethodPost},
|
||||
"/eth/v1/beacon/pool/voluntary_exits": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/bls_to_execution_changes": {http.MethodGet, http.MethodPost},
|
||||
"/prysm/v1/beacon/individual_votes": {http.MethodPost},
|
||||
"/eth/v1/beacon/genesis": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/root": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/fork": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/finality_checkpoints": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/validators": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/states/{state_id}/validators/{validator_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/validator_balances": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/states/{state_id}/committees": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/sync_committees": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/randao": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/pending_deposits": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/pending_partial_withdrawals": {http.MethodGet},
|
||||
"/eth/v1/beacon/headers": {http.MethodGet},
|
||||
"/eth/v1/beacon/headers/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/blinded_blocks": {http.MethodPost},
|
||||
"/eth/v2/beacon/blinded_blocks": {http.MethodPost},
|
||||
"/eth/v1/beacon/blocks": {http.MethodPost},
|
||||
"/eth/v2/beacon/blocks": {http.MethodPost},
|
||||
"/eth/v2/beacon/blocks/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/blocks/{block_id}/root": {http.MethodGet},
|
||||
"/eth/v1/beacon/blocks/{block_id}/attestations": {http.MethodGet},
|
||||
"/eth/v2/beacon/blocks/{block_id}/attestations": {http.MethodGet},
|
||||
"/eth/v1/beacon/blob_sidecars/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/deposit_snapshot": {http.MethodGet},
|
||||
"/eth/v1/beacon/blinded_blocks/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/pool/attestations": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v2/beacon/pool/attestations": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v2/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/proposer_slashings": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/sync_committees": {http.MethodPost},
|
||||
"/eth/v1/beacon/pool/voluntary_exits": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/bls_to_execution_changes": {http.MethodGet, http.MethodPost},
|
||||
"/prysm/v1/beacon/individual_votes": {http.MethodPost},
|
||||
}
|
||||
|
||||
lightClientRoutes := map[string][]string{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,6 +34,7 @@ import (
|
||||
|
||||
const broadcastBLSChangesRateLimit = 128
|
||||
|
||||
// Deprecated: use ListAttestationsV2 instead
|
||||
// ListAttestations retrieves attestations known by the node but
|
||||
// not necessarily incorporated into any block. Allows filtering by committee index or slot.
|
||||
func (s *Server) ListAttestations(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -632,7 +633,7 @@ func (s *Server) SubmitBLSToExecutionChanges(w http.ResponseWriter, r *http.Requ
|
||||
toBroadcast = append(toBroadcast, sbls)
|
||||
}
|
||||
}
|
||||
go s.broadcastBLSChanges(ctx, toBroadcast)
|
||||
go s.broadcastBLSChanges(context.Background(), toBroadcast)
|
||||
if len(failures) > 0 {
|
||||
failuresErr := &server.IndexedVerificationFailureError{
|
||||
Code: http.StatusBadRequest,
|
||||
@@ -707,6 +708,7 @@ func (s *Server) ListBLSToExecutionChanges(w http.ResponseWriter, r *http.Reques
|
||||
})
|
||||
}
|
||||
|
||||
// Deprecated: use GetAttesterSlashingsV2 instead
|
||||
// GetAttesterSlashings retrieves attester slashings known by the node but
|
||||
// not necessarily incorporated into any block.
|
||||
func (s *Server) GetAttesterSlashings(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -327,7 +327,7 @@ func TestGetBlockV2(t *testing.T) {
|
||||
resp := &structs.GetBlockV2Response{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
assert.Equal(t, version.String(version.Fulu), resp.Version)
|
||||
sbb := &structs.SignedBeaconBlockFulu{Message: &structs.BeaconBlockFulu{}}
|
||||
sbb := &structs.SignedBeaconBlockFulu{Message: &structs.BeaconBlockElectra{}}
|
||||
require.NoError(t, json.Unmarshal(resp.Data.Message, sbb.Message))
|
||||
sbb.Signature = resp.Data.Signature
|
||||
blk, err := sbb.ToConsensus()
|
||||
@@ -1528,7 +1528,7 @@ func TestPublishBlock(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlock(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Phase0)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Phase0)), writer.Body.String())
|
||||
})
|
||||
t.Run("Fulu", func(t *testing.T) {
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
@@ -1564,7 +1564,7 @@ func TestPublishBlock(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlock(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Bellatrix)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Bellatrix)), writer.Body.String())
|
||||
})
|
||||
t.Run("wrong version header", func(t *testing.T) {
|
||||
server := &Server{
|
||||
@@ -1577,7 +1577,7 @@ func TestPublishBlock(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlock(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Capella)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Capella)), writer.Body.String())
|
||||
})
|
||||
t.Run("syncing", func(t *testing.T) {
|
||||
chainService := &chainMock.ChainService{}
|
||||
@@ -1612,6 +1612,20 @@ func TestVersionHeaderFromRequest(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Fulu), versionHead)
|
||||
})
|
||||
t.Run("Blinded Fulu block returns fulu header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.FuluForkEpoch = 7
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
var signedblock *structs.SignedBlindedBeaconBlockFulu
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.BlindedFuluBlock), &signedblock))
|
||||
signedblock.Message.Slot = fmt.Sprintf("%d", uint64(params.BeaconConfig().SlotsPerEpoch)*uint64(params.BeaconConfig().FuluForkEpoch))
|
||||
newBlock, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
versionHead, err := versionHeaderFromRequest(newBlock)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Fulu), versionHead)
|
||||
})
|
||||
t.Run("Electra block contents returns electra header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.ElectraForkEpoch = 6
|
||||
@@ -1626,6 +1640,20 @@ func TestVersionHeaderFromRequest(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Electra), versionHead)
|
||||
})
|
||||
t.Run("Blinded Electra block returns electra header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.ElectraForkEpoch = 6
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
var signedblock *structs.SignedBlindedBeaconBlockElectra
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.BlindedElectraBlock), &signedblock))
|
||||
signedblock.Message.Slot = fmt.Sprintf("%d", uint64(params.BeaconConfig().SlotsPerEpoch)*uint64(params.BeaconConfig().ElectraForkEpoch))
|
||||
newBlock, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
versionHead, err := versionHeaderFromRequest(newBlock)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Electra), versionHead)
|
||||
})
|
||||
t.Run("Deneb block contents returns deneb header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.DenebForkEpoch = 5
|
||||
@@ -1640,7 +1668,21 @@ func TestVersionHeaderFromRequest(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Deneb), versionHead)
|
||||
})
|
||||
t.Run("Capella block returns capella header", func(t *testing.T) {
|
||||
t.Run("Blinded Deneb block returns Deneb header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.DenebForkEpoch = 5
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
var signedblock *structs.SignedBlindedBeaconBlockDeneb
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.BlindedDenebBlock), &signedblock))
|
||||
signedblock.Message.Slot = fmt.Sprintf("%d", uint64(params.BeaconConfig().SlotsPerEpoch)*uint64(params.BeaconConfig().DenebForkEpoch))
|
||||
newBlock, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
versionHead, err := versionHeaderFromRequest(newBlock)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Deneb), versionHead)
|
||||
})
|
||||
t.Run("Capella block returns Capella header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.CapellaForkEpoch = 4
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
@@ -1654,6 +1696,20 @@ func TestVersionHeaderFromRequest(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Capella), versionHead)
|
||||
})
|
||||
t.Run("Blinded Capella block returns Capella header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.CapellaForkEpoch = 4
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
var signedblock *structs.SignedBlindedBeaconBlockCapella
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.BlindedCapellaBlock), &signedblock))
|
||||
signedblock.Message.Slot = fmt.Sprintf("%d", uint64(params.BeaconConfig().SlotsPerEpoch)*uint64(params.BeaconConfig().CapellaForkEpoch))
|
||||
newBlock, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
versionHead, err := versionHeaderFromRequest(newBlock)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Capella), versionHead)
|
||||
})
|
||||
t.Run("Bellatrix block returns capella header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.BellatrixForkEpoch = 3
|
||||
@@ -1668,6 +1724,20 @@ func TestVersionHeaderFromRequest(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Bellatrix), versionHead)
|
||||
})
|
||||
t.Run("Blinded Capella block returns Capella header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.BellatrixForkEpoch = 3
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
var signedblock *structs.SignedBlindedBeaconBlockBellatrix
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.BlindedBellatrixBlock), &signedblock))
|
||||
signedblock.Message.Slot = fmt.Sprintf("%d", uint64(params.BeaconConfig().SlotsPerEpoch)*uint64(params.BeaconConfig().BellatrixForkEpoch))
|
||||
newBlock, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
versionHead, err := versionHeaderFromRequest(newBlock)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Bellatrix), versionHead)
|
||||
})
|
||||
t.Run("Altair block returns capella header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.AltairForkEpoch = 2
|
||||
@@ -1840,8 +1910,7 @@ func TestPublishBlockSSZ(t *testing.T) {
|
||||
t.Run("Electra", func(t *testing.T) {
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
|
||||
// Convert back Fulu to Electra when there is at least one difference between Electra and Fulu blocks.
|
||||
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_Fulu)
|
||||
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_Electra)
|
||||
return ok
|
||||
}))
|
||||
server := &Server{
|
||||
@@ -1849,16 +1918,16 @@ func TestPublishBlockSSZ(t *testing.T) {
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
}
|
||||
|
||||
var blk structs.SignedBeaconBlockContentsFulu
|
||||
var blk structs.SignedBeaconBlockContentsElectra
|
||||
err := json.Unmarshal([]byte(rpctesting.FuluBlockContents), &blk)
|
||||
require.NoError(t, err)
|
||||
genericBlock, err := blk.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
ssz, err := genericBlock.GetFulu().MarshalSSZ()
|
||||
ssz, err := genericBlock.GetElectra().MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(ssz))
|
||||
request.Header.Set("Content-Type", api.OctetStreamMediaType)
|
||||
request.Header.Set(api.VersionHeader, version.String(version.Fulu))
|
||||
request.Header.Set(api.VersionHeader, version.String(version.Electra))
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlock(writer, request)
|
||||
@@ -1909,7 +1978,7 @@ func TestPublishBlockSSZ(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlock(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Bellatrix)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Bellatrix)), writer.Body.String())
|
||||
})
|
||||
t.Run("wrong version header", func(t *testing.T) {
|
||||
server := &Server{
|
||||
@@ -1930,7 +1999,7 @@ func TestPublishBlockSSZ(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlock(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Capella)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Capella)), writer.Body.String())
|
||||
})
|
||||
t.Run("syncing", func(t *testing.T) {
|
||||
chainService := &chainMock.ChainService{}
|
||||
@@ -2072,12 +2141,11 @@ func TestPublishBlindedBlock(t *testing.T) {
|
||||
t.Run("Blinded Electra", func(t *testing.T) {
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
|
||||
// Convert back Fulu to Electra when there is at least one difference between Electra and Fulu blocks.
|
||||
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedFulu)
|
||||
converted, err := structs.BlindedBeaconBlockFuluFromConsensus(block.BlindedFulu.Message)
|
||||
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedElectra)
|
||||
converted, err := structs.BlindedBeaconBlockElectraFromConsensus(block.BlindedElectra.Message)
|
||||
require.NoError(t, err)
|
||||
var signedblock *structs.SignedBlindedBeaconBlockFulu
|
||||
err = json.Unmarshal([]byte(rpctesting.BlindedFuluBlock), &signedblock)
|
||||
var signedblock *structs.SignedBlindedBeaconBlockElectra
|
||||
err = json.Unmarshal([]byte(rpctesting.BlindedElectraBlock), &signedblock)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, converted, signedblock.Message)
|
||||
return ok
|
||||
@@ -2094,6 +2162,52 @@ func TestPublishBlindedBlock(t *testing.T) {
|
||||
server.PublishBlindedBlock(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
})
|
||||
t.Run("Blinded Electra block without version header succeeds", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.ElectraForkEpoch = 6
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
var signedblock *structs.SignedBlindedBeaconBlockElectra
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.BlindedElectraBlock), &signedblock))
|
||||
signedblock.Message.Slot = fmt.Sprintf("%d", uint64(params.BeaconConfig().SlotsPerEpoch)*uint64(params.BeaconConfig().ElectraForkEpoch))
|
||||
newBlock, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
|
||||
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedElectra)
|
||||
converted, err := structs.BlindedBeaconBlockElectraFromConsensus(block.BlindedElectra.Message)
|
||||
require.NoError(t, err)
|
||||
var signedblock *structs.SignedBlindedBeaconBlockElectra
|
||||
err = json.Unmarshal(newBlock, &signedblock)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, converted, signedblock.Message)
|
||||
return ok
|
||||
}))
|
||||
server := &Server{
|
||||
V1Alpha1ValidatorServer: v1alpha1Server,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(newBlock))
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlindedBlock(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
})
|
||||
t.Run("Blinded Electra block without version header on wrong fork", func(t *testing.T) {
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
server := &Server{
|
||||
V1Alpha1ValidatorServer: v1alpha1Server,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
}
|
||||
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BlindedElectraBlock)))
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlindedBlock(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
// block is sent with slot == 1 which means it's in the phase0 fork
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Phase0)), writer.Body.String())
|
||||
})
|
||||
t.Run("Blinded Fulu", func(t *testing.T) {
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
|
||||
@@ -2129,7 +2243,7 @@ func TestPublishBlindedBlock(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlindedBlock(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Bellatrix)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Bellatrix)), writer.Body.String())
|
||||
})
|
||||
t.Run("wrong version header", func(t *testing.T) {
|
||||
server := &Server{
|
||||
@@ -2142,7 +2256,7 @@ func TestPublishBlindedBlock(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlindedBlock(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Capella)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Capella)), writer.Body.String())
|
||||
})
|
||||
t.Run("syncing", func(t *testing.T) {
|
||||
chainService := &chainMock.ChainService{}
|
||||
@@ -2305,8 +2419,7 @@ func TestPublishBlindedBlockSSZ(t *testing.T) {
|
||||
t.Run("Electra", func(t *testing.T) {
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
|
||||
// Convert back Fulu to Electra when there is at least one difference between Electra and Fulu blocks.
|
||||
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedFulu)
|
||||
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedElectra)
|
||||
return ok
|
||||
}))
|
||||
server := &Server{
|
||||
@@ -2314,16 +2427,16 @@ func TestPublishBlindedBlockSSZ(t *testing.T) {
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
}
|
||||
|
||||
var blk structs.SignedBlindedBeaconBlockFulu
|
||||
err := json.Unmarshal([]byte(rpctesting.BlindedFuluBlock), &blk)
|
||||
var blk structs.SignedBlindedBeaconBlockElectra
|
||||
err := json.Unmarshal([]byte(rpctesting.BlindedElectraBlock), &blk)
|
||||
require.NoError(t, err)
|
||||
genericBlock, err := blk.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
ssz, err := genericBlock.GetBlindedFulu().MarshalSSZ()
|
||||
ssz, err := genericBlock.GetBlindedElectra().MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(ssz))
|
||||
request.Header.Set("Content-Type", api.OctetStreamMediaType)
|
||||
request.Header.Set(api.VersionHeader, version.String(version.Fulu))
|
||||
request.Header.Set(api.VersionHeader, version.String(version.Electra))
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlindedBlock(writer, request)
|
||||
@@ -2366,7 +2479,7 @@ func TestPublishBlindedBlockSSZ(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlindedBlock(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Bellatrix)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Bellatrix)), writer.Body.String())
|
||||
})
|
||||
t.Run("wrong version header", func(t *testing.T) {
|
||||
server := &Server{
|
||||
@@ -2387,7 +2500,7 @@ func TestPublishBlindedBlockSSZ(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlindedBlock(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Capella)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Capella)), writer.Body.String())
|
||||
})
|
||||
t.Run("syncing", func(t *testing.T) {
|
||||
chainService := &chainMock.ChainService{}
|
||||
@@ -2585,7 +2698,7 @@ func TestPublishBlockV2(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlockV2(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block:", version.String(version.Bellatrix)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block:", version.String(version.Bellatrix)), writer.Body.String())
|
||||
})
|
||||
t.Run("wrong version header", func(t *testing.T) {
|
||||
server := &Server{
|
||||
@@ -2598,7 +2711,7 @@ func TestPublishBlockV2(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlockV2(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block:", version.String(version.Capella)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block:", version.String(version.Capella)), writer.Body.String())
|
||||
})
|
||||
t.Run("missing version header", func(t *testing.T) {
|
||||
server := &Server{
|
||||
@@ -2773,8 +2886,7 @@ func TestPublishBlockV2SSZ(t *testing.T) {
|
||||
t.Run("Electra", func(t *testing.T) {
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
|
||||
// Convert back Fulu to Electra when there is at least one difference between Electra and Fulu blocks.
|
||||
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_Fulu)
|
||||
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_Electra)
|
||||
return ok
|
||||
}))
|
||||
server := &Server{
|
||||
@@ -2782,16 +2894,16 @@ func TestPublishBlockV2SSZ(t *testing.T) {
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
}
|
||||
|
||||
var blk structs.SignedBeaconBlockContentsFulu
|
||||
err := json.Unmarshal([]byte(rpctesting.FuluBlockContents), &blk)
|
||||
var blk structs.SignedBeaconBlockContentsElectra
|
||||
err := json.Unmarshal([]byte(rpctesting.ElectraBlockContents), &blk)
|
||||
require.NoError(t, err)
|
||||
genericBlock, err := blk.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
ssz, err := genericBlock.GetFulu().MarshalSSZ()
|
||||
ssz, err := genericBlock.GetElectra().MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(ssz))
|
||||
request.Header.Set("Content-Type", api.OctetStreamMediaType)
|
||||
request.Header.Set(api.VersionHeader, version.String(version.Fulu))
|
||||
request.Header.Set(api.VersionHeader, version.String(version.Electra))
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlockV2(writer, request)
|
||||
@@ -2842,7 +2954,7 @@ func TestPublishBlockV2SSZ(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlockV2(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Bellatrix)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Bellatrix)), writer.Body.String())
|
||||
})
|
||||
t.Run("wrong version header", func(t *testing.T) {
|
||||
server := &Server{
|
||||
@@ -2863,7 +2975,7 @@ func TestPublishBlockV2SSZ(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlockV2(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Capella)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Capella)), writer.Body.String())
|
||||
})
|
||||
t.Run("missing version header", func(t *testing.T) {
|
||||
server := &Server{
|
||||
@@ -3018,12 +3130,11 @@ func TestPublishBlindedBlockV2(t *testing.T) {
|
||||
t.Run("Blinded Electra", func(t *testing.T) {
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
|
||||
// Convert back Fulu to Electra when there is at least one difference between Electra and Fulu blocks.
|
||||
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedFulu)
|
||||
converted, err := structs.BlindedBeaconBlockFuluFromConsensus(block.BlindedFulu.Message)
|
||||
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedElectra)
|
||||
converted, err := structs.BlindedBeaconBlockElectraFromConsensus(block.BlindedElectra.Message)
|
||||
require.NoError(t, err)
|
||||
var signedblock *structs.SignedBlindedBeaconBlockFulu
|
||||
err = json.Unmarshal([]byte(rpctesting.BlindedFuluBlock), &signedblock)
|
||||
var signedblock *structs.SignedBlindedBeaconBlockElectra
|
||||
err = json.Unmarshal([]byte(rpctesting.BlindedElectraBlock), &signedblock)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, converted, signedblock.Message)
|
||||
return ok
|
||||
@@ -3075,7 +3186,7 @@ func TestPublishBlindedBlockV2(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlindedBlockV2(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block:", version.String(version.Bellatrix)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block:", version.String(version.Bellatrix)), writer.Body.String())
|
||||
})
|
||||
t.Run("wrong version header", func(t *testing.T) {
|
||||
server := &Server{
|
||||
@@ -3088,7 +3199,7 @@ func TestPublishBlindedBlockV2(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlindedBlockV2(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Capella)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Capella)), writer.Body.String())
|
||||
})
|
||||
t.Run("missing version header", func(t *testing.T) {
|
||||
server := &Server{
|
||||
@@ -3263,8 +3374,7 @@ func TestPublishBlindedBlockV2SSZ(t *testing.T) {
|
||||
t.Run("Electra", func(t *testing.T) {
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
|
||||
// Convert back Fulu to Electra when there is at least one difference between Electra and Fulu blocks.
|
||||
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedFulu)
|
||||
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedElectra)
|
||||
return ok
|
||||
}))
|
||||
server := &Server{
|
||||
@@ -3272,16 +3382,16 @@ func TestPublishBlindedBlockV2SSZ(t *testing.T) {
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
}
|
||||
|
||||
var blk structs.SignedBlindedBeaconBlockFulu
|
||||
err := json.Unmarshal([]byte(rpctesting.BlindedFuluBlock), &blk)
|
||||
var blk structs.SignedBlindedBeaconBlockElectra
|
||||
err := json.Unmarshal([]byte(rpctesting.BlindedElectraBlock), &blk)
|
||||
require.NoError(t, err)
|
||||
genericBlock, err := blk.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
ssz, err := genericBlock.GetBlindedFulu().MarshalSSZ()
|
||||
ssz, err := genericBlock.GetBlindedElectra().MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(ssz))
|
||||
request.Header.Set("Content-Type", api.OctetStreamMediaType)
|
||||
request.Header.Set(api.VersionHeader, version.String(version.Fulu))
|
||||
request.Header.Set(api.VersionHeader, version.String(version.Electra))
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlindedBlock(writer, request)
|
||||
@@ -3324,7 +3434,7 @@ func TestPublishBlindedBlockV2SSZ(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlindedBlockV2(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Bellatrix)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Bellatrix)), writer.Body.String())
|
||||
})
|
||||
t.Run("wrong version header", func(t *testing.T) {
|
||||
server := &Server{
|
||||
@@ -3345,7 +3455,7 @@ func TestPublishBlindedBlockV2SSZ(t *testing.T) {
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlindedBlockV2(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Capella)), writer.Body.String())
|
||||
assert.StringContains(t, fmt.Sprintf("could not decode request body into %s consensus block", version.String(version.Capella)), writer.Body.String())
|
||||
})
|
||||
t.Run("missing version header", func(t *testing.T) {
|
||||
server := &Server{
|
||||
@@ -4644,3 +4754,386 @@ func Test_validateBlobSidecars(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.ErrorContains(t, "could not verify blob proof: can't verify opening proof", s.validateBlobSidecars(b, [][]byte{blob[:]}, [][]byte{proof[:]}))
|
||||
}
|
||||
|
||||
func TestGetPendingDeposits(t *testing.T) {
|
||||
st, _ := util.DeterministicGenesisStateElectra(t, 10)
|
||||
|
||||
validators := st.Validators()
|
||||
dummySig := make([]byte, 96)
|
||||
for j := 0; j < 96; j++ {
|
||||
dummySig[j] = byte(j)
|
||||
}
|
||||
deps := make([]*eth.PendingDeposit, 10)
|
||||
for i := 0; i < len(deps); i += 1 {
|
||||
deps[i] = ð.PendingDeposit{
|
||||
PublicKey: validators[i].PublicKey,
|
||||
WithdrawalCredentials: validators[i].WithdrawalCredentials,
|
||||
Amount: 100,
|
||||
Slot: 0,
|
||||
Signature: dummySig,
|
||||
}
|
||||
}
|
||||
require.NoError(t, st.SetPendingDeposits(deps))
|
||||
|
||||
chainService := &chainMock.ChainService{
|
||||
Optimistic: false,
|
||||
FinalizedRoots: map[[32]byte]bool{},
|
||||
}
|
||||
server := &Server{
|
||||
Stater: &testutil.MockStater{
|
||||
BeaconState: st,
|
||||
},
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
}
|
||||
|
||||
t.Run("json response", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/states/{state_id}/pending_deposits", nil)
|
||||
req.SetPathValue("state_id", "head")
|
||||
rec := httptest.NewRecorder()
|
||||
rec.Body = new(bytes.Buffer)
|
||||
|
||||
server.GetPendingDeposits(rec, req)
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
require.Equal(t, "electra", rec.Header().Get(api.VersionHeader))
|
||||
|
||||
var resp structs.GetPendingDepositsResponse
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
|
||||
|
||||
expectedVersion := version.String(st.Version())
|
||||
require.Equal(t, expectedVersion, resp.Version)
|
||||
|
||||
require.Equal(t, false, resp.ExecutionOptimistic)
|
||||
require.Equal(t, false, resp.Finalized)
|
||||
|
||||
expectedDeposits := structs.PendingDepositsFromConsensus(deps)
|
||||
require.DeepEqual(t, expectedDeposits, resp.Data)
|
||||
})
|
||||
t.Run("ssz response", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/states/{state_id}/pending_deposits", nil)
|
||||
req.Header.Set("Accept", "application/octet-stream")
|
||||
req.SetPathValue("state_id", "head")
|
||||
rec := httptest.NewRecorder()
|
||||
rec.Body = new(bytes.Buffer)
|
||||
|
||||
server.GetPendingDeposits(rec, req)
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
require.Equal(t, "electra", rec.Header().Get(api.VersionHeader))
|
||||
|
||||
responseBytes := rec.Body.Bytes()
|
||||
var recoveredDeposits []*eth.PendingDeposit
|
||||
|
||||
// Verify total size matches expected number of deposits
|
||||
depositSize := (ð.PendingDeposit{}).SizeSSZ()
|
||||
require.Equal(t, len(responseBytes), depositSize*len(deps))
|
||||
|
||||
for i := 0; i < len(deps); i++ {
|
||||
start := i * depositSize
|
||||
end := start + depositSize
|
||||
|
||||
var deposit eth.PendingDeposit
|
||||
require.NoError(t, deposit.UnmarshalSSZ(responseBytes[start:end]))
|
||||
recoveredDeposits = append(recoveredDeposits, &deposit)
|
||||
}
|
||||
require.DeepEqual(t, deps, recoveredDeposits)
|
||||
})
|
||||
t.Run("pre electra state", func(t *testing.T) {
|
||||
preElectraSt, _ := util.DeterministicGenesisStateDeneb(t, 1)
|
||||
preElectraServer := &Server{
|
||||
Stater: &testutil.MockStater{
|
||||
BeaconState: preElectraSt,
|
||||
},
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
}
|
||||
|
||||
// Test JSON request
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/states/{state_id}/pending_deposits", nil)
|
||||
req.SetPathValue("state_id", "head")
|
||||
rec := httptest.NewRecorder()
|
||||
rec.Body = new(bytes.Buffer)
|
||||
|
||||
preElectraServer.GetPendingDeposits(rec, req)
|
||||
require.Equal(t, http.StatusBadRequest, rec.Code)
|
||||
|
||||
var errResp struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
|
||||
require.Equal(t, "state_id is prior to electra", errResp.Message)
|
||||
|
||||
// Test SSZ request
|
||||
sszReq := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/states/{state_id}/pending_deposits", nil)
|
||||
sszReq.Header.Set("Accept", "application/octet-stream")
|
||||
sszReq.SetPathValue("state_id", "head")
|
||||
sszRec := httptest.NewRecorder()
|
||||
sszRec.Body = new(bytes.Buffer)
|
||||
|
||||
preElectraServer.GetPendingDeposits(sszRec, sszReq)
|
||||
require.Equal(t, http.StatusBadRequest, sszRec.Code)
|
||||
|
||||
var sszErrResp struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
require.NoError(t, json.Unmarshal(sszRec.Body.Bytes(), &sszErrResp))
|
||||
require.Equal(t, "state_id is prior to electra", sszErrResp.Message)
|
||||
})
|
||||
t.Run("missing state_id parameter", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/states/{state_id}/pending_deposits", nil)
|
||||
// Intentionally not setting state_id
|
||||
rec := httptest.NewRecorder()
|
||||
rec.Body = new(bytes.Buffer)
|
||||
|
||||
server.GetPendingDeposits(rec, req)
|
||||
require.Equal(t, http.StatusBadRequest, rec.Code)
|
||||
|
||||
var errResp struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
|
||||
require.Equal(t, "state_id is required in URL params", errResp.Message)
|
||||
})
|
||||
t.Run("optimistic node", func(t *testing.T) {
|
||||
optimisticChainService := &chainMock.ChainService{
|
||||
Optimistic: true,
|
||||
FinalizedRoots: map[[32]byte]bool{},
|
||||
}
|
||||
optimisticServer := &Server{
|
||||
Stater: server.Stater,
|
||||
OptimisticModeFetcher: optimisticChainService,
|
||||
FinalizationFetcher: optimisticChainService,
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/states/{state_id}/pending_deposits", nil)
|
||||
req.SetPathValue("state_id", "head")
|
||||
rec := httptest.NewRecorder()
|
||||
rec.Body = new(bytes.Buffer)
|
||||
|
||||
optimisticServer.GetPendingDeposits(rec, req)
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
|
||||
var resp structs.GetPendingDepositsResponse
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
|
||||
require.Equal(t, true, resp.ExecutionOptimistic)
|
||||
})
|
||||
|
||||
t.Run("finalized node", func(t *testing.T) {
|
||||
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
finalizedChainService := &chainMock.ChainService{
|
||||
Optimistic: false,
|
||||
FinalizedRoots: map[[32]byte]bool{blockRoot: true},
|
||||
}
|
||||
finalizedServer := &Server{
|
||||
Stater: server.Stater,
|
||||
OptimisticModeFetcher: finalizedChainService,
|
||||
FinalizationFetcher: finalizedChainService,
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/states/{state_id}/pending_deposits", nil)
|
||||
req.SetPathValue("state_id", "head")
|
||||
rec := httptest.NewRecorder()
|
||||
rec.Body = new(bytes.Buffer)
|
||||
|
||||
finalizedServer.GetPendingDeposits(rec, req)
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
|
||||
var resp structs.GetPendingDepositsResponse
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
|
||||
require.Equal(t, true, resp.Finalized)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetPendingPartialWithdrawals(t *testing.T) {
|
||||
st, _ := util.DeterministicGenesisStateElectra(t, 10)
|
||||
for i := 0; i < 10; i += 1 {
|
||||
err := st.AppendPendingPartialWithdrawal(
|
||||
ð.PendingPartialWithdrawal{
|
||||
Index: primitives.ValidatorIndex(i),
|
||||
Amount: 100,
|
||||
WithdrawableEpoch: primitives.Epoch(0),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
withdrawals, err := st.PendingPartialWithdrawals()
|
||||
require.NoError(t, err)
|
||||
|
||||
chainService := &chainMock.ChainService{
|
||||
Optimistic: false,
|
||||
FinalizedRoots: map[[32]byte]bool{},
|
||||
}
|
||||
server := &Server{
|
||||
Stater: &testutil.MockStater{
|
||||
BeaconState: st,
|
||||
},
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
}
|
||||
|
||||
t.Run("json response", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/states/{state_id}/pending_partial_withdrawals", nil)
|
||||
req.SetPathValue("state_id", "head")
|
||||
rec := httptest.NewRecorder()
|
||||
rec.Body = new(bytes.Buffer)
|
||||
|
||||
server.GetPendingPartialWithdrawals(rec, req)
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
require.Equal(t, "electra", rec.Header().Get(api.VersionHeader))
|
||||
|
||||
var resp structs.GetPendingPartialWithdrawalsResponse
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
|
||||
|
||||
expectedVersion := version.String(st.Version())
|
||||
require.Equal(t, expectedVersion, resp.Version)
|
||||
|
||||
require.Equal(t, false, resp.ExecutionOptimistic)
|
||||
require.Equal(t, false, resp.Finalized)
|
||||
|
||||
expectedWithdrawals := structs.PendingPartialWithdrawalsFromConsensus(withdrawals)
|
||||
require.DeepEqual(t, expectedWithdrawals, resp.Data)
|
||||
})
|
||||
|
||||
t.Run("ssz response", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/states/{state_id}/pending_partial_withdrawals", nil)
|
||||
req.Header.Set("Accept", "application/octet-stream")
|
||||
req.SetPathValue("state_id", "head")
|
||||
rec := httptest.NewRecorder()
|
||||
rec.Body = new(bytes.Buffer)
|
||||
|
||||
server.GetPendingPartialWithdrawals(rec, req)
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
require.Equal(t, "electra", rec.Header().Get(api.VersionHeader))
|
||||
|
||||
responseBytes := rec.Body.Bytes()
|
||||
var recoveredWithdrawals []*eth.PendingPartialWithdrawal
|
||||
|
||||
withdrawalSize := (ð.PendingPartialWithdrawal{}).SizeSSZ()
|
||||
require.Equal(t, len(responseBytes), withdrawalSize*len(withdrawals))
|
||||
|
||||
for i := 0; i < len(withdrawals); i++ {
|
||||
start := i * withdrawalSize
|
||||
end := start + withdrawalSize
|
||||
|
||||
var withdrawal eth.PendingPartialWithdrawal
|
||||
require.NoError(t, withdrawal.UnmarshalSSZ(responseBytes[start:end]))
|
||||
recoveredWithdrawals = append(recoveredWithdrawals, &withdrawal)
|
||||
}
|
||||
require.DeepEqual(t, withdrawals, recoveredWithdrawals)
|
||||
})
|
||||
|
||||
t.Run("pre electra state", func(t *testing.T) {
|
||||
preElectraSt, _ := util.DeterministicGenesisStateDeneb(t, 1)
|
||||
preElectraServer := &Server{
|
||||
Stater: &testutil.MockStater{
|
||||
BeaconState: preElectraSt,
|
||||
},
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
}
|
||||
|
||||
// Test JSON request
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/states/{state_id}/pending_partial_withdrawals", nil)
|
||||
req.SetPathValue("state_id", "head")
|
||||
rec := httptest.NewRecorder()
|
||||
rec.Body = new(bytes.Buffer)
|
||||
|
||||
preElectraServer.GetPendingPartialWithdrawals(rec, req)
|
||||
require.Equal(t, http.StatusBadRequest, rec.Code)
|
||||
|
||||
var errResp struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
|
||||
require.Equal(t, "state_id is prior to electra", errResp.Message)
|
||||
|
||||
// Test SSZ request
|
||||
sszReq := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/states/{state_id}/pending_partial_withdrawals", nil)
|
||||
sszReq.Header.Set("Accept", "application/octet-stream")
|
||||
sszReq.SetPathValue("state_id", "head")
|
||||
sszRec := httptest.NewRecorder()
|
||||
sszRec.Body = new(bytes.Buffer)
|
||||
|
||||
preElectraServer.GetPendingPartialWithdrawals(sszRec, sszReq)
|
||||
require.Equal(t, http.StatusBadRequest, sszRec.Code)
|
||||
|
||||
var sszErrResp struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
require.NoError(t, json.Unmarshal(sszRec.Body.Bytes(), &sszErrResp))
|
||||
require.Equal(t, "state_id is prior to electra", sszErrResp.Message)
|
||||
})
|
||||
|
||||
t.Run("missing state_id parameter", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/states/{state_id}/pending_partial_withdrawals", nil)
|
||||
// Intentionally not setting state_id
|
||||
rec := httptest.NewRecorder()
|
||||
rec.Body = new(bytes.Buffer)
|
||||
|
||||
server.GetPendingPartialWithdrawals(rec, req)
|
||||
require.Equal(t, http.StatusBadRequest, rec.Code)
|
||||
|
||||
var errResp struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
|
||||
require.Equal(t, "state_id is required in URL params", errResp.Message)
|
||||
})
|
||||
|
||||
t.Run("optimistic node", func(t *testing.T) {
|
||||
optimisticChainService := &chainMock.ChainService{
|
||||
Optimistic: true,
|
||||
FinalizedRoots: map[[32]byte]bool{},
|
||||
}
|
||||
optimisticServer := &Server{
|
||||
Stater: server.Stater,
|
||||
OptimisticModeFetcher: optimisticChainService,
|
||||
FinalizationFetcher: optimisticChainService,
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/states/{state_id}/pending_partial_withdrawals", nil)
|
||||
req.SetPathValue("state_id", "head")
|
||||
rec := httptest.NewRecorder()
|
||||
rec.Body = new(bytes.Buffer)
|
||||
|
||||
optimisticServer.GetPendingPartialWithdrawals(rec, req)
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
|
||||
var resp structs.GetPendingPartialWithdrawalsResponse
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
|
||||
require.Equal(t, true, resp.ExecutionOptimistic)
|
||||
})
|
||||
|
||||
t.Run("finalized node", func(t *testing.T) {
|
||||
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
finalizedChainService := &chainMock.ChainService{
|
||||
Optimistic: false,
|
||||
FinalizedRoots: map[[32]byte]bool{blockRoot: true},
|
||||
}
|
||||
finalizedServer := &Server{
|
||||
Stater: server.Stater,
|
||||
OptimisticModeFetcher: finalizedChainService,
|
||||
FinalizationFetcher: finalizedChainService,
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/states/{state_id}/pending_partial_withdrawals", nil)
|
||||
req.SetPathValue("state_id", "head")
|
||||
rec := httptest.NewRecorder()
|
||||
rec.Body = new(bytes.Buffer)
|
||||
|
||||
finalizedServer.GetPendingPartialWithdrawals(rec, req)
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
|
||||
var resp structs.GetPendingPartialWithdrawalsResponse
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
|
||||
require.Equal(t, true, resp.Finalized)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||
)
|
||||
|
||||
// Deprecated: use SSE from events for `payload attributes` instead
|
||||
// ExpectedWithdrawals get the withdrawals computed from the specified state, that will be included in the block that gets built on the specified state.
|
||||
func (s *Server) ExpectedWithdrawals(w http.ResponseWriter, r *http.Request) {
|
||||
// Retrieve beacon state
|
||||
@@ -125,7 +126,7 @@ func buildExpectedWithdrawalsData(withdrawals []*enginev1.Withdrawal) []*structs
|
||||
|
||||
func handleWrapError(err error, message string, code int) *httputil.DefaultJsonError {
|
||||
return &httputil.DefaultJsonError{
|
||||
Message: errors.Wrapf(err, message).Error(),
|
||||
Message: errors.Wrap(err, message).Error(),
|
||||
Code: code,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +160,8 @@ func TestGetSpec(t *testing.T) {
|
||||
config.MaxTransactionsPerPayload = 99
|
||||
config.FieldElementsPerBlob = 100
|
||||
config.KzgCommitmentInclusionProofDepth = 101
|
||||
config.BlobsidecarSubnetCount = 102
|
||||
config.BlobsidecarSubnetCountElectra = 103
|
||||
|
||||
var dbp [4]byte
|
||||
copy(dbp[:], []byte{'0', '0', '0', '1'})
|
||||
@@ -198,7 +200,7 @@ func TestGetSpec(t *testing.T) {
|
||||
data, ok := resp.Data.(map[string]interface{})
|
||||
require.Equal(t, true, ok)
|
||||
|
||||
assert.Equal(t, 168, len(data))
|
||||
assert.Equal(t, 170, len(data))
|
||||
for k, v := range data {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
switch k {
|
||||
@@ -559,6 +561,10 @@ func TestGetSpec(t *testing.T) {
|
||||
assert.Equal(t, "100", v)
|
||||
case "KZG_COMMITMENT_INCLUSION_PROOF_DEPTH":
|
||||
assert.Equal(t, "101", v)
|
||||
case "BLOB_SIDECAR_SUBNET_COUNT":
|
||||
assert.Equal(t, "102", v)
|
||||
case "BLOB_SIDECAR_SUBNET_COUNT_ELECTRA":
|
||||
assert.Equal(t, "103", v)
|
||||
default:
|
||||
t.Errorf("Incorrect key: %s", k)
|
||||
}
|
||||
|
||||
@@ -190,6 +190,7 @@ func (s *Server) GetForkChoice(w http.ResponseWriter, r *http.Request) {
|
||||
Balance: fmt.Sprintf("%d", n.Balance),
|
||||
ExecutionOptimistic: n.ExecutionOptimistic,
|
||||
TimeStamp: fmt.Sprintf("%d", n.Timestamp),
|
||||
Target: fmt.Sprintf("%#x", n.Target),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ func newTopicRequest(topics []string) (*topicRequest, error) {
|
||||
} else if topicsForOpsFeed[name] {
|
||||
req.needOpsFeed = true
|
||||
} else {
|
||||
return nil, errors.Wrapf(errInvalidTopicName, name)
|
||||
return nil, errors.Wrap(errInvalidTopicName, name)
|
||||
}
|
||||
req.topics[name] = true
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"handlers.go",
|
||||
"helpers.go",
|
||||
"server.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/light-client",
|
||||
@@ -17,11 +16,9 @@ go_library(
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/rpc/eth/shared:go_default_library",
|
||||
"//beacon-chain/rpc/lookup:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/interfaces:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//monitoring/tracing/trace:go_default_library",
|
||||
"//network/httputil:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v5/api"
|
||||
"github.com/prysmaticlabs/prysm/v5/api/server/structs"
|
||||
lightclient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/shared"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
@@ -182,18 +183,31 @@ func (s *Server) GetLightClientFinalityUpdate(w http.ResponseWriter, req *http.R
|
||||
return
|
||||
}
|
||||
|
||||
update, err := newLightClientFinalityUpdateFromBeaconState(ctx, s.ChainInfoFetcher.CurrentSlot(), st, block, attestedState, attestedBlock, finalizedBlock)
|
||||
update, err := lightclient.NewLightClientFinalityUpdateFromBeaconState(ctx, s.ChainInfoFetcher.CurrentSlot(), st, block, attestedState, attestedBlock, finalizedBlock)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, "Could not get light client finality update: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
response := &structs.LightClientFinalityUpdateResponse{
|
||||
Version: version.String(attestedState.Version()),
|
||||
Data: update,
|
||||
if httputil.RespondWithSsz(req) {
|
||||
ssz, err := update.MarshalSSZ()
|
||||
if err != nil {
|
||||
httputil.HandleError(w, "Could not marshal finality update to SSZ: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
httputil.WriteSsz(w, ssz, "light_client_finality_update.ssz")
|
||||
} else {
|
||||
updateStruct, err := structs.LightClientFinalityUpdateFromConsensus(update)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, "Could not convert light client finality update to API struct: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
response := &structs.LightClientFinalityUpdateResponse{
|
||||
Version: version.String(attestedState.Version()),
|
||||
Data: updateStruct,
|
||||
}
|
||||
httputil.WriteJson(w, response)
|
||||
}
|
||||
|
||||
httputil.WriteJson(w, response)
|
||||
}
|
||||
|
||||
// GetLightClientOptimisticUpdate - implements https://github.com/ethereum/beacon-APIs/blob/263f4ed6c263c967f13279c7a9f5629b51c5fc55/apis/beacon/light_client/optimistic_update.yaml
|
||||
@@ -232,18 +246,31 @@ func (s *Server) GetLightClientOptimisticUpdate(w http.ResponseWriter, req *http
|
||||
return
|
||||
}
|
||||
|
||||
update, err := newLightClientOptimisticUpdateFromBeaconState(ctx, s.ChainInfoFetcher.CurrentSlot(), st, block, attestedState, attestedBlock)
|
||||
update, err := lightclient.NewLightClientOptimisticUpdateFromBeaconState(ctx, s.ChainInfoFetcher.CurrentSlot(), st, block, attestedState, attestedBlock)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, "Could not get light client optimistic update: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
response := &structs.LightClientOptimisticUpdateResponse{
|
||||
Version: version.String(attestedState.Version()),
|
||||
Data: update,
|
||||
if httputil.RespondWithSsz(req) {
|
||||
ssz, err := update.MarshalSSZ()
|
||||
if err != nil {
|
||||
httputil.HandleError(w, "Could not marshal optimistic update to SSZ: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
httputil.WriteSsz(w, ssz, "light_client_optimistic_update.ssz")
|
||||
} else {
|
||||
updateStruct, err := structs.LightClientOptimisticUpdateFromConsensus(update)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, "Could not convert light client optimistic update to API struct: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
response := &structs.LightClientOptimisticUpdateResponse{
|
||||
Version: version.String(attestedState.Version()),
|
||||
Data: updateStruct,
|
||||
}
|
||||
httputil.WriteJson(w, response)
|
||||
}
|
||||
|
||||
httputil.WriteJson(w, response)
|
||||
}
|
||||
|
||||
// suitableBlock returns the latest block that satisfies all criteria required for creating a new update
|
||||
|
||||
@@ -1105,116 +1105,226 @@ func TestLightClientHandler_GetLightClientFinalityUpdate(t *testing.T) {
|
||||
EnableLightClient: true,
|
||||
})
|
||||
defer resetFn()
|
||||
|
||||
helpers.ClearCache()
|
||||
ctx := context.Background()
|
||||
config := params.BeaconConfig()
|
||||
slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
||||
|
||||
attestedState, err := util.NewBeaconStateAltair()
|
||||
require.NoError(t, err)
|
||||
err = attestedState.SetSlot(slot.Sub(1))
|
||||
require.NoError(t, err)
|
||||
t.Run("altair", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
||||
|
||||
require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{
|
||||
Epoch: config.AltairForkEpoch - 10,
|
||||
Root: make([]byte, 32),
|
||||
}))
|
||||
attestedState, err := util.NewBeaconStateAltair()
|
||||
require.NoError(t, err)
|
||||
err = attestedState.SetSlot(slot.Sub(1))
|
||||
require.NoError(t, err)
|
||||
|
||||
parent := util.NewBeaconBlockAltair()
|
||||
parent.Block.Slot = slot.Sub(1)
|
||||
require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{
|
||||
Epoch: config.AltairForkEpoch - 10,
|
||||
Root: make([]byte, 32),
|
||||
}))
|
||||
|
||||
signedParent, err := blocks.NewSignedBeaconBlock(parent)
|
||||
require.NoError(t, err)
|
||||
parent := util.NewBeaconBlockAltair()
|
||||
parent.Block.Slot = slot.Sub(1)
|
||||
|
||||
parentHeader, err := signedParent.Header()
|
||||
require.NoError(t, err)
|
||||
attestedHeader := parentHeader.Header
|
||||
signedParent, err := blocks.NewSignedBeaconBlock(parent)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = attestedState.SetLatestBlockHeader(attestedHeader)
|
||||
require.NoError(t, err)
|
||||
attestedStateRoot, err := attestedState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
parentHeader, err := signedParent.Header()
|
||||
require.NoError(t, err)
|
||||
attestedHeader := parentHeader.Header
|
||||
|
||||
// get a new signed block so the root is updated with the new state root
|
||||
parent.Block.StateRoot = attestedStateRoot[:]
|
||||
signedParent, err = blocks.NewSignedBeaconBlock(parent)
|
||||
require.NoError(t, err)
|
||||
err = attestedState.SetLatestBlockHeader(attestedHeader)
|
||||
require.NoError(t, err)
|
||||
attestedStateRoot, err := attestedState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
st, err := util.NewBeaconStateAltair()
|
||||
require.NoError(t, err)
|
||||
err = st.SetSlot(slot)
|
||||
require.NoError(t, err)
|
||||
// get a new signed block so the root is updated with the new state root
|
||||
parent.Block.StateRoot = attestedStateRoot[:]
|
||||
signedParent, err = blocks.NewSignedBeaconBlock(parent)
|
||||
require.NoError(t, err)
|
||||
|
||||
parentRoot, err := signedParent.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
st, err := util.NewBeaconStateAltair()
|
||||
require.NoError(t, err)
|
||||
err = st.SetSlot(slot)
|
||||
require.NoError(t, err)
|
||||
|
||||
block := util.NewBeaconBlockAltair()
|
||||
block.Block.Slot = slot
|
||||
block.Block.ParentRoot = parentRoot[:]
|
||||
parentRoot, err := signedParent.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
for i := uint64(0); i < config.SyncCommitteeSize; i++ {
|
||||
block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
|
||||
}
|
||||
block := util.NewBeaconBlockAltair()
|
||||
block.Block.Slot = slot
|
||||
block.Block.ParentRoot = parentRoot[:]
|
||||
|
||||
signedBlock, err := blocks.NewSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
for i := uint64(0); i < config.SyncCommitteeSize; i++ {
|
||||
block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
|
||||
}
|
||||
|
||||
h, err := signedBlock.Header()
|
||||
require.NoError(t, err)
|
||||
signedBlock, err := blocks.NewSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.SetLatestBlockHeader(h.Header)
|
||||
require.NoError(t, err)
|
||||
stateRoot, err := st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
h, err := signedBlock.Header()
|
||||
require.NoError(t, err)
|
||||
|
||||
// get a new signed block so the root is updated with the new state root
|
||||
block.Block.StateRoot = stateRoot[:]
|
||||
signedBlock, err = blocks.NewSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
err = st.SetLatestBlockHeader(h.Header)
|
||||
require.NoError(t, err)
|
||||
stateRoot, err := st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
root, err := block.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
// get a new signed block so the root is updated with the new state root
|
||||
block.Block.StateRoot = stateRoot[:]
|
||||
signedBlock, err = blocks.NewSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
|
||||
mockBlocker := &testutil.MockBlocker{
|
||||
RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{
|
||||
parentRoot: signedParent,
|
||||
root: signedBlock,
|
||||
},
|
||||
SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{
|
||||
slot.Sub(1): signedParent,
|
||||
slot: signedBlock,
|
||||
},
|
||||
}
|
||||
mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{
|
||||
root: true,
|
||||
}}
|
||||
mockChainInfoFetcher := &mock.ChainService{Slot: &slot}
|
||||
s := &Server{
|
||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{
|
||||
slot.Sub(1): attestedState,
|
||||
slot: st,
|
||||
}},
|
||||
Blocker: mockBlocker,
|
||||
HeadFetcher: mockChainService,
|
||||
ChainInfoFetcher: mockChainInfoFetcher,
|
||||
}
|
||||
request := httptest.NewRequest("GET", "http://foo.com", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
root, err := block.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
s.GetLightClientFinalityUpdate(writer, request)
|
||||
mockBlocker := &testutil.MockBlocker{
|
||||
RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{
|
||||
parentRoot: signedParent,
|
||||
root: signedBlock,
|
||||
},
|
||||
SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{
|
||||
slot.Sub(1): signedParent,
|
||||
slot: signedBlock,
|
||||
},
|
||||
}
|
||||
mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{
|
||||
root: true,
|
||||
}}
|
||||
mockChainInfoFetcher := &mock.ChainService{Slot: &slot}
|
||||
s := &Server{
|
||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{
|
||||
slot.Sub(1): attestedState,
|
||||
slot: st,
|
||||
}},
|
||||
Blocker: mockBlocker,
|
||||
HeadFetcher: mockChainService,
|
||||
ChainInfoFetcher: mockChainInfoFetcher,
|
||||
}
|
||||
request := httptest.NewRequest("GET", "http://foo.com", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
require.Equal(t, http.StatusOK, writer.Code)
|
||||
var resp *structs.LightClientUpdateResponse
|
||||
err = json.Unmarshal(writer.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
var respHeader structs.LightClientHeader
|
||||
err = json.Unmarshal(resp.Data.AttestedHeader, &respHeader)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "altair", resp.Version)
|
||||
require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot)
|
||||
require.NotNil(t, resp.Data)
|
||||
s.GetLightClientFinalityUpdate(writer, request)
|
||||
|
||||
require.Equal(t, http.StatusOK, writer.Code)
|
||||
var resp *structs.LightClientUpdateResponse
|
||||
err = json.Unmarshal(writer.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
var respHeader structs.LightClientHeader
|
||||
err = json.Unmarshal(resp.Data.AttestedHeader, &respHeader)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "altair", resp.Version)
|
||||
require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot)
|
||||
require.NotNil(t, resp.Data)
|
||||
})
|
||||
|
||||
t.Run("altair SSZ", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
||||
|
||||
attestedState, err := util.NewBeaconStateAltair()
|
||||
require.NoError(t, err)
|
||||
err = attestedState.SetSlot(slot.Sub(1))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{
|
||||
Epoch: config.AltairForkEpoch - 10,
|
||||
Root: make([]byte, 32),
|
||||
}))
|
||||
|
||||
parent := util.NewBeaconBlockAltair()
|
||||
parent.Block.Slot = slot.Sub(1)
|
||||
|
||||
signedParent, err := blocks.NewSignedBeaconBlock(parent)
|
||||
require.NoError(t, err)
|
||||
|
||||
parentHeader, err := signedParent.Header()
|
||||
require.NoError(t, err)
|
||||
attestedHeader := parentHeader.Header
|
||||
|
||||
err = attestedState.SetLatestBlockHeader(attestedHeader)
|
||||
require.NoError(t, err)
|
||||
attestedStateRoot, err := attestedState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// get a new signed block so the root is updated with the new state root
|
||||
parent.Block.StateRoot = attestedStateRoot[:]
|
||||
signedParent, err = blocks.NewSignedBeaconBlock(parent)
|
||||
require.NoError(t, err)
|
||||
|
||||
st, err := util.NewBeaconStateAltair()
|
||||
require.NoError(t, err)
|
||||
err = st.SetSlot(slot)
|
||||
require.NoError(t, err)
|
||||
|
||||
parentRoot, err := signedParent.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
block := util.NewBeaconBlockAltair()
|
||||
block.Block.Slot = slot
|
||||
block.Block.ParentRoot = parentRoot[:]
|
||||
|
||||
for i := uint64(0); i < config.SyncCommitteeSize; i++ {
|
||||
block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
|
||||
}
|
||||
|
||||
signedBlock, err := blocks.NewSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
|
||||
h, err := signedBlock.Header()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.SetLatestBlockHeader(h.Header)
|
||||
require.NoError(t, err)
|
||||
stateRoot, err := st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// get a new signed block so the root is updated with the new state root
|
||||
block.Block.StateRoot = stateRoot[:]
|
||||
signedBlock, err = blocks.NewSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
|
||||
root, err := block.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
mockBlocker := &testutil.MockBlocker{
|
||||
RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{
|
||||
parentRoot: signedParent,
|
||||
root: signedBlock,
|
||||
},
|
||||
SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{
|
||||
slot.Sub(1): signedParent,
|
||||
slot: signedBlock,
|
||||
},
|
||||
}
|
||||
mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{
|
||||
root: true,
|
||||
}}
|
||||
mockChainInfoFetcher := &mock.ChainService{Slot: &slot}
|
||||
s := &Server{
|
||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{
|
||||
slot.Sub(1): attestedState,
|
||||
slot: st,
|
||||
}},
|
||||
Blocker: mockBlocker,
|
||||
HeadFetcher: mockChainService,
|
||||
ChainInfoFetcher: mockChainInfoFetcher,
|
||||
}
|
||||
request := httptest.NewRequest("GET", "http://foo.com", nil)
|
||||
request.Header.Add("Accept", "application/octet-stream")
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.GetLightClientFinalityUpdate(writer, request)
|
||||
|
||||
require.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
var resp pb.LightClientFinalityUpdateAltair
|
||||
err = resp.UnmarshalSSZ(writer.Body.Bytes())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, attestedHeader.Slot, resp.AttestedHeader.Beacon.Slot)
|
||||
require.DeepEqual(t, attestedHeader.BodyRoot, resp.AttestedHeader.Beacon.BodyRoot)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLightClientHandler_GetLightClientOptimisticUpdate(t *testing.T) {
|
||||
@@ -1335,6 +1445,114 @@ func TestLightClientHandler_GetLightClientOptimisticUpdate(t *testing.T) {
|
||||
require.NotNil(t, resp.Data)
|
||||
})
|
||||
|
||||
t.Run("altair SSZ", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
||||
|
||||
attestedState, err := util.NewBeaconStateAltair()
|
||||
require.NoError(t, err)
|
||||
err = attestedState.SetSlot(slot.Sub(1))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{
|
||||
Epoch: config.AltairForkEpoch - 10,
|
||||
Root: make([]byte, 32),
|
||||
}))
|
||||
|
||||
parent := util.NewBeaconBlockAltair()
|
||||
parent.Block.Slot = slot.Sub(1)
|
||||
|
||||
signedParent, err := blocks.NewSignedBeaconBlock(parent)
|
||||
require.NoError(t, err)
|
||||
|
||||
parentHeader, err := signedParent.Header()
|
||||
require.NoError(t, err)
|
||||
attestedHeader := parentHeader.Header
|
||||
|
||||
err = attestedState.SetLatestBlockHeader(attestedHeader)
|
||||
require.NoError(t, err)
|
||||
attestedStateRoot, err := attestedState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// get a new signed block so the root is updated with the new state root
|
||||
parent.Block.StateRoot = attestedStateRoot[:]
|
||||
signedParent, err = blocks.NewSignedBeaconBlock(parent)
|
||||
require.NoError(t, err)
|
||||
|
||||
st, err := util.NewBeaconStateAltair()
|
||||
require.NoError(t, err)
|
||||
err = st.SetSlot(slot)
|
||||
require.NoError(t, err)
|
||||
|
||||
parentRoot, err := signedParent.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
block := util.NewBeaconBlockAltair()
|
||||
block.Block.Slot = slot
|
||||
block.Block.ParentRoot = parentRoot[:]
|
||||
|
||||
for i := uint64(0); i < config.SyncCommitteeSize; i++ {
|
||||
block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
|
||||
}
|
||||
|
||||
signedBlock, err := blocks.NewSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
|
||||
h, err := signedBlock.Header()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.SetLatestBlockHeader(h.Header)
|
||||
require.NoError(t, err)
|
||||
stateRoot, err := st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// get a new signed block so the root is updated with the new state root
|
||||
block.Block.StateRoot = stateRoot[:]
|
||||
signedBlock, err = blocks.NewSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
|
||||
root, err := block.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
mockBlocker := &testutil.MockBlocker{
|
||||
RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{
|
||||
parentRoot: signedParent,
|
||||
root: signedBlock,
|
||||
},
|
||||
SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{
|
||||
slot.Sub(1): signedParent,
|
||||
slot: signedBlock,
|
||||
},
|
||||
}
|
||||
mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{
|
||||
root: true,
|
||||
}}
|
||||
mockChainInfoFetcher := &mock.ChainService{Slot: &slot}
|
||||
s := &Server{
|
||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{
|
||||
slot.Sub(1): attestedState,
|
||||
slot: st,
|
||||
}},
|
||||
Blocker: mockBlocker,
|
||||
HeadFetcher: mockChainService,
|
||||
ChainInfoFetcher: mockChainInfoFetcher,
|
||||
}
|
||||
request := httptest.NewRequest("GET", "http://foo.com", nil)
|
||||
request.Header.Add("Accept", "application/octet-stream")
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.GetLightClientOptimisticUpdate(writer, request)
|
||||
|
||||
require.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
var resp pb.LightClientOptimisticUpdateAltair
|
||||
err = resp.UnmarshalSSZ(writer.Body.Bytes())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp.AttestedHeader.Beacon.Slot, attestedHeader.Slot)
|
||||
require.DeepEqual(t, resp.AttestedHeader.Beacon.BodyRoot, attestedHeader.BodyRoot)
|
||||
})
|
||||
|
||||
t.Run("capella", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
slot := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
||||
@@ -1445,6 +1663,114 @@ func TestLightClientHandler_GetLightClientOptimisticUpdate(t *testing.T) {
|
||||
require.NotNil(t, resp.Data)
|
||||
})
|
||||
|
||||
t.Run("capella SSZ", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
slot := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
||||
|
||||
attestedState, err := util.NewBeaconStateCapella()
|
||||
require.NoError(t, err)
|
||||
err = attestedState.SetSlot(slot.Sub(1))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{
|
||||
Epoch: config.AltairForkEpoch - 10,
|
||||
Root: make([]byte, 32),
|
||||
}))
|
||||
|
||||
parent := util.NewBeaconBlockCapella()
|
||||
parent.Block.Slot = slot.Sub(1)
|
||||
|
||||
signedParent, err := blocks.NewSignedBeaconBlock(parent)
|
||||
require.NoError(t, err)
|
||||
|
||||
parentHeader, err := signedParent.Header()
|
||||
require.NoError(t, err)
|
||||
attestedHeader := parentHeader.Header
|
||||
|
||||
err = attestedState.SetLatestBlockHeader(attestedHeader)
|
||||
require.NoError(t, err)
|
||||
attestedStateRoot, err := attestedState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// get a new signed block so the root is updated with the new state root
|
||||
parent.Block.StateRoot = attestedStateRoot[:]
|
||||
signedParent, err = blocks.NewSignedBeaconBlock(parent)
|
||||
require.NoError(t, err)
|
||||
|
||||
st, err := util.NewBeaconStateCapella()
|
||||
require.NoError(t, err)
|
||||
err = st.SetSlot(slot)
|
||||
require.NoError(t, err)
|
||||
|
||||
parentRoot, err := signedParent.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
block := util.NewBeaconBlockCapella()
|
||||
block.Block.Slot = slot
|
||||
block.Block.ParentRoot = parentRoot[:]
|
||||
|
||||
for i := uint64(0); i < config.SyncCommitteeSize; i++ {
|
||||
block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
|
||||
}
|
||||
|
||||
signedBlock, err := blocks.NewSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
|
||||
h, err := signedBlock.Header()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.SetLatestBlockHeader(h.Header)
|
||||
require.NoError(t, err)
|
||||
stateRoot, err := st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// get a new signed block so the root is updated with the new state root
|
||||
block.Block.StateRoot = stateRoot[:]
|
||||
signedBlock, err = blocks.NewSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
|
||||
root, err := block.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
mockBlocker := &testutil.MockBlocker{
|
||||
RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{
|
||||
parentRoot: signedParent,
|
||||
root: signedBlock,
|
||||
},
|
||||
SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{
|
||||
slot.Sub(1): signedParent,
|
||||
slot: signedBlock,
|
||||
},
|
||||
}
|
||||
mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{
|
||||
root: true,
|
||||
}}
|
||||
mockChainInfoFetcher := &mock.ChainService{Slot: &slot}
|
||||
s := &Server{
|
||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{
|
||||
slot.Sub(1): attestedState,
|
||||
slot: st,
|
||||
}},
|
||||
Blocker: mockBlocker,
|
||||
HeadFetcher: mockChainService,
|
||||
ChainInfoFetcher: mockChainInfoFetcher,
|
||||
}
|
||||
request := httptest.NewRequest("GET", "http://foo.com", nil)
|
||||
request.Header.Add("Accept", "application/octet-stream")
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.GetLightClientOptimisticUpdate(writer, request)
|
||||
|
||||
require.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
var resp pb.LightClientOptimisticUpdateCapella
|
||||
err = resp.UnmarshalSSZ(writer.Body.Bytes())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp.AttestedHeader.Beacon.Slot, attestedHeader.Slot)
|
||||
require.DeepEqual(t, resp.AttestedHeader.Beacon.BodyRoot, attestedHeader.BodyRoot)
|
||||
})
|
||||
|
||||
t.Run("deneb", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
slot := primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
||||
@@ -1554,6 +1880,114 @@ func TestLightClientHandler_GetLightClientOptimisticUpdate(t *testing.T) {
|
||||
require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot)
|
||||
require.NotNil(t, resp.Data)
|
||||
})
|
||||
|
||||
t.Run("deneb SSZ", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
slot := primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
||||
|
||||
attestedState, err := util.NewBeaconStateDeneb()
|
||||
require.NoError(t, err)
|
||||
err = attestedState.SetSlot(slot.Sub(1))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{
|
||||
Epoch: config.AltairForkEpoch - 10,
|
||||
Root: make([]byte, 32),
|
||||
}))
|
||||
|
||||
parent := util.NewBeaconBlockDeneb()
|
||||
parent.Block.Slot = slot.Sub(1)
|
||||
|
||||
signedParent, err := blocks.NewSignedBeaconBlock(parent)
|
||||
require.NoError(t, err)
|
||||
|
||||
parentHeader, err := signedParent.Header()
|
||||
require.NoError(t, err)
|
||||
attestedHeader := parentHeader.Header
|
||||
|
||||
err = attestedState.SetLatestBlockHeader(attestedHeader)
|
||||
require.NoError(t, err)
|
||||
attestedStateRoot, err := attestedState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// get a new signed block so the root is updated with the new state root
|
||||
parent.Block.StateRoot = attestedStateRoot[:]
|
||||
signedParent, err = blocks.NewSignedBeaconBlock(parent)
|
||||
require.NoError(t, err)
|
||||
|
||||
st, err := util.NewBeaconStateDeneb()
|
||||
require.NoError(t, err)
|
||||
err = st.SetSlot(slot)
|
||||
require.NoError(t, err)
|
||||
|
||||
parentRoot, err := signedParent.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
block := util.NewBeaconBlockDeneb()
|
||||
block.Block.Slot = slot
|
||||
block.Block.ParentRoot = parentRoot[:]
|
||||
|
||||
for i := uint64(0); i < config.SyncCommitteeSize; i++ {
|
||||
block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
|
||||
}
|
||||
|
||||
signedBlock, err := blocks.NewSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
|
||||
h, err := signedBlock.Header()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = st.SetLatestBlockHeader(h.Header)
|
||||
require.NoError(t, err)
|
||||
stateRoot, err := st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// get a new signed block so the root is updated with the new state root
|
||||
block.Block.StateRoot = stateRoot[:]
|
||||
signedBlock, err = blocks.NewSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
|
||||
root, err := block.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
mockBlocker := &testutil.MockBlocker{
|
||||
RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{
|
||||
parentRoot: signedParent,
|
||||
root: signedBlock,
|
||||
},
|
||||
SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{
|
||||
slot.Sub(1): signedParent,
|
||||
slot: signedBlock,
|
||||
},
|
||||
}
|
||||
mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{
|
||||
root: true,
|
||||
}}
|
||||
mockChainInfoFetcher := &mock.ChainService{Slot: &slot}
|
||||
s := &Server{
|
||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{
|
||||
slot.Sub(1): attestedState,
|
||||
slot: st,
|
||||
}},
|
||||
Blocker: mockBlocker,
|
||||
HeadFetcher: mockChainService,
|
||||
ChainInfoFetcher: mockChainInfoFetcher,
|
||||
}
|
||||
request := httptest.NewRequest("GET", "http://foo.com", nil)
|
||||
request.Header.Add("Accept", "application/octet-stream")
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.GetLightClientOptimisticUpdate(writer, request)
|
||||
|
||||
require.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
var resp pb.LightClientOptimisticUpdateDeneb
|
||||
err = resp.UnmarshalSSZ(writer.Body.Bytes())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp.AttestedHeader.Beacon.Slot, attestedHeader.Slot)
|
||||
require.DeepEqual(t, resp.AttestedHeader.Beacon.BodyRoot, attestedHeader.BodyRoot)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLightClientHandler_GetLightClientEventBlock(t *testing.T) {
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package lightclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v5/api/server/structs"
|
||||
lightclient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||
)
|
||||
|
||||
func newLightClientFinalityUpdateFromBeaconState(
|
||||
ctx context.Context,
|
||||
currentSlot primitives.Slot,
|
||||
state state.BeaconState,
|
||||
block interfaces.ReadOnlySignedBeaconBlock,
|
||||
attestedState state.BeaconState,
|
||||
attestedBlock interfaces.ReadOnlySignedBeaconBlock,
|
||||
finalizedBlock interfaces.ReadOnlySignedBeaconBlock,
|
||||
) (*structs.LightClientFinalityUpdate, error) {
|
||||
result, err := lightclient.NewLightClientFinalityUpdateFromBeaconState(ctx, currentSlot, state, block, attestedState, attestedBlock, finalizedBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return structs.LightClientFinalityUpdateFromConsensus(result)
|
||||
}
|
||||
|
||||
func newLightClientOptimisticUpdateFromBeaconState(
|
||||
ctx context.Context,
|
||||
currentSlot primitives.Slot,
|
||||
state state.BeaconState,
|
||||
block interfaces.ReadOnlySignedBeaconBlock,
|
||||
attestedState state.BeaconState,
|
||||
attestedBlock interfaces.ReadOnlySignedBeaconBlock,
|
||||
) (*structs.LightClientOptimisticUpdate, error) {
|
||||
result, err := lightclient.NewLightClientOptimisticUpdateFromBeaconState(ctx, currentSlot, state, block, attestedState, attestedBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return structs.LightClientOptimisticUpdateFromConsensus(result)
|
||||
}
|
||||
@@ -239,7 +239,7 @@ func TestGetPeers(t *testing.T) {
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected ID '" + expectedId + "' not found")
|
||||
t.Error("Expected ID '" + expectedId + "' not found")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -43,6 +43,7 @@ import (
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// Deprecated: use GetAggregateAttestationV2 instead
|
||||
// GetAggregateAttestation aggregates all attestations matching the given attestation data root and slot, returning the aggregated result.
|
||||
func (s *Server) GetAggregateAttestation(w http.ResponseWriter, r *http.Request) {
|
||||
_, span := trace.StartSpan(r.Context(), "validator.GetAggregateAttestation")
|
||||
@@ -256,6 +257,7 @@ func (s *Server) SubmitContributionAndProofs(w http.ResponseWriter, r *http.Requ
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated: use SubmitAggregateAndProofsV2 instead
|
||||
// SubmitAggregateAndProofs verifies given aggregate and proofs and publishes them on appropriate gossipsub topic.
|
||||
func (s *Server) SubmitAggregateAndProofs(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := trace.StartSpan(r.Context(), "validator.SubmitAggregateAndProofs")
|
||||
@@ -634,6 +636,15 @@ func (s *Server) ProduceSyncCommitteeContribution(w http.ResponseWriter, r *http
|
||||
ctx, span := trace.StartSpan(r.Context(), "validator.ProduceSyncCommitteeContribution")
|
||||
defer span.End()
|
||||
|
||||
isOptimistic, err := s.OptimisticModeFetcher.IsOptimistic(ctx)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if isOptimistic {
|
||||
httputil.HandleError(w, "Beacon node is currently syncing and not serving request on that endpoint", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
_, index, ok := shared.UintFromQuery(w, r, "subcommittee_index", true)
|
||||
if !ok {
|
||||
return
|
||||
|
||||
@@ -2063,4 +2063,86 @@ func TestProduceBlockV3SSZ(t *testing.T) {
|
||||
require.Equal(t, "electra", writer.Header().Get(api.VersionHeader))
|
||||
require.Equal(t, "10000000000", writer.Header().Get(api.ConsensusBlockValueHeader))
|
||||
})
|
||||
t.Run("Fulu", func(t *testing.T) {
|
||||
var block *structs.SignedBeaconBlockContentsFulu
|
||||
err := json.Unmarshal([]byte(rpctesting.FuluBlockContents), &block)
|
||||
require.NoError(t, err)
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().GetBeaconBlock(gomock.Any(), ð.BlockRequest{
|
||||
Slot: 1,
|
||||
RandaoReveal: bRandao,
|
||||
Graffiti: bGraffiti,
|
||||
SkipMevBoost: false,
|
||||
}).Return(
|
||||
func() (*eth.GenericBeaconBlock, error) {
|
||||
b, err := block.ToUnsigned().ToGeneric()
|
||||
require.NoError(t, err)
|
||||
b.PayloadValue = "2000"
|
||||
return b, nil
|
||||
}())
|
||||
server := &Server{
|
||||
V1Alpha1Server: v1alpha1Server,
|
||||
SyncChecker: syncChecker,
|
||||
OptimisticModeFetcher: chainService,
|
||||
BlockRewardFetcher: rewardFetcher,
|
||||
}
|
||||
request := httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://foo.example/eth/v3/validator/blocks/1?randao_reveal=%s&graffiti=%s", randao, graffiti), nil)
|
||||
request.Header.Set("Accept", api.OctetStreamMediaType)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.ProduceBlockV3(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
g, err := block.ToUnsigned().ToGeneric()
|
||||
require.NoError(t, err)
|
||||
bl, ok := g.Block.(*eth.GenericBeaconBlock_Fulu)
|
||||
require.Equal(t, true, ok)
|
||||
ssz, err := bl.Fulu.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(ssz), writer.Body.String())
|
||||
require.Equal(t, "false", writer.Header().Get(api.ExecutionPayloadBlindedHeader))
|
||||
require.Equal(t, "2000", writer.Header().Get(api.ExecutionPayloadValueHeader))
|
||||
require.Equal(t, "fulu", writer.Header().Get(api.VersionHeader))
|
||||
require.Equal(t, "10000000000", writer.Header().Get(api.ConsensusBlockValueHeader))
|
||||
})
|
||||
t.Run("Blinded Fulu", func(t *testing.T) {
|
||||
var block *structs.SignedBlindedBeaconBlockFulu
|
||||
err := json.Unmarshal([]byte(rpctesting.BlindedFuluBlock), &block)
|
||||
require.NoError(t, err)
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().GetBeaconBlock(gomock.Any(), ð.BlockRequest{
|
||||
Slot: 1,
|
||||
RandaoReveal: bRandao,
|
||||
Graffiti: bGraffiti,
|
||||
SkipMevBoost: false,
|
||||
}).Return(
|
||||
func() (*eth.GenericBeaconBlock, error) {
|
||||
b, err := block.Message.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
b.PayloadValue = "2000"
|
||||
return b, nil
|
||||
}())
|
||||
server := &Server{
|
||||
V1Alpha1Server: v1alpha1Server,
|
||||
SyncChecker: syncChecker,
|
||||
OptimisticModeFetcher: chainService,
|
||||
BlockRewardFetcher: rewardFetcher,
|
||||
}
|
||||
request := httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://foo.example/eth/v3/validator/blocks/1?randao_reveal=%s&graffiti=%s", randao, graffiti), nil)
|
||||
request.Header.Set("Accept", api.OctetStreamMediaType)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.ProduceBlockV3(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
g, err := block.Message.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
bl, ok := g.Block.(*eth.GenericBeaconBlock_BlindedFulu)
|
||||
require.Equal(t, true, ok)
|
||||
ssz, err := bl.BlindedFulu.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(ssz), writer.Body.String())
|
||||
require.Equal(t, "true", writer.Header().Get(api.ExecutionPayloadBlindedHeader))
|
||||
require.Equal(t, "2000", writer.Header().Get(api.ExecutionPayloadValueHeader))
|
||||
require.Equal(t, "fulu", writer.Header().Get(api.VersionHeader))
|
||||
require.Equal(t, "10000000000", writer.Header().Get(api.ConsensusBlockValueHeader))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1584,7 +1584,8 @@ func TestProduceSyncCommitteeContribution(t *testing.T) {
|
||||
SyncCommitteeIndices: []primitives.CommitteeIndex{0},
|
||||
},
|
||||
},
|
||||
SyncCommitteePool: syncCommitteePool,
|
||||
SyncCommitteePool: syncCommitteePool,
|
||||
OptimisticModeFetcher: &mockChain.ChainService{},
|
||||
}
|
||||
t.Run("ok", func(t *testing.T) {
|
||||
url := "http://example.com?slot=1&subcommittee_index=1&beacon_block_root=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
||||
@@ -1672,7 +1673,8 @@ func TestProduceSyncCommitteeContribution(t *testing.T) {
|
||||
SyncCommitteeIndices: []primitives.CommitteeIndex{0},
|
||||
},
|
||||
},
|
||||
SyncCommitteePool: syncCommitteePool,
|
||||
SyncCommitteePool: syncCommitteePool,
|
||||
OptimisticModeFetcher: &mockChain.ChainService{},
|
||||
}
|
||||
server.ProduceSyncCommitteeContribution(writer, request)
|
||||
assert.Equal(t, http.StatusNotFound, writer.Code)
|
||||
|
||||
@@ -110,6 +110,14 @@ func convertToBlockContainer(blk interfaces.ReadOnlySignedBeaconBlock, root [32]
|
||||
ctr.Block = ðpb.BeaconBlockContainer_BlindedDenebBlock{BlindedDenebBlock: pbStruct}
|
||||
case *ethpb.SignedBeaconBlockDeneb:
|
||||
ctr.Block = ðpb.BeaconBlockContainer_DenebBlock{DenebBlock: pbStruct}
|
||||
case *ethpb.SignedBlindedBeaconBlockElectra:
|
||||
ctr.Block = ðpb.BeaconBlockContainer_BlindedElectraBlock{BlindedElectraBlock: pbStruct}
|
||||
case *ethpb.SignedBeaconBlockElectra:
|
||||
ctr.Block = ðpb.BeaconBlockContainer_ElectraBlock{ElectraBlock: pbStruct}
|
||||
case *ethpb.SignedBlindedBeaconBlockFulu:
|
||||
ctr.Block = ðpb.BeaconBlockContainer_BlindedFuluBlock{BlindedFuluBlock: pbStruct}
|
||||
case *ethpb.SignedBeaconBlockFulu:
|
||||
ctr.Block = ðpb.BeaconBlockContainer_FuluBlock{FuluBlock: pbStruct}
|
||||
default:
|
||||
return nil, errors.Errorf("block type is not recognized: %d", blk.Version())
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func (vs *Server) constructFuluBlock(blockProto proto.Message, isBlinded bool, p
|
||||
if isBlinded {
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedFulu{BlindedFulu: blockProto.(*ethpb.BlindedBeaconBlockFulu)}, IsBlinded: true, PayloadValue: payloadValue}
|
||||
}
|
||||
fuluContents := ðpb.BeaconBlockContentsFulu{Block: blockProto.(*ethpb.BeaconBlockFulu)}
|
||||
fuluContents := ðpb.BeaconBlockContentsFulu{Block: blockProto.(*ethpb.BeaconBlockElectra)}
|
||||
if bundle != nil {
|
||||
fuluContents.KzgProofs = bundle.Proofs
|
||||
fuluContents.Blobs = bundle.Blobs
|
||||
|
||||
@@ -417,7 +417,7 @@ func (vs *Server) PrepareBeaconProposer(
|
||||
for _, r := range request.Recipients {
|
||||
recipient := hexutil.Encode(r.FeeRecipient)
|
||||
if !common.IsHexAddress(recipient) {
|
||||
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("Invalid fee recipient address: %v", recipient))
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Invalid fee recipient address: %v", recipient)
|
||||
}
|
||||
// Use default address if the burn address is return
|
||||
feeRecipient := primitives.ExecutionAddress(r.FeeRecipient)
|
||||
@@ -470,7 +470,7 @@ func (vs *Server) GetFeeRecipientByPubKey(ctx context.Context, request *ethpb.Fe
|
||||
}, nil
|
||||
} else {
|
||||
log.WithError(err).Error("An error occurred while retrieving fee recipient from db")
|
||||
return nil, status.Errorf(codes.Internal, err.Error())
|
||||
return nil, status.Errorf(codes.Internal, "error=%s", err)
|
||||
}
|
||||
}
|
||||
return ðpb.FeeRecipientByPubKeyResponse{
|
||||
|
||||
@@ -2,8 +2,10 @@ package validator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -277,7 +279,14 @@ func (a proposerAtts) sortOnChainAggregates() (proposerAtts, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
return a.sortByProfitabilityUsingMaxCover()
|
||||
// Sort by slot first, then by bit count.
|
||||
slices.SortFunc(a, func(a, b ethpb.Att) int {
|
||||
return cmp.Or(
|
||||
-cmp.Compare(a.GetData().Slot, b.GetData().Slot),
|
||||
-cmp.Compare(a.GetAggregationBits().Count(), b.GetAggregationBits().Count()))
|
||||
})
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Separate attestations by slot, as slot number takes higher precedence when sorting.
|
||||
|
||||
@@ -794,17 +794,19 @@ func Test_packAttestations_ElectraOnChainAggregates(t *testing.T) {
|
||||
|
||||
require.NoError(t, st.SetSlot(params.BeaconConfig().SlotsPerEpoch+1))
|
||||
|
||||
atts, err := s.packAttestations(ctx, st, params.BeaconConfig().SlotsPerEpoch)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 6, len(atts))
|
||||
assert.Equal(t, true,
|
||||
atts[0].GetAggregationBits().Count() >= atts[1].GetAggregationBits().Count() &&
|
||||
atts[1].GetAggregationBits().Count() >= atts[2].GetAggregationBits().Count() &&
|
||||
atts[2].GetAggregationBits().Count() >= atts[3].GetAggregationBits().Count() &&
|
||||
atts[3].GetAggregationBits().Count() >= atts[4].GetAggregationBits().Count() &&
|
||||
atts[4].GetAggregationBits().Count() >= atts[5].GetAggregationBits().Count(),
|
||||
"on-chain aggregates are not sorted by aggregation bit count",
|
||||
)
|
||||
t.Run("ok", func(t *testing.T) {
|
||||
atts, err := s.packAttestations(ctx, st, params.BeaconConfig().SlotsPerEpoch)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 6, len(atts))
|
||||
assert.Equal(t, true,
|
||||
atts[0].GetAggregationBits().Count() >= atts[1].GetAggregationBits().Count() &&
|
||||
atts[1].GetAggregationBits().Count() >= atts[2].GetAggregationBits().Count() &&
|
||||
atts[2].GetAggregationBits().Count() >= atts[3].GetAggregationBits().Count() &&
|
||||
atts[3].GetAggregationBits().Count() >= atts[4].GetAggregationBits().Count() &&
|
||||
atts[4].GetAggregationBits().Count() >= atts[5].GetAggregationBits().Count(),
|
||||
"on-chain aggregates are not sorted by aggregation bit count",
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("slot takes precedence", func(t *testing.T) {
|
||||
moreRecentAtt := ðpb.AttestationElectra{
|
||||
@@ -814,7 +816,7 @@ func Test_packAttestations_ElectraOnChainAggregates(t *testing.T) {
|
||||
Signature: sig.Marshal(),
|
||||
}
|
||||
require.NoError(t, pool.SaveUnaggregatedAttestations([]ethpb.Att{moreRecentAtt}))
|
||||
atts, err = s.packAttestations(ctx, st, params.BeaconConfig().SlotsPerEpoch)
|
||||
atts, err := s.packAttestations(ctx, st, params.BeaconConfig().SlotsPerEpoch)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 7, len(atts))
|
||||
assert.Equal(t, true, atts[0].GetData().Slot == 1)
|
||||
|
||||
@@ -17,7 +17,7 @@ func getEmptyBlock(slot primitives.Slot) (interfaces.SignedBeaconBlock, error) {
|
||||
epoch := slots.ToEpoch(slot)
|
||||
switch {
|
||||
case epoch >= params.BeaconConfig().FuluForkEpoch:
|
||||
sBlk, err = blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockFulu{Block: ðpb.BeaconBlockFulu{Body: ðpb.BeaconBlockBodyFulu{}}})
|
||||
sBlk, err = blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockFulu{Block: ðpb.BeaconBlockElectra{Body: ðpb.BeaconBlockBodyElectra{}}})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not initialize block for proposal: %v", err)
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func Test_getEmptyBlock(t *testing.T) {
|
||||
name: "fulu",
|
||||
slot: primitives.Slot(params.BeaconConfig().FuluForkEpoch) * params.BeaconConfig().SlotsPerEpoch,
|
||||
want: func() interfaces.ReadOnlySignedBeaconBlock {
|
||||
b, err := blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockFulu{Block: ðpb.BeaconBlockFulu{Body: ðpb.BeaconBlockBodyFulu{}}})
|
||||
b, err := blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockFulu{Block: ðpb.BeaconBlockElectra{Body: ðpb.BeaconBlockBodyElectra{}}})
|
||||
require.NoError(t, err)
|
||||
return b
|
||||
},
|
||||
|
||||
@@ -737,11 +737,11 @@ func TestServer_GetBeaconBlock_Fulu(t *testing.T) {
|
||||
},
|
||||
}
|
||||
blk := ðpb.SignedBeaconBlockFulu{
|
||||
Block: ðpb.BeaconBlockFulu{
|
||||
Block: ðpb.BeaconBlockElectra{
|
||||
Slot: fuluSlot + 1,
|
||||
ParentRoot: parentRoot[:],
|
||||
StateRoot: genesis.Block.StateRoot,
|
||||
Body: ðpb.BeaconBlockBodyFulu{
|
||||
Body: ðpb.BeaconBlockBodyElectra{
|
||||
RandaoReveal: genesis.Block.Body.RandaoReveal,
|
||||
Graffiti: genesis.Block.Body.Graffiti,
|
||||
Eth1Data: genesis.Block.Body.Eth1Data,
|
||||
|
||||
@@ -269,7 +269,7 @@ func (vs *Server) optimisticStatus(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return status.Errorf(codes.Unavailable, errOptimisticMode.Error())
|
||||
return status.Errorf(codes.Unavailable, "error=%v", errOptimisticMode)
|
||||
}
|
||||
|
||||
// validatorStatus searches for the requested validator's state and deposit to retrieve its inclusion estimate. Also returns the validators index.
|
||||
|
||||
@@ -36,7 +36,7 @@ func (vs *Server) GetSyncMessageBlockRoot(
|
||||
// It also saves the sync committee message into the pending pool for block inclusion.
|
||||
func (vs *Server) SubmitSyncMessage(ctx context.Context, msg *ethpb.SyncCommitteeMessage) (*emptypb.Empty, error) {
|
||||
if err := vs.CoreService.SubmitSyncMessage(ctx, msg); err != nil {
|
||||
return &emptypb.Empty{}, status.Errorf(core.ErrorReasonToGRPC(err.Reason), err.Err.Error())
|
||||
return &emptypb.Empty{}, status.Errorf(core.ErrorReasonToGRPC(err.Reason), "error=%s", err.Err)
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
@@ -105,7 +105,7 @@ func (vs *Server) SubmitSignedContributionAndProof(
|
||||
) (*emptypb.Empty, error) {
|
||||
err := vs.CoreService.SubmitSignedContributionAndProof(ctx, s)
|
||||
if err != nil {
|
||||
return &emptypb.Empty{}, status.Errorf(core.ErrorReasonToGRPC(err.Reason), err.Err.Error())
|
||||
return &emptypb.Empty{}, status.Errorf(core.ErrorReasonToGRPC(err.Reason), "error=%s", err.Err)
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ func (b *BeaconState) ToProtoUnsafe() interface{} {
|
||||
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
|
||||
HistoricalSummaries: b.historicalSummaries,
|
||||
}
|
||||
case version.Electra:
|
||||
case version.Electra, version.Fulu:
|
||||
return ðpb.BeaconStateElectra{
|
||||
GenesisTime: b.genesisTime,
|
||||
GenesisValidatorsRoot: gvrCopy[:],
|
||||
@@ -222,46 +222,6 @@ func (b *BeaconState) ToProtoUnsafe() interface{} {
|
||||
PendingPartialWithdrawals: b.pendingPartialWithdrawals,
|
||||
PendingConsolidations: b.pendingConsolidations,
|
||||
}
|
||||
case version.Fulu:
|
||||
return ðpb.BeaconStateFulu{
|
||||
GenesisTime: b.genesisTime,
|
||||
GenesisValidatorsRoot: gvrCopy[:],
|
||||
Slot: b.slot,
|
||||
Fork: b.fork,
|
||||
LatestBlockHeader: b.latestBlockHeader,
|
||||
BlockRoots: br,
|
||||
StateRoots: sr,
|
||||
HistoricalRoots: b.historicalRoots.Slice(),
|
||||
Eth1Data: b.eth1Data,
|
||||
Eth1DataVotes: b.eth1DataVotes,
|
||||
Eth1DepositIndex: b.eth1DepositIndex,
|
||||
Validators: vals,
|
||||
Balances: bals,
|
||||
RandaoMixes: rm,
|
||||
Slashings: b.slashings,
|
||||
PreviousEpochParticipation: b.previousEpochParticipation,
|
||||
CurrentEpochParticipation: b.currentEpochParticipation,
|
||||
JustificationBits: b.justificationBits,
|
||||
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint,
|
||||
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint,
|
||||
FinalizedCheckpoint: b.finalizedCheckpoint,
|
||||
InactivityScores: inactivityScores,
|
||||
CurrentSyncCommittee: b.currentSyncCommittee,
|
||||
NextSyncCommittee: b.nextSyncCommittee,
|
||||
LatestExecutionPayloadHeader: b.latestExecutionPayloadHeaderDeneb,
|
||||
NextWithdrawalIndex: b.nextWithdrawalIndex,
|
||||
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
|
||||
HistoricalSummaries: b.historicalSummaries,
|
||||
DepositRequestsStartIndex: b.depositRequestsStartIndex,
|
||||
DepositBalanceToConsume: b.depositBalanceToConsume,
|
||||
ExitBalanceToConsume: b.exitBalanceToConsume,
|
||||
EarliestExitEpoch: b.earliestExitEpoch,
|
||||
ConsolidationBalanceToConsume: b.consolidationBalanceToConsume,
|
||||
EarliestConsolidationEpoch: b.earliestConsolidationEpoch,
|
||||
PendingDeposits: b.pendingDeposits,
|
||||
PendingPartialWithdrawals: b.pendingPartialWithdrawals,
|
||||
PendingConsolidations: b.pendingConsolidations,
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@@ -428,7 +388,7 @@ func (b *BeaconState) ToProto() interface{} {
|
||||
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
|
||||
HistoricalSummaries: b.historicalSummariesVal(),
|
||||
}
|
||||
case version.Electra:
|
||||
case version.Electra, version.Fulu:
|
||||
return ðpb.BeaconStateElectra{
|
||||
GenesisTime: b.genesisTime,
|
||||
GenesisValidatorsRoot: gvrCopy[:],
|
||||
@@ -468,46 +428,6 @@ func (b *BeaconState) ToProto() interface{} {
|
||||
PendingPartialWithdrawals: b.pendingPartialWithdrawalsVal(),
|
||||
PendingConsolidations: b.pendingConsolidationsVal(),
|
||||
}
|
||||
case version.Fulu:
|
||||
return ðpb.BeaconStateFulu{
|
||||
GenesisTime: b.genesisTime,
|
||||
GenesisValidatorsRoot: gvrCopy[:],
|
||||
Slot: b.slot,
|
||||
Fork: b.forkVal(),
|
||||
LatestBlockHeader: b.latestBlockHeaderVal(),
|
||||
BlockRoots: br,
|
||||
StateRoots: sr,
|
||||
HistoricalRoots: b.historicalRoots.Slice(),
|
||||
Eth1Data: b.eth1DataVal(),
|
||||
Eth1DataVotes: b.eth1DataVotesVal(),
|
||||
Eth1DepositIndex: b.eth1DepositIndex,
|
||||
Validators: b.validatorsVal(),
|
||||
Balances: b.balancesVal(),
|
||||
RandaoMixes: rm,
|
||||
Slashings: b.slashingsVal(),
|
||||
PreviousEpochParticipation: b.previousEpochParticipationVal(),
|
||||
CurrentEpochParticipation: b.currentEpochParticipationVal(),
|
||||
JustificationBits: b.justificationBitsVal(),
|
||||
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpointVal(),
|
||||
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpointVal(),
|
||||
FinalizedCheckpoint: b.finalizedCheckpointVal(),
|
||||
InactivityScores: b.inactivityScoresVal(),
|
||||
CurrentSyncCommittee: b.currentSyncCommitteeVal(),
|
||||
NextSyncCommittee: b.nextSyncCommitteeVal(),
|
||||
LatestExecutionPayloadHeader: b.latestExecutionPayloadHeaderDeneb.Copy(),
|
||||
NextWithdrawalIndex: b.nextWithdrawalIndex,
|
||||
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
|
||||
HistoricalSummaries: b.historicalSummariesVal(),
|
||||
DepositRequestsStartIndex: b.depositRequestsStartIndex,
|
||||
DepositBalanceToConsume: b.depositBalanceToConsume,
|
||||
ExitBalanceToConsume: b.exitBalanceToConsume,
|
||||
EarliestExitEpoch: b.earliestExitEpoch,
|
||||
ConsolidationBalanceToConsume: b.consolidationBalanceToConsume,
|
||||
EarliestConsolidationEpoch: b.earliestConsolidationEpoch,
|
||||
PendingDeposits: b.pendingDepositsVal(),
|
||||
PendingPartialWithdrawals: b.pendingPartialWithdrawalsVal(),
|
||||
PendingConsolidations: b.pendingConsolidationsVal(),
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@@ -634,12 +554,4 @@ func ProtobufBeaconStateElectra(s interface{}) (*ethpb.BeaconStateElectra, error
|
||||
return pbState, nil
|
||||
}
|
||||
|
||||
// ProtobufBeaconStateFulu transforms an input into beacon state Fulu in the form of protobuf.
|
||||
// Error is returned if the input is not type protobuf beacon state.
|
||||
func ProtobufBeaconStateFulu(s interface{}) (*ethpb.BeaconStateFulu, error) {
|
||||
pbState, ok := s.(*ethpb.BeaconStateFulu)
|
||||
if !ok {
|
||||
return nil, errors.New("input is not type pb.BeaconStateFulu")
|
||||
}
|
||||
return pbState, nil
|
||||
}
|
||||
var ProtobufBeaconStateFulu = ProtobufBeaconStateElectra
|
||||
|
||||
@@ -159,8 +159,8 @@ func InitializeFromProtoElectra(st *ethpb.BeaconStateElectra) (state.BeaconState
|
||||
}
|
||||
|
||||
// InitializeFromProtoFulu the beacon state from a protobuf representation.
|
||||
func InitializeFromProtoFulu(st *ethpb.BeaconStateFulu) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafeFulu(proto.Clone(st).(*ethpb.BeaconStateFulu))
|
||||
func InitializeFromProtoFulu(st *ethpb.BeaconStateElectra) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafeFulu(proto.Clone(st).(*ethpb.BeaconStateElectra))
|
||||
}
|
||||
|
||||
// InitializeFromProtoUnsafePhase0 directly uses the beacon state protobuf fields
|
||||
@@ -842,7 +842,7 @@ func InitializeFromProtoUnsafeElectra(st *ethpb.BeaconStateElectra) (state.Beaco
|
||||
|
||||
// InitializeFromProtoUnsafeFulu directly uses the beacon state protobuf fields
|
||||
// and sets them as fields of the BeaconState type.
|
||||
func InitializeFromProtoUnsafeFulu(st *ethpb.BeaconStateFulu) (state.BeaconState, error) {
|
||||
func InitializeFromProtoUnsafeFulu(st *ethpb.BeaconStateElectra) (state.BeaconState, error) {
|
||||
if st == nil {
|
||||
return nil, errors.New("received nil state")
|
||||
}
|
||||
|
||||
@@ -121,9 +121,10 @@ func (s *State) Resume(ctx context.Context, fState state.BeaconState) (state.Bea
|
||||
return nil, err
|
||||
}
|
||||
fRoot := bytesutil.ToBytes32(c.Root)
|
||||
st := fState
|
||||
// Resume as genesis state if last finalized root is zero hashes.
|
||||
if fRoot == params.BeaconConfig().ZeroHash {
|
||||
st, err := s.beaconDB.GenesisState(ctx)
|
||||
st, err = s.beaconDB.GenesisState(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get genesis state")
|
||||
}
|
||||
@@ -132,10 +133,13 @@ func (s *State) Resume(ctx context.Context, fState state.BeaconState) (state.Bea
|
||||
if err != nil {
|
||||
return nil, stderrors.Join(ErrNoGenesisBlock, err)
|
||||
}
|
||||
return st, s.SaveState(ctx, gbr, st)
|
||||
fRoot = gbr
|
||||
if err := s.SaveState(ctx, gbr, st); err != nil {
|
||||
return nil, errors.Wrap(err, "could not save genesis state")
|
||||
}
|
||||
}
|
||||
|
||||
if fState == nil || fState.IsNil() {
|
||||
if st == nil || st.IsNil() {
|
||||
return nil, errors.New("finalized state is nil")
|
||||
}
|
||||
|
||||
@@ -145,20 +149,22 @@ func (s *State) Resume(ctx context.Context, fState state.BeaconState) (state.Bea
|
||||
}
|
||||
}()
|
||||
|
||||
s.finalizedInfo = &finalizedInfo{slot: fState.Slot(), root: fRoot, state: fState.Copy()}
|
||||
fEpoch := slots.ToEpoch(fState.Slot())
|
||||
s.finalizedInfo = &finalizedInfo{slot: st.Slot(), root: fRoot, state: st.Copy()}
|
||||
populatePubkeyCache(ctx, st)
|
||||
return st, nil
|
||||
}
|
||||
|
||||
// Pre-populate the pubkey cache with the validator public keys from the finalized state.
|
||||
// This process takes about 30 seconds on mainnet with 450,000 validators.
|
||||
func populatePubkeyCache(ctx context.Context, st state.BeaconState) {
|
||||
epoch := slots.ToEpoch(st.Slot())
|
||||
go populatePubkeyCacheOnce.Do(func() {
|
||||
log.Debug("Populating pubkey cache")
|
||||
start := time.Now()
|
||||
if err := fState.ReadFromEveryValidator(func(_ int, val state.ReadOnlyValidator) error {
|
||||
if err := st.ReadFromEveryValidator(func(_ int, val state.ReadOnlyValidator) error {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
// Do not cache for non-active validators.
|
||||
if !helpers.IsActiveValidatorUsingTrie(val, fEpoch) {
|
||||
if !helpers.IsActiveValidatorUsingTrie(val, epoch) {
|
||||
return nil
|
||||
}
|
||||
pub := val.PublicKey()
|
||||
@@ -169,8 +175,6 @@ func (s *State) Resume(ctx context.Context, fState state.BeaconState) (state.Bea
|
||||
}
|
||||
log.WithField("duration", time.Since(start)).Debug("Done populating pubkey cache")
|
||||
})
|
||||
|
||||
return fState, nil
|
||||
}
|
||||
|
||||
// SaveFinalizedState saves the finalized slot, root and state into memory to be used by state gen service.
|
||||
|
||||
@@ -87,6 +87,8 @@ func extractValidDataTypeFromTopic(topic string, digest []byte, clock *startup.C
|
||||
return extractDataTypeFromTypeMap(types.AttestationMap, digest, clock)
|
||||
case p2p.AggregateAndProofSubnetTopicFormat:
|
||||
return extractDataTypeFromTypeMap(types.AggregateAttestationMap, digest, clock)
|
||||
case p2p.AttesterSlashingSubnetTopicFormat:
|
||||
return extractDataTypeFromTypeMap(types.AttesterSlashingMap, digest, clock)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -137,13 +137,14 @@ func TestExtractDataType(t *testing.T) {
|
||||
chain blockchain.ChainInfoFetcher
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantBlock interfaces.ReadOnlySignedBeaconBlock
|
||||
wantMd metadata.Metadata
|
||||
wantAtt ethpb.Att
|
||||
wantAggregate ethpb.SignedAggregateAttAndProof
|
||||
wantErr bool
|
||||
name string
|
||||
args args
|
||||
wantBlock interfaces.ReadOnlySignedBeaconBlock
|
||||
wantMd metadata.Metadata
|
||||
wantAtt ethpb.Att
|
||||
wantAggregate ethpb.SignedAggregateAttAndProof
|
||||
wantAttSlashing ethpb.AttSlashing
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "no digest",
|
||||
@@ -156,10 +157,11 @@ func TestExtractDataType(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return wsb
|
||||
}(),
|
||||
wantMd: wrapper.WrappedMetadataV0(ðpb.MetaDataV0{}),
|
||||
wantAtt: ðpb.Attestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProof{},
|
||||
wantErr: false,
|
||||
wantMd: wrapper.WrappedMetadataV0(ðpb.MetaDataV0{}),
|
||||
wantAtt: ðpb.Attestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProof{},
|
||||
wantAttSlashing: ðpb.AttesterSlashing{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid digest",
|
||||
@@ -167,11 +169,12 @@ func TestExtractDataType(t *testing.T) {
|
||||
digest: []byte{0x00, 0x01},
|
||||
chain: &mock.ChainService{ValidatorsRoot: [32]byte{}},
|
||||
},
|
||||
wantBlock: nil,
|
||||
wantMd: nil,
|
||||
wantAtt: nil,
|
||||
wantAggregate: nil,
|
||||
wantErr: true,
|
||||
wantBlock: nil,
|
||||
wantMd: nil,
|
||||
wantAtt: nil,
|
||||
wantAggregate: nil,
|
||||
wantAttSlashing: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "non existent digest",
|
||||
@@ -179,11 +182,12 @@ func TestExtractDataType(t *testing.T) {
|
||||
digest: []byte{0x00, 0x01, 0x02, 0x03},
|
||||
chain: &mock.ChainService{ValidatorsRoot: [32]byte{}},
|
||||
},
|
||||
wantBlock: nil,
|
||||
wantMd: nil,
|
||||
wantAtt: nil,
|
||||
wantAggregate: nil,
|
||||
wantErr: true,
|
||||
wantBlock: nil,
|
||||
wantMd: nil,
|
||||
wantAtt: nil,
|
||||
wantAggregate: nil,
|
||||
wantAttSlashing: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "genesis fork version",
|
||||
@@ -196,9 +200,10 @@ func TestExtractDataType(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return wsb
|
||||
}(),
|
||||
wantAtt: ðpb.Attestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProof{},
|
||||
wantErr: false,
|
||||
wantAtt: ðpb.Attestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProof{},
|
||||
wantAttSlashing: ðpb.AttesterSlashing{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "altair fork version",
|
||||
@@ -211,10 +216,11 @@ func TestExtractDataType(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return wsb
|
||||
}(),
|
||||
wantMd: wrapper.WrappedMetadataV1(ðpb.MetaDataV1{}),
|
||||
wantAtt: ðpb.Attestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProof{},
|
||||
wantErr: false,
|
||||
wantMd: wrapper.WrappedMetadataV1(ðpb.MetaDataV1{}),
|
||||
wantAtt: ðpb.Attestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProof{},
|
||||
wantAttSlashing: ðpb.AttesterSlashing{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "bellatrix fork version",
|
||||
@@ -227,10 +233,11 @@ func TestExtractDataType(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return wsb
|
||||
}(),
|
||||
wantMd: wrapper.WrappedMetadataV1(ðpb.MetaDataV1{}),
|
||||
wantAtt: ðpb.Attestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProof{},
|
||||
wantErr: false,
|
||||
wantMd: wrapper.WrappedMetadataV1(ðpb.MetaDataV1{}),
|
||||
wantAtt: ðpb.Attestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProof{},
|
||||
wantAttSlashing: ðpb.AttesterSlashing{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "capella fork version",
|
||||
@@ -243,10 +250,11 @@ func TestExtractDataType(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return wsb
|
||||
}(),
|
||||
wantMd: wrapper.WrappedMetadataV1(ðpb.MetaDataV1{}),
|
||||
wantAtt: ðpb.Attestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProof{},
|
||||
wantErr: false,
|
||||
wantMd: wrapper.WrappedMetadataV1(ðpb.MetaDataV1{}),
|
||||
wantAtt: ðpb.Attestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProof{},
|
||||
wantAttSlashing: ðpb.AttesterSlashing{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "deneb fork version",
|
||||
@@ -259,10 +267,11 @@ func TestExtractDataType(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return wsb
|
||||
}(),
|
||||
wantMd: wrapper.WrappedMetadataV1(ðpb.MetaDataV1{}),
|
||||
wantAtt: ðpb.Attestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProof{},
|
||||
wantErr: false,
|
||||
wantMd: wrapper.WrappedMetadataV1(ðpb.MetaDataV1{}),
|
||||
wantAtt: ðpb.Attestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProof{},
|
||||
wantAttSlashing: ðpb.AttesterSlashing{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "electra fork version",
|
||||
@@ -275,10 +284,11 @@ func TestExtractDataType(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return wsb
|
||||
}(),
|
||||
wantMd: wrapper.WrappedMetadataV1(ðpb.MetaDataV1{}),
|
||||
wantAtt: ðpb.SingleAttestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProofElectra{},
|
||||
wantErr: false,
|
||||
wantMd: wrapper.WrappedMetadataV1(ðpb.MetaDataV1{}),
|
||||
wantAtt: ðpb.SingleAttestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProofElectra{},
|
||||
wantAttSlashing: ðpb.AttesterSlashingElectra{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "fulu fork version",
|
||||
@@ -287,14 +297,15 @@ func TestExtractDataType(t *testing.T) {
|
||||
chain: &mock.ChainService{ValidatorsRoot: [32]byte{}},
|
||||
},
|
||||
wantBlock: func() interfaces.ReadOnlySignedBeaconBlock {
|
||||
wsb, err := blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockFulu{Block: ðpb.BeaconBlockFulu{Body: ðpb.BeaconBlockBodyFulu{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}}}})
|
||||
wsb, err := blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockFulu{Block: ðpb.BeaconBlockElectra{Body: ðpb.BeaconBlockBodyElectra{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}}}})
|
||||
require.NoError(t, err)
|
||||
return wsb
|
||||
}(),
|
||||
wantMd: wrapper.WrappedMetadataV1(ðpb.MetaDataV1{}),
|
||||
wantAtt: ðpb.SingleAttestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProofElectra{},
|
||||
wantErr: false,
|
||||
wantMd: wrapper.WrappedMetadataV1(ðpb.MetaDataV1{}),
|
||||
wantAtt: ðpb.SingleAttestation{},
|
||||
wantAggregate: ðpb.SignedAggregateAttestationAndProofElectra{},
|
||||
wantAttSlashing: ðpb.AttesterSlashingElectra{},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -323,6 +334,14 @@ func TestExtractDataType(t *testing.T) {
|
||||
if !reflect.DeepEqual(gotAggregate, tt.wantAggregate) {
|
||||
t.Errorf("aggregate: got = %v, want %v", gotAggregate, tt.wantAggregate)
|
||||
}
|
||||
gotAttSlashing, err := extractDataTypeFromTypeMap(types.AttesterSlashingMap, tt.args.digest, tt.args.chain)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("attester slashing: error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(gotAttSlashing, tt.wantAttSlashing) {
|
||||
t.Errorf("attester slashin: got = %v, want %v", gotAttSlashing, tt.wantAttSlashing)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +148,7 @@ func (s *Service) deregisterFromPastFork(currentEpoch primitives.Epoch) error {
|
||||
for topic := range topicsToRemove {
|
||||
fullTopic := topic + s.cfg.p2p.Encoding().ProtocolSuffix()
|
||||
s.cfg.p2p.Host().RemoveStreamHandler(protocol.ID(fullTopic))
|
||||
log.WithField("topic", fullTopic).Debug("Removed RPC handler")
|
||||
}
|
||||
|
||||
// Run through all our current active topics and see
|
||||
|
||||
@@ -188,3 +188,11 @@ func WithAvailableBlocker(avb coverage.AvailableBlocker) Option {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSlasherEnabled configures the sync package to support slashing detection.
|
||||
func WithSlasherEnabled(enabled bool) Option {
|
||||
return func(s *Service) error {
|
||||
s.slasherEnabled = enabled
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,8 +140,7 @@ func (s *Service) processUnaggregated(ctx context.Context, att ethpb.Att) {
|
||||
data := att.GetData()
|
||||
|
||||
// This is an important validation before retrieving attestation pre state to defend against
|
||||
// attestation's target intentionally reference checkpoint that's long ago.
|
||||
// Verify current finalized checkpoint is an ancestor of the block defined by the attestation's beacon block root.
|
||||
// attestation's target intentionally referencing a checkpoint that's long ago.
|
||||
if !s.cfg.chain.InForkchoice(bytesutil.ToBytes32(data.BeaconBlockRoot)) {
|
||||
log.WithError(blockchain.ErrNotDescendantOfFinalized).Debug("Could not verify finalized consistency")
|
||||
return
|
||||
@@ -169,35 +168,57 @@ func (s *Service) processUnaggregated(ctx context.Context, att ethpb.Att) {
|
||||
return
|
||||
}
|
||||
|
||||
var singleAtt *ethpb.SingleAttestation
|
||||
// Decide if the attestation is an Electra SingleAttestation or a Phase0 unaggregated attestation
|
||||
var (
|
||||
attForValidation ethpb.Att
|
||||
broadcastAtt ethpb.Att
|
||||
eventType feed.EventType
|
||||
eventData interface{}
|
||||
)
|
||||
|
||||
if att.Version() >= version.Electra {
|
||||
var ok bool
|
||||
singleAtt, ok = att.(*ethpb.SingleAttestation)
|
||||
singleAtt, ok := att.(*ethpb.SingleAttestation)
|
||||
if !ok {
|
||||
log.Debugf("Attestation has wrong type (expected %T, got %T)", ðpb.SingleAttestation{}, att)
|
||||
return
|
||||
}
|
||||
att = singleAtt.ToAttestationElectra(committee)
|
||||
// Convert Electra SingleAttestation to unaggregated ElectraAttestation. This is needed because many parts of the codebase assume that attestations have a certain structure and SingleAttestation validates these assumptions.
|
||||
attForValidation = singleAtt.ToAttestationElectra(committee)
|
||||
broadcastAtt = singleAtt
|
||||
eventType = operation.SingleAttReceived
|
||||
eventData = &operation.SingleAttReceivedData{
|
||||
Attestation: singleAtt,
|
||||
}
|
||||
} else {
|
||||
// Phase0 attestation
|
||||
attForValidation = att
|
||||
broadcastAtt = att
|
||||
eventType = operation.UnaggregatedAttReceived
|
||||
eventData = &operation.UnAggregatedAttReceivedData{
|
||||
Attestation: att,
|
||||
}
|
||||
}
|
||||
|
||||
valid, err = s.validateUnaggregatedAttWithState(ctx, att, preState)
|
||||
valid, err = s.validateUnaggregatedAttWithState(ctx, attForValidation, preState)
|
||||
if err != nil {
|
||||
log.WithError(err).Debug("Pending unaggregated attestation failed validation")
|
||||
return
|
||||
}
|
||||
|
||||
if valid == pubsub.ValidationAccept {
|
||||
if features.Get().EnableExperimentalAttestationPool {
|
||||
if err = s.cfg.attestationCache.Add(att); err != nil {
|
||||
if err = s.cfg.attestationCache.Add(attForValidation); err != nil {
|
||||
log.WithError(err).Debug("Could not save unaggregated attestation")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := s.cfg.attPool.SaveUnaggregatedAttestation(att); err != nil {
|
||||
if err := s.cfg.attPool.SaveUnaggregatedAttestation(attForValidation); err != nil {
|
||||
log.WithError(err).Debug("Could not save unaggregated attestation")
|
||||
return
|
||||
}
|
||||
}
|
||||
s.setSeenCommitteeIndicesSlot(data.Slot, data.CommitteeIndex, att.GetAggregationBits())
|
||||
|
||||
s.setSeenCommitteeIndicesSlot(data.Slot, attForValidation.GetCommitteeIndex(), attForValidation.GetAggregationBits())
|
||||
|
||||
valCount, err := helpers.ActiveValidatorCount(ctx, preState, slots.ToEpoch(data.Slot))
|
||||
if err != nil {
|
||||
@@ -205,34 +226,16 @@ func (s *Service) processUnaggregated(ctx context.Context, att ethpb.Att) {
|
||||
return
|
||||
}
|
||||
|
||||
// Broadcasting the signed attestation again once a node is able to process it.
|
||||
var attToBroadcast ethpb.Att
|
||||
if singleAtt != nil {
|
||||
attToBroadcast = singleAtt
|
||||
} else {
|
||||
attToBroadcast = att
|
||||
}
|
||||
if err := s.cfg.p2p.BroadcastAttestation(ctx, helpers.ComputeSubnetForAttestation(valCount, attToBroadcast), attToBroadcast); err != nil {
|
||||
// Broadcast the final 'broadcastAtt' object
|
||||
if err := s.cfg.p2p.BroadcastAttestation(ctx, helpers.ComputeSubnetForAttestation(valCount, broadcastAtt), broadcastAtt); err != nil {
|
||||
log.WithError(err).Debug("Could not broadcast")
|
||||
}
|
||||
|
||||
// Broadcast the unaggregated attestation on a feed to notify other services in the beacon node
|
||||
// of a received unaggregated attestation.
|
||||
if singleAtt != nil {
|
||||
s.cfg.attestationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: operation.SingleAttReceived,
|
||||
Data: &operation.SingleAttReceivedData{
|
||||
Attestation: singleAtt,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
s.cfg.attestationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: operation.UnaggregatedAttReceived,
|
||||
Data: &operation.UnAggregatedAttReceivedData{
|
||||
Attestation: att,
|
||||
},
|
||||
})
|
||||
}
|
||||
// Feed event notification for other services
|
||||
s.cfg.attestationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: eventType,
|
||||
Data: eventData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -706,3 +706,41 @@ func Test_attsAreEqual_Committee(t *testing.T) {
|
||||
assert.Equal(t, false, attsAreEqual(att1, att2))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SeenCommitteeIndicesSlot(t *testing.T) {
|
||||
t.Run("phase 0 success", func(t *testing.T) {
|
||||
s := &Service{
|
||||
seenUnAggregatedAttestationCache: lruwrpr.New(1),
|
||||
}
|
||||
data := ðpb.AttestationData{Slot: 1, CommitteeIndex: 44}
|
||||
att := ðpb.Attestation{
|
||||
AggregationBits: bitfield.Bitlist{0x01},
|
||||
Data: data,
|
||||
}
|
||||
s.setSeenCommitteeIndicesSlot(data.Slot, att.GetCommitteeIndex(), att.GetAggregationBits())
|
||||
b := append(bytesutil.Bytes32(uint64(1)), bytesutil.Bytes32(uint64(44))...)
|
||||
b = append(b, bytesutil.SafeCopyBytes(att.GetAggregationBits())...)
|
||||
_, ok := s.seenUnAggregatedAttestationCache.Get(string(b))
|
||||
require.Equal(t, true, ok)
|
||||
})
|
||||
t.Run("electra success", func(t *testing.T) {
|
||||
s := &Service{
|
||||
seenUnAggregatedAttestationCache: lruwrpr.New(1),
|
||||
}
|
||||
// committee index is 0 post electra for attestation electra
|
||||
data := ðpb.AttestationData{Slot: 1, CommitteeIndex: 0}
|
||||
cb := primitives.NewAttestationCommitteeBits()
|
||||
cb.SetBitAt(uint64(63), true)
|
||||
att := ðpb.AttestationElectra{
|
||||
AggregationBits: bitfield.Bitlist{0x01},
|
||||
Data: data,
|
||||
CommitteeBits: cb,
|
||||
}
|
||||
ci := att.GetCommitteeIndex()
|
||||
s.setSeenCommitteeIndicesSlot(data.Slot, ci, att.GetAggregationBits())
|
||||
b := append(bytesutil.Bytes32(uint64(1)), bytesutil.Bytes32(uint64(63))...)
|
||||
b = append(b, bytesutil.SafeCopyBytes(att.GetAggregationBits())...)
|
||||
_, ok := s.seenUnAggregatedAttestationCache.Get(string(b))
|
||||
require.Equal(t, true, ok)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -303,6 +303,7 @@ func (s *Service) registerRPC(baseTopic string, handle rpcHandler) {
|
||||
}
|
||||
}
|
||||
})
|
||||
log.Debug("Registered new RPC handler")
|
||||
}
|
||||
|
||||
func logStreamErrors(err error, topic string) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user