mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Compare commits
90 Commits
deneb-devn
...
blockPropo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
752ccefc61 | ||
|
|
350a421550 | ||
|
|
e259549c91 | ||
|
|
d645918057 | ||
|
|
23ce313930 | ||
|
|
1fca343689 | ||
|
|
6482b12c52 | ||
|
|
d5d40f6e9a | ||
|
|
a626ab5d05 | ||
|
|
948c94ce23 | ||
|
|
faa0a2c4cf | ||
|
|
c45cb7e188 | ||
|
|
0b10263dd5 | ||
|
|
b53309d0b7 | ||
|
|
95b289497b | ||
|
|
bfd9cf3bfb | ||
|
|
7c75f1d6b5 | ||
|
|
189b326cbd | ||
|
|
3bc808352f | ||
|
|
6d6ce4734c | ||
|
|
d9afc0060e | ||
|
|
d0c740f477 | ||
|
|
8ca6f9b0e6 | ||
|
|
9e9a0f7532 | ||
|
|
20ad9a35be | ||
|
|
d42928edad | ||
|
|
3369458e60 | ||
|
|
cbe67f1970 | ||
|
|
5bb482e5d6 | ||
|
|
d541e2aca9 | ||
|
|
96c75246a4 | ||
|
|
83494c5b23 | ||
|
|
a10ffa9c0e | ||
|
|
e545b57f26 | ||
|
|
c026b9e897 | ||
|
|
a19044051f | ||
|
|
1ebef16196 | ||
|
|
8af634a6a0 | ||
|
|
884ba4959a | ||
|
|
75e94120b4 | ||
|
|
82ca19cd46 | ||
|
|
20f4d21b83 | ||
|
|
c018981951 | ||
|
|
b92226bedb | ||
|
|
d35461affd | ||
|
|
ee159f3380 | ||
|
|
6b3d18cb77 | ||
|
|
07955c891b | ||
|
|
57f97feb84 | ||
|
|
2bf0560dc7 | ||
|
|
a40f903f76 | ||
|
|
ba55ae8cea | ||
|
|
27aac105d7 | ||
|
|
115d565f49 | ||
|
|
019e0b56e2 | ||
|
|
0efb038984 | ||
|
|
63d81144e9 | ||
|
|
6edbfa3128 | ||
|
|
194b3b1c5e | ||
|
|
996ec67229 | ||
|
|
c7b2c011d8 | ||
|
|
d15122fae2 | ||
|
|
3e17dbb532 | ||
|
|
a75e78ddb4 | ||
|
|
1862422db9 | ||
|
|
152d21059e | ||
|
|
2b410893a0 | ||
|
|
826267310e | ||
|
|
d5057cfb42 | ||
|
|
8d01cf2ec1 | ||
|
|
e4e315da94 | ||
|
|
0a4e42545e | ||
|
|
6fa2d768b5 | ||
|
|
0f228896b0 | ||
|
|
6896b41963 | ||
|
|
3bf6abe27c | ||
|
|
c1391f0de3 | ||
|
|
bd0d7478b3 | ||
|
|
b6a1da21f4 | ||
|
|
180058ed48 | ||
|
|
f7a567d1d3 | ||
|
|
6d02c9ae12 | ||
|
|
6c2e6ca855 | ||
|
|
fbdccf8055 | ||
|
|
83cfe11ca0 | ||
|
|
135e9f51ec | ||
|
|
d33c1974da | ||
|
|
88a2e3d953 | ||
|
|
cea42a4b7d | ||
|
|
9971d71bc5 |
32
.bazelrc
32
.bazelrc
@@ -3,6 +3,7 @@ import %workspace%/build/bazelrc/convenience.bazelrc
|
||||
import %workspace%/build/bazelrc/correctness.bazelrc
|
||||
import %workspace%/build/bazelrc/cross.bazelrc
|
||||
import %workspace%/build/bazelrc/debug.bazelrc
|
||||
import %workspace%/build/bazelrc/hermetic-cc.bazelrc
|
||||
import %workspace%/build/bazelrc/performance.bazelrc
|
||||
|
||||
# E2E run with debug gotag
|
||||
@@ -14,7 +15,7 @@ coverage --define=coverage_enabled=1
|
||||
# Stamp binaries with git information
|
||||
build --workspace_status_command=./hack/workspace_status.sh
|
||||
|
||||
build --define blst_disabled=false
|
||||
build --define blst_disabled=false --define blst_modern=true
|
||||
run --define blst_disabled=false
|
||||
|
||||
build:blst_disabled --define blst_disabled=true
|
||||
@@ -27,30 +28,7 @@ build:minimal --@io_bazel_rules_go//go/config:tags=minimal
|
||||
build:release --compilation_mode=opt
|
||||
build:release --stamp
|
||||
|
||||
# LLVM compiler for building C/C++ dependencies.
|
||||
build:llvm --define compiler=llvm
|
||||
build:llvm --copt -fno-sanitize=vptr,function
|
||||
build:llvm --linkopt -fno-sanitize=vptr,function
|
||||
# --incompatible_enable_cc_toolchain_resolution not needed after this issue is closed https://github.com/bazelbuild/bazel/issues/7260
|
||||
build:llvm --incompatible_enable_cc_toolchain_resolution
|
||||
|
||||
build:asan --copt -fsanitize=address,undefined
|
||||
build:asan --copt -fno-omit-frame-pointer
|
||||
build:asan --linkopt -fsanitize=address,undefined
|
||||
build:asan --copt -fno-sanitize=vptr,function
|
||||
build:asan --linkopt -fno-sanitize=vptr,function
|
||||
build:asan --copt -DADDRESS_SANITIZER=1
|
||||
build:asan --copt -D__SANITIZE_ADDRESS__
|
||||
build:asan --linkopt -ldl
|
||||
|
||||
build:llvm-asan --config=llvm
|
||||
build:llvm-asan --config=asan
|
||||
build:llvm-asan --linkopt -fuse-ld=ld.lld
|
||||
|
||||
build:fuzz --@io_bazel_rules_go//go/config:tags=fuzz
|
||||
|
||||
# Build binary with cgo symbolizer for debugging / profiling.
|
||||
build:cgo_symbolizer --config=llvm
|
||||
build:cgo_symbolizer --copt=-g
|
||||
build:cgo_symbolizer --define=USE_CGO_SYMBOLIZER=true
|
||||
build:cgo_symbolizer -c dbg
|
||||
@@ -59,9 +37,13 @@ build:cgo_symbolizer --define=gotags=cgosymbolizer_enabled
|
||||
# toolchain build debug configs
|
||||
#------------------------------
|
||||
build:debug --sandbox_debug
|
||||
build:debug --toolchain_resolution_debug
|
||||
build:debug --toolchain_resolution_debug=".*"
|
||||
build:debug --verbose_failures
|
||||
build:debug -s
|
||||
|
||||
# Set bazel gotag
|
||||
build --define gotags=bazel
|
||||
|
||||
# Abseil requires c++14 or greater.
|
||||
build --cxxopt=-std=c++20
|
||||
build --host_cxxopt=-std=c++20
|
||||
|
||||
@@ -1 +1 @@
|
||||
6.1.0
|
||||
6.2.1
|
||||
|
||||
@@ -3,7 +3,6 @@ load("@com_github_atlassian_bazel_tools//gometalinter:def.bzl", "gometalinter")
|
||||
load("@com_github_atlassian_bazel_tools//goimports:def.bzl", "goimports")
|
||||
load("@io_kubernetes_build//defs:run_in_workspace.bzl", "workspace_binary")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "nogo")
|
||||
load("@vaticle_bazel_distribution//common:rules.bzl", "assemble_targz", "assemble_versioned")
|
||||
load("@bazel_skylib//rules:common_settings.bzl", "string_setting")
|
||||
|
||||
prefix = "github.com/prysmaticlabs/prysm"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Contribution Guidelines
|
||||
|
||||
Note: The latest and most up to date documenation can be found on our [docs portal](https://docs.prylabs.network/docs/contribute/contribution-guidelines).
|
||||
Note: The latest and most up-to-date documentation can be found on our [docs portal](https://docs.prylabs.network/docs/contribute/contribution-guidelines).
|
||||
|
||||
Excited by our work and want to get involved in building out our sharding releases? Or maybe you haven't learned as much about the Ethereum protocol but are a savvy developer?
|
||||
|
||||
@@ -10,9 +10,9 @@ You can explore our [Open Issues](https://github.com/prysmaticlabs/prysm/issues)
|
||||
|
||||
**1. Set up Prysm following the instructions in README.md.**
|
||||
|
||||
**2. Fork the prysm repo.**
|
||||
**2. Fork the Prysm repo.**
|
||||
|
||||
Sign in to your Github account or create a new account if you do not have one already. Then navigate your browser to https://github.com/prysmaticlabs/prysm/. In the upper right hand corner of the page, click “fork”. This will create a copy of the Prysm repo in your account.
|
||||
Sign in to your GitHub account or create a new account if you do not have one already. Then navigate your browser to https://github.com/prysmaticlabs/prysm/. In the upper right hand corner of the page, click “fork”. This will create a copy of the Prysm repo in your account.
|
||||
|
||||
**3. Create a local clone of Prysm.**
|
||||
|
||||
@@ -23,7 +23,7 @@ $ git clone https://github.com/prysmaticlabs/prysm.git
|
||||
$ cd $GOPATH/src/github.com/prysmaticlabs/prysm
|
||||
```
|
||||
|
||||
**4. Link your local clone to the fork on your Github repo.**
|
||||
**4. Link your local clone to the fork on your GitHub repo.**
|
||||
|
||||
```
|
||||
$ git remote add myprysmrepo https://github.com/<your_github_user_name>/prysm.git
|
||||
@@ -68,7 +68,7 @@ $ go test <file_you_are_working_on>
|
||||
$ git add --all
|
||||
```
|
||||
|
||||
This command stages all of the files that you have changed. You can add individual files by specifying the file name or names and eliminating the “-- all”.
|
||||
This command stages all the files that you have changed. You can add individual files by specifying the file name or names and eliminating the “-- all”.
|
||||
|
||||
**11. Commit the file or files.**
|
||||
|
||||
@@ -96,8 +96,7 @@ If there are conflicts between your edits and those made by others since you sta
|
||||
$ git status
|
||||
```
|
||||
|
||||
Open those files one at a time and you
|
||||
will see lines inserted by Git that identify the conflicts:
|
||||
Open those files one at a time, and you will see lines inserted by Git that identify the conflicts:
|
||||
|
||||
```
|
||||
<<<<<< HEAD
|
||||
@@ -119,7 +118,7 @@ $ git push myrepo feature-in-progress-branch
|
||||
|
||||
**15. Check to be sure your fork of the Prysm repo contains your feature branch with the latest edits.**
|
||||
|
||||
Navigate to your fork of the repo on Github. On the upper left where the current branch is listed, change the branch to your feature-in-progress-branch. Open the files that you have worked on and check to make sure they include your changes.
|
||||
Navigate to your fork of the repo on GitHub. On the upper left where the current branch is listed, change the branch to your feature-in-progress-branch. Open the files that you have worked on and check to make sure they include your changes.
|
||||
|
||||
**16. Create a pull request.**
|
||||
|
||||
@@ -151,7 +150,7 @@ pick hash fix a bug
|
||||
pick hash add a feature
|
||||
```
|
||||
|
||||
Replace the word pick with the word “squash” for every line but the first so you end with ….
|
||||
Replace the word pick with the word “squash” for every line but the first, so you end with ….
|
||||
|
||||
```
|
||||
pick hash do some work
|
||||
@@ -178,7 +177,7 @@ We consider two types of contributions to our repo and categorize them as follow
|
||||
Anyone can become a part-time contributor and help out on implementing Ethereum consensus. The responsibilities of a part-time contributor include:
|
||||
|
||||
- Engaging in Gitter conversations, asking the questions on how to begin contributing to the project
|
||||
- Opening up github issues to express interest in code to implement
|
||||
- Opening up GitHub issues to express interest in code to implement
|
||||
- Opening up PRs referencing any open issue in the repo. PRs should include:
|
||||
- Detailed context of what would be required for merge
|
||||
- Tests that are consistent with how other tests are written in our implementation
|
||||
@@ -188,12 +187,12 @@ Anyone can become a part-time contributor and help out on implementing Ethereum
|
||||
|
||||
### Core Contributors
|
||||
|
||||
Core contributors are remote contractors of Prysmatic Labs, LLC. and are considered critical team members of our organization. Core devs have all of the responsibilities of part-time contributors plus the majority of the following:
|
||||
Core contributors are remote contractors of Prysmatic Labs, LLC. and are considered critical team members of our organization. Core devs have all the responsibilities of part-time contributors plus the majority of the following:
|
||||
|
||||
- Stay up to date on the latest beacon chain specification
|
||||
- Monitor github issues and PR’s to make sure owner, labels, descriptions are correct
|
||||
- Monitor GitHub issues and PR’s to make sure owner, labels, descriptions are correct
|
||||
- Formulate independent ideas, suggest new work to do, point out improvements to existing approaches
|
||||
- Participate in code review, ensure code quality is excellent, and have ensure high code coverage
|
||||
- Participate in code review, ensure code quality is excellent, and ensure high code coverage
|
||||
- Help with social media presence, write bi-weekly development update
|
||||
- Represent Prysmatic Labs at events to help spread the word on scalability research and solutions
|
||||
|
||||
|
||||
46
WORKSPACE
46
WORKSPACE
@@ -16,27 +16,37 @@ load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies")
|
||||
|
||||
rules_pkg_dependencies()
|
||||
|
||||
HERMETIC_CC_TOOLCHAIN_VERSION = "v2.0.0"
|
||||
|
||||
http_archive(
|
||||
name = "com_grail_bazel_toolchain",
|
||||
sha256 = "b210fc8e58782ef171f428bfc850ed7179bdd805543ebd1aa144b9c93489134f",
|
||||
strip_prefix = "bazel-toolchain-83e69ba9e4b4fdad0d1d057fcb87addf77c281c9",
|
||||
urls = ["https://github.com/grailbio/bazel-toolchain/archive/83e69ba9e4b4fdad0d1d057fcb87addf77c281c9.tar.gz"],
|
||||
name = "hermetic_cc_toolchain",
|
||||
sha256 = "57f03a6c29793e8add7bd64186fc8066d23b5ffd06fe9cc6b0b8c499914d3a65",
|
||||
urls = [
|
||||
"https://mirror.bazel.build/github.com/uber/hermetic_cc_toolchain/releases/download/{0}/hermetic_cc_toolchain-{0}.tar.gz".format(HERMETIC_CC_TOOLCHAIN_VERSION),
|
||||
"https://github.com/uber/hermetic_cc_toolchain/releases/download/{0}/hermetic_cc_toolchain-{0}.tar.gz".format(HERMETIC_CC_TOOLCHAIN_VERSION),
|
||||
],
|
||||
)
|
||||
|
||||
load("@com_grail_bazel_toolchain//toolchain:deps.bzl", "bazel_toolchain_dependencies")
|
||||
load("@hermetic_cc_toolchain//toolchain:defs.bzl", zig_toolchains = "toolchains")
|
||||
|
||||
bazel_toolchain_dependencies()
|
||||
zig_toolchains()
|
||||
|
||||
load("@com_grail_bazel_toolchain//toolchain:rules.bzl", "llvm_toolchain")
|
||||
|
||||
llvm_toolchain(
|
||||
name = "llvm_toolchain",
|
||||
llvm_version = "13.0.1",
|
||||
# Register zig sdk toolchains with support for Ubuntu 20.04 (Focal Fossa) which has an EOL date of April, 2025.
|
||||
# For ubuntu glibc support, see https://launchpad.net/ubuntu/+source/glibc
|
||||
register_toolchains(
|
||||
"@zig_sdk//toolchain:linux_amd64_gnu.2.31",
|
||||
"@zig_sdk//toolchain:linux_arm64_gnu.2.31",
|
||||
# Hermetic cc toolchain is not yet supported on darwin. Sysroot needs to be provided.
|
||||
# See https://github.com/uber/hermetic_cc_toolchain#osx-sysroot
|
||||
# "@zig_sdk//toolchain:darwin_amd64",
|
||||
# "@zig_sdk//toolchain:darwin_arm64",
|
||||
# Windows builds are not supported yet.
|
||||
# "@zig_sdk//toolchain:windows_amd64",
|
||||
)
|
||||
|
||||
load("@llvm_toolchain//:toolchains.bzl", "llvm_register_toolchains")
|
||||
load("@prysm//tools/cross-toolchain:darwin_cc_hack.bzl", "configure_nonhermetic_darwin")
|
||||
|
||||
llvm_register_toolchains()
|
||||
configure_nonhermetic_darwin()
|
||||
|
||||
load("@prysm//tools/cross-toolchain:prysm_toolchains.bzl", "configure_prysm_toolchains")
|
||||
|
||||
@@ -311,11 +321,13 @@ http_archive(
|
||||
url = "https://github.com/bazelbuild/buildtools/archive/f2aed9ee205d62d45c55cfabbfd26342f8526862.zip",
|
||||
)
|
||||
|
||||
git_repository(
|
||||
http_archive(
|
||||
name = "com_google_protobuf",
|
||||
commit = "436bd7880e458532901c58f4d9d1ea23fa7edd52",
|
||||
remote = "https://github.com/protocolbuffers/protobuf",
|
||||
shallow_since = "1617835118 -0700",
|
||||
sha256 = "4e176116949be52b0408dfd24f8925d1eb674a781ae242a75296b17a1c721395",
|
||||
strip_prefix = "protobuf-23.3",
|
||||
urls = [
|
||||
"https://github.com/protocolbuffers/protobuf/archive/v23.3.tar.gz",
|
||||
],
|
||||
)
|
||||
|
||||
# Group the sources of the library so that CMake rule have access to it
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/ssz/detect"
|
||||
"github.com/prysmaticlabs/prysm/v4/io/file"
|
||||
"github.com/prysmaticlabs/prysm/v4/runtime/version"
|
||||
@@ -19,6 +20,8 @@ import (
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
var errCheckpointBlockMismatch = errors.New("mismatch between checkpoint sync state and block")
|
||||
|
||||
// OriginData represents the BeaconState and ReadOnlySignedBeaconBlock necessary to start an empty Beacon Node
|
||||
// using Checkpoint Sync.
|
||||
type OriginData struct {
|
||||
@@ -75,37 +78,40 @@ func DownloadFinalizedData(ctx context.Context, client *Client) (*OriginData, er
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error unmarshaling finalized state to correct version")
|
||||
}
|
||||
if s.Slot() != s.LatestBlockHeader().Slot {
|
||||
return nil, fmt.Errorf("finalized state slot does not match latest block header slot %d != %d", s.Slot(), s.LatestBlockHeader().Slot)
|
||||
}
|
||||
|
||||
sr, err := s.HashTreeRoot(ctx)
|
||||
slot := s.LatestBlockHeader().Slot
|
||||
bb, err := client.GetBlock(ctx, IdFromSlot(slot))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to compute htr for finalized state at slot=%d", s.Slot())
|
||||
}
|
||||
header := s.LatestBlockHeader()
|
||||
header.StateRoot = sr[:]
|
||||
br, err := header.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error while computing block root using state data")
|
||||
}
|
||||
|
||||
bb, err := client.GetBlock(ctx, IdFromRoot(br))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error requesting block by root = %#x", br)
|
||||
return nil, errors.Wrapf(err, "error requesting block by slot = %d", slot)
|
||||
}
|
||||
b, err := vu.UnmarshalBeaconBlock(bb)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to unmarshal block to a supported type using the detected fork schedule")
|
||||
}
|
||||
realBlockRoot, err := b.Block().HashTreeRoot()
|
||||
br, err := b.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error computing hash_tree_root of retrieved block")
|
||||
}
|
||||
bodyRoot, err := b.Block().Body().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error computing hash_tree_root of retrieved block body")
|
||||
}
|
||||
|
||||
log.Printf("BeaconState slot=%d, Block slot=%d", s.Slot(), b.Block().Slot())
|
||||
log.Printf("BeaconState htr=%#x, Block state_root=%#x", sr, b.Block().StateRoot())
|
||||
log.Printf("BeaconState latest_block_header htr=%#x, block htr=%#x", br, realBlockRoot)
|
||||
sbr := bytesutil.ToBytes32(s.LatestBlockHeader().BodyRoot)
|
||||
if sbr != bodyRoot {
|
||||
return nil, errors.Wrapf(errCheckpointBlockMismatch, "state body root = %#x, block body root = %#x", sbr, bodyRoot)
|
||||
}
|
||||
sr, err := s.HashTreeRoot(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to compute htr for finalized state at slot=%d", s.Slot())
|
||||
}
|
||||
|
||||
log.
|
||||
WithField("block_slot", b.Block().Slot()).
|
||||
WithField("state_slot", s.Slot()).
|
||||
WithField("state_root", sr).
|
||||
WithField("block_root", br).
|
||||
Info("Downloaded checkpoint sync state and block.")
|
||||
return &OriginData{
|
||||
st: s,
|
||||
b: b,
|
||||
|
||||
@@ -128,6 +128,7 @@ func TestDownloadWeakSubjectivityCheckpoint(t *testing.T) {
|
||||
wst, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
fork, err := forkForEpoch(cfg, epoch)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, wst.SetFork(fork))
|
||||
|
||||
// set up checkpoint block
|
||||
@@ -226,6 +227,7 @@ func TestDownloadBackwardsCompatibleCombined(t *testing.T) {
|
||||
wst, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
fork, err := forkForEpoch(cfg, cfg.GenesisEpoch)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, wst.SetFork(fork))
|
||||
|
||||
// set up checkpoint block
|
||||
@@ -399,6 +401,7 @@ func TestDownloadFinalizedData(t *testing.T) {
|
||||
st, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
fork, err := forkForEpoch(cfg, epoch)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, st.SetFork(fork))
|
||||
require.NoError(t, st.SetSlot(slot))
|
||||
|
||||
@@ -440,7 +443,7 @@ func TestDownloadFinalizedData(t *testing.T) {
|
||||
case renderGetStatePath(IdFinalized):
|
||||
res.StatusCode = http.StatusOK
|
||||
res.Body = io.NopCloser(bytes.NewBuffer(ms))
|
||||
case renderGetBlockPath(IdFromRoot(br)):
|
||||
case renderGetBlockPath(IdFromSlot(b.Block().Slot())):
|
||||
res.StatusCode = http.StatusOK
|
||||
res.Body = io.NopCloser(bytes.NewBuffer(mb))
|
||||
default:
|
||||
|
||||
@@ -3,7 +3,6 @@ package builder
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
ssz "github.com/prysmaticlabs/fastssz"
|
||||
consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
|
||||
@@ -162,9 +161,6 @@ func WrappedBuilderBidCapella(p *ethpb.BuilderBidCapella) (Bid, error) {
|
||||
|
||||
// Header returns the execution data interface.
|
||||
func (b builderBidCapella) Header() (interfaces.ExecutionData, error) {
|
||||
if b.p == nil {
|
||||
return nil, errors.New("builder bid is nil")
|
||||
}
|
||||
// We have to convert big endian to little endian because the value is coming from the execution layer.
|
||||
v := big.NewInt(0).SetBytes(bytesutil.ReverseByteOrder(b.p.Value))
|
||||
return blocks.WrappedExecutionPayloadHeaderCapella(b.p.Header, math.WeiToGwei(v))
|
||||
|
||||
@@ -14,14 +14,17 @@ import (
|
||||
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// SignedValidatorRegistration a struct for signed validator registrations.
|
||||
type SignedValidatorRegistration struct {
|
||||
*eth.SignedValidatorRegistrationV1
|
||||
}
|
||||
|
||||
// ValidatorRegistration a struct for validator registrations.
|
||||
type ValidatorRegistration struct {
|
||||
*eth.ValidatorRegistrationV1
|
||||
}
|
||||
|
||||
// MarshalJSON returns a json representation copy of signed validator registration.
|
||||
func (r *SignedValidatorRegistration) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Message *ValidatorRegistration `json:"message"`
|
||||
@@ -32,6 +35,7 @@ func (r *SignedValidatorRegistration) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON returns a byte representation of signed validator registration from json.
|
||||
func (r *SignedValidatorRegistration) UnmarshalJSON(b []byte) error {
|
||||
if r.SignedValidatorRegistrationV1 == nil {
|
||||
r.SignedValidatorRegistrationV1 = ð.SignedValidatorRegistrationV1{}
|
||||
@@ -48,6 +52,7 @@ func (r *SignedValidatorRegistration) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns a json representation copy of validator registration.
|
||||
func (r *ValidatorRegistration) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
FeeRecipient hexutil.Bytes `json:"fee_recipient"`
|
||||
@@ -62,6 +67,7 @@ func (r *ValidatorRegistration) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON returns a byte representation of validator registration from json.
|
||||
func (r *ValidatorRegistration) UnmarshalJSON(b []byte) error {
|
||||
if r.ValidatorRegistrationV1 == nil {
|
||||
r.ValidatorRegistrationV1 = ð.ValidatorRegistrationV1{}
|
||||
@@ -92,6 +98,7 @@ func (r *ValidatorRegistration) UnmarshalJSON(b []byte) error {
|
||||
var errInvalidUint256 = errors.New("invalid Uint256")
|
||||
var errDecodeUint256 = errors.New("unable to decode into Uint256")
|
||||
|
||||
// Uint256 a wrapper representation of big.Int
|
||||
type Uint256 struct {
|
||||
*big.Int
|
||||
}
|
||||
@@ -118,7 +125,7 @@ func sszBytesToUint256(b []byte) (Uint256, error) {
|
||||
return Uint256{Int: bi}, nil
|
||||
}
|
||||
|
||||
// SSZBytes creates an ssz-style (little-endian byte slice) representation of the Uint256
|
||||
// SSZBytes creates an ssz-style (little-endian byte slice) representation of the Uint256.
|
||||
func (s Uint256) SSZBytes() []byte {
|
||||
if !isValidUint256(s.Int) {
|
||||
return []byte{}
|
||||
@@ -126,18 +133,19 @@ func (s Uint256) SSZBytes() []byte {
|
||||
return bytesutil.PadTo(bytesutil.ReverseByteOrder(s.Int.Bytes()), 32)
|
||||
}
|
||||
|
||||
// UnmarshalJSON takes in a byte array and unmarshals the value in Uint256
|
||||
func (s *Uint256) UnmarshalJSON(t []byte) error {
|
||||
start := 0
|
||||
end := len(t)
|
||||
if t[0] == '"' {
|
||||
start += 1
|
||||
if len(t) < 2 {
|
||||
return errors.Errorf("provided Uint256 json string is too short: %s", string(t))
|
||||
}
|
||||
if t[end-1] == '"' {
|
||||
end -= 1
|
||||
if t[0] != '"' || t[end-1] != '"' {
|
||||
return errors.Errorf("provided Uint256 json string is malformed: %s", string(t))
|
||||
}
|
||||
return s.UnmarshalText(t[start:end])
|
||||
return s.UnmarshalText(t[1 : end-1])
|
||||
}
|
||||
|
||||
// UnmarshalText takes in a byte array and unmarshals the text in Uint256
|
||||
func (s *Uint256) UnmarshalText(t []byte) error {
|
||||
if s.Int == nil {
|
||||
s.Int = big.NewInt(0)
|
||||
@@ -153,6 +161,7 @@ func (s *Uint256) UnmarshalText(t []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns a json byte representation of Uint256.
|
||||
func (s Uint256) MarshalJSON() ([]byte, error) {
|
||||
t, err := s.MarshalText()
|
||||
if err != nil {
|
||||
@@ -163,6 +172,7 @@ func (s Uint256) MarshalJSON() ([]byte, error) {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// MarshalText returns a text byte representation of Uint256.
|
||||
func (s Uint256) MarshalText() ([]byte, error) {
|
||||
if !isValidUint256(s.Int) {
|
||||
return nil, errors.Wrapf(errInvalidUint256, "value=%s", s.Int)
|
||||
@@ -170,22 +180,27 @@ func (s Uint256) MarshalText() ([]byte, error) {
|
||||
return []byte(s.String()), nil
|
||||
}
|
||||
|
||||
// Uint64String is a custom type that allows marshalling from text to uint64 and vice versa.
|
||||
type Uint64String uint64
|
||||
|
||||
// UnmarshalText takes a byte array and unmarshals the text in Uint64String.
|
||||
func (s *Uint64String) UnmarshalText(t []byte) error {
|
||||
u, err := strconv.ParseUint(string(t), 10, 64)
|
||||
*s = Uint64String(u)
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalText returns a byte representation of the text from Uint64String.
|
||||
func (s Uint64String) MarshalText() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf("%d", s)), nil
|
||||
}
|
||||
|
||||
// VersionResponse is a JSON representation of a field in the builder API header response.
|
||||
type VersionResponse struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// ExecHeaderResponse is a JSON representation of the builder API header response for Bellatrix.
|
||||
type ExecHeaderResponse struct {
|
||||
Version string `json:"version"`
|
||||
Data struct {
|
||||
@@ -194,6 +209,7 @@ type ExecHeaderResponse struct {
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// ToProto returns a SignedBuilderBid from ExecHeaderResponse for Bellatrix.
|
||||
func (ehr *ExecHeaderResponse) ToProto() (*eth.SignedBuilderBid, error) {
|
||||
bb, err := ehr.Data.Message.ToProto()
|
||||
if err != nil {
|
||||
@@ -205,6 +221,7 @@ func (ehr *ExecHeaderResponse) ToProto() (*eth.SignedBuilderBid, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ToProto returns a BuilderBid Proto for Bellatrix.
|
||||
func (bb *BuilderBid) ToProto() (*eth.BuilderBid, error) {
|
||||
header, err := bb.Header.ToProto()
|
||||
if err != nil {
|
||||
@@ -217,31 +234,34 @@ func (bb *BuilderBid) ToProto() (*eth.BuilderBid, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ToProto returns a ExecutionPayloadHeader for Bellatrix.
|
||||
func (h *ExecutionPayloadHeader) ToProto() (*v1.ExecutionPayloadHeader, error) {
|
||||
return &v1.ExecutionPayloadHeader{
|
||||
ParentHash: h.ParentHash,
|
||||
FeeRecipient: h.FeeRecipient,
|
||||
StateRoot: h.StateRoot,
|
||||
ReceiptsRoot: h.ReceiptsRoot,
|
||||
LogsBloom: h.LogsBloom,
|
||||
PrevRandao: h.PrevRandao,
|
||||
ParentHash: bytesutil.SafeCopyBytes(h.ParentHash),
|
||||
FeeRecipient: bytesutil.SafeCopyBytes(h.FeeRecipient),
|
||||
StateRoot: bytesutil.SafeCopyBytes(h.StateRoot),
|
||||
ReceiptsRoot: bytesutil.SafeCopyBytes(h.ReceiptsRoot),
|
||||
LogsBloom: bytesutil.SafeCopyBytes(h.LogsBloom),
|
||||
PrevRandao: bytesutil.SafeCopyBytes(h.PrevRandao),
|
||||
BlockNumber: uint64(h.BlockNumber),
|
||||
GasLimit: uint64(h.GasLimit),
|
||||
GasUsed: uint64(h.GasUsed),
|
||||
Timestamp: uint64(h.Timestamp),
|
||||
ExtraData: h.ExtraData,
|
||||
BaseFeePerGas: h.BaseFeePerGas.SSZBytes(),
|
||||
BlockHash: h.BlockHash,
|
||||
TransactionsRoot: h.TransactionsRoot,
|
||||
ExtraData: bytesutil.SafeCopyBytes(h.ExtraData),
|
||||
BaseFeePerGas: bytesutil.SafeCopyBytes(h.BaseFeePerGas.SSZBytes()),
|
||||
BlockHash: bytesutil.SafeCopyBytes(h.BlockHash),
|
||||
TransactionsRoot: bytesutil.SafeCopyBytes(h.TransactionsRoot),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BuilderBid is part of ExecHeaderResponse for Bellatrix.
|
||||
type BuilderBid struct {
|
||||
Header *ExecutionPayloadHeader `json:"header"`
|
||||
Value Uint256 `json:"value"`
|
||||
Pubkey hexutil.Bytes `json:"pubkey"`
|
||||
}
|
||||
|
||||
// ExecutionPayloadHeader is a field in BuilderBid.
|
||||
type ExecutionPayloadHeader struct {
|
||||
ParentHash hexutil.Bytes `json:"parent_hash"`
|
||||
FeeRecipient hexutil.Bytes `json:"fee_recipient"`
|
||||
@@ -260,6 +280,7 @@ type ExecutionPayloadHeader struct {
|
||||
*v1.ExecutionPayloadHeader
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON bytes representation of ExecutionPayloadHeader.
|
||||
func (h *ExecutionPayloadHeader) MarshalJSON() ([]byte, error) {
|
||||
type MarshalCaller ExecutionPayloadHeader
|
||||
baseFeePerGas, err := sszBytesToUint256(h.ExecutionPayloadHeader.BaseFeePerGas)
|
||||
@@ -284,6 +305,7 @@ func (h *ExecutionPayloadHeader) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON takes in a JSON byte array and sets ExecutionPayloadHeader.
|
||||
func (h *ExecutionPayloadHeader) UnmarshalJSON(b []byte) error {
|
||||
type UnmarshalCaller ExecutionPayloadHeader
|
||||
uc := &UnmarshalCaller{}
|
||||
@@ -297,11 +319,13 @@ func (h *ExecutionPayloadHeader) UnmarshalJSON(b []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecPayloadResponse is the builder API /eth/v1/builder/blinded_blocks for Bellatrix.
|
||||
type ExecPayloadResponse struct {
|
||||
Version string `json:"version"`
|
||||
Data ExecutionPayload `json:"data"`
|
||||
}
|
||||
|
||||
// ExecutionPayload is a field of ExecPayloadResponse
|
||||
type ExecutionPayload struct {
|
||||
ParentHash hexutil.Bytes `json:"parent_hash"`
|
||||
FeeRecipient hexutil.Bytes `json:"fee_recipient"`
|
||||
@@ -319,29 +343,31 @@ type ExecutionPayload struct {
|
||||
Transactions []hexutil.Bytes `json:"transactions"`
|
||||
}
|
||||
|
||||
// ToProto returns a ExecutionPayload Proto from ExecPayloadResponse
|
||||
func (r *ExecPayloadResponse) ToProto() (*v1.ExecutionPayload, error) {
|
||||
return r.Data.ToProto()
|
||||
}
|
||||
|
||||
// ToProto returns a ExecutionPayload Proto
|
||||
func (p *ExecutionPayload) ToProto() (*v1.ExecutionPayload, error) {
|
||||
txs := make([][]byte, len(p.Transactions))
|
||||
for i := range p.Transactions {
|
||||
txs[i] = p.Transactions[i]
|
||||
txs[i] = bytesutil.SafeCopyBytes(p.Transactions[i])
|
||||
}
|
||||
return &v1.ExecutionPayload{
|
||||
ParentHash: p.ParentHash,
|
||||
FeeRecipient: p.FeeRecipient,
|
||||
StateRoot: p.StateRoot,
|
||||
ReceiptsRoot: p.ReceiptsRoot,
|
||||
LogsBloom: p.LogsBloom,
|
||||
PrevRandao: p.PrevRandao,
|
||||
ParentHash: bytesutil.SafeCopyBytes(p.ParentHash),
|
||||
FeeRecipient: bytesutil.SafeCopyBytes(p.FeeRecipient),
|
||||
StateRoot: bytesutil.SafeCopyBytes(p.StateRoot),
|
||||
ReceiptsRoot: bytesutil.SafeCopyBytes(p.ReceiptsRoot),
|
||||
LogsBloom: bytesutil.SafeCopyBytes(p.LogsBloom),
|
||||
PrevRandao: bytesutil.SafeCopyBytes(p.PrevRandao),
|
||||
BlockNumber: uint64(p.BlockNumber),
|
||||
GasLimit: uint64(p.GasLimit),
|
||||
GasUsed: uint64(p.GasUsed),
|
||||
Timestamp: uint64(p.Timestamp),
|
||||
ExtraData: p.ExtraData,
|
||||
BaseFeePerGas: p.BaseFeePerGas.SSZBytes(),
|
||||
BlockHash: p.BlockHash,
|
||||
ExtraData: bytesutil.SafeCopyBytes(p.ExtraData),
|
||||
BaseFeePerGas: bytesutil.SafeCopyBytes(p.BaseFeePerGas.SSZBytes()),
|
||||
BlockHash: bytesutil.SafeCopyBytes(p.BlockHash),
|
||||
Transactions: txs,
|
||||
}, nil
|
||||
}
|
||||
@@ -355,22 +381,22 @@ func FromProto(payload *v1.ExecutionPayload) (ExecutionPayload, error) {
|
||||
}
|
||||
txs := make([]hexutil.Bytes, len(payload.Transactions))
|
||||
for i := range payload.Transactions {
|
||||
txs[i] = payload.Transactions[i]
|
||||
txs[i] = bytesutil.SafeCopyBytes(payload.Transactions[i])
|
||||
}
|
||||
return ExecutionPayload{
|
||||
ParentHash: payload.ParentHash,
|
||||
FeeRecipient: payload.FeeRecipient,
|
||||
StateRoot: payload.StateRoot,
|
||||
ReceiptsRoot: payload.ReceiptsRoot,
|
||||
LogsBloom: payload.LogsBloom,
|
||||
PrevRandao: payload.PrevRandao,
|
||||
ParentHash: bytesutil.SafeCopyBytes(payload.ParentHash),
|
||||
FeeRecipient: bytesutil.SafeCopyBytes(payload.FeeRecipient),
|
||||
StateRoot: bytesutil.SafeCopyBytes(payload.StateRoot),
|
||||
ReceiptsRoot: bytesutil.SafeCopyBytes(payload.ReceiptsRoot),
|
||||
LogsBloom: bytesutil.SafeCopyBytes(payload.LogsBloom),
|
||||
PrevRandao: bytesutil.SafeCopyBytes(payload.PrevRandao),
|
||||
BlockNumber: Uint64String(payload.BlockNumber),
|
||||
GasLimit: Uint64String(payload.GasLimit),
|
||||
GasUsed: Uint64String(payload.GasUsed),
|
||||
Timestamp: Uint64String(payload.Timestamp),
|
||||
ExtraData: payload.ExtraData,
|
||||
ExtraData: bytesutil.SafeCopyBytes(payload.ExtraData),
|
||||
BaseFeePerGas: bFee,
|
||||
BlockHash: payload.BlockHash,
|
||||
BlockHash: bytesutil.SafeCopyBytes(payload.BlockHash),
|
||||
Transactions: txs,
|
||||
}, nil
|
||||
}
|
||||
@@ -384,36 +410,37 @@ func FromProtoCapella(payload *v1.ExecutionPayloadCapella) (ExecutionPayloadCape
|
||||
}
|
||||
txs := make([]hexutil.Bytes, len(payload.Transactions))
|
||||
for i := range payload.Transactions {
|
||||
txs[i] = payload.Transactions[i]
|
||||
txs[i] = bytesutil.SafeCopyBytes(payload.Transactions[i])
|
||||
}
|
||||
withdrawals := make([]Withdrawal, len(payload.Withdrawals))
|
||||
for i, w := range payload.Withdrawals {
|
||||
withdrawals[i] = Withdrawal{
|
||||
Index: Uint256{Int: big.NewInt(0).SetUint64(w.Index)},
|
||||
ValidatorIndex: Uint256{Int: big.NewInt(0).SetUint64(uint64(w.ValidatorIndex))},
|
||||
Address: w.Address,
|
||||
Address: bytesutil.SafeCopyBytes(w.Address),
|
||||
Amount: Uint256{Int: big.NewInt(0).SetUint64(w.Amount)},
|
||||
}
|
||||
}
|
||||
return ExecutionPayloadCapella{
|
||||
ParentHash: payload.ParentHash,
|
||||
FeeRecipient: payload.FeeRecipient,
|
||||
StateRoot: payload.StateRoot,
|
||||
ReceiptsRoot: payload.ReceiptsRoot,
|
||||
LogsBloom: payload.LogsBloom,
|
||||
PrevRandao: payload.PrevRandao,
|
||||
ParentHash: bytesutil.SafeCopyBytes(payload.ParentHash),
|
||||
FeeRecipient: bytesutil.SafeCopyBytes(payload.FeeRecipient),
|
||||
StateRoot: bytesutil.SafeCopyBytes(payload.StateRoot),
|
||||
ReceiptsRoot: bytesutil.SafeCopyBytes(payload.ReceiptsRoot),
|
||||
LogsBloom: bytesutil.SafeCopyBytes(payload.LogsBloom),
|
||||
PrevRandao: bytesutil.SafeCopyBytes(payload.PrevRandao),
|
||||
BlockNumber: Uint64String(payload.BlockNumber),
|
||||
GasLimit: Uint64String(payload.GasLimit),
|
||||
GasUsed: Uint64String(payload.GasUsed),
|
||||
Timestamp: Uint64String(payload.Timestamp),
|
||||
ExtraData: payload.ExtraData,
|
||||
ExtraData: bytesutil.SafeCopyBytes(payload.ExtraData),
|
||||
BaseFeePerGas: bFee,
|
||||
BlockHash: payload.BlockHash,
|
||||
BlockHash: bytesutil.SafeCopyBytes(payload.BlockHash),
|
||||
Transactions: txs,
|
||||
Withdrawals: withdrawals,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ExecHeaderResponseCapella is the response of builder API /eth/v1/builder/header/{slot}/{parent_hash}/{pubkey} for Capella.
|
||||
type ExecHeaderResponseCapella struct {
|
||||
Data struct {
|
||||
Signature hexutil.Bytes `json:"signature"`
|
||||
@@ -421,6 +448,7 @@ type ExecHeaderResponseCapella struct {
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// ToProto returns a SignedBuilderBidCapella Proto from ExecHeaderResponseCapella.
|
||||
func (ehr *ExecHeaderResponseCapella) ToProto() (*eth.SignedBuilderBidCapella, error) {
|
||||
bb, err := ehr.Data.Message.ToProto()
|
||||
if err != nil {
|
||||
@@ -428,10 +456,11 @@ func (ehr *ExecHeaderResponseCapella) ToProto() (*eth.SignedBuilderBidCapella, e
|
||||
}
|
||||
return ð.SignedBuilderBidCapella{
|
||||
Message: bb,
|
||||
Signature: ehr.Data.Signature,
|
||||
Signature: bytesutil.SafeCopyBytes(ehr.Data.Signature),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ToProto returns a BuilderBidCapella Proto.
|
||||
func (bb *BuilderBidCapella) ToProto() (*eth.BuilderBidCapella, error) {
|
||||
header, err := bb.Header.ToProto()
|
||||
if err != nil {
|
||||
@@ -439,37 +468,40 @@ func (bb *BuilderBidCapella) ToProto() (*eth.BuilderBidCapella, error) {
|
||||
}
|
||||
return ð.BuilderBidCapella{
|
||||
Header: header,
|
||||
Value: bb.Value.SSZBytes(),
|
||||
Pubkey: bb.Pubkey,
|
||||
Value: bytesutil.SafeCopyBytes(bb.Value.SSZBytes()),
|
||||
Pubkey: bytesutil.SafeCopyBytes(bb.Pubkey),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ToProto returns a ExecutionPayloadHeaderCapella Proto
|
||||
func (h *ExecutionPayloadHeaderCapella) ToProto() (*v1.ExecutionPayloadHeaderCapella, error) {
|
||||
return &v1.ExecutionPayloadHeaderCapella{
|
||||
ParentHash: h.ParentHash,
|
||||
FeeRecipient: h.FeeRecipient,
|
||||
StateRoot: h.StateRoot,
|
||||
ReceiptsRoot: h.ReceiptsRoot,
|
||||
LogsBloom: h.LogsBloom,
|
||||
PrevRandao: h.PrevRandao,
|
||||
ParentHash: bytesutil.SafeCopyBytes(h.ParentHash),
|
||||
FeeRecipient: bytesutil.SafeCopyBytes(h.FeeRecipient),
|
||||
StateRoot: bytesutil.SafeCopyBytes(h.StateRoot),
|
||||
ReceiptsRoot: bytesutil.SafeCopyBytes(h.ReceiptsRoot),
|
||||
LogsBloom: bytesutil.SafeCopyBytes(h.LogsBloom),
|
||||
PrevRandao: bytesutil.SafeCopyBytes(h.PrevRandao),
|
||||
BlockNumber: uint64(h.BlockNumber),
|
||||
GasLimit: uint64(h.GasLimit),
|
||||
GasUsed: uint64(h.GasUsed),
|
||||
Timestamp: uint64(h.Timestamp),
|
||||
ExtraData: h.ExtraData,
|
||||
BaseFeePerGas: h.BaseFeePerGas.SSZBytes(),
|
||||
BlockHash: h.BlockHash,
|
||||
TransactionsRoot: h.TransactionsRoot,
|
||||
WithdrawalsRoot: h.WithdrawalsRoot,
|
||||
ExtraData: bytesutil.SafeCopyBytes(h.ExtraData),
|
||||
BaseFeePerGas: bytesutil.SafeCopyBytes(h.BaseFeePerGas.SSZBytes()),
|
||||
BlockHash: bytesutil.SafeCopyBytes(h.BlockHash),
|
||||
TransactionsRoot: bytesutil.SafeCopyBytes(h.TransactionsRoot),
|
||||
WithdrawalsRoot: bytesutil.SafeCopyBytes(h.WithdrawalsRoot),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BuilderBidCapella is field of ExecHeaderResponseCapella.
|
||||
type BuilderBidCapella struct {
|
||||
Header *ExecutionPayloadHeaderCapella `json:"header"`
|
||||
Value Uint256 `json:"value"`
|
||||
Pubkey hexutil.Bytes `json:"pubkey"`
|
||||
}
|
||||
|
||||
// ExecutionPayloadHeaderCapella is a field in BuilderBidCapella.
|
||||
type ExecutionPayloadHeaderCapella struct {
|
||||
ParentHash hexutil.Bytes `json:"parent_hash"`
|
||||
FeeRecipient hexutil.Bytes `json:"fee_recipient"`
|
||||
@@ -489,6 +521,7 @@ type ExecutionPayloadHeaderCapella struct {
|
||||
*v1.ExecutionPayloadHeaderCapella
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte representation of ExecutionPayloadHeaderCapella.
|
||||
func (h *ExecutionPayloadHeaderCapella) MarshalJSON() ([]byte, error) {
|
||||
type MarshalCaller ExecutionPayloadHeaderCapella
|
||||
baseFeePerGas, err := sszBytesToUint256(h.ExecutionPayloadHeaderCapella.BaseFeePerGas)
|
||||
@@ -514,6 +547,7 @@ func (h *ExecutionPayloadHeaderCapella) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON takes a JSON byte array and sets ExecutionPayloadHeaderCapella.
|
||||
func (h *ExecutionPayloadHeaderCapella) UnmarshalJSON(b []byte) error {
|
||||
type UnmarshalCaller ExecutionPayloadHeaderCapella
|
||||
uc := &UnmarshalCaller{}
|
||||
@@ -527,11 +561,13 @@ func (h *ExecutionPayloadHeaderCapella) UnmarshalJSON(b []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecPayloadResponseCapella is the builder API /eth/v1/builder/blinded_blocks for Capella.
|
||||
type ExecPayloadResponseCapella struct {
|
||||
Version string `json:"version"`
|
||||
Data ExecutionPayloadCapella `json:"data"`
|
||||
}
|
||||
|
||||
// ExecutionPayloadCapella is a field of ExecPayloadResponseCapella.
|
||||
type ExecutionPayloadCapella struct {
|
||||
ParentHash hexutil.Bytes `json:"parent_hash"`
|
||||
FeeRecipient hexutil.Bytes `json:"fee_recipient"`
|
||||
@@ -550,43 +586,46 @@ type ExecutionPayloadCapella struct {
|
||||
Withdrawals []Withdrawal `json:"withdrawals"`
|
||||
}
|
||||
|
||||
// ToProto returns a ExecutionPayloadCapella Proto.
|
||||
func (r *ExecPayloadResponseCapella) ToProto() (*v1.ExecutionPayloadCapella, error) {
|
||||
return r.Data.ToProto()
|
||||
}
|
||||
|
||||
// ToProto returns a ExecutionPayloadCapella Proto.
|
||||
func (p *ExecutionPayloadCapella) ToProto() (*v1.ExecutionPayloadCapella, error) {
|
||||
txs := make([][]byte, len(p.Transactions))
|
||||
for i := range p.Transactions {
|
||||
txs[i] = p.Transactions[i]
|
||||
txs[i] = bytesutil.SafeCopyBytes(p.Transactions[i])
|
||||
}
|
||||
withdrawals := make([]*v1.Withdrawal, len(p.Withdrawals))
|
||||
for i, w := range p.Withdrawals {
|
||||
withdrawals[i] = &v1.Withdrawal{
|
||||
Index: w.Index.Uint64(),
|
||||
ValidatorIndex: types.ValidatorIndex(w.ValidatorIndex.Uint64()),
|
||||
Address: w.Address,
|
||||
Address: bytesutil.SafeCopyBytes(w.Address),
|
||||
Amount: w.Amount.Uint64(),
|
||||
}
|
||||
}
|
||||
return &v1.ExecutionPayloadCapella{
|
||||
ParentHash: p.ParentHash,
|
||||
FeeRecipient: p.FeeRecipient,
|
||||
StateRoot: p.StateRoot,
|
||||
ReceiptsRoot: p.ReceiptsRoot,
|
||||
LogsBloom: p.LogsBloom,
|
||||
PrevRandao: p.PrevRandao,
|
||||
ParentHash: bytesutil.SafeCopyBytes(p.ParentHash),
|
||||
FeeRecipient: bytesutil.SafeCopyBytes(p.FeeRecipient),
|
||||
StateRoot: bytesutil.SafeCopyBytes(p.StateRoot),
|
||||
ReceiptsRoot: bytesutil.SafeCopyBytes(p.ReceiptsRoot),
|
||||
LogsBloom: bytesutil.SafeCopyBytes(p.LogsBloom),
|
||||
PrevRandao: bytesutil.SafeCopyBytes(p.PrevRandao),
|
||||
BlockNumber: uint64(p.BlockNumber),
|
||||
GasLimit: uint64(p.GasLimit),
|
||||
GasUsed: uint64(p.GasUsed),
|
||||
Timestamp: uint64(p.Timestamp),
|
||||
ExtraData: p.ExtraData,
|
||||
BaseFeePerGas: p.BaseFeePerGas.SSZBytes(),
|
||||
BlockHash: p.BlockHash,
|
||||
ExtraData: bytesutil.SafeCopyBytes(p.ExtraData),
|
||||
BaseFeePerGas: bytesutil.SafeCopyBytes(p.BaseFeePerGas.SSZBytes()),
|
||||
BlockHash: bytesutil.SafeCopyBytes(p.BlockHash),
|
||||
Transactions: txs,
|
||||
Withdrawals: withdrawals,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Withdrawal is a field of ExecutionPayloadCapella.
|
||||
type Withdrawal struct {
|
||||
Index Uint256 `json:"index"`
|
||||
ValidatorIndex Uint256 `json:"validator_index"`
|
||||
@@ -594,18 +633,22 @@ type Withdrawal struct {
|
||||
Amount Uint256 `json:"amount"`
|
||||
}
|
||||
|
||||
// SignedBlindedBeaconBlockBellatrix is the request object for builder API /eth/v1/builder/blinded_blocks.
|
||||
type SignedBlindedBeaconBlockBellatrix struct {
|
||||
*eth.SignedBlindedBeaconBlockBellatrix
|
||||
}
|
||||
|
||||
// BlindedBeaconBlockBellatrix is a field in SignedBlindedBeaconBlockBellatrix.
|
||||
type BlindedBeaconBlockBellatrix struct {
|
||||
*eth.BlindedBeaconBlockBellatrix
|
||||
}
|
||||
|
||||
// BlindedBeaconBlockBodyBellatrix is a field in BlindedBeaconBlockBellatrix.
|
||||
type BlindedBeaconBlockBodyBellatrix struct {
|
||||
*eth.BlindedBeaconBlockBodyBellatrix
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of SignedBlindedBeaconBlockBellatrix.
|
||||
func (r *SignedBlindedBeaconBlockBellatrix) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Message *BlindedBeaconBlockBellatrix `json:"message"`
|
||||
@@ -616,6 +659,7 @@ func (r *SignedBlindedBeaconBlockBellatrix) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of BlindedBeaconBlockBellatrix.
|
||||
func (b *BlindedBeaconBlockBellatrix) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Slot string `json:"slot"`
|
||||
@@ -632,10 +676,12 @@ func (b *BlindedBeaconBlockBellatrix) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// ProposerSlashing is a field in BlindedBeaconBlockBodyCapella.
|
||||
type ProposerSlashing struct {
|
||||
*eth.ProposerSlashing
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of ProposerSlashing.
|
||||
func (s *ProposerSlashing) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
SignedHeader1 *SignedBeaconBlockHeader `json:"signed_header_1"`
|
||||
@@ -646,10 +692,12 @@ func (s *ProposerSlashing) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// SignedBeaconBlockHeader is a field of ProposerSlashing.
|
||||
type SignedBeaconBlockHeader struct {
|
||||
*eth.SignedBeaconBlockHeader
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of SignedBeaconBlockHeader.
|
||||
func (h *SignedBeaconBlockHeader) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Header *BeaconBlockHeader `json:"message"`
|
||||
@@ -660,10 +708,12 @@ func (h *SignedBeaconBlockHeader) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// BeaconBlockHeader is a field of SignedBeaconBlockHeader.
|
||||
type BeaconBlockHeader struct {
|
||||
*eth.BeaconBlockHeader
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of BeaconBlockHeader.
|
||||
func (h *BeaconBlockHeader) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Slot string `json:"slot"`
|
||||
@@ -680,10 +730,12 @@ func (h *BeaconBlockHeader) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// IndexedAttestation is a field of AttesterSlashing.
|
||||
type IndexedAttestation struct {
|
||||
*eth.IndexedAttestation
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of IndexedAttestation.
|
||||
func (a *IndexedAttestation) MarshalJSON() ([]byte, error) {
|
||||
indices := make([]string, len(a.IndexedAttestation.AttestingIndices))
|
||||
for i := range a.IndexedAttestation.AttestingIndices {
|
||||
@@ -700,10 +752,12 @@ func (a *IndexedAttestation) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// AttesterSlashing is a field of a Beacon Block Body.
|
||||
type AttesterSlashing struct {
|
||||
*eth.AttesterSlashing
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of AttesterSlashing.
|
||||
func (s *AttesterSlashing) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Attestation1 *IndexedAttestation `json:"attestation_1"`
|
||||
@@ -714,10 +768,12 @@ func (s *AttesterSlashing) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// Checkpoint is a field of AttestationData.
|
||||
type Checkpoint struct {
|
||||
*eth.Checkpoint
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of Checkpoint.
|
||||
func (c *Checkpoint) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Epoch string `json:"epoch"`
|
||||
@@ -728,10 +784,12 @@ func (c *Checkpoint) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// AttestationData is a field of IndexedAttestation.
|
||||
type AttestationData struct {
|
||||
*eth.AttestationData
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of AttestationData.
|
||||
func (a *AttestationData) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Slot string `json:"slot"`
|
||||
@@ -748,10 +806,12 @@ func (a *AttestationData) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// Attestation is a field of Beacon Block Body.
|
||||
type Attestation struct {
|
||||
*eth.Attestation
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of Attestation.
|
||||
func (a *Attestation) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
AggregationBits hexutil.Bytes `json:"aggregation_bits"`
|
||||
@@ -764,10 +824,12 @@ func (a *Attestation) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// DepositData is a field of Deposit.
|
||||
type DepositData struct {
|
||||
*eth.Deposit_Data
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of DepositData.
|
||||
func (d *DepositData) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
PublicKey hexutil.Bytes `json:"pubkey"`
|
||||
@@ -782,10 +844,12 @@ func (d *DepositData) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// Deposit is a field of Beacon Block Body.
|
||||
type Deposit struct {
|
||||
*eth.Deposit
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of Deposit.
|
||||
func (d *Deposit) MarshalJSON() ([]byte, error) {
|
||||
proof := make([]hexutil.Bytes, len(d.Proof))
|
||||
for i := range d.Proof {
|
||||
@@ -800,10 +864,12 @@ func (d *Deposit) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// SignedVoluntaryExit is a field of Beacon Block Body.
|
||||
type SignedVoluntaryExit struct {
|
||||
*eth.SignedVoluntaryExit
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of SignedVoluntaryExit.
|
||||
func (sve *SignedVoluntaryExit) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Message *VoluntaryExit `json:"message"`
|
||||
@@ -814,10 +880,12 @@ func (sve *SignedVoluntaryExit) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// VoluntaryExit is a field in SignedVoluntaryExit
|
||||
type VoluntaryExit struct {
|
||||
*eth.VoluntaryExit
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of VoluntaryExit
|
||||
func (ve *VoluntaryExit) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Epoch string `json:"epoch"`
|
||||
@@ -828,10 +896,12 @@ func (ve *VoluntaryExit) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// SyncAggregate is a field of Beacon Block Body.
|
||||
type SyncAggregate struct {
|
||||
*eth.SyncAggregate
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of SyncAggregate.
|
||||
func (s *SyncAggregate) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
SyncCommitteeBits hexutil.Bytes `json:"sync_committee_bits"`
|
||||
@@ -842,10 +912,12 @@ func (s *SyncAggregate) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// Eth1Data is a field of Beacon Block Body.
|
||||
type Eth1Data struct {
|
||||
*eth.Eth1Data
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of Eth1Data.
|
||||
func (e *Eth1Data) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
DepositRoot hexutil.Bytes `json:"deposit_root"`
|
||||
@@ -858,6 +930,7 @@ func (e *Eth1Data) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of BlindedBeaconBlockBodyBellatrix.
|
||||
func (b *BlindedBeaconBlockBodyBellatrix) MarshalJSON() ([]byte, error) {
|
||||
sve := make([]*SignedVoluntaryExit, len(b.BlindedBeaconBlockBodyBellatrix.VoluntaryExits))
|
||||
for i := range b.BlindedBeaconBlockBodyBellatrix.VoluntaryExits {
|
||||
@@ -904,10 +977,12 @@ func (b *BlindedBeaconBlockBodyBellatrix) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// SignedBLSToExecutionChange is a field in Beacon Block Body for capella and above.
|
||||
type SignedBLSToExecutionChange struct {
|
||||
*eth.SignedBLSToExecutionChange
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of SignedBLSToExecutionChange.
|
||||
func (ch *SignedBLSToExecutionChange) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Message *BLSToExecutionChange `json:"message"`
|
||||
@@ -918,10 +993,12 @@ func (ch *SignedBLSToExecutionChange) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// BLSToExecutionChange is a field in SignedBLSToExecutionChange.
|
||||
type BLSToExecutionChange struct {
|
||||
*eth.BLSToExecutionChange
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of BLSToExecutionChange.
|
||||
func (ch *BLSToExecutionChange) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
ValidatorIndex string `json:"validator_index"`
|
||||
@@ -934,18 +1011,22 @@ func (ch *BLSToExecutionChange) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// SignedBlindedBeaconBlockCapella is part of the request object sent to builder API /eth/v1/builder/blinded_blocks for Capella.
|
||||
type SignedBlindedBeaconBlockCapella struct {
|
||||
*eth.SignedBlindedBeaconBlockCapella
|
||||
}
|
||||
|
||||
// BlindedBeaconBlockCapella is a field in SignedBlindedBeaconBlockCapella.
|
||||
type BlindedBeaconBlockCapella struct {
|
||||
*eth.BlindedBeaconBlockCapella
|
||||
}
|
||||
|
||||
// BlindedBeaconBlockBodyCapella is a field in BlindedBeaconBlockCapella.
|
||||
type BlindedBeaconBlockBodyCapella struct {
|
||||
*eth.BlindedBeaconBlockBodyCapella
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of SignedBlindedBeaconBlockCapella.
|
||||
func (b *SignedBlindedBeaconBlockCapella) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Message *BlindedBeaconBlockCapella `json:"message"`
|
||||
@@ -956,6 +1037,7 @@ func (b *SignedBlindedBeaconBlockCapella) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of BlindedBeaconBlockCapella
|
||||
func (b *BlindedBeaconBlockCapella) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Slot string `json:"slot"`
|
||||
@@ -972,6 +1054,7 @@ func (b *BlindedBeaconBlockCapella) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte array representation of BlindedBeaconBlockBodyCapella
|
||||
func (b *BlindedBeaconBlockBodyCapella) MarshalJSON() ([]byte, error) {
|
||||
sve := make([]*SignedVoluntaryExit, len(b.VoluntaryExits))
|
||||
for i := range b.VoluntaryExits {
|
||||
@@ -1024,6 +1107,7 @@ func (b *BlindedBeaconBlockBodyCapella) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// ErrorMessage is a JSON representation of the builder API's returned error message.
|
||||
type ErrorMessage struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
|
||||
@@ -1156,6 +1156,14 @@ func TestUint256Unmarshal(t *testing.T) {
|
||||
require.Equal(t, expected, string(m))
|
||||
}
|
||||
|
||||
func TestUint256Unmarshal_BadData(t *testing.T) {
|
||||
var bigNum Uint256
|
||||
|
||||
assert.ErrorContains(t, "provided Uint256 json string is too short", bigNum.UnmarshalJSON([]byte{'"'}))
|
||||
assert.ErrorContains(t, "provided Uint256 json string is malformed", bigNum.UnmarshalJSON([]byte{'"', '1', '2'}))
|
||||
|
||||
}
|
||||
|
||||
func TestUint256UnmarshalNegative(t *testing.T) {
|
||||
m := "-1"
|
||||
var value Uint256
|
||||
|
||||
@@ -340,7 +340,13 @@ func (s *Service) IsOptimistic(_ context.Context) (bool, error) {
|
||||
}
|
||||
s.headLock.RLock()
|
||||
headRoot := s.head.root
|
||||
headSlot := s.head.slot
|
||||
headOptimistic := s.head.optimistic
|
||||
s.headLock.RUnlock()
|
||||
// we trust the head package for recent head slots, otherwise fallback to forkchoice
|
||||
if headSlot+2 >= s.CurrentSlot() {
|
||||
return headOptimistic, nil
|
||||
}
|
||||
|
||||
s.cfg.ForkChoiceStore.RLock()
|
||||
defer s.cfg.ForkChoiceStore.RUnlock()
|
||||
|
||||
@@ -422,6 +422,12 @@ func TestService_IsOptimistic(t *testing.T) {
|
||||
|
||||
opt, err := c.IsOptimistic(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, primitives.Slot(0), c.CurrentSlot())
|
||||
require.Equal(t, false, opt)
|
||||
|
||||
c.SetGenesisTime(time.Now().Add(-time.Second * time.Duration(4*params.BeaconConfig().SecondsPerSlot)))
|
||||
opt, err = c.IsOptimistic(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, opt)
|
||||
}
|
||||
|
||||
|
||||
@@ -41,13 +41,15 @@ var (
|
||||
type invalidBlock struct {
|
||||
invalidAncestorRoots [][32]byte
|
||||
error
|
||||
root [32]byte
|
||||
root [32]byte
|
||||
lastValidHash [32]byte
|
||||
}
|
||||
|
||||
type invalidBlockError interface {
|
||||
Error() string
|
||||
InvalidAncestorRoots() [][32]byte
|
||||
BlockRoot() [32]byte
|
||||
LastValidHash() [32]byte
|
||||
}
|
||||
|
||||
// BlockRoot returns the invalid block root.
|
||||
@@ -55,6 +57,11 @@ func (e invalidBlock) BlockRoot() [32]byte {
|
||||
return e.root
|
||||
}
|
||||
|
||||
// LastValidHash returns the last valid hash root.
|
||||
func (e invalidBlock) LastValidHash() [32]byte {
|
||||
return e.lastValidHash
|
||||
}
|
||||
|
||||
// InvalidAncestorRoots returns an optional list of invalid roots of the invalid block which leads up last valid root.
|
||||
func (e invalidBlock) InvalidAncestorRoots() [][32]byte {
|
||||
return e.invalidAncestorRoots
|
||||
@@ -72,6 +79,19 @@ func IsInvalidBlock(e error) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// InvalidBlockLVH returns the invalid block last valid hash root. If the error
|
||||
// doesn't have a last valid hash, [32]byte{} is returned.
|
||||
func InvalidBlockLVH(e error) [32]byte {
|
||||
if e == nil {
|
||||
return [32]byte{}
|
||||
}
|
||||
d, ok := e.(invalidBlockError)
|
||||
if !ok {
|
||||
return [32]byte{}
|
||||
}
|
||||
return d.LastValidHash()
|
||||
}
|
||||
|
||||
// InvalidBlockRoot returns the invalid block root. If the error
|
||||
// doesn't have an invalid blockroot. [32]byte{} is returned.
|
||||
func InvalidBlockRoot(e error) [32]byte {
|
||||
|
||||
@@ -71,7 +71,6 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
|
||||
|
||||
nextSlot := s.CurrentSlot() + 1 // Cache payload ID for next slot proposer.
|
||||
hasAttr, attr, proposerId := s.getPayloadAttribute(ctx, arg.headState, nextSlot, arg.headRoot[:])
|
||||
|
||||
payloadID, lastValidHash, err := s.cfg.ExecutionEngineCaller.ForkchoiceUpdated(ctx, fcs, attr)
|
||||
if err != nil {
|
||||
switch err {
|
||||
@@ -154,7 +153,7 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
|
||||
var pId [8]byte
|
||||
copy(pId[:], payloadID[:])
|
||||
s.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(nextSlot, proposerId, pId, arg.headRoot)
|
||||
} else if hasAttr && payloadID == nil {
|
||||
} else if hasAttr && payloadID == nil && !features.Get().PrepareAllPayloads {
|
||||
log.WithFields(logrus.Fields{
|
||||
"blockHash": fmt.Sprintf("%#x", headPayload.BlockHash()),
|
||||
"slot": headBlk.Slot(),
|
||||
@@ -220,35 +219,37 @@ func (s *Service) notifyNewPayload(ctx context.Context, postStateVersion int,
|
||||
}).Info("Called new payload with optimistic block")
|
||||
return false, nil
|
||||
case execution.ErrInvalidPayloadStatus:
|
||||
newPayloadInvalidNodeCount.Inc()
|
||||
root, err := blk.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
invalidRoots, err := s.cfg.ForkChoiceStore.SetOptimisticToInvalid(ctx, root, blk.Block().ParentRoot(), bytesutil.ToBytes32(lastValidHash))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := s.removeInvalidBlockAndState(ctx, invalidRoots); err != nil {
|
||||
return false, err
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"slot": blk.Block().Slot(),
|
||||
"blockRoot": fmt.Sprintf("%#x", root),
|
||||
"invalidChildrenCount": len(invalidRoots),
|
||||
}).Warn("Pruned invalid blocks")
|
||||
lvh := bytesutil.ToBytes32(lastValidHash)
|
||||
return false, invalidBlock{
|
||||
invalidAncestorRoots: invalidRoots,
|
||||
error: ErrInvalidPayload,
|
||||
error: ErrInvalidPayload,
|
||||
lastValidHash: lvh,
|
||||
}
|
||||
case execution.ErrInvalidBlockHashPayloadStatus:
|
||||
newPayloadInvalidNodeCount.Inc()
|
||||
return false, ErrInvalidBlockHashPayloadStatus
|
||||
default:
|
||||
return false, errors.WithMessage(ErrUndefinedExecutionEngineError, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// reportInvalidBlock deals with the event that an invalid block was detected by the execution layer
|
||||
func (s *Service) reportInvalidBlock(ctx context.Context, root, parentRoot, lvh [32]byte) error {
|
||||
newPayloadInvalidNodeCount.Inc()
|
||||
invalidRoots, err := s.cfg.ForkChoiceStore.SetOptimisticToInvalid(ctx, root, parentRoot, lvh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.removeInvalidBlockAndState(ctx, invalidRoots); err != nil {
|
||||
return err
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"blockRoot": fmt.Sprintf("%#x", root),
|
||||
"invalidChildrenCount": len(invalidRoots),
|
||||
}).Warn("Pruned invalid blocks")
|
||||
return invalidBlock{
|
||||
invalidAncestorRoots: invalidRoots,
|
||||
error: ErrInvalidPayload,
|
||||
lastValidHash: lvh,
|
||||
}
|
||||
}
|
||||
|
||||
// getPayloadAttributes returns the payload attributes for the given state and slot.
|
||||
// The attribute is required to initiate a payload build process in the context of an `engine_forkchoiceUpdated` call.
|
||||
func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState, slot primitives.Slot, headRoot []byte) (bool, payloadattribute.Attributer, primitives.ValidatorIndex) {
|
||||
|
||||
@@ -743,6 +743,37 @@ func Test_NotifyNewPayload_SetOptimisticToValid(t *testing.T) {
|
||||
require.Equal(t, true, validated)
|
||||
}
|
||||
|
||||
func Test_reportInvalidBlock(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
params.OverrideBeaconConfig(params.MainnetConfig())
|
||||
service, tr := minimalTestService(t)
|
||||
ctx, _, fcs := tr.ctx, tr.db, tr.fcs
|
||||
jcp := ðpb.Checkpoint{}
|
||||
st, root, err := prepareForkchoiceState(ctx, 0, [32]byte{'A'}, [32]byte{}, [32]byte{'a'}, jcp, jcp)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
||||
st, root, err = prepareForkchoiceState(ctx, 1, [32]byte{'B'}, [32]byte{'A'}, [32]byte{'b'}, jcp, jcp)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
||||
st, root, err = prepareForkchoiceState(ctx, 2, [32]byte{'C'}, [32]byte{'B'}, [32]byte{'c'}, jcp, jcp)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
||||
|
||||
st, root, err = prepareForkchoiceState(ctx, 3, [32]byte{'D'}, [32]byte{'C'}, [32]byte{'d'}, jcp, jcp)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
||||
|
||||
require.NoError(t, fcs.SetOptimisticToValid(ctx, [32]byte{'A'}))
|
||||
err = service.reportInvalidBlock(ctx, [32]byte{'D'}, [32]byte{'C'}, [32]byte{'a'})
|
||||
require.Equal(t, IsInvalidBlock(err), true)
|
||||
require.Equal(t, InvalidBlockLVH(err), [32]byte{'a'})
|
||||
invalidRoots := InvalidAncestorRoots(err)
|
||||
require.Equal(t, 3, len(invalidRoots))
|
||||
require.Equal(t, [32]byte{'D'}, invalidRoots[0])
|
||||
require.Equal(t, [32]byte{'C'}, invalidRoots[1])
|
||||
require.Equal(t, [32]byte{'B'}, invalidRoots[2])
|
||||
}
|
||||
|
||||
func Test_GetPayloadAttribute(t *testing.T) {
|
||||
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
|
||||
ctx := tr.ctx
|
||||
|
||||
@@ -47,9 +47,11 @@ func (s *Service) UpdateAndSaveHeadWithBalances(ctx context.Context) error {
|
||||
|
||||
// This defines the current chain service's view of head.
|
||||
type head struct {
|
||||
root [32]byte // current head root.
|
||||
block interfaces.ReadOnlySignedBeaconBlock // current head block.
|
||||
state state.BeaconState // current head state.
|
||||
root [32]byte // current head root.
|
||||
block interfaces.ReadOnlySignedBeaconBlock // current head block.
|
||||
state state.BeaconState // current head state.
|
||||
slot primitives.Slot // the head block slot number
|
||||
optimistic bool // optimistic status when saved head
|
||||
}
|
||||
|
||||
// This saves head info to the local service cache, it also saves the
|
||||
@@ -94,6 +96,10 @@ func (s *Service) saveHead(ctx context.Context, newHeadRoot [32]byte, headBlock
|
||||
return errors.Wrap(err, "could not get old head root")
|
||||
}
|
||||
oldHeadRoot := bytesutil.ToBytes32(r)
|
||||
isOptimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(newHeadRoot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not check if node is optimistically synced")
|
||||
}
|
||||
if headBlock.Block().ParentRoot() != oldHeadRoot {
|
||||
// A chain re-org occurred, so we fire an event notifying the rest of the services.
|
||||
commonRoot, forkSlot, err := s.cfg.ForkChoiceStore.CommonAncestor(ctx, oldHeadRoot, newHeadRoot)
|
||||
@@ -125,10 +131,6 @@ func (s *Service) saveHead(ctx context.Context, newHeadRoot [32]byte, headBlock
|
||||
reorgDistance.Observe(float64(dis))
|
||||
reorgDepth.Observe(float64(dep))
|
||||
|
||||
isOptimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(newHeadRoot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not check if node is optimistically synced")
|
||||
}
|
||||
s.cfg.StateNotifier.StateFeed().Send(&feed.Event{
|
||||
Type: statefeed.Reorg,
|
||||
Data: ðpbv1.EventChainReorg{
|
||||
@@ -150,7 +152,14 @@ func (s *Service) saveHead(ctx context.Context, newHeadRoot [32]byte, headBlock
|
||||
}
|
||||
|
||||
// Cache the new head info.
|
||||
if err := s.setHead(newHeadRoot, headBlock, headState); err != nil {
|
||||
newHead := &head{
|
||||
root: newHeadRoot,
|
||||
block: headBlock,
|
||||
state: headState,
|
||||
optimistic: isOptimistic,
|
||||
slot: headBlock.Block().Slot(),
|
||||
}
|
||||
if err := s.setHead(newHead); err != nil {
|
||||
return errors.Wrap(err, "could not set head")
|
||||
}
|
||||
|
||||
@@ -195,20 +204,22 @@ func (s *Service) saveHeadNoDB(ctx context.Context, b interfaces.ReadOnlySignedB
|
||||
return nil
|
||||
}
|
||||
|
||||
// This sets head view object which is used to track the head slot, root, block and state.
|
||||
func (s *Service) setHead(root [32]byte, block interfaces.ReadOnlySignedBeaconBlock, state state.BeaconState) error {
|
||||
// This sets head view object which is used to track the head slot, root, block, state and optimistic status
|
||||
func (s *Service) setHead(newHead *head) error {
|
||||
s.headLock.Lock()
|
||||
defer s.headLock.Unlock()
|
||||
|
||||
// This does a full copy of the block and state.
|
||||
bCp, err := block.Copy()
|
||||
bCp, err := newHead.block.Copy()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.head = &head{
|
||||
root: root,
|
||||
block: bCp,
|
||||
state: state.Copy(),
|
||||
root: newHead.root,
|
||||
block: bCp,
|
||||
state: newHead.state.Copy(),
|
||||
optimistic: newHead.optimistic,
|
||||
slot: newHead.slot,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -157,7 +157,11 @@ func (s *Service) getSyncCommitteeHeadState(ctx context.Context, slot primitives
|
||||
if headState == nil || headState.IsNil() {
|
||||
return nil, errors.New("nil state")
|
||||
}
|
||||
headState, err = transition.ProcessSlotsIfPossible(ctx, headState, slot)
|
||||
headRoot, err := s.HeadRoot(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headState, err = transition.ProcessSlotsUsingNextSlotCache(ctx, headState, headRoot, slot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/signing"
|
||||
dbTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
|
||||
func TestService_HeadSyncCommitteeIndices(t *testing.T) {
|
||||
s, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().TargetCommitteeSize)
|
||||
c := &Service{}
|
||||
c := &Service{cfg: &config{BeaconDB: dbTest.SetupDB(t)}}
|
||||
c.head = &head{state: s}
|
||||
|
||||
// Current period
|
||||
@@ -38,7 +39,7 @@ func TestService_HeadSyncCommitteeIndices(t *testing.T) {
|
||||
|
||||
func TestService_headCurrentSyncCommitteeIndices(t *testing.T) {
|
||||
s, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().TargetCommitteeSize)
|
||||
c := &Service{}
|
||||
c := &Service{cfg: &config{BeaconDB: dbTest.SetupDB(t)}}
|
||||
c.head = &head{state: s}
|
||||
|
||||
// Process slot up to `EpochsPerSyncCommitteePeriod` so it can `ProcessSyncCommitteeUpdates`.
|
||||
@@ -66,7 +67,7 @@ func TestService_headNextSyncCommitteeIndices(t *testing.T) {
|
||||
|
||||
func TestService_HeadSyncCommitteePubKeys(t *testing.T) {
|
||||
s, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().TargetCommitteeSize)
|
||||
c := &Service{}
|
||||
c := &Service{cfg: &config{BeaconDB: dbTest.SetupDB(t)}}
|
||||
c.head = &head{state: s}
|
||||
|
||||
// Process slot up to 2 * `EpochsPerSyncCommitteePeriod` so it can run `ProcessSyncCommitteeUpdates` twice.
|
||||
@@ -81,7 +82,7 @@ func TestService_HeadSyncCommitteePubKeys(t *testing.T) {
|
||||
|
||||
func TestService_HeadSyncCommitteeDomain(t *testing.T) {
|
||||
s, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().TargetCommitteeSize)
|
||||
c := &Service{}
|
||||
c := &Service{cfg: &config{BeaconDB: dbTest.SetupDB(t)}}
|
||||
c.head = &head{state: s}
|
||||
|
||||
wanted, err := signing.Domain(s.Fork(), slots.ToEpoch(s.Slot()), params.BeaconConfig().DomainSyncCommittee, s.GenesisValidatorsRoot())
|
||||
|
||||
@@ -120,7 +120,7 @@ func logPayload(block interfaces.ReadOnlyBeaconBlock) error {
|
||||
fields := logrus.Fields{
|
||||
"blockHash": fmt.Sprintf("%#x", bytesutil.Trunc(payload.BlockHash())),
|
||||
"parentHash": fmt.Sprintf("%#x", bytesutil.Trunc(payload.ParentHash())),
|
||||
"blockNumber": payload.BlockNumber,
|
||||
"blockNumber": payload.BlockNumber(),
|
||||
"gasUtilized": fmt.Sprintf("%.2f", gasUtilized),
|
||||
}
|
||||
if block.Version() >= version.Capella {
|
||||
|
||||
@@ -172,3 +172,10 @@ func WithClockSynchronizer(gs *startup.ClockSynchronizer) Option {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithSyncComplete(c chan struct{}) Option {
|
||||
return func(s *Service) error {
|
||||
s.syncComplete = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,8 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.ReadOnlySignedB
|
||||
}
|
||||
|
||||
// Verify that the parent block is in forkchoice
|
||||
if !s.cfg.ForkChoiceStore.HasNode(b.ParentRoot()) {
|
||||
parentRoot := b.ParentRoot()
|
||||
if !s.cfg.ForkChoiceStore.HasNode(parentRoot) {
|
||||
return ErrNotDescendantOfFinalized
|
||||
}
|
||||
|
||||
@@ -134,9 +135,12 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.ReadOnlySignedB
|
||||
}
|
||||
isValidPayload, err := s.notifyNewPayload(ctx, postStateVersion, postStateHeader, signed)
|
||||
if err != nil {
|
||||
if IsInvalidBlock(err) && InvalidBlockLVH(err) != [32]byte{} {
|
||||
return s.reportInvalidBlock(ctx, blockRoot, parentRoot, InvalidBlockLVH(err))
|
||||
}
|
||||
return errors.Wrap(err, "could not validate new payload")
|
||||
}
|
||||
if isValidPayload {
|
||||
if signed.Version() < version.Capella && isValidPayload {
|
||||
if err := s.validateMergeTransitionBlock(ctx, preStateVersion, preStateHeader, signed); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -145,8 +149,7 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.ReadOnlySignedB
|
||||
if err := s.savePostStateInfo(ctx, blockRoot, signed, postState); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.insertBlockToForkchoiceStore(ctx, signed.Block(), blockRoot, postState); err != nil {
|
||||
if err := s.cfg.ForkChoiceStore.InsertNode(ctx, postState, blockRoot); err != nil {
|
||||
return errors.Wrapf(err, "could not insert block %d to fork choice store", signed.Block().Slot())
|
||||
}
|
||||
if err := s.handleBlockAttestations(ctx, signed.Block(), postState); err != nil {
|
||||
@@ -186,7 +189,6 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.ReadOnlySignedB
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
justified := s.cfg.ForkChoiceStore.JustifiedCheckpoint()
|
||||
start := time.Now()
|
||||
headRoot, err := s.cfg.ForkChoiceStore.Head(ctx)
|
||||
@@ -257,10 +259,6 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.ReadOnlySignedB
|
||||
if err := s.updateFinalized(ctx, ðpb.Checkpoint{Epoch: finalized.Epoch, Root: finalized.Root[:]}); err != nil {
|
||||
return err
|
||||
}
|
||||
isOptimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(finalized.Root)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not check if node is optimistically synced")
|
||||
}
|
||||
go func() {
|
||||
// Send an event regarding the new finalized checkpoint over a common event feed.
|
||||
stateRoot := signed.Block().StateRoot()
|
||||
@@ -270,7 +268,7 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.ReadOnlySignedB
|
||||
Epoch: postState.FinalizedCheckpoint().Epoch,
|
||||
Block: postState.FinalizedCheckpoint().Root,
|
||||
State: stateRoot[:],
|
||||
ExecutionOptimistic: isOptimistic,
|
||||
ExecutionOptimistic: isValidPayload,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -285,7 +283,7 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.ReadOnlySignedB
|
||||
}()
|
||||
}
|
||||
defer reportAttestationInclusion(b)
|
||||
if err := s.handleEpochBoundary(ctx, postState); err != nil {
|
||||
if err := s.handleEpochBoundary(ctx, postState, blockRoot[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
onBlockProcessingTime.Observe(float64(time.Since(startTime).Milliseconds()))
|
||||
@@ -483,14 +481,14 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.ReadOnlySi
|
||||
}
|
||||
|
||||
// Epoch boundary bookkeeping such as logging epoch summaries.
|
||||
func (s *Service) handleEpochBoundary(ctx context.Context, postState state.BeaconState) error {
|
||||
func (s *Service) handleEpochBoundary(ctx context.Context, postState state.BeaconState, blockRoot []byte) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.handleEpochBoundary")
|
||||
defer span.End()
|
||||
|
||||
var err error
|
||||
if postState.Slot()+1 == s.nextEpochBoundarySlot {
|
||||
copied := postState.Copy()
|
||||
copied, err := transition.ProcessSlots(ctx, copied, copied.Slot()+1)
|
||||
copied, err := transition.ProcessSlotsUsingNextSlotCache(ctx, copied, blockRoot, copied.Slot()+1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -498,10 +496,32 @@ func (s *Service) handleEpochBoundary(ctx context.Context, postState state.Beaco
|
||||
if err := helpers.UpdateCommitteeCache(ctx, copied, coreTime.CurrentEpoch(copied)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := helpers.UpdateProposerIndicesInCache(ctx, copied); err != nil {
|
||||
e := coreTime.CurrentEpoch(copied)
|
||||
if err := helpers.UpdateProposerIndicesInCache(ctx, copied, e); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
// Use a custom deadline here, since this method runs asynchronously.
|
||||
// We ignore the parent method's context and instead create a new one
|
||||
// with a custom deadline, therefore using the background context instead.
|
||||
slotCtx, cancel := context.WithTimeout(context.Background(), slotDeadline)
|
||||
defer cancel()
|
||||
if err := helpers.UpdateProposerIndicesInCache(slotCtx, copied, e+1); err != nil {
|
||||
log.WithError(err).Warn("Failed to cache next epoch proposers")
|
||||
}
|
||||
}()
|
||||
} else if postState.Slot() >= s.nextEpochBoundarySlot {
|
||||
postState = postState.Copy()
|
||||
if s.nextEpochBoundarySlot != 0 {
|
||||
ep := slots.ToEpoch(s.nextEpochBoundarySlot)
|
||||
_, nextProposerIndexToSlots, err := helpers.CommitteeAssignments(ctx, postState, ep)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range nextProposerIndexToSlots {
|
||||
s.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(v[0], k, [8]byte{}, [32]byte{})
|
||||
}
|
||||
}
|
||||
s.nextEpochBoundarySlot, err = slots.EpochStart(coreTime.NextEpoch(postState))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -512,7 +532,7 @@ func (s *Service) handleEpochBoundary(ctx context.Context, postState state.Beaco
|
||||
if err := helpers.UpdateCommitteeCache(ctx, postState, coreTime.CurrentEpoch(postState)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := helpers.UpdateProposerIndicesInCache(ctx, postState); err != nil {
|
||||
if err := helpers.UpdateProposerIndicesInCache(ctx, postState, coreTime.CurrentEpoch(postState)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -524,27 +544,9 @@ func (s *Service) handleEpochBoundary(ctx context.Context, postState state.Beaco
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// This feeds in the block to fork choice store. It's allows fork choice store
|
||||
// to gain information on the most current chain.
|
||||
func (s *Service) insertBlockToForkchoiceStore(ctx context.Context, blk interfaces.ReadOnlyBeaconBlock, root [32]byte, st state.BeaconState) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.insertBlockToForkchoiceStore")
|
||||
defer span.End()
|
||||
|
||||
if !s.cfg.ForkChoiceStore.HasNode(blk.ParentRoot()) {
|
||||
fCheckpoint := st.FinalizedCheckpoint()
|
||||
jCheckpoint := st.CurrentJustifiedCheckpoint()
|
||||
if err := s.fillInForkChoiceMissingBlocks(ctx, blk, fCheckpoint, jCheckpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return s.cfg.ForkChoiceStore.InsertNode(ctx, st, root)
|
||||
}
|
||||
|
||||
// This feeds in the attestations included in the block to fork choice store. It's allows fork choice store
|
||||
// to gain information on the most current chain.
|
||||
func (s *Service) handleBlockAttestations(ctx context.Context, blk interfaces.ReadOnlyBeaconBlock, st state.BeaconState) error {
|
||||
@@ -652,18 +654,17 @@ func (s *Service) validateMergeTransitionBlock(ctx context.Context, stateVersion
|
||||
// This routine checks if there is a cached proposer payload ID available for the next slot proposer.
|
||||
// If there is not, it will call forkchoice updated with the correct payload attribute then cache the payload ID.
|
||||
func (s *Service) runLateBlockTasks() {
|
||||
_, err := s.clockWaiter.WaitForClock(s.ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("runLateBlockTasks encountered an error waiting for initialization")
|
||||
if err := s.waitForSync(); err != nil {
|
||||
log.WithError(err).Error("failed to wait for initial sync")
|
||||
return
|
||||
}
|
||||
|
||||
attThreshold := params.BeaconConfig().SecondsPerSlot / 3
|
||||
ticker := slots.NewSlotTickerWithOffset(s.genesisTime, time.Duration(attThreshold)*time.Second, params.BeaconConfig().SecondsPerSlot)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C():
|
||||
s.lateBlockTasks(s.ctx)
|
||||
|
||||
case <-s.ctx.Done():
|
||||
log.Debug("Context closed, exiting routine")
|
||||
return
|
||||
@@ -720,3 +721,13 @@ func (s *Service) lateBlockTasks(ctx context.Context) {
|
||||
log.WithError(err).Debug("could not perform late block tasks: failed to update forkchoice with engine")
|
||||
}
|
||||
}
|
||||
|
||||
// waitForSync blocks until the node is synced to the head.
|
||||
func (s *Service) waitForSync() error {
|
||||
select {
|
||||
case <-s.syncComplete:
|
||||
return nil
|
||||
case <-s.ctx.Done():
|
||||
return errors.New("context closed, exiting goroutine")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -636,7 +636,7 @@ func TestHandleEpochBoundary_UpdateFirstSlot(t *testing.T) {
|
||||
s, _ := util.DeterministicGenesisState(t, 1024)
|
||||
service.head = &head{state: s}
|
||||
require.NoError(t, s.SetSlot(2*params.BeaconConfig().SlotsPerEpoch))
|
||||
require.NoError(t, service.handleEpochBoundary(ctx, s))
|
||||
require.NoError(t, service.handleEpochBoundary(ctx, s, []byte{}))
|
||||
require.Equal(t, 3*params.BeaconConfig().SlotsPerEpoch, service.nextEpochBoundarySlot)
|
||||
}
|
||||
|
||||
|
||||
@@ -94,8 +94,10 @@ func (s *Service) spawnProcessAttestationsRoutine() {
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
case <-pat.C():
|
||||
log.Infof("proposer_mocker: calling updated head via offset ticker")
|
||||
s.UpdateHead(s.ctx, s.CurrentSlot()+1)
|
||||
case <-st.C():
|
||||
log.Infof("proposer_mocker: calling updated head via normal slot ticker in spawn atts")
|
||||
s.cfg.ForkChoiceStore.Lock()
|
||||
if err := s.cfg.ForkChoiceStore.NewSlot(s.ctx, s.CurrentSlot()); err != nil {
|
||||
log.WithError(err).Error("could not process new slot")
|
||||
@@ -124,6 +126,8 @@ func (s *Service) UpdateHead(ctx context.Context, proposingSlot primitives.Slot)
|
||||
}
|
||||
s.processAttestations(ctx, disparity)
|
||||
|
||||
log.Infof("proposer_mocker: process attestations in fc took %s", time.Since(start).String())
|
||||
|
||||
processAttsElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
|
||||
|
||||
start = time.Now()
|
||||
@@ -136,11 +140,14 @@ func (s *Service) UpdateHead(ctx context.Context, proposingSlot primitives.Slot)
|
||||
s.headLock.RUnlock()
|
||||
}
|
||||
newAttHeadElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
|
||||
log.Infof("proposer_mocker: head root in fc took %s", time.Since(start).String())
|
||||
|
||||
changed, err := s.forkchoiceUpdateWithExecution(s.ctx, newHeadRoot, proposingSlot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not update forkchoice")
|
||||
}
|
||||
log.Infof("proposer_mocker: fcu call in fc took %s", time.Since(start).String())
|
||||
|
||||
if changed {
|
||||
s.headLock.RLock()
|
||||
log.WithFields(logrus.Fields{
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
|
||||
statefeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/state"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
||||
coreTime "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/time"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/execution"
|
||||
@@ -60,6 +61,7 @@ type Service struct {
|
||||
wsVerifier *WeakSubjectivityVerifier
|
||||
clockSetter startup.ClockSetter
|
||||
clockWaiter startup.ClockWaiter
|
||||
syncComplete chan struct{}
|
||||
}
|
||||
|
||||
// config options for the service.
|
||||
@@ -307,7 +309,13 @@ func (s *Service) initializeHeadFromDB(ctx context.Context) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized block")
|
||||
}
|
||||
if err := s.setHead(finalizedRoot, finalizedBlock, finalizedState); err != nil {
|
||||
if err := s.setHead(&head{
|
||||
finalizedRoot,
|
||||
finalizedBlock,
|
||||
finalizedState,
|
||||
finalizedBlock.Block().Slot(),
|
||||
false,
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "could not set head")
|
||||
}
|
||||
|
||||
@@ -401,7 +409,7 @@ func (s *Service) initializeBeaconChain(
|
||||
if err := helpers.UpdateCommitteeCache(ctx, genesisState, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := helpers.UpdateProposerIndicesInCache(ctx, genesisState); err != nil {
|
||||
if err := helpers.UpdateProposerIndicesInCache(ctx, genesisState, coreTime.CurrentEpoch(genesisState)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -439,7 +447,13 @@ func (s *Service) saveGenesisData(ctx context.Context, genesisState state.Beacon
|
||||
}
|
||||
s.cfg.ForkChoiceStore.SetGenesisTime(uint64(s.genesisTime.Unix()))
|
||||
|
||||
if err := s.setHead(genesisBlkRoot, genesisBlk, genesisState); err != nil {
|
||||
if err := s.setHead(&head{
|
||||
genesisBlkRoot,
|
||||
genesisBlk,
|
||||
genesisState,
|
||||
genesisBlk.Block().Slot(),
|
||||
false,
|
||||
}); err != nil {
|
||||
log.WithError(err).Fatal("Could not set head")
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -377,9 +377,7 @@ func TestHasBlock_ForkChoiceAndDB_DoublyLinkedTree(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
beaconState, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
wsb, err := consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.insertBlockToForkchoiceStore(ctx, wsb.Block(), r, beaconState))
|
||||
require.NoError(t, s.cfg.ForkChoiceStore.InsertNode(ctx, beaconState, r))
|
||||
|
||||
assert.Equal(t, false, s.hasBlock(ctx, [32]byte{}), "Should not have block")
|
||||
assert.Equal(t, true, s.hasBlock(ctx, r), "Should have block")
|
||||
@@ -453,9 +451,7 @@ func BenchmarkHasBlockForkChoiceStore_DoublyLinkedTree(b *testing.B) {
|
||||
bs := ðpb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)}, CurrentJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)}}
|
||||
beaconState, err := state_native.InitializeFromProtoPhase0(bs)
|
||||
require.NoError(b, err)
|
||||
wsb, err := consensusblocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(b, err)
|
||||
require.NoError(b, s.insertBlockToForkchoiceStore(ctx, wsb.Block(), r, beaconState))
|
||||
require.NoError(b, s.cfg.ForkChoiceStore.InsertNode(ctx, beaconState, r))
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
||||
@@ -13,6 +13,15 @@ import (
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// AttDelta contains rewards and penalties for a single attestation.
|
||||
type AttDelta struct {
|
||||
HeadReward uint64
|
||||
SourceReward uint64
|
||||
SourcePenalty uint64
|
||||
TargetReward uint64
|
||||
TargetPenalty uint64
|
||||
}
|
||||
|
||||
// InitializePrecomputeValidators precomputes individual validator for its attested balances and the total sum of validators attested balances of the epoch.
|
||||
func InitializePrecomputeValidators(ctx context.Context, beaconState state.BeaconState) ([]*precompute.Validator, *precompute.Balance, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "altair.InitializePrecomputeValidators")
|
||||
@@ -226,7 +235,7 @@ func ProcessRewardsAndPenaltiesPrecompute(
|
||||
return beaconState, errors.New("validator registries not the same length as state's validator registries")
|
||||
}
|
||||
|
||||
attsRewards, attsPenalties, err := AttestationsDelta(beaconState, bal, vals)
|
||||
attDeltas, err := AttestationsDelta(beaconState, bal, vals)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get attestation delta")
|
||||
}
|
||||
@@ -237,11 +246,12 @@ func ProcessRewardsAndPenaltiesPrecompute(
|
||||
|
||||
// Compute the post balance of the validator after accounting for the
|
||||
// attester and proposer rewards and penalties.
|
||||
balances[i], err = helpers.IncreaseBalanceWithVal(balances[i], attsRewards[i])
|
||||
delta := attDeltas[i]
|
||||
balances[i], err = helpers.IncreaseBalanceWithVal(balances[i], delta.HeadReward+delta.SourceReward+delta.TargetReward)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
balances[i] = helpers.DecreaseBalanceWithVal(balances[i], attsPenalties[i])
|
||||
balances[i] = helpers.DecreaseBalanceWithVal(balances[i], delta.SourcePenalty+delta.TargetPenalty)
|
||||
|
||||
vals[i].AfterEpochTransitionBalance = balances[i]
|
||||
}
|
||||
@@ -255,10 +265,8 @@ func ProcessRewardsAndPenaltiesPrecompute(
|
||||
|
||||
// AttestationsDelta computes and returns the rewards and penalties differences for individual validators based on the
|
||||
// voting records.
|
||||
func AttestationsDelta(beaconState state.BeaconState, bal *precompute.Balance, vals []*precompute.Validator) (rewards, penalties []uint64, err error) {
|
||||
numOfVals := beaconState.NumValidators()
|
||||
rewards = make([]uint64, numOfVals)
|
||||
penalties = make([]uint64, numOfVals)
|
||||
func AttestationsDelta(beaconState state.BeaconState, bal *precompute.Balance, vals []*precompute.Validator) ([]*AttDelta, error) {
|
||||
attDeltas := make([]*AttDelta, len(vals))
|
||||
|
||||
cfg := params.BeaconConfig()
|
||||
prevEpoch := time.PrevEpoch(beaconState)
|
||||
@@ -272,29 +280,29 @@ func AttestationsDelta(beaconState state.BeaconState, bal *precompute.Balance, v
|
||||
bias := cfg.InactivityScoreBias
|
||||
inactivityPenaltyQuotient, err := beaconState.InactivityPenaltyQuotient()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
inactivityDenominator := bias * inactivityPenaltyQuotient
|
||||
|
||||
for i, v := range vals {
|
||||
rewards[i], penalties[i], err = attestationDelta(bal, v, baseRewardMultiplier, inactivityDenominator, leak)
|
||||
attDeltas[i], err = attestationDelta(bal, v, baseRewardMultiplier, inactivityDenominator, leak)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return rewards, penalties, nil
|
||||
return attDeltas, nil
|
||||
}
|
||||
|
||||
func attestationDelta(
|
||||
bal *precompute.Balance,
|
||||
val *precompute.Validator,
|
||||
baseRewardMultiplier, inactivityDenominator uint64,
|
||||
inactivityLeak bool) (reward, penalty uint64, err error) {
|
||||
inactivityLeak bool) (*AttDelta, error) {
|
||||
eligible := val.IsActivePrevEpoch || (val.IsSlashed && !val.IsWithdrawableCurrentEpoch)
|
||||
// Per spec `ActiveCurrentEpoch` can't be 0 to process attestation delta.
|
||||
if !eligible || bal.ActiveCurrentEpoch == 0 {
|
||||
return 0, 0, nil
|
||||
return &AttDelta{}, nil
|
||||
}
|
||||
|
||||
cfg := params.BeaconConfig()
|
||||
@@ -307,32 +315,32 @@ func attestationDelta(
|
||||
srcWeight := cfg.TimelySourceWeight
|
||||
tgtWeight := cfg.TimelyTargetWeight
|
||||
headWeight := cfg.TimelyHeadWeight
|
||||
reward, penalty = uint64(0), uint64(0)
|
||||
attDelta := &AttDelta{}
|
||||
// Process source reward / penalty
|
||||
if val.IsPrevEpochSourceAttester && !val.IsSlashed {
|
||||
if !inactivityLeak {
|
||||
n := baseReward * srcWeight * (bal.PrevEpochAttested / increment)
|
||||
reward += n / (activeIncrement * weightDenominator)
|
||||
attDelta.SourceReward += n / (activeIncrement * weightDenominator)
|
||||
}
|
||||
} else {
|
||||
penalty += baseReward * srcWeight / weightDenominator
|
||||
attDelta.SourcePenalty += baseReward * srcWeight / weightDenominator
|
||||
}
|
||||
|
||||
// Process target reward / penalty
|
||||
if val.IsPrevEpochTargetAttester && !val.IsSlashed {
|
||||
if !inactivityLeak {
|
||||
n := baseReward * tgtWeight * (bal.PrevEpochTargetAttested / increment)
|
||||
reward += n / (activeIncrement * weightDenominator)
|
||||
attDelta.TargetReward += n / (activeIncrement * weightDenominator)
|
||||
}
|
||||
} else {
|
||||
penalty += baseReward * tgtWeight / weightDenominator
|
||||
attDelta.TargetPenalty += baseReward * tgtWeight / weightDenominator
|
||||
}
|
||||
|
||||
// Process head reward / penalty
|
||||
if val.IsPrevEpochHeadAttester && !val.IsSlashed {
|
||||
if !inactivityLeak {
|
||||
n := baseReward * headWeight * (bal.PrevEpochHeadAttested / increment)
|
||||
reward += n / (activeIncrement * weightDenominator)
|
||||
attDelta.HeadReward += n / (activeIncrement * weightDenominator)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,10 +349,10 @@ func attestationDelta(
|
||||
if !val.IsPrevEpochTargetAttester || val.IsSlashed {
|
||||
n, err := math.Mul64(effectiveBalance, val.InactivityScore)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
return &AttDelta{}, err
|
||||
}
|
||||
penalty += n / inactivityDenominator
|
||||
attDelta.TargetPenalty += n / inactivityDenominator
|
||||
}
|
||||
|
||||
return reward, penalty, nil
|
||||
return attDelta, nil
|
||||
}
|
||||
|
||||
@@ -213,9 +213,16 @@ func TestAttestationsDelta(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
validators, balance, err = ProcessEpochParticipation(context.Background(), s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
rewards, penalties, err := AttestationsDelta(s, balance, validators)
|
||||
deltas, err := AttestationsDelta(s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
|
||||
rewards := make([]uint64, len(deltas))
|
||||
penalties := make([]uint64, len(deltas))
|
||||
for i, d := range deltas {
|
||||
rewards[i] = d.HeadReward + d.SourceReward + d.TargetReward
|
||||
penalties[i] = d.SourcePenalty + d.TargetPenalty
|
||||
}
|
||||
|
||||
// Reward amount should increase as validator index increases due to setup.
|
||||
for i := 1; i < len(rewards); i++ {
|
||||
require.Equal(t, true, rewards[i] > rewards[i-1])
|
||||
@@ -244,9 +251,16 @@ func TestAttestationsDeltaBellatrix(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
validators, balance, err = ProcessEpochParticipation(context.Background(), s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
rewards, penalties, err := AttestationsDelta(s, balance, validators)
|
||||
deltas, err := AttestationsDelta(s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
|
||||
rewards := make([]uint64, len(deltas))
|
||||
penalties := make([]uint64, len(deltas))
|
||||
for i, d := range deltas {
|
||||
rewards[i] = d.HeadReward + d.SourceReward + d.TargetReward
|
||||
penalties[i] = d.SourcePenalty + d.TargetPenalty
|
||||
}
|
||||
|
||||
// Reward amount should increase as validator index increases due to setup.
|
||||
for i := 1; i < len(rewards); i++ {
|
||||
require.Equal(t, true, rewards[i] > rewards[i-1])
|
||||
@@ -285,8 +299,15 @@ func TestProcessRewardsAndPenaltiesPrecompute_Ok(t *testing.T) {
|
||||
}
|
||||
|
||||
wanted := make([]uint64, s.NumValidators())
|
||||
rewards, penalties, err := AttestationsDelta(s, balance, validators)
|
||||
deltas, err := AttestationsDelta(s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
|
||||
rewards := make([]uint64, len(deltas))
|
||||
penalties := make([]uint64, len(deltas))
|
||||
for i, d := range deltas {
|
||||
rewards[i] = d.HeadReward + d.SourceReward + d.TargetReward
|
||||
penalties[i] = d.SourcePenalty + d.TargetPenalty
|
||||
}
|
||||
for i := range rewards {
|
||||
wanted[i] += rewards[i]
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package altair
|
||||
|
||||
import (
|
||||
"context"
|
||||
goErrors "errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -22,6 +23,10 @@ import (
|
||||
|
||||
const maxRandomByte = uint64(1<<8 - 1)
|
||||
|
||||
var (
|
||||
ErrTooLate = errors.New("sync message is too late")
|
||||
)
|
||||
|
||||
// ValidateNilSyncContribution validates the following fields are not nil:
|
||||
// -the contribution and proof itself
|
||||
// -the message within contribution and proof
|
||||
@@ -217,7 +222,7 @@ func ValidateSyncMessageTime(slot primitives.Slot, genesisTime time.Time, clockD
|
||||
upperBound := time.Now().Add(clockDisparity)
|
||||
// Verify sync message slot is within the time range.
|
||||
if messageTime.Before(lowerBound) || messageTime.After(upperBound) {
|
||||
return fmt.Errorf(
|
||||
syncErr := fmt.Errorf(
|
||||
"sync message time %v (slot %d) not within allowable range of %v (slot %d) to %v (slot %d)",
|
||||
messageTime,
|
||||
slot,
|
||||
@@ -226,6 +231,11 @@ func ValidateSyncMessageTime(slot primitives.Slot, genesisTime time.Time, clockD
|
||||
upperBound,
|
||||
uint64(upperBound.Unix()-genesisTime.Unix())/params.BeaconConfig().SecondsPerSlot,
|
||||
)
|
||||
// Wrap error message if sync message is too late.
|
||||
if messageTime.Before(lowerBound) {
|
||||
syncErr = goErrors.Join(ErrTooLate, syncErr)
|
||||
}
|
||||
return syncErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ go_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",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrTooLate = errors.New("attestation is too late")
|
||||
)
|
||||
|
||||
// ValidateNilAttestation checks if any composite field of input attestation is nil.
|
||||
// Access to these nil fields will result in run time panic,
|
||||
// it is recommended to run these checks as first line of defense.
|
||||
@@ -164,7 +168,7 @@ func ValidateAttestationTime(attSlot primitives.Slot, genesisTime time.Time, clo
|
||||
)
|
||||
if attTime.Before(lowerBounds) {
|
||||
attReceivedTooLateCount.Inc()
|
||||
return attError
|
||||
return errors.Join(ErrTooLate, attError)
|
||||
}
|
||||
if attTime.After(upperBounds) {
|
||||
attReceivedTooEarlyCount.Inc()
|
||||
|
||||
@@ -336,20 +336,21 @@ func UpdateCommitteeCache(ctx context.Context, state state.ReadOnlyBeaconState,
|
||||
}
|
||||
|
||||
// UpdateProposerIndicesInCache updates proposer indices entry of the committee cache.
|
||||
func UpdateProposerIndicesInCache(ctx context.Context, state state.ReadOnlyBeaconState) error {
|
||||
// Input state is used to retrieve active validator indices.
|
||||
// Input epoch is the epoch to retrieve proposer indices for.
|
||||
func UpdateProposerIndicesInCache(ctx context.Context, state state.ReadOnlyBeaconState, epoch primitives.Epoch) error {
|
||||
// The cache uses the state root at the (current epoch - 1)'s slot as key. (e.g. for epoch 2, the key is root at slot 63)
|
||||
// Which is the reason why we skip genesis epoch.
|
||||
if time.CurrentEpoch(state) <= params.BeaconConfig().GenesisEpoch+params.BeaconConfig().MinSeedLookahead {
|
||||
if epoch <= params.BeaconConfig().GenesisEpoch+params.BeaconConfig().MinSeedLookahead {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Use state root from (current_epoch - 1))
|
||||
wantedEpoch := time.PrevEpoch(state)
|
||||
s, err := slots.EpochEnd(wantedEpoch)
|
||||
s, err := slots.EpochEnd(epoch - 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r, err := StateRootAtSlot(state, s)
|
||||
r, err := state.StateRootAtIndex(uint64(s % params.BeaconConfig().SlotsPerHistoricalRoot))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -366,11 +367,11 @@ func UpdateProposerIndicesInCache(ctx context.Context, state state.ReadOnlyBeaco
|
||||
return nil
|
||||
}
|
||||
|
||||
indices, err := ActiveValidatorIndices(ctx, state, time.CurrentEpoch(state))
|
||||
indices, err := ActiveValidatorIndices(ctx, state, epoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proposerIndices, err := precomputeProposerIndices(state, indices)
|
||||
proposerIndices, err := precomputeProposerIndices(state, indices, epoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -432,11 +433,10 @@ func computeCommittee(
|
||||
|
||||
// This computes proposer indices of the current epoch and returns a list of proposer indices,
|
||||
// the index of the list represents the slot number.
|
||||
func precomputeProposerIndices(state state.ReadOnlyBeaconState, activeIndices []primitives.ValidatorIndex) ([]primitives.ValidatorIndex, error) {
|
||||
func precomputeProposerIndices(state state.ReadOnlyBeaconState, activeIndices []primitives.ValidatorIndex, e primitives.Epoch) ([]primitives.ValidatorIndex, error) {
|
||||
hashFunc := hash.CustomSHA256Hasher()
|
||||
proposerIndices := make([]primitives.ValidatorIndex, params.BeaconConfig().SlotsPerEpoch)
|
||||
|
||||
e := time.CurrentEpoch(state)
|
||||
seed, err := Seed(state, e, params.BeaconConfig().DomainBeaconProposer)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not generate seed")
|
||||
|
||||
@@ -639,7 +639,7 @@ func TestPrecomputeProposerIndices_Ok(t *testing.T) {
|
||||
indices, err := ActiveValidatorIndices(context.Background(), state, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
proposerIndices, err := precomputeProposerIndices(state, indices)
|
||||
proposerIndices, err := precomputeProposerIndices(state, indices, time.CurrentEpoch(state))
|
||||
require.NoError(t, err)
|
||||
|
||||
var wantedProposerIndices []primitives.ValidatorIndex
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var CommitteeCacheInProgressHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
@@ -261,7 +262,7 @@ func BeaconProposerIndex(ctx context.Context, state state.ReadOnlyBeaconState) (
|
||||
}
|
||||
return proposerIndices[state.Slot()%params.BeaconConfig().SlotsPerEpoch], nil
|
||||
}
|
||||
if err := UpdateProposerIndicesInCache(ctx, state); err != nil {
|
||||
if err := UpdateProposerIndicesInCache(ctx, state, time.CurrentEpoch(state)); err != nil {
|
||||
return 0, errors.Wrap(err, "could not update committee cache")
|
||||
}
|
||||
}
|
||||
@@ -396,3 +397,22 @@ func isEligibleForActivation(activationEligibilityEpoch, activationEpoch, finali
|
||||
return activationEligibilityEpoch <= finalizedEpoch &&
|
||||
activationEpoch == params.BeaconConfig().FarFutureEpoch
|
||||
}
|
||||
|
||||
// LastActivatedValidatorIndex provides the last activated validator given a state
|
||||
func LastActivatedValidatorIndex(ctx context.Context, st state.ReadOnlyBeaconState) (primitives.ValidatorIndex, error) {
|
||||
_, span := trace.StartSpan(ctx, "helpers.LastActivatedValidatorIndex")
|
||||
defer span.End()
|
||||
var lastActivatedvalidatorIndex primitives.ValidatorIndex
|
||||
// linear search because status are not sorted
|
||||
for j := st.NumValidators() - 1; j >= 0; j-- {
|
||||
val, err := st.ValidatorAtIndexReadOnly(primitives.ValidatorIndex(j))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if IsActiveValidatorUsingTrie(val, time.CurrentEpoch(st)) {
|
||||
lastActivatedvalidatorIndex = primitives.ValidatorIndex(j)
|
||||
break
|
||||
}
|
||||
}
|
||||
return lastActivatedvalidatorIndex, nil
|
||||
}
|
||||
|
||||
@@ -727,3 +727,26 @@ func computeProposerIndexWithValidators(validators []*ethpb.Validator, activeInd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLastActivatedValidatorIndex_OK(t *testing.T) {
|
||||
beaconState, err := state_native.InitializeFromProtoPhase0(ðpb.BeaconState{})
|
||||
require.NoError(t, err)
|
||||
|
||||
validators := make([]*ethpb.Validator, 4)
|
||||
balances := make([]uint64, len(validators))
|
||||
for i := uint64(0); i < 4; i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: make([]byte, params.BeaconConfig().BLSPubkeyLength),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
EffectiveBalance: 32 * 1e9,
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
balances[i] = validators[i].EffectiveBalance
|
||||
}
|
||||
require.NoError(t, beaconState.SetValidators(validators))
|
||||
require.NoError(t, beaconState.SetBalances(balances))
|
||||
|
||||
index, err := LastActivatedValidatorIndex(context.Background(), beaconState)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, index, primitives.ValidatorIndex(3))
|
||||
}
|
||||
|
||||
@@ -79,8 +79,8 @@ func ExecuteStateTransitionNoVerifyAnySig(
|
||||
}
|
||||
stateRoot := signed.Block().StateRoot()
|
||||
if !bytes.Equal(postStateRoot[:], stateRoot[:]) {
|
||||
return nil, nil, fmt.Errorf("could not validate state root, wanted: %#x, received: %#x",
|
||||
postStateRoot[:], signed.Block().StateRoot())
|
||||
return nil, nil, fmt.Errorf("could not validate state root, wanted: %#x, received: %#x and output %s",
|
||||
postStateRoot[:], signed.Block().StateRoot(), st.CheckFieldTries())
|
||||
}
|
||||
|
||||
return set, st, nil
|
||||
|
||||
@@ -115,28 +115,32 @@ func FuzzExchangeTransitionConfiguration(f *testing.F) {
|
||||
|
||||
func FuzzExecutionPayload(f *testing.F) {
|
||||
logsBloom := [256]byte{'j', 'u', 'n', 'k'}
|
||||
execData := &engine.ExecutableData{
|
||||
ParentHash: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
|
||||
FeeRecipient: common.Address([20]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF}),
|
||||
StateRoot: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
|
||||
ReceiptsRoot: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
|
||||
LogsBloom: logsBloom[:],
|
||||
Random: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
|
||||
Number: math.MaxUint64,
|
||||
GasLimit: math.MaxUint64,
|
||||
GasUsed: math.MaxUint64,
|
||||
Timestamp: 100,
|
||||
ExtraData: nil,
|
||||
BaseFeePerGas: big.NewInt(math.MaxInt),
|
||||
BlockHash: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
|
||||
Transactions: [][]byte{{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}, {0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}, {0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}, {0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}},
|
||||
execData := &engine.ExecutionPayloadEnvelope{
|
||||
ExecutionPayload: &engine.ExecutableData{
|
||||
ParentHash: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
|
||||
FeeRecipient: common.Address([20]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF}),
|
||||
StateRoot: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
|
||||
ReceiptsRoot: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
|
||||
LogsBloom: logsBloom[:],
|
||||
Random: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
|
||||
Number: math.MaxUint64,
|
||||
GasLimit: math.MaxUint64,
|
||||
GasUsed: math.MaxUint64,
|
||||
Timestamp: 100,
|
||||
ExtraData: nil,
|
||||
BaseFeePerGas: big.NewInt(math.MaxInt),
|
||||
BlockHash: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
|
||||
Transactions: [][]byte{{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}, {0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}, {0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}, {0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}},
|
||||
Withdrawals: []*types.Withdrawal{},
|
||||
},
|
||||
BlockValue: nil,
|
||||
}
|
||||
output, err := json.Marshal(execData)
|
||||
assert.NoError(f, err)
|
||||
f.Add(output)
|
||||
f.Fuzz(func(t *testing.T, jsonBlob []byte) {
|
||||
gethResp := &engine.ExecutableData{}
|
||||
prysmResp := &pb.ExecutionPayload{}
|
||||
gethResp := &engine.ExecutionPayloadEnvelope{}
|
||||
prysmResp := &pb.ExecutionPayloadCapellaWithValue{}
|
||||
gethErr := json.Unmarshal(jsonBlob, gethResp)
|
||||
prysmErr := json.Unmarshal(jsonBlob, prysmResp)
|
||||
assert.Equal(t, gethErr != nil, prysmErr != nil, fmt.Sprintf("geth and prysm unmarshaller return inconsistent errors. %v and %v", gethErr, prysmErr))
|
||||
@@ -147,10 +151,10 @@ func FuzzExecutionPayload(f *testing.F) {
|
||||
gethBlob, gethErr := json.Marshal(gethResp)
|
||||
prysmBlob, prysmErr := json.Marshal(prysmResp)
|
||||
assert.Equal(t, gethErr != nil, prysmErr != nil, "geth and prysm unmarshaller return inconsistent errors")
|
||||
newGethResp := &engine.ExecutableData{}
|
||||
newGethResp := &engine.ExecutionPayloadEnvelope{}
|
||||
newGethErr := json.Unmarshal(prysmBlob, newGethResp)
|
||||
assert.NoError(t, newGethErr)
|
||||
newGethResp2 := &engine.ExecutableData{}
|
||||
newGethResp2 := &engine.ExecutionPayloadEnvelope{}
|
||||
newGethErr = json.Unmarshal(gethBlob, newGethResp2)
|
||||
assert.NoError(t, newGethErr)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/runtime/version"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -29,6 +30,9 @@ func (s *Service) processSyncAggregate(state state.BeaconState, blk interfaces.R
|
||||
if blk == nil || blk.Body() == nil {
|
||||
return
|
||||
}
|
||||
if blk.Version() == version.Phase0 {
|
||||
return
|
||||
}
|
||||
bits, err := blk.Body().SyncAggregate()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get SyncAggregate")
|
||||
|
||||
@@ -230,13 +230,13 @@ func New(cliCtx *cli.Context, opts ...Option) (*BeaconNode, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugln("Registering Determinstic Genesis Service")
|
||||
if err := beacon.registerDeterminsticGenesisService(); err != nil {
|
||||
log.Debugln("Registering Deterministic Genesis Service")
|
||||
if err := beacon.registerDeterministicGenesisService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugln("Registering Blockchain Service")
|
||||
if err := beacon.registerBlockchainService(beacon.forkChoicer, synchronizer); err != nil {
|
||||
if err := beacon.registerBlockchainService(beacon.forkChoicer, synchronizer, beacon.initialSyncComplete); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -590,7 +590,7 @@ func (b *BeaconNode) registerAttestationPool() error {
|
||||
return b.services.RegisterService(s)
|
||||
}
|
||||
|
||||
func (b *BeaconNode) registerBlockchainService(fc forkchoice.ForkChoicer, gs *startup.ClockSynchronizer) error {
|
||||
func (b *BeaconNode) registerBlockchainService(fc forkchoice.ForkChoicer, gs *startup.ClockSynchronizer, syncComplete chan struct{}) error {
|
||||
var web3Service *execution.Service
|
||||
if err := b.services.FetchService(&web3Service); err != nil {
|
||||
return err
|
||||
@@ -621,6 +621,7 @@ func (b *BeaconNode) registerBlockchainService(fc forkchoice.ForkChoicer, gs *st
|
||||
blockchain.WithFinalizedStateAtStartUp(b.finalizedStateAtStartUp),
|
||||
blockchain.WithProposerIdsCache(b.proposerIdsCache),
|
||||
blockchain.WithClockSynchronizer(gs),
|
||||
blockchain.WithSyncComplete(syncComplete),
|
||||
)
|
||||
|
||||
blockchainService, err := blockchain.NewService(b.ctx, opts...)
|
||||
@@ -923,7 +924,7 @@ func (b *BeaconNode) registerGRPCGateway(router *mux.Router) error {
|
||||
return b.services.RegisterService(g)
|
||||
}
|
||||
|
||||
func (b *BeaconNode) registerDeterminsticGenesisService() error {
|
||||
func (b *BeaconNode) registerDeterministicGenesisService() error {
|
||||
genesisTime := b.cliCtx.Uint64(flags.InteropGenesisTimeFlag.Name)
|
||||
genesisValidators := b.cliCtx.Uint64(flags.InteropNumValidatorsFlag.Name)
|
||||
|
||||
@@ -986,7 +987,8 @@ func (b *BeaconNode) registerBuilderService(cliCtx *cli.Context) error {
|
||||
opts := append(b.serviceFlagOpts.builderOpts,
|
||||
builder.WithHeadFetcher(chainService),
|
||||
builder.WithDatabase(b.db))
|
||||
if cliCtx.Bool(flags.EnableRegistrationCache.Name) {
|
||||
// make cache the default.
|
||||
if !cliCtx.Bool(features.DisableRegistrationCache.Name) {
|
||||
opts = append(opts, builder.WithRegistrationCache())
|
||||
}
|
||||
svc, err := builder.NewService(b.ctx, opts...)
|
||||
|
||||
@@ -47,6 +47,7 @@ go_test(
|
||||
deps = [
|
||||
"//async:go_default_library",
|
||||
"//beacon-chain/operations/attestations/kv:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
|
||||
@@ -14,6 +14,7 @@ go_library(
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//crypto/hash:go_default_library",
|
||||
@@ -39,8 +40,8 @@ go_test(
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
|
||||
@@ -2,9 +2,12 @@ package kv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
attaggregation "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/attestation/aggregation/attestations"
|
||||
@@ -23,21 +26,11 @@ func (c *AttCaches) AggregateUnaggregatedAttestations(ctx context.Context) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.aggregateUnaggregatedAttestations(ctx, unaggregatedAtts)
|
||||
return c.aggregateUnaggregatedAtts(ctx, unaggregatedAtts)
|
||||
}
|
||||
|
||||
// AggregateUnaggregatedAttestationsBySlotIndex aggregates the unaggregated attestations and saves
|
||||
// newly aggregated attestations in the pool. Unaggregated attestations are filtered by slot and
|
||||
// committee index.
|
||||
func (c *AttCaches) AggregateUnaggregatedAttestationsBySlotIndex(ctx context.Context, slot primitives.Slot, committeeIndex primitives.CommitteeIndex) error {
|
||||
ctx, span := trace.StartSpan(ctx, "operations.attestations.kv.AggregateUnaggregatedAttestationsBySlotIndex")
|
||||
defer span.End()
|
||||
unaggregatedAtts := c.UnaggregatedAttestationsBySlotIndex(ctx, slot, committeeIndex)
|
||||
return c.aggregateUnaggregatedAttestations(ctx, unaggregatedAtts)
|
||||
}
|
||||
|
||||
func (c *AttCaches) aggregateUnaggregatedAttestations(ctx context.Context, unaggregatedAtts []*ethpb.Attestation) error {
|
||||
ctx, span := trace.StartSpan(ctx, "operations.attestations.kv.aggregateUnaggregatedAttestations")
|
||||
func (c *AttCaches) aggregateUnaggregatedAtts(ctx context.Context, unaggregatedAtts []*ethpb.Attestation) error {
|
||||
_, span := trace.StartSpan(ctx, "operations.attestations.kv.aggregateUnaggregatedAtts")
|
||||
defer span.End()
|
||||
|
||||
attsByDataRoot := make(map[[32]byte][]*ethpb.Attestation, len(unaggregatedAtts))
|
||||
@@ -52,26 +45,32 @@ func (c *AttCaches) aggregateUnaggregatedAttestations(ctx context.Context, unagg
|
||||
// Aggregate unaggregated attestations from the pool and save them in the pool.
|
||||
// Track the unaggregated attestations that aren't able to aggregate.
|
||||
leftOverUnaggregatedAtt := make(map[[32]byte]bool)
|
||||
for _, atts := range attsByDataRoot {
|
||||
aggregated, err := attaggregation.AggregateDisjointOneBitAtts(atts)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not aggregate unaggregated attestations")
|
||||
}
|
||||
if aggregated == nil {
|
||||
return errors.New("could not aggregate unaggregated attestations")
|
||||
}
|
||||
if helpers.IsAggregated(aggregated) {
|
||||
if err := c.SaveAggregatedAttestations([]*ethpb.Attestation{aggregated}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
h, err := hashFn(aggregated)
|
||||
|
||||
if features.Get().AggregateParallel {
|
||||
leftOverUnaggregatedAtt = c.aggregateParallel(attsByDataRoot, leftOverUnaggregatedAtt)
|
||||
} else {
|
||||
for _, atts := range attsByDataRoot {
|
||||
aggregated, err := attaggregation.AggregateDisjointOneBitAtts(atts)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "could not aggregate unaggregated attestations")
|
||||
}
|
||||
if aggregated == nil {
|
||||
return errors.New("could not aggregate unaggregated attestations")
|
||||
}
|
||||
if helpers.IsAggregated(aggregated) {
|
||||
if err := c.SaveAggregatedAttestations([]*ethpb.Attestation{aggregated}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
h, err := hashFn(aggregated)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
leftOverUnaggregatedAtt[h] = true
|
||||
}
|
||||
leftOverUnaggregatedAtt[h] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the unaggregated attestations from the pool that were successfully aggregated.
|
||||
for _, att := range unaggregatedAtts {
|
||||
h, err := hashFn(att)
|
||||
@@ -88,6 +87,58 @@ func (c *AttCaches) aggregateUnaggregatedAttestations(ctx context.Context, unagg
|
||||
return nil
|
||||
}
|
||||
|
||||
// aggregateParallel aggregates attestations in parallel for `atts` and saves them in the pool,
|
||||
// returns the unaggregated attestations that weren't able to aggregate.
|
||||
// Given `n` CPU cores, it creates a channel of size `n` and spawns `n` goroutines to aggregate attestations
|
||||
func (c *AttCaches) aggregateParallel(atts map[[32]byte][]*ethpb.Attestation, leftOver map[[32]byte]bool) map[[32]byte]bool {
|
||||
var leftoverLock sync.Mutex
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
n := runtime.GOMAXPROCS(0) // defaults to the value of runtime.NumCPU
|
||||
ch := make(chan []*ethpb.Attestation, n)
|
||||
wg.Add(n)
|
||||
for i := 0; i < n; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for as := range ch {
|
||||
aggregated, err := attaggregation.AggregateDisjointOneBitAtts(as)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not aggregate unaggregated attestations")
|
||||
continue
|
||||
}
|
||||
if aggregated == nil {
|
||||
log.Error("nil aggregated attestation")
|
||||
continue
|
||||
}
|
||||
if helpers.IsAggregated(aggregated) {
|
||||
if err := c.SaveAggregatedAttestations([]*ethpb.Attestation{aggregated}); err != nil {
|
||||
log.WithError(err).Error("could not save aggregated attestation")
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
h, err := hashFn(aggregated)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not hash attestation")
|
||||
continue
|
||||
}
|
||||
leftoverLock.Lock()
|
||||
leftOver[h] = true
|
||||
leftoverLock.Unlock()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for _, as := range atts {
|
||||
ch <- as
|
||||
}
|
||||
|
||||
close(ch)
|
||||
wg.Wait()
|
||||
|
||||
return leftOver
|
||||
}
|
||||
|
||||
// SaveAggregatedAttestation saves an aggregated attestation in cache.
|
||||
func (c *AttCaches) SaveAggregatedAttestation(att *ethpb.Attestation) error {
|
||||
if err := helpers.ValidateNilAttestation(att); err != nil {
|
||||
@@ -165,7 +216,7 @@ func (c *AttCaches) AggregatedAttestations() []*ethpb.Attestation {
|
||||
// AggregatedAttestationsBySlotIndex returns the aggregated attestations in cache,
|
||||
// filtered by committee index and slot.
|
||||
func (c *AttCaches) AggregatedAttestationsBySlotIndex(ctx context.Context, slot primitives.Slot, committeeIndex primitives.CommitteeIndex) []*ethpb.Attestation {
|
||||
ctx, span := trace.StartSpan(ctx, "operations.attestations.kv.AggregatedAttestationsBySlotIndex")
|
||||
_, span := trace.StartSpan(ctx, "operations.attestations.kv.AggregatedAttestationsBySlotIndex")
|
||||
defer span.End()
|
||||
|
||||
atts := make([]*ethpb.Attestation, 0)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
fssz "github.com/prysmaticlabs/fastssz"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
||||
@@ -18,6 +18,11 @@ import (
|
||||
)
|
||||
|
||||
func TestKV_Aggregated_AggregateUnaggregatedAttestations(t *testing.T) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
AggregateParallel: true,
|
||||
})
|
||||
defer resetFn()
|
||||
|
||||
cache := NewAttCaches()
|
||||
priv, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
@@ -39,61 +44,6 @@ func TestKV_Aggregated_AggregateUnaggregatedAttestations(t *testing.T) {
|
||||
require.Equal(t, 1, len(cache.AggregatedAttestationsBySlotIndex(context.Background(), 2, 0)), "Did not aggregate correctly")
|
||||
}
|
||||
|
||||
func TestKV_Aggregated_AggregateUnaggregatedAttestationsBySlotIndex(t *testing.T) {
|
||||
cache := NewAttCaches()
|
||||
genData := func(slot primitives.Slot, committeeIndex primitives.CommitteeIndex) *ethpb.AttestationData {
|
||||
return util.HydrateAttestationData(ðpb.AttestationData{
|
||||
Slot: slot,
|
||||
CommitteeIndex: committeeIndex,
|
||||
})
|
||||
}
|
||||
genSign := func() []byte {
|
||||
priv, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
return priv.Sign([]byte{'a'}).Marshal()
|
||||
}
|
||||
|
||||
atts := []*ethpb.Attestation{
|
||||
// The first slot.
|
||||
{AggregationBits: bitfield.Bitlist{0b1001}, Data: genData(1, 2), Signature: genSign()},
|
||||
{AggregationBits: bitfield.Bitlist{0b1010}, Data: genData(1, 2), Signature: genSign()},
|
||||
{AggregationBits: bitfield.Bitlist{0b1100}, Data: genData(1, 2), Signature: genSign()},
|
||||
{AggregationBits: bitfield.Bitlist{0b1001}, Data: genData(1, 3), Signature: genSign()},
|
||||
{AggregationBits: bitfield.Bitlist{0b1100}, Data: genData(1, 3), Signature: genSign()},
|
||||
// The second slot.
|
||||
{AggregationBits: bitfield.Bitlist{0b1001}, Data: genData(2, 3), Signature: genSign()},
|
||||
{AggregationBits: bitfield.Bitlist{0b1010}, Data: genData(2, 3), Signature: genSign()},
|
||||
{AggregationBits: bitfield.Bitlist{0b1100}, Data: genData(2, 4), Signature: genSign()},
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
// Make sure that no error is produced if aggregation is requested on empty unaggregated list.
|
||||
require.NoError(t, cache.AggregateUnaggregatedAttestationsBySlotIndex(ctx, 1, 2))
|
||||
require.NoError(t, cache.AggregateUnaggregatedAttestationsBySlotIndex(ctx, 2, 3))
|
||||
require.Equal(t, 0, len(cache.UnaggregatedAttestationsBySlotIndex(ctx, 1, 2)))
|
||||
require.Equal(t, 0, len(cache.AggregatedAttestationsBySlotIndex(ctx, 1, 2)), "Did not aggregate correctly")
|
||||
require.Equal(t, 0, len(cache.UnaggregatedAttestationsBySlotIndex(ctx, 1, 3)))
|
||||
require.Equal(t, 0, len(cache.AggregatedAttestationsBySlotIndex(ctx, 1, 3)), "Did not aggregate correctly")
|
||||
|
||||
// Persist unaggregated attestations, and aggregate on per slot/committee index base.
|
||||
require.NoError(t, cache.SaveUnaggregatedAttestations(atts))
|
||||
require.NoError(t, cache.AggregateUnaggregatedAttestationsBySlotIndex(ctx, 1, 2))
|
||||
require.NoError(t, cache.AggregateUnaggregatedAttestationsBySlotIndex(ctx, 2, 3))
|
||||
|
||||
// Committee attestations at a slot should be aggregated.
|
||||
require.Equal(t, 0, len(cache.UnaggregatedAttestationsBySlotIndex(ctx, 1, 2)))
|
||||
require.Equal(t, 1, len(cache.AggregatedAttestationsBySlotIndex(ctx, 1, 2)), "Did not aggregate correctly")
|
||||
// Committee attestations haven't been aggregated.
|
||||
require.Equal(t, 2, len(cache.UnaggregatedAttestationsBySlotIndex(ctx, 1, 3)))
|
||||
require.Equal(t, 0, len(cache.AggregatedAttestationsBySlotIndex(ctx, 1, 3)), "Did not aggregate correctly")
|
||||
// Committee at a second slot is aggregated.
|
||||
require.Equal(t, 0, len(cache.UnaggregatedAttestationsBySlotIndex(ctx, 2, 3)))
|
||||
require.Equal(t, 1, len(cache.AggregatedAttestationsBySlotIndex(ctx, 2, 3)), "Did not aggregate correctly")
|
||||
// The second committee at second slot is not aggregated.
|
||||
require.Equal(t, 1, len(cache.UnaggregatedAttestationsBySlotIndex(ctx, 2, 4)))
|
||||
require.Equal(t, 0, len(cache.AggregatedAttestationsBySlotIndex(ctx, 2, 4)), "Did not aggregate correctly")
|
||||
}
|
||||
|
||||
func TestKV_Aggregated_SaveAggregatedAttestation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
type Pool interface {
|
||||
// For Aggregated attestations
|
||||
AggregateUnaggregatedAttestations(ctx context.Context) error
|
||||
AggregateUnaggregatedAttestationsBySlotIndex(ctx context.Context, slot primitives.Slot, committeeIndex primitives.CommitteeIndex) error
|
||||
SaveAggregatedAttestation(att *ethpb.Attestation) error
|
||||
SaveAggregatedAttestations(atts []*ethpb.Attestation) error
|
||||
AggregatedAttestations() []*ethpb.Attestation
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
attaggregation "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/attestation/aggregation/attestations"
|
||||
@@ -17,6 +18,11 @@ import (
|
||||
)
|
||||
|
||||
func TestBatchAttestations_Multiple(t *testing.T) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
AggregateParallel: true,
|
||||
})
|
||||
defer resetFn()
|
||||
|
||||
s, err := NewService(context.Background(), &Config{Pool: NewPool()})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -460,6 +460,19 @@ func convertToUdpMultiAddr(node *enode.Node) ([]ma.Multiaddr, error) {
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
func peerIdsFromMultiAddrs(addrs []ma.Multiaddr) []peer.ID {
|
||||
peers := []peer.ID{}
|
||||
for _, a := range addrs {
|
||||
info, err := peer.AddrInfoFromP2pAddr(a)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("Could not derive peer info from multiaddress %s", a.String())
|
||||
continue
|
||||
}
|
||||
peers = append(peers, info.ID)
|
||||
}
|
||||
return peers
|
||||
}
|
||||
|
||||
func multiAddrFromString(address string) (ma.Multiaddr, error) {
|
||||
return ma.NewMultiaddr(address)
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ func (s *Service) buildOptions(ip net.IP, priKey *ecdsa.PrivateKey) []libp2p.Opt
|
||||
libp2p.UserAgent(version.BuildData()),
|
||||
libp2p.ConnectionGater(s),
|
||||
libp2p.Transport(tcp.NewTCPTransport),
|
||||
libp2p.Muxer("/mplex/6.7.0", mplex.DefaultTransport),
|
||||
libp2p.DefaultMuxers,
|
||||
libp2p.Muxer("/mplex/6.7.0", mplex.DefaultTransport),
|
||||
}
|
||||
|
||||
options = append(options, libp2p.Security(noise.ID, noise.New))
|
||||
|
||||
@@ -123,7 +123,7 @@ func TestDefaultMultiplexers(t *testing.T) {
|
||||
err = cfg.Apply(append(opts, libp2p.FallbackDefaults)...)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, protocol.ID("/mplex/6.7.0"), cfg.Muxers[0].ID)
|
||||
assert.Equal(t, protocol.ID("/yamux/1.0.0"), cfg.Muxers[1].ID)
|
||||
assert.Equal(t, protocol.ID("/yamux/1.0.0"), cfg.Muxers[0].ID)
|
||||
assert.Equal(t, protocol.ID("/mplex/6.7.0"), cfg.Muxers[1].ID)
|
||||
|
||||
}
|
||||
|
||||
@@ -38,9 +38,10 @@ type StoreConfig struct {
|
||||
// the mutex when accessing data.
|
||||
type Store struct {
|
||||
sync.RWMutex
|
||||
ctx context.Context
|
||||
config *StoreConfig
|
||||
peers map[peer.ID]*PeerData
|
||||
ctx context.Context
|
||||
config *StoreConfig
|
||||
peers map[peer.ID]*PeerData
|
||||
trustedPeers map[peer.ID]bool
|
||||
}
|
||||
|
||||
// PeerData aggregates protocol and application level info about a single peer.
|
||||
@@ -69,9 +70,10 @@ type PeerData struct {
|
||||
// NewStore creates new peer data store.
|
||||
func NewStore(ctx context.Context, config *StoreConfig) *Store {
|
||||
return &Store{
|
||||
ctx: ctx,
|
||||
config: config,
|
||||
peers: make(map[peer.ID]*PeerData),
|
||||
ctx: ctx,
|
||||
config: config,
|
||||
peers: make(map[peer.ID]*PeerData),
|
||||
trustedPeers: make(map[peer.ID]bool),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,12 +107,25 @@ func (s *Store) DeletePeerData(pid peer.ID) {
|
||||
delete(s.peers, pid)
|
||||
}
|
||||
|
||||
// SetTrustedPeers sets our desired trusted peer set.
|
||||
func (s *Store) SetTrustedPeers(peers []peer.ID) {
|
||||
for _, p := range peers {
|
||||
s.trustedPeers[p] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Peers returns map of peer data objects.
|
||||
// Important: it is assumed that store mutex is locked when calling this method.
|
||||
func (s *Store) Peers() map[peer.ID]*PeerData {
|
||||
return s.peers
|
||||
}
|
||||
|
||||
// IsTrustedPeer checks that the provided peer
|
||||
// is in our trusted peer set.
|
||||
func (s *Store) IsTrustedPeer(p peer.ID) bool {
|
||||
return s.trustedPeers[p]
|
||||
}
|
||||
|
||||
// Config exposes store configuration params.
|
||||
func (s *Store) Config() *StoreConfig {
|
||||
return s.config
|
||||
|
||||
@@ -80,3 +80,20 @@ func TestStore_PeerDataGetOrCreate(t *testing.T) {
|
||||
assert.Equal(t, uint64(0), peerData.ProcessedBlocks)
|
||||
require.Equal(t, 1, len(store.Peers()))
|
||||
}
|
||||
|
||||
func TestStore_TrustedPeers(t *testing.T) {
|
||||
store := peerdata.NewStore(context.Background(), &peerdata.StoreConfig{
|
||||
MaxPeers: 12,
|
||||
})
|
||||
|
||||
pid1 := peer.ID("00001")
|
||||
pid2 := peer.ID("00002")
|
||||
pid3 := peer.ID("00003")
|
||||
|
||||
tPeers := []peer.ID{pid1, pid2, pid3}
|
||||
store.SetTrustedPeers(tPeers)
|
||||
|
||||
assert.Equal(t, true, store.IsTrustedPeer(pid1))
|
||||
assert.Equal(t, true, store.IsTrustedPeer(pid2))
|
||||
assert.Equal(t, true, store.IsTrustedPeer(pid3))
|
||||
}
|
||||
|
||||
@@ -335,6 +335,10 @@ func (p *Status) IsBad(pid peer.ID) bool {
|
||||
|
||||
// isBad is the lock-free version of IsBad.
|
||||
func (p *Status) isBad(pid peer.ID) bool {
|
||||
// Do not disconnect from trusted peers.
|
||||
if p.store.IsTrustedPeer(pid) {
|
||||
return false
|
||||
}
|
||||
return p.isfromBadIP(pid) || p.scorers.IsBadPeerNoLock(pid)
|
||||
}
|
||||
|
||||
@@ -769,7 +773,7 @@ func (p *Status) PeersToPrune() []peer.ID {
|
||||
// Select connected and inbound peers to prune.
|
||||
for pid, peerData := range p.store.Peers() {
|
||||
if peerData.ConnState == PeerConnected &&
|
||||
peerData.Direction == network.DirInbound {
|
||||
peerData.Direction == network.DirInbound && !p.store.IsTrustedPeer(pid) {
|
||||
peersToPrune = append(peersToPrune, &peerResp{
|
||||
pid: pid,
|
||||
score: p.scorers.ScoreNoLock(pid),
|
||||
@@ -835,7 +839,7 @@ func (p *Status) deprecatedPeersToPrune() []peer.ID {
|
||||
// Select connected and inbound peers to prune.
|
||||
for pid, peerData := range p.store.Peers() {
|
||||
if peerData.ConnState == PeerConnected &&
|
||||
peerData.Direction == network.DirInbound {
|
||||
peerData.Direction == network.DirInbound && !p.store.IsTrustedPeer(pid) {
|
||||
peersToPrune = append(peersToPrune, &peerResp{
|
||||
pid: pid,
|
||||
badResp: peerData.BadResponses,
|
||||
@@ -900,6 +904,14 @@ func (p *Status) ConnectedPeerLimit() uint64 {
|
||||
return uint64(maxLim) - maxLimitBuffer
|
||||
}
|
||||
|
||||
// SetTrustedPeers sets our trusted peer set into
|
||||
// our peerstore.
|
||||
func (p *Status) SetTrustedPeers(peers []peer.ID) {
|
||||
p.store.Lock()
|
||||
defer p.store.Unlock()
|
||||
p.store.SetTrustedPeers(peers)
|
||||
}
|
||||
|
||||
// this method assumes the store lock is acquired before
|
||||
// executing the method.
|
||||
func (p *Status) isfromBadIP(pid peer.ID) bool {
|
||||
|
||||
@@ -755,6 +755,78 @@ func TestPrunePeers(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrunePeers_TrustedPeers(t *testing.T) {
|
||||
p := peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
PeerLimit: 30,
|
||||
ScorerParams: &scorers.Config{
|
||||
BadResponsesScorerConfig: &scorers.BadResponsesScorerConfig{
|
||||
Threshold: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
for i := 0; i < 15; i++ {
|
||||
// Peer added to peer handler.
|
||||
createPeer(t, p, nil, network.DirOutbound, peerdata.PeerConnectionState(ethpb.ConnectionState_CONNECTED))
|
||||
}
|
||||
// Assert there are no prunable peers.
|
||||
peersToPrune := p.PeersToPrune()
|
||||
assert.Equal(t, 0, len(peersToPrune))
|
||||
|
||||
for i := 0; i < 18; i++ {
|
||||
// Peer added to peer handler.
|
||||
createPeer(t, p, nil, network.DirInbound, peerdata.PeerConnectionState(ethpb.ConnectionState_CONNECTED))
|
||||
}
|
||||
|
||||
// Assert there are the correct prunable peers.
|
||||
peersToPrune = p.PeersToPrune()
|
||||
assert.Equal(t, 3, len(peersToPrune))
|
||||
|
||||
// Add in more peers.
|
||||
for i := 0; i < 13; i++ {
|
||||
// Peer added to peer handler.
|
||||
createPeer(t, p, nil, network.DirInbound, peerdata.PeerConnectionState(ethpb.ConnectionState_CONNECTED))
|
||||
}
|
||||
|
||||
trustedPeers := []peer.ID{}
|
||||
// Set up bad scores for inbound peers.
|
||||
inboundPeers := p.InboundConnected()
|
||||
for i, pid := range inboundPeers {
|
||||
modulo := i % 5
|
||||
// Increment bad scores for peers.
|
||||
for j := 0; j < modulo; j++ {
|
||||
p.Scorers().BadResponsesScorer().Increment(pid)
|
||||
}
|
||||
if modulo == 4 {
|
||||
trustedPeers = append(trustedPeers, pid)
|
||||
}
|
||||
}
|
||||
p.SetTrustedPeers(trustedPeers)
|
||||
// Assert all peers more than max are prunable.
|
||||
peersToPrune = p.PeersToPrune()
|
||||
assert.Equal(t, 16, len(peersToPrune))
|
||||
|
||||
// Check that trusted peers are not pruned.
|
||||
for _, pid := range peersToPrune {
|
||||
for _, tPid := range trustedPeers {
|
||||
assert.NotEqual(t, pid.String(), tPid.String())
|
||||
}
|
||||
}
|
||||
for _, pid := range peersToPrune {
|
||||
dir, err := p.Direction(pid)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, network.DirInbound, dir)
|
||||
}
|
||||
|
||||
// Ensure it is in the descending order.
|
||||
currScore := p.Scorers().Score(peersToPrune[0])
|
||||
for _, pid := range peersToPrune {
|
||||
score := p.Scorers().BadResponsesScorer().Score(pid)
|
||||
assert.Equal(t, true, currScore >= score)
|
||||
currScore = score
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus_BestPeer(t *testing.T) {
|
||||
type peerConfig struct {
|
||||
headSlot primitives.Slot
|
||||
|
||||
@@ -210,6 +210,10 @@ func (s *Service) Start() {
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not connect to static peer")
|
||||
}
|
||||
// Set trusted peers for those that are provided as static addresses.
|
||||
pids := peerIdsFromMultiAddrs(addrs)
|
||||
s.peers.SetTrustedPeers(pids)
|
||||
peersToWatch = append(peersToWatch, s.cfg.StaticPeers...)
|
||||
s.connectWithAllPeers(addrs)
|
||||
}
|
||||
// Initialize metadata according to the
|
||||
|
||||
@@ -25,6 +25,7 @@ go_library(
|
||||
"//beacon-chain/operations/voluntaryexits:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//beacon-chain/rpc/eth/beacon:go_default_library",
|
||||
"//beacon-chain/rpc/eth/builder:go_default_library",
|
||||
"//beacon-chain/rpc/eth/debug:go_default_library",
|
||||
"//beacon-chain/rpc/eth/events:go_default_library",
|
||||
"//beacon-chain/rpc/eth/node:go_default_library",
|
||||
|
||||
@@ -3,6 +3,7 @@ package apimiddleware
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -17,9 +18,14 @@ func (p *EpochParticipation) UnmarshalJSON(b []byte) error {
|
||||
if len(b) < 2 {
|
||||
return errors.New("epoch participation length must be at least 2")
|
||||
}
|
||||
if b[0] != '"' || b[len(b)-1] != '"' {
|
||||
return errors.Errorf("provided epoch participation json string is malformed: %s", string(b))
|
||||
}
|
||||
|
||||
// Remove leading and trailing quotation marks.
|
||||
decoded, err := base64.StdEncoding.DecodeString(string(b[1 : len(b)-1]))
|
||||
jsonString := string(b)
|
||||
jsonString = strings.Trim(jsonString, "\"")
|
||||
decoded, err := base64.StdEncoding.DecodeString(jsonString)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not decode epoch participation base64 value")
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestUnmarshalEpochParticipation(t *testing.T) {
|
||||
ep := EpochParticipation{}
|
||||
err := ep.UnmarshalJSON([]byte(":illegal:"))
|
||||
require.NotNil(t, err)
|
||||
assert.ErrorContains(t, "could not decode epoch participation base64 value", err)
|
||||
assert.ErrorContains(t, "provided epoch participation json string is malformed", err)
|
||||
})
|
||||
t.Run("length too small", func(t *testing.T) {
|
||||
ep := EpochParticipation{}
|
||||
@@ -36,4 +36,8 @@ func TestUnmarshalEpochParticipation(t *testing.T) {
|
||||
require.NoError(t, ep.UnmarshalJSON([]byte("null")))
|
||||
assert.DeepEqual(t, EpochParticipation([]string{}), ep)
|
||||
})
|
||||
t.Run("invalid value", func(t *testing.T) {
|
||||
ep := EpochParticipation{}
|
||||
require.ErrorContains(t, "provided epoch participation json string is malformed", ep.UnmarshalJSON([]byte("XdHJ1ZQ==X")))
|
||||
})
|
||||
}
|
||||
|
||||
48
beacon-chain/rpc/eth/builder/BUILD.bazel
Normal file
48
beacon-chain/rpc/eth/builder/BUILD.bazel
Normal file
@@ -0,0 +1,48 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"handlers.go",
|
||||
"server.go",
|
||||
"structs.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/builder",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/transition:go_default_library",
|
||||
"//beacon-chain/rpc/lookup:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//network:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_gorilla_mux//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["handlers_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
"//beacon-chain/rpc/testutil:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//network:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_gorilla_mux//:go_default_library",
|
||||
],
|
||||
)
|
||||
131
beacon-chain/rpc/eth/builder/handlers.go
Normal file
131
beacon-chain/rpc/eth/builder/handlers.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/network"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
)
|
||||
|
||||
// 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
|
||||
stateId := mux.Vars(r)["state_id"]
|
||||
if stateId == "" {
|
||||
network.WriteError(w, &network.DefaultErrorJson{
|
||||
Message: "state_id is required in URL params",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
st, err := s.Stater.State(r.Context(), []byte(stateId))
|
||||
if err != nil {
|
||||
network.WriteError(w, handleWrapError(err, "could not retrieve state", http.StatusNotFound))
|
||||
return
|
||||
}
|
||||
queryParam := r.URL.Query().Get("proposal_slot")
|
||||
var proposalSlot primitives.Slot
|
||||
if queryParam != "" {
|
||||
pSlot, err := strconv.ParseUint(queryParam, 10, 64)
|
||||
if err != nil {
|
||||
network.WriteError(w, handleWrapError(err, "invalid proposal slot value", http.StatusBadRequest))
|
||||
return
|
||||
}
|
||||
proposalSlot = primitives.Slot(pSlot)
|
||||
} else {
|
||||
proposalSlot = st.Slot() + 1
|
||||
}
|
||||
// Perform sanity checks on proposal slot before computing state
|
||||
capellaStart, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
|
||||
if err != nil {
|
||||
network.WriteError(w, handleWrapError(err, "could not calculate Capella start slot", http.StatusInternalServerError))
|
||||
return
|
||||
}
|
||||
if proposalSlot < capellaStart {
|
||||
network.WriteError(w, &network.DefaultErrorJson{
|
||||
Message: "expected withdrawals are not supported before Capella fork",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
if proposalSlot <= st.Slot() {
|
||||
network.WriteError(w, &network.DefaultErrorJson{
|
||||
Message: fmt.Sprintf("proposal slot must be bigger than state slot. proposal slot: %d, state slot: %d", proposalSlot, st.Slot()),
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
lookAheadLimit := uint64(params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().MaxSeedLookahead)))
|
||||
if st.Slot().Add(lookAheadLimit) <= proposalSlot {
|
||||
network.WriteError(w, &network.DefaultErrorJson{
|
||||
Message: fmt.Sprintf("proposal slot cannot be >= %d slots ahead of state slot", lookAheadLimit),
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
// Get metadata for response
|
||||
isOptimistic, err := s.OptimisticModeFetcher.IsOptimistic(r.Context())
|
||||
if err != nil {
|
||||
network.WriteError(w, handleWrapError(err, "could not get optimistic mode info", http.StatusInternalServerError))
|
||||
return
|
||||
}
|
||||
root, err := helpers.BlockRootAtSlot(st, st.Slot()-1)
|
||||
if err != nil {
|
||||
network.WriteError(w, handleWrapError(err, "could not get block root", http.StatusInternalServerError))
|
||||
return
|
||||
}
|
||||
var blockRoot = [32]byte(root)
|
||||
isFinalized := s.FinalizationFetcher.IsFinalized(r.Context(), blockRoot)
|
||||
// Advance state forward to proposal slot
|
||||
st, err = transition.ProcessSlots(r.Context(), st, proposalSlot)
|
||||
if err != nil {
|
||||
network.WriteError(w, &network.DefaultErrorJson{
|
||||
Message: "could not process slots",
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
withdrawals, err := st.ExpectedWithdrawals()
|
||||
if err != nil {
|
||||
network.WriteError(w, &network.DefaultErrorJson{
|
||||
Message: "could not get expected withdrawals",
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
network.WriteJson(w, &ExpectedWithdrawalsResponse{
|
||||
ExecutionOptimistic: isOptimistic,
|
||||
Finalized: isFinalized,
|
||||
Data: buildExpectedWithdrawalsData(withdrawals),
|
||||
})
|
||||
}
|
||||
|
||||
func buildExpectedWithdrawalsData(withdrawals []*enginev1.Withdrawal) []*ExpectedWithdrawal {
|
||||
data := make([]*ExpectedWithdrawal, len(withdrawals))
|
||||
for i, withdrawal := range withdrawals {
|
||||
data[i] = &ExpectedWithdrawal{
|
||||
Address: hexutil.Encode(withdrawal.Address),
|
||||
Amount: strconv.FormatUint(withdrawal.Amount, 10),
|
||||
Index: strconv.FormatUint(withdrawal.Index, 10),
|
||||
ValidatorIndex: strconv.FormatUint(uint64(withdrawal.ValidatorIndex), 10),
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func handleWrapError(err error, message string, code int) *network.DefaultErrorJson {
|
||||
return &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, message).Error(),
|
||||
Code: code,
|
||||
}
|
||||
}
|
||||
210
beacon-chain/rpc/eth/builder/handlers_test.go
Normal file
210
beacon-chain/rpc/eth/builder/handlers_test.go
Normal file
@@ -0,0 +1,210 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/gorilla/mux"
|
||||
mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/v4/network"
|
||||
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/util"
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
)
|
||||
|
||||
func TestExpectedWithdrawals_BadRequest(t *testing.T) {
|
||||
st, err := util.NewBeaconStateCapella()
|
||||
slotsAhead := 5000
|
||||
require.NoError(t, err)
|
||||
capellaSlot, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
|
||||
require.NoError(t, err)
|
||||
currentSlot := capellaSlot + primitives.Slot(slotsAhead)
|
||||
require.NoError(t, st.SetSlot(currentSlot))
|
||||
mockChainService := &mock.ChainService{Optimistic: true}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
path string
|
||||
urlParams map[string]string
|
||||
state state.BeaconState
|
||||
errorMessage string
|
||||
}{
|
||||
{
|
||||
name: "no state_id url params",
|
||||
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot" +
|
||||
strconv.FormatUint(uint64(currentSlot), 10),
|
||||
urlParams: map[string]string{},
|
||||
state: nil,
|
||||
errorMessage: "state_id is required in URL params",
|
||||
},
|
||||
{
|
||||
name: "invalid proposal slot value",
|
||||
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot=aaa",
|
||||
urlParams: map[string]string{"state_id": "head"},
|
||||
state: st,
|
||||
errorMessage: "invalid proposal slot value",
|
||||
},
|
||||
{
|
||||
name: "proposal slot < Capella start slot",
|
||||
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot=" +
|
||||
strconv.FormatUint(uint64(capellaSlot)-1, 10),
|
||||
urlParams: map[string]string{"state_id": "head"},
|
||||
state: st,
|
||||
errorMessage: "expected withdrawals are not supported before Capella fork",
|
||||
},
|
||||
{
|
||||
name: "proposal slot == Capella start slot",
|
||||
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot=" +
|
||||
strconv.FormatUint(uint64(capellaSlot), 10),
|
||||
urlParams: map[string]string{"state_id": "head"},
|
||||
state: st,
|
||||
errorMessage: "proposal slot must be bigger than state slot",
|
||||
},
|
||||
{
|
||||
name: "Proposal slot >= 128 slots ahead of state slot",
|
||||
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot=" +
|
||||
strconv.FormatUint(uint64(currentSlot+128), 10),
|
||||
urlParams: map[string]string{"state_id": "head"},
|
||||
state: st,
|
||||
errorMessage: "proposal slot cannot be >= 128 slots ahead of state slot",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
s := &Server{
|
||||
FinalizationFetcher: mockChainService,
|
||||
OptimisticModeFetcher: mockChainService,
|
||||
Stater: &testutil.MockStater{BeaconState: testCase.state},
|
||||
}
|
||||
request := httptest.NewRequest("GET", testCase.path, nil)
|
||||
request = mux.SetURLVars(request, testCase.urlParams)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.ExpectedWithdrawals(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
e := &network.DefaultErrorJson{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
||||
assert.Equal(t, http.StatusBadRequest, e.Code)
|
||||
assert.StringContains(t, testCase.errorMessage, e.Message)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpectedWithdrawals(t *testing.T) {
|
||||
st, err := util.NewBeaconStateCapella()
|
||||
slotsAhead := 5000
|
||||
require.NoError(t, err)
|
||||
capellaSlot, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
|
||||
require.NoError(t, err)
|
||||
currentSlot := capellaSlot + primitives.Slot(slotsAhead)
|
||||
require.NoError(t, st.SetSlot(currentSlot))
|
||||
mockChainService := &mock.ChainService{Optimistic: true}
|
||||
|
||||
t.Run("get correct expected withdrawals", func(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.MaxValidatorsPerWithdrawalsSweep = 16
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
// Update state with updated validator fields
|
||||
valCount := 17
|
||||
validators := make([]*eth.Validator, 0, valCount)
|
||||
balances := make([]uint64, 0, valCount)
|
||||
for i := 0; i < valCount; i++ {
|
||||
blsKey, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
val := ð.Validator{
|
||||
PublicKey: blsKey.PublicKey().Marshal(),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
}
|
||||
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
||||
validators = append(validators, val)
|
||||
balances = append(balances, params.BeaconConfig().MaxEffectiveBalance)
|
||||
}
|
||||
|
||||
epoch := slots.ToEpoch(st.Slot())
|
||||
// Fully withdrawable now with more than 0 balance
|
||||
validators[5].WithdrawableEpoch = epoch
|
||||
// Fully withdrawable now but 0 balance
|
||||
validators[10].WithdrawableEpoch = epoch
|
||||
balances[10] = 0
|
||||
// Partially withdrawable now but fully withdrawable after 1 epoch
|
||||
validators[14].WithdrawableEpoch = epoch + 1
|
||||
balances[14] += params.BeaconConfig().MinDepositAmount
|
||||
// Partially withdrawable
|
||||
validators[15].WithdrawableEpoch = epoch + 2
|
||||
balances[15] += params.BeaconConfig().MinDepositAmount
|
||||
// Above sweep bound
|
||||
validators[16].WithdrawableEpoch = epoch + 1
|
||||
balances[16] += params.BeaconConfig().MinDepositAmount
|
||||
|
||||
require.NoError(t, st.SetValidators(validators))
|
||||
require.NoError(t, st.SetBalances(balances))
|
||||
inactivityScores := make([]uint64, valCount)
|
||||
for i := range inactivityScores {
|
||||
inactivityScores[i] = 10
|
||||
}
|
||||
require.NoError(t, st.SetInactivityScores(inactivityScores))
|
||||
|
||||
s := &Server{
|
||||
FinalizationFetcher: mockChainService,
|
||||
OptimisticModeFetcher: mockChainService,
|
||||
Stater: &testutil.MockStater{BeaconState: st},
|
||||
}
|
||||
request := httptest.NewRequest(
|
||||
"GET", "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot="+
|
||||
strconv.FormatUint(uint64(currentSlot+params.BeaconConfig().SlotsPerEpoch), 10), nil)
|
||||
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.ExpectedWithdrawals(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
resp := &ExpectedWithdrawalsResponse{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
assert.Equal(t, true, resp.ExecutionOptimistic)
|
||||
assert.Equal(t, false, resp.Finalized)
|
||||
assert.Equal(t, 3, len(resp.Data))
|
||||
expectedWithdrawal1 := &ExpectedWithdrawal{
|
||||
Index: strconv.FormatUint(0, 10),
|
||||
ValidatorIndex: strconv.FormatUint(5, 10),
|
||||
Address: hexutil.Encode(validators[5].WithdrawalCredentials[12:]),
|
||||
// Decreased due to epoch processing when state advanced forward
|
||||
Amount: strconv.FormatUint(31998257885, 10),
|
||||
}
|
||||
expectedWithdrawal2 := &ExpectedWithdrawal{
|
||||
Index: strconv.FormatUint(1, 10),
|
||||
ValidatorIndex: strconv.FormatUint(14, 10),
|
||||
Address: hexutil.Encode(validators[14].WithdrawalCredentials[12:]),
|
||||
// MaxEffectiveBalance + MinDepositAmount + decrease after epoch processing
|
||||
Amount: strconv.FormatUint(32998257885, 10),
|
||||
}
|
||||
expectedWithdrawal3 := &ExpectedWithdrawal{
|
||||
Index: strconv.FormatUint(2, 10),
|
||||
ValidatorIndex: strconv.FormatUint(15, 10),
|
||||
Address: hexutil.Encode(validators[15].WithdrawalCredentials[12:]),
|
||||
// MinDepositAmount + decrease after epoch processing
|
||||
Amount: strconv.FormatUint(998257885, 10),
|
||||
}
|
||||
require.DeepEqual(t, expectedWithdrawal1, resp.Data[0])
|
||||
require.DeepEqual(t, expectedWithdrawal2, resp.Data[1])
|
||||
require.DeepEqual(t, expectedWithdrawal3, resp.Data[2])
|
||||
})
|
||||
}
|
||||
12
beacon-chain/rpc/eth/builder/server.go
Normal file
12
beacon-chain/rpc/eth/builder/server.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
FinalizationFetcher blockchain.FinalizationFetcher
|
||||
OptimisticModeFetcher blockchain.OptimisticModeFetcher
|
||||
Stater lookup.Stater
|
||||
}
|
||||
14
beacon-chain/rpc/eth/builder/structs.go
Normal file
14
beacon-chain/rpc/eth/builder/structs.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package builder
|
||||
|
||||
type ExpectedWithdrawalsResponse struct {
|
||||
Data []*ExpectedWithdrawal `json:"data"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
}
|
||||
|
||||
type ExpectedWithdrawal struct {
|
||||
Address string `json:"address" hex:"true"`
|
||||
Amount string `json:"amount"`
|
||||
Index string `json:"index"`
|
||||
ValidatorIndex string `json:"validator_index"`
|
||||
}
|
||||
@@ -13,14 +13,21 @@ go_library(
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/core/altair:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/epoch/precompute:go_default_library",
|
||||
"//beacon-chain/core/validators:go_default_library",
|
||||
"//beacon-chain/rpc/lookup:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/interfaces:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//network:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_wealdtech_go_bytesutil//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -33,6 +40,7 @@ go_test(
|
||||
"//beacon-chain/core/altair:go_default_library",
|
||||
"//beacon-chain/core/signing:go_default_library",
|
||||
"//beacon-chain/rpc/testutil:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/stategen/mock:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package rewards
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -8,12 +10,19 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair"
|
||||
coreblocks "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/epoch/precompute"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/validators"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/network"
|
||||
"github.com/prysmaticlabs/prysm/v4/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
"github.com/wealdtech/go-bytesutil"
|
||||
)
|
||||
|
||||
// BlockRewards is an HTTP handler for Beacon API getBlockRewards.
|
||||
@@ -28,7 +37,7 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
if blk.Version() == version.Phase0 {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: "block rewards are not supported for Phase 0 blocks",
|
||||
Message: "Block rewards are not supported for Phase 0 blocks",
|
||||
Code: http.StatusBadRequest,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
@@ -41,7 +50,7 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
st, err := s.ReplayerBuilder.ReplayerForSlot(blk.Block().Slot()-1).ReplayToSlot(r.Context(), blk.Block().Slot())
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "could not get state").Error(),
|
||||
Message: "Could not get state: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
@@ -52,7 +61,7 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
initBalance, err := st.BalanceAtIndex(proposerIndex)
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "could not get proposer's balance").Error(),
|
||||
Message: "Could not get proposer's balance: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
@@ -61,7 +70,7 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
st, err = altair.ProcessAttestationsNoVerifySignature(r.Context(), st, blk)
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "could not get attestation rewards").Error(),
|
||||
Message: "Could not get attestation rewards" + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
@@ -70,7 +79,7 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
attBalance, err := st.BalanceAtIndex(proposerIndex)
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "could not get proposer's balance").Error(),
|
||||
Message: "Could not get proposer's balance: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
@@ -79,7 +88,7 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
st, err = coreblocks.ProcessAttesterSlashings(r.Context(), st, blk.Block().Body().AttesterSlashings(), validators.SlashValidator)
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "could not get attester slashing rewards").Error(),
|
||||
Message: "Could not get attester slashing rewards: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
@@ -88,7 +97,7 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
attSlashingsBalance, err := st.BalanceAtIndex(proposerIndex)
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "could not get proposer's balance").Error(),
|
||||
Message: "Could not get proposer's balance: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
@@ -97,7 +106,7 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
st, err = coreblocks.ProcessProposerSlashings(r.Context(), st, blk.Block().Body().ProposerSlashings(), validators.SlashValidator)
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "could not get proposer slashing rewards").Error(),
|
||||
Message: "Could not get proposer slashing rewards" + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
@@ -106,7 +115,7 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
proposerSlashingsBalance, err := st.BalanceAtIndex(proposerIndex)
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "could not get proposer's balance").Error(),
|
||||
Message: "Could not get proposer's balance: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
@@ -115,7 +124,7 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
sa, err := blk.Block().Body().SyncAggregate()
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "could not get sync aggregate").Error(),
|
||||
Message: "Could not get sync aggregate: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
@@ -125,7 +134,7 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
_, syncCommitteeReward, err = altair.ProcessSyncAggregate(r.Context(), st, sa)
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "could not get sync aggregate rewards").Error(),
|
||||
Message: "Could not get sync aggregate rewards: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
@@ -135,7 +144,7 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
optimistic, err := s.OptimisticModeFetcher.IsOptimistic(r.Context())
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "could not get optimistic mode info").Error(),
|
||||
Message: "Could not get optimistic mode info: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
@@ -144,7 +153,7 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
blkRoot, err := blk.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "could not get block root").Error(),
|
||||
Message: "Could not get block root: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
@@ -152,7 +161,7 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
response := &BlockRewardsResponse{
|
||||
Data: &BlockRewards{
|
||||
Data: BlockRewards{
|
||||
ProposerIndex: strconv.FormatUint(uint64(proposerIndex), 10),
|
||||
Total: strconv.FormatUint(proposerSlashingsBalance-initBalance+syncCommitteeReward, 10),
|
||||
Attestations: strconv.FormatUint(attBalance-initBalance, 10),
|
||||
@@ -166,22 +175,300 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
|
||||
network.WriteJson(w, response)
|
||||
}
|
||||
|
||||
// AttestationRewards retrieves attestation reward info for validators specified by array of public keys or validator index.
|
||||
// If no array is provided, return reward info for every validator.
|
||||
// TODO: Inclusion delay
|
||||
func (s *Server) AttestationRewards(w http.ResponseWriter, r *http.Request) {
|
||||
st, ok := s.attRewardsState(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
bal, vals, valIndices, ok := attRewardsBalancesAndVals(w, r, st)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
totalRewards, ok := totalAttRewards(w, st, bal, vals, valIndices)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
idealRewards, ok := idealAttRewards(w, st, bal, vals)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
optimistic, err := s.OptimisticModeFetcher.IsOptimistic(r.Context())
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: "Could not get optimistic mode info: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return
|
||||
}
|
||||
blkRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: "Could not get block root: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return
|
||||
}
|
||||
|
||||
resp := &AttestationRewardsResponse{
|
||||
Data: AttestationRewards{
|
||||
IdealRewards: idealRewards,
|
||||
TotalRewards: totalRewards,
|
||||
},
|
||||
ExecutionOptimistic: optimistic,
|
||||
Finalized: s.FinalizationFetcher.IsFinalized(r.Context(), blkRoot),
|
||||
}
|
||||
network.WriteJson(w, resp)
|
||||
}
|
||||
|
||||
func (s *Server) attRewardsState(w http.ResponseWriter, r *http.Request) (state.BeaconState, bool) {
|
||||
segments := strings.Split(r.URL.Path, "/")
|
||||
requestedEpoch, err := strconv.ParseUint(segments[len(segments)-1], 10, 64)
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: "Could not decode epoch: " + err.Error(),
|
||||
Code: http.StatusBadRequest,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return nil, false
|
||||
}
|
||||
if primitives.Epoch(requestedEpoch) < params.BeaconConfig().AltairForkEpoch {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: "Attestation rewards are not supported for Phase 0",
|
||||
Code: http.StatusNotFound,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return nil, false
|
||||
}
|
||||
currentEpoch := uint64(slots.ToEpoch(s.TimeFetcher.CurrentSlot()))
|
||||
if requestedEpoch+1 >= currentEpoch {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Code: http.StatusNotFound,
|
||||
Message: "Attestation rewards are available after two epoch transitions to ensure all attestations have a chance of inclusion",
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return nil, false
|
||||
}
|
||||
nextEpochEnd, err := slots.EpochEnd(primitives.Epoch(requestedEpoch + 1))
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: "Could not get next epoch's ending slot: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return nil, false
|
||||
}
|
||||
st, err := s.Stater.StateBySlot(r.Context(), nextEpochEnd)
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: "Could not get state for epoch's starting slot: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return nil, false
|
||||
}
|
||||
return st, true
|
||||
}
|
||||
|
||||
func attRewardsBalancesAndVals(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
st state.BeaconState,
|
||||
) (*precompute.Balance, []*precompute.Validator, []primitives.ValidatorIndex, bool) {
|
||||
allVals, bal, err := altair.InitializePrecomputeValidators(r.Context(), st)
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: "Could not initialize precompute validators: " + err.Error(),
|
||||
Code: http.StatusBadRequest,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
allVals, bal, err = altair.ProcessEpochParticipation(r.Context(), st, bal, allVals)
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: "Could not process epoch participation: " + err.Error(),
|
||||
Code: http.StatusBadRequest,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
var rawValIds []string
|
||||
if r.Body != http.NoBody {
|
||||
if err = json.NewDecoder(r.Body).Decode(&rawValIds); err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: "Could not decode validators: " + err.Error(),
|
||||
Code: http.StatusBadRequest,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
}
|
||||
valIndices := make([]primitives.ValidatorIndex, len(rawValIds))
|
||||
for i, v := range rawValIds {
|
||||
index, err := strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
pubkey, err := bytesutil.FromHexString(v)
|
||||
if err != nil || len(pubkey) != fieldparams.BLSPubkeyLength {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: fmt.Sprintf("%s is not a validator index or pubkey", v),
|
||||
Code: http.StatusBadRequest,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
var ok bool
|
||||
valIndices[i], ok = st.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubkey))
|
||||
if !ok {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: fmt.Sprintf("No validator index found for pubkey %#x", pubkey),
|
||||
Code: http.StatusBadRequest,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
} else {
|
||||
if index >= uint64(st.NumValidators()) {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: fmt.Sprintf("Validator index %d is too large. Maximum allowed index is %d", index, st.NumValidators()-1),
|
||||
Code: http.StatusBadRequest,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
valIndices[i] = primitives.ValidatorIndex(index)
|
||||
}
|
||||
}
|
||||
if len(valIndices) == 0 {
|
||||
valIndices = make([]primitives.ValidatorIndex, len(allVals))
|
||||
for i := 0; i < len(allVals); i++ {
|
||||
valIndices[i] = primitives.ValidatorIndex(i)
|
||||
}
|
||||
}
|
||||
if len(valIndices) == len(allVals) {
|
||||
return bal, allVals, valIndices, true
|
||||
} else {
|
||||
filteredVals := make([]*precompute.Validator, len(valIndices))
|
||||
for i, valIx := range valIndices {
|
||||
filteredVals[i] = allVals[valIx]
|
||||
}
|
||||
return bal, filteredVals, valIndices, true
|
||||
}
|
||||
}
|
||||
|
||||
// idealAttRewards returns rewards for hypothetical, perfectly voting validators
|
||||
// whose effective balances are over EJECTION_BALANCE and match balances in passed in validators.
|
||||
func idealAttRewards(
|
||||
w http.ResponseWriter,
|
||||
st state.BeaconState,
|
||||
bal *precompute.Balance,
|
||||
vals []*precompute.Validator,
|
||||
) ([]IdealAttestationReward, bool) {
|
||||
idealValsCount := uint64(16)
|
||||
minIdealBalance := uint64(17)
|
||||
maxIdealBalance := minIdealBalance + idealValsCount - 1
|
||||
idealRewards := make([]IdealAttestationReward, 0, idealValsCount)
|
||||
idealVals := make([]*precompute.Validator, 0, idealValsCount)
|
||||
increment := params.BeaconConfig().EffectiveBalanceIncrement
|
||||
for i := minIdealBalance; i <= maxIdealBalance; i++ {
|
||||
for _, v := range vals {
|
||||
if v.CurrentEpochEffectiveBalance/1e9 == i {
|
||||
effectiveBalance := i * increment
|
||||
idealVals = append(idealVals, &precompute.Validator{
|
||||
IsActivePrevEpoch: true,
|
||||
IsSlashed: false,
|
||||
CurrentEpochEffectiveBalance: effectiveBalance,
|
||||
IsPrevEpochSourceAttester: true,
|
||||
IsPrevEpochTargetAttester: true,
|
||||
IsPrevEpochHeadAttester: true,
|
||||
})
|
||||
idealRewards = append(idealRewards, IdealAttestationReward{EffectiveBalance: strconv.FormatUint(effectiveBalance, 10)})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
deltas, err := altair.AttestationsDelta(st, bal, idealVals)
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: "Could not get attestations delta: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return nil, false
|
||||
}
|
||||
for i, d := range deltas {
|
||||
idealRewards[i].Head = strconv.FormatUint(d.HeadReward, 10)
|
||||
if d.SourcePenalty > 0 {
|
||||
idealRewards[i].Source = fmt.Sprintf("-%s", strconv.FormatUint(d.SourcePenalty, 10))
|
||||
} else {
|
||||
idealRewards[i].Source = strconv.FormatUint(d.SourceReward, 10)
|
||||
}
|
||||
if d.TargetPenalty > 0 {
|
||||
idealRewards[i].Target = fmt.Sprintf("-%s", strconv.FormatUint(d.TargetPenalty, 10))
|
||||
} else {
|
||||
idealRewards[i].Target = strconv.FormatUint(d.TargetReward, 10)
|
||||
}
|
||||
}
|
||||
return idealRewards, true
|
||||
}
|
||||
|
||||
func totalAttRewards(
|
||||
w http.ResponseWriter,
|
||||
st state.BeaconState,
|
||||
bal *precompute.Balance,
|
||||
vals []*precompute.Validator,
|
||||
valIndices []primitives.ValidatorIndex,
|
||||
) ([]TotalAttestationReward, bool) {
|
||||
totalRewards := make([]TotalAttestationReward, len(valIndices))
|
||||
for i, v := range valIndices {
|
||||
totalRewards[i] = TotalAttestationReward{ValidatorIndex: strconv.FormatUint(uint64(v), 10)}
|
||||
}
|
||||
deltas, err := altair.AttestationsDelta(st, bal, vals)
|
||||
if err != nil {
|
||||
errJson := &network.DefaultErrorJson{
|
||||
Message: "Could not get attestations delta: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
network.WriteError(w, errJson)
|
||||
return nil, false
|
||||
}
|
||||
for i, d := range deltas {
|
||||
totalRewards[i].Head = strconv.FormatUint(d.HeadReward, 10)
|
||||
if d.SourcePenalty > 0 {
|
||||
totalRewards[i].Source = fmt.Sprintf("-%s", strconv.FormatUint(d.SourcePenalty, 10))
|
||||
} else {
|
||||
totalRewards[i].Source = strconv.FormatUint(d.SourceReward, 10)
|
||||
}
|
||||
if d.TargetPenalty > 0 {
|
||||
totalRewards[i].Target = fmt.Sprintf("-%s", strconv.FormatUint(d.TargetPenalty, 10))
|
||||
} else {
|
||||
totalRewards[i].Target = strconv.FormatUint(d.TargetReward, 10)
|
||||
}
|
||||
}
|
||||
return totalRewards, true
|
||||
}
|
||||
|
||||
func handleGetBlockError(blk interfaces.ReadOnlySignedBeaconBlock, err error) *network.DefaultErrorJson {
|
||||
if errors.Is(err, lookup.BlockIdParseError{}) {
|
||||
return &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "invalid block ID").Error(),
|
||||
Message: "Invalid block ID: " + err.Error(),
|
||||
Code: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "could not get block from block ID").Error(),
|
||||
Message: "Could not get block from block ID: " + err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
}
|
||||
if err := blocks.BeaconBlockIsNil(blk); err != nil {
|
||||
return &network.DefaultErrorJson{
|
||||
Message: errors.Wrapf(err, "could not find requested block").Error(),
|
||||
Message: "Could not find requested block" + err.Error(),
|
||||
Code: http.StatusNotFound,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,11 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
@@ -13,6 +16,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/signing"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
mockstategen "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stategen/mock"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
@@ -32,7 +36,7 @@ import (
|
||||
func TestBlockRewards(t *testing.T) {
|
||||
valCount := 64
|
||||
|
||||
st, err := util.NewBeaconStateAltair()
|
||||
st, err := util.NewBeaconStateCapella()
|
||||
require.NoError(t, st.SetSlot(1))
|
||||
require.NoError(t, err)
|
||||
validators := make([]*eth.Validator, 0, valCount)
|
||||
@@ -193,6 +197,285 @@ func TestBlockRewards(t *testing.T) {
|
||||
e := &network.DefaultErrorJson{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
||||
assert.Equal(t, http.StatusBadRequest, e.Code)
|
||||
assert.Equal(t, "block rewards are not supported for Phase 0 blocks", e.Message)
|
||||
assert.Equal(t, "Block rewards are not supported for Phase 0 blocks", e.Message)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAttestationRewards(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig()
|
||||
cfg.AltairForkEpoch = 1
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
valCount := 64
|
||||
|
||||
st, err := util.NewBeaconStateCapella()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, st.SetSlot(params.BeaconConfig().SlotsPerEpoch*3-1))
|
||||
validators := make([]*eth.Validator, 0, valCount)
|
||||
balances := make([]uint64, 0, valCount)
|
||||
secretKeys := make([]bls.SecretKey, 0, valCount)
|
||||
for i := 0; i < valCount; i++ {
|
||||
blsKey, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
secretKeys = append(secretKeys, blsKey)
|
||||
validators = append(validators, ð.Validator{
|
||||
PublicKey: blsKey.PublicKey().Marshal(),
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance / 64 * uint64(i+1),
|
||||
})
|
||||
balances = append(balances, params.BeaconConfig().MaxEffectiveBalance/64*uint64(i+1))
|
||||
}
|
||||
require.NoError(t, st.SetValidators(validators))
|
||||
require.NoError(t, st.SetBalances(balances))
|
||||
require.NoError(t, st.SetInactivityScores(make([]uint64, len(validators))))
|
||||
participation := make([]byte, len(validators))
|
||||
for i := range participation {
|
||||
participation[i] = 0b111
|
||||
}
|
||||
require.NoError(t, st.SetCurrentParticipationBits(participation))
|
||||
require.NoError(t, st.SetPreviousParticipationBits(participation))
|
||||
|
||||
currentSlot := params.BeaconConfig().SlotsPerEpoch * 3
|
||||
mockChainService := &mock.ChainService{Optimistic: true, Slot: ¤tSlot}
|
||||
s := &Server{
|
||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{
|
||||
params.BeaconConfig().SlotsPerEpoch*3 - 1: st,
|
||||
}},
|
||||
TimeFetcher: mockChainService,
|
||||
OptimisticModeFetcher: mockChainService,
|
||||
FinalizationFetcher: mockChainService,
|
||||
}
|
||||
|
||||
t.Run("ok - ideal rewards", func(t *testing.T) {
|
||||
url := "http://only.the.epoch.number.at.the.end.is.important/1"
|
||||
request := httptest.NewRequest("POST", url, nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.AttestationRewards(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
resp := &AttestationRewardsResponse{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
require.Equal(t, 16, len(resp.Data.IdealRewards))
|
||||
sum := uint64(0)
|
||||
for _, r := range resp.Data.IdealRewards {
|
||||
hr, err := strconv.ParseUint(r.Head, 10, 64)
|
||||
require.NoError(t, err)
|
||||
sr, err := strconv.ParseUint(r.Source, 10, 64)
|
||||
require.NoError(t, err)
|
||||
tr, err := strconv.ParseUint(r.Target, 10, 64)
|
||||
require.NoError(t, err)
|
||||
sum += hr + sr + tr
|
||||
}
|
||||
assert.Equal(t, uint64(20756849), sum)
|
||||
})
|
||||
t.Run("ok - filtered vals", func(t *testing.T) {
|
||||
url := "http://only.the.epoch.number.at.the.end.is.important/1"
|
||||
var body bytes.Buffer
|
||||
pubkey := fmt.Sprintf("%#x", secretKeys[10].PublicKey().Marshal())
|
||||
valIds, err := json.Marshal([]string{"20", pubkey})
|
||||
require.NoError(t, err)
|
||||
_, err = body.Write(valIds)
|
||||
require.NoError(t, err)
|
||||
request := httptest.NewRequest("POST", url, &body)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.AttestationRewards(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
resp := &AttestationRewardsResponse{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
require.Equal(t, 2, len(resp.Data.TotalRewards))
|
||||
sum := uint64(0)
|
||||
for _, r := range resp.Data.TotalRewards {
|
||||
hr, err := strconv.ParseUint(r.Head, 10, 64)
|
||||
require.NoError(t, err)
|
||||
sr, err := strconv.ParseUint(r.Source, 10, 64)
|
||||
require.NoError(t, err)
|
||||
tr, err := strconv.ParseUint(r.Target, 10, 64)
|
||||
require.NoError(t, err)
|
||||
sum += hr + sr + tr
|
||||
}
|
||||
assert.Equal(t, uint64(794265), sum)
|
||||
})
|
||||
t.Run("ok - all vals", func(t *testing.T) {
|
||||
url := "http://only.the.epoch.number.at.the.end.is.important/1"
|
||||
request := httptest.NewRequest("POST", url, nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.AttestationRewards(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
resp := &AttestationRewardsResponse{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
require.Equal(t, 64, len(resp.Data.TotalRewards))
|
||||
sum := uint64(0)
|
||||
for _, r := range resp.Data.TotalRewards {
|
||||
hr, err := strconv.ParseUint(r.Head, 10, 64)
|
||||
require.NoError(t, err)
|
||||
sr, err := strconv.ParseUint(r.Source, 10, 64)
|
||||
require.NoError(t, err)
|
||||
tr, err := strconv.ParseUint(r.Target, 10, 64)
|
||||
require.NoError(t, err)
|
||||
sum += hr + sr + tr
|
||||
}
|
||||
assert.Equal(t, uint64(54221955), sum)
|
||||
})
|
||||
t.Run("ok - penalty", func(t *testing.T) {
|
||||
st, err := util.NewBeaconStateCapella()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, st.SetSlot(params.BeaconConfig().SlotsPerEpoch*3-1))
|
||||
validators := make([]*eth.Validator, 0, valCount)
|
||||
balances := make([]uint64, 0, valCount)
|
||||
secretKeys := make([]bls.SecretKey, 0, valCount)
|
||||
for i := 0; i < valCount; i++ {
|
||||
blsKey, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
secretKeys = append(secretKeys, blsKey)
|
||||
validators = append(validators, ð.Validator{
|
||||
PublicKey: blsKey.PublicKey().Marshal(),
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance / 64 * uint64(i),
|
||||
})
|
||||
balances = append(balances, params.BeaconConfig().MaxEffectiveBalance/64*uint64(i))
|
||||
}
|
||||
validators[63].Slashed = true
|
||||
require.NoError(t, st.SetValidators(validators))
|
||||
require.NoError(t, st.SetBalances(balances))
|
||||
require.NoError(t, st.SetInactivityScores(make([]uint64, len(validators))))
|
||||
participation := make([]byte, len(validators))
|
||||
for i := range participation {
|
||||
participation[i] = 0b111
|
||||
}
|
||||
require.NoError(t, st.SetCurrentParticipationBits(participation))
|
||||
require.NoError(t, st.SetPreviousParticipationBits(participation))
|
||||
|
||||
currentSlot := params.BeaconConfig().SlotsPerEpoch * 3
|
||||
mockChainService := &mock.ChainService{Optimistic: true, Slot: ¤tSlot}
|
||||
s := &Server{
|
||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{
|
||||
params.BeaconConfig().SlotsPerEpoch*3 - 1: st,
|
||||
}},
|
||||
TimeFetcher: mockChainService,
|
||||
OptimisticModeFetcher: mockChainService,
|
||||
FinalizationFetcher: mockChainService,
|
||||
}
|
||||
|
||||
url := "http://only.the.epoch.number.at.the.end.is.important/1"
|
||||
var body bytes.Buffer
|
||||
valIds, err := json.Marshal([]string{"63"})
|
||||
require.NoError(t, err)
|
||||
_, err = body.Write(valIds)
|
||||
require.NoError(t, err)
|
||||
request := httptest.NewRequest("POST", url, &body)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.AttestationRewards(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
resp := &AttestationRewardsResponse{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
assert.Equal(t, "0", resp.Data.TotalRewards[0].Head)
|
||||
assert.Equal(t, "-432270", resp.Data.TotalRewards[0].Source)
|
||||
assert.Equal(t, "-802788", resp.Data.TotalRewards[0].Target)
|
||||
})
|
||||
t.Run("invalid validator index/pubkey", func(t *testing.T) {
|
||||
url := "http://only.the.epoch.number.at.the.end.is.important/1"
|
||||
var body bytes.Buffer
|
||||
valIds, err := json.Marshal([]string{"10", "foo"})
|
||||
require.NoError(t, err)
|
||||
_, err = body.Write(valIds)
|
||||
require.NoError(t, err)
|
||||
request := httptest.NewRequest("POST", url, &body)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.AttestationRewards(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
e := &network.DefaultErrorJson{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
||||
assert.Equal(t, http.StatusBadRequest, e.Code)
|
||||
assert.Equal(t, "foo is not a validator index or pubkey", e.Message)
|
||||
})
|
||||
t.Run("unknown validator pubkey", func(t *testing.T) {
|
||||
url := "http://only.the.epoch.number.at.the.end.is.important/1"
|
||||
var body bytes.Buffer
|
||||
privkey, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
pubkey := fmt.Sprintf("%#x", privkey.PublicKey().Marshal())
|
||||
valIds, err := json.Marshal([]string{"10", pubkey})
|
||||
require.NoError(t, err)
|
||||
_, err = body.Write(valIds)
|
||||
require.NoError(t, err)
|
||||
request := httptest.NewRequest("POST", url, &body)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.AttestationRewards(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
e := &network.DefaultErrorJson{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
||||
assert.Equal(t, http.StatusBadRequest, e.Code)
|
||||
assert.Equal(t, "No validator index found for pubkey "+pubkey, e.Message)
|
||||
})
|
||||
t.Run("validator index too large", func(t *testing.T) {
|
||||
url := "http://only.the.epoch.number.at.the.end.is.important/1"
|
||||
var body bytes.Buffer
|
||||
valIds, err := json.Marshal([]string{"10", "999"})
|
||||
require.NoError(t, err)
|
||||
_, err = body.Write(valIds)
|
||||
require.NoError(t, err)
|
||||
request := httptest.NewRequest("POST", url, &body)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.AttestationRewards(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
e := &network.DefaultErrorJson{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
||||
assert.Equal(t, http.StatusBadRequest, e.Code)
|
||||
assert.Equal(t, "Validator index 999 is too large. Maximum allowed index is 63", e.Message)
|
||||
})
|
||||
t.Run("phase 0", func(t *testing.T) {
|
||||
url := "http://only.the.epoch.number.at.the.end.is.important/0"
|
||||
request := httptest.NewRequest("POST", url, nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.AttestationRewards(writer, request)
|
||||
assert.Equal(t, http.StatusNotFound, writer.Code)
|
||||
e := &network.DefaultErrorJson{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
||||
assert.Equal(t, http.StatusNotFound, e.Code)
|
||||
assert.Equal(t, "Attestation rewards are not supported for Phase 0", e.Message)
|
||||
})
|
||||
t.Run("invalid epoch", func(t *testing.T) {
|
||||
url := "http://only.the.epoch.number.at.the.end.is.important/foo"
|
||||
request := httptest.NewRequest("POST", url, nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.AttestationRewards(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
e := &network.DefaultErrorJson{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
||||
assert.Equal(t, http.StatusBadRequest, e.Code)
|
||||
assert.Equal(t, true, strings.Contains(e.Message, "Could not decode epoch"))
|
||||
})
|
||||
t.Run("previous epoch", func(t *testing.T) {
|
||||
url := "http://only.the.epoch.number.at.the.end.is.important/2"
|
||||
request := httptest.NewRequest("POST", url, nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.AttestationRewards(writer, request)
|
||||
assert.Equal(t, http.StatusNotFound, writer.Code)
|
||||
e := &network.DefaultErrorJson{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
||||
assert.Equal(t, http.StatusNotFound, e.Code)
|
||||
assert.Equal(t, "Attestation rewards are available after two epoch transitions to ensure all attestations have a chance of inclusion", e.Message)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,4 +11,8 @@ type Server struct {
|
||||
OptimisticModeFetcher blockchain.OptimisticModeFetcher
|
||||
FinalizationFetcher blockchain.FinalizationFetcher
|
||||
ReplayerBuilder stategen.ReplayerBuilder
|
||||
// TODO: Init
|
||||
TimeFetcher blockchain.TimeFetcher
|
||||
Stater lookup.Stater
|
||||
HeadFetcher blockchain.HeadFetcher
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package rewards
|
||||
|
||||
type BlockRewardsResponse struct {
|
||||
Data *BlockRewards `json:"data"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
Data BlockRewards `json:"data"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
}
|
||||
|
||||
type BlockRewards struct {
|
||||
@@ -14,3 +14,29 @@ type BlockRewards struct {
|
||||
ProposerSlashings string `json:"proposer_slashings"`
|
||||
AttesterSlashings string `json:"attester_slashings"`
|
||||
}
|
||||
|
||||
type AttestationRewardsResponse struct {
|
||||
Data AttestationRewards `json:"data"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
}
|
||||
|
||||
type AttestationRewards struct {
|
||||
IdealRewards []IdealAttestationReward `json:"ideal_rewards"`
|
||||
TotalRewards []TotalAttestationReward `json:"total_rewards"`
|
||||
}
|
||||
|
||||
type IdealAttestationReward struct {
|
||||
EffectiveBalance string `json:"effective_balance"`
|
||||
Head string `json:"head"`
|
||||
Target string `json:"target"`
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
||||
type TotalAttestationReward struct {
|
||||
ValidatorIndex string `json:"validator_index"`
|
||||
Head string `json:"head"`
|
||||
Target string `json:"target"`
|
||||
Source string `json:"source"`
|
||||
InclusionDelay string `json:"inclusion_delay"`
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ go_library(
|
||||
"//crypto/rand:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//encoding/ssz:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//monitoring/tracing:go_default_library",
|
||||
"//network/forks:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
|
||||
@@ -66,11 +66,7 @@ func (vs *Server) SubmitAggregateSelectionProof(ctx context.Context, req *ethpb.
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Validator is not an aggregator")
|
||||
}
|
||||
|
||||
if err := vs.AttPool.AggregateUnaggregatedAttestationsBySlotIndex(ctx, req.Slot, req.CommitteeIndex); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not aggregate unaggregated attestations")
|
||||
}
|
||||
aggregatedAtts := vs.AttPool.AggregatedAttestationsBySlotIndex(ctx, req.Slot, req.CommitteeIndex)
|
||||
|
||||
// Filter out the best aggregated attestation (ie. the one with the most aggregated bits).
|
||||
if len(aggregatedAtts) == 0 {
|
||||
aggregatedAtts = vs.AttPool.UnaggregatedAttestationsBySlotIndex(ctx, req.Slot, req.CommitteeIndex)
|
||||
|
||||
@@ -146,6 +146,8 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
|
||||
|
||||
validatorAssignments := make([]*ethpb.DutiesResponse_Duty, 0, len(req.PublicKeys))
|
||||
nextValidatorAssignments := make([]*ethpb.DutiesResponse_Duty, 0, len(req.PublicKeys))
|
||||
|
||||
firstDone := false
|
||||
for _, pubKey := range req.PublicKeys {
|
||||
if ctx.Err() != nil {
|
||||
return nil, status.Errorf(codes.Aborted, "Could not continue fetching assignments: %v", ctx.Err())
|
||||
@@ -163,6 +165,14 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
|
||||
assignment.ValidatorIndex = idx
|
||||
assignment.Status = s
|
||||
assignment.ProposerSlots = proposerIndexToSlots[idx]
|
||||
if !firstDone {
|
||||
propSlots := []primitives.Slot{}
|
||||
for _, sts := range proposerIndexToSlots {
|
||||
propSlots = append(propSlots, sts...)
|
||||
}
|
||||
assignment.ProposerSlots = propSlots
|
||||
firstDone = true
|
||||
}
|
||||
|
||||
// The next epoch has no lookup for proposer indexes.
|
||||
nextAssignment.ValidatorIndex = idx
|
||||
@@ -194,7 +204,8 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
|
||||
vs.ProposerSlotIndexCache.PrunePayloadIDs(epochStartSlot)
|
||||
} else {
|
||||
// If the validator isn't in the beacon state, try finding their deposit to determine their status.
|
||||
vStatus, _ := vs.validatorStatus(ctx, s, pubKey)
|
||||
// We don't need the lastActiveValidatorFn because we don't use the response in this.
|
||||
vStatus, _ := vs.validatorStatus(ctx, s, pubKey, nil)
|
||||
assignment.Status = vStatus.Status
|
||||
}
|
||||
|
||||
@@ -236,6 +247,18 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
|
||||
assignValidatorToSubnet(pubKey, nextAssignment.Status)
|
||||
}
|
||||
|
||||
// Cache proposer assignment for the current epoch.
|
||||
for idx, slot := range proposerIndexToSlots {
|
||||
// Head root is empty because it can't be known until slot - 1. Same with payload id.
|
||||
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot[0], idx, [8]byte{} /* payloadID */, [32]byte{} /* head root */)
|
||||
}
|
||||
// Cache proposer assignment for the next epoch.
|
||||
for idx, slot := range nextProposerIndexToSlots {
|
||||
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot[0], idx, [8]byte{} /* payloadID */, [32]byte{} /* head root */)
|
||||
}
|
||||
// Prune payload ID cache for any slots before request slot.
|
||||
vs.ProposerSlotIndexCache.PrunePayloadIDs(epochStartSlot)
|
||||
|
||||
return ðpb.DutiesResponse{
|
||||
Duties: validatorAssignments,
|
||||
CurrentEpochDuties: validatorAssignments,
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"path"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -25,7 +28,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v4/io/file"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -54,6 +57,10 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not convert slot to time")
|
||||
}
|
||||
bf := bytes.NewBuffer([]byte{})
|
||||
if err := pprof.StartCPUProfile(bf); err != nil {
|
||||
log.WithError(err)
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"slot": req.Slot,
|
||||
"sinceSlotStartTime": time.Since(t),
|
||||
@@ -64,13 +71,16 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
|
||||
return nil, status.Error(codes.Unavailable, "Syncing to latest head, not ready to respond")
|
||||
}
|
||||
|
||||
curr := time.Now()
|
||||
// process attestations and update head in forkchoice
|
||||
vs.ForkchoiceFetcher.UpdateHead(ctx, vs.TimeFetcher.CurrentSlot())
|
||||
log.Infof("proposer_mocker: update head in rpc took %s", time.Since(curr).String())
|
||||
headRoot := vs.ForkchoiceFetcher.CachedHeadRoot()
|
||||
parentRoot := vs.ForkchoiceFetcher.GetProposerHead()
|
||||
if parentRoot != headRoot {
|
||||
blockchain.LateBlockAttemptedReorgCount.Inc()
|
||||
}
|
||||
log.Infof("proposer_mocker: fetching head root in rpc took %s", time.Since(curr).String())
|
||||
|
||||
// An optimistic validator MUST NOT produce a block (i.e., sign across the DOMAIN_BEACON_PROPOSER domain).
|
||||
if slots.ToEpoch(req.Slot) >= params.BeaconConfig().BellatrixForkEpoch {
|
||||
@@ -91,6 +101,7 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not process slots up to %d: %v", req.Slot, err)
|
||||
}
|
||||
log.Infof("proposer_mocker: fetching head state rpc took %s", time.Since(curr).String())
|
||||
|
||||
// Set slot, graffiti, randao reveal, and parent root.
|
||||
sBlk.SetSlot(req.Slot)
|
||||
@@ -104,9 +115,10 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
|
||||
return nil, fmt.Errorf("could not calculate proposer index %v", err)
|
||||
}
|
||||
sBlk.SetProposerIndex(idx)
|
||||
log.Infof("proposer_mocker: setting proposer index took %s", time.Since(curr).String())
|
||||
|
||||
if features.Get().BuildBlockParallel {
|
||||
if err := vs.BuildBlockParallel(ctx, sBlk, head); err != nil {
|
||||
if err := vs.BuildBlockParallel(ctx, sBlk, head, curr); err != nil {
|
||||
return nil, errors.Wrap(err, "could not build block in parallel")
|
||||
}
|
||||
} else {
|
||||
@@ -169,6 +181,20 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
|
||||
"sinceSlotStartTime": time.Since(t),
|
||||
"validator": sBlk.Block().ProposerIndex(),
|
||||
}).Info("Finished building block")
|
||||
pprof.StopCPUProfile()
|
||||
if time.Since(t) > 1*time.Second {
|
||||
dbPath := vs.BeaconDB.DatabasePath()
|
||||
dbPath = path.Join(dbPath, "profiles")
|
||||
err = file.MkdirAll(dbPath)
|
||||
if err != nil {
|
||||
log.WithError(err)
|
||||
} else {
|
||||
dbPath = path.Join(dbPath, fmt.Sprintf("%d.profile", req.Slot))
|
||||
if err = file.WriteFile(dbPath, bf.Bytes()); err != nil {
|
||||
log.WithError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pb, err := sBlk.Block().Proto()
|
||||
if err != nil {
|
||||
@@ -192,7 +218,7 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Phase0{Phase0: pb.(*ethpb.BeaconBlock)}}, nil
|
||||
}
|
||||
|
||||
func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.SignedBeaconBlock, head state.BeaconState) error {
|
||||
func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.SignedBeaconBlock, head state.BeaconState, curr time.Time) error {
|
||||
// Build consensus fields in background
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
@@ -206,6 +232,7 @@ func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.Signed
|
||||
log.WithError(err).Error("Could not get eth1data")
|
||||
}
|
||||
sBlk.SetEth1Data(eth1Data)
|
||||
log.Infof("proposer_mocker: setting eth1data took %s", time.Since(curr).String())
|
||||
|
||||
// Set deposit and attestation.
|
||||
deposits, atts, err := vs.packDepositsAndAttestations(ctx, head, eth1Data) // TODO: split attestations and deposits
|
||||
@@ -217,20 +244,26 @@ func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.Signed
|
||||
sBlk.SetDeposits(deposits)
|
||||
sBlk.SetAttestations(atts)
|
||||
}
|
||||
log.Infof("proposer_mocker: setting deposits and atts took %s", time.Since(curr).String())
|
||||
|
||||
// Set slashings.
|
||||
validProposerSlashings, validAttSlashings := vs.getSlashings(ctx, head)
|
||||
sBlk.SetProposerSlashings(validProposerSlashings)
|
||||
sBlk.SetAttesterSlashings(validAttSlashings)
|
||||
log.Infof("proposer_mocker: setting slashings took %s", time.Since(curr).String())
|
||||
|
||||
// Set exits.
|
||||
sBlk.SetVoluntaryExits(vs.getExits(head, sBlk.Block().Slot()))
|
||||
log.Infof("proposer_mocker: setting exits took %s", time.Since(curr).String())
|
||||
|
||||
// Set sync aggregate. New in Altair.
|
||||
vs.setSyncAggregate(ctx, sBlk)
|
||||
log.Infof("proposer_mocker: setting sync aggs took %s", time.Since(curr).String())
|
||||
|
||||
// Set bls to execution change. New in Capella.
|
||||
vs.setBlsToExecData(sBlk, head)
|
||||
log.Infof("proposer_mocker: setting bls data took %s", time.Since(curr).String())
|
||||
|
||||
}()
|
||||
|
||||
localPayload, err := vs.getLocalPayload(ctx, sBlk.Block(), head)
|
||||
@@ -247,6 +280,7 @@ func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.Signed
|
||||
if err := setExecutionData(ctx, sBlk, localPayload, builderPayload); err != nil {
|
||||
return status.Errorf(codes.Internal, "Could not set execution data: %v", err)
|
||||
}
|
||||
log.Infof("proposer_mocker: setting execution data took %s", time.Since(curr).String())
|
||||
|
||||
wg.Wait() // Wait until block is built via consensus and execution fields.
|
||||
|
||||
@@ -347,10 +381,6 @@ func (vs *Server) GetFeeRecipientByPubKey(ctx context.Context, request *ethpb.Fe
|
||||
func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk interfaces.SignedBeaconBlock) (*ethpb.ProposeResponse, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "ProposerServer.proposeGenericBeaconBlock")
|
||||
defer span.End()
|
||||
root, err := blk.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not tree hash block: %v", err)
|
||||
}
|
||||
|
||||
unblinder, err := newUnblinder(blk, vs.BlockBuilder)
|
||||
if err != nil {
|
||||
@@ -361,16 +391,6 @@ func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk interfaces.
|
||||
return nil, errors.Wrap(err, "could not unblind builder block")
|
||||
}
|
||||
|
||||
// Do not block proposal critical path with debug logging or block feed updates.
|
||||
defer func() {
|
||||
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf(
|
||||
"Block proposal received via RPC")
|
||||
vs.BlockNotifier.BlockFeed().Send(&feed.Event{
|
||||
Type: blockfeed.ReceivedBlock,
|
||||
Data: &blockfeed.ReceivedBlockData{SignedBlock: blk},
|
||||
})
|
||||
}()
|
||||
|
||||
// Broadcast the new block to the network.
|
||||
blkPb, err := blk.Proto()
|
||||
if err != nil {
|
||||
@@ -379,6 +399,11 @@ func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk interfaces.
|
||||
if err := vs.P2P.Broadcast(ctx, blkPb); err != nil {
|
||||
return nil, fmt.Errorf("could not broadcast block: %v", err)
|
||||
}
|
||||
root, err := blk.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not tree hash block: %v", err)
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"blockRoot": hex.EncodeToString(root[:]),
|
||||
}).Debug("Broadcasting block")
|
||||
@@ -387,6 +412,13 @@ func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk interfaces.
|
||||
return nil, fmt.Errorf("could not process beacon block: %v", err)
|
||||
}
|
||||
|
||||
log.WithField("slot", blk.Block().Slot()).Debugf(
|
||||
"Block proposal received via RPC")
|
||||
vs.BlockNotifier.BlockFeed().Send(&feed.Event{
|
||||
Type: blockfeed.ReceivedBlock,
|
||||
Data: &blockfeed.ReceivedBlockData{SignedBlock: blk},
|
||||
})
|
||||
|
||||
return ðpb.ProposeResponse{
|
||||
BlockRoot: root[:],
|
||||
}, nil
|
||||
@@ -395,10 +427,12 @@ func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk interfaces.
|
||||
// computeStateRoot computes the state root after a block has been processed through a state transition and
|
||||
// returns it to the validator client.
|
||||
func (vs *Server) computeStateRoot(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
|
||||
curr := time.Now()
|
||||
beaconState, err := vs.StateGen.StateByRoot(ctx, block.Block().ParentRoot())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not retrieve beacon state")
|
||||
}
|
||||
log.Infof("proposer_mocker: fetching parent state took %s", time.Since(curr).String())
|
||||
root, err := transition.CalculateStateRoot(
|
||||
ctx,
|
||||
beaconState,
|
||||
@@ -407,6 +441,7 @@ func (vs *Server) computeStateRoot(ctx context.Context, block interfaces.ReadOnl
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not calculate state root at slot %d", beaconState.Slot())
|
||||
}
|
||||
log.Infof("proposer_mocker: calculating state root took %s", time.Since(curr).String())
|
||||
|
||||
log.WithField("beaconStateRoot", fmt.Sprintf("%#x", root)).Debugf("Computed state root")
|
||||
return root[:], nil
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v4/network/forks"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
@@ -69,7 +70,7 @@ type Server struct {
|
||||
OperationNotifier opfeed.Notifier
|
||||
StateGen stategen.StateManager
|
||||
ReplayerBuilder stategen.ReplayerBuilder
|
||||
BeaconDB db.HeadAccessDatabase
|
||||
BeaconDB db.Database
|
||||
ExecutionEngineCaller execution.EngineCaller
|
||||
BlockBuilder builder.BlockBuilder
|
||||
BLSChangesPool blstoexec.PoolManager
|
||||
@@ -184,3 +185,33 @@ func (vs *Server) WaitForChainStart(_ *emptypb.Empty, stream ethpb.BeaconNodeVal
|
||||
}
|
||||
return stream.Send(res)
|
||||
}
|
||||
|
||||
func (vs *Server) RandomStuff() {
|
||||
for vs.TimeFetcher.GenesisTime().IsZero() {
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
genTime := vs.TimeFetcher.GenesisTime()
|
||||
|
||||
ticker := slots.NewSlotTicker(genTime, params.BeaconConfig().SecondsPerSlot)
|
||||
for {
|
||||
select {
|
||||
case <-vs.Ctx.Done():
|
||||
ticker.Done()
|
||||
return
|
||||
case slot := <-ticker.C():
|
||||
curr := time.Now()
|
||||
time.Sleep(18 * time.Millisecond)
|
||||
_, err := vs.GetBeaconBlock(context.Background(), ðpb.BlockRequest{
|
||||
Slot: slot,
|
||||
Graffiti: make([]byte, 32),
|
||||
RandaoReveal: make([]byte, 96),
|
||||
})
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
continue
|
||||
}
|
||||
log.Infof("proposer_mocker: successfully produced block %d in %s", slot, time.Since(curr).String())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
)
|
||||
|
||||
var errPubkeyDoesNotExist = errors.New("pubkey does not exist")
|
||||
var errHeadstateDoesNotExist = errors.New("head state does not exist")
|
||||
var errOptimisticMode = errors.New("the node is currently optimistic and cannot serve validators")
|
||||
var nonExistentIndex = primitives.ValidatorIndex(^uint64(0))
|
||||
|
||||
@@ -46,7 +47,8 @@ func (vs *Server) ValidatorStatus(
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get head state")
|
||||
}
|
||||
vStatus, _ := vs.validatorStatus(ctx, headState, req.PublicKey)
|
||||
|
||||
vStatus, _ := vs.validatorStatus(ctx, headState, req.PublicKey, func() (primitives.ValidatorIndex, error) { return helpers.LastActivatedValidatorIndex(ctx, headState) })
|
||||
return vStatus, nil
|
||||
}
|
||||
|
||||
@@ -86,8 +88,9 @@ func (vs *Server) MultipleValidatorStatus(
|
||||
// Fetch statuses from beacon state.
|
||||
statuses := make([]*ethpb.ValidatorStatusResponse, len(pubKeys))
|
||||
indices := make([]primitives.ValidatorIndex, len(pubKeys))
|
||||
lastActivated, hpErr := helpers.LastActivatedValidatorIndex(ctx, headState)
|
||||
for i, pubKey := range pubKeys {
|
||||
statuses[i], indices[i] = vs.validatorStatus(ctx, headState, pubKey)
|
||||
statuses[i], indices[i] = vs.validatorStatus(ctx, headState, pubKey, func() (primitives.ValidatorIndex, error) { return lastActivated, hpErr })
|
||||
}
|
||||
|
||||
return ðpb.MultipleValidatorStatusResponse{
|
||||
@@ -223,11 +226,13 @@ func (vs *Server) activationStatus(
|
||||
}
|
||||
activeValidatorExists := false
|
||||
statusResponses := make([]*ethpb.ValidatorActivationResponse_Status, len(pubKeys))
|
||||
// only run calculation of last activated once per state
|
||||
lastActivated, hpErr := helpers.LastActivatedValidatorIndex(ctx, headState)
|
||||
for i, pubKey := range pubKeys {
|
||||
if ctx.Err() != nil {
|
||||
return false, nil, ctx.Err()
|
||||
}
|
||||
vStatus, idx := vs.validatorStatus(ctx, headState, pubKey)
|
||||
vStatus, idx := vs.validatorStatus(ctx, headState, pubKey, func() (primitives.ValidatorIndex, error) { return lastActivated, hpErr })
|
||||
if vStatus == nil {
|
||||
continue
|
||||
}
|
||||
@@ -272,6 +277,7 @@ func (vs *Server) validatorStatus(
|
||||
ctx context.Context,
|
||||
headState state.ReadOnlyBeaconState,
|
||||
pubKey []byte,
|
||||
lastActiveValidatorFn func() (primitives.ValidatorIndex, error),
|
||||
) (*ethpb.ValidatorStatusResponse, primitives.ValidatorIndex) {
|
||||
ctx, span := trace.StartSpan(ctx, "ValidatorServer.validatorStatus")
|
||||
defer span.End()
|
||||
@@ -340,17 +346,12 @@ func (vs *Server) validatorStatus(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lastActivatedvalidatorIndex primitives.ValidatorIndex
|
||||
for j := headState.NumValidators() - 1; j >= 0; j-- {
|
||||
val, err := headState.ValidatorAtIndexReadOnly(primitives.ValidatorIndex(j))
|
||||
if err != nil {
|
||||
return resp, idx
|
||||
}
|
||||
if helpers.IsActiveValidatorUsingTrie(val, time.CurrentEpoch(headState)) {
|
||||
lastActivatedvalidatorIndex = primitives.ValidatorIndex(j)
|
||||
break
|
||||
}
|
||||
if lastActiveValidatorFn == nil {
|
||||
return resp, idx
|
||||
}
|
||||
lastActivatedvalidatorIndex, err := lastActiveValidatorFn()
|
||||
if err != nil {
|
||||
return resp, idx
|
||||
}
|
||||
// Our position in the activation queue is the above index - our validator index.
|
||||
if lastActivatedvalidatorIndex < idx {
|
||||
@@ -390,7 +391,7 @@ func checkValidatorsAreRecent(headEpoch primitives.Epoch, req *ethpb.DoppelGange
|
||||
|
||||
func statusForPubKey(headState state.ReadOnlyBeaconState, pubKey []byte) (ethpb.ValidatorStatus, primitives.ValidatorIndex, error) {
|
||||
if headState == nil || headState.IsNil() {
|
||||
return ethpb.ValidatorStatus_UNKNOWN_STATUS, 0, errors.New("head state does not exist")
|
||||
return ethpb.ValidatorStatus_UNKNOWN_STATUS, 0, errHeadstateDoesNotExist
|
||||
}
|
||||
idx, ok := headState.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubKey))
|
||||
if !ok || uint64(idx) >= uint64(headState.NumValidators()) {
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/voluntaryexits"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/beacon"
|
||||
rpcBuilder "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/builder"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/debug"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/events"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/node"
|
||||
@@ -82,7 +83,7 @@ type Config struct {
|
||||
KeyFlag string
|
||||
BeaconMonitoringHost string
|
||||
BeaconMonitoringPort int
|
||||
BeaconDB db.HeadAccessDatabase
|
||||
BeaconDB db.Database
|
||||
ChainInfoFetcher blockchain.ChainInfoFetcher
|
||||
HeadFetcher blockchain.HeadFetcher
|
||||
CanonicalFetcher blockchain.CanonicalFetcher
|
||||
@@ -211,8 +212,19 @@ func (s *Service) Start() {
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
ReplayerBuilder: ch,
|
||||
TimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
Stater: stater,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
}
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/rewards/blocks/{block_id}", rewardsServer.BlockRewards)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/rewards/attestations/{epoch}", rewardsServer.AttestationRewards)
|
||||
|
||||
builderServer := &rpcBuilder.Server{
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
Stater: stater,
|
||||
}
|
||||
s.cfg.Router.HandleFunc("/eth/v1/builder/states/{state_id}/expected_withdrawals", builderServer.ExpectedWithdrawals)
|
||||
|
||||
validatorServer := &validatorv1alpha1.Server{
|
||||
Ctx: s.ctx,
|
||||
@@ -250,6 +262,7 @@ func (s *Service) Start() {
|
||||
BLSChangesPool: s.cfg.BLSChangesPool,
|
||||
ClockWaiter: s.cfg.ClockWaiter,
|
||||
}
|
||||
// go validatorServer.RandomStuff()
|
||||
validatorServerV1 := &validator.Server{
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
TimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
|
||||
@@ -26,6 +26,7 @@ type FieldTrie struct {
|
||||
length uint64
|
||||
numOfElems int
|
||||
isTransferred bool
|
||||
isCompressed bool
|
||||
}
|
||||
|
||||
// NewFieldTrie is the constructor for the field trie data structure. It creates the corresponding
|
||||
@@ -200,7 +201,6 @@ func (f *FieldTrie) TransferTrie() *FieldTrie {
|
||||
numOfElems: f.numOfElems,
|
||||
}
|
||||
}
|
||||
f.isTransferred = true
|
||||
nTrie := &FieldTrie{
|
||||
fieldLayers: f.fieldLayers,
|
||||
field: f.field,
|
||||
@@ -210,11 +210,43 @@ func (f *FieldTrie) TransferTrie() *FieldTrie {
|
||||
length: f.length,
|
||||
numOfElems: f.numOfElems,
|
||||
}
|
||||
/*
|
||||
// For validator fields we special case the transferance process
|
||||
if f.field == types.Validators {
|
||||
newLyr := make([]*[32]byte, len(f.fieldLayers[0]))
|
||||
copy(newLyr, f.fieldLayers[0])
|
||||
f.fieldLayers = [][]*[32]byte{newLyr}
|
||||
f.isCompressed = true
|
||||
} else {
|
||||
// Zero out field layers here.
|
||||
f.fieldLayers = nil
|
||||
f.isTransferred = true
|
||||
}*/
|
||||
// Zero out field layers here.
|
||||
f.fieldLayers = nil
|
||||
f.isTransferred = true
|
||||
|
||||
return nTrie
|
||||
}
|
||||
|
||||
func (f *FieldTrie) ExpandTrie() error {
|
||||
if !f.isCompressed {
|
||||
return errors.Errorf("Unsuitable trie provided to expand, it has length of %d instead of 1", len(f.fieldLayers))
|
||||
}
|
||||
fieldRoots := make([][32]byte, len(f.fieldLayers[0]))
|
||||
for i, v := range f.fieldLayers[0] {
|
||||
copiedRt := *v
|
||||
fieldRoots[i] = copiedRt
|
||||
}
|
||||
if f.dataType == types.CompositeArray {
|
||||
f.fieldLayers = stateutil.ReturnTrieLayerVariable(fieldRoots, f.length)
|
||||
} else {
|
||||
return errors.Errorf("Wrong data type for trie: %v", f.dataType)
|
||||
}
|
||||
f.isCompressed = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// TrieRoot returns the corresponding root of the trie.
|
||||
func (f *FieldTrie) TrieRoot() ([32]byte, error) {
|
||||
if f.Empty() {
|
||||
@@ -249,9 +281,23 @@ func (f *FieldTrie) Empty() bool {
|
||||
return f == nil || len(f.fieldLayers) == 0 || f.isTransferred
|
||||
}
|
||||
|
||||
func (f *FieldTrie) IsCompressed() bool {
|
||||
return f.isCompressed
|
||||
}
|
||||
|
||||
// InsertFieldLayer manually inserts a field layer. This method
|
||||
// bypasses the normal method of field computation, it is only
|
||||
// meant to be used in tests.
|
||||
func (f *FieldTrie) InsertFieldLayer(layer [][]*[32]byte) {
|
||||
f.fieldLayers = layer
|
||||
}
|
||||
|
||||
func (f *FieldTrie) FieldLayer() [][]*[32]byte {
|
||||
return f.fieldLayers
|
||||
}
|
||||
|
||||
func copyLayer(lyr []*[32]byte) []*[32]byte {
|
||||
newLyr := make([]*[32]byte, len(lyr))
|
||||
copy(newLyr, lyr)
|
||||
return newLyr
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ func handle32ByteArrays(val [][32]byte, indices []uint64, convertAll bool) ([][3
|
||||
func handleValidatorSlice(val []*ethpb.Validator, indices []uint64, convertAll bool) ([][32]byte, error) {
|
||||
length := len(indices)
|
||||
if convertAll {
|
||||
length = len(val)
|
||||
return stateutil.OptimizedValidatorRoots(val)
|
||||
}
|
||||
roots := make([][32]byte, 0, length)
|
||||
rootCreator := func(input *ethpb.Validator) error {
|
||||
@@ -218,15 +218,6 @@ func handleValidatorSlice(val []*ethpb.Validator, indices []uint64, convertAll b
|
||||
roots = append(roots, newRoot)
|
||||
return nil
|
||||
}
|
||||
if convertAll {
|
||||
for i := range val {
|
||||
err := rootCreator(val[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return roots, nil
|
||||
}
|
||||
if len(val) > 0 {
|
||||
for _, idx := range indices {
|
||||
if idx > uint64(len(val))-1 {
|
||||
|
||||
@@ -251,6 +251,10 @@ func TestFieldTrie_NativeState_fieldConvertersNative(t *testing.T) {
|
||||
field: types.FieldIndex(9),
|
||||
indices: []uint64{1},
|
||||
elements: []*ethpb.Eth1Data{
|
||||
{
|
||||
DepositRoot: make([]byte, fieldparams.RootLength),
|
||||
DepositCount: 2,
|
||||
},
|
||||
{
|
||||
DepositRoot: make([]byte, fieldparams.RootLength),
|
||||
DepositCount: 1,
|
||||
@@ -321,11 +325,14 @@ func TestFieldTrie_NativeState_fieldConvertersNative(t *testing.T) {
|
||||
wantHex: []string{"0x7d7696e7f12593934afcd87a0d38e1a981bee63cb4cf0568ba36a6e0596eeccb"},
|
||||
},
|
||||
{
|
||||
name: "Attestations",
|
||||
name: "Attestations convertAll false",
|
||||
args: &args{
|
||||
field: types.FieldIndex(15),
|
||||
indices: []uint64{1},
|
||||
elements: []*ethpb.PendingAttestation{
|
||||
{
|
||||
ProposerIndex: 0,
|
||||
},
|
||||
{
|
||||
ProposerIndex: 1,
|
||||
},
|
||||
@@ -352,8 +359,12 @@ func TestFieldTrie_NativeState_fieldConvertersNative(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
roots, err := fieldConverters(tt.args.field, tt.args.indices, tt.args.elements, tt.args.convertAll)
|
||||
if err != nil && tt.errMsg != "" {
|
||||
require.ErrorContains(t, tt.errMsg, err)
|
||||
if err != nil {
|
||||
if tt.errMsg != "" {
|
||||
require.ErrorContains(t, tt.errMsg, err)
|
||||
} else {
|
||||
t.Error("Unexpected error: " + err.Error())
|
||||
}
|
||||
} else {
|
||||
for i, root := range roots {
|
||||
hex := hexutil.Encode(root[:])
|
||||
|
||||
@@ -22,6 +22,7 @@ type BeaconState interface {
|
||||
Copy() BeaconState
|
||||
CopyAllTries()
|
||||
HashTreeRoot(ctx context.Context) ([32]byte, error)
|
||||
CheckFieldTries() string
|
||||
StateProver
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ go_library(
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_fastssz//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@in_gopkg_d4l3k_messagediff_v1//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
],
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/runtime/version"
|
||||
"go.opencensus.io/trace"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
var phase0Fields = []types.FieldIndex{
|
||||
@@ -860,6 +861,27 @@ func (b *BeaconState) CopyAllTries() {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BeaconState) CheckFieldTries() string {
|
||||
fLayer := b.stateFieldLeaves[types.Validators].FieldLayer()
|
||||
fTrie, err := fieldtrie.NewFieldTrie(types.Validators, fieldMap[types.Validators], b.validators, b.stateFieldLeaves[types.Validators].Length())
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
nLayer := fTrie.FieldLayer()
|
||||
fRoots := make([][32]byte, len(fLayer[0]))
|
||||
for i, r := range fLayer[0] {
|
||||
rt := *r
|
||||
fRoots[i] = rt
|
||||
}
|
||||
nRoots := make([][32]byte, len(nLayer[0]))
|
||||
for i, r := range nLayer[0] {
|
||||
rt := *r
|
||||
nRoots[i] = rt
|
||||
}
|
||||
dff, _ := messagediff.PrettyDiff(fRoots, nRoots)
|
||||
return dff
|
||||
}
|
||||
|
||||
func (b *BeaconState) recomputeFieldTrie(index types.FieldIndex, elements interface{}) ([32]byte, error) {
|
||||
fTrie := b.stateFieldLeaves[index]
|
||||
fTrieMutex := fTrie.RWMutex
|
||||
@@ -867,7 +889,7 @@ func (b *BeaconState) recomputeFieldTrie(index types.FieldIndex, elements interf
|
||||
// and therefore we would call Unlock() on a different object.
|
||||
fTrieMutex.Lock()
|
||||
|
||||
if fTrie.Empty() {
|
||||
if fTrie.Empty() && !fTrie.IsCompressed() {
|
||||
err := b.resetFieldTrie(index, elements, fTrie.Length())
|
||||
if err != nil {
|
||||
fTrieMutex.Unlock()
|
||||
@@ -879,6 +901,12 @@ func (b *BeaconState) recomputeFieldTrie(index types.FieldIndex, elements interf
|
||||
return b.stateFieldLeaves[index].TrieRoot()
|
||||
}
|
||||
|
||||
if fTrie.IsCompressed() {
|
||||
if err := fTrie.ExpandTrie(); err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
}
|
||||
|
||||
if fTrie.FieldReference().Refs() > 1 {
|
||||
fTrie.FieldReference().MinusRef()
|
||||
newTrie := fTrie.TransferTrie()
|
||||
|
||||
@@ -155,6 +155,20 @@ func (e *epochBoundaryState) put(blockRoot [32]byte, s state.BeaconState) error
|
||||
func (e *epochBoundaryState) delete(blockRoot [32]byte) error {
|
||||
e.lock.Lock()
|
||||
defer e.lock.Unlock()
|
||||
rInfo, ok, err := e.getByBlockRootLockFree(blockRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
slotInfo := &slotRootInfo{
|
||||
slot: rInfo.state.Slot(),
|
||||
blockRoot: blockRoot,
|
||||
}
|
||||
if err = e.slotRootCache.Delete(slotInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
return e.rootStateCache.Delete(&rootStateInfo{
|
||||
root: blockRoot,
|
||||
})
|
||||
|
||||
@@ -2,8 +2,10 @@ package stategen
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
@@ -79,6 +81,52 @@ func (s *State) saveStateByRoot(ctx context.Context, blockRoot [32]byte, st stat
|
||||
if err := s.epochBoundaryStateCache.put(blockRoot, st); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Always check that the correct epoch boundary states have been saved
|
||||
// for the current epoch.
|
||||
epochStart, err := slots.EpochStart(slots.ToEpoch(st.Slot()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rInfo, ok, err := s.epochBoundaryStateCache.getBySlot(epochStart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bRoot, err := helpers.BlockRootAtSlot(st, epochStart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nonCanonicalState := ok && [32]byte(bRoot) != rInfo.root
|
||||
|
||||
// We would only recover the boundary states under 2 conditions:
|
||||
//
|
||||
// 1) Would indicate that the epoch boundary was skipped due to a missed slot, we
|
||||
// then recover by saving the state at that particular slot here.
|
||||
//
|
||||
// 2) Check that the canonical epoch boundary state has been saved
|
||||
// in our epoch boundary cache.
|
||||
if !ok || nonCanonicalState {
|
||||
// Only recover the state if it is in our hot state cache, otherwise we
|
||||
// simply skip this step.
|
||||
if s.hotStateCache.has([32]byte(bRoot)) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"slot": epochStart,
|
||||
"root": fmt.Sprintf("%#x", bRoot),
|
||||
}).Debug("Recovering state for epoch boundary cache")
|
||||
|
||||
// Remove the non-canonical state from our cache.
|
||||
if nonCanonicalState {
|
||||
if err := s.epochBoundaryStateCache.delete(rInfo.root); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
hState := s.hotStateCache.get([32]byte(bRoot))
|
||||
if err := s.epochBoundaryStateCache.put([32]byte(bRoot), hState); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// On an intermediate slot, save state summary.
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
testDB "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
|
||||
doublylinkedtree "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/doubly-linked-tree"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/util"
|
||||
@@ -137,6 +138,81 @@ func TestSaveState_NoSaveNotEpochBoundary(t *testing.T) {
|
||||
require.Equal(t, false, beaconDB.HasState(ctx, r))
|
||||
}
|
||||
|
||||
func TestSaveState_RecoverForEpochBoundary(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
service := New(beaconDB, doublylinkedtree.New())
|
||||
|
||||
beaconState, _ := util.DeterministicGenesisState(t, 32)
|
||||
require.NoError(t, beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch-1))
|
||||
r := [32]byte{'A'}
|
||||
boundaryRoot := [32]byte{'B'}
|
||||
require.NoError(t, beaconState.UpdateBlockRootAtIndex(0, boundaryRoot))
|
||||
|
||||
b := util.NewBeaconBlock()
|
||||
util.SaveBlock(t, ctx, beaconDB, b)
|
||||
gRoot, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, gRoot))
|
||||
// Save boundary state to the hot state cache.
|
||||
boundaryState, _ := util.DeterministicGenesisState(t, 32)
|
||||
service.hotStateCache.put(boundaryRoot, boundaryState)
|
||||
require.NoError(t, service.SaveState(ctx, r, beaconState))
|
||||
|
||||
rInfo, ok, err := service.epochBoundaryStateCache.getByBlockRoot(boundaryRoot)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, ok, "state does not exist in cache")
|
||||
assert.Equal(t, rInfo.root, boundaryRoot, "incorrect root of root state info")
|
||||
assert.Equal(t, rInfo.state.Slot(), primitives.Slot(0), "incorrect slot of state")
|
||||
}
|
||||
|
||||
func TestSaveState_RecoverForEpochBoundary_NonCanonicalBoundaryState(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
service := New(beaconDB, doublylinkedtree.New())
|
||||
|
||||
beaconState, _ := util.DeterministicGenesisState(t, 32)
|
||||
require.NoError(t, beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch-1))
|
||||
r := [32]byte{'A'}
|
||||
boundaryRoot := [32]byte{'B'}
|
||||
require.NoError(t, beaconState.UpdateBlockRootAtIndex(0, boundaryRoot))
|
||||
|
||||
// Epoch boundary state is inserted for slot 0
|
||||
nonCanonicalRoot := [32]byte{'C'}
|
||||
nonCanonicalSt, _ := util.DeterministicGenesisState(t, 32)
|
||||
require.NoError(t, service.epochBoundaryStateCache.put(nonCanonicalRoot, nonCanonicalSt))
|
||||
|
||||
_, ok, err := service.epochBoundaryStateCache.getByBlockRoot(nonCanonicalRoot)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, ok, "state does not exist in cache")
|
||||
|
||||
rInfo, ok, err := service.epochBoundaryStateCache.getBySlot(0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, nonCanonicalRoot, rInfo.root)
|
||||
|
||||
b := util.NewBeaconBlock()
|
||||
util.SaveBlock(t, ctx, beaconDB, b)
|
||||
gRoot, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, gRoot))
|
||||
// Save boundary state to the hot state cache.
|
||||
boundaryState, _ := util.DeterministicGenesisState(t, 32)
|
||||
service.hotStateCache.put(boundaryRoot, boundaryState)
|
||||
|
||||
require.NoError(t, service.SaveState(ctx, r, beaconState))
|
||||
|
||||
rInfo, ok, err = service.epochBoundaryStateCache.getBySlot(0)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, ok, "state does not exist in cache")
|
||||
assert.Equal(t, rInfo.root, boundaryRoot, "incorrect root of root state info")
|
||||
assert.Equal(t, rInfo.state.Slot(), primitives.Slot(0), "incorrect slot of state")
|
||||
|
||||
_, ok, err = service.epochBoundaryStateCache.getByBlockRoot(nonCanonicalRoot)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, false, ok, "state exists in cache")
|
||||
}
|
||||
|
||||
func TestSaveState_CanSaveHotStateToDB(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -30,7 +30,7 @@ func ValidatorRegistryRoot(vals []*ethpb.Validator) ([32]byte, error) {
|
||||
}
|
||||
|
||||
func validatorRegistryRoot(validators []*ethpb.Validator) ([32]byte, error) {
|
||||
roots, err := optimizedValidatorRoots(validators)
|
||||
roots, err := OptimizedValidatorRoots(validators)
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
@@ -51,7 +51,9 @@ func validatorRegistryRoot(validators []*ethpb.Validator) ([32]byte, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func optimizedValidatorRoots(validators []*ethpb.Validator) ([][32]byte, error) {
|
||||
// OptimizedValidatorRoots uses an optimized routine with gohashtree in order to
|
||||
// derive a list of validator roots from a list of validator objects.
|
||||
func OptimizedValidatorRoots(validators []*ethpb.Validator) ([][32]byte, error) {
|
||||
// Exit early if no validators are provided.
|
||||
if len(validators) == 0 {
|
||||
return [][32]byte{}, nil
|
||||
|
||||
@@ -139,7 +139,7 @@ func (s *Service) writeBlockBatchToStream(ctx context.Context, batch blockBatch,
|
||||
continue
|
||||
}
|
||||
if chunkErr := s.chunkBlockWriter(stream, b); chunkErr != nil {
|
||||
log.WithError(chunkErr).Error("Could not send a chunked response")
|
||||
log.WithError(chunkErr).Debug("Could not send a chunked response")
|
||||
return chunkErr
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
@@ -13,6 +14,8 @@ import (
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/peers"
|
||||
"github.com/prysmaticlabs/prysm/v4/cmd/beacon-chain/flags"
|
||||
@@ -283,7 +286,7 @@ func (s *Service) wrapAndReportValidation(topic string, v wrappedVal) (string, p
|
||||
messageFailedValidationCounter.WithLabelValues(topic).Inc()
|
||||
}
|
||||
if b == pubsub.ValidationIgnore {
|
||||
if err != nil {
|
||||
if err != nil && !errorIsIgnored(err) {
|
||||
log.WithError(err).WithFields(logrus.Fields{
|
||||
"topic": topic,
|
||||
"multiaddress": multiAddr(pid, s.cfg.p2p.Peers()),
|
||||
@@ -781,3 +784,13 @@ func multiAddr(pid peer.ID, stat *peers.Status) string {
|
||||
}
|
||||
return addrs.String()
|
||||
}
|
||||
|
||||
func errorIsIgnored(err error) bool {
|
||||
if errors.Is(err, helpers.ErrTooLate) {
|
||||
return true
|
||||
}
|
||||
if errors.Is(err, altair.ErrTooLate) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -203,6 +203,7 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms
|
||||
log.WithFields(logrus.Fields{
|
||||
"blockSlot": blk.Block().Slot(),
|
||||
"sinceSlotStartTime": receivedTime.Sub(startTime),
|
||||
"validationTime": prysmTime.Now().Sub(receivedTime),
|
||||
"proposerIndex": blk.Block().ProposerIndex(),
|
||||
"graffiti": string(graffiti[:]),
|
||||
}).Debug("Received block")
|
||||
|
||||
19
build/bazelrc/hermetic-cc.bazelrc
Normal file
19
build/bazelrc/hermetic-cc.bazelrc
Normal file
@@ -0,0 +1,19 @@
|
||||
# From Bazel's perspective, this is almost equivalent to always specifying
|
||||
# --extra_toolchains on every bazel <...> command-line invocation. It also
|
||||
# means there is no way to disable the toolchain with the command line. This is
|
||||
# useful if you find bazel-hermetic-cc useful enough to compile for all of your
|
||||
# targets and tools.
|
||||
#
|
||||
# With BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 Bazel stops detecting the default
|
||||
# host toolchain. Configuring toolchains is complicated enough, and the
|
||||
# auto-detection (read: fallback to non-hermetic toolchain) is a footgun best
|
||||
# avoided. This option is not documented in bazel, so may break. If you intend
|
||||
# to use the hermetic toolchain exclusively, it won't hurt.
|
||||
build --action_env BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
|
||||
|
||||
# This snippet instructs Bazel to use the registered "new kinds of toolchains".
|
||||
# This flag not needed after this issue is closed https://github.com/bazelbuild/bazel/issues/7260
|
||||
build --incompatible_enable_cc_toolchain_resolution
|
||||
|
||||
# Add a no-op warning for users still using --config=llvm
|
||||
build:llvm --unconditional_warning="llvm config is no longer used as clang is now the default compiler"
|
||||
@@ -202,11 +202,7 @@ var (
|
||||
Usage: "Sets the maximum number of headers that a deposit log query can fetch.",
|
||||
Value: uint64(1000),
|
||||
}
|
||||
// EnableRegistrationCache a temporary flag for enabling the validator registration cache instead of db.
|
||||
EnableRegistrationCache = &cli.BoolFlag{
|
||||
Name: "enable-registration-cache",
|
||||
Usage: "A temporary flag for enabling the validator registration cache instead of persisting in db. The cache will clear on restart.",
|
||||
}
|
||||
|
||||
// WeakSubjectivityCheckpoint defines the weak subjectivity checkpoint the node must sync through to defend against long range attacks.
|
||||
WeakSubjectivityCheckpoint = &cli.StringFlag{
|
||||
Name: "weak-subjectivity-checkpoint",
|
||||
|
||||
@@ -58,7 +58,6 @@ var appFlags = []cli.Flag{
|
||||
flags.InteropGenesisTimeFlag,
|
||||
flags.SlotsPerArchivedPoint,
|
||||
flags.EnableDebugRPCEndpoints,
|
||||
flags.EnableRegistrationCache,
|
||||
flags.SubscribeToAllSubnets,
|
||||
flags.HistoricalSlasherNode,
|
||||
flags.ChainID,
|
||||
|
||||
@@ -113,7 +113,6 @@ var appHelpFlagGroups = []flagGroup{
|
||||
flags.BlockBatchLimit,
|
||||
flags.BlockBatchLimitBurstFactor,
|
||||
flags.EnableDebugRPCEndpoints,
|
||||
flags.EnableRegistrationCache,
|
||||
flags.SubscribeToAllSubnets,
|
||||
flags.HistoricalSlasherNode,
|
||||
flags.ChainID,
|
||||
|
||||
@@ -93,7 +93,7 @@ var (
|
||||
// StaticPeers specifies a set of peers to connect to explicitly.
|
||||
StaticPeers = &cli.StringSliceFlag{
|
||||
Name: "peer",
|
||||
Usage: "Connect with this peer. This flag may be used multiple times.",
|
||||
Usage: "Connect with this peer, this flag may be used multiple times. This peer is recognized as a trusted peer.",
|
||||
}
|
||||
// BootstrapNode tells the beacon node which bootstrap node to connect to
|
||||
BootstrapNode = &cli.StringSliceFlag{
|
||||
|
||||
@@ -52,11 +52,11 @@ func createKeystore(t *testing.T, path string) (*keymanager.Keystore, string) {
|
||||
id, err := uuid.NewRandom()
|
||||
require.NoError(t, err)
|
||||
keystoreFile := &keymanager.Keystore{
|
||||
Crypto: cryptoFields,
|
||||
ID: id.String(),
|
||||
Pubkey: fmt.Sprintf("%x", validatingKey.PublicKey().Marshal()),
|
||||
Version: encryptor.Version(),
|
||||
Name: encryptor.Name(),
|
||||
Crypto: cryptoFields,
|
||||
ID: id.String(),
|
||||
Pubkey: fmt.Sprintf("%x", validatingKey.PublicKey().Marshal()),
|
||||
Version: encryptor.Version(),
|
||||
Description: encryptor.Name(),
|
||||
}
|
||||
encoded, err := json.MarshalIndent(keystoreFile, "", "\t")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -134,7 +134,6 @@ func TestExitAccountsCli_OK_AllPublicKeys(t *testing.T) {
|
||||
|
||||
mockNodeClient.EXPECT().
|
||||
GetGenesis(gomock.Any(), gomock.Any()).
|
||||
Times(2).
|
||||
Return(ðpb.Genesis{GenesisTime: genesisTime}, nil)
|
||||
|
||||
mockValidatorClient.EXPECT().
|
||||
|
||||
@@ -70,6 +70,7 @@ type Flags struct {
|
||||
PrepareAllPayloads bool // PrepareAllPayloads informs the engine to prepare a block on every slot.
|
||||
|
||||
BuildBlockParallel bool // BuildBlockParallel builds beacon block for proposer in parallel.
|
||||
AggregateParallel bool // AggregateParallel aggregates attestations in parallel.
|
||||
|
||||
// KeystoreImportDebounceInterval specifies the time duration the validator waits to reload new keys if they have
|
||||
// changed on disk. This feature is for advanced use cases only.
|
||||
@@ -229,6 +230,10 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
|
||||
logEnabled(disableBuildBlockParallel)
|
||||
cfg.BuildBlockParallel = false
|
||||
}
|
||||
if ctx.IsSet(aggregateParallel.Name) {
|
||||
logEnabled(aggregateParallel)
|
||||
cfg.AggregateParallel = true
|
||||
}
|
||||
if ctx.IsSet(disableResourceManager.Name) {
|
||||
logEnabled(disableResourceManager)
|
||||
cfg.DisableResourceManager = true
|
||||
|
||||
@@ -32,6 +32,12 @@ var (
|
||||
Usage: deprecatedUsage,
|
||||
Hidden: true,
|
||||
}
|
||||
|
||||
deprecatedEnableRegistrationCache = &cli.BoolFlag{
|
||||
Name: "enable-registration-cache",
|
||||
Usage: deprecatedUsage,
|
||||
Hidden: true,
|
||||
}
|
||||
)
|
||||
|
||||
// Deprecated flags for both the beacon node and validator client.
|
||||
@@ -41,6 +47,7 @@ var deprecatedFlags = []cli.Flag{
|
||||
deprecatedEnableReorgLateBlocks,
|
||||
deprecatedDisableGossipBatchAggregation,
|
||||
deprecatedBuildBlockParallel,
|
||||
deprecatedEnableRegistrationCache,
|
||||
}
|
||||
|
||||
// deprecatedBeaconFlags contains flags that are still used by other components
|
||||
|
||||
@@ -148,6 +148,17 @@ var (
|
||||
Name: "disable-resource-manager",
|
||||
Usage: "Disables running the libp2p resource manager",
|
||||
}
|
||||
|
||||
// DisableRegistrationCache a flag for disabling the validator registration cache and use db instead.
|
||||
DisableRegistrationCache = &cli.BoolFlag{
|
||||
Name: "diable-registration-cache",
|
||||
Usage: "A temporary flag for disabling the validator registration cache instead of using the db. note: registrations do not clear on restart while using the db",
|
||||
}
|
||||
|
||||
aggregateParallel = &cli.BoolFlag{
|
||||
Name: "aggregate-parallel",
|
||||
Usage: "Enables parallel aggregation of attestations",
|
||||
}
|
||||
)
|
||||
|
||||
// devModeFlags holds list of flags that are set when development mode is on.
|
||||
@@ -200,6 +211,8 @@ var BeaconChainFlags = append(deprecatedBeaconFlags, append(deprecatedFlags, []c
|
||||
aggregateSecondInterval,
|
||||
aggregateThirdInterval,
|
||||
disableResourceManager,
|
||||
DisableRegistrationCache,
|
||||
aggregateParallel,
|
||||
}...)...)
|
||||
|
||||
// E2EBeaconChainFlags contains a list of the beacon chain feature flags to be tested in E2E.
|
||||
|
||||
@@ -84,6 +84,15 @@ type ProposerSettings struct {
|
||||
DefaultConfig *ProposerOption
|
||||
}
|
||||
|
||||
// ShouldBeSaved goes through checks to see if the value should be saveable
|
||||
// Pseudocode: conditions for being saved into the database
|
||||
// 1. settings are not nil
|
||||
// 2. proposeconfig is not nil (this defines specific settings for each validator key), default config can be nil in this case and fall back to beacon node settings
|
||||
// 3. defaultconfig is not nil, meaning it has at least fee recipient settings (this defines general settings for all validator keys but keys will use settings from propose config if available), propose config can be nil in this case
|
||||
func (settings *ProposerSettings) ShouldBeSaved() bool {
|
||||
return settings != nil && (settings.ProposeConfig != nil || settings.DefaultConfig != nil && settings.DefaultConfig.FeeRecipientConfig != nil)
|
||||
}
|
||||
|
||||
// ToPayload converts struct to ProposerSettingsPayload
|
||||
func (ps *ProposerSettings) ToPayload() *validatorpb.ProposerSettingsPayload {
|
||||
if ps == nil {
|
||||
|
||||
@@ -98,3 +98,117 @@ func Test_Proposer_Setting_Cloning(t *testing.T) {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestProposerSettings_ShouldBeSaved(t *testing.T) {
|
||||
key1hex := "0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a"
|
||||
key1, err := hexutil.Decode(key1hex)
|
||||
require.NoError(t, err)
|
||||
type fields struct {
|
||||
ProposeConfig map[[fieldparams.BLSPubkeyLength]byte]*ProposerOption
|
||||
DefaultConfig *ProposerOption
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "Should be saved, proposeconfig populated and no default config",
|
||||
fields: fields{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*ProposerOption{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
BuilderConfig: &BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(40000000),
|
||||
Relays: []string{"https://example-relay.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: nil,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Should be saved, default populated and no proposeconfig ",
|
||||
fields: fields{
|
||||
ProposeConfig: nil,
|
||||
DefaultConfig: &ProposerOption{
|
||||
FeeRecipientConfig: &FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
BuilderConfig: &BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(40000000),
|
||||
Relays: []string{"https://example-relay.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Should be saved, all populated",
|
||||
fields: fields{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*ProposerOption{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
BuilderConfig: &BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(40000000),
|
||||
Relays: []string{"https://example-relay.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &ProposerOption{
|
||||
FeeRecipientConfig: &FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
BuilderConfig: &BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(40000000),
|
||||
Relays: []string{"https://example-relay.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: "Should not be saved, proposeconfig not populated and default not populated",
|
||||
fields: fields{
|
||||
ProposeConfig: nil,
|
||||
DefaultConfig: nil,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Should not be saved, builder data only",
|
||||
fields: fields{
|
||||
ProposeConfig: nil,
|
||||
DefaultConfig: &ProposerOption{
|
||||
BuilderConfig: &BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(40000000),
|
||||
Relays: []string{"https://example-relay.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
settings := &ProposerSettings{
|
||||
ProposeConfig: tt.fields.ProposeConfig,
|
||||
DefaultConfig: tt.fields.DefaultConfig,
|
||||
}
|
||||
if got := settings.ShouldBeSaved(); got != tt.want {
|
||||
t.Errorf("ShouldBeSaved() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
11
deps.bzl
11
deps.bzl
@@ -2458,6 +2458,8 @@ def prysm_deps():
|
||||
],
|
||||
build_file_proto_mode = "disable_global",
|
||||
importpath = "github.com/libp2p/go-libp2p",
|
||||
patch_args = ["-p1"],
|
||||
patches = ["//third_party:com_github_libp2p_go_libp2p.patch"],
|
||||
sum = "h1:KwA7pXKXpz8hG6Cr1fMA7UkgleogcwQj0sxl5qquWRg=",
|
||||
version = "v0.27.5",
|
||||
)
|
||||
@@ -4865,12 +4867,3 @@ def prysm_deps():
|
||||
sum = "h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=",
|
||||
version = "v1.24.0",
|
||||
)
|
||||
|
||||
# Note: go_repository is already wrapped with maybe!
|
||||
maybe(
|
||||
git_repository,
|
||||
name = "vaticle_bazel_distribution",
|
||||
commit = "96424c85195a97dad81f69fdbbef2e1574bf8801",
|
||||
remote = "https://github.com/vaticle/bazel-distribution",
|
||||
shallow_since = "1569509514 +0300",
|
||||
)
|
||||
|
||||
@@ -134,6 +134,7 @@ func TestUnmarshalState(t *testing.T) {
|
||||
}()
|
||||
bc := params.BeaconConfig()
|
||||
altairSlot, err := slots.EpochStart(bc.AltairForkEpoch)
|
||||
require.NoError(t, err)
|
||||
bellaSlot, err := slots.EpochStart(bc.BellatrixForkEpoch)
|
||||
require.NoError(t, err)
|
||||
cases := []struct {
|
||||
@@ -205,6 +206,7 @@ func TestUnmarshalBlock(t *testing.T) {
|
||||
altairv := bytesutil.ToBytes4(params.BeaconConfig().AltairForkVersion)
|
||||
bellav := bytesutil.ToBytes4(params.BeaconConfig().BellatrixForkVersion)
|
||||
altairS, err := slots.EpochStart(params.BeaconConfig().AltairForkEpoch)
|
||||
require.NoError(t, err)
|
||||
bellaS, err := slots.EpochStart(params.BeaconConfig().BellatrixForkEpoch)
|
||||
require.NoError(t, err)
|
||||
cases := []struct {
|
||||
@@ -296,6 +298,7 @@ func TestUnmarshalBlindedBlock(t *testing.T) {
|
||||
altairv := bytesutil.ToBytes4(params.BeaconConfig().AltairForkVersion)
|
||||
bellav := bytesutil.ToBytes4(params.BeaconConfig().BellatrixForkVersion)
|
||||
altairS, err := slots.EpochStart(params.BeaconConfig().AltairForkEpoch)
|
||||
require.NoError(t, err)
|
||||
bellaS, err := slots.EpochStart(params.BeaconConfig().BellatrixForkEpoch)
|
||||
require.NoError(t, err)
|
||||
cases := []struct {
|
||||
|
||||
@@ -147,11 +147,11 @@
|
||||
}
|
||||
},
|
||||
"ineffassign": {
|
||||
"only_files": {
|
||||
"beacon-chain/.*": "",
|
||||
"shared/.*": "",
|
||||
"slasher/.*": "",
|
||||
"validator/.*": ""
|
||||
"exclude_files": {
|
||||
"external/.*": "Third party code",
|
||||
"rules_go_work-.*": "Third party code",
|
||||
".*\\.pb.*.go": "Generated code is ok",
|
||||
".*generated\\.ssz\\.go": "Generated code is ok"
|
||||
}
|
||||
},
|
||||
"properpermissions": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user