mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 05:47:59 -05:00
Compare commits
23 Commits
hardenBina
...
v3.2.2.rc-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfd8bcff84 | ||
|
|
86a883aa19 | ||
|
|
c000e8fde5 | ||
|
|
75e5887f07 | ||
|
|
4ca3c5b058 | ||
|
|
25d06d41be | ||
|
|
0a87210514 | ||
|
|
196798eacc | ||
|
|
17fe935343 | ||
|
|
ac4483417d | ||
|
|
0d3fb0a32b | ||
|
|
3d337b07e1 | ||
|
|
11b90e1f63 | ||
|
|
3c73bac798 | ||
|
|
91fee5db17 | ||
|
|
155b0c161e | ||
|
|
a7010d817d | ||
|
|
c0dd233a1c | ||
|
|
c391fad258 | ||
|
|
e92b546a36 | ||
|
|
765345ac3a | ||
|
|
ec13d52f03 | ||
|
|
08ebc99bc3 |
178
.bazelrc
178
.bazelrc
@@ -1,9 +1,9 @@
|
||||
# Print warnings for tests with inappropriate test size or timeout.
|
||||
test --test_verbose_timeout_warnings
|
||||
|
||||
# Only build test targets when running bazel test //...
|
||||
test --build_tests_only
|
||||
test --test_output=errors
|
||||
# Import bazelrc presets
|
||||
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/performance.bazelrc
|
||||
|
||||
# E2E run with debug gotag
|
||||
test:e2e --define gotags=debug
|
||||
@@ -11,21 +11,9 @@ test:e2e --define gotags=debug
|
||||
# Clearly indicate that coverage is enabled to disable certain nogo checks.
|
||||
coverage --define=coverage_enabled=1
|
||||
|
||||
# Fix for rules_docker. See: https://github.com/bazelbuild/rules_docker/issues/842
|
||||
build --host_force_python=PY2
|
||||
run --host_force_python=PY2
|
||||
|
||||
# Networking is blocked for tests by default, add "requires-network" tag to your test if networking
|
||||
# is required within the sandbox. Network sandboxing only works on linux.
|
||||
build --sandbox_default_allow_network=false
|
||||
|
||||
# Stamp binaries with git information
|
||||
build --workspace_status_command=./hack/workspace_status.sh
|
||||
|
||||
# Prevent PATH changes from rebuilding when switching from IDE to command line.
|
||||
build --incompatible_strict_action_env
|
||||
run --incompatible_strict_action_env
|
||||
|
||||
build --define blst_disabled=false
|
||||
run --define blst_disabled=false
|
||||
|
||||
@@ -68,42 +56,6 @@ build:cgo_symbolizer --define=USE_CGO_SYMBOLIZER=true
|
||||
build:cgo_symbolizer -c dbg
|
||||
build:cgo_symbolizer --define=gotags=cgosymbolizer_enabled
|
||||
|
||||
# multi-arch cross-compiling toolchain configs:
|
||||
-----------------------------------------------
|
||||
build:cross --crosstool_top=@prysm_toolchains//:multiarch_toolchain
|
||||
build:cross --host_platform=@io_bazel_rules_go//go/toolchain:linux_amd64
|
||||
build:cross --host_crosstool_top=@prysm_toolchains//:hostonly_toolchain
|
||||
|
||||
# linux_amd64 config for cross compiler toolchain, not strictly necessary since host/exec env is amd64
|
||||
build:linux_amd64 --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64_cgo
|
||||
|
||||
# osx_amd64 config for cross compiler toolchain
|
||||
build:osx_amd64 --config=cross
|
||||
build:osx_amd64 --platforms=@io_bazel_rules_go//go/toolchain:darwin_amd64_cgo
|
||||
build:osx_amd64 --compiler=osxcross
|
||||
|
||||
# osx_arm64 config for cross compiler toolchain
|
||||
build:osx_arm64 --config=cross
|
||||
build:osx_arm64 --platforms=@io_bazel_rules_go//go/toolchain:darwin_arm64_cgo
|
||||
build:osx_arm64 --compiler=osxcross
|
||||
build:osx_arm64 --cpu=aarch64
|
||||
|
||||
# windows
|
||||
build:windows_amd64 --config=cross
|
||||
build:windows_amd64 --platforms=@io_bazel_rules_go//go/toolchain:windows_amd64_cgo
|
||||
build:windows_amd64 --compiler=mingw-w64
|
||||
|
||||
# linux_arm64 conifg for cross compiler toolchain
|
||||
build:linux_arm64 --config=cross
|
||||
build:linux_arm64 --platforms=@io_bazel_rules_go//go/toolchain:linux_arm64_cgo
|
||||
build:linux_arm64 --copt=-funsafe-math-optimizations
|
||||
build:linux_arm64 --copt=-ftree-vectorize
|
||||
build:linux_arm64 --copt=-fomit-frame-pointer
|
||||
build:linux_arm64 --cpu=aarch64
|
||||
build:linux_arm64 --compiler=clang
|
||||
build:linux_arm64 --copt=-march=armv8-a
|
||||
|
||||
|
||||
# toolchain build debug configs
|
||||
#------------------------------
|
||||
build:debug --sandbox_debug
|
||||
@@ -111,123 +63,5 @@ build:debug --toolchain_resolution_debug
|
||||
build:debug --verbose_failures
|
||||
build:debug -s
|
||||
|
||||
# windows debug
|
||||
build:windows_amd64_debug --config=windows_amd64
|
||||
build:windows_amd64_debug --config=debug
|
||||
|
||||
# osx_amd64 debug config
|
||||
build:osx_amd64_debug --config=debug
|
||||
build:osx_amd64_debug --config=osx_amd64
|
||||
|
||||
# osx_arm64 debug config
|
||||
build:osx_arm64_debug --config=debug
|
||||
build:osx_arm64_debug --config=osx_arm64
|
||||
|
||||
# linux_arm64_debug
|
||||
build:linux_arm64_debug --config=linux_arm64
|
||||
build:linux_arm64_debug --config=debug
|
||||
|
||||
# linux_amd64_debug
|
||||
build:linux_amd64_debug --config=linux_amd64
|
||||
build:linux_amd64_debug --config=debug
|
||||
|
||||
|
||||
# Docker Sandbox Configs
|
||||
#-----------------------
|
||||
# Note all docker sandbox configs must run from a linux x86_64 host
|
||||
# build:docker-sandbox --experimental_docker_image=gcr.io/prysmaticlabs/rbe-worker:latest
|
||||
build:docker-sandbox --spawn_strategy=docker --strategy=Javac=docker --genrule_strategy=docker
|
||||
build:docker-sandbox --define=EXECUTOR=remote
|
||||
build:docker-sandbox --experimental_docker_verbose
|
||||
build:docker-sandbox --experimental_enable_docker_sandbox
|
||||
build:docker-sandbox --crosstool_top=@rbe_ubuntu_clang//cc:toolchain
|
||||
build:docker-sandbox --host_javabase=@rbe_ubuntu_clang//java:jdk
|
||||
build:docker-sandbox --javabase=@rbe_ubuntu_clang//java:jdk
|
||||
build:docker-sandbox --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
|
||||
build:docker-sandbox --java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
|
||||
build:docker-sandbox --extra_execution_platforms=@rbe_ubuntu_clang//config:platform
|
||||
build:docker-sandbox --host_platform=@rbe_ubuntu_clang//config:platform
|
||||
build:docker-sandbox --platforms=@rbe_ubuntu_clang//config:platform
|
||||
build:docker-sandbox --extra_toolchains=@prysm_toolchains//:cc-toolchain-multiarch
|
||||
|
||||
# windows_amd64 docker sandbox build config
|
||||
build:windows_amd64_docker --config=docker-sandbox --config=windows_amd64
|
||||
build:windows_amd64_docker_debug --config=windows_amd64_docker --config=debug
|
||||
|
||||
# osx_amd64 docker sandbox build config
|
||||
build:osx_amd64_docker --config=docker-sandbox --config=osx_amd64
|
||||
build:osx_amd64_docker_debug --config=osx_amd64_docker --config=debug
|
||||
|
||||
# osx_arm64 docker sandbox build config
|
||||
build:osx_arm64_docker --config=docker-sandbox --config=osx_arm64
|
||||
build:osx_arm64_docker_debug --config=osx_arm64_docker --config=debug
|
||||
|
||||
# linux_arm64 docker sandbox build config
|
||||
build:linux_arm64_docker --config=docker-sandbox --config=linux_arm64
|
||||
build:linux_arm64_docker_debug --config=linux_arm64_docker --config=debug
|
||||
|
||||
# linux_amd64 docker sandbox build config
|
||||
build:linux_amd64_docker --config=docker-sandbox --config=linux_amd64
|
||||
build:linux_amd64_docker_debug --config=linux_amd64_docker --config=debug
|
||||
|
||||
|
||||
# Remote Build Execution
|
||||
#-----------------------
|
||||
# Originally from https://github.com/bazelbuild/bazel-toolchains/blob/master/bazelrc/bazel-2.0.0.bazelrc
|
||||
#
|
||||
# Depending on how many machines are in the remote execution instance, setting
|
||||
# this higher can make builds faster by allowing more jobs to run in parallel.
|
||||
# Setting it too high can result in jobs that timeout, however, while waiting
|
||||
# for a remote machine to execute them.
|
||||
build:remote --jobs=50
|
||||
|
||||
# Set several flags related to specifying the platform, toolchain and java
|
||||
# properties.
|
||||
# These flags should only be used as is for the rbe-ubuntu16-04 container
|
||||
# and need to be adapted to work with other toolchain containers.
|
||||
build:remote --host_javabase=@rbe_ubuntu_clang//java:jdk
|
||||
build:remote --javabase=@rbe_ubuntu_clang//java:jdk
|
||||
build:remote --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
|
||||
build:remote --java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
|
||||
build:remote --crosstool_top=@rbe_ubuntu_clang//cc:toolchain
|
||||
build:remote --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
|
||||
# Platform flags:
|
||||
# The toolchain container used for execution is defined in the target indicated
|
||||
# by "extra_execution_platforms", "host_platform" and "platforms".
|
||||
# More about platforms: https://docs.bazel.build/versions/master/platforms.html
|
||||
build:remote --extra_toolchains=@rbe_ubuntu_clang//config:cc-toolchain
|
||||
build:remote --extra_execution_platforms=@rbe_ubuntu_clang//config:platform
|
||||
build:remote --host_platform=@rbe_ubuntu_clang//config:platform
|
||||
build:remote --platforms=@rbe_ubuntu_clang//config:platform
|
||||
|
||||
# Starting with Bazel 0.27.0 strategies do not need to be explicitly
|
||||
# defined. See https://github.com/bazelbuild/bazel/issues/7480
|
||||
build:remote --define=EXECUTOR=remote
|
||||
|
||||
# Enable remote execution so actions are performed on the remote systems.
|
||||
# build:remote --remote_executor=grpcs://remotebuildexecution.googleapis.com
|
||||
|
||||
# Enforce stricter environment rules, which eliminates some non-hermetic
|
||||
# behavior and therefore improves both the remote cache hit rate and the
|
||||
# correctness and repeatability of the build.
|
||||
build:remote --incompatible_strict_action_env=true
|
||||
|
||||
# Set a higher timeout value, just in case.
|
||||
build:remote --remote_timeout=3600
|
||||
|
||||
# Enable authentication. This will pick up application default credentials by
|
||||
# default. You can use --google_credentials=some_file.json to use a service
|
||||
# account credential instead.
|
||||
# build:remote --google_default_credentials=true
|
||||
|
||||
# Enable build without the bytes
|
||||
# See: https://github.com/bazelbuild/bazel/issues/6862
|
||||
build:remote --experimental_remote_download_outputs=toplevel --experimental_inmemory_jdeps_files --experimental_inmemory_dotd_files
|
||||
|
||||
build:remote --remote_local_fallback
|
||||
|
||||
# Ignore GoStdLib with remote caching
|
||||
build --modify_execution_info='GoStdlib.*=+no-remote-cache'
|
||||
|
||||
# Set bazel gotag
|
||||
build --define gotags=bazel
|
||||
|
||||
39
WORKSPACE
39
WORKSPACE
@@ -76,9 +76,8 @@ http_archive(
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_docker",
|
||||
sha256 = "1f4e59843b61981a96835dc4ac377ad4da9f8c334ebe5e0bb3f58f80c09735f4",
|
||||
strip_prefix = "rules_docker-0.19.0",
|
||||
urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.19.0/rules_docker-v0.19.0.tar.gz"],
|
||||
sha256 = "b1e80761a8a8243d03ebca8845e9cc1ba6c82ce7c5179ce2b295cd36f7e394bf",
|
||||
urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.25.0/rules_docker-v0.25.0.tar.gz"],
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -122,32 +121,36 @@ load(
|
||||
"container_pull",
|
||||
)
|
||||
|
||||
# Pulled gcr.io/distroless/cc-debian11:latest on 2022-02-23
|
||||
container_pull(
|
||||
name = "cc_image_base",
|
||||
digest = "sha256:41036fc7ed8df0f6addc18484cef0c94a85867508967789f947e11ffd5ff0cc8",
|
||||
name = "cc_image_base_amd64",
|
||||
digest = "sha256:2a0daf90a7deb78465bfca3ef2eee6e91ce0a5706059f05d79d799a51d339523",
|
||||
registry = "gcr.io",
|
||||
repository = "distroless/cc",
|
||||
repository = "distroless/cc-debian11",
|
||||
)
|
||||
|
||||
# Pulled gcr.io/distroless/cc-debian11:debug on 2022-02-23
|
||||
container_pull(
|
||||
name = "cc_debug_image_base",
|
||||
digest = "sha256:6865ad48467c89c3c3524d4c426f52ad12d9ab7dec31fad31fae69da40eb6445",
|
||||
name = "cc_debug_image_base_amd64",
|
||||
digest = "sha256:7bd596f5f200588f13a69c268eea6ce428b222b67cd7428d6a7fef95e75c052a",
|
||||
registry = "gcr.io",
|
||||
repository = "distroless/cc",
|
||||
repository = "distroless/cc-debian11",
|
||||
)
|
||||
|
||||
# Pulled from gcr.io/distroless/base-debian11:latest on 2022-02-23
|
||||
container_pull(
|
||||
name = "go_image_base",
|
||||
digest = "sha256:b9b124f955961599e72630654107a0cf04e08e6fa777fa250b8f840728abd770",
|
||||
name = "go_image_base_amd64",
|
||||
digest = "sha256:34e682800774ecbd0954b1663d90238505f1ba5543692dbc75feef7dd4839e90",
|
||||
registry = "gcr.io",
|
||||
repository = "distroless/base",
|
||||
repository = "distroless/base-debian11",
|
||||
)
|
||||
|
||||
# Pulled from gcr.io/distroless/base-debian11:debug on 2022-02-23
|
||||
container_pull(
|
||||
name = "go_debug_image_base",
|
||||
digest = "sha256:65668d2b78d25df3d8ccf5a778d017fcaba513b52078c700083eaeef212b9979",
|
||||
name = "go_debug_image_base_amd64",
|
||||
digest = "sha256:0f503c6bfd207793bc416f20a35bf6b75d769a903c48f180ad73f60f7b60d7bd",
|
||||
registry = "gcr.io",
|
||||
repository = "distroless/base",
|
||||
repository = "distroless/base-debian11",
|
||||
)
|
||||
|
||||
container_pull(
|
||||
@@ -282,9 +285,9 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "82b01a48b143fe0f2fb7fb5f5dd385c1f934335a12d7954f08b1d45d77427b5e",
|
||||
strip_prefix = "eth2-networks-674f7a1d01d9c18345456eab76e3871b3df2126b",
|
||||
url = "https://github.com/eth-clients/eth2-networks/archive/674f7a1d01d9c18345456eab76e3871b3df2126b.tar.gz",
|
||||
sha256 = "2701e1e1a3ec10c673fe7dbdbbe6f02c8ae8c922aebbf6e720d8c72d5458aafe",
|
||||
strip_prefix = "eth2-networks-7b4897888cebef23801540236f73123e21774954",
|
||||
url = "https://github.com/eth-clients/eth2-networks/archive/7b4897888cebef23801540236f73123e21774954.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
|
||||
@@ -84,7 +84,7 @@ type FinalizationFetcher interface {
|
||||
FinalizedCheckpt() *ethpb.Checkpoint
|
||||
CurrentJustifiedCheckpt() *ethpb.Checkpoint
|
||||
PreviousJustifiedCheckpt() *ethpb.Checkpoint
|
||||
VerifyFinalizedBlkDescendant(ctx context.Context, blockRoot [32]byte) error
|
||||
InForkchoice([32]byte) bool
|
||||
IsFinalized(ctx context.Context, blockRoot [32]byte) bool
|
||||
}
|
||||
|
||||
@@ -96,24 +96,32 @@ type OptimisticModeFetcher interface {
|
||||
|
||||
// FinalizedCheckpt returns the latest finalized checkpoint from chain store.
|
||||
func (s *Service) FinalizedCheckpt() *ethpb.Checkpoint {
|
||||
s.ForkChoicer().RLock()
|
||||
defer s.ForkChoicer().RUnlock()
|
||||
cp := s.ForkChoicer().FinalizedCheckpoint()
|
||||
return ðpb.Checkpoint{Epoch: cp.Epoch, Root: bytesutil.SafeCopyBytes(cp.Root[:])}
|
||||
}
|
||||
|
||||
// PreviousJustifiedCheckpt returns the current justified checkpoint from chain store.
|
||||
func (s *Service) PreviousJustifiedCheckpt() *ethpb.Checkpoint {
|
||||
s.ForkChoicer().RLock()
|
||||
defer s.ForkChoicer().RUnlock()
|
||||
cp := s.ForkChoicer().PreviousJustifiedCheckpoint()
|
||||
return ðpb.Checkpoint{Epoch: cp.Epoch, Root: bytesutil.SafeCopyBytes(cp.Root[:])}
|
||||
}
|
||||
|
||||
// CurrentJustifiedCheckpt returns the current justified checkpoint from chain store.
|
||||
func (s *Service) CurrentJustifiedCheckpt() *ethpb.Checkpoint {
|
||||
s.ForkChoicer().RLock()
|
||||
defer s.ForkChoicer().RUnlock()
|
||||
cp := s.ForkChoicer().JustifiedCheckpoint()
|
||||
return ðpb.Checkpoint{Epoch: cp.Epoch, Root: bytesutil.SafeCopyBytes(cp.Root[:])}
|
||||
}
|
||||
|
||||
// BestJustifiedCheckpt returns the best justified checkpoint from store.
|
||||
func (s *Service) BestJustifiedCheckpt() *ethpb.Checkpoint {
|
||||
s.ForkChoicer().RLock()
|
||||
defer s.ForkChoicer().RUnlock()
|
||||
cp := s.ForkChoicer().BestJustifiedCheckpoint()
|
||||
return ðpb.Checkpoint{Epoch: cp.Epoch, Root: bytesutil.SafeCopyBytes(cp.Root[:])}
|
||||
}
|
||||
@@ -277,6 +285,8 @@ func (s *Service) CurrentFork() *ethpb.Fork {
|
||||
|
||||
// IsCanonical returns true if the input block root is part of the canonical chain.
|
||||
func (s *Service) IsCanonical(ctx context.Context, blockRoot [32]byte) (bool, error) {
|
||||
s.ForkChoicer().RLock()
|
||||
defer s.ForkChoicer().RUnlock()
|
||||
// If the block has not been finalized, check fork choice store to see if the block is canonical
|
||||
if s.cfg.ForkChoiceStore.HasNode(blockRoot) {
|
||||
return s.cfg.ForkChoiceStore.IsCanonical(blockRoot), nil
|
||||
@@ -289,6 +299,8 @@ func (s *Service) IsCanonical(ctx context.Context, blockRoot [32]byte) (bool, er
|
||||
// ChainHeads returns all possible chain heads (leaves of fork choice tree).
|
||||
// Heads roots and heads slots are returned.
|
||||
func (s *Service) ChainHeads() ([][32]byte, []primitives.Slot) {
|
||||
s.ForkChoicer().RLock()
|
||||
defer s.ForkChoicer().RUnlock()
|
||||
return s.cfg.ForkChoiceStore.Tips()
|
||||
}
|
||||
|
||||
@@ -330,6 +342,8 @@ func (s *Service) IsOptimistic(ctx context.Context) (bool, error) {
|
||||
headRoot := s.head.root
|
||||
s.headLock.RUnlock()
|
||||
|
||||
s.ForkChoicer().RLock()
|
||||
defer s.ForkChoicer().RUnlock()
|
||||
optimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(headRoot)
|
||||
if err == nil {
|
||||
return optimistic, nil
|
||||
@@ -345,6 +359,8 @@ func (s *Service) IsOptimistic(ctx context.Context) (bool, error) {
|
||||
// IsFinalized returns true if the input root is finalized.
|
||||
// It first checks latest finalized root then checks finalized root index in DB.
|
||||
func (s *Service) IsFinalized(ctx context.Context, root [32]byte) bool {
|
||||
s.ForkChoicer().RLock()
|
||||
defer s.ForkChoicer().RUnlock()
|
||||
if s.ForkChoicer().FinalizedCheckpoint().Root == root {
|
||||
return true
|
||||
}
|
||||
@@ -356,10 +372,21 @@ func (s *Service) IsFinalized(ctx context.Context, root [32]byte) bool {
|
||||
return s.cfg.BeaconDB.IsFinalizedBlock(ctx, root)
|
||||
}
|
||||
|
||||
// InForkchoice returns true if the given root is found in forkchoice
|
||||
// This in particular means that the blockroot is a descendant of the
|
||||
// finalized checkpoint
|
||||
func (s *Service) InForkchoice(root [32]byte) bool {
|
||||
s.ForkChoicer().RLock()
|
||||
defer s.ForkChoicer().RUnlock()
|
||||
return s.ForkChoicer().HasNode(root)
|
||||
}
|
||||
|
||||
// IsOptimisticForRoot takes the root as argument instead of the current head
|
||||
// and returns true if it is optimistic.
|
||||
func (s *Service) IsOptimisticForRoot(ctx context.Context, root [32]byte) (bool, error) {
|
||||
s.ForkChoicer().RLock()
|
||||
optimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(root)
|
||||
s.ForkChoicer().RUnlock()
|
||||
if err == nil {
|
||||
return optimistic, nil
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ var (
|
||||
// errWSBlockNotFoundInEpoch is returned when a block is not found in the WS cache or DB within epoch.
|
||||
errWSBlockNotFoundInEpoch = errors.New("weak subjectivity root not found in db within epoch")
|
||||
// errNotDescendantOfFinalized is returned when a block is not a descendant of the finalized checkpoint
|
||||
errNotDescendantOfFinalized = invalidBlock{error: errors.New("not descendant of finalized checkpoint")}
|
||||
ErrNotDescendantOfFinalized = invalidBlock{error: errors.New("not descendant of finalized checkpoint")}
|
||||
)
|
||||
|
||||
// An invalid block is the block that fails state transition based on the core protocol rules.
|
||||
|
||||
@@ -52,6 +52,7 @@ type head struct {
|
||||
|
||||
// This saves head info to the local service cache, it also saves the
|
||||
// new head root to the DB.
|
||||
// Caller of the method MUST aqcuire a lock on forkchoice.
|
||||
func (s *Service) saveHead(ctx context.Context, newHeadRoot [32]byte, headBlock interfaces.ReadOnlySignedBeaconBlock, headState state.BeaconState) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.saveHead")
|
||||
defer span.End()
|
||||
@@ -122,7 +123,7 @@ func (s *Service) saveHead(ctx context.Context, newHeadRoot [32]byte, headBlock
|
||||
reorgDistance.Observe(float64(dis))
|
||||
reorgDepth.Observe(float64(dep))
|
||||
|
||||
isOptimistic, err := s.IsOptimistic(ctx)
|
||||
isOptimistic, err := s.ForkChoicer().IsOptimistic(newHeadRoot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not check if node is optimistically synced")
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/transition"
|
||||
testDB "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing"
|
||||
doublylinkedtree "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/doubly-linked-tree"
|
||||
forkchoicetypes "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/types"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state/stategen"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
@@ -324,86 +323,3 @@ func TestVerifyBeaconBlock_OK(t *testing.T) {
|
||||
|
||||
assert.NoError(t, service.verifyBeaconBlock(ctx, d), "Did not receive the wanted error")
|
||||
}
|
||||
|
||||
func TestVerifyFinalizedConsistency_InconsistentRoot_DoublyLinkedTree(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
fcs := doublylinkedtree.New()
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB, fcs)),
|
||||
WithForkChoiceStore(fcs),
|
||||
}
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
|
||||
b32 := util.NewBeaconBlock()
|
||||
b32.Block.Slot = 32
|
||||
util.SaveBlock(t, ctx, service.cfg.BeaconDB, b32)
|
||||
r32, err := b32.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, service.ForkChoicer().UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: 1}))
|
||||
b33 := util.NewBeaconBlock()
|
||||
b33.Block.Slot = 33
|
||||
b33.Block.ParentRoot = r32[:]
|
||||
util.SaveBlock(t, ctx, service.cfg.BeaconDB, b33)
|
||||
r33, err := b33.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = service.VerifyFinalizedConsistency(r33[:])
|
||||
require.ErrorContains(t, "Root and finalized store are not consistent", err)
|
||||
}
|
||||
|
||||
func TestVerifyFinalizedConsistency_OK(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
opts := testServiceOptsWithDB(t)
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, 33, [32]byte{'a'}, [32]byte{}, [32]byte{}, ojc, ojc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||
|
||||
err = service.VerifyFinalizedConsistency(blkRoot[:])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestVerifyFinalizedConsistency_IsCanonical(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
opts := testServiceOptsWithDB(t)
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
|
||||
b32 := util.NewBeaconBlock()
|
||||
b32.Block.Slot = 32
|
||||
r32, err := b32.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
b33 := util.NewBeaconBlock()
|
||||
b33.Block.Slot = 33
|
||||
b33.Block.ParentRoot = r32[:]
|
||||
r33, err := b33.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, b32.Block.Slot, r32, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, b33.Block.Slot, r33, r32, params.BeaconConfig().ZeroHash, ojc, ofc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||
|
||||
jc := &forkchoicetypes.Checkpoint{Epoch: 0, Root: r32}
|
||||
bState, _ := util.DeterministicGenesisState(t, 10)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, bState, r32))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(ctx, jc))
|
||||
_, err = service.cfg.ForkChoiceStore.Head(ctx)
|
||||
require.NoError(t, err)
|
||||
err = service.VerifyFinalizedConsistency(r33[:])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -107,6 +107,11 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.ReadOnlySignedB
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify that the parent block is in forkchoice
|
||||
if !s.ForkChoicer().HasNode(b.ParentRoot()) {
|
||||
return ErrNotDescendantOfFinalized
|
||||
}
|
||||
|
||||
// Save current justified and finalized epochs for future use.
|
||||
currStoreJustifiedEpoch := s.ForkChoicer().JustifiedCheckpoint().Epoch
|
||||
currStoreFinalizedEpoch := s.ForkChoicer().FinalizedCheckpoint().Epoch
|
||||
@@ -567,6 +572,7 @@ func (s *Service) handleBlockAttestations(ctx context.Context, blk interfaces.Re
|
||||
|
||||
// InsertSlashingsToForkChoiceStore inserts attester slashing indices to fork choice store.
|
||||
// To call this function, it's caller's responsibility to ensure the slashing object is valid.
|
||||
// This function requires a write lock on forkchoice.
|
||||
func (s *Service) InsertSlashingsToForkChoiceStore(ctx context.Context, slashings []*ethpb.AttesterSlashing) {
|
||||
for _, slashing := range slashings {
|
||||
indices := blocks.SlashableAttesterIndices(slashing)
|
||||
@@ -687,26 +693,31 @@ func (s *Service) fillMissingBlockPayloadId(ctx context.Context, ti time.Time) e
|
||||
if !atHalfSlot(ti) {
|
||||
return nil
|
||||
}
|
||||
if s.CurrentSlot() == s.cfg.ForkChoiceStore.HighestReceivedBlockSlot() {
|
||||
s.ForkChoicer().RLock()
|
||||
highestReceivedSlot := s.cfg.ForkChoiceStore.HighestReceivedBlockSlot()
|
||||
s.ForkChoicer().RUnlock()
|
||||
if s.CurrentSlot() == highestReceivedSlot {
|
||||
return nil
|
||||
}
|
||||
// Head root should be empty when retrieving proposer index for the next slot.
|
||||
_, id, has := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(s.CurrentSlot()+1, [32]byte{} /* head root */)
|
||||
// There exists proposer for next slot, but we haven't called fcu w/ payload attribute yet.
|
||||
if has && id == [8]byte{} {
|
||||
missedPayloadIDFilledCount.Inc()
|
||||
headBlock, err := s.headBlock()
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
if _, err := s.notifyForkchoiceUpdate(ctx, ¬ifyForkchoiceUpdateArg{
|
||||
headState: s.headState(ctx),
|
||||
headRoot: s.headRoot(),
|
||||
headBlock: headBlock.Block(),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !has || id != [8]byte{} {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
missedPayloadIDFilledCount.Inc()
|
||||
s.headLock.RLock()
|
||||
headBlock, err := s.headBlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
headState := s.headState(ctx)
|
||||
headRoot := s.headRoot()
|
||||
s.headLock.RUnlock()
|
||||
_, err = s.notifyForkchoiceUpdate(ctx, ¬ifyForkchoiceUpdateArg{
|
||||
headState: headState,
|
||||
headRoot: headRoot,
|
||||
headBlock: headBlock.Block(),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
@@ -14,7 +13,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
mathutil "github.com/prysmaticlabs/prysm/v3/math"
|
||||
"github.com/prysmaticlabs/prysm/v3/monitoring/tracing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
||||
"go.opencensus.io/trace"
|
||||
@@ -71,10 +69,6 @@ func (s *Service) verifyBlkPreState(ctx context.Context, b interfaces.ReadOnlyBe
|
||||
return errors.New("could not reconstruct parent state")
|
||||
}
|
||||
|
||||
if err := s.VerifyFinalizedBlkDescendant(ctx, parentRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
has, err := s.cfg.StateGen.HasState(ctx, parentRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -88,35 +82,6 @@ func (s *Service) verifyBlkPreState(ctx context.Context, b interfaces.ReadOnlyBe
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyFinalizedBlkDescendant validates if input block root is a descendant of the
|
||||
// current finalized block root.
|
||||
func (s *Service) VerifyFinalizedBlkDescendant(ctx context.Context, root [32]byte) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.VerifyFinalizedBlkDescendant")
|
||||
defer span.End()
|
||||
finalized := s.ForkChoicer().FinalizedCheckpoint()
|
||||
fRoot := s.ensureRootNotZeros(finalized.Root)
|
||||
fSlot, err := slots.EpochStart(finalized.Epoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bFinalizedRoot, err := s.ancestor(ctx, root[:], fSlot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized block root")
|
||||
}
|
||||
if bFinalizedRoot == nil {
|
||||
return fmt.Errorf("no finalized block known for block %#x", bytesutil.Trunc(root[:]))
|
||||
}
|
||||
|
||||
if !bytes.Equal(bFinalizedRoot, fRoot[:]) {
|
||||
err := fmt.Errorf("block %#x is not a descendant of the current finalized block slot %d, %#x != %#x",
|
||||
bytesutil.Trunc(root[:]), fSlot, bytesutil.Trunc(bFinalizedRoot),
|
||||
bytesutil.Trunc(fRoot[:]))
|
||||
tracing.AnnotateError(span, err)
|
||||
return invalidBlock{error: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyBlkFinalizedSlot validates input block is not less than or equal
|
||||
// to current finalized slot.
|
||||
func (s *Service) verifyBlkFinalizedSlot(b interfaces.ReadOnlyBeaconBlock) error {
|
||||
@@ -272,7 +237,7 @@ func (s *Service) fillInForkChoiceMissingBlocks(ctx context.Context, blk interfa
|
||||
return nil
|
||||
}
|
||||
if root != s.ensureRootNotZeros(finalized.Root) && !s.ForkChoicer().HasNode(root) {
|
||||
return errNotDescendantOfFinalized
|
||||
return ErrNotDescendantOfFinalized
|
||||
}
|
||||
return s.cfg.ForkChoiceStore.InsertChain(ctx, pendingNodes)
|
||||
}
|
||||
|
||||
@@ -65,6 +65,10 @@ func TestStore_OnBlock(t *testing.T) {
|
||||
st, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, st.Copy(), validGenesisRoot))
|
||||
ojc := ðpb.Checkpoint{}
|
||||
stfcs, root, err := prepareForkchoiceState(ctx, 0, validGenesisRoot, [32]byte{}, [32]byte{}, ojc, ojc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fcs.InsertNode(ctx, stfcs, root))
|
||||
roots, err := blockTree1(t, beaconDB, validGenesisRoot[:])
|
||||
require.NoError(t, err)
|
||||
random := util.NewBeaconBlock()
|
||||
@@ -73,11 +77,16 @@ func TestStore_OnBlock(t *testing.T) {
|
||||
util.SaveBlock(t, ctx, beaconDB, random)
|
||||
randomParentRoot, err := random.Block.HashTreeRoot()
|
||||
assert.NoError(t, err)
|
||||
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(ctx, ðpb.StateSummary{Slot: st.Slot(), Root: randomParentRoot[:]}))
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, st.Copy(), randomParentRoot))
|
||||
randomParentRoot2 := roots[1]
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(ctx, ðpb.StateSummary{Slot: st.Slot(), Root: randomParentRoot2}))
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, st.Copy(), bytesutil.ToBytes32(randomParentRoot2)))
|
||||
stfcs, root, err = prepareForkchoiceState(ctx, 2, bytesutil.ToBytes32(randomParentRoot2),
|
||||
validGenesisRoot, [32]byte{'r'}, ojc, ojc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fcs.InsertNode(ctx, stfcs, root))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -108,10 +117,11 @@ func TestStore_OnBlock(t *testing.T) {
|
||||
blk: func() *ethpb.SignedBeaconBlock {
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block.ParentRoot = randomParentRoot[:]
|
||||
b.Block.Slot = 2
|
||||
return b
|
||||
}(),
|
||||
s: st.Copy(),
|
||||
wantErrString: "is not a descendant of the current finalized block",
|
||||
wantErrString: "not descendant of finalized checkpoint",
|
||||
},
|
||||
{
|
||||
name: "same slot as finalized block",
|
||||
@@ -441,7 +451,7 @@ func TestFillForkChoiceMissingBlocks_FinalizedSibling(t *testing.T) {
|
||||
|
||||
err = service.fillInForkChoiceMissingBlocks(
|
||||
context.Background(), wsb.Block(), beaconState.FinalizedCheckpoint(), beaconState.CurrentJustifiedCheckpoint())
|
||||
require.Equal(t, errNotDescendantOfFinalized.Error(), err.Error())
|
||||
require.Equal(t, ErrNotDescendantOfFinalized.Error(), err.Error())
|
||||
}
|
||||
|
||||
// blockTree1 constructs the following tree:
|
||||
@@ -707,87 +717,6 @@ func TestEnsureRootNotZeroHashes(t *testing.T) {
|
||||
assert.Equal(t, root, r, "Did not get wanted justified root")
|
||||
}
|
||||
|
||||
func TestVerifyBlkDescendant(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
fcs := doublylinkedtree.New()
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB, fcs)),
|
||||
WithForkChoiceStore(fcs),
|
||||
}
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block.Slot = 32
|
||||
r, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
util.SaveBlock(t, ctx, beaconDB, b)
|
||||
|
||||
b1 := util.NewBeaconBlock()
|
||||
b1.Block.Slot = 32
|
||||
b1.Block.Body.Graffiti = bytesutil.PadTo([]byte{'a'}, 32)
|
||||
r1, err := b1.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
util.SaveBlock(t, ctx, beaconDB, b1)
|
||||
|
||||
type args struct {
|
||||
parentRoot [32]byte
|
||||
finalizedRoot [32]byte
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantedErr string
|
||||
invalidBlockRoot bool
|
||||
}{
|
||||
{
|
||||
name: "could not get finalized block in block service cache",
|
||||
args: args{
|
||||
finalizedRoot: [32]byte{'a'},
|
||||
},
|
||||
wantedErr: "block not found in cache or db",
|
||||
},
|
||||
{
|
||||
name: "could not get finalized block root in DB",
|
||||
args: args{
|
||||
finalizedRoot: r,
|
||||
parentRoot: [32]byte{'a'},
|
||||
},
|
||||
wantedErr: "could not get finalized block root",
|
||||
},
|
||||
{
|
||||
name: "is not descendant",
|
||||
args: args{
|
||||
finalizedRoot: r1,
|
||||
parentRoot: r,
|
||||
},
|
||||
wantedErr: "is not a descendant of the current finalized block slot",
|
||||
invalidBlockRoot: true,
|
||||
},
|
||||
{
|
||||
name: "is descendant",
|
||||
args: args{
|
||||
finalizedRoot: r,
|
||||
parentRoot: r,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.ForkChoicer().UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Root: tt.args.finalizedRoot, Epoch: 1}))
|
||||
err = service.VerifyFinalizedBlkDescendant(ctx, tt.args.parentRoot)
|
||||
if tt.wantedErr != "" {
|
||||
assert.ErrorContains(t, tt.wantedErr, err)
|
||||
if tt.invalidBlockRoot {
|
||||
require.Equal(t, true, IsInvalidBlock(err))
|
||||
}
|
||||
} else if err != nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleEpochBoundary_UpdateFirstSlot(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
opts := testServiceOptsNoDB()
|
||||
@@ -2140,63 +2069,14 @@ func TestNoViableHead_Reboot(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
root, err = b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.onBlock(ctx, wsb, root))
|
||||
// Check that the head is still INVALID and the node is optimistic
|
||||
// We use onBlockBatch here because the valid chain is missing in forkchoice
|
||||
require.NoError(t, service.onBlockBatch(ctx, []interfaces.ReadOnlySignedBeaconBlock{wsb}, [][32]byte{root}))
|
||||
// Check that the head is now VALID and the node is not optimistic
|
||||
require.Equal(t, genesisRoot, service.ensureRootNotZeros(service.ForkChoicer().CachedHeadRoot()))
|
||||
headRoot, err = service.HeadRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, genesisRoot, bytesutil.ToBytes32(headRoot))
|
||||
|
||||
optimistic, err = service.IsOptimistic(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, optimistic)
|
||||
st, err = service.cfg.StateGen.StateByRoot(ctx, root)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Import blocks 21--23
|
||||
for i := 21; i < 24; i++ {
|
||||
driftGenesisTime(service, int64(i), 0)
|
||||
require.NoError(t, err)
|
||||
b, err := util.GenerateFullBlockBellatrix(st, keys, util.DefaultBlockGenConfig(), primitives.Slot(i))
|
||||
require.NoError(t, err)
|
||||
wsb, err := consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
root, err = b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
err = service.onBlock(ctx, wsb, root)
|
||||
require.NoError(t, err)
|
||||
st, err = service.cfg.StateGen.StateByRoot(ctx, root)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
// Head should still be INVALID and the node is optimistic
|
||||
require.Equal(t, genesisRoot, service.ensureRootNotZeros(service.ForkChoicer().CachedHeadRoot()))
|
||||
headRoot, err = service.HeadRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, genesisRoot, bytesutil.ToBytes32(headRoot))
|
||||
|
||||
optimistic, err = service.IsOptimistic(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, optimistic)
|
||||
|
||||
// Import block 24, it should justify Epoch 3 and become HEAD, the node
|
||||
// recovers
|
||||
driftGenesisTime(service, 24, 0)
|
||||
b, err = util.GenerateFullBlockBellatrix(st, keys, util.DefaultBlockGenConfig(), 24)
|
||||
require.NoError(t, err)
|
||||
wsb, err = consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
root, err = b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
service.ForkChoicer().SetBalancesByRooter(service.cfg.StateGen.ActiveNonSlashedBalancesByRoot)
|
||||
err = service.onBlock(ctx, wsb, root)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, root, service.ForkChoicer().CachedHeadRoot())
|
||||
headRoot, err = service.HeadRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, root, bytesutil.ToBytes32(headRoot))
|
||||
|
||||
sjc = service.CurrentJustifiedCheckpt()
|
||||
require.Equal(t, primitives.Epoch(3), sjc.Epoch)
|
||||
optimistic, err = service.IsOptimistic(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, optimistic)
|
||||
|
||||
@@ -29,7 +29,7 @@ type AttestationStateFetcher interface {
|
||||
type AttestationReceiver interface {
|
||||
AttestationStateFetcher
|
||||
VerifyLmdFfgConsistency(ctx context.Context, att *ethpb.Attestation) error
|
||||
VerifyFinalizedConsistency(root []byte) error
|
||||
InForkchoice([32]byte) bool
|
||||
}
|
||||
|
||||
// AttestationTargetState returns the pre state of attestation.
|
||||
@@ -60,19 +60,6 @@ func (s *Service) VerifyLmdFfgConsistency(ctx context.Context, a *ethpb.Attestat
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyFinalizedConsistency verifies input root is consistent with finalized store.
|
||||
// When the input root is not be consistent with finalized store then we know it is not
|
||||
// on the finalized check point that leads to current canonical chain and should be rejected accordingly.
|
||||
func (s *Service) VerifyFinalizedConsistency(root []byte) error {
|
||||
// A canonical root implies the root to has an ancestor that aligns with finalized check point.
|
||||
// In this case, we could exit early to save on additional computation.
|
||||
blockRoot := bytesutil.ToBytes32(root)
|
||||
if !s.cfg.ForkChoiceStore.HasNode(blockRoot) {
|
||||
return errors.New("Root and finalized store are not consistent")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// This routine processes fork choice attestations from the pool to account for validator votes and fork choice.
|
||||
func (s *Service) spawnProcessAttestationsRoutine(stateFeed *event.Feed) {
|
||||
// Wait for state to be initialized.
|
||||
@@ -121,11 +108,9 @@ func (s *Service) spawnProcessAttestationsRoutine(stateFeed *event.Feed) {
|
||||
// UpdateHead updates the canonical head of the chain based on information from fork-choice attestations and votes.
|
||||
// It requires no external inputs.
|
||||
func (s *Service) UpdateHead(ctx context.Context) error {
|
||||
// Only one process can process attestations and update head at a time.
|
||||
s.processAttestationsLock.Lock()
|
||||
defer s.processAttestationsLock.Unlock()
|
||||
|
||||
start := time.Now()
|
||||
s.ForkChoicer().Lock()
|
||||
defer s.ForkChoicer().Unlock()
|
||||
s.processAttestations(ctx)
|
||||
processAttsElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
statefeed "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/feed/state"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v3/monitoring/tracing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/runtime/version"
|
||||
@@ -45,6 +46,9 @@ func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySig
|
||||
return err
|
||||
}
|
||||
|
||||
s.cfg.ForkChoiceStore.Lock()
|
||||
defer s.cfg.ForkChoiceStore.Unlock()
|
||||
|
||||
// Apply state transition on the new block.
|
||||
if err := s.onBlock(ctx, blockCopy, blockRoot); err != nil {
|
||||
err := errors.Wrap(err, "could not process block")
|
||||
@@ -63,11 +67,13 @@ func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySig
|
||||
}
|
||||
|
||||
// Reports on block and fork choice metrics.
|
||||
finalized := s.FinalizedCheckpt()
|
||||
cp := s.ForkChoicer().FinalizedCheckpoint()
|
||||
finalized := ðpb.Checkpoint{Epoch: cp.Epoch, Root: bytesutil.SafeCopyBytes(cp.Root[:])}
|
||||
reportSlotMetrics(blockCopy.Block().Slot(), s.HeadSlot(), s.CurrentSlot(), finalized)
|
||||
|
||||
// Log block sync status.
|
||||
justified := s.CurrentJustifiedCheckpt()
|
||||
cp = s.ForkChoicer().JustifiedCheckpoint()
|
||||
justified := ðpb.Checkpoint{Epoch: cp.Epoch, Root: bytesutil.SafeCopyBytes(cp.Root[:])}
|
||||
if err := logBlockSyncStatus(blockCopy.Block(), blockRoot, justified, finalized, receivedTime, uint64(s.genesisTime.Unix())); err != nil {
|
||||
log.WithError(err).Error("Unable to log block sync status")
|
||||
}
|
||||
@@ -90,6 +96,9 @@ func (s *Service) ReceiveBlockBatch(ctx context.Context, blocks []interfaces.Rea
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.ReceiveBlockBatch")
|
||||
defer span.End()
|
||||
|
||||
s.cfg.ForkChoiceStore.Lock()
|
||||
defer s.cfg.ForkChoiceStore.Unlock()
|
||||
|
||||
// Apply state transition on the incoming newly received block batches, one by one.
|
||||
if err := s.onBlockBatch(ctx, blocks, blkRoots); err != nil {
|
||||
err := errors.Wrap(err, "could not process block in batch")
|
||||
@@ -114,14 +123,15 @@ func (s *Service) ReceiveBlockBatch(ctx context.Context, blocks []interfaces.Rea
|
||||
})
|
||||
|
||||
// Reports on blockCopy and fork choice metrics.
|
||||
finalized := s.FinalizedCheckpt()
|
||||
cp := s.ForkChoicer().FinalizedCheckpoint()
|
||||
finalized := ðpb.Checkpoint{Epoch: cp.Epoch, Root: bytesutil.SafeCopyBytes(cp.Root[:])}
|
||||
reportSlotMetrics(blockCopy.Block().Slot(), s.HeadSlot(), s.CurrentSlot(), finalized)
|
||||
}
|
||||
|
||||
if err := s.cfg.BeaconDB.SaveBlocks(ctx, s.getInitSyncBlocks()); err != nil {
|
||||
return err
|
||||
}
|
||||
finalized := s.FinalizedCheckpt()
|
||||
finalized := s.ForkChoicer().FinalizedCheckpoint()
|
||||
if finalized == nil {
|
||||
return errNilFinalizedInStore
|
||||
}
|
||||
@@ -142,6 +152,8 @@ func (s *Service) HasBlock(ctx context.Context, root [32]byte) bool {
|
||||
|
||||
// ReceiveAttesterSlashing receives an attester slashing and inserts it to forkchoice
|
||||
func (s *Service) ReceiveAttesterSlashing(ctx context.Context, slashing *ethpb.AttesterSlashing) {
|
||||
s.ForkChoicer().Lock()
|
||||
defer s.ForkChoicer().Unlock()
|
||||
s.InsertSlashingsToForkChoiceStore(ctx, []*ethpb.AttesterSlashing{slashing})
|
||||
}
|
||||
|
||||
@@ -179,11 +191,12 @@ func (s *Service) handleBlockBLSToExecChanges(blk interfaces.ReadOnlyBeaconBlock
|
||||
|
||||
// This checks whether it's time to start saving hot state to DB.
|
||||
// It's time when there's `epochsSinceFinalitySaveHotStateDB` epochs of non-finality.
|
||||
// Requires a read lock on forkchoice
|
||||
func (s *Service) checkSaveHotStateDB(ctx context.Context) error {
|
||||
currentEpoch := slots.ToEpoch(s.CurrentSlot())
|
||||
// Prevent `sinceFinality` going underflow.
|
||||
var sinceFinality primitives.Epoch
|
||||
finalized := s.FinalizedCheckpt()
|
||||
finalized := s.ForkChoicer().FinalizedCheckpoint()
|
||||
if finalized == nil {
|
||||
return errNilFinalizedInStore
|
||||
}
|
||||
|
||||
@@ -97,7 +97,8 @@ func TestService_ReceiveBlock(t *testing.T) {
|
||||
),
|
||||
},
|
||||
check: func(t *testing.T, s *Service) {
|
||||
pending := s.cfg.ExitPool.PendingExits(genesis, 1, true /* no limit */)
|
||||
pending, err := s.cfg.ExitPool.PendingExits()
|
||||
require.NoError(t, err)
|
||||
if len(pending) != 0 {
|
||||
t.Errorf(
|
||||
"Did not mark the correct number of exits. Got %d pending but wanted %d",
|
||||
|
||||
@@ -44,20 +44,19 @@ import (
|
||||
// Service represents a service that handles the internal
|
||||
// logic of managing the full PoS beacon chain.
|
||||
type Service struct {
|
||||
cfg *config
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
genesisTime time.Time
|
||||
head *head
|
||||
headLock sync.RWMutex
|
||||
originBlockRoot [32]byte // genesis root, or weak subjectivity checkpoint root, depending on how the node is initialized
|
||||
nextEpochBoundarySlot primitives.Slot
|
||||
boundaryRoots [][32]byte
|
||||
checkpointStateCache *cache.CheckpointStateCache
|
||||
initSyncBlocks map[[32]byte]interfaces.ReadOnlySignedBeaconBlock
|
||||
initSyncBlocksLock sync.RWMutex
|
||||
wsVerifier *WeakSubjectivityVerifier
|
||||
processAttestationsLock sync.Mutex
|
||||
cfg *config
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
genesisTime time.Time
|
||||
head *head
|
||||
headLock sync.RWMutex
|
||||
originBlockRoot [32]byte // genesis root, or weak subjectivity checkpoint root, depending on how the node is initialized
|
||||
nextEpochBoundarySlot primitives.Slot
|
||||
boundaryRoots [][32]byte
|
||||
checkpointStateCache *cache.CheckpointStateCache
|
||||
initSyncBlocks map[[32]byte]interfaces.ReadOnlySignedBeaconBlock
|
||||
initSyncBlocksLock sync.RWMutex
|
||||
wsVerifier *WeakSubjectivityVerifier
|
||||
}
|
||||
|
||||
// config options for the service.
|
||||
@@ -200,6 +199,8 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error {
|
||||
}
|
||||
|
||||
fRoot := s.ensureRootNotZeros(bytesutil.ToBytes32(finalized.Root))
|
||||
s.cfg.ForkChoiceStore.Lock()
|
||||
defer s.cfg.ForkChoiceStore.Unlock()
|
||||
if err := s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(s.ctx, &forkchoicetypes.Checkpoint{Epoch: justified.Epoch,
|
||||
Root: bytesutil.ToBytes32(justified.Root)}); err != nil {
|
||||
return errors.Wrap(err, "could not update forkchoice's justified checkpoint")
|
||||
@@ -426,6 +427,8 @@ func (s *Service) saveGenesisData(ctx context.Context, genesisState state.Beacon
|
||||
s.originBlockRoot = genesisBlkRoot
|
||||
s.cfg.StateGen.SaveFinalizedState(0 /*slot*/, genesisBlkRoot, genesisState)
|
||||
|
||||
s.cfg.ForkChoiceStore.Lock()
|
||||
defer s.cfg.ForkChoiceStore.Unlock()
|
||||
if err := s.cfg.ForkChoiceStore.InsertNode(ctx, genesisState, genesisBlkRoot); err != nil {
|
||||
log.WithError(err).Fatal("Could not process genesis block for fork choice")
|
||||
}
|
||||
@@ -446,6 +449,7 @@ func (s *Service) saveGenesisData(ctx context.Context, genesisState state.Beacon
|
||||
// 1.) Check fork choice store.
|
||||
// 2.) Check DB.
|
||||
// Checking 1.) is ten times faster than checking 2.)
|
||||
// this function requires a lock in forkchoice
|
||||
func (s *Service) hasBlock(ctx context.Context, root [32]byte) bool {
|
||||
if s.cfg.ForkChoiceStore.HasNode(root) {
|
||||
return true
|
||||
|
||||
@@ -32,6 +32,7 @@ var ErrNilState = errors.New("nil state")
|
||||
|
||||
// ChainService defines the mock interface for testing
|
||||
type ChainService struct {
|
||||
NotFinalized bool
|
||||
Optimistic bool
|
||||
ValidAttestation bool
|
||||
ValidatorsRoot [32]byte
|
||||
@@ -389,11 +390,6 @@ func (_ *ChainService) HeadGenesisValidatorsRoot() [32]byte {
|
||||
return [32]byte{}
|
||||
}
|
||||
|
||||
// VerifyFinalizedBlkDescendant mocks VerifyBlkDescendant and always returns nil.
|
||||
func (s *ChainService) VerifyFinalizedBlkDescendant(_ context.Context, _ [32]byte) error {
|
||||
return s.VerifyBlkDescendantErr
|
||||
}
|
||||
|
||||
// VerifyLmdFfgConsistency mocks VerifyLmdFfgConsistency and always returns nil.
|
||||
func (_ *ChainService) VerifyLmdFfgConsistency(_ context.Context, a *ethpb.Attestation) error {
|
||||
if !bytes.Equal(a.Data.BeaconBlockRoot, a.Data.Target.Root) {
|
||||
@@ -402,14 +398,6 @@ func (_ *ChainService) VerifyLmdFfgConsistency(_ context.Context, a *ethpb.Attes
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyFinalizedConsistency mocks VerifyFinalizedConsistency and always returns nil.
|
||||
func (s *ChainService) VerifyFinalizedConsistency(r []byte) error {
|
||||
if !bytes.Equal(r, s.FinalizedCheckPoint.Root) {
|
||||
return errors.New("Root and finalized store are not consistent")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChainHeads mocks ChainHeads and always return nil.
|
||||
func (_ *ChainService) ChainHeads() ([][32]byte, []primitives.Slot) {
|
||||
return [][32]byte{
|
||||
@@ -459,6 +447,11 @@ func (s *ChainService) IsOptimistic(_ context.Context) (bool, error) {
|
||||
return s.Optimistic, nil
|
||||
}
|
||||
|
||||
// InForkchoice mocks the same method in the chain service
|
||||
func (s *ChainService) InForkchoice(_ [32]byte) bool {
|
||||
return !s.NotFinalized
|
||||
}
|
||||
|
||||
// IsOptimisticForRoot mocks the same method in the chain service.
|
||||
func (s *ChainService) IsOptimisticForRoot(_ context.Context, root [32]byte) (bool, error) {
|
||||
s.OptimisticCheckRootReceived = root
|
||||
|
||||
@@ -44,49 +44,51 @@ import (
|
||||
// increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
|
||||
// else:
|
||||
// decrease_balance(state, participant_index, participant_reward)
|
||||
func ProcessSyncAggregate(ctx context.Context, s state.BeaconState, sync *ethpb.SyncAggregate) (state.BeaconState, error) {
|
||||
s, votedKeys, err := processSyncAggregate(ctx, s, sync)
|
||||
func ProcessSyncAggregate(ctx context.Context, s state.BeaconState, sync *ethpb.SyncAggregate) (state.BeaconState, uint64, error) {
|
||||
s, votedKeys, reward, err := processSyncAggregate(ctx, s, sync)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not filter sync committee votes")
|
||||
return nil, 0, errors.Wrap(err, "could not filter sync committee votes")
|
||||
}
|
||||
|
||||
if err := VerifySyncCommitteeSig(s, votedKeys, sync.SyncCommitteeSignature); err != nil {
|
||||
return nil, errors.Wrap(err, "could not verify sync committee signature")
|
||||
return nil, 0, errors.Wrap(err, "could not verify sync committee signature")
|
||||
}
|
||||
return s, nil
|
||||
return s, reward, nil
|
||||
}
|
||||
|
||||
// processSyncAggregate applies all the logic in the spec function `process_sync_aggregate` except
|
||||
// verifying the BLS signatures. It returns the modified beacons state and the list of validators'
|
||||
// public keys that voted, for future signature verification.
|
||||
// verifying the BLS signatures. It returns the modified beacons state, the list of validators'
|
||||
// public keys that voted (for future signature verification) and the proposer reward for including
|
||||
// sync aggregate messages.
|
||||
func processSyncAggregate(ctx context.Context, s state.BeaconState, sync *ethpb.SyncAggregate) (
|
||||
state.BeaconState,
|
||||
[]bls.PublicKey,
|
||||
uint64,
|
||||
error) {
|
||||
currentSyncCommittee, err := s.CurrentSyncCommittee()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
if currentSyncCommittee == nil {
|
||||
return nil, nil, errors.New("nil current sync committee in state")
|
||||
return nil, nil, 0, errors.New("nil current sync committee in state")
|
||||
}
|
||||
committeeKeys := currentSyncCommittee.Pubkeys
|
||||
if sync.SyncCommitteeBits.Len() > uint64(len(committeeKeys)) {
|
||||
return nil, nil, errors.New("bits length exceeds committee length")
|
||||
return nil, nil, 0, errors.New("bits length exceeds committee length")
|
||||
}
|
||||
votedKeys := make([]bls.PublicKey, 0, len(committeeKeys))
|
||||
|
||||
activeBalance, err := helpers.TotalActiveBalance(s)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
proposerReward, participantReward, err := SyncRewards(activeBalance)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
proposerIndex, err := helpers.BeaconProposerIndex(ctx, s)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
earnedProposerReward := uint64(0)
|
||||
@@ -94,29 +96,29 @@ func processSyncAggregate(ctx context.Context, s state.BeaconState, sync *ethpb.
|
||||
vIdx, exists := s.ValidatorIndexByPubkey(bytesutil.ToBytes48(committeeKeys[i]))
|
||||
// Impossible scenario.
|
||||
if !exists {
|
||||
return nil, nil, errors.New("validator public key does not exist in state")
|
||||
return nil, nil, 0, errors.New("validator public key does not exist in state")
|
||||
}
|
||||
|
||||
if sync.SyncCommitteeBits.BitAt(i) {
|
||||
pubKey, err := bls.PublicKeyFromBytes(committeeKeys[i])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
votedKeys = append(votedKeys, pubKey)
|
||||
if err := helpers.IncreaseBalance(s, vIdx, participantReward); err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
earnedProposerReward += proposerReward
|
||||
} else {
|
||||
if err := helpers.DecreaseBalance(s, vIdx, participantReward); err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := helpers.IncreaseBalance(s, proposerIndex, earnedProposerReward); err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
return s, votedKeys, err
|
||||
return s, votedKeys, earnedProposerReward, err
|
||||
}
|
||||
|
||||
// VerifySyncCommitteeSig verifies sync committee signature `syncSig` is valid with respect to public keys `syncKeys`.
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/util"
|
||||
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
||||
@@ -53,8 +54,10 @@ func TestProcessSyncCommittee_PerfectParticipation(t *testing.T) {
|
||||
SyncCommitteeSignature: aggregatedSig,
|
||||
}
|
||||
|
||||
beaconState, err = altair.ProcessSyncAggregate(context.Background(), beaconState, syncAggregate)
|
||||
var reward uint64
|
||||
beaconState, reward, err = altair.ProcessSyncAggregate(context.Background(), beaconState, syncAggregate)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, uint64(72192), reward)
|
||||
|
||||
// Use a non-sync committee index to compare profitability.
|
||||
syncCommittee := make(map[primitives.ValidatorIndex]bool)
|
||||
@@ -127,7 +130,7 @@ func TestProcessSyncCommittee_MixParticipation_BadSignature(t *testing.T) {
|
||||
SyncCommitteeSignature: aggregatedSig,
|
||||
}
|
||||
|
||||
_, err = altair.ProcessSyncAggregate(context.Background(), beaconState, syncAggregate)
|
||||
_, _, err = altair.ProcessSyncAggregate(context.Background(), beaconState, syncAggregate)
|
||||
require.ErrorContains(t, "invalid sync committee signature", err)
|
||||
}
|
||||
|
||||
@@ -164,7 +167,7 @@ func TestProcessSyncCommittee_MixParticipation_GoodSignature(t *testing.T) {
|
||||
SyncCommitteeSignature: aggregatedSig,
|
||||
}
|
||||
|
||||
_, err = altair.ProcessSyncAggregate(context.Background(), beaconState, syncAggregate)
|
||||
_, _, err = altair.ProcessSyncAggregate(context.Background(), beaconState, syncAggregate)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -189,7 +192,7 @@ func TestProcessSyncCommittee_DontPrecompute(t *testing.T) {
|
||||
SyncCommitteeBits: syncBits,
|
||||
}
|
||||
require.NoError(t, beaconState.UpdateBalancesAtIndex(idx, 0))
|
||||
st, votedKeys, err := altair.ProcessSyncAggregateEported(context.Background(), beaconState, syncAggregate)
|
||||
st, votedKeys, _, err := altair.ProcessSyncAggregateEported(context.Background(), beaconState, syncAggregate)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 511, len(votedKeys))
|
||||
require.DeepEqual(t, committeeKeys[0], votedKeys[0].Marshal())
|
||||
@@ -212,7 +215,7 @@ func TestProcessSyncCommittee_processSyncAggregate(t *testing.T) {
|
||||
SyncCommitteeBits: syncBits,
|
||||
}
|
||||
|
||||
st, votedKeys, err := altair.ProcessSyncAggregateEported(context.Background(), beaconState, syncAggregate)
|
||||
st, votedKeys, _, err := altair.ProcessSyncAggregateEported(context.Background(), beaconState, syncAggregate)
|
||||
require.NoError(t, err)
|
||||
votedMap := make(map[[fieldparams.BLSPubkeyLength]byte]bool)
|
||||
for _, key := range votedKeys {
|
||||
|
||||
@@ -116,7 +116,7 @@ func VerifyExitAndSignature(
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyExitConditions implements the spec defined validation for voluntary exits(excluding signatures).
|
||||
// verifyExitConditions implements the spec defined validation for voluntary exits (excluding signatures).
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
//
|
||||
|
||||
@@ -351,7 +351,7 @@ func ProcessBlockForStateRoot(
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get sync aggregate from block")
|
||||
}
|
||||
state, err = altair.ProcessSyncAggregate(ctx, state, sa)
|
||||
state, _, err = altair.ProcessSyncAggregate(ctx, state, sa)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "process_sync_aggregate failed")
|
||||
}
|
||||
|
||||
@@ -178,7 +178,6 @@ func SlashValidator(
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get proposer idx")
|
||||
}
|
||||
// In phase 0, the proposer is the whistleblower.
|
||||
whistleBlowerIdx := proposerIdx
|
||||
whistleblowerReward := validator.EffectiveBalance / params.BeaconConfig().WhistleBlowerRewardQuotient
|
||||
proposerReward := whistleblowerReward / proposerRewardQuotient
|
||||
|
||||
@@ -18,7 +18,6 @@ go_library(
|
||||
"log.go",
|
||||
"migration.go",
|
||||
"migration_archived_index.go",
|
||||
"migration_blinded_beacon_blocks.go",
|
||||
"migration_block_slot_index.go",
|
||||
"migration_state_validators.go",
|
||||
"schema.go",
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
ssz "github.com/prysmaticlabs/fastssz"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/db/filters"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
|
||||
@@ -272,6 +271,22 @@ func (s *Store) SaveBlock(ctx context.Context, signed interfaces.ReadOnlySignedB
|
||||
return s.SaveBlocks(ctx, []interfaces.ReadOnlySignedBeaconBlock{signed})
|
||||
}
|
||||
|
||||
// This function determines if we should save beacon blocks in the DB in blinded format by checking
|
||||
// if a `saveBlindedBeaconBlocks` key exists in the database. Otherwise, we check if the last
|
||||
// blocked stored to check if it is blinded, and then write that `saveBlindedBeaconBlocks` key
|
||||
// to the DB for future checks.
|
||||
func (s *Store) shouldSaveBlinded(ctx context.Context) (bool, error) {
|
||||
var saveBlinded bool
|
||||
if err := s.db.View(func(tx *bolt.Tx) error {
|
||||
metadataBkt := tx.Bucket(chainMetadataBucket)
|
||||
saveBlinded = len(metadataBkt.Get(saveBlindedBeaconBlocksKey)) > 0
|
||||
return nil
|
||||
}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return saveBlinded, nil
|
||||
}
|
||||
|
||||
// SaveBlocks via bulk updates to the db.
|
||||
func (s *Store) SaveBlocks(ctx context.Context, blks []interfaces.ReadOnlySignedBeaconBlock) error {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveBlocks")
|
||||
@@ -287,7 +302,7 @@ func (s *Store) SaveBlocks(ctx context.Context, blks []interfaces.ReadOnlySigned
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
enc, err := marshalBlock(ctx, blk)
|
||||
enc, err := s.marshalBlock(ctx, blk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -296,6 +311,10 @@ func (s *Store) SaveBlocks(ctx context.Context, blks []interfaces.ReadOnlySigned
|
||||
indicesByBucket := createBlockIndicesFromBlock(ctx, blk.Block())
|
||||
indicesForBlocks[i] = indicesByBucket
|
||||
}
|
||||
saveBlinded, err := s.shouldSaveBlinded(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(blocksBucket)
|
||||
for i, blk := range blks {
|
||||
@@ -305,7 +324,7 @@ func (s *Store) SaveBlocks(ctx context.Context, blks []interfaces.ReadOnlySigned
|
||||
if err := updateValueForIndices(ctx, indicesForBlocks[i], blockRoots[i], tx); err != nil {
|
||||
return errors.Wrap(err, "could not update DB indices")
|
||||
}
|
||||
if features.Get().EnableOnlyBlindedBeaconBlocks {
|
||||
if saveBlinded {
|
||||
blindedBlock, err := blk.ToBlinded()
|
||||
if err != nil {
|
||||
if !errors.Is(err, blocks.ErrUnsupportedVersion) {
|
||||
@@ -809,50 +828,71 @@ func unmarshalBlock(_ context.Context, enc []byte) (interfaces.ReadOnlySignedBea
|
||||
return blocks.NewSignedBeaconBlock(rawBlock)
|
||||
}
|
||||
|
||||
// marshal versioned beacon block from struct type down to bytes.
|
||||
func marshalBlock(_ context.Context, blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
|
||||
func (s *Store) marshalBlock(
|
||||
ctx context.Context,
|
||||
blk interfaces.ReadOnlySignedBeaconBlock,
|
||||
) ([]byte, error) {
|
||||
shouldBlind, err := s.shouldSaveBlinded(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if shouldBlind {
|
||||
return marshalBlockBlinded(ctx, blk)
|
||||
}
|
||||
return marshalBlockFull(ctx, blk)
|
||||
}
|
||||
|
||||
// Encodes a full beacon block to the DB with its associated key.
|
||||
func marshalBlockFull(
|
||||
_ context.Context,
|
||||
blk interfaces.ReadOnlySignedBeaconBlock,
|
||||
) ([]byte, error) {
|
||||
var encodedBlock []byte
|
||||
var err error
|
||||
blockToSave := blk
|
||||
if features.Get().EnableOnlyBlindedBeaconBlocks {
|
||||
blindedBlock, err := blk.ToBlinded()
|
||||
switch {
|
||||
case errors.Is(err, blocks.ErrUnsupportedVersion):
|
||||
encodedBlock, err = blk.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not marshal non-blinded block")
|
||||
}
|
||||
case err != nil:
|
||||
return nil, errors.Wrap(err, "could not convert block to blinded format")
|
||||
default:
|
||||
encodedBlock, err = blindedBlock.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not marshal blinded block")
|
||||
}
|
||||
blockToSave = blindedBlock
|
||||
}
|
||||
} else {
|
||||
encodedBlock, err = blk.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encodedBlock, err = blk.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch blockToSave.Version() {
|
||||
switch blk.Version() {
|
||||
case version.Capella:
|
||||
if blockToSave.IsBlinded() {
|
||||
return snappy.Encode(nil, append(capellaBlindKey, encodedBlock...)), nil
|
||||
}
|
||||
return snappy.Encode(nil, append(capellaKey, encodedBlock...)), nil
|
||||
case version.Bellatrix:
|
||||
if blockToSave.IsBlinded() {
|
||||
return snappy.Encode(nil, append(bellatrixBlindKey, encodedBlock...)), nil
|
||||
}
|
||||
return snappy.Encode(nil, append(bellatrixKey, encodedBlock...)), nil
|
||||
case version.Altair:
|
||||
return snappy.Encode(nil, append(altairKey, encodedBlock...)), nil
|
||||
case version.Phase0:
|
||||
return snappy.Encode(nil, encodedBlock), nil
|
||||
default:
|
||||
return nil, errors.New("Unknown block version")
|
||||
return nil, errors.New("unknown block version")
|
||||
}
|
||||
}
|
||||
|
||||
// Encodes a blinded beacon block with its associated key.
|
||||
// If the block does not support blinding, we then encode it as a full
|
||||
// block with its associated key by calling marshalBlockFull.
|
||||
func marshalBlockBlinded(
|
||||
ctx context.Context,
|
||||
blk interfaces.ReadOnlySignedBeaconBlock,
|
||||
) ([]byte, error) {
|
||||
blindedBlock, err := blk.ToBlinded()
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, blocks.ErrUnsupportedVersion):
|
||||
return marshalBlockFull(ctx, blk)
|
||||
default:
|
||||
return nil, errors.Wrap(err, "could not convert block to blinded format")
|
||||
}
|
||||
}
|
||||
encodedBlock, err := blindedBlock.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not marshal blinded block")
|
||||
}
|
||||
switch blk.Version() {
|
||||
case version.Capella:
|
||||
return snappy.Encode(nil, append(capellaBlindKey, encodedBlock...)), nil
|
||||
case version.Bellatrix:
|
||||
return snappy.Encode(nil, append(bellatrixBlindKey, encodedBlock...)), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported block version: %v", blk.Version())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
)
|
||||
|
||||
@@ -10,7 +9,4 @@ func init() {
|
||||
if err := params.SetActive(params.MainnetTestConfig()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
features.Init(&features.Flags{
|
||||
EnableOnlyBlindedBeaconBlocks: true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/db/iface"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/io/file"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
@@ -194,7 +195,8 @@ func NewKVStore(ctx context.Context, dirPath string) (*Store, error) {
|
||||
if err = prometheus.Register(createBoltCollector(kv.db)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = kv.checkNeedsResync(); err != nil {
|
||||
// Setup the type of block storage used depending on whether or not this is a fresh database.
|
||||
if err := kv.setupBlockStorageType(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return kv, nil
|
||||
@@ -229,21 +231,60 @@ func (s *Store) DatabasePath() string {
|
||||
return s.databasePath
|
||||
}
|
||||
|
||||
func (s *Store) checkNeedsResync() error {
|
||||
return s.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(migrationsBucket)
|
||||
hasDisabledFeature := !features.Get().EnableOnlyBlindedBeaconBlocks
|
||||
if hasDisabledFeature && bkt.Get(migrationBlindedBeaconBlocksKey) != nil {
|
||||
return fmt.Errorf(
|
||||
"you have disabled the flag %s, and your node must resync to ensure your "+
|
||||
"database is compatible. If you do not want to resync, please re-enable the %s flag",
|
||||
features.EnableOnlyBlindedBeaconBlocks.Name,
|
||||
features.EnableOnlyBlindedBeaconBlocks.Name,
|
||||
)
|
||||
func (s *Store) setupBlockStorageType(ctx context.Context) error {
|
||||
// We check if we want to save blinded beacon blocks by checking a key in the db
|
||||
// otherwise, we check the last stored block and set that key in the DB if it is blinded.
|
||||
headBlock, err := s.HeadBlock(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get head block when setting up block storage type")
|
||||
}
|
||||
err = blocks.BeaconBlockIsNil(headBlock)
|
||||
isNilBlk := err != nil
|
||||
saveFull := features.Get().SaveFullExecutionPayloads
|
||||
|
||||
var saveBlinded bool
|
||||
if err := s.db.Update(func(tx *bolt.Tx) error {
|
||||
// If we have a key stating we wish to save blinded beacon blocks, then we set saveBlinded to true.
|
||||
metadataBkt := tx.Bucket(chainMetadataBucket)
|
||||
keyExists := len(metadataBkt.Get(saveBlindedBeaconBlocksKey)) > 0
|
||||
if keyExists {
|
||||
saveBlinded = true
|
||||
return nil
|
||||
}
|
||||
// If the head block exists and is blinded, we update the key in the DB to
|
||||
// say we wish to save all blocks as blinded.
|
||||
if !isNilBlk && headBlock.IsBlinded() {
|
||||
if err := metadataBkt.Put(saveBlindedBeaconBlocksKey, []byte{1}); err != nil {
|
||||
return err
|
||||
}
|
||||
saveBlinded = true
|
||||
}
|
||||
if isNilBlk && !saveFull {
|
||||
if err := metadataBkt.Put(saveBlindedBeaconBlocksKey, []byte{1}); err != nil {
|
||||
return err
|
||||
}
|
||||
saveBlinded = true
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the user wants to save full execution payloads but their database is saving blinded blocks only,
|
||||
// we then throw an error as the node should not start.
|
||||
if saveFull && saveBlinded {
|
||||
return fmt.Errorf(
|
||||
"cannot use the %s flag with this existing database, as it has already been initialized to only store "+
|
||||
"execution payload headers (aka blinded beacon blocks). If you want to use this flag, you must re-sync your node with a fresh "+
|
||||
"database. We recommend using checkpoint sync https://docs.prylabs.network/docs/prysm-usage/checkpoint-sync/",
|
||||
features.SaveFullExecutionPayloads.Name,
|
||||
)
|
||||
}
|
||||
if saveFull {
|
||||
log.Warn("Saving full beacon blocks to the database. For greater disk space savings, we recommend resyncing from an empty database with " +
|
||||
"checkpoint sync to save only blinded beacon blocks by default")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createBuckets(tx *bolt.Tx, buckets ...[]byte) error {
|
||||
|
||||
@@ -2,10 +2,14 @@ package kv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/util"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
@@ -19,16 +23,169 @@ func setupDB(t testing.TB) *Store {
|
||||
return db
|
||||
}
|
||||
|
||||
func Test_checkNeedsResync(t *testing.T) {
|
||||
store := setupDB(t)
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
EnableOnlyBlindedBeaconBlocks: false,
|
||||
func Test_setupBlockStorageType(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
t.Run("fresh database with feature enabled to store full blocks should store full blocks", func(t *testing.T) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
SaveFullExecutionPayloads: true,
|
||||
})
|
||||
defer resetFn()
|
||||
store := setupDB(t)
|
||||
|
||||
blk := util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Body.ExecutionPayload.BlockNumber = 1
|
||||
wrappedBlock, err := blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
root, err := wrappedBlock.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, store.SaveBlock(ctx, wrappedBlock))
|
||||
require.NoError(t, store.SaveStateSummary(ctx, ðpb.StateSummary{Root: root[:]}))
|
||||
require.NoError(t, store.SaveHeadBlockRoot(ctx, root))
|
||||
retrievedBlk, err := store.Block(ctx, root)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, retrievedBlk.IsBlinded())
|
||||
require.DeepEqual(t, wrappedBlock, retrievedBlk)
|
||||
})
|
||||
t.Run("fresh database with default settings should store blinded", func(t *testing.T) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
SaveFullExecutionPayloads: false,
|
||||
})
|
||||
defer resetFn()
|
||||
store := setupDB(t)
|
||||
|
||||
blk := util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Body.ExecutionPayload.BlockNumber = 1
|
||||
wrappedBlock, err := blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
root, err := wrappedBlock.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, store.SaveBlock(ctx, wrappedBlock))
|
||||
require.NoError(t, store.SaveStateSummary(ctx, ðpb.StateSummary{Root: root[:]}))
|
||||
require.NoError(t, store.SaveHeadBlockRoot(ctx, root))
|
||||
retrievedBlk, err := store.Block(ctx, root)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, retrievedBlk.IsBlinded())
|
||||
|
||||
wantedBlk, err := wrappedBlock.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, wantedBlk, retrievedBlk)
|
||||
})
|
||||
t.Run("existing database with blinded blocks but no key in metadata bucket should continue storing blinded blocks", func(t *testing.T) {
|
||||
store := setupDB(t)
|
||||
require.NoError(t, store.db.Update(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(chainMetadataBucket).Put(saveBlindedBeaconBlocksKey, []byte{1})
|
||||
}))
|
||||
|
||||
blk := util.NewBlindedBeaconBlockBellatrix()
|
||||
blk.Block.Body.ExecutionPayloadHeader.BlockNumber = 1
|
||||
wrappedBlock, err := blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
root, err := wrappedBlock.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, store.SaveBlock(ctx, wrappedBlock))
|
||||
require.NoError(t, store.SaveStateSummary(ctx, ðpb.StateSummary{Root: root[:]}))
|
||||
require.NoError(t, store.SaveHeadBlockRoot(ctx, root))
|
||||
retrievedBlk, err := store.Block(ctx, root)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, retrievedBlk.IsBlinded())
|
||||
require.DeepEqual(t, wrappedBlock, retrievedBlk)
|
||||
|
||||
// We then delete the key from the bucket.
|
||||
require.NoError(t, store.db.Update(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(chainMetadataBucket).Delete(saveBlindedBeaconBlocksKey)
|
||||
}))
|
||||
|
||||
// Not a fresh database, has blinded blocks already and should continue being that way.
|
||||
err = store.setupBlockStorageType(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
var shouldSaveBlinded bool
|
||||
require.NoError(t, store.db.Update(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(chainMetadataBucket)
|
||||
shouldSaveBlinded = len(bkt.Get(saveBlindedBeaconBlocksKey)) > 0
|
||||
return nil
|
||||
}))
|
||||
|
||||
// Should have set the chain metadata bucket to save blinded
|
||||
require.Equal(t, true, shouldSaveBlinded)
|
||||
|
||||
blkFull := util.NewBeaconBlockBellatrix()
|
||||
blkFull.Block.Body.ExecutionPayload.BlockNumber = 2
|
||||
wrappedBlock, err = blocks.NewSignedBeaconBlock(blkFull)
|
||||
require.NoError(t, err)
|
||||
root, err = wrappedBlock.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, store.SaveBlock(ctx, wrappedBlock))
|
||||
retrievedBlk, err = store.Block(ctx, root)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, true, retrievedBlk.IsBlinded())
|
||||
wrappedBlinded, err := wrappedBlock.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, wrappedBlinded, retrievedBlk)
|
||||
})
|
||||
t.Run("existing database with full blocks type should continue storing full blocks", func(t *testing.T) {
|
||||
store := setupDB(t)
|
||||
require.NoError(t, store.db.Update(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(chainMetadataBucket).Delete(saveBlindedBeaconBlocksKey)
|
||||
}))
|
||||
|
||||
blk := util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Body.ExecutionPayload.BlockNumber = 1
|
||||
wrappedBlock, err := blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
root, err := wrappedBlock.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, store.SaveBlock(ctx, wrappedBlock))
|
||||
require.NoError(t, store.SaveStateSummary(ctx, ðpb.StateSummary{Root: root[:]}))
|
||||
require.NoError(t, store.SaveHeadBlockRoot(ctx, root))
|
||||
retrievedBlk, err := store.Block(ctx, root)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, retrievedBlk.IsBlinded())
|
||||
require.DeepEqual(t, wrappedBlock, retrievedBlk)
|
||||
|
||||
// Not a fresh database, has full blocks already and should continue being that way.
|
||||
err = store.setupBlockStorageType(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
blk = util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Body.ExecutionPayload.BlockNumber = 2
|
||||
wrappedBlock, err = blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
root, err = wrappedBlock.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, store.SaveBlock(ctx, wrappedBlock))
|
||||
retrievedBlk, err = store.Block(ctx, root)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, retrievedBlk.IsBlinded())
|
||||
require.DeepEqual(t, wrappedBlock, retrievedBlk)
|
||||
})
|
||||
t.Run("existing database with blinded blocks type should error if user enables full blocks feature flag", func(t *testing.T) {
|
||||
store := setupDB(t)
|
||||
|
||||
blk := util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Body.ExecutionPayload.BlockNumber = 1
|
||||
wrappedBlock, err := blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
root, err := wrappedBlock.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, store.SaveBlock(ctx, wrappedBlock))
|
||||
require.NoError(t, store.SaveStateSummary(ctx, ðpb.StateSummary{Root: root[:]}))
|
||||
require.NoError(t, store.SaveHeadBlockRoot(ctx, root))
|
||||
retrievedBlk, err := store.Block(ctx, root)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, retrievedBlk.IsBlinded())
|
||||
wantedBlk, err := wrappedBlock.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, wantedBlk, retrievedBlk)
|
||||
|
||||
// Trying to enable full blocks with a database that is already storing blinded blocks should error.
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
SaveFullExecutionPayloads: true,
|
||||
})
|
||||
defer resetFn()
|
||||
err = store.setupBlockStorageType(ctx)
|
||||
errMsg := "cannot use the %s flag with this existing database, as it has already been initialized"
|
||||
require.ErrorContains(t, fmt.Sprintf(errMsg, features.SaveFullExecutionPayloads.Name), err)
|
||||
})
|
||||
defer resetFn()
|
||||
require.NoError(t, store.db.Update(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(migrationsBucket)
|
||||
return bkt.Put(migrationBlindedBeaconBlocksKey, migrationCompleted)
|
||||
}))
|
||||
err := store.checkNeedsResync()
|
||||
require.ErrorContains(t, "your node must resync", err)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ var migrations = []migration{
|
||||
migrateArchivedIndex,
|
||||
migrateBlockSlotIndex,
|
||||
migrateStateValidators,
|
||||
migrateBlindedBeaconBlocksEnabled,
|
||||
}
|
||||
|
||||
// RunMigrations defined in the migrations array.
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
var migrationBlindedBeaconBlocksKey = []byte("blinded-beacon-blocks-enabled")
|
||||
|
||||
func migrateBlindedBeaconBlocksEnabled(ctx context.Context, db *bolt.DB) error {
|
||||
if !features.Get().EnableOnlyBlindedBeaconBlocks {
|
||||
return nil // Only write to the migrations bucket if the feature is enabled.
|
||||
}
|
||||
if updateErr := db.Update(func(tx *bolt.Tx) error {
|
||||
mb := tx.Bucket(migrationsBucket)
|
||||
if b := mb.Get(migrationBlindedBeaconBlocksKey); bytes.Equal(b, migrationCompleted) {
|
||||
return nil // Migration already completed.
|
||||
}
|
||||
return mb.Put(migrationBlindedBeaconBlocksKey, migrationCompleted)
|
||||
}); updateErr != nil {
|
||||
return updateErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -49,12 +49,12 @@ var (
|
||||
|
||||
// Below keys are used to identify objects are to be fork compatible.
|
||||
// Objects that are only compatible with specific forks should be prefixed with such keys.
|
||||
altairKey = []byte("altair")
|
||||
bellatrixKey = []byte("merge")
|
||||
bellatrixBlindKey = []byte("blind-bellatrix")
|
||||
capellaKey = []byte("capella")
|
||||
capellaBlindKey = []byte("blind-capella")
|
||||
|
||||
altairKey = []byte("altair")
|
||||
bellatrixKey = []byte("merge")
|
||||
bellatrixBlindKey = []byte("blind-bellatrix")
|
||||
capellaKey = []byte("capella")
|
||||
capellaBlindKey = []byte("blind-capella")
|
||||
saveBlindedBeaconBlocksKey = []byte("save-blinded-beacon-blocks")
|
||||
// block root included in the beacon state used by weak subjectivity initial sync
|
||||
originCheckpointBlockRootKey = []byte("origin-checkpoint-block-root")
|
||||
// block root tracking the progress of backfill, or pointing at genesis if backfill has not been initiated
|
||||
|
||||
@@ -129,9 +129,9 @@ go_test(
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//accounts/abi/bind/backends:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//beacon/engine:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core/beacon:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
"@com_github_holiman_uint256//:go_default_library",
|
||||
|
||||
@@ -240,7 +240,7 @@ func TestService_logTtdStatus_NotSyncedClient(t *testing.T) {
|
||||
|
||||
ttd := new(uint256.Int)
|
||||
reached, err := service.logTtdStatus(context.Background(), ttd.SetUint64(24343))
|
||||
require.NoError(t, err)
|
||||
require.ErrorContains(t, "missing required field 'parentHash' for Header", err)
|
||||
require.Equal(t, false, reached)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/beacon/engine"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/beacon"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution"
|
||||
@@ -23,10 +23,10 @@ import (
|
||||
|
||||
func FuzzForkChoiceResponse(f *testing.F) {
|
||||
valHash := common.Hash([32]byte{0xFF, 0x01})
|
||||
payloadID := beacon.PayloadID([8]byte{0x01, 0xFF, 0xAA, 0x00, 0xEE, 0xFE, 0x00, 0x00})
|
||||
payloadID := engine.PayloadID([8]byte{0x01, 0xFF, 0xAA, 0x00, 0xEE, 0xFE, 0x00, 0x00})
|
||||
valErr := "asjajshjahsaj"
|
||||
seed := &beacon.ForkChoiceResponse{
|
||||
PayloadStatus: beacon.PayloadStatusV1{
|
||||
seed := &engine.ForkChoiceResponse{
|
||||
PayloadStatus: engine.PayloadStatusV1{
|
||||
Status: "INVALID_TERMINAL_BLOCK",
|
||||
LatestValidHash: &valHash,
|
||||
ValidationError: &valErr,
|
||||
@@ -37,7 +37,7 @@ func FuzzForkChoiceResponse(f *testing.F) {
|
||||
assert.NoError(f, err)
|
||||
f.Add(output)
|
||||
f.Fuzz(func(t *testing.T, jsonBlob []byte) {
|
||||
gethResp := &beacon.ForkChoiceResponse{}
|
||||
gethResp := &engine.ForkChoiceResponse{}
|
||||
prysmResp := &execution.ForkchoiceUpdatedResponse{}
|
||||
gethErr := json.Unmarshal(jsonBlob, gethResp)
|
||||
prysmErr := json.Unmarshal(jsonBlob, prysmResp)
|
||||
@@ -49,14 +49,14 @@ func FuzzForkChoiceResponse(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 := &beacon.ForkChoiceResponse{}
|
||||
newGethResp := &engine.ForkChoiceResponse{}
|
||||
newGethErr := json.Unmarshal(prysmBlob, newGethResp)
|
||||
assert.NoError(t, newGethErr)
|
||||
if newGethResp.PayloadStatus.Status == "UNKNOWN" {
|
||||
return
|
||||
}
|
||||
|
||||
newGethResp2 := &beacon.ForkChoiceResponse{}
|
||||
newGethResp2 := &engine.ForkChoiceResponse{}
|
||||
newGethErr = json.Unmarshal(gethBlob, newGethResp2)
|
||||
assert.NoError(t, newGethErr)
|
||||
|
||||
@@ -75,7 +75,7 @@ func FuzzForkChoiceResponse(f *testing.F) {
|
||||
func FuzzExchangeTransitionConfiguration(f *testing.F) {
|
||||
valHash := common.Hash([32]byte{0xFF, 0x01})
|
||||
ttd := hexutil.Big(*big.NewInt(math.MaxInt))
|
||||
seed := &beacon.TransitionConfigurationV1{
|
||||
seed := &engine.TransitionConfigurationV1{
|
||||
TerminalTotalDifficulty: &ttd,
|
||||
TerminalBlockHash: valHash,
|
||||
TerminalBlockNumber: hexutil.Uint64(math.MaxUint64),
|
||||
@@ -85,7 +85,7 @@ func FuzzExchangeTransitionConfiguration(f *testing.F) {
|
||||
assert.NoError(f, err)
|
||||
f.Add(output)
|
||||
f.Fuzz(func(t *testing.T, jsonBlob []byte) {
|
||||
gethResp := &beacon.TransitionConfigurationV1{}
|
||||
gethResp := &engine.TransitionConfigurationV1{}
|
||||
prysmResp := &pb.TransitionConfiguration{}
|
||||
gethErr := json.Unmarshal(jsonBlob, gethResp)
|
||||
prysmErr := json.Unmarshal(jsonBlob, prysmResp)
|
||||
@@ -103,11 +103,11 @@ func FuzzExchangeTransitionConfiguration(f *testing.F) {
|
||||
if gethErr != nil {
|
||||
t.Errorf("%s %s", gethResp.TerminalTotalDifficulty.String(), prysmResp.TerminalTotalDifficulty)
|
||||
}
|
||||
newGethResp := &beacon.TransitionConfigurationV1{}
|
||||
newGethResp := &engine.TransitionConfigurationV1{}
|
||||
newGethErr := json.Unmarshal(prysmBlob, newGethResp)
|
||||
assert.NoError(t, newGethErr)
|
||||
|
||||
newGethResp2 := &beacon.TransitionConfigurationV1{}
|
||||
newGethResp2 := &engine.TransitionConfigurationV1{}
|
||||
newGethErr = json.Unmarshal(gethBlob, newGethResp2)
|
||||
assert.NoError(t, newGethErr)
|
||||
})
|
||||
@@ -115,7 +115,7 @@ func FuzzExchangeTransitionConfiguration(f *testing.F) {
|
||||
|
||||
func FuzzExecutionPayload(f *testing.F) {
|
||||
logsBloom := [256]byte{'j', 'u', 'n', 'k'}
|
||||
execData := &beacon.ExecutableData{
|
||||
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}),
|
||||
@@ -135,7 +135,7 @@ func FuzzExecutionPayload(f *testing.F) {
|
||||
assert.NoError(f, err)
|
||||
f.Add(output)
|
||||
f.Fuzz(func(t *testing.T, jsonBlob []byte) {
|
||||
gethResp := &beacon.ExecutableData{}
|
||||
gethResp := &engine.ExecutableData{}
|
||||
prysmResp := &pb.ExecutionPayload{}
|
||||
gethErr := json.Unmarshal(jsonBlob, gethResp)
|
||||
prysmErr := json.Unmarshal(jsonBlob, prysmResp)
|
||||
@@ -147,10 +147,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 := &beacon.ExecutableData{}
|
||||
newGethResp := &engine.ExecutableData{}
|
||||
newGethErr := json.Unmarshal(prysmBlob, newGethResp)
|
||||
assert.NoError(t, newGethErr)
|
||||
newGethResp2 := &beacon.ExecutableData{}
|
||||
newGethResp2 := &engine.ExecutableData{}
|
||||
newGethErr = json.Unmarshal(gethBlob, newGethResp2)
|
||||
assert.NoError(t, newGethErr)
|
||||
|
||||
|
||||
@@ -46,8 +46,6 @@ func New() *ForkChoice {
|
||||
|
||||
// NodeCount returns the current number of nodes in the Store.
|
||||
func (f *ForkChoice) NodeCount() int {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
return len(f.store.nodeByRoot)
|
||||
}
|
||||
|
||||
@@ -58,16 +56,9 @@ func (f *ForkChoice) Head(
|
||||
) ([32]byte, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.Head")
|
||||
defer span.End()
|
||||
f.votesLock.Lock()
|
||||
defer f.votesLock.Unlock()
|
||||
|
||||
calledHeadCount.Inc()
|
||||
|
||||
// Using the write lock here because subsequent calls to `updateBalances`, `applyProposerBoostScore`,
|
||||
// `applyWeightChanges`, `updateBestDescendant`, and `head` require write operations on nodes.
|
||||
f.store.nodesLock.Lock()
|
||||
defer f.store.nodesLock.Unlock()
|
||||
|
||||
if err := f.updateBalances(); err != nil {
|
||||
return [32]byte{}, errors.Wrap(err, "could not update balances")
|
||||
}
|
||||
@@ -94,8 +85,6 @@ func (f *ForkChoice) Head(
|
||||
func (f *ForkChoice) ProcessAttestation(ctx context.Context, validatorIndices []uint64, blockRoot [32]byte, targetEpoch primitives.Epoch) {
|
||||
_, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.ProcessAttestation")
|
||||
defer span.End()
|
||||
f.votesLock.Lock()
|
||||
defer f.votesLock.Unlock()
|
||||
|
||||
for _, index := range validatorIndices {
|
||||
// Validator indices will grow the vote cache.
|
||||
@@ -161,7 +150,6 @@ func (f *ForkChoice) InsertNode(ctx context.Context, state state.BeaconState, ro
|
||||
|
||||
// updateCheckpoints update the checkpoints when inserting a new node.
|
||||
func (f *ForkChoice) updateCheckpoints(ctx context.Context, jc, fc *ethpb.Checkpoint) error {
|
||||
f.store.checkpointsLock.Lock()
|
||||
if jc.Epoch > f.store.justifiedCheckpoint.Epoch {
|
||||
if jc.Epoch > f.store.bestJustifiedCheckpoint.Epoch {
|
||||
f.store.bestJustifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: jc.Epoch,
|
||||
@@ -175,7 +163,6 @@ func (f *ForkChoice) updateCheckpoints(ctx context.Context, jc, fc *ethpb.Checkp
|
||||
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: jc.Epoch,
|
||||
Root: root}
|
||||
if err := f.updateJustifiedBalances(ctx, root); err != nil {
|
||||
f.store.checkpointsLock.Unlock()
|
||||
return errors.Wrap(err, "could not update justified balances")
|
||||
}
|
||||
} else {
|
||||
@@ -186,25 +173,18 @@ func (f *ForkChoice) updateCheckpoints(ctx context.Context, jc, fc *ethpb.Checkp
|
||||
}
|
||||
jSlot, err := slots.EpochStart(currentJcp.Epoch)
|
||||
if err != nil {
|
||||
f.store.checkpointsLock.Unlock()
|
||||
return err
|
||||
}
|
||||
jcRoot := bytesutil.ToBytes32(jc.Root)
|
||||
// Releasing here the checkpoints lock because
|
||||
// AncestorRoot acquires a lock on nodes and that can
|
||||
// cause a double lock.
|
||||
f.store.checkpointsLock.Unlock()
|
||||
root, err := f.AncestorRoot(ctx, jcRoot, jSlot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.store.checkpointsLock.Lock()
|
||||
if root == currentRoot {
|
||||
f.store.prevJustifiedCheckpoint = f.store.justifiedCheckpoint
|
||||
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: jc.Epoch,
|
||||
Root: jcRoot}
|
||||
if err := f.updateJustifiedBalances(ctx, jcRoot); err != nil {
|
||||
f.store.checkpointsLock.Unlock()
|
||||
return errors.Wrap(err, "could not update justified balances")
|
||||
}
|
||||
}
|
||||
@@ -214,14 +194,12 @@ func (f *ForkChoice) updateCheckpoints(ctx context.Context, jc, fc *ethpb.Checkp
|
||||
jcRoot := bytesutil.ToBytes32(jc.Root)
|
||||
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: jc.Epoch, Root: jcRoot}
|
||||
if err := f.updateJustifiedBalances(ctx, jcRoot); err != nil {
|
||||
f.store.checkpointsLock.Unlock()
|
||||
return errors.Wrap(err, "could not update justified balances")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update finalization
|
||||
if fc.Epoch <= f.store.finalizedCheckpoint.Epoch {
|
||||
f.store.checkpointsLock.Unlock()
|
||||
return nil
|
||||
}
|
||||
f.store.finalizedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: fc.Epoch,
|
||||
@@ -231,29 +209,21 @@ func (f *ForkChoice) updateCheckpoints(ctx context.Context, jc, fc *ethpb.Checkp
|
||||
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: jc.Epoch,
|
||||
Root: root}
|
||||
if err := f.updateJustifiedBalances(ctx, root); err != nil {
|
||||
f.store.checkpointsLock.Unlock()
|
||||
return errors.Wrap(err, "could not update justified balances")
|
||||
}
|
||||
}
|
||||
f.store.checkpointsLock.Unlock()
|
||||
return f.store.prune(ctx)
|
||||
}
|
||||
|
||||
// HasNode returns true if the node exists in fork choice store,
|
||||
// false else wise.
|
||||
func (f *ForkChoice) HasNode(root [32]byte) bool {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
_, ok := f.store.nodeByRoot[root]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsCanonical returns true if the given root is part of the canonical chain.
|
||||
func (f *ForkChoice) IsCanonical(root [32]byte) bool {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
node, ok := f.store.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return false
|
||||
@@ -273,9 +243,6 @@ func (f *ForkChoice) IsCanonical(root [32]byte) bool {
|
||||
|
||||
// IsOptimistic returns true if the given root has been optimistically synced.
|
||||
func (f *ForkChoice) IsOptimistic(root [32]byte) (bool, error) {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
if f.store.allTipsAreInvalid {
|
||||
return true, nil
|
||||
}
|
||||
@@ -293,9 +260,6 @@ func (f *ForkChoice) AncestorRoot(ctx context.Context, root [32]byte, slot primi
|
||||
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.AncestorRoot")
|
||||
defer span.End()
|
||||
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
node, ok := f.store.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return [32]byte{}, errors.Wrap(ErrNilNode, "could not determine ancestor root")
|
||||
@@ -317,12 +281,8 @@ func (f *ForkChoice) AncestorRoot(ctx context.Context, root [32]byte, slot primi
|
||||
}
|
||||
|
||||
// updateBalances updates the balances that directly voted for each block taking into account the
|
||||
// validators' latest votes. This function requires a lock in Store.nodesLock
|
||||
// and votesLock
|
||||
// validators' latest votes.
|
||||
func (f *ForkChoice) updateBalances() error {
|
||||
// lock checkpoints for the justified balances
|
||||
f.store.checkpointsLock.RLock()
|
||||
defer f.store.checkpointsLock.RUnlock()
|
||||
newBalances := f.justifiedBalances
|
||||
|
||||
for index, vote := range f.votes {
|
||||
@@ -367,7 +327,6 @@ func (f *ForkChoice) updateBalances() error {
|
||||
return errors.Wrap(ErrNilNode, "could not update balances")
|
||||
}
|
||||
if currentNode.balance < oldBalance {
|
||||
f.store.proposerBoostLock.RLock()
|
||||
log.WithFields(logrus.Fields{
|
||||
"nodeRoot": fmt.Sprintf("%#x", bytesutil.Trunc(vote.currentRoot[:])),
|
||||
"oldBalance": oldBalance,
|
||||
@@ -377,7 +336,6 @@ func (f *ForkChoice) updateBalances() error {
|
||||
"previousProposerBoostRoot": fmt.Sprintf("%#x", bytesutil.Trunc(f.store.previousProposerBoostRoot[:])),
|
||||
"previousProposerBoostScore": f.store.previousProposerBoostScore,
|
||||
}).Warning("node with invalid balance, setting it to zero")
|
||||
f.store.proposerBoostLock.RUnlock()
|
||||
currentNode.balance = 0
|
||||
} else {
|
||||
currentNode.balance -= oldBalance
|
||||
@@ -405,8 +363,6 @@ func (f *ForkChoice) ProposerBoost() [fieldparams.RootLength]byte {
|
||||
|
||||
// SetOptimisticToValid sets the node with the given root as a fully validated node
|
||||
func (f *ForkChoice) SetOptimisticToValid(ctx context.Context, root [fieldparams.RootLength]byte) error {
|
||||
f.store.nodesLock.Lock()
|
||||
defer f.store.nodesLock.Unlock()
|
||||
node, ok := f.store.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return errors.Wrap(ErrNilNode, "could not set node to valid")
|
||||
@@ -416,29 +372,21 @@ func (f *ForkChoice) SetOptimisticToValid(ctx context.Context, root [fieldparams
|
||||
|
||||
// BestJustifiedCheckpoint of fork choice store.
|
||||
func (f *ForkChoice) BestJustifiedCheckpoint() *forkchoicetypes.Checkpoint {
|
||||
f.store.checkpointsLock.RLock()
|
||||
defer f.store.checkpointsLock.RUnlock()
|
||||
return f.store.bestJustifiedCheckpoint
|
||||
}
|
||||
|
||||
// PreviousJustifiedCheckpoint of fork choice store.
|
||||
func (f *ForkChoice) PreviousJustifiedCheckpoint() *forkchoicetypes.Checkpoint {
|
||||
f.store.checkpointsLock.RLock()
|
||||
defer f.store.checkpointsLock.RUnlock()
|
||||
return f.store.prevJustifiedCheckpoint
|
||||
}
|
||||
|
||||
// JustifiedCheckpoint of fork choice store.
|
||||
func (f *ForkChoice) JustifiedCheckpoint() *forkchoicetypes.Checkpoint {
|
||||
f.store.checkpointsLock.RLock()
|
||||
defer f.store.checkpointsLock.RUnlock()
|
||||
return f.store.justifiedCheckpoint
|
||||
}
|
||||
|
||||
// FinalizedCheckpoint of fork choice store.
|
||||
func (f *ForkChoice) FinalizedCheckpoint() *forkchoicetypes.Checkpoint {
|
||||
f.store.checkpointsLock.RLock()
|
||||
defer f.store.checkpointsLock.RUnlock()
|
||||
return f.store.finalizedCheckpoint
|
||||
}
|
||||
|
||||
@@ -451,11 +399,6 @@ func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root, parentRoo
|
||||
// store-tracked list. Votes from these validators are not accounted for
|
||||
// in forkchoice.
|
||||
func (f *ForkChoice) InsertSlashedIndex(_ context.Context, index primitives.ValidatorIndex) {
|
||||
f.votesLock.RLock()
|
||||
defer f.votesLock.RUnlock()
|
||||
|
||||
f.store.nodesLock.Lock()
|
||||
defer f.store.nodesLock.Unlock()
|
||||
// return early if the index was already included:
|
||||
if f.store.slashedIndices[index] {
|
||||
return
|
||||
@@ -489,8 +432,6 @@ func (f *ForkChoice) UpdateJustifiedCheckpoint(ctx context.Context, jc *forkchoi
|
||||
if jc == nil {
|
||||
return errInvalidNilCheckpoint
|
||||
}
|
||||
f.store.checkpointsLock.Lock()
|
||||
defer f.store.checkpointsLock.Unlock()
|
||||
f.store.prevJustifiedCheckpoint = f.store.justifiedCheckpoint
|
||||
f.store.justifiedCheckpoint = jc
|
||||
f.store.bestJustifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: jc.Epoch, Root: jc.Root}
|
||||
@@ -505,8 +446,6 @@ func (f *ForkChoice) UpdateFinalizedCheckpoint(fc *forkchoicetypes.Checkpoint) e
|
||||
if fc == nil {
|
||||
return errInvalidNilCheckpoint
|
||||
}
|
||||
f.store.checkpointsLock.Lock()
|
||||
defer f.store.checkpointsLock.Unlock()
|
||||
f.store.finalizedCheckpoint = fc
|
||||
return nil
|
||||
}
|
||||
@@ -516,9 +455,6 @@ func (f *ForkChoice) CommonAncestor(ctx context.Context, r1 [32]byte, r2 [32]byt
|
||||
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.CommonAncestorRoot")
|
||||
defer span.End()
|
||||
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
n1, ok := f.store.nodeByRoot[r1]
|
||||
if !ok || n1 == nil {
|
||||
return [32]byte{}, 0, forkchoice.ErrUnknownCommonAncestor
|
||||
@@ -601,8 +537,6 @@ func (f *ForkChoice) SetOriginRoot(root [32]byte) {
|
||||
|
||||
// CachedHeadRoot returns the last cached head root
|
||||
func (f *ForkChoice) CachedHeadRoot() [32]byte {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
node := f.store.headNode
|
||||
if node == nil {
|
||||
return [32]byte{}
|
||||
@@ -612,8 +546,6 @@ func (f *ForkChoice) CachedHeadRoot() [32]byte {
|
||||
|
||||
// FinalizedPayloadBlockHash returns the hash of the payload at the finalized checkpoint
|
||||
func (f *ForkChoice) FinalizedPayloadBlockHash() [32]byte {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
root := f.FinalizedCheckpoint().Root
|
||||
node, ok := f.store.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
@@ -625,8 +557,6 @@ func (f *ForkChoice) FinalizedPayloadBlockHash() [32]byte {
|
||||
|
||||
// JustifiedPayloadBlockHash returns the hash of the payload at the justified checkpoint
|
||||
func (f *ForkChoice) JustifiedPayloadBlockHash() [32]byte {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
root := f.JustifiedCheckpoint().Root
|
||||
node, ok := f.store.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
@@ -692,8 +622,6 @@ func (f *ForkChoice) SetBalancesByRooter(handler forkchoice.BalancesByRooter) {
|
||||
|
||||
// Weight returns the weight of the given root if found on the store
|
||||
func (f *ForkChoice) Weight(root [32]byte) (uint64, error) {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
n, ok := f.store.nodeByRoot[root]
|
||||
if !ok || n == nil {
|
||||
return 0, ErrNilNode
|
||||
@@ -702,7 +630,6 @@ func (f *ForkChoice) Weight(root [32]byte) (uint64, error) {
|
||||
}
|
||||
|
||||
// updateJustifiedBalances updates the validators balances on the justified checkpoint pointed by root.
|
||||
// This function requires a lock on checkpointsLock being held by the caller.
|
||||
func (f *ForkChoice) updateJustifiedBalances(ctx context.Context, root [32]byte) error {
|
||||
balances, err := f.balancesByRoot(ctx, root)
|
||||
if err != nil {
|
||||
|
||||
@@ -205,7 +205,6 @@ func TestForkChoice_IsCanonicalReorg(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, st, blkRoot))
|
||||
|
||||
f.store.nodesLock.Lock()
|
||||
f.store.nodeByRoot[[32]byte{'3'}].balance = 10
|
||||
require.NoError(t, f.store.treeRootNode.applyWeightChanges(ctx))
|
||||
require.Equal(t, uint64(10), f.store.nodeByRoot[[32]byte{'1'}].weight)
|
||||
@@ -213,7 +212,6 @@ func TestForkChoice_IsCanonicalReorg(t *testing.T) {
|
||||
|
||||
require.NoError(t, f.store.treeRootNode.updateBestDescendant(ctx, 1, 1, 1))
|
||||
require.DeepEqual(t, [32]byte{'3'}, f.store.treeRootNode.bestDescendant.root)
|
||||
f.store.nodesLock.Unlock()
|
||||
|
||||
r1 := [32]byte{'1'}
|
||||
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 1, Root: r1}
|
||||
|
||||
@@ -21,8 +21,7 @@ const orphanLateBlockFirstThreshold = 4
|
||||
const processAttestationsThreshold = 10
|
||||
|
||||
// applyWeightChanges recomputes the weight of the node passed as an argument and all of its descendants,
|
||||
// using the current balance stored in each node. This function requires a lock
|
||||
// in Store.nodesLock
|
||||
// using the current balance stored in each node.
|
||||
func (n *Node) applyWeightChanges(ctx context.Context) error {
|
||||
// Recursively calling the children to sum their weights.
|
||||
childrenWeight := uint64(0)
|
||||
@@ -43,7 +42,7 @@ func (n *Node) applyWeightChanges(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// updateBestDescendant updates the best descendant of this node and its
|
||||
// children. This function assumes the caller has a lock on Store.nodesLock
|
||||
// children.
|
||||
func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finalizedEpoch, currentEpoch primitives.Epoch) error {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
|
||||
@@ -27,8 +27,6 @@ func TestNode_ApplyWeightChanges_PositiveChange(t *testing.T) {
|
||||
// The updated balances of each node is 100
|
||||
s := f.store
|
||||
|
||||
s.nodesLock.Lock()
|
||||
defer s.nodesLock.Unlock()
|
||||
s.nodeByRoot[indexToHash(1)].balance = 100
|
||||
s.nodeByRoot[indexToHash(2)].balance = 100
|
||||
s.nodeByRoot[indexToHash(3)].balance = 100
|
||||
@@ -55,8 +53,6 @@ func TestNode_ApplyWeightChanges_NegativeChange(t *testing.T) {
|
||||
|
||||
// The updated balances of each node is 100
|
||||
s := f.store
|
||||
s.nodesLock.Lock()
|
||||
defer s.nodesLock.Unlock()
|
||||
s.nodeByRoot[indexToHash(1)].weight = 400
|
||||
s.nodeByRoot[indexToHash(2)].weight = 400
|
||||
s.nodeByRoot[indexToHash(3)].weight = 400
|
||||
|
||||
@@ -31,6 +31,8 @@ import (
|
||||
// if ancestor_at_finalized_slot == store.finalized_checkpoint.root:
|
||||
// store.justified_checkpoint = store.best_justified_checkpoint
|
||||
func (f *ForkChoice) NewSlot(ctx context.Context, slot primitives.Slot) error {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
// Reset proposer boost root
|
||||
if err := f.resetBoostedProposerRoot(ctx); err != nil {
|
||||
return errors.Wrap(err, "could not reset boosted proposer root in fork choice")
|
||||
@@ -42,11 +44,9 @@ func (f *ForkChoice) NewSlot(ctx context.Context, slot primitives.Slot) error {
|
||||
}
|
||||
|
||||
// Update store.justified_checkpoint if a better checkpoint on the store.finalized_checkpoint chain
|
||||
f.store.checkpointsLock.RLock()
|
||||
bjcp := f.store.bestJustifiedCheckpoint
|
||||
jcp := f.store.justifiedCheckpoint
|
||||
fcp := f.store.finalizedCheckpoint
|
||||
f.store.checkpointsLock.RUnlock()
|
||||
if bjcp.Epoch > jcp.Epoch {
|
||||
finalizedSlot, err := slots.EpochStart(fcp.Epoch)
|
||||
if err != nil {
|
||||
@@ -62,13 +62,11 @@ func (f *ForkChoice) NewSlot(ctx context.Context, slot primitives.Slot) error {
|
||||
return err
|
||||
}
|
||||
if r == fcp.Root {
|
||||
f.store.checkpointsLock.Lock()
|
||||
f.store.prevJustifiedCheckpoint = jcp
|
||||
f.store.justifiedCheckpoint = bjcp
|
||||
if err := f.updateJustifiedBalances(ctx, bjcp.Root); err != nil {
|
||||
log.Error("could not update justified balances")
|
||||
}
|
||||
f.store.checkpointsLock.Unlock()
|
||||
}
|
||||
}
|
||||
if !features.Get().DisablePullTips {
|
||||
|
||||
@@ -8,34 +8,28 @@ import (
|
||||
)
|
||||
|
||||
func (s *Store) setOptimisticToInvalid(ctx context.Context, root, parentRoot, payloadHash [32]byte) ([][32]byte, error) {
|
||||
s.nodesLock.RLock()
|
||||
invalidRoots := make([][32]byte, 0)
|
||||
node, ok := s.nodeByRoot[root]
|
||||
if !ok {
|
||||
node, ok = s.nodeByRoot[parentRoot]
|
||||
if !ok || node == nil {
|
||||
s.nodesLock.RUnlock()
|
||||
return invalidRoots, errors.Wrap(ErrNilNode, "could not set node to invalid")
|
||||
}
|
||||
// return early if the parent is LVH
|
||||
if node.payloadHash == payloadHash {
|
||||
s.nodesLock.RUnlock()
|
||||
return invalidRoots, nil
|
||||
}
|
||||
} else {
|
||||
if node == nil {
|
||||
s.nodesLock.RUnlock()
|
||||
return invalidRoots, errors.Wrap(ErrNilNode, "could not set node to invalid")
|
||||
}
|
||||
if node.parent.root != parentRoot {
|
||||
s.nodesLock.RUnlock()
|
||||
return invalidRoots, errInvalidParentRoot
|
||||
}
|
||||
}
|
||||
firstInvalid := node
|
||||
for ; firstInvalid.parent != nil && firstInvalid.parent.payloadHash != payloadHash; firstInvalid = firstInvalid.parent {
|
||||
if ctx.Err() != nil {
|
||||
s.nodesLock.RUnlock()
|
||||
return invalidRoots, ctx.Err()
|
||||
}
|
||||
}
|
||||
@@ -44,20 +38,16 @@ func (s *Store) setOptimisticToInvalid(ctx context.Context, root, parentRoot, pa
|
||||
if firstInvalid.parent == nil {
|
||||
// return early if the invalid node was not imported
|
||||
if node.root == parentRoot {
|
||||
s.nodesLock.RUnlock()
|
||||
return invalidRoots, nil
|
||||
}
|
||||
firstInvalid = node
|
||||
}
|
||||
s.nodesLock.RUnlock()
|
||||
return s.removeNode(ctx, firstInvalid)
|
||||
}
|
||||
|
||||
// removeNode removes the node with the given root and all of its children
|
||||
// from the Fork Choice Store.
|
||||
func (s *Store) removeNode(ctx context.Context, node *Node) ([][32]byte, error) {
|
||||
s.nodesLock.Lock()
|
||||
defer s.nodesLock.Unlock()
|
||||
invalidRoots := make([][32]byte, 0)
|
||||
|
||||
if node == nil {
|
||||
@@ -96,7 +86,6 @@ func (s *Store) removeNodeAndChildren(ctx context.Context, node *Node, invalidRo
|
||||
}
|
||||
}
|
||||
invalidRoots = append(invalidRoots, node.root)
|
||||
s.proposerBoostLock.Lock()
|
||||
if node.root == s.proposerBoostRoot {
|
||||
s.proposerBoostRoot = [32]byte{}
|
||||
}
|
||||
@@ -104,7 +93,6 @@ func (s *Store) removeNodeAndChildren(ctx context.Context, node *Node, invalidRo
|
||||
s.previousProposerBoostRoot = params.BeaconConfig().ZeroHash
|
||||
s.previousProposerBoostScore = 0
|
||||
}
|
||||
s.proposerBoostLock.Unlock()
|
||||
delete(s.nodeByRoot, node.root)
|
||||
delete(s.nodeByPayload, node.payloadHash)
|
||||
return invalidRoots, nil
|
||||
|
||||
@@ -237,19 +237,15 @@ func TestSetOptimisticToInvalid_ProposerBoost(t *testing.T) {
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 101, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
f.store.proposerBoostLock.Lock()
|
||||
f.store.proposerBoostRoot = [32]byte{'c'}
|
||||
f.store.previousProposerBoostScore = 10
|
||||
f.store.previousProposerBoostRoot = [32]byte{'b'}
|
||||
f.store.proposerBoostLock.Unlock()
|
||||
|
||||
_, err = f.SetOptimisticToInvalid(ctx, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'A'})
|
||||
require.NoError(t, err)
|
||||
f.store.proposerBoostLock.RLock()
|
||||
require.Equal(t, uint64(0), f.store.previousProposerBoostScore)
|
||||
require.DeepEqual(t, [32]byte{}, f.store.proposerBoostRoot)
|
||||
require.DeepEqual(t, params.BeaconConfig().ZeroHash, f.store.previousProposerBoostRoot)
|
||||
f.store.proposerBoostLock.RUnlock()
|
||||
}
|
||||
|
||||
// This is a regression test (10565)
|
||||
|
||||
@@ -10,23 +10,14 @@ import (
|
||||
|
||||
// resetBoostedProposerRoot sets the value of the proposer boosted root to zeros.
|
||||
func (f *ForkChoice) resetBoostedProposerRoot(_ context.Context) error {
|
||||
f.store.proposerBoostLock.Lock()
|
||||
f.store.proposerBoostRoot = [32]byte{}
|
||||
f.store.proposerBoostLock.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyProposerBoostScore applies the current proposer boost scores to the
|
||||
// relevant nodes. This function requires a lock in Store.nodesLock.
|
||||
// relevant nodes.
|
||||
func (f *ForkChoice) applyProposerBoostScore() error {
|
||||
s := f.store
|
||||
s.proposerBoostLock.Lock()
|
||||
defer s.proposerBoostLock.Unlock()
|
||||
|
||||
// acquire checkpoints lock for the justified balances
|
||||
s.checkpointsLock.RLock()
|
||||
defer s.checkpointsLock.RUnlock()
|
||||
|
||||
proposerScore := uint64(0)
|
||||
if s.previousProposerBoostRoot != params.BeaconConfig().ZeroHash {
|
||||
previousNode, ok := s.nodeByRoot[s.previousProposerBoostRoot]
|
||||
@@ -53,7 +44,5 @@ func (f *ForkChoice) applyProposerBoostScore() error {
|
||||
|
||||
// ProposerBoost of fork choice store.
|
||||
func (s *Store) proposerBoost() [fieldparams.RootLength]byte {
|
||||
s.proposerBoostLock.RLock()
|
||||
defer s.proposerBoostLock.RUnlock()
|
||||
return s.proposerBoostRoot
|
||||
}
|
||||
|
||||
@@ -34,9 +34,6 @@ const orphanLateBlockProposingEarly = 2
|
||||
func (f *ForkChoice) ShouldOverrideFCU() (override bool) {
|
||||
override = false
|
||||
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
// We only need to override FCU if our current head is from the current
|
||||
// slot. This differs from the spec implementation in that we assume
|
||||
// that we will call this function in the previous slot to proposing.
|
||||
@@ -61,9 +58,7 @@ func (f *ForkChoice) ShouldOverrideFCU() (override bool) {
|
||||
return
|
||||
}
|
||||
// Only reorg if we have been finalizing
|
||||
f.store.checkpointsLock.RLock()
|
||||
finalizedEpoch := f.store.finalizedCheckpoint.Epoch
|
||||
f.store.checkpointsLock.RUnlock()
|
||||
if slots.ToEpoch(head.slot+1) > finalizedEpoch+params.BeaconConfig().ReorgMaxEpochsSinceFinalization {
|
||||
return
|
||||
}
|
||||
@@ -95,9 +90,6 @@ func (f *ForkChoice) ShouldOverrideFCU() (override bool) {
|
||||
// This function needs to be called only when proposing a block and all
|
||||
// attestation processing has already happened.
|
||||
func (f *ForkChoice) GetProposerHead() [32]byte {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
head := f.store.headNode
|
||||
if head == nil {
|
||||
return [32]byte{}
|
||||
@@ -121,9 +113,7 @@ func (f *ForkChoice) GetProposerHead() [32]byte {
|
||||
return head.root
|
||||
}
|
||||
// Only reorg if we have been finalizing
|
||||
f.store.checkpointsLock.RLock()
|
||||
finalizedEpoch := f.store.finalizedCheckpoint.Epoch
|
||||
f.store.checkpointsLock.RUnlock()
|
||||
if slots.ToEpoch(head.slot+1) > finalizedEpoch+params.BeaconConfig().ReorgMaxEpochsSinceFinalization {
|
||||
return head.root
|
||||
}
|
||||
|
||||
@@ -14,12 +14,10 @@ import (
|
||||
)
|
||||
|
||||
// head starts from justified root and then follows the best descendant links
|
||||
// to find the best block for head. This function assumes a lock on s.nodesLock
|
||||
// to find the best block for head.
|
||||
func (s *Store) head(ctx context.Context) ([32]byte, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.head")
|
||||
defer span.End()
|
||||
s.checkpointsLock.RLock()
|
||||
defer s.checkpointsLock.RUnlock()
|
||||
|
||||
if err := ctx.Err(); err != nil {
|
||||
return [32]byte{}, err
|
||||
@@ -71,9 +69,6 @@ func (s *Store) insert(ctx context.Context,
|
||||
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.insert")
|
||||
defer span.End()
|
||||
|
||||
s.nodesLock.Lock()
|
||||
defer s.nodesLock.Unlock()
|
||||
|
||||
// Return if the block has been inserted into Store before.
|
||||
if n, ok := s.nodeByRoot[root]; ok {
|
||||
return n, nil
|
||||
@@ -115,16 +110,12 @@ func (s *Store) insert(ctx context.Context,
|
||||
currentSlot := slots.CurrentSlot(s.genesisTime)
|
||||
boostThreshold := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot
|
||||
if currentSlot == slot && secondsIntoSlot < boostThreshold {
|
||||
s.proposerBoostLock.Lock()
|
||||
s.proposerBoostRoot = root
|
||||
s.proposerBoostLock.Unlock()
|
||||
}
|
||||
|
||||
// Update best descendants
|
||||
s.checkpointsLock.RLock()
|
||||
jEpoch := s.justifiedCheckpoint.Epoch
|
||||
fEpoch := s.finalizedCheckpoint.Epoch
|
||||
s.checkpointsLock.RUnlock()
|
||||
if err := s.treeRootNode.updateBestDescendant(ctx, jEpoch, fEpoch, slots.ToEpoch(currentSlot)); err != nil {
|
||||
return n, err
|
||||
}
|
||||
@@ -147,7 +138,7 @@ func (s *Store) insert(ctx context.Context,
|
||||
|
||||
// pruneFinalizedNodeByRootMap prunes the `nodeByRoot` map
|
||||
// starting from `node` down to the finalized Node or to a leaf of the Fork
|
||||
// choice store. This method assumes a lock on nodesLock.
|
||||
// choice store.
|
||||
func (s *Store) pruneFinalizedNodeByRootMap(ctx context.Context, node, finalizedNode *Node) error {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
@@ -173,12 +164,8 @@ func (s *Store) prune(ctx context.Context) error {
|
||||
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.Prune")
|
||||
defer span.End()
|
||||
|
||||
s.nodesLock.Lock()
|
||||
defer s.nodesLock.Unlock()
|
||||
s.checkpointsLock.RLock()
|
||||
finalizedRoot := s.finalizedCheckpoint.Root
|
||||
finalizedEpoch := s.finalizedCheckpoint.Epoch
|
||||
s.checkpointsLock.RUnlock()
|
||||
finalizedNode, ok := s.nodeByRoot[finalizedRoot]
|
||||
if !ok || finalizedNode == nil {
|
||||
return errors.WithMessage(errUnknownFinalizedRoot, fmt.Sprintf("%#x", finalizedRoot))
|
||||
@@ -222,9 +209,6 @@ func (s *Store) tips() ([][32]byte, []primitives.Slot) {
|
||||
var roots [][32]byte
|
||||
var slots []primitives.Slot
|
||||
|
||||
s.nodesLock.RLock()
|
||||
defer s.nodesLock.RUnlock()
|
||||
|
||||
for root, node := range s.nodeByRoot {
|
||||
if len(node.children) == 0 {
|
||||
roots = append(roots, root)
|
||||
@@ -236,8 +220,6 @@ func (s *Store) tips() ([][32]byte, []primitives.Slot) {
|
||||
|
||||
// HighestReceivedBlockSlot returns the highest slot received by the forkchoice
|
||||
func (f *ForkChoice) HighestReceivedBlockSlot() primitives.Slot {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
if f.store.highestReceivedNode == nil {
|
||||
return 0
|
||||
}
|
||||
@@ -246,8 +228,6 @@ func (f *ForkChoice) HighestReceivedBlockSlot() primitives.Slot {
|
||||
|
||||
// HighestReceivedBlockRoot returns the highest slot root received by the forkchoice
|
||||
func (f *ForkChoice) HighestReceivedBlockRoot() [32]byte {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
if f.store.highestReceivedNode == nil {
|
||||
return [32]byte{}
|
||||
}
|
||||
@@ -256,8 +236,6 @@ func (f *ForkChoice) HighestReceivedBlockRoot() [32]byte {
|
||||
|
||||
// ReceivedBlocksLastEpoch returns the number of blocks received in the last epoch
|
||||
func (f *ForkChoice) ReceivedBlocksLastEpoch() (uint64, error) {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
count := uint64(0)
|
||||
lowerBound := slots.CurrentSlot(f.store.genesisTime)
|
||||
var err error
|
||||
|
||||
@@ -11,12 +11,12 @@ import (
|
||||
|
||||
// ForkChoice defines the overall fork choice store which includes all block nodes, validator's latest votes and balances.
|
||||
type ForkChoice struct {
|
||||
sync.RWMutex
|
||||
store *Store
|
||||
votes []Vote // tracks individual validator's last vote.
|
||||
votesLock sync.RWMutex
|
||||
votes []Vote // tracks individual validator's last vote.
|
||||
balances []uint64 // tracks individual validator's balances last accounted in votes.
|
||||
justifiedBalances []uint64 // tracks individual validator's last justified balances.
|
||||
numActiveValidators uint64 // tracks the total number of active validators. Requires a checkpoints lock to read/write
|
||||
numActiveValidators uint64 // tracks the total number of active validators.
|
||||
balancesByRoot forkchoice.BalancesByRooter // handler to obtain balances for the state with a given root
|
||||
}
|
||||
|
||||
@@ -31,16 +31,13 @@ type Store struct {
|
||||
proposerBoostRoot [fieldparams.RootLength]byte // latest block root that was boosted after being received in a timely manner.
|
||||
previousProposerBoostRoot [fieldparams.RootLength]byte // previous block root that was boosted after being received in a timely manner.
|
||||
previousProposerBoostScore uint64 // previous proposer boosted root score.
|
||||
committeeWeight uint64 // tracks the total active validator balance divided by the number of slots per Epoch. Requires a checkpoints lock to read/write
|
||||
committeeWeight uint64 // tracks the total active validator balance divided by the number of slots per Epoch.
|
||||
treeRootNode *Node // the root node of the store tree.
|
||||
headNode *Node // last head Node
|
||||
nodeByRoot map[[fieldparams.RootLength]byte]*Node // nodes indexed by roots.
|
||||
nodeByPayload map[[fieldparams.RootLength]byte]*Node // nodes indexed by payload Hash
|
||||
slashedIndices map[primitives.ValidatorIndex]bool // the list of equivocating validator indices
|
||||
originRoot [fieldparams.RootLength]byte // The genesis block root
|
||||
nodesLock sync.RWMutex
|
||||
proposerBoostLock sync.RWMutex
|
||||
checkpointsLock sync.RWMutex
|
||||
genesisTime uint64
|
||||
highestReceivedNode *Node // The highest slot node.
|
||||
receivedBlocksLastEpoch [fieldparams.SlotsPerEpoch]primitives.Slot // Using `highestReceivedSlot`. The slot of blocks received in the last epoch.
|
||||
|
||||
@@ -16,9 +16,6 @@ import (
|
||||
)
|
||||
|
||||
func (s *Store) setUnrealizedJustifiedEpoch(root [32]byte, epoch primitives.Epoch) error {
|
||||
s.nodesLock.Lock()
|
||||
defer s.nodesLock.Unlock()
|
||||
|
||||
node, ok := s.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return errors.Wrap(ErrNilNode, "could not set unrealized justified epoch")
|
||||
@@ -31,9 +28,6 @@ func (s *Store) setUnrealizedJustifiedEpoch(root [32]byte, epoch primitives.Epoc
|
||||
}
|
||||
|
||||
func (s *Store) setUnrealizedFinalizedEpoch(root [32]byte, epoch primitives.Epoch) error {
|
||||
s.nodesLock.Lock()
|
||||
defer s.nodesLock.Unlock()
|
||||
|
||||
node, ok := s.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return errors.Wrap(ErrNilNode, "could not set unrealized finalized epoch")
|
||||
@@ -48,10 +42,6 @@ func (s *Store) setUnrealizedFinalizedEpoch(root [32]byte, epoch primitives.Epoc
|
||||
// updateUnrealizedCheckpoints "realizes" the unrealized justified and finalized
|
||||
// epochs stored within nodes. It should be called at the beginning of each epoch.
|
||||
func (f *ForkChoice) updateUnrealizedCheckpoints(ctx context.Context) error {
|
||||
f.store.nodesLock.Lock()
|
||||
defer f.store.nodesLock.Unlock()
|
||||
f.store.checkpointsLock.Lock()
|
||||
defer f.store.checkpointsLock.Unlock()
|
||||
for _, node := range f.store.nodeByRoot {
|
||||
node.justifiedEpoch = node.unrealizedJustifiedEpoch
|
||||
node.finalizedEpoch = node.unrealizedFinalizedEpoch
|
||||
@@ -81,16 +71,9 @@ func (f *ForkChoice) updateUnrealizedCheckpoints(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (s *Store) pullTips(state state.BeaconState, node *Node, jc, fc *ethpb.Checkpoint) (*ethpb.Checkpoint, *ethpb.Checkpoint) {
|
||||
s.nodesLock.Lock()
|
||||
defer s.nodesLock.Unlock()
|
||||
|
||||
if node.parent == nil { // Nothing to do if the parent is nil.
|
||||
return jc, fc
|
||||
}
|
||||
|
||||
s.checkpointsLock.Lock()
|
||||
defer s.checkpointsLock.Unlock()
|
||||
|
||||
currentEpoch := slots.ToEpoch(slots.CurrentSlot(s.genesisTime))
|
||||
stateSlot := state.Slot()
|
||||
stateEpoch := slots.ToEpoch(stateSlot)
|
||||
|
||||
@@ -23,16 +23,12 @@ func TestStore_SetUnrealizedEpochs(t *testing.T) {
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
f.store.nodesLock.RLock()
|
||||
require.Equal(t, primitives.Epoch(1), f.store.nodeByRoot[[32]byte{'b'}].unrealizedJustifiedEpoch)
|
||||
require.Equal(t, primitives.Epoch(1), f.store.nodeByRoot[[32]byte{'b'}].unrealizedFinalizedEpoch)
|
||||
f.store.nodesLock.RUnlock()
|
||||
require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'b'}, 2))
|
||||
require.NoError(t, f.store.setUnrealizedFinalizedEpoch([32]byte{'b'}, 2))
|
||||
f.store.nodesLock.RLock()
|
||||
require.Equal(t, primitives.Epoch(2), f.store.nodeByRoot[[32]byte{'b'}].unrealizedJustifiedEpoch)
|
||||
require.Equal(t, primitives.Epoch(2), f.store.nodeByRoot[[32]byte{'b'}].unrealizedFinalizedEpoch)
|
||||
f.store.nodesLock.RUnlock()
|
||||
|
||||
require.ErrorIs(t, errInvalidUnrealizedJustifiedEpoch, f.store.setUnrealizedJustifiedEpoch([32]byte{'b'}, 0))
|
||||
require.ErrorIs(t, errInvalidUnrealizedFinalizedEpoch, f.store.setUnrealizedFinalizedEpoch([32]byte{'b'}, 0))
|
||||
|
||||
@@ -16,6 +16,10 @@ type BalancesByRooter func(context.Context, [32]byte) ([]uint64, error)
|
||||
|
||||
// ForkChoicer represents the full fork choice interface composed of all the sub-interfaces.
|
||||
type ForkChoicer interface {
|
||||
Lock()
|
||||
Unlock()
|
||||
RLock()
|
||||
RUnlock()
|
||||
HeadRetriever // to compute head.
|
||||
BlockProcessor // to track new block for fork choice.
|
||||
AttestationProcessor // to track new attestation for fork choice.
|
||||
|
||||
@@ -129,12 +129,11 @@ func configureInteropConfig(cliCtx *cli.Context) error {
|
||||
if cliCtx.IsSet(cmd.ChainConfigFileFlag.Name) {
|
||||
return nil
|
||||
}
|
||||
genStateIsSet := cliCtx.IsSet(flags.InteropGenesisStateFlag.Name)
|
||||
genTimeIsSet := cliCtx.IsSet(flags.InteropGenesisTimeFlag.Name)
|
||||
numValsIsSet := cliCtx.IsSet(flags.InteropNumValidatorsFlag.Name)
|
||||
votesIsSet := cliCtx.IsSet(flags.InteropMockEth1DataVotesFlag.Name)
|
||||
|
||||
if genStateIsSet || genTimeIsSet || numValsIsSet || votesIsSet {
|
||||
if genTimeIsSet || numValsIsSet || votesIsSet {
|
||||
if err := params.SetActive(params.InteropConfig().Copy()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -219,17 +219,6 @@ func TestConfigureInterop(t *testing.T) {
|
||||
},
|
||||
"interop",
|
||||
},
|
||||
{
|
||||
"genesis state set",
|
||||
func() *cli.Context {
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String(flags.InteropGenesisStateFlag.Name, "", "")
|
||||
assert.NoError(t, set.Set(flags.InteropGenesisStateFlag.Name, "/path/"))
|
||||
return cli.NewContext(&app, set, nil)
|
||||
},
|
||||
"interop",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
@@ -67,9 +66,6 @@ import (
|
||||
|
||||
const testSkipPowFlag = "test-skip-pow"
|
||||
|
||||
// 128MB max message size when enabling debug endpoints.
|
||||
const debugGrpcMaxMsgSize = 1 << 27
|
||||
|
||||
// Used as a struct to keep cli flag options for configuring services
|
||||
// for the beacon node. We keep this as a separate struct to not pollute the actual BeaconNode
|
||||
// struct, as it is merely used to pass down configuration options into the appropriate services.
|
||||
@@ -762,10 +758,9 @@ func (b *BeaconNode) registerRPCService() error {
|
||||
}
|
||||
|
||||
genesisValidators := b.cliCtx.Uint64(flags.InteropNumValidatorsFlag.Name)
|
||||
genesisStatePath := b.cliCtx.String(flags.InteropGenesisStateFlag.Name)
|
||||
var depositFetcher depositcache.DepositFetcher
|
||||
var chainStartFetcher execution.ChainStartFetcher
|
||||
if genesisValidators > 0 || genesisStatePath != "" {
|
||||
if genesisValidators > 0 {
|
||||
var interopService *interopcoldstart.Service
|
||||
if err := b.services.FetchService(&interopService); err != nil {
|
||||
return err
|
||||
@@ -787,9 +782,6 @@ func (b *BeaconNode) registerRPCService() error {
|
||||
|
||||
maxMsgSize := b.cliCtx.Int(cmd.GrpcMaxCallRecvMsgSizeFlag.Name)
|
||||
enableDebugRPCEndpoints := b.cliCtx.Bool(flags.EnableDebugRPCEndpoints.Name)
|
||||
if enableDebugRPCEndpoints {
|
||||
maxMsgSize = int(math.Max(float64(maxMsgSize), debugGrpcMaxMsgSize))
|
||||
}
|
||||
|
||||
p2pService := b.fetchP2P()
|
||||
rpcService := rpc.NewService(b.ctx, &rpc.Config{
|
||||
@@ -881,9 +873,6 @@ func (b *BeaconNode) registerGRPCGateway() error {
|
||||
maxCallSize := b.cliCtx.Uint64(cmd.GrpcMaxCallRecvMsgSizeFlag.Name)
|
||||
httpModules := b.cliCtx.String(flags.HTTPModules.Name)
|
||||
timeout := b.cliCtx.Int(cmd.ApiTimeoutFlag.Name)
|
||||
if enableDebugRPCEndpoints {
|
||||
maxCallSize = uint64(math.Max(float64(maxCallSize), debugGrpcMaxMsgSize))
|
||||
}
|
||||
|
||||
gatewayConfig := gateway.DefaultConfig(enableDebugRPCEndpoints, httpModules)
|
||||
muxs := make([]*apigateway.PbMux, 0)
|
||||
@@ -917,15 +906,13 @@ func (b *BeaconNode) registerGRPCGateway() error {
|
||||
func (b *BeaconNode) registerDeterminsticGenesisService() error {
|
||||
genesisTime := b.cliCtx.Uint64(flags.InteropGenesisTimeFlag.Name)
|
||||
genesisValidators := b.cliCtx.Uint64(flags.InteropNumValidatorsFlag.Name)
|
||||
genesisStatePath := b.cliCtx.String(flags.InteropGenesisStateFlag.Name)
|
||||
|
||||
if genesisValidators > 0 || genesisStatePath != "" {
|
||||
if genesisValidators > 0 {
|
||||
svc := interopcoldstart.NewService(b.ctx, &interopcoldstart.Config{
|
||||
GenesisTime: genesisTime,
|
||||
NumValidators: genesisValidators,
|
||||
BeaconDB: b.db,
|
||||
DepositCache: b.depositCache,
|
||||
GenesisPath: genesisStatePath,
|
||||
})
|
||||
svc.Start()
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ func TestNodeStart_Ok_registerDeterministicGenesisService(t *testing.T) {
|
||||
genesisBytes, err := genesisState.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, os.WriteFile("genesis_ssz.json", genesisBytes, 0666))
|
||||
set.String(flags.InteropGenesisStateFlag.Name, "genesis_ssz.json", "")
|
||||
set.String("genesis-state", "genesis_ssz.json", "")
|
||||
ctx := cli.NewContext(&app, set, nil)
|
||||
node, err := New(ctx, WithBlockchainFlagOptions([]blockchain.Option{}),
|
||||
WithBuilderFlagOptions([]builder.Option{}),
|
||||
|
||||
@@ -4,33 +4,40 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"service.go",
|
||||
"pool.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/beacon-chain/operations/voluntaryexits",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//container/doubly-linked-list:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["service_test.go"],
|
||||
srcs = ["pool_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/core/signing:go_default_library",
|
||||
"//beacon-chain/core/time:go_default_library",
|
||||
"//beacon-chain/state/state-native:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//crypto/bls/common:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
eth "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
@@ -14,12 +12,17 @@ type PoolMock struct {
|
||||
}
|
||||
|
||||
// PendingExits --
|
||||
func (m *PoolMock) PendingExits(_ state.ReadOnlyBeaconState, _ primitives.Slot, _ bool) []*eth.SignedVoluntaryExit {
|
||||
return m.Exits
|
||||
func (m *PoolMock) PendingExits() ([]*eth.SignedVoluntaryExit, error) {
|
||||
return m.Exits, nil
|
||||
}
|
||||
|
||||
// ExitsForInclusion --
|
||||
func (m *PoolMock) ExitsForInclusion(_ state.ReadOnlyBeaconState, _ primitives.Slot) ([]*eth.SignedVoluntaryExit, error) {
|
||||
return m.Exits, nil
|
||||
}
|
||||
|
||||
// InsertVoluntaryExit --
|
||||
func (m *PoolMock) InsertVoluntaryExit(_ context.Context, _ state.ReadOnlyBeaconState, exit *eth.SignedVoluntaryExit) {
|
||||
func (m *PoolMock) InsertVoluntaryExit(exit *eth.SignedVoluntaryExit) {
|
||||
m.Exits = append(m.Exits, exit)
|
||||
}
|
||||
|
||||
|
||||
139
beacon-chain/operations/voluntaryexits/pool.go
Normal file
139
beacon-chain/operations/voluntaryexits/pool.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package voluntaryexits
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
doublylinkedlist "github.com/prysmaticlabs/prysm/v3/container/doubly-linked-list"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// PoolManager maintains pending and seen voluntary exits.
|
||||
// This pool is used by proposers to insert voluntary exits into new blocks.
|
||||
type PoolManager interface {
|
||||
PendingExits() ([]*ethpb.SignedVoluntaryExit, error)
|
||||
ExitsForInclusion(state state.ReadOnlyBeaconState, slot types.Slot) ([]*ethpb.SignedVoluntaryExit, error)
|
||||
InsertVoluntaryExit(exit *ethpb.SignedVoluntaryExit)
|
||||
MarkIncluded(exit *ethpb.SignedVoluntaryExit)
|
||||
}
|
||||
|
||||
// Pool is a concrete implementation of PoolManager.
|
||||
type Pool struct {
|
||||
lock sync.RWMutex
|
||||
pending doublylinkedlist.List[*ethpb.SignedVoluntaryExit]
|
||||
m map[types.ValidatorIndex]*doublylinkedlist.Node[*ethpb.SignedVoluntaryExit]
|
||||
}
|
||||
|
||||
// NewPool returns an initialized pool.
|
||||
func NewPool() *Pool {
|
||||
return &Pool{
|
||||
pending: doublylinkedlist.List[*ethpb.SignedVoluntaryExit]{},
|
||||
m: make(map[types.ValidatorIndex]*doublylinkedlist.Node[*ethpb.SignedVoluntaryExit]),
|
||||
}
|
||||
}
|
||||
|
||||
// PendingExits returns all objects from the pool.
|
||||
func (p *Pool) PendingExits() ([]*ethpb.SignedVoluntaryExit, error) {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
|
||||
result := make([]*ethpb.SignedVoluntaryExit, p.pending.Len())
|
||||
node := p.pending.First()
|
||||
var err error
|
||||
for i := 0; node != nil; i++ {
|
||||
result[i], err = node.Value()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
node, err = node.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ExitsForInclusion returns objects that are ready for inclusion at the given slot. This method will not
|
||||
// return more than the block enforced MaxVoluntaryExits.
|
||||
func (p *Pool) ExitsForInclusion(state state.ReadOnlyBeaconState, slot types.Slot) ([]*ethpb.SignedVoluntaryExit, error) {
|
||||
p.lock.RLock()
|
||||
length := int(math.Min(float64(params.BeaconConfig().MaxVoluntaryExits), float64(p.pending.Len())))
|
||||
result := make([]*ethpb.SignedVoluntaryExit, 0, length)
|
||||
node := p.pending.First()
|
||||
for node != nil && len(result) < length {
|
||||
exit, err := node.Value()
|
||||
if err != nil {
|
||||
p.lock.RUnlock()
|
||||
return nil, err
|
||||
}
|
||||
if exit.Exit.Epoch > slots.ToEpoch(slot) {
|
||||
node, err = node.Next()
|
||||
if err != nil {
|
||||
p.lock.RUnlock()
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
validator, err := state.ValidatorAtIndexReadOnly(exit.Exit.ValidatorIndex)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Warningf("could not get validator at index %d", exit.Exit.ValidatorIndex)
|
||||
node, err = node.Next()
|
||||
if err != nil {
|
||||
p.lock.RUnlock()
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err = blocks.VerifyExitAndSignature(validator, state.Slot(), state.Fork(), exit, state.GenesisValidatorsRoot()); err != nil {
|
||||
logrus.WithError(err).Warning("removing invalid exit from pool")
|
||||
p.lock.RUnlock()
|
||||
// MarkIncluded removes the invalid exit from the pool
|
||||
p.MarkIncluded(exit)
|
||||
p.lock.RLock()
|
||||
} else {
|
||||
result = append(result, exit)
|
||||
}
|
||||
node, err = node.Next()
|
||||
if err != nil {
|
||||
p.lock.RUnlock()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
p.lock.RUnlock()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// InsertVoluntaryExit into the pool.
|
||||
func (p *Pool) InsertVoluntaryExit(exit *ethpb.SignedVoluntaryExit) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
_, exists := p.m[exit.Exit.ValidatorIndex]
|
||||
if exists {
|
||||
return
|
||||
}
|
||||
|
||||
p.pending.Append(doublylinkedlist.NewNode(exit))
|
||||
p.m[exit.Exit.ValidatorIndex] = p.pending.Last()
|
||||
}
|
||||
|
||||
// MarkIncluded is used when an exit has been included in a beacon block. Every block seen by this
|
||||
// node should call this method to include the exit. This will remove the exit from the pool.
|
||||
func (p *Pool) MarkIncluded(exit *ethpb.SignedVoluntaryExit) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
node := p.m[exit.Exit.ValidatorIndex]
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
|
||||
delete(p.m, exit.Exit.ValidatorIndex)
|
||||
p.pending.Remove(node)
|
||||
}
|
||||
327
beacon-chain/operations/voluntaryexits/pool_test.go
Normal file
327
beacon-chain/operations/voluntaryexits/pool_test.go
Normal file
@@ -0,0 +1,327 @@
|
||||
package voluntaryexits
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/signing"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/time"
|
||||
state_native "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v3/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/v3/crypto/bls/common"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
||||
)
|
||||
|
||||
func TestPendingExits(t *testing.T) {
|
||||
t.Run("empty pool", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
changes, err := pool.PendingExits()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, len(changes))
|
||||
})
|
||||
t.Run("non-empty pool", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
pool.InsertVoluntaryExit(ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 0,
|
||||
ValidatorIndex: 0,
|
||||
},
|
||||
})
|
||||
pool.InsertVoluntaryExit(ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 0,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
})
|
||||
changes, err := pool.PendingExits()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, len(changes))
|
||||
})
|
||||
}
|
||||
|
||||
func TestExitsForInclusion(t *testing.T) {
|
||||
spb := ðpb.BeaconStateCapella{
|
||||
Fork: ðpb.Fork{
|
||||
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
},
|
||||
}
|
||||
stateSlot := types.Slot(uint64(params.BeaconConfig().ShardCommitteePeriod) * uint64(params.BeaconConfig().SlotsPerEpoch))
|
||||
spb.Slot = stateSlot
|
||||
numValidators := 2 * params.BeaconConfig().MaxVoluntaryExits
|
||||
validators := make([]*ethpb.Validator, numValidators)
|
||||
exits := make([]*ethpb.VoluntaryExit, numValidators)
|
||||
privKeys := make([]common.SecretKey, numValidators)
|
||||
|
||||
for i := range validators {
|
||||
v := ðpb.Validator{}
|
||||
if i == len(validators)-2 {
|
||||
// exit for this validator is invalid
|
||||
v.ExitEpoch = 0
|
||||
} else {
|
||||
v.ExitEpoch = params.BeaconConfig().FarFutureEpoch
|
||||
}
|
||||
priv, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
privKeys[i] = priv
|
||||
pubkey := priv.PublicKey().Marshal()
|
||||
v.PublicKey = pubkey
|
||||
|
||||
message := ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(i),
|
||||
}
|
||||
// exit for future slot
|
||||
if i == len(validators)-1 {
|
||||
message.Epoch = slots.ToEpoch(stateSlot) + 1
|
||||
}
|
||||
|
||||
validators[i] = v
|
||||
exits[i] = message
|
||||
}
|
||||
spb.Validators = validators
|
||||
st, err := state_native.InitializeFromProtoCapella(spb)
|
||||
require.NoError(t, err)
|
||||
|
||||
signedExits := make([]*ethpb.SignedVoluntaryExit, numValidators)
|
||||
for i, message := range exits {
|
||||
signature, err := signing.ComputeDomainAndSign(st, time.CurrentEpoch(st), message, params.BeaconConfig().DomainVoluntaryExit, privKeys[i])
|
||||
require.NoError(t, err)
|
||||
|
||||
signed := ðpb.SignedVoluntaryExit{
|
||||
Exit: message,
|
||||
Signature: signature,
|
||||
}
|
||||
signedExits[i] = signed
|
||||
}
|
||||
|
||||
t.Run("empty pool", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
exits, err := pool.ExitsForInclusion(st, stateSlot)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, len(exits))
|
||||
})
|
||||
t.Run("less than MaxVoluntaryExits in pool", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
for i := uint64(0); i < params.BeaconConfig().MaxVoluntaryExits-1; i++ {
|
||||
pool.InsertVoluntaryExit(signedExits[i])
|
||||
}
|
||||
exits, err := pool.ExitsForInclusion(st, stateSlot)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int(params.BeaconConfig().MaxVoluntaryExits)-1, len(exits))
|
||||
})
|
||||
t.Run("MaxVoluntaryExits in pool", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
for i := uint64(0); i < params.BeaconConfig().MaxVoluntaryExits; i++ {
|
||||
pool.InsertVoluntaryExit(signedExits[i])
|
||||
}
|
||||
exits, err := pool.ExitsForInclusion(st, stateSlot)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int(params.BeaconConfig().MaxVoluntaryExits), len(exits))
|
||||
})
|
||||
t.Run("more than MaxVoluntaryExits in pool", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
for i := uint64(0); i < numValidators; i++ {
|
||||
pool.InsertVoluntaryExit(signedExits[i])
|
||||
}
|
||||
exits, err := pool.ExitsForInclusion(st, stateSlot)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int(params.BeaconConfig().MaxVoluntaryExits), len(exits))
|
||||
for _, ch := range exits {
|
||||
assert.NotEqual(t, types.ValidatorIndex(params.BeaconConfig().MaxVoluntaryExits), ch.Exit.ValidatorIndex)
|
||||
}
|
||||
})
|
||||
t.Run("exit for future epoch not returned", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
pool.InsertVoluntaryExit(signedExits[len(signedExits)-1])
|
||||
exits, err := pool.ExitsForInclusion(st, stateSlot)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, len(exits))
|
||||
})
|
||||
t.Run("invalid exit not returned", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
pool.InsertVoluntaryExit(signedExits[len(signedExits)-2])
|
||||
exits, err := pool.ExitsForInclusion(st, stateSlot)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, len(exits))
|
||||
})
|
||||
}
|
||||
|
||||
func TestInsertExit(t *testing.T) {
|
||||
t.Run("empty pool", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
exit := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(0),
|
||||
},
|
||||
}
|
||||
pool.InsertVoluntaryExit(exit)
|
||||
require.Equal(t, 1, pool.pending.Len())
|
||||
require.Equal(t, 1, len(pool.m))
|
||||
n, ok := pool.m[0]
|
||||
require.Equal(t, true, ok)
|
||||
v, err := n.Value()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, exit, v)
|
||||
})
|
||||
t.Run("item in pool", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
old := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(0),
|
||||
},
|
||||
}
|
||||
exit := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(1),
|
||||
},
|
||||
}
|
||||
pool.InsertVoluntaryExit(old)
|
||||
pool.InsertVoluntaryExit(exit)
|
||||
require.Equal(t, 2, pool.pending.Len())
|
||||
require.Equal(t, 2, len(pool.m))
|
||||
n, ok := pool.m[0]
|
||||
require.Equal(t, true, ok)
|
||||
v, err := n.Value()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, old, v)
|
||||
n, ok = pool.m[1]
|
||||
require.Equal(t, true, ok)
|
||||
v, err = n.Value()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, exit, v)
|
||||
})
|
||||
t.Run("validator index already exists", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
old := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(0),
|
||||
},
|
||||
Signature: []byte("old"),
|
||||
}
|
||||
exit := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(0),
|
||||
},
|
||||
Signature: []byte("exit"),
|
||||
}
|
||||
pool.InsertVoluntaryExit(old)
|
||||
pool.InsertVoluntaryExit(exit)
|
||||
assert.Equal(t, 1, pool.pending.Len())
|
||||
require.Equal(t, 1, len(pool.m))
|
||||
n, ok := pool.m[0]
|
||||
require.Equal(t, true, ok)
|
||||
v, err := n.Value()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, old, v)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMarkIncluded(t *testing.T) {
|
||||
t.Run("one element in pool", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
exit := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(0),
|
||||
}}
|
||||
pool.InsertVoluntaryExit(exit)
|
||||
pool.MarkIncluded(exit)
|
||||
assert.Equal(t, 0, pool.pending.Len())
|
||||
_, ok := pool.m[0]
|
||||
assert.Equal(t, false, ok)
|
||||
})
|
||||
t.Run("first of multiple elements", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
first := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(0),
|
||||
}}
|
||||
second := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(1),
|
||||
}}
|
||||
third := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(2),
|
||||
}}
|
||||
pool.InsertVoluntaryExit(first)
|
||||
pool.InsertVoluntaryExit(second)
|
||||
pool.InsertVoluntaryExit(third)
|
||||
pool.MarkIncluded(first)
|
||||
require.Equal(t, 2, pool.pending.Len())
|
||||
_, ok := pool.m[0]
|
||||
assert.Equal(t, false, ok)
|
||||
})
|
||||
t.Run("last of multiple elements", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
first := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(0),
|
||||
}}
|
||||
second := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(1),
|
||||
}}
|
||||
third := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(2),
|
||||
}}
|
||||
pool.InsertVoluntaryExit(first)
|
||||
pool.InsertVoluntaryExit(second)
|
||||
pool.InsertVoluntaryExit(third)
|
||||
pool.MarkIncluded(third)
|
||||
require.Equal(t, 2, pool.pending.Len())
|
||||
_, ok := pool.m[2]
|
||||
assert.Equal(t, false, ok)
|
||||
})
|
||||
t.Run("in the middle of multiple elements", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
first := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(0),
|
||||
}}
|
||||
second := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(1),
|
||||
}}
|
||||
third := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(2),
|
||||
}}
|
||||
pool.InsertVoluntaryExit(first)
|
||||
pool.InsertVoluntaryExit(second)
|
||||
pool.InsertVoluntaryExit(third)
|
||||
pool.MarkIncluded(second)
|
||||
require.Equal(t, 2, pool.pending.Len())
|
||||
_, ok := pool.m[1]
|
||||
assert.Equal(t, false, ok)
|
||||
})
|
||||
t.Run("not in pool", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
first := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(0),
|
||||
}}
|
||||
second := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(1),
|
||||
}}
|
||||
exit := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: types.ValidatorIndex(2),
|
||||
}}
|
||||
pool.InsertVoluntaryExit(first)
|
||||
pool.InsertVoluntaryExit(second)
|
||||
pool.MarkIncluded(exit)
|
||||
require.Equal(t, 2, pool.pending.Len())
|
||||
_, ok := pool.m[0]
|
||||
require.Equal(t, true, ok)
|
||||
assert.NotNil(t, pool.m[0])
|
||||
_, ok = pool.m[1]
|
||||
require.Equal(t, true, ok)
|
||||
assert.NotNil(t, pool.m[1])
|
||||
})
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package voluntaryexits
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// PoolManager maintains pending and seen voluntary exits.
|
||||
// This pool is used by proposers to insert voluntary exits into new blocks.
|
||||
type PoolManager interface {
|
||||
PendingExits(state state.ReadOnlyBeaconState, slot primitives.Slot, noLimit bool) []*ethpb.SignedVoluntaryExit
|
||||
InsertVoluntaryExit(ctx context.Context, state state.ReadOnlyBeaconState, exit *ethpb.SignedVoluntaryExit)
|
||||
MarkIncluded(exit *ethpb.SignedVoluntaryExit)
|
||||
}
|
||||
|
||||
// Pool is a concrete implementation of PoolManager.
|
||||
type Pool struct {
|
||||
lock sync.RWMutex
|
||||
pending []*ethpb.SignedVoluntaryExit
|
||||
}
|
||||
|
||||
// NewPool accepts a head fetcher (for reading the validator set) and returns an initialized
|
||||
// voluntary exit pool.
|
||||
func NewPool() *Pool {
|
||||
return &Pool{
|
||||
pending: make([]*ethpb.SignedVoluntaryExit, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// PendingExits returns exits that are ready for inclusion at the given slot. This method will not
|
||||
// return more than the block enforced MaxVoluntaryExits.
|
||||
func (p *Pool) PendingExits(state state.ReadOnlyBeaconState, slot primitives.Slot, noLimit bool) []*ethpb.SignedVoluntaryExit {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
|
||||
// Allocate pending slice with a capacity of min(len(p.pending), maxVoluntaryExits) since the
|
||||
// array cannot exceed the max and is typically less than the max value.
|
||||
maxExits := params.BeaconConfig().MaxVoluntaryExits
|
||||
if noLimit {
|
||||
maxExits = uint64(len(p.pending))
|
||||
}
|
||||
pending := make([]*ethpb.SignedVoluntaryExit, 0, maxExits)
|
||||
for _, e := range p.pending {
|
||||
if e.Exit.Epoch > slots.ToEpoch(slot) {
|
||||
continue
|
||||
}
|
||||
if v, err := state.ValidatorAtIndexReadOnly(e.Exit.ValidatorIndex); err == nil &&
|
||||
v.ExitEpoch() == params.BeaconConfig().FarFutureEpoch {
|
||||
pending = append(pending, e)
|
||||
if uint64(len(pending)) == maxExits {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return pending
|
||||
}
|
||||
|
||||
// InsertVoluntaryExit into the pool. This method is a no-op if the pending exit already exists,
|
||||
// or the validator is already exited.
|
||||
func (p *Pool) InsertVoluntaryExit(ctx context.Context, state state.ReadOnlyBeaconState, exit *ethpb.SignedVoluntaryExit) {
|
||||
ctx, span := trace.StartSpan(ctx, "exitPool.InsertVoluntaryExit")
|
||||
defer span.End()
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
// Prevent malformed messages from being inserted.
|
||||
if exit == nil || exit.Exit == nil {
|
||||
return
|
||||
}
|
||||
|
||||
existsInPending, index := existsInList(p.pending, exit.Exit.ValidatorIndex)
|
||||
// If the item exists in the pending list and includes a more favorable, earlier
|
||||
// exit epoch, we replace it in the pending list. If it exists but the prior condition is false,
|
||||
// we simply return.
|
||||
if existsInPending {
|
||||
if exit.Exit.Epoch < p.pending[index].Exit.Epoch {
|
||||
p.pending[index] = exit
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Has the validator been exited already?
|
||||
if v, err := state.ValidatorAtIndexReadOnly(exit.Exit.ValidatorIndex); err != nil ||
|
||||
v.ExitEpoch() != params.BeaconConfig().FarFutureEpoch {
|
||||
return
|
||||
}
|
||||
|
||||
// Insert into pending list and sort.
|
||||
p.pending = append(p.pending, exit)
|
||||
sort.Slice(p.pending, func(i, j int) bool {
|
||||
return p.pending[i].Exit.ValidatorIndex < p.pending[j].Exit.ValidatorIndex
|
||||
})
|
||||
}
|
||||
|
||||
// MarkIncluded is used when an exit has been included in a beacon block. Every block seen by this
|
||||
// node should call this method to include the exit. This will remove the exit from
|
||||
// the pending exits slice.
|
||||
func (p *Pool) MarkIncluded(exit *ethpb.SignedVoluntaryExit) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
exists, index := existsInList(p.pending, exit.Exit.ValidatorIndex)
|
||||
if exists {
|
||||
// Exit we want is present at p.pending[index], so we remove it.
|
||||
p.pending = append(p.pending[:index], p.pending[index+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
// Binary search to check if the index exists in the list of pending exits.
|
||||
func existsInList(pending []*ethpb.SignedVoluntaryExit, searchingFor primitives.ValidatorIndex) (bool, int) {
|
||||
i := sort.Search(len(pending), func(j int) bool {
|
||||
return pending[j].Exit.ValidatorIndex >= searchingFor
|
||||
})
|
||||
if i < len(pending) && pending[i].Exit.ValidatorIndex == searchingFor {
|
||||
return true, i
|
||||
}
|
||||
return false, -1
|
||||
}
|
||||
@@ -1,530 +0,0 @@
|
||||
package voluntaryexits
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
state_native "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestPool_InsertVoluntaryExit(t *testing.T) {
|
||||
type fields struct {
|
||||
pending []*ethpb.SignedVoluntaryExit
|
||||
}
|
||||
type args struct {
|
||||
exit *ethpb.SignedVoluntaryExit
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want []*ethpb.SignedVoluntaryExit
|
||||
}{
|
||||
{
|
||||
name: "Prevent inserting nil exit",
|
||||
fields: fields{
|
||||
pending: make([]*ethpb.SignedVoluntaryExit, 0),
|
||||
},
|
||||
args: args{
|
||||
exit: nil,
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{},
|
||||
},
|
||||
{
|
||||
name: "Prevent inserting malformed exit",
|
||||
fields: fields{
|
||||
pending: make([]*ethpb.SignedVoluntaryExit, 0),
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: nil,
|
||||
},
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{},
|
||||
},
|
||||
{
|
||||
name: "Empty list",
|
||||
fields: fields{
|
||||
pending: make([]*ethpb.SignedVoluntaryExit, 0),
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Duplicate identical exit",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Duplicate exit in pending list",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Duplicate validator index",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 20,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Duplicate received with more favorable exit epoch",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 4,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 4,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Exit for already exited validator",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{},
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{},
|
||||
},
|
||||
{
|
||||
name: "Maintains sorted order",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 10,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 10,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
validators := []*ethpb.Validator{
|
||||
{ // 0
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
},
|
||||
{ // 1
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
},
|
||||
{ // 2 - Already exited.
|
||||
ExitEpoch: 15,
|
||||
},
|
||||
{ // 3
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := &Pool{
|
||||
pending: tt.fields.pending,
|
||||
}
|
||||
s, err := state_native.InitializeFromProtoUnsafePhase0(ðpb.BeaconState{Validators: validators})
|
||||
require.NoError(t, err)
|
||||
p.InsertVoluntaryExit(ctx, s, tt.args.exit)
|
||||
if len(p.pending) != len(tt.want) {
|
||||
t.Fatalf("Mismatched lengths of pending list. Got %d, wanted %d.", len(p.pending), len(tt.want))
|
||||
}
|
||||
for i := range p.pending {
|
||||
if !proto.Equal(p.pending[i], tt.want[i]) {
|
||||
t.Errorf("Pending exit at index %d does not match expected. Got=%v wanted=%v", i, p.pending[i], tt.want[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPool_MarkIncluded(t *testing.T) {
|
||||
type fields struct {
|
||||
pending []*ethpb.SignedVoluntaryExit
|
||||
}
|
||||
type args struct {
|
||||
exit *ethpb.SignedVoluntaryExit
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want fields
|
||||
}{
|
||||
{
|
||||
name: "Removes from pending list",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{ValidatorIndex: 1},
|
||||
},
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{ValidatorIndex: 2},
|
||||
},
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{ValidatorIndex: 3},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{ValidatorIndex: 2},
|
||||
},
|
||||
},
|
||||
want: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{ValidatorIndex: 1},
|
||||
},
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{ValidatorIndex: 3},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := &Pool{
|
||||
pending: tt.fields.pending,
|
||||
}
|
||||
p.MarkIncluded(tt.args.exit)
|
||||
if len(p.pending) != len(tt.want.pending) {
|
||||
t.Fatalf("Mismatched lengths of pending list. Got %d, wanted %d.", len(p.pending), len(tt.want.pending))
|
||||
}
|
||||
for i := range p.pending {
|
||||
if !proto.Equal(p.pending[i], tt.want.pending[i]) {
|
||||
t.Errorf("Pending exit at index %d does not match expected. Got=%v wanted=%v", i, p.pending[i], tt.want.pending[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPool_PendingExits(t *testing.T) {
|
||||
type fields struct {
|
||||
pending []*ethpb.SignedVoluntaryExit
|
||||
noLimit bool
|
||||
}
|
||||
type args struct {
|
||||
slot primitives.Slot
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want []*ethpb.SignedVoluntaryExit
|
||||
}{
|
||||
{
|
||||
name: "Empty list",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{},
|
||||
},
|
||||
args: args{
|
||||
slot: 100000,
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{},
|
||||
},
|
||||
{
|
||||
name: "All eligible",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 0}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 1}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 2}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 3}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 4}},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
slot: 1000000,
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 0}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 1}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 2}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 3}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 4}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "All eligible, above max",
|
||||
fields: fields{
|
||||
noLimit: true,
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 0}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 1}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 2}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 3}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 4}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 5}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 6}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 7}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 8}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 9}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 10}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 11}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 12}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 13}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 14}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 15}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 16}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 17}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 18}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 19}},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
slot: 1000000,
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 0}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 1}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 2}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 3}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 4}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 5}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 6}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 7}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 8}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 9}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 10}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 11}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 12}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 13}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 14}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 15}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 16}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 17}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 18}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 19}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "All eligible, block max",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 0}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 1}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 2}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 3}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 4}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 5}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 6}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 7}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 8}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 9}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 10}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 11}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 12}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 13}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 14}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 15}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 16}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 17}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 18}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 19}},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
slot: 1000000,
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 0}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 1}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 2}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 3}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 4}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 5}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 6}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 7}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 8}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 9}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 10}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 11}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 12}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 13}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 14}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 15}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Some eligible",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 0}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 3}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 4}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 2}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 1}},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
slot: 2 * params.BeaconConfig().SlotsPerEpoch,
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 0}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 2}},
|
||||
{Exit: ðpb.VoluntaryExit{Epoch: 1}},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := &Pool{
|
||||
pending: tt.fields.pending,
|
||||
}
|
||||
s, err := state_native.InitializeFromProtoUnsafePhase0(ðpb.BeaconState{Validators: []*ethpb.Validator{{ExitEpoch: params.BeaconConfig().FarFutureEpoch}}})
|
||||
require.NoError(t, err)
|
||||
if got := p.PendingExits(s, tt.args.slot, tt.fields.noLimit); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("PendingExits() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -106,6 +106,7 @@ go_test(
|
||||
"//beacon-chain/rpc/testutil:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/state-native:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/interfaces:go_default_library",
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
dbTest "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing"
|
||||
executionTest "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/testing"
|
||||
mockp2p "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/testing"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
|
||||
@@ -1397,6 +1398,10 @@ func TestServer_GetBlockV2(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Bellatrix", func(t *testing.T) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
SaveFullExecutionPayloads: true,
|
||||
})
|
||||
defer resetFn()
|
||||
beaconDB := dbTest.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -1519,6 +1524,10 @@ func TestServer_GetBlockV2(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Capella", func(t *testing.T) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
SaveFullExecutionPayloads: true,
|
||||
})
|
||||
defer resetFn()
|
||||
beaconDB := dbTest.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -1825,6 +1834,10 @@ func TestServer_GetBlockSSZV2(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Bellatrix", func(t *testing.T) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
SaveFullExecutionPayloads: true,
|
||||
})
|
||||
defer resetFn()
|
||||
beaconDB := dbTest.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -1860,6 +1873,10 @@ func TestServer_GetBlockSSZV2(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Capella", func(t *testing.T) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
SaveFullExecutionPayloads: true,
|
||||
})
|
||||
defer resetFn()
|
||||
beaconDB := dbTest.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
@@ -253,16 +253,13 @@ func (bs *Server) SubmitProposerSlashing(ctx context.Context, req *ethpbv1.Propo
|
||||
// ListPoolVoluntaryExits retrieves voluntary exits known by the node but
|
||||
// not necessarily incorporated into any block.
|
||||
func (bs *Server) ListPoolVoluntaryExits(ctx context.Context, _ *emptypb.Empty) (*ethpbv1.VoluntaryExitsPoolResponse, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon.ListPoolVoluntaryExits")
|
||||
_, span := trace.StartSpan(ctx, "beacon.ListPoolVoluntaryExits")
|
||||
defer span.End()
|
||||
|
||||
headState, err := bs.ChainInfoFetcher.HeadState(ctx)
|
||||
sourceExits, err := bs.VoluntaryExitsPool.PendingExits()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not get head state: %v", err)
|
||||
return nil, status.Error(codes.Internal, "Could not get exits from the pool")
|
||||
}
|
||||
|
||||
sourceExits := bs.VoluntaryExitsPool.PendingExits(headState, headState.Slot(), true /* return unlimited exits */)
|
||||
|
||||
exits := make([]*ethpbv1.SignedVoluntaryExit, len(sourceExits))
|
||||
for i, s := range sourceExits {
|
||||
exits[i] = migration.V1Alpha1ExitToV1(s)
|
||||
@@ -302,7 +299,7 @@ func (bs *Server) SubmitVoluntaryExit(ctx context.Context, req *ethpbv1.SignedVo
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Invalid voluntary exit: %v", err)
|
||||
}
|
||||
|
||||
bs.VoluntaryExitsPool.InsertVoluntaryExit(ctx, headState, alphaExit)
|
||||
bs.VoluntaryExitsPool.InsertVoluntaryExit(alphaExit)
|
||||
if err := bs.Broadcaster.Broadcast(ctx, alphaExit); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not broadcast voluntary exit object: %v", err)
|
||||
}
|
||||
|
||||
@@ -770,7 +770,8 @@ func TestSubmitVoluntaryExit_Ok(t *testing.T) {
|
||||
|
||||
_, err = s.SubmitVoluntaryExit(ctx, exit)
|
||||
require.NoError(t, err)
|
||||
pendingExits := s.VoluntaryExitsPool.PendingExits(bs, bs.Slot(), true)
|
||||
pendingExits, err := s.VoluntaryExitsPool.PendingExits()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(pendingExits))
|
||||
assert.DeepEqual(t, migration.V1ExitToV1Alpha1(exit), pendingExits[0])
|
||||
assert.Equal(t, true, broadcaster.BroadcastCalled)
|
||||
|
||||
@@ -166,5 +166,7 @@ func (ds *Server) ListForkChoiceHeadsV2(ctx context.Context, _ *emptypb.Empty) (
|
||||
|
||||
// GetForkChoice returns a dump fork choice store.
|
||||
func (ds *Server) GetForkChoice(ctx context.Context, _ *emptypb.Empty) (*ethpbv1.ForkChoiceDump, error) {
|
||||
ds.ForkFetcher.ForkChoicer().RLock()
|
||||
defer ds.ForkFetcher.ForkChoicer().RUnlock()
|
||||
return ds.ForkFetcher.ForkChoicer().ForkChoiceDump(ctx)
|
||||
}
|
||||
|
||||
@@ -558,10 +558,6 @@ func TestServer_ListBeaconBlocks_Genesis(t *testing.T) {
|
||||
}
|
||||
|
||||
func runListBlocksGenesis(t *testing.T, blk interfaces.ReadOnlySignedBeaconBlock, blkContainer *ethpb.BeaconBlockContainer) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
EnableOnlyBlindedBeaconBlocks: true,
|
||||
})
|
||||
defer resetFn()
|
||||
db := dbTest.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -745,6 +741,10 @@ func TestServer_ListBeaconBlocks_Pagination(t *testing.T) {
|
||||
runListBeaconBlocksPagination(t, orphanedB, blockCreator, containerCreator)
|
||||
})
|
||||
t.Run("bellatrix block", func(t *testing.T) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
SaveFullExecutionPayloads: true,
|
||||
})
|
||||
defer resetFn()
|
||||
blk := util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Slot = 300
|
||||
blockCreator := func(i primitives.Slot) interfaces.ReadOnlySignedBeaconBlock {
|
||||
@@ -769,6 +769,10 @@ func TestServer_ListBeaconBlocks_Pagination(t *testing.T) {
|
||||
runListBeaconBlocksPagination(t, orphanedB, blockCreator, containerCreator)
|
||||
})
|
||||
t.Run("capella block", func(t *testing.T) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
SaveFullExecutionPayloads: true,
|
||||
})
|
||||
defer resetFn()
|
||||
blk := util.NewBeaconBlockCapella()
|
||||
blk.Block.Slot = 300
|
||||
blockCreator := func(i primitives.Slot) interfaces.ReadOnlySignedBeaconBlock {
|
||||
|
||||
@@ -45,7 +45,7 @@ func (vs *Server) ProposeExit(ctx context.Context, req *ethpb.SignedVoluntaryExi
|
||||
},
|
||||
})
|
||||
|
||||
vs.ExitPool.InsertVoluntaryExit(ctx, s, req)
|
||||
vs.ExitPool.InsertVoluntaryExit(req)
|
||||
|
||||
r, err := req.Exit.HashTreeRoot()
|
||||
if err != nil {
|
||||
|
||||
@@ -45,6 +45,9 @@ func (vs *Server) circuitBreakBuilder(s primitives.Slot) (bool, error) {
|
||||
return true, errors.New("no fork choicer configured")
|
||||
}
|
||||
|
||||
vs.ForkFetcher.ForkChoicer().RLock()
|
||||
defer vs.ForkFetcher.ForkChoicer().RUnlock()
|
||||
|
||||
// Circuit breaker is active if the missing consecutive slots greater than `MaxBuilderConsecutiveMissedSlots`.
|
||||
highestReceivedSlot := vs.ForkFetcher.ForkChoicer().HighestReceivedBlockSlot()
|
||||
maxConsecutiveSkipSlotsAllowed := params.BeaconConfig().MaxBuilderConsecutiveMissedSlots
|
||||
|
||||
@@ -1,28 +1,16 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
func (vs *Server) getExits(head state.BeaconState, slot primitives.Slot) []*ethpb.SignedVoluntaryExit {
|
||||
exits := vs.ExitPool.PendingExits(head, slot, false /*noLimit*/)
|
||||
validExits := make([]*ethpb.SignedVoluntaryExit, 0, len(exits))
|
||||
|
||||
for _, exit := range exits {
|
||||
val, err := head.ValidatorAtIndexReadOnly(exit.Exit.ValidatorIndex)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("Could not retrieve validator index")
|
||||
continue
|
||||
}
|
||||
if err := blocks.VerifyExitAndSignature(val, head.Slot(), head.Fork(), exit, head.GenesisValidatorsRoot()); err != nil {
|
||||
log.WithError(err).Warn("Could not verify exit for block inclusion")
|
||||
continue
|
||||
}
|
||||
validExits = append(validExits, exit)
|
||||
exits, err := vs.ExitPool.ExitsForInclusion(head, slot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get exits")
|
||||
return []*ethpb.SignedVoluntaryExit{}
|
||||
}
|
||||
|
||||
return validExits
|
||||
return exits
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/operations/voluntaryexits"
|
||||
@@ -28,7 +27,7 @@ func TestServer_getExits(t *testing.T) {
|
||||
for i := primitives.ValidatorIndex(0); uint64(i) < params.BeaconConfig().MaxVoluntaryExits; i++ {
|
||||
exit, err := util.GenerateVoluntaryExits(beaconState, privKeys[i], i)
|
||||
require.NoError(t, err)
|
||||
proposerServer.ExitPool.InsertVoluntaryExit(context.Background(), beaconState, exit)
|
||||
proposerServer.ExitPool.InsertVoluntaryExit(exit)
|
||||
exits[i] = exit
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -26,7 +27,7 @@ var (
|
||||
debug = flag.Bool("debug", false, "Enable debug logging")
|
||||
allowedOrigins = flag.String("corsdomain", "localhost:4242", "A comma separated list of CORS domains to allow")
|
||||
enableDebugRPCEndpoints = flag.Bool("enable-debug-rpc-endpoints", false, "Enable debug rpc endpoints such as /eth/v1alpha1/beacon/state")
|
||||
grpcMaxMsgSize = flag.Int("grpc-max-msg-size", 1<<22, "Integer to define max recieve message call size")
|
||||
grpcMaxMsgSize = flag.Int("grpc-max-msg-size", math.MaxInt32, "Integer to define max recieve message call size")
|
||||
httpModules = flag.String(
|
||||
"http-modules",
|
||||
strings.Join([]string{flags.PrysmAPIModule, flags.EthAPIModule}, ","),
|
||||
|
||||
@@ -121,8 +121,7 @@ func convertRandaoMixes(indices []uint64, elements interface{}, convertAll bool)
|
||||
func convertEth1DataVotes(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
|
||||
val, ok := elements.([]*ethpb.Eth1Data)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("Wanted type of %v but got %v",
|
||||
reflect.TypeOf([]*ethpb.Eth1Data{}).Name(), reflect.TypeOf(elements).Name())
|
||||
return nil, errors.Errorf("Wanted type of %T but got %T", []*ethpb.Eth1Data{}, elements)
|
||||
}
|
||||
return handleEth1DataSlice(val, indices, convertAll)
|
||||
}
|
||||
@@ -130,8 +129,7 @@ func convertEth1DataVotes(indices []uint64, elements interface{}, convertAll boo
|
||||
func convertValidators(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
|
||||
val, ok := elements.([]*ethpb.Validator)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("Wanted type of %v but got %v",
|
||||
reflect.TypeOf([]*ethpb.Validator{}).Name(), reflect.TypeOf(elements).Name())
|
||||
return nil, errors.Errorf("Wanted type of %T but got %T", []*ethpb.Validator{}, elements)
|
||||
}
|
||||
return handleValidatorSlice(val, indices, convertAll)
|
||||
}
|
||||
@@ -139,8 +137,7 @@ func convertValidators(indices []uint64, elements interface{}, convertAll bool)
|
||||
func convertAttestations(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
|
||||
val, ok := elements.([]*ethpb.PendingAttestation)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("Wanted type of %v but got %v",
|
||||
reflect.TypeOf([]*ethpb.PendingAttestation{}).Name(), reflect.TypeOf(elements).Name())
|
||||
return nil, errors.Errorf("Wanted type of %T but got %T", []*ethpb.PendingAttestation{}, elements)
|
||||
}
|
||||
return handlePendingAttestationSlice(val, indices, convertAll)
|
||||
}
|
||||
@@ -148,8 +145,7 @@ func convertAttestations(indices []uint64, elements interface{}, convertAll bool
|
||||
func convertBalances(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
|
||||
val, ok := elements.([]uint64)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("Wanted type of %v but got %v",
|
||||
reflect.TypeOf([]uint64{}).Name(), reflect.TypeOf(elements).Name())
|
||||
return nil, errors.Errorf("Wanted type of %T but got %T", []uint64{}, elements)
|
||||
}
|
||||
return handleBalanceSlice(val, indices, convertAll)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package fieldtrie
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
@@ -270,7 +269,7 @@ func TestFieldTrie_NativeState_fieldConvertersNative(t *testing.T) {
|
||||
convertAll: true,
|
||||
},
|
||||
wantHex: nil,
|
||||
errMsg: fmt.Sprintf("Wanted type of %v", reflect.TypeOf([]*ethpb.Eth1Data{}).Name()),
|
||||
errMsg: fmt.Sprintf("Wanted type of %T", []*ethpb.Eth1Data{}),
|
||||
},
|
||||
{
|
||||
name: "Balance",
|
||||
@@ -305,7 +304,7 @@ func TestFieldTrie_NativeState_fieldConvertersNative(t *testing.T) {
|
||||
convertAll: true,
|
||||
},
|
||||
wantHex: nil,
|
||||
errMsg: fmt.Sprintf("Wanted type of %v", reflect.TypeOf([]*ethpb.Validator{}).Name()),
|
||||
errMsg: fmt.Sprintf("Wanted type of %T", []*ethpb.Validator{}),
|
||||
},
|
||||
{
|
||||
name: "Attestations",
|
||||
|
||||
@@ -96,6 +96,8 @@ func New(beaconDB db.NoHeadAccessDatabase, fc forkchoice.ForkChoicer, opts ...St
|
||||
for _, o := range opts {
|
||||
o(s)
|
||||
}
|
||||
fc.Lock()
|
||||
defer fc.Unlock()
|
||||
fc.SetBalancesByRooter(s.ActiveNonSlashedBalancesByRoot)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -204,7 +204,6 @@ go_test(
|
||||
"//beacon-chain/sync/initial-sync/testing:go_default_library",
|
||||
"//cache/lru:go_default_library",
|
||||
"//cmd/beacon-chain/flags:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"github.com/prysmaticlabs/prysm/v3/async"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
@@ -115,8 +116,8 @@ func (s *Service) processAttestations(ctx context.Context, attestations []*ethpb
|
||||
// This is an important validation before retrieving attestation pre state to defend against
|
||||
// attestation's target intentionally reference checkpoint that's long ago.
|
||||
// Verify current finalized checkpoint is an ancestor of the block defined by the attestation's beacon block root.
|
||||
if err := s.cfg.chain.VerifyFinalizedConsistency(att.Aggregate.Data.BeaconBlockRoot); err != nil {
|
||||
log.WithError(err).Debug("Could not verify finalized consistency")
|
||||
if !s.cfg.chain.InForkchoice(bytesutil.ToBytes32(att.Aggregate.Data.BeaconBlockRoot)) {
|
||||
log.WithError(blockchain.ErrNotDescendantOfFinalized).Debug("Could not verify finalized consistency")
|
||||
continue
|
||||
}
|
||||
if err := s.cfg.chain.VerifyLmdFfgConsistency(ctx, att.Aggregate); err != nil {
|
||||
|
||||
@@ -116,7 +116,8 @@ func TestProcessPendingAtts_HasBlockSaveUnAggregatedAtt(t *testing.T) {
|
||||
FinalizedCheckPoint: ðpb.Checkpoint{
|
||||
Root: aggregateAndProof.Aggregate.Data.BeaconBlockRoot,
|
||||
Epoch: 0,
|
||||
}},
|
||||
},
|
||||
},
|
||||
attPool: attestations.NewPool(),
|
||||
},
|
||||
blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof),
|
||||
@@ -150,8 +151,10 @@ func TestProcessPendingAtts_NoBroadcastWithBadSignature(t *testing.T) {
|
||||
cfg: &config{
|
||||
p2p: p1,
|
||||
beaconDB: db,
|
||||
chain: &mock.ChainService{State: s, Genesis: prysmTime.Now(), FinalizedCheckPoint: ðpb.Checkpoint{Root: make([]byte, 32)}},
|
||||
attPool: attestations.NewPool(),
|
||||
chain: &mock.ChainService{
|
||||
State: s,
|
||||
Genesis: prysmTime.Now(), FinalizedCheckPoint: ðpb.Checkpoint{Root: make([]byte, 32)}},
|
||||
attPool: attestations.NewPool(),
|
||||
},
|
||||
blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof),
|
||||
}
|
||||
|
||||
@@ -185,6 +185,7 @@ func TestRegularSyncBeaconBlockSubscriber_ExecutionEngineTimesOut(t *testing.T)
|
||||
db := dbtest.SetupDB(t)
|
||||
|
||||
p1 := p2ptest.NewTestP2P(t)
|
||||
fcs := doublylinkedtree.New()
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
p2p: p1,
|
||||
@@ -195,7 +196,7 @@ func TestRegularSyncBeaconBlockSubscriber_ExecutionEngineTimesOut(t *testing.T)
|
||||
},
|
||||
ReceiveBlockMockErr: execution.ErrHTTPTimeout,
|
||||
},
|
||||
stateGen: stategen.New(db, doublylinkedtree.New()),
|
||||
stateGen: stategen.New(db, fcs),
|
||||
},
|
||||
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
@@ -701,7 +702,9 @@ func TestService_ProcessPendingBlockOnCorrectSlot(t *testing.T) {
|
||||
db := dbtest.SetupDB(t)
|
||||
|
||||
p1 := p2ptest.NewTestP2P(t)
|
||||
mockChain := mock.ChainService{Genesis: time.Unix(time.Now().Unix()-int64(params.BeaconConfig().SecondsPerSlot), 0),
|
||||
fcs := doublylinkedtree.New()
|
||||
mockChain := mock.ChainService{
|
||||
Genesis: time.Unix(time.Now().Unix()-int64(params.BeaconConfig().SecondsPerSlot), 0),
|
||||
FinalizedCheckPoint: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
}}
|
||||
@@ -710,7 +713,7 @@ func TestService_ProcessPendingBlockOnCorrectSlot(t *testing.T) {
|
||||
p2p: p1,
|
||||
beaconDB: db,
|
||||
chain: &mockChain,
|
||||
stateGen: stategen.New(db, doublylinkedtree.New()),
|
||||
stateGen: stategen.New(db, fcs),
|
||||
},
|
||||
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
p2ptest "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/testing"
|
||||
p2ptypes "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/types"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/beacon-chain/flags"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
@@ -164,7 +163,6 @@ func TestRPCBeaconBlocksByRange_ReturnCorrectNumberBack(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRPCBeaconBlocksByRange_ReconstructsPayloads(t *testing.T) {
|
||||
features.Init(&features.Flags{EnableOnlyBlindedBeaconBlocks: true})
|
||||
p1 := p2ptest.NewTestP2P(t)
|
||||
p2 := p2ptest.NewTestP2P(t)
|
||||
p1.Connect(p2)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func (s *Service) voluntaryExitSubscriber(ctx context.Context, msg proto.Message) error {
|
||||
func (s *Service) voluntaryExitSubscriber(_ context.Context, msg proto.Message) error {
|
||||
ve, ok := msg.(*ethpb.SignedVoluntaryExit)
|
||||
if !ok {
|
||||
return fmt.Errorf("wrong type, expected: *ethpb.SignedVoluntaryExit got: %T", msg)
|
||||
@@ -20,11 +20,7 @@ func (s *Service) voluntaryExitSubscriber(ctx context.Context, msg proto.Message
|
||||
}
|
||||
s.setExitIndexSeen(ve.Exit.ValidatorIndex)
|
||||
|
||||
headState, err := s.cfg.chain.HeadState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.cfg.exitPool.InsertVoluntaryExit(ctx, headState, ve)
|
||||
s.cfg.exitPool.InsertVoluntaryExit(ve)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/feed"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/feed/operation"
|
||||
@@ -140,9 +141,9 @@ func (s *Service) validateAggregatedAtt(ctx context.Context, signed *ethpb.Signe
|
||||
}
|
||||
|
||||
// Verify current finalized checkpoint is an ancestor of the block defined by the attestation's beacon block root.
|
||||
if err := s.cfg.chain.VerifyFinalizedConsistency(signed.Message.Aggregate.Data.BeaconBlockRoot); err != nil {
|
||||
tracing.AnnotateError(span, err)
|
||||
return pubsub.ValidationIgnore, err
|
||||
if !s.cfg.chain.InForkchoice(bytesutil.ToBytes32(signed.Message.Aggregate.Data.BeaconBlockRoot)) {
|
||||
tracing.AnnotateError(span, blockchain.ErrNotDescendantOfFinalized)
|
||||
return pubsub.ValidationIgnore, blockchain.ErrNotDescendantOfFinalized
|
||||
}
|
||||
|
||||
bs, err := s.cfg.chain.AttestationTargetState(ctx, signed.Message.Aggregate.Data.Target)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/feed"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/feed/operation"
|
||||
@@ -136,9 +137,9 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, p
|
||||
return pubsub.ValidationIgnore, nil
|
||||
}
|
||||
|
||||
if err := s.cfg.chain.VerifyFinalizedConsistency(att.Data.BeaconBlockRoot); err != nil {
|
||||
tracing.AnnotateError(span, err)
|
||||
return pubsub.ValidationIgnore, err
|
||||
if !s.cfg.chain.InForkchoice(bytesutil.ToBytes32(att.Data.BeaconBlockRoot)) {
|
||||
tracing.AnnotateError(span, blockchain.ErrNotDescendantOfFinalized)
|
||||
return pubsub.ValidationIgnore, blockchain.ErrNotDescendantOfFinalized
|
||||
}
|
||||
if err := s.cfg.chain.VerifyLmdFfgConsistency(ctx, att); err != nil {
|
||||
tracing.AnnotateError(span, err)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/feed"
|
||||
blockfeed "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/feed/block"
|
||||
@@ -214,9 +215,9 @@ func (s *Service) validateBeaconBlock(ctx context.Context, blk interfaces.ReadOn
|
||||
ctx, span := trace.StartSpan(ctx, "sync.validateBeaconBlock")
|
||||
defer span.End()
|
||||
|
||||
if err := s.cfg.chain.VerifyFinalizedBlkDescendant(ctx, blk.Block().ParentRoot()); err != nil {
|
||||
if !s.cfg.chain.InForkchoice(blk.Block().ParentRoot()) {
|
||||
s.setBadBlock(ctx, blockRoot)
|
||||
return err
|
||||
return blockchain.ErrNotDescendantOfFinalized
|
||||
}
|
||||
|
||||
parentState, err := s.cfg.stateGen.StateByRoot(ctx, blk.Block().ParentRoot())
|
||||
|
||||
@@ -831,8 +831,10 @@ func TestValidateBeaconBlockPubSub_ParentNotFinalizedDescendant(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
stateGen := stategen.New(db, doublylinkedtree.New())
|
||||
chainService := &mock.ChainService{Genesis: time.Unix(time.Now().Unix()-int64(params.BeaconConfig().SecondsPerSlot), 0),
|
||||
State: beaconState,
|
||||
chainService := &mock.ChainService{
|
||||
Genesis: time.Unix(time.Now().Unix()-int64(params.BeaconConfig().SecondsPerSlot), 0),
|
||||
NotFinalized: true,
|
||||
State: beaconState,
|
||||
FinalizedCheckPoint: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
Root: make([]byte, 32),
|
||||
@@ -869,7 +871,7 @@ func TestValidateBeaconBlockPubSub_ParentNotFinalizedDescendant(t *testing.T) {
|
||||
}
|
||||
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
|
||||
assert.Equal(t, pubsub.ValidationReject, res, "Wrong validation result returned")
|
||||
require.ErrorContains(t, "not part of finalized chain", err)
|
||||
require.ErrorContains(t, "not descendant of finalized checkpoint", err)
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_InvalidParentBlock(t *testing.T) {
|
||||
|
||||
33
build/bazelrc/convenience.bazelrc
Normal file
33
build/bazelrc/convenience.bazelrc
Normal file
@@ -0,0 +1,33 @@
|
||||
# Attempt to build & test every target whose prerequisites were successfully built.
|
||||
# Docs: https://bazel.build/docs/user-manual#keep-going
|
||||
build --keep_going
|
||||
test --keep_going
|
||||
|
||||
# Output test errors to stderr so users don't have to `cat` or open test failure log files when test
|
||||
# fail. This makes the log noiser in exchange for reducing the time-to-feedback on test failures for
|
||||
# users.
|
||||
# Docs: https://bazel.build/docs/user-manual#test-output
|
||||
test --test_output=errors
|
||||
|
||||
# Show the output files created by builds that requested more than one target. This helps users
|
||||
# locate the build outputs in more cases
|
||||
# Docs: https://bazel.build/docs/user-manual#show-result
|
||||
build --show_result=20
|
||||
|
||||
# Bazel picks up host-OS-specific config lines from bazelrc files. For example, if the host OS is
|
||||
# Linux and you run bazel build, Bazel picks up lines starting with build:linux. Supported OS
|
||||
# identifiers are `linux`, `macos`, `windows`, `freebsd`, and `openbsd`. Enabling this flag is
|
||||
# equivalent to using `--config=linux` on Linux, `--config=windows` on Windows, etc.
|
||||
# Docs: https://bazel.build/reference/command-line-reference#flag--enable_platform_specific_config
|
||||
common --enable_platform_specific_config
|
||||
|
||||
# Output a heap dump if an OOM is thrown during a Bazel invocation
|
||||
# (including OOMs due to `--experimental_oom_more_eagerly_threshold`).
|
||||
# The dump will be written to `<output_base>/<invocation_id>.heapdump.hprof`.
|
||||
# You may need to configure CI to capture this artifact and upload for later use.
|
||||
# Docs: https://bazel.build/reference/command-line-reference#flag--heap_dump_on_oom
|
||||
build --heap_dump_on_oom
|
||||
|
||||
# Change the behavior of bazel test to not bother building targets which aren't dependencies of the
|
||||
# tests. Matches some developer expectations.
|
||||
test --build_tests_only
|
||||
63
build/bazelrc/correctness.bazelrc
Normal file
63
build/bazelrc/correctness.bazelrc
Normal file
@@ -0,0 +1,63 @@
|
||||
# Do not upload locally executed action results to the remote cache.
|
||||
# This should be the default for local builds so local builds cannot poison the remote cache.
|
||||
# It should be flipped to `--remote_upload_local_results` on CI
|
||||
# by using `--bazelrc=.aspect/bazelrc/ci.bazelrc`.
|
||||
# Docs: https://bazel.build/reference/command-line-reference#flag--remote_upload_local_results
|
||||
build --noremote_upload_local_results
|
||||
|
||||
# Don't allow network access for build actions in the sandbox.
|
||||
# Ensures that you don't accidentally make non-hermetic actions/tests which depend on remote
|
||||
# services.
|
||||
# Developers should tag targets with `tags=["requires-network"]` to opt-out of the enforcement.
|
||||
# Docs: https://bazel.build/reference/command-line-reference#flag--sandbox_default_allow_network
|
||||
build --sandbox_default_allow_network=false
|
||||
test --sandbox_default_allow_network=false
|
||||
|
||||
# Warn if a test's timeout is significantly longer than the test's actual execution time.
|
||||
# Bazel's default for test_timeout is medium (5 min), but most tests should instead be short (1 min).
|
||||
# While a test's timeout should be set such that it is not flaky, a test that has a highly
|
||||
# over-generous timeout can hide real problems that crop up unexpectedly.
|
||||
# For instance, a test that normally executes in a minute or two should not have a timeout of
|
||||
# ETERNAL or LONG as these are much, much too generous.
|
||||
# Docs: https://bazel.build/docs/user-manual#test-verbose-timeout-warnings
|
||||
test --test_verbose_timeout_warnings
|
||||
|
||||
# Allow the Bazel server to check directory sources for changes. Ensures that the Bazel server
|
||||
# notices when a directory changes, if you have a directory listed in the srcs of some target.
|
||||
# Recommended when using
|
||||
# [copy_directory](https://github.com/aspect-build/bazel-lib/blob/main/docs/copy_directory.md) and
|
||||
# [rules_js](https://github.com/aspect-build/rules_js) since npm package are source directories
|
||||
# inputs to copy_directory actions.
|
||||
# Docs: https://bazel.build/reference/command-line-reference#flag--host_jvm_args
|
||||
startup --host_jvm_args=-DBAZEL_TRACK_SOURCE_DIRECTORIES=1
|
||||
|
||||
# Allow exclusive tests to run in the sandbox. Fixes a bug where Bazel doesn't enable sandboxing for
|
||||
# tests with `tags=["exclusive"]`.
|
||||
# Docs: https://bazel.build/reference/command-line-reference#flag--incompatible_exclusive_test_sandboxed
|
||||
test --incompatible_exclusive_test_sandboxed
|
||||
|
||||
# Use a static value for `PATH` and does not inherit `LD_LIBRARY_PATH`. Doesn't let environment
|
||||
# variables like `PATH` sneak into the build, which can cause massive cache misses when they change.
|
||||
# Use `--action_env=ENV_VARIABLE` if you want to inherit specific environment variables from the
|
||||
# client, but note that doing so can prevent cross-user caching if a shared cache is used.
|
||||
# Docs: https://bazel.build/reference/command-line-reference#flag--incompatible_strict_action_env
|
||||
build --incompatible_strict_action_env
|
||||
|
||||
# Propagate tags from a target declaration to the actions' execution requirements.
|
||||
# Ensures that tags applied in your BUILD file, like `tags=["no-remote"]`
|
||||
# get propagated to actions created by the rule.
|
||||
# Without this option, you rely on rules authors to manually check the tags you passed
|
||||
# and apply relevant ones to the actions they create.
|
||||
# See https://github.com/bazelbuild/bazel/issues/8830 for details.
|
||||
# Docs: https://bazel.build/reference/command-line-reference#flag--experimental_allow_tags_propagation
|
||||
build --experimental_allow_tags_propagation
|
||||
fetch --experimental_allow_tags_propagation
|
||||
query --experimental_allow_tags_propagation
|
||||
|
||||
# Do not automatically create `__init__.py` files in the runfiles of Python targets. Fixes the wrong
|
||||
# default that comes from Google's internal monorepo by using `__init__.py` to delimit a Python
|
||||
# package. Precisely, when a `py_binary` or `py_test` target has `legacy_create_init` set to `auto (the
|
||||
# default), it is treated as false if and only if this flag is set. See
|
||||
# https://github.com/bazelbuild/bazel/issues/10076.
|
||||
# Docs: https://bazel.build/reference/command-line-reference#flag--incompatible_default_to_explicit_init_py
|
||||
build --incompatible_default_to_explicit_init_py
|
||||
74
build/bazelrc/cross.bazelrc
Normal file
74
build/bazelrc/cross.bazelrc
Normal file
@@ -0,0 +1,74 @@
|
||||
# multi-arch cross-compiling toolchain configs
|
||||
-----------------------------------------------
|
||||
|
||||
build:cross --crosstool_top=@prysm_toolchains//:multiarch_toolchain
|
||||
build:cross --host_platform=@io_bazel_rules_go//go/toolchain:linux_amd64
|
||||
build:cross --host_crosstool_top=@prysm_toolchains//:hostonly_toolchain
|
||||
|
||||
# linux_amd64 config for cross compiler toolchain, not strictly necessary since host/exec env is amd64
|
||||
build:linux_amd64 --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64_cgo
|
||||
|
||||
# osx_amd64 config for cross compiler toolchain
|
||||
build:osx_amd64 --config=cross
|
||||
build:osx_amd64 --platforms=@io_bazel_rules_go//go/toolchain:darwin_amd64_cgo
|
||||
build:osx_amd64 --compiler=osxcross
|
||||
|
||||
# osx_arm64 config for cross compiler toolchain
|
||||
build:osx_arm64 --config=cross
|
||||
build:osx_arm64 --platforms=@io_bazel_rules_go//go/toolchain:darwin_arm64_cgo
|
||||
build:osx_arm64 --compiler=osxcross
|
||||
build:osx_arm64 --cpu=aarch64
|
||||
|
||||
# windows
|
||||
build:windows_amd64 --config=cross
|
||||
build:windows_amd64 --platforms=@io_bazel_rules_go//go/toolchain:windows_amd64_cgo
|
||||
build:windows_amd64 --compiler=mingw-w64
|
||||
|
||||
# linux_arm64 conifg for cross compiler toolchain
|
||||
build:linux_arm64 --config=cross
|
||||
build:linux_arm64 --platforms=@io_bazel_rules_go//go/toolchain:linux_arm64_cgo
|
||||
build:linux_arm64 --copt=-funsafe-math-optimizations
|
||||
build:linux_arm64 --copt=-ftree-vectorize
|
||||
build:linux_arm64 --copt=-fomit-frame-pointer
|
||||
build:linux_arm64 --cpu=aarch64
|
||||
build:linux_arm64 --compiler=clang
|
||||
build:linux_arm64 --copt=-march=armv8-a
|
||||
|
||||
|
||||
# Docker Sandbox Configs
|
||||
#-----------------------
|
||||
# Note all docker sandbox configs must run from a linux x86_64 host
|
||||
# build:docker-sandbox --experimental_docker_image=gcr.io/prysmaticlabs/rbe-worker:latest
|
||||
build:docker-sandbox --spawn_strategy=docker --strategy=Javac=docker --genrule_strategy=docker
|
||||
build:docker-sandbox --define=EXECUTOR=remote
|
||||
build:docker-sandbox --experimental_docker_verbose
|
||||
build:docker-sandbox --experimental_enable_docker_sandbox
|
||||
build:docker-sandbox --crosstool_top=@rbe_ubuntu_clang//cc:toolchain
|
||||
build:docker-sandbox --host_javabase=@rbe_ubuntu_clang//java:jdk
|
||||
build:docker-sandbox --javabase=@rbe_ubuntu_clang//java:jdk
|
||||
build:docker-sandbox --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
|
||||
build:docker-sandbox --java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
|
||||
build:docker-sandbox --extra_execution_platforms=@rbe_ubuntu_clang//config:platform
|
||||
build:docker-sandbox --host_platform=@rbe_ubuntu_clang//config:platform
|
||||
build:docker-sandbox --platforms=@rbe_ubuntu_clang//config:platform
|
||||
build:docker-sandbox --extra_toolchains=@prysm_toolchains//:cc-toolchain-multiarch
|
||||
|
||||
# windows_amd64 docker sandbox build config
|
||||
build:windows_amd64_docker --config=docker-sandbox --config=windows_amd64
|
||||
build:windows_amd64_docker_debug --config=windows_amd64_docker --config=debug
|
||||
|
||||
# osx_amd64 docker sandbox build config
|
||||
build:osx_amd64_docker --config=docker-sandbox --config=osx_amd64
|
||||
build:osx_amd64_docker_debug --config=osx_amd64_docker --config=debug
|
||||
|
||||
# osx_arm64 docker sandbox build config
|
||||
build:osx_arm64_docker --config=docker-sandbox --config=osx_arm64
|
||||
build:osx_arm64_docker_debug --config=osx_arm64_docker --config=debug
|
||||
|
||||
# linux_arm64 docker sandbox build config
|
||||
build:linux_arm64_docker --config=docker-sandbox --config=linux_arm64
|
||||
build:linux_arm64_docker_debug --config=linux_arm64_docker --config=debug
|
||||
|
||||
# linux_amd64 docker sandbox build config
|
||||
build:linux_amd64_docker --config=docker-sandbox --config=linux_amd64
|
||||
build:linux_amd64_docker_debug --config=linux_amd64_docker --config=debug
|
||||
19
build/bazelrc/debug.bazelrc
Normal file
19
build/bazelrc/debug.bazelrc
Normal file
@@ -0,0 +1,19 @@
|
||||
############################################################
|
||||
# Use `bazel test --config=debug` to enable these settings #
|
||||
############################################################
|
||||
|
||||
# Stream stdout/stderr output from each test in real-time.
|
||||
# Docs: https://bazel.build/docs/user-manual#test-output
|
||||
test:debug --test_output=streamed
|
||||
|
||||
# Run one test at a time.
|
||||
# Docs: https://bazel.build/reference/command-line-reference#flag--test_strategy
|
||||
test:debug --test_strategy=exclusive
|
||||
|
||||
# Prevent long running tests from timing out.
|
||||
# Docs: https://bazel.build/docs/user-manual#test-timeout
|
||||
test:debug --test_timeout=9999
|
||||
|
||||
# Always run tests even if they have cached results.
|
||||
# Docs: https://bazel.build/docs/user-manual#cache-test-results
|
||||
test:debug --nocache_test_results
|
||||
44
build/bazelrc/performance.bazelrc
Normal file
44
build/bazelrc/performance.bazelrc
Normal file
@@ -0,0 +1,44 @@
|
||||
# Merkle tree calculations will be memoized to improve the remote cache hit checking speed. The
|
||||
# memory foot print of the cache is controlled by `--experimental_remote_merkle_tree_cache_size`.
|
||||
# Docs: https://bazel.build/reference/command-line-reference#flag--experimental_remote_merkle_tree_cache
|
||||
build --experimental_remote_merkle_tree_cache
|
||||
query --experimental_remote_merkle_tree_cache
|
||||
|
||||
# The number of Merkle trees to memoize to improve the remote cache hit checking speed. Even though
|
||||
# the cache is automatically pruned according to Java's handling of soft references, out-of-memory
|
||||
# errors can occur if set too high. If set to 0 the cache size is unlimited. Optimal value varies
|
||||
# depending on project's size.
|
||||
# Docs: https://bazel.build/reference/command-line-reference#flag--experimental_remote_merkle_tree_cache_size
|
||||
build --experimental_remote_merkle_tree_cache_size=1000
|
||||
query --experimental_remote_merkle_tree_cache_size=1000
|
||||
|
||||
# Speed up all builds by not checking if output files have been modified. Lets you make changes to
|
||||
# the output tree without triggering a build for local debugging. For example, you can modify
|
||||
# [rules_js](https://github.com/aspect-build/rules_js) 3rd party npm packages in the output tree
|
||||
# when local debugging.
|
||||
# Docs: https://github.com/bazelbuild/bazel/blob/1af61b21df99edc2fc66939cdf14449c2661f873/src/main/java/com/google/devtools/build/lib/pkgcache/PackageOptions.java#L185
|
||||
build --noexperimental_check_output_files
|
||||
fetch --noexperimental_check_output_files
|
||||
query --noexperimental_check_output_files
|
||||
|
||||
# Don't apply `--noremote_upload_local_results` and `--noremote_accept_cached` to the disk cache.
|
||||
# If you have both `--noremote_upload_local_results` and `--disk_cache`, then this fixes a bug where
|
||||
# Bazel doesn't write to the local disk cache as it treats as a remote cache.
|
||||
# Docs: https://bazel.build/reference/command-line-reference#flag--incompatible_remote_results_ignore_disk
|
||||
build --incompatible_remote_results_ignore_disk
|
||||
|
||||
# Directories used by sandboxed non-worker execution may be reused to avoid unnecessary setup costs.
|
||||
# Save time on Sandbox creation and deletion when many of the same kind of action run during the
|
||||
# build.
|
||||
# No longer experimental in Bazel 6: https://github.com/bazelbuild/bazel/commit/c1a95501a5611878e5cc43a3cc531f2b9e47835b
|
||||
# Docs: https://bazel.build/reference/command-line-reference#flag--reuse_sandbox_directories
|
||||
build --experimental_reuse_sandbox_directories
|
||||
|
||||
# Some actions are always IO-intensive but require little compute. It's wasteful to put the output
|
||||
# in the remote cache, it just saturates the network and fills the cache storage causing earlier
|
||||
# evictions. It's also not worth sending them for remote execution.
|
||||
# For actions like PackageTar and GoStdLib actions it's usually faster to just re-run the work locally every time.
|
||||
# You'll have to look at an execution log to figure out what other action mnemonics you care about.
|
||||
# In some cases you may need to patch rulesets to add a mnemonic to actions that don't have one.
|
||||
# https://bazel.build/reference/command-line-reference#flag--modify_execution_info
|
||||
build --modify_execution_info='PackageTar=+no-remote,GoStdlib.*=+no-remote'
|
||||
@@ -58,7 +58,6 @@ go_image(
|
||||
container_image(
|
||||
name = "image_with_creation_time",
|
||||
base = "image",
|
||||
stamp = True,
|
||||
tags = ["manual"],
|
||||
visibility = ["//beacon-chain:__pkg__"],
|
||||
)
|
||||
|
||||
@@ -5,13 +5,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// InteropGenesisStateFlag defines a flag for the beacon node to load genesis state via file.
|
||||
InteropGenesisStateFlag = &cli.StringFlag{
|
||||
Name: "interop-genesis-state",
|
||||
Usage: "The genesis state file (.SSZ) to load from. Note: loading from an interop genesis " +
|
||||
"state does not use a web3 connection to read any deposits. This interop " +
|
||||
"functionality should not be used with public testnets.",
|
||||
}
|
||||
// InteropMockEth1DataVotesFlag enables mocking the eth1 proof-of-work chain data put into blocks by proposers.
|
||||
InteropMockEth1DataVotesFlag = &cli.BoolFlag{
|
||||
Name: "interop-eth1data-votes",
|
||||
|
||||
@@ -56,7 +56,6 @@ var appFlags = []cli.Flag{
|
||||
flags.BlockBatchLimit,
|
||||
flags.BlockBatchLimitBurstFactor,
|
||||
flags.InteropMockEth1DataVotesFlag,
|
||||
flags.InteropGenesisStateFlag,
|
||||
flags.InteropNumValidatorsFlag,
|
||||
flags.InteropGenesisTimeFlag,
|
||||
flags.SlotsPerArchivedPoint,
|
||||
|
||||
@@ -94,7 +94,6 @@ var appHelpFlagGroups = []flagGroup{
|
||||
Name: "beacon-chain",
|
||||
Flags: []cli.Flag{
|
||||
flags.InteropMockEth1DataVotesFlag,
|
||||
flags.InteropGenesisStateFlag,
|
||||
flags.DepositContractFlag,
|
||||
flags.ContractDeploymentBlock,
|
||||
flags.RPCHost,
|
||||
@@ -173,7 +172,7 @@ var appHelpFlagGroups = []flagGroup{
|
||||
{
|
||||
Name: "interop",
|
||||
Flags: []cli.Flag{
|
||||
flags.InteropGenesisStateFlag,
|
||||
genesis.StatePath,
|
||||
flags.InteropGenesisTimeFlag,
|
||||
flags.InteropNumValidatorsFlag,
|
||||
},
|
||||
|
||||
11
cmd/flags.go
11
cmd/flags.go
@@ -3,6 +3,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
@@ -215,9 +216,13 @@ var (
|
||||
}
|
||||
// GrpcMaxCallRecvMsgSizeFlag defines the max call message size for GRPC
|
||||
GrpcMaxCallRecvMsgSizeFlag = &cli.IntFlag{
|
||||
Name: "grpc-max-msg-size",
|
||||
Usage: "Integer to define max recieve message call size (default: 4194304 (for 4MB))",
|
||||
Value: 1 << 22,
|
||||
Name: "grpc-max-msg-size",
|
||||
Usage: "Integer to define max recieve message call size. If serving a public gRPC server, " +
|
||||
"set this to a more reasonable size to avoid resource exhaustion from large messages. " +
|
||||
"Validators with as many as 10000 keys can be run with a max message size of less than " +
|
||||
"50Mb. The default here is set to a very high value for local users. " +
|
||||
"(default: 2147483647 (2Gi)).",
|
||||
Value: math.MaxInt32,
|
||||
}
|
||||
// AcceptTosFlag specifies user acceptance of ToS for non-interactive environments.
|
||||
AcceptTosFlag = &cli.BoolFlag{
|
||||
|
||||
@@ -38,7 +38,6 @@ go_image(
|
||||
container_image(
|
||||
name = "image_with_creation_time",
|
||||
base = "image",
|
||||
stamp = True,
|
||||
tags = ["manual"],
|
||||
visibility = ["//cmd/prysmctl:__pkg__"],
|
||||
)
|
||||
|
||||
@@ -80,7 +80,7 @@ var (
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "config-name",
|
||||
Usage: "Config kind to be used for generating the genesis state. Default: mainnet. Options include mainnet, interop, minimal, prater, ropsten, sepolia. --chain-config-file will override this flag.",
|
||||
Usage: "Config kind to be used for generating the genesis state. Default: mainnet. Options include mainnet, interop, minimal, prater, sepolia. --chain-config-file will override this flag.",
|
||||
Destination: &generateGenesisStateFlags.ConfigName,
|
||||
Value: params.MainnetName,
|
||||
},
|
||||
|
||||
@@ -112,7 +112,6 @@ var Commands = []*cli.Command{
|
||||
flags.ForceExitFlag,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.RopstenTestnet,
|
||||
features.SepoliaTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
|
||||
@@ -53,7 +53,6 @@ go_image(
|
||||
container_image(
|
||||
name = "image_with_creation_time",
|
||||
base = "image",
|
||||
stamp = True,
|
||||
tags = ["manual"],
|
||||
visibility = ["//validator:__pkg__"],
|
||||
)
|
||||
|
||||
@@ -28,7 +28,6 @@ var Commands = &cli.Command{
|
||||
flags.DeletePublicKeysFlag,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.RopstenTestnet,
|
||||
features.SepoliaTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
@@ -65,7 +64,6 @@ var Commands = &cli.Command{
|
||||
flags.GrpcRetryDelayFlag,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.RopstenTestnet,
|
||||
features.SepoliaTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
@@ -99,7 +97,6 @@ var Commands = &cli.Command{
|
||||
flags.BackupPasswordFile,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.RopstenTestnet,
|
||||
features.SepoliaTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
@@ -130,7 +127,6 @@ var Commands = &cli.Command{
|
||||
flags.ImportPrivateKeyFileFlag,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.RopstenTestnet,
|
||||
features.SepoliaTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
@@ -172,7 +168,6 @@ var Commands = &cli.Command{
|
||||
flags.ForceExitFlag,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.RopstenTestnet,
|
||||
features.SepoliaTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
|
||||
@@ -23,7 +23,6 @@ var Commands = &cli.Command{
|
||||
flags.SlashingProtectionExportDirFlag,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.RopstenTestnet,
|
||||
features.SepoliaTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
@@ -51,7 +50,6 @@ var Commands = &cli.Command{
|
||||
flags.SlashingProtectionJSONFileFlag,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.RopstenTestnet,
|
||||
features.SepoliaTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
|
||||
@@ -34,7 +34,6 @@ var Commands = &cli.Command{
|
||||
flags.SkipMnemonic25thWordCheckFlag,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.RopstenTestnet,
|
||||
features.SepoliaTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
@@ -67,7 +66,6 @@ var Commands = &cli.Command{
|
||||
flags.RemoteSignerCACertPathFlag,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.RopstenTestnet,
|
||||
features.SepoliaTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
@@ -99,7 +97,6 @@ var Commands = &cli.Command{
|
||||
flags.SkipMnemonic25thWordCheckFlag,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.RopstenTestnet,
|
||||
features.SepoliaTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
|
||||
@@ -67,7 +67,7 @@ type Flags struct {
|
||||
EnableVectorizedHTR bool // EnableVectorizedHTR specifies whether the beacon state will use the optimized sha256 routines.
|
||||
DisableForkchoiceDoublyLinkedTree bool // DisableForkChoiceDoublyLinkedTree specifies whether fork choice store will use a doubly linked tree.
|
||||
EnableBatchGossipAggregation bool // EnableBatchGossipAggregation specifies whether to further aggregate our gossip batches before verifying them.
|
||||
EnableOnlyBlindedBeaconBlocks bool // EnableOnlyBlindedBeaconBlocks enables only storing blinded beacon blocks in the DB post-Bellatrix fork.
|
||||
SaveFullExecutionPayloads bool // Save full beacon blocks with execution payloads in the database.
|
||||
EnableStartOptimistic bool // EnableStartOptimistic treats every block as optimistic at startup.
|
||||
|
||||
DisableStakinContractCheck bool // Disables check for deposit contract when proposing blocks
|
||||
@@ -125,13 +125,6 @@ func configureTestnet(ctx *cli.Context) error {
|
||||
}
|
||||
applyPraterFeatureFlags(ctx)
|
||||
params.UsePraterNetworkConfig()
|
||||
} else if ctx.Bool(RopstenTestnet.Name) {
|
||||
log.Warn("Running on the Ropsten Beacon Chain Testnet")
|
||||
if err := params.SetActive(params.RopstenConfig().Copy()); err != nil {
|
||||
return err
|
||||
}
|
||||
applyRopstenFeatureFlags(ctx)
|
||||
params.UseRopstenNetworkConfig()
|
||||
} else if ctx.Bool(SepoliaTestnet.Name) {
|
||||
log.Warn("Running on the Sepolia Beacon Chain Testnet")
|
||||
if err := params.SetActive(params.SepoliaConfig().Copy()); err != nil {
|
||||
@@ -154,13 +147,6 @@ func configureTestnet(ctx *cli.Context) error {
|
||||
|
||||
// Insert feature flags within the function to be enabled for Prater testnet.
|
||||
func applyPraterFeatureFlags(ctx *cli.Context) {
|
||||
if err := ctx.Set(EnableOnlyBlindedBeaconBlocks.Names()[0], "true"); err != nil {
|
||||
log.WithError(err).Debug("error enabling only saving blinded beacon blocks flag")
|
||||
}
|
||||
}
|
||||
|
||||
// Insert feature flags within the function to be enabled for Ropsten testnet.
|
||||
func applyRopstenFeatureFlags(ctx *cli.Context) {
|
||||
}
|
||||
|
||||
// Insert feature flags within the function to be enabled for Sepolia testnet.
|
||||
@@ -247,9 +233,9 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
|
||||
logDisabled(disableGossipBatchAggregation)
|
||||
cfg.EnableBatchGossipAggregation = false
|
||||
}
|
||||
if ctx.Bool(EnableOnlyBlindedBeaconBlocks.Name) {
|
||||
logEnabled(EnableOnlyBlindedBeaconBlocks)
|
||||
cfg.EnableOnlyBlindedBeaconBlocks = true
|
||||
if ctx.Bool(SaveFullExecutionPayloads.Name) {
|
||||
logEnabled(SaveFullExecutionPayloads)
|
||||
cfg.SaveFullExecutionPayloads = true
|
||||
}
|
||||
if ctx.Bool(enableStartupOptimistic.Name) {
|
||||
logEnabled(enableStartupOptimistic)
|
||||
|
||||
@@ -82,6 +82,21 @@ var (
|
||||
Usage: deprecatedUsage,
|
||||
Hidden: true,
|
||||
}
|
||||
deprecatedEnableOnlyBlindedBeaconBlocks = &cli.BoolFlag{
|
||||
Name: "enable-only-blinded-beacon-blocks",
|
||||
Usage: deprecatedUsage,
|
||||
Hidden: true,
|
||||
}
|
||||
deprecatedRopstenTestnet = &cli.StringFlag{
|
||||
Name: "ropsten",
|
||||
Usage: deprecatedUsage,
|
||||
Hidden: true,
|
||||
}
|
||||
deprecatedInteropGenesisState = &cli.StringFlag{
|
||||
Name: "interop-genesis-state",
|
||||
Usage: deprecatedUsage,
|
||||
Hidden: true,
|
||||
}
|
||||
)
|
||||
|
||||
// Deprecated flags for both the beacon node and validator client.
|
||||
@@ -100,10 +115,13 @@ var deprecatedFlags = []cli.Flag{
|
||||
deprecatedFallbackProvider,
|
||||
deprecatedEnableDefensivePull,
|
||||
deprecatedDisableNativeState,
|
||||
deprecatedRopstenTestnet,
|
||||
deprecatedInteropGenesisState,
|
||||
}
|
||||
|
||||
// deprecatedBeaconFlags contains flags that are still used by other components
|
||||
// and therefore cannot be added to deprecatedFlags
|
||||
var deprecatedBeaconFlags = []cli.Flag{
|
||||
deprecatedBackupWebHookFlag,
|
||||
deprecatedEnableOnlyBlindedBeaconBlocks,
|
||||
}
|
||||
|
||||
@@ -13,11 +13,6 @@ var (
|
||||
Usage: "Run Prysm configured for the Prater / Goerli test network",
|
||||
Aliases: []string{"goerli"},
|
||||
}
|
||||
// RopstenTestnet flag for the multiclient Ethereum consensus testnet.
|
||||
RopstenTestnet = &cli.BoolFlag{
|
||||
Name: "ropsten",
|
||||
Usage: "Run Prysm configured for the Ropsten beacon chain test network",
|
||||
}
|
||||
// SepoliaTestnet flag for the multiclient Ethereum consensus testnet.
|
||||
SepoliaTestnet = &cli.BoolFlag{
|
||||
Name: "sepolia",
|
||||
@@ -114,10 +109,6 @@ var (
|
||||
Name: "disable-gossip-batch-aggregation",
|
||||
Usage: "Disables new methods to further aggregate our gossip batches before verifying them.",
|
||||
}
|
||||
EnableOnlyBlindedBeaconBlocks = &cli.BoolFlag{
|
||||
Name: "enable-only-blinded-beacon-blocks",
|
||||
Usage: "Enables storing only blinded beacon blocks in the database without full execution layer transactions",
|
||||
}
|
||||
enableStartupOptimistic = &cli.BoolFlag{
|
||||
Name: "startup-optimistic",
|
||||
Usage: "Treats every block as optimistically synced at launch. Use with caution",
|
||||
@@ -128,6 +119,10 @@ var (
|
||||
Name: "enable-full-ssz-data-logging",
|
||||
Usage: "Enables displaying logs for full ssz data on rejected gossip messages",
|
||||
}
|
||||
SaveFullExecutionPayloads = &cli.BoolFlag{
|
||||
Name: "save-full-execution-payloads",
|
||||
Usage: "Saves beacon blocks with full execution payloads instead of execution payload headers in the database",
|
||||
}
|
||||
EnableBeaconRESTApi = &cli.BoolFlag{
|
||||
Name: "enable-beacon-rest-api",
|
||||
Usage: "Experimental enable of the beacon REST API when querying a beacon node",
|
||||
@@ -146,7 +141,6 @@ var ValidatorFlags = append(deprecatedFlags, []cli.Flag{
|
||||
writeWalletPasswordOnWebOnboarding,
|
||||
enableExternalSlasherProtectionFlag,
|
||||
PraterTestnet,
|
||||
RopstenTestnet,
|
||||
SepoliaTestnet,
|
||||
Mainnet,
|
||||
dynamicKeyReloadDebounceInterval,
|
||||
@@ -167,7 +161,6 @@ var BeaconChainFlags = append(deprecatedBeaconFlags, append(deprecatedFlags, []c
|
||||
writeSSZStateTransitionsFlag,
|
||||
disableGRPCConnectionLogging,
|
||||
PraterTestnet,
|
||||
RopstenTestnet,
|
||||
SepoliaTestnet,
|
||||
Mainnet,
|
||||
disablePeerScorer,
|
||||
@@ -179,7 +172,7 @@ var BeaconChainFlags = append(deprecatedBeaconFlags, append(deprecatedFlags, []c
|
||||
disableVecHTR,
|
||||
disableForkChoiceDoublyLinkedTree,
|
||||
disableGossipBatchAggregation,
|
||||
EnableOnlyBlindedBeaconBlocks,
|
||||
SaveFullExecutionPayloads,
|
||||
enableStartupOptimistic,
|
||||
disableDefensivePull,
|
||||
enableFullSSZDataLogging,
|
||||
@@ -195,6 +188,5 @@ var E2EBeaconChainFlags = []string{
|
||||
var NetworkFlags = []cli.Flag{
|
||||
Mainnet,
|
||||
PraterTestnet,
|
||||
RopstenTestnet,
|
||||
SepoliaTestnet,
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ go_library(
|
||||
"network_config.go",
|
||||
"testnet_e2e_config.go",
|
||||
"testnet_prater_config.go",
|
||||
"testnet_ropsten_config.go",
|
||||
"testnet_sepolia_config.go",
|
||||
"testutils.go",
|
||||
"testutils_develop.go", # keep
|
||||
|
||||
@@ -8,7 +8,6 @@ func init() {
|
||||
E2ETestConfig(),
|
||||
E2EMainnetTestConfig(),
|
||||
InteropConfig(),
|
||||
RopstenConfig(),
|
||||
}
|
||||
configs = newConfigset(defaults...)
|
||||
// ensure that main net is always present and active by default
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user