Compare commits

...

35 Commits

Author SHA1 Message Date
Potuz
66eacdb14f lock the state when reading from all validators 2023-10-04 14:22:10 -03:00
Radosław Kapka
7454041356 Use middleware to handle comma-separated query params (#12995) 2023-10-04 15:49:42 +02:00
Radosław Kapka
f37301c0c0 Remove remote slashing protection feature (#12989)
* Remove remote slashing protection feature

* test fix

* remove mock from tests

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-04 04:15:06 +00:00
Nishant Das
cf1bfb9d67 Make New Engine Methods The Default (#12997) 2023-10-04 01:44:31 +00:00
Potuz
9d8dd5c9ad Don't prune proposer ID cache in a loop (#12996)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-03 23:25:53 +00:00
Delweng
f812bdcf60 beacon-node/state: alloc 1more item for append case (#12832)
* beacon-chain/state/attenstation: alloc +1 items for append

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/state/eth1: alloc +1 items for append

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/state/misc: alloc +1 items for append

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/state/participation: alloc +1 items for append

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/state/validator: alloc +1 items for append

Signed-off-by: jsvisa <delweng@gmail.com>

* Add some benchmarks

* Evaluate append vs copy. Apply results

* fix copy issue

* revert copy changes from a5ba8d4352

---------

Signed-off-by: jsvisa <delweng@gmail.com>
Co-authored-by: Nishant Das <nishdas93@gmail.com>
Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
2023-10-03 20:41:55 +00:00
terencechain
58c0899676 Don't mark block as bad in validateBeaconBlock for pending queue (#12983)
* Don't mark block as bad in validateBeaconBlock for pending queue

* Fix tests
2023-10-03 16:36:23 +00:00
terencechain
4628c19f51 Refactor churn limit helpers (#12971) 2023-10-03 14:14:41 +00:00
Radosław Kapka
58f23d2302 Handle hex in Blocker, Stater and optimistic mode checker (#12979)
* Handle hex in `Blocker`

* hex in stater

* ineff fix

* optimistic mode

---------

Co-authored-by: Nishant Das <nishdas93@gmail.com>
2023-10-03 05:52:29 +00:00
Potuz
9e33723b26 Revert "Do not cache proposer ID on GetProposerDuties (#12939)" (#12986)
This reverts commit e2591f7c5b.

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-03 04:04:17 +00:00
Potuz
2c32a87d17 Tweak LastRoot to return head root (#12985) 2023-10-03 02:22:52 +00:00
Potuz
56f3dafb54 Lock forkchoice on late block tasks (#12978)
Co-authored-by: Nishant Das <nishdas93@gmail.com>
2023-10-02 17:07:03 +00:00
Nicolás Pernas Maradei
70380660b3 [1/5] Light client sync protocol (#12853)
* generate SyncCommittee SSZ

* refactor error handling

* rewards: use http2.HandleError

* add light client protobuf files

* add light client helpers
2023-10-02 15:34:34 +00:00
Radosław Kapka
ecd55e5462 Remove go-playground/validator from Beacon API (#12973)
* Remove go-playground/validator from Beacon API

* gzl

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-02 13:21:59 +00:00
Nishant Das
46b2442127 Do Not Calculate Churn With 0 Exits (#12976) 2023-10-02 01:46:58 +00:00
Radosław Kapka
d8b2d9060f Return RpcError from core service's SubmitSyncMessage (#12974)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-09-30 14:19:09 +00:00
Justin Traglia
b667c68c87 Default to the portable version of blst (#12720)
* Default to the portable version of blst

* Update based on review comments

---------

Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
2023-09-29 15:35:15 -05:00
Lucas Saldanha
fb19ee8895 Updating Teku mainnet bootnodes ENRs (#12962)
Co-authored-by: Nishant Das <nishdas93@gmail.com>
Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
2023-09-29 10:11:58 +00:00
terencechain
51b8075474 Use skip mev-boost flag for GetBlock RPC (#12969)
Co-authored-by: Nishant Das <nishdas93@gmail.com>
2023-09-29 16:48:40 +08:00
Radosław Kapka
367504f403 Support comma-separated query params in Beacon API (#12966) 2023-09-29 15:40:23 +08:00
Potuz
b4e72f1e19 Deneb spectests release v1.4.0-beta.2-hotfix (#12959)
* Update and use max per epoch churn limit

* Update spec tests

* Fix e2e test

* deneb fork epoch condition

* Fix lint and better casting

* fix ordering

* fix check

* gaz

* Fix more tests

* Apply proposer boost to first block in equivocation

* Increase timeout

* Don't increase timeout, it's not the reason

* implement deneb forkchoice spectests

expose ReceiveBlob from the blockchain package

* spin_off_helper

* remove minimal tests

* Terence's review

* Add process register test for Deneb

* Terence's suggestion

Co-authored-by: terencechain <terence@prysmaticlabs.com>

* fix forkchoice minimal

* fix minimal sha

* general sha

* different repos

* different repos

---------

Co-authored-by: terence tsao <terence@prysmaticlabs.com>
Co-authored-by: nisdas <nishdas93@gmail.com>
2023-09-28 21:05:23 +00:00
Preston Van Loon
a7361cd5ab Docker: Add "manual" build tag to reduce build times in CI (#12967)
* Add manual tags to oci images. This reduces build times for build and test in CI.

* Ran buildifier on tools dir
2023-09-28 17:49:26 +00:00
Preston Van Loon
02ee6897bb Multiarch docker containers (#12428)
* Add bazel-zig-cc for a hermetic cc toolchain

* gazelle

* Remove llvm

* remove wl

* Add new URLs for renamed repo

* gazelle

* Update to v2.0.0-rc1

* bump to rc2

* Proof of concept multi-arch containers for beacon-chain

* TODO and gaz

* Refactor to starlark macro. Use a version of bash that actually works

* progress

* gaz

* multirun to use multiple repositories, but doesn't work with tag stamping

* Revert "multirun to use multiple repositories, but doesn't work with tag stamping"

This reverts commit 93afa76f65.

* Add targets for all supported docker images and temporarily set the repository to prysm-dev for testing

* use a temporary fix to see if it works on buildkite

* Revert "use a temporary fix to see if it works on buildkite"

This reverts commit ddc79283ca.

* testing a cURL fix

* try fix with my fix

* Revert "try fix with my fix"

This reverts commit bb7521bf47.

* Revert "testing a cURL fix"

This reverts commit 8a4782110f.

* try tip of main branch for rules_oci

* update to 1.2.0

* Update rules_oci to v1.3.0

* Update rule_oci to 1.3.4

* Disable experimental_remote_downloader

* Remove extra zig bazelrc

* Move image deps to its own file

* PR feedback

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-09-28 15:24:16 +00:00
terencechain
c20f188966 Deprecate safe slots to import flag (#12964) 2023-09-28 17:40:58 +08:00
terencechain
5870536dca Add a flag to configure blob retention epoch period (#12941)
* Add a flag to configure blob retention epoch period

* Add test

* Kasey's feedback

* More Kasey's feedback

* Fix lint and tests

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-09-27 14:59:52 +00:00
james-prysm
c8b39e08ef cleanup on api types (#12961)
* removing code duplication using shared types

* gaz

* fixing test

* fixing tests
2023-09-27 09:26:32 -05:00
Sammy Rosso
b0b4e42436 HTTP Beacon API: /eth/v1/beacon/blocks and /eth/v1/beacon/blinded_blocks (#12827)
* Add PublishBlock

* Add endpoints

* Cleanup

* Modify publishBlock to work for v1 and v2

* Remove v2 + add server receiver

* Cleanup remaining endpoints

* Remove error from SszRequested

* Remove unused functions
2023-09-27 12:51:37 +00:00
terencechain
b0caea3fae Refactor getLocalPayloadAndBlobs with New Helper getParentBlockHash (#12951)
* Implement getParentBlockHash helper

* Specify errors
2023-09-27 03:08:17 +00:00
Roberto Bayardo
a46370f5bf clean up code around setting geth client headers (#11748) 2023-09-26 15:15:28 +00:00
Nishant Das
0919b2245f Avoid Public Key Copies During Aggregation (#12944)
* Add in optimization

* add better test

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-09-26 11:13:19 +00:00
Nishant Das
42c192d97d Fix More Racy Tests in Blockchain (#12957) 2023-09-26 09:17:11 +00:00
Potuz
3394bbe359 forkchoice return last canonical root of epoch (#12954)
* forkchoice return last canonical root of epoch

* move inside loop

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-09-25 16:36:43 +00:00
Nishant Das
70225186ff Fix TestService_ReceiveBlock (#12953)
* fix tests

* radek's review

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-09-25 15:24:32 +00:00
terencechain
942d63fcc1 Refactor construct generic beacon block for proposer (#12943) 2023-09-25 07:07:04 -07:00
Sammy Rosso
90fb2325db /eth/v1/config/deposit_contract return string instead of uint (#12952)
* Convert uint to string

* Add test

* Rename test
2023-09-25 13:02:47 +00:00
199 changed files with 4173 additions and 4834 deletions

View File

@@ -15,7 +15,7 @@ coverage --define=coverage_enabled=1
# Stamp binaries with git information
build --workspace_status_command=./hack/workspace_status.sh
build --define blst_disabled=false --define blst_modern=true
build --define blst_disabled=false
run --define blst_disabled=false
build:blst_disabled --define blst_disabled=true

View File

@@ -12,7 +12,8 @@
#build:remote-cache --disk_cache=
build:remote-cache --remote_download_toplevel
build:remote-cache --remote_cache=grpc://bazel-remote-cache:9092
build:remote-cache --experimental_remote_downloader=grpc://bazel-remote-cache:9092
# Does not work with rules_oci. See https://github.com/bazel-contrib/rules_oci/issues/292
#build:remote-cache --experimental_remote_downloader=grpc://bazel-remote-cache:9092
build:remote-cache --remote_local_fallback
build:remote-cache --experimental_remote_cache_async
build:remote-cache --experimental_remote_merkle_tree_cache

View File

@@ -75,14 +75,14 @@ jobs:
- name: Build
# Use blst tag to allow go and bazel builds for blst.
run: go build -v ./...
env:
CGO_CFLAGS: "-O -D__BLST_PORTABLE__"
env:
CGO_CFLAGS: "-O2 -D__BLST_PORTABLE__"
# fuzz leverage go tag based stubs at compile time.
# Building and testing with these tags should be checked and enforced at pre-submit.
- name: Test for fuzzing
run: go test -tags=fuzz,develop ./... -test.run=^Fuzz
env:
CGO_CFLAGS: "-O -D__BLST_PORTABLE__"
env:
CGO_CFLAGS: "-O2 -D__BLST_PORTABLE__"
# Tests run via Bazel for now...
# - name: Test

View File

@@ -50,8 +50,6 @@ load("@prysm//tools/cross-toolchain:prysm_toolchains.bzl", "configure_prysm_tool
configure_prysm_toolchains()
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "bazel_skylib",
sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c",
@@ -87,6 +85,24 @@ http_archive(
urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.25.0/rules_docker-v0.25.0.tar.gz"],
)
http_archive(
name = "rules_oci",
sha256 = "c71c25ed333a4909d2dd77e0b16c39e9912525a98c7fa85144282be8d04ef54c",
strip_prefix = "rules_oci-1.3.4",
url = "https://github.com/bazel-contrib/rules_oci/releases/download/v1.3.4/rules_oci-v1.3.4.tar.gz",
)
load("@rules_oci//oci:dependencies.bzl", "rules_oci_dependencies")
rules_oci_dependencies()
load("@rules_oci//oci:repositories.bzl", "LATEST_CRANE_VERSION", "oci_register_toolchains")
oci_register_toolchains(
name = "oci",
crane_version = LATEST_CRANE_VERSION,
)
http_archive(
name = "io_bazel_rules_go",
patch_args = ["-p1"],
@@ -167,6 +183,24 @@ container_pull(
repository = "pinglamb/alpine-glibc",
)
load("@rules_oci//oci:pull.bzl", "oci_pull")
# A multi-arch base image
oci_pull(
name = "linux_debian11_multiarch_base", # Debian bullseye
digest = "sha256:9b8e0854865dcaf49470b4ec305df45957020fbcf17b71eeb50ffd3bc5bf885d", # 2023-05-17
image = "gcr.io/distroless/cc-debian11",
platforms = [
"linux/amd64",
"linux/arm64",
],
reproducible = True,
)
load("@prysm//tools:image_deps.bzl", "prysm_image_deps")
prysm_image_deps()
load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")
go_rules_dependencies()
@@ -213,7 +247,9 @@ filegroup(
url = "https://github.com/ethereum/EIPs/archive/5480440fe51742ed23342b68cf106cefd427e39d.tar.gz",
)
consensus_spec_version = "v1.4.0-beta.1"
consensus_spec_test_version = "v1.4.0-beta.2-hotfix"
consensus_spec_version = "v1.4.0-beta.2"
bls_test_version = "v0.1.1"
@@ -229,8 +265,8 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "24399b60ce3fbeb2311952d213dc3731b6dcb0f8881b016c283de5b518d2bbba",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/general.tar.gz" % consensus_spec_version,
sha256 = "99770a001189f66204a4ef79161c8002bcbbcbd8236f1c6479bd5b83a3c68d42",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/general.tar.gz" % consensus_spec_test_version,
)
http_archive(
@@ -245,8 +281,8 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "8e656ee48d2e2ebc9cf9baedb81f27925bc625b3e3fbb2883444a08758a5884a",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/minimal.tar.gz" % consensus_spec_version,
sha256 = "56763f6492ee137108271007d62feef60d8e3f1698e53dee4bc4b07e55f7326b",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/minimal.tar.gz" % consensus_spec_test_version,
)
http_archive(
@@ -261,8 +297,8 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "8bd137da6cc57a25383bfac5bc37e31265098145278bd8002b88e24c8b4718b9",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/mainnet.tar.gz" % consensus_spec_version,
sha256 = "bc1cac1a991cdc7426efea14385dcf215df85ed3f0572b824ad6a1d7ca0c89ad",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/mainnet.tar.gz" % consensus_spec_test_version,
)
http_archive(
@@ -276,7 +312,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "2bc1edb6e4a4f86c00317c04618a90b0ca29ee1eba833d0a64dd67fdd83fdbe3",
sha256 = "c5898001aaab2a5bb38a39ff9d17a52f1f9befcc26e63752cbf556040f0c884e",
strip_prefix = "consensus-specs-" + consensus_spec_version[1:],
url = "https://github.com/ethereum/consensus-specs/archive/refs/tags/%s.tar.gz" % consensus_spec_version,
)
@@ -337,9 +373,6 @@ http_archive(
],
)
# Group the sources of the library so that CMake rule have access to it
all_content = """filegroup(name = "all", srcs = glob(["**"]), visibility = ["//visibility:public"])"""
# External dependencies
load("//:deps.bzl", "prysm_deps")

View File

@@ -13,6 +13,7 @@ go_library(
"//api/client:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/rpc/apimiddleware:go_default_library",
"//beacon-chain/rpc/eth/shared:go_default_library",
"//beacon-chain/state:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",

View File

@@ -14,6 +14,7 @@ import (
"text/template"
"github.com/prysmaticlabs/prysm/v4/api/client"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
"github.com/prysmaticlabs/prysm/v4/network/forks"
v1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
@@ -148,14 +149,14 @@ func (c *Client) GetFork(ctx context.Context, stateId StateOrBlockId) (*ethpb.Fo
if err != nil {
return nil, errors.Wrapf(err, "error requesting fork by state id = %s", stateId)
}
fr := &forkResponse{}
dataWrapper := &struct{ Data *forkResponse }{Data: fr}
fr := &shared.Fork{}
dataWrapper := &struct{ Data *shared.Fork }{Data: fr}
err = json.Unmarshal(body, dataWrapper)
if err != nil {
return nil, errors.Wrap(err, "error decoding json response in GetFork")
}
return fr.Fork()
return fr.ToConsensus()
}
// GetForkSchedule retrieve all forks, past present and future, of which this node is aware.
@@ -335,40 +336,8 @@ func (c *Client) GetBLStoExecutionChanges(ctx context.Context) (*apimiddleware.B
return poolResponse, nil
}
type forkResponse struct {
PreviousVersion string `json:"previous_version"`
CurrentVersion string `json:"current_version"`
Epoch string `json:"epoch"`
}
func (f *forkResponse) Fork() (*ethpb.Fork, error) {
epoch, err := strconv.ParseUint(f.Epoch, 10, 64)
if err != nil {
return nil, err
}
cSlice, err := hexutil.Decode(f.CurrentVersion)
if err != nil {
return nil, err
}
if len(cSlice) != 4 {
return nil, fmt.Errorf("got %d byte version for CurrentVersion, expected 4 bytes. hex=%s", len(cSlice), f.CurrentVersion)
}
pSlice, err := hexutil.Decode(f.PreviousVersion)
if err != nil {
return nil, err
}
if len(pSlice) != 4 {
return nil, fmt.Errorf("got %d byte version, expected 4 bytes. version hex=%s", len(pSlice), f.PreviousVersion)
}
return &ethpb.Fork{
CurrentVersion: cSlice,
PreviousVersion: pSlice,
Epoch: primitives.Epoch(epoch),
}, nil
}
type forkScheduleResponse struct {
Data []forkResponse
Data []shared.Fork
}
func (fsr *forkScheduleResponse) OrderedForkSchedule() (forks.OrderedSchedule, error) {

View File

@@ -54,7 +54,6 @@ go_test(
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_golang_protobuf//proto:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],

View File

@@ -17,6 +17,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/monitoring/tracing"
"github.com/prysmaticlabs/prysm/v4/network"
"github.com/prysmaticlabs/prysm/v4/network/authorization"
@@ -269,9 +270,13 @@ func (c *Client) RegisterValidator(ctx context.Context, svr []*ethpb.SignedValid
tracing.AnnotateError(span, err)
return err
}
vs := make([]*SignedValidatorRegistration, len(svr))
vs := make([]*shared.SignedValidatorRegistration, len(svr))
for i := 0; i < len(svr); i++ {
vs[i] = &SignedValidatorRegistration{SignedValidatorRegistrationV1: svr[i]}
svrJson, err := shared.SignedValidatorRegistrationFromConsensus(svr[i])
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to encode to SignedValidatorRegistration at index %d", i))
}
vs[i] = svrJson
}
body, err := json.Marshal(vs)
if err != nil {
@@ -296,12 +301,14 @@ func (c *Client) SubmitBlindedBlock(ctx context.Context, sb interfaces.ReadOnlyS
if err != nil {
return nil, nil, errors.Wrapf(err, "could not get protobuf block")
}
b := &SignedBlindedBeaconBlockBellatrix{SignedBlindedBeaconBlockBellatrix: psb}
b, err := shared.SignedBlindedBeaconBlockBellatrixFromConsensus(&ethpb.SignedBlindedBeaconBlockBellatrix{Block: psb.Block, Signature: bytesutil.SafeCopyBytes(psb.Signature)})
if err != nil {
return nil, nil, errors.Wrapf(err, "could not convert SignedBlindedBeaconBlockBellatrix to json marshalable type")
}
body, err := json.Marshal(b)
if err != nil {
return nil, nil, errors.Wrap(err, "error encoding the SignedBlindedBeaconBlockBellatrix value body in SubmitBlindedBlock")
}
versionOpt := func(r *http.Request) {
r.Header.Add("Eth-Consensus-Version", version.String(version.Bellatrix))
}
@@ -331,12 +338,14 @@ func (c *Client) SubmitBlindedBlock(ctx context.Context, sb interfaces.ReadOnlyS
if err != nil {
return nil, nil, errors.Wrapf(err, "could not get protobuf block")
}
b := &SignedBlindedBeaconBlockCapella{SignedBlindedBeaconBlockCapella: psb}
b, err := shared.SignedBlindedBeaconBlockCapellaFromConsensus(&ethpb.SignedBlindedBeaconBlockCapella{Block: psb.Block, Signature: bytesutil.SafeCopyBytes(psb.Signature)})
if err != nil {
return nil, nil, errors.Wrapf(err, "could not convert SignedBlindedBeaconBlockCapella to json marshalable type")
}
body, err := json.Marshal(b)
if err != nil {
return nil, nil, errors.Wrap(err, "error encoding the SignedBlindedBeaconBlockCapella value body in SubmitBlindedBlockCapella")
}
versionOpt := func(r *http.Request) {
r.Header.Add("Eth-Consensus-Version", version.String(version.Capella))
}

File diff suppressed because one or more lines are too long

View File

@@ -16,87 +16,6 @@ import (
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
)
// SignedValidatorRegistration a struct for signed validator registrations.
type SignedValidatorRegistration struct {
*eth.SignedValidatorRegistrationV1
}
// ValidatorRegistration a struct for validator registrations.
type ValidatorRegistration struct {
*eth.ValidatorRegistrationV1
}
// MarshalJSON returns a json representation copy of signed validator registration.
func (r *SignedValidatorRegistration) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Message *ValidatorRegistration `json:"message"`
Signature hexutil.Bytes `json:"signature"`
}{
Message: &ValidatorRegistration{r.Message},
Signature: r.SignedValidatorRegistrationV1.Signature,
})
}
// UnmarshalJSON returns a byte representation of signed validator registration from json.
func (r *SignedValidatorRegistration) UnmarshalJSON(b []byte) error {
if r.SignedValidatorRegistrationV1 == nil {
r.SignedValidatorRegistrationV1 = &eth.SignedValidatorRegistrationV1{}
}
o := struct {
Message *ValidatorRegistration `json:"message"`
Signature hexutil.Bytes `json:"signature"`
}{}
if err := json.Unmarshal(b, &o); err != nil {
return err
}
r.Message = o.Message.ValidatorRegistrationV1
r.Signature = o.Signature
return nil
}
// MarshalJSON returns a json representation copy of validator registration.
func (r *ValidatorRegistration) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
FeeRecipient hexutil.Bytes `json:"fee_recipient"`
GasLimit string `json:"gas_limit"`
Timestamp string `json:"timestamp"`
Pubkey hexutil.Bytes `json:"pubkey"`
}{
FeeRecipient: r.FeeRecipient,
GasLimit: fmt.Sprintf("%d", r.GasLimit),
Timestamp: fmt.Sprintf("%d", r.Timestamp),
Pubkey: r.Pubkey,
})
}
// UnmarshalJSON returns a byte representation of validator registration from json.
func (r *ValidatorRegistration) UnmarshalJSON(b []byte) error {
if r.ValidatorRegistrationV1 == nil {
r.ValidatorRegistrationV1 = &eth.ValidatorRegistrationV1{}
}
o := struct {
FeeRecipient hexutil.Bytes `json:"fee_recipient"`
GasLimit string `json:"gas_limit"`
Timestamp string `json:"timestamp"`
Pubkey hexutil.Bytes `json:"pubkey"`
}{}
if err := json.Unmarshal(b, &o); err != nil {
return err
}
r.FeeRecipient = o.FeeRecipient
r.Pubkey = o.Pubkey
var err error
if r.GasLimit, err = strconv.ParseUint(o.GasLimit, 10, 64); err != nil {
return errors.Wrap(err, "failed to parse gas limit")
}
if r.Timestamp, err = strconv.ParseUint(o.Timestamp, 10, 64); err != nil {
return errors.Wrap(err, "failed to parse timestamp")
}
return nil
}
var errInvalidUint256 = errors.New("invalid Uint256")
var errDecodeUint256 = errors.New("unable to decode into Uint256")
@@ -636,44 +555,6 @@ type SignedBlindedBeaconBlockBellatrix struct {
*eth.SignedBlindedBeaconBlockBellatrix
}
// BlindedBeaconBlockBellatrix is a field in SignedBlindedBeaconBlockBellatrix.
type BlindedBeaconBlockBellatrix struct {
*eth.BlindedBeaconBlockBellatrix
}
// BlindedBeaconBlockBodyBellatrix is a field in BlindedBeaconBlockBellatrix.
type BlindedBeaconBlockBodyBellatrix struct {
*eth.BlindedBeaconBlockBodyBellatrix
}
// MarshalJSON returns a JSON byte array representation of SignedBlindedBeaconBlockBellatrix.
func (r *SignedBlindedBeaconBlockBellatrix) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Message *BlindedBeaconBlockBellatrix `json:"message"`
Signature hexutil.Bytes `json:"signature"`
}{
Message: &BlindedBeaconBlockBellatrix{r.SignedBlindedBeaconBlockBellatrix.Block},
Signature: r.SignedBlindedBeaconBlockBellatrix.Signature,
})
}
// MarshalJSON returns a JSON byte array representation of BlindedBeaconBlockBellatrix.
func (b *BlindedBeaconBlockBellatrix) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Slot string `json:"slot"`
ProposerIndex string `json:"proposer_index"`
ParentRoot hexutil.Bytes `json:"parent_root"`
StateRoot hexutil.Bytes `json:"state_root"`
Body *BlindedBeaconBlockBodyBellatrix `json:"body"`
}{
Slot: fmt.Sprintf("%d", b.Slot),
ProposerIndex: fmt.Sprintf("%d", b.ProposerIndex),
ParentRoot: b.ParentRoot,
StateRoot: b.StateRoot,
Body: &BlindedBeaconBlockBodyBellatrix{b.BlindedBeaconBlockBellatrix.Body},
})
}
// ProposerSlashing is a field in BlindedBeaconBlockBodyCapella.
type ProposerSlashing struct {
*eth.ProposerSlashing
@@ -928,53 +809,6 @@ func (e *Eth1Data) MarshalJSON() ([]byte, error) {
})
}
// MarshalJSON returns a JSON byte array representation of BlindedBeaconBlockBodyBellatrix.
func (b *BlindedBeaconBlockBodyBellatrix) MarshalJSON() ([]byte, error) {
sve := make([]*SignedVoluntaryExit, len(b.BlindedBeaconBlockBodyBellatrix.VoluntaryExits))
for i := range b.BlindedBeaconBlockBodyBellatrix.VoluntaryExits {
sve[i] = &SignedVoluntaryExit{SignedVoluntaryExit: b.BlindedBeaconBlockBodyBellatrix.VoluntaryExits[i]}
}
deps := make([]*Deposit, len(b.BlindedBeaconBlockBodyBellatrix.Deposits))
for i := range b.BlindedBeaconBlockBodyBellatrix.Deposits {
deps[i] = &Deposit{Deposit: b.BlindedBeaconBlockBodyBellatrix.Deposits[i]}
}
atts := make([]*Attestation, len(b.BlindedBeaconBlockBodyBellatrix.Attestations))
for i := range b.BlindedBeaconBlockBodyBellatrix.Attestations {
atts[i] = &Attestation{Attestation: b.BlindedBeaconBlockBodyBellatrix.Attestations[i]}
}
atsl := make([]*AttesterSlashing, len(b.BlindedBeaconBlockBodyBellatrix.AttesterSlashings))
for i := range b.BlindedBeaconBlockBodyBellatrix.AttesterSlashings {
atsl[i] = &AttesterSlashing{AttesterSlashing: b.BlindedBeaconBlockBodyBellatrix.AttesterSlashings[i]}
}
pros := make([]*ProposerSlashing, len(b.BlindedBeaconBlockBodyBellatrix.ProposerSlashings))
for i := range b.BlindedBeaconBlockBodyBellatrix.ProposerSlashings {
pros[i] = &ProposerSlashing{ProposerSlashing: b.BlindedBeaconBlockBodyBellatrix.ProposerSlashings[i]}
}
return json.Marshal(struct {
RandaoReveal hexutil.Bytes `json:"randao_reveal"`
Eth1Data *Eth1Data `json:"eth1_data"`
Graffiti hexutil.Bytes `json:"graffiti"`
ProposerSlashings []*ProposerSlashing `json:"proposer_slashings"`
AttesterSlashings []*AttesterSlashing `json:"attester_slashings"`
Attestations []*Attestation `json:"attestations"`
Deposits []*Deposit `json:"deposits"`
VoluntaryExits []*SignedVoluntaryExit `json:"voluntary_exits"`
SyncAggregate *SyncAggregate `json:"sync_aggregate"`
ExecutionPayloadHeader *ExecutionPayloadHeader `json:"execution_payload_header"`
}{
RandaoReveal: b.RandaoReveal,
Eth1Data: &Eth1Data{b.BlindedBeaconBlockBodyBellatrix.Eth1Data},
Graffiti: b.BlindedBeaconBlockBodyBellatrix.Graffiti,
ProposerSlashings: pros,
AttesterSlashings: atsl,
Attestations: atts,
Deposits: deps,
VoluntaryExits: sve,
SyncAggregate: &SyncAggregate{b.BlindedBeaconBlockBodyBellatrix.SyncAggregate},
ExecutionPayloadHeader: &ExecutionPayloadHeader{ExecutionPayloadHeader: b.BlindedBeaconBlockBodyBellatrix.ExecutionPayloadHeader},
})
}
// SignedBLSToExecutionChange is a field in Beacon Block Body for capella and above.
type SignedBLSToExecutionChange struct {
*eth.SignedBLSToExecutionChange
@@ -1009,102 +843,6 @@ func (ch *BLSToExecutionChange) MarshalJSON() ([]byte, error) {
})
}
// SignedBlindedBeaconBlockCapella is part of the request object sent to builder API /eth/v1/builder/blinded_blocks for Capella.
type SignedBlindedBeaconBlockCapella struct {
*eth.SignedBlindedBeaconBlockCapella
}
// BlindedBeaconBlockCapella is a field in SignedBlindedBeaconBlockCapella.
type BlindedBeaconBlockCapella struct {
*eth.BlindedBeaconBlockCapella
}
// BlindedBeaconBlockBodyCapella is a field in BlindedBeaconBlockCapella.
type BlindedBeaconBlockBodyCapella struct {
*eth.BlindedBeaconBlockBodyCapella
}
// MarshalJSON returns a JSON byte array representation of SignedBlindedBeaconBlockCapella.
func (b *SignedBlindedBeaconBlockCapella) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Message *BlindedBeaconBlockCapella `json:"message"`
Signature hexutil.Bytes `json:"signature"`
}{
Message: &BlindedBeaconBlockCapella{b.Block},
Signature: b.Signature,
})
}
// MarshalJSON returns a JSON byte array representation of BlindedBeaconBlockCapella
func (b *BlindedBeaconBlockCapella) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Slot string `json:"slot"`
ProposerIndex string `json:"proposer_index"`
ParentRoot hexutil.Bytes `json:"parent_root"`
StateRoot hexutil.Bytes `json:"state_root"`
Body *BlindedBeaconBlockBodyCapella `json:"body"`
}{
Slot: fmt.Sprintf("%d", b.Slot),
ProposerIndex: fmt.Sprintf("%d", b.ProposerIndex),
ParentRoot: b.ParentRoot,
StateRoot: b.StateRoot,
Body: &BlindedBeaconBlockBodyCapella{b.Body},
})
}
// MarshalJSON returns a JSON byte array representation of BlindedBeaconBlockBodyCapella
func (b *BlindedBeaconBlockBodyCapella) MarshalJSON() ([]byte, error) {
sve := make([]*SignedVoluntaryExit, len(b.VoluntaryExits))
for i := range b.VoluntaryExits {
sve[i] = &SignedVoluntaryExit{SignedVoluntaryExit: b.VoluntaryExits[i]}
}
deps := make([]*Deposit, len(b.Deposits))
for i := range b.Deposits {
deps[i] = &Deposit{Deposit: b.Deposits[i]}
}
atts := make([]*Attestation, len(b.Attestations))
for i := range b.Attestations {
atts[i] = &Attestation{Attestation: b.Attestations[i]}
}
atsl := make([]*AttesterSlashing, len(b.AttesterSlashings))
for i := range b.AttesterSlashings {
atsl[i] = &AttesterSlashing{AttesterSlashing: b.AttesterSlashings[i]}
}
pros := make([]*ProposerSlashing, len(b.ProposerSlashings))
for i := range b.ProposerSlashings {
pros[i] = &ProposerSlashing{ProposerSlashing: b.ProposerSlashings[i]}
}
chs := make([]*SignedBLSToExecutionChange, len(b.BlsToExecutionChanges))
for i := range b.BlsToExecutionChanges {
chs[i] = &SignedBLSToExecutionChange{SignedBLSToExecutionChange: b.BlsToExecutionChanges[i]}
}
return json.Marshal(struct {
RandaoReveal hexutil.Bytes `json:"randao_reveal"`
Eth1Data *Eth1Data `json:"eth1_data"`
Graffiti hexutil.Bytes `json:"graffiti"`
ProposerSlashings []*ProposerSlashing `json:"proposer_slashings"`
AttesterSlashings []*AttesterSlashing `json:"attester_slashings"`
Attestations []*Attestation `json:"attestations"`
Deposits []*Deposit `json:"deposits"`
VoluntaryExits []*SignedVoluntaryExit `json:"voluntary_exits"`
BLSToExecutionChanges []*SignedBLSToExecutionChange `json:"bls_to_execution_changes"`
SyncAggregate *SyncAggregate `json:"sync_aggregate"`
ExecutionPayloadHeader *ExecutionPayloadHeaderCapella `json:"execution_payload_header"`
}{
RandaoReveal: b.RandaoReveal,
Eth1Data: &Eth1Data{b.Eth1Data},
Graffiti: b.Graffiti,
ProposerSlashings: pros,
AttesterSlashings: atsl,
Attestations: atts,
Deposits: deps,
VoluntaryExits: sve,
BLSToExecutionChanges: chs,
SyncAggregate: &SyncAggregate{b.SyncAggregate},
ExecutionPayloadHeader: &ExecutionPayloadHeaderCapella{ExecutionPayloadHeaderCapella: b.ExecutionPayloadHeader},
})
}
// ExecHeaderResponseDeneb is the header response for builder API /eth/v1/builder/header/{slot}/{parent_hash}/{pubkey}.
type ExecHeaderResponseDeneb struct {
Data struct {

View File

@@ -12,8 +12,8 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/protobuf/proto"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/math"
v1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
@@ -38,7 +38,8 @@ func TestSignedValidatorRegistration_MarshalJSON(t *testing.T) {
},
Signature: make([]byte, 96),
}
a := &SignedValidatorRegistration{SignedValidatorRegistrationV1: svr}
a, err := shared.SignedValidatorRegistrationFromConsensus(svr)
require.NoError(t, err)
je, err := json.Marshal(a)
require.NoError(t, err)
// decode with a struct w/ plain strings so we can check the string encoding of the hex fields
@@ -55,11 +56,11 @@ func TestSignedValidatorRegistration_MarshalJSON(t *testing.T) {
require.Equal(t, "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", un.Message.Pubkey)
t.Run("roundtrip", func(t *testing.T) {
b := &SignedValidatorRegistration{}
b := &shared.SignedValidatorRegistration{}
if err := json.Unmarshal(je, b); err != nil {
require.NoError(t, err)
}
require.Equal(t, proto.Equal(a.SignedValidatorRegistrationV1, b.SignedValidatorRegistrationV1), true)
require.DeepEqual(t, a, b)
})
}
@@ -1529,7 +1530,7 @@ func TestUint256UnmarshalTooBig(t *testing.T) {
func TestMarshalBlindedBeaconBlockBodyBellatrix(t *testing.T) {
expected, err := os.ReadFile("testdata/blinded-block.json")
require.NoError(t, err)
b := &BlindedBeaconBlockBellatrix{BlindedBeaconBlockBellatrix: &eth.BlindedBeaconBlockBellatrix{
b, err := shared.BlindedBeaconBlockBellatrixFromConsensus(&eth.BlindedBeaconBlockBellatrix{
Slot: 1,
ProposerIndex: 1,
ParentRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
@@ -1546,7 +1547,8 @@ func TestMarshalBlindedBeaconBlockBodyBellatrix(t *testing.T) {
SyncAggregate: pbSyncAggregate(),
ExecutionPayloadHeader: pbExecutionPayloadHeader(t),
},
}}
})
require.NoError(t, err)
m, err := json.Marshal(b)
require.NoError(t, err)
// string error output is easier to deal with
@@ -1558,7 +1560,7 @@ func TestMarshalBlindedBeaconBlockBodyBellatrix(t *testing.T) {
func TestMarshalBlindedBeaconBlockBodyCapella(t *testing.T) {
expected, err := os.ReadFile("testdata/blinded-block-capella.json")
require.NoError(t, err)
b := &BlindedBeaconBlockCapella{BlindedBeaconBlockCapella: &eth.BlindedBeaconBlockCapella{
b, err := shared.BlindedBeaconBlockCapellaFromConsensus(&eth.BlindedBeaconBlockCapella{
Slot: 1,
ProposerIndex: 1,
ParentRoot: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
@@ -1575,7 +1577,8 @@ func TestMarshalBlindedBeaconBlockBodyCapella(t *testing.T) {
SyncAggregate: pbSyncAggregate(),
ExecutionPayloadHeader: pbExecutionPayloadHeaderCapella(t),
},
}}
})
require.NoError(t, err)
m, err := json.Marshal(b)
require.NoError(t, err)
// string error output is easier to deal with

View File

@@ -12,6 +12,7 @@ go_library(
"head.go",
"head_sync_committee_info.go",
"init_sync_process_block.go",
"lightclient.go",
"log.go",
"merge_ascii_art.go",
"metrics.go",
@@ -77,6 +78,8 @@ go_library(
"//monitoring/tracing:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/eth/v1:go_default_library",
"//proto/eth/v2:go_default_library",
"//proto/migration:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/attestation:go_default_library",
"//runtime/version:go_default_library",

View File

@@ -0,0 +1,246 @@
package blockchain
import (
"bytes"
"context"
"fmt"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
ethpbv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v4/proto/migration"
"github.com/prysmaticlabs/prysm/v4/time/slots"
)
const (
finalityBranchNumOfLeaves = 6
)
// CreateLightClientFinalityUpdate - implements https://github.com/ethereum/consensus-specs/blob/3d235740e5f1e641d3b160c8688f26e7dc5a1894/specs/altair/light-client/full-node.md#create_light_client_finality_update
// def create_light_client_finality_update(update: LightClientUpdate) -> LightClientFinalityUpdate:
//
// return LightClientFinalityUpdate(
// attested_header=update.attested_header,
// finalized_header=update.finalized_header,
// finality_branch=update.finality_branch,
// sync_aggregate=update.sync_aggregate,
// signature_slot=update.signature_slot,
// )
func CreateLightClientFinalityUpdate(update *ethpbv2.LightClientUpdate) *ethpbv2.LightClientFinalityUpdate {
return &ethpbv2.LightClientFinalityUpdate{
AttestedHeader: update.AttestedHeader,
FinalizedHeader: update.FinalizedHeader,
FinalityBranch: update.FinalityBranch,
SyncAggregate: update.SyncAggregate,
SignatureSlot: update.SignatureSlot,
}
}
// CreateLightClientOptimisticUpdate - implements https://github.com/ethereum/consensus-specs/blob/3d235740e5f1e641d3b160c8688f26e7dc5a1894/specs/altair/light-client/full-node.md#create_light_client_optimistic_update
// def create_light_client_optimistic_update(update: LightClientUpdate) -> LightClientOptimisticUpdate:
//
// return LightClientOptimisticUpdate(
// attested_header=update.attested_header,
// sync_aggregate=update.sync_aggregate,
// signature_slot=update.signature_slot,
// )
func CreateLightClientOptimisticUpdate(update *ethpbv2.LightClientUpdate) *ethpbv2.LightClientOptimisticUpdate {
return &ethpbv2.LightClientOptimisticUpdate{
AttestedHeader: update.AttestedHeader,
SyncAggregate: update.SyncAggregate,
SignatureSlot: update.SignatureSlot,
}
}
func NewLightClientOptimisticUpdateFromBeaconState(
ctx context.Context,
state state.BeaconState,
block interfaces.ReadOnlySignedBeaconBlock,
attestedState state.BeaconState) (*ethpbv2.LightClientUpdate, error) {
// assert compute_epoch_at_slot(attested_state.slot) >= ALTAIR_FORK_EPOCH
attestedEpoch := slots.ToEpoch(attestedState.Slot())
if attestedEpoch < types.Epoch(params.BeaconConfig().AltairForkEpoch) {
return nil, fmt.Errorf("invalid attested epoch %d", attestedEpoch)
}
// assert sum(block.message.body.sync_aggregate.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS
syncAggregate, err := block.Block().Body().SyncAggregate()
if err != nil {
return nil, fmt.Errorf("could not get sync aggregate %v", err)
}
if syncAggregate.SyncCommitteeBits.Count() < params.BeaconConfig().MinSyncCommitteeParticipants {
return nil, fmt.Errorf("invalid sync committee bits count %d", syncAggregate.SyncCommitteeBits.Count())
}
// assert state.slot == state.latest_block_header.slot
if state.Slot() != state.LatestBlockHeader().Slot {
return nil, fmt.Errorf("state slot %d not equal to latest block header slot %d", state.Slot(), state.LatestBlockHeader().Slot)
}
// assert hash_tree_root(header) == hash_tree_root(block.message)
header := state.LatestBlockHeader()
stateRoot, err := state.HashTreeRoot(ctx)
if err != nil {
return nil, fmt.Errorf("could not get state root %v", err)
}
header.StateRoot = stateRoot[:]
headerRoot, err := header.HashTreeRoot()
if err != nil {
return nil, fmt.Errorf("could not get header root %v", err)
}
blockRoot, err := block.Block().HashTreeRoot()
if err != nil {
return nil, fmt.Errorf("could not get block root %v", err)
}
if headerRoot != blockRoot {
return nil, fmt.Errorf("header root %#x not equal to block root %#x", headerRoot, blockRoot)
}
// assert attested_state.slot == attested_state.latest_block_header.slot
if attestedState.Slot() != attestedState.LatestBlockHeader().Slot {
return nil, fmt.Errorf("attested state slot %d not equal to attested latest block header slot %d", attestedState.Slot(), attestedState.LatestBlockHeader().Slot)
}
// attested_header = attested_state.latest_block_header.copy()
attestedHeader := attestedState.LatestBlockHeader()
// attested_header.state_root = hash_tree_root(attested_state)
attestedStateRoot, err := attestedState.HashTreeRoot(ctx)
if err != nil {
return nil, fmt.Errorf("could not get attested state root %v", err)
}
attestedHeader.StateRoot = attestedStateRoot[:]
// assert hash_tree_root(attested_header) == block.message.parent_root
attestedHeaderRoot, err := attestedHeader.HashTreeRoot()
if err != nil {
return nil, fmt.Errorf("could not get attested header root %v", err)
}
if attestedHeaderRoot != block.Block().ParentRoot() {
return nil, fmt.Errorf("attested header root %#x not equal to block parent root %#x", attestedHeaderRoot, block.Block().ParentRoot())
}
// Return result
attestedHeaderResult := &ethpbv1.BeaconBlockHeader{
Slot: attestedHeader.Slot,
ProposerIndex: attestedHeader.ProposerIndex,
ParentRoot: attestedHeader.ParentRoot,
StateRoot: attestedHeader.StateRoot,
BodyRoot: attestedHeader.BodyRoot,
}
syncAggregateResult := &ethpbv1.SyncAggregate{
SyncCommitteeBits: syncAggregate.SyncCommitteeBits,
SyncCommitteeSignature: syncAggregate.SyncCommitteeSignature,
}
result := &ethpbv2.LightClientUpdate{
AttestedHeader: attestedHeaderResult,
SyncAggregate: syncAggregateResult,
SignatureSlot: block.Block().Slot(),
}
return result, nil
}
func NewLightClientFinalityUpdateFromBeaconState(
ctx context.Context,
state state.BeaconState,
block interfaces.ReadOnlySignedBeaconBlock,
attestedState state.BeaconState,
finalizedBlock interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.LightClientUpdate, error) {
result, err := NewLightClientOptimisticUpdateFromBeaconState(
ctx,
state,
block,
attestedState,
)
if err != nil {
return nil, err
}
// Indicate finality whenever possible
var finalizedHeader *ethpbv1.BeaconBlockHeader
var finalityBranch [][]byte
if finalizedBlock != nil && !finalizedBlock.IsNil() {
if finalizedBlock.Block().Slot() != 0 {
tempFinalizedHeader, err := finalizedBlock.Header()
if err != nil {
return nil, fmt.Errorf("could not get finalized header %v", err)
}
finalizedHeader = migration.V1Alpha1SignedHeaderToV1(tempFinalizedHeader).GetMessage()
finalizedHeaderRoot, err := finalizedHeader.HashTreeRoot()
if err != nil {
return nil, fmt.Errorf("could not get finalized header root %v", err)
}
if finalizedHeaderRoot != bytesutil.ToBytes32(attestedState.FinalizedCheckpoint().Root) {
return nil, fmt.Errorf("finalized header root %#x not equal to attested finalized checkpoint root %#x", finalizedHeaderRoot, bytesutil.ToBytes32(attestedState.FinalizedCheckpoint().Root))
}
} else {
if !bytes.Equal(attestedState.FinalizedCheckpoint().Root, make([]byte, 32)) {
return nil, fmt.Errorf("invalid finalized header root %v", attestedState.FinalizedCheckpoint().Root)
}
finalizedHeader = &ethpbv1.BeaconBlockHeader{
Slot: 0,
ProposerIndex: 0,
ParentRoot: make([]byte, 32),
StateRoot: make([]byte, 32),
BodyRoot: make([]byte, 32),
}
}
var bErr error
finalityBranch, bErr = attestedState.FinalizedRootProof(ctx)
if bErr != nil {
return nil, fmt.Errorf("could not get finalized root proof %v", bErr)
}
} else {
finalizedHeader = &ethpbv1.BeaconBlockHeader{
Slot: 0,
ProposerIndex: 0,
ParentRoot: make([]byte, 32),
StateRoot: make([]byte, 32),
BodyRoot: make([]byte, 32),
}
finalityBranch = make([][]byte, finalityBranchNumOfLeaves)
for i := 0; i < finalityBranchNumOfLeaves; i++ {
finalityBranch[i] = make([]byte, 32)
}
}
result.FinalizedHeader = finalizedHeader
result.FinalityBranch = finalityBranch
return result, nil
}
func NewLightClientUpdateFromFinalityUpdate(update *ethpbv2.LightClientFinalityUpdate) *ethpbv2.LightClientUpdate {
return &ethpbv2.LightClientUpdate{
AttestedHeader: update.AttestedHeader,
FinalizedHeader: update.FinalizedHeader,
FinalityBranch: update.FinalityBranch,
SyncAggregate: update.SyncAggregate,
SignatureSlot: update.SignatureSlot,
}
}
func NewLightClientUpdateFromOptimisticUpdate(update *ethpbv2.LightClientOptimisticUpdate) *ethpbv2.LightClientUpdate {
return &ethpbv2.LightClientUpdate{
AttestedHeader: update.AttestedHeader,
SyncAggregate: update.SyncAggregate,
SignatureSlot: update.SignatureSlot,
}
}

View File

@@ -0,0 +1,160 @@
package blockchain
import (
"context"
"testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
v1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
)
type testlc struct {
t *testing.T
ctx context.Context
state state.BeaconState
block interfaces.ReadOnlySignedBeaconBlock
attestedState state.BeaconState
attestedHeader *ethpb.BeaconBlockHeader
}
func newTestLc(t *testing.T) *testlc {
return &testlc{t: t}
}
func (l *testlc) setupTest() *testlc {
ctx := context.Background()
slot := primitives.Slot(params.BeaconConfig().AltairForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)).Add(1)
attestedState, err := util.NewBeaconStateCapella()
require.NoError(l.t, err)
err = attestedState.SetSlot(slot)
require.NoError(l.t, err)
parent := util.NewBeaconBlockCapella()
parent.Block.Slot = slot
signedParent, err := blocks.NewSignedBeaconBlock(parent)
require.NoError(l.t, err)
parentHeader, err := signedParent.Header()
require.NoError(l.t, err)
attestedHeader := parentHeader.Header
err = attestedState.SetLatestBlockHeader(attestedHeader)
require.NoError(l.t, err)
attestedStateRoot, err := attestedState.HashTreeRoot(ctx)
require.NoError(l.t, err)
// get a new signed block so the root is updated with the new state root
parent.Block.StateRoot = attestedStateRoot[:]
signedParent, err = blocks.NewSignedBeaconBlock(parent)
require.NoError(l.t, err)
state, err := util.NewBeaconStateCapella()
require.NoError(l.t, err)
err = state.SetSlot(slot)
require.NoError(l.t, err)
parentRoot, err := signedParent.Block().HashTreeRoot()
require.NoError(l.t, err)
block := util.NewBeaconBlockCapella()
block.Block.Slot = slot
block.Block.ParentRoot = parentRoot[:]
for i := uint64(0); i < params.BeaconConfig().MinSyncCommitteeParticipants; i++ {
block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
}
signedBlock, err := blocks.NewSignedBeaconBlock(block)
require.NoError(l.t, err)
h, err := signedBlock.Header()
require.NoError(l.t, err)
err = state.SetLatestBlockHeader(h.Header)
require.NoError(l.t, err)
stateRoot, err := state.HashTreeRoot(ctx)
require.NoError(l.t, err)
// get a new signed block so the root is updated with the new state root
block.Block.StateRoot = stateRoot[:]
signedBlock, err = blocks.NewSignedBeaconBlock(block)
require.NoError(l.t, err)
l.state = state
l.attestedState = attestedState
l.attestedHeader = attestedHeader
l.block = signedBlock
l.ctx = ctx
return l
}
func (l *testlc) checkAttestedHeader(update *ethpbv2.LightClientUpdate) {
require.Equal(l.t, l.attestedHeader.Slot, update.AttestedHeader.Slot, "Attested header slot is not equal")
require.Equal(l.t, l.attestedHeader.ProposerIndex, update.AttestedHeader.ProposerIndex, "Attested header proposer index is not equal")
require.DeepSSZEqual(l.t, l.attestedHeader.ParentRoot, update.AttestedHeader.ParentRoot, "Attested header parent root is not equal")
require.DeepSSZEqual(l.t, l.attestedHeader.BodyRoot, update.AttestedHeader.BodyRoot, "Attested header body root is not equal")
attestedStateRoot, err := l.attestedState.HashTreeRoot(l.ctx)
require.NoError(l.t, err)
require.DeepSSZEqual(l.t, attestedStateRoot[:], update.AttestedHeader.StateRoot, "Attested header state root is not equal")
}
func (l *testlc) checkSyncAggregate(update *ethpbv2.LightClientUpdate) {
syncAggregate, err := l.block.Block().Body().SyncAggregate()
require.NoError(l.t, err)
require.DeepSSZEqual(l.t, syncAggregate.SyncCommitteeBits, update.SyncAggregate.SyncCommitteeBits, "SyncAggregate bits is not equal")
require.DeepSSZEqual(l.t, syncAggregate.SyncCommitteeSignature, update.SyncAggregate.SyncCommitteeSignature, "SyncAggregate signature is not equal")
}
func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T) {
l := newTestLc(t).setupTest()
update, err := NewLightClientOptimisticUpdateFromBeaconState(l.ctx, l.state, l.block, l.attestedState)
require.NoError(t, err)
require.NotNil(t, update, "update is nil")
require.Equal(t, l.block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal")
l.checkSyncAggregate(update)
l.checkAttestedHeader(update)
require.Equal(t, (*v1.BeaconBlockHeader)(nil), update.FinalizedHeader, "Finalized header is not nil")
require.DeepSSZEqual(t, ([][]byte)(nil), update.FinalityBranch, "Finality branch is not nil")
}
func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) {
l := newTestLc(t).setupTest()
update, err := NewLightClientFinalityUpdateFromBeaconState(l.ctx, l.state, l.block, l.attestedState, nil)
require.NoError(t, err)
require.NotNil(t, update, "update is nil")
require.Equal(t, l.block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal")
l.checkSyncAggregate(update)
l.checkAttestedHeader(update)
zeroHash := params.BeaconConfig().ZeroHash[:]
require.NotNil(t, update.FinalizedHeader, "Finalized header is nil")
require.Equal(t, primitives.Slot(0), update.FinalizedHeader.Slot, "Finalized header slot is not zero")
require.Equal(t, primitives.ValidatorIndex(0), update.FinalizedHeader.ProposerIndex, "Finalized header proposer index is not zero")
require.DeepSSZEqual(t, zeroHash, update.FinalizedHeader.ParentRoot, "Finalized header parent root is not zero")
require.DeepSSZEqual(t, zeroHash, update.FinalizedHeader.StateRoot, "Finalized header state root is not zero")
require.DeepSSZEqual(t, zeroHash, update.FinalizedHeader.BodyRoot, "Finalized header body root is not zero")
require.Equal(t, finalityBranchNumOfLeaves, len(update.FinalityBranch), "Invalid finality branch leaves")
for _, leaf := range update.FinalityBranch {
require.DeepSSZEqual(t, zeroHash, leaf, "Leaf is not zero")
}
}

View File

@@ -649,11 +649,13 @@ func (s *Service) lateBlockTasks(ctx context.Context) {
return
}
s.headLock.RUnlock()
s.cfg.ForkChoiceStore.RLock()
_, err = s.notifyForkchoiceUpdate(ctx, &notifyForkchoiceUpdateArg{
headState: headState,
headRoot: headRoot,
headBlock: headBlock.Block(),
})
s.cfg.ForkChoiceStore.RUnlock()
if err != nil {
log.WithError(err).Debug("could not perform late block tasks: failed to update forkchoice with engine")
}

View File

@@ -1,7 +1,23 @@
package blockchain
import (
"context"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
)
// SendNewBlobEvent sends a message to the BlobNotifier channel that the blob
// for the blocroot `root` is ready in the database
func (s *Service) SendNewBlobEvent(root [32]byte, index uint64) {
func (s *Service) sendNewBlobEvent(root [32]byte, index uint64) {
s.blobNotifiers.forRoot(root) <- index
}
// ReceiveBlob saves the blob to database and sends the new event
func (s *Service) ReceiveBlob(ctx context.Context, b *ethpb.BlobSidecar) error {
if err := s.cfg.BeaconDB.SaveBlobSidecar(ctx, []*ethpb.BlobSidecar{b}); err != nil {
return err
}
s.sendNewBlobEvent([32]byte(b.BlockRoot), b.Index)
return nil
}

View File

@@ -3,6 +3,7 @@ package blockchain
import (
"bytes"
"context"
"time"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
@@ -21,7 +22,6 @@ import (
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/attestation"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"github.com/prysmaticlabs/prysm/v4/time"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"go.opencensus.io/trace"
"golang.org/x/sync/errgroup"
@@ -42,7 +42,7 @@ type BlockReceiver interface {
// BlobReceiver interface defines the methods of chain service for receiving new
// blobs
type BlobReceiver interface {
SendNewBlobEvent([32]byte, uint64)
ReceiveBlob(context.Context, *ethpb.BlobSidecar) error
}
// SlashingReceiver interface defines the methods of chain service for receiving validated slashing over the wire.

View File

@@ -23,9 +23,10 @@ func TestService_ReceiveBlock(t *testing.T) {
ctx := context.Background()
genesis, keys := util.DeterministicGenesisState(t, 64)
copiedGen := genesis.Copy()
genFullBlock := func(t *testing.T, conf *util.BlockGenConfig, slot primitives.Slot) *ethpb.SignedBeaconBlock {
blk, err := util.GenerateFullBlock(genesis, keys, conf, slot)
assert.NoError(t, err)
blk, err := util.GenerateFullBlock(copiedGen.Copy(), keys, conf, slot)
require.NoError(t, err)
return blk
}
//params.SetupTestConfigCleanupWithLock(t)
@@ -108,6 +109,9 @@ func TestService_ReceiveBlock(t *testing.T) {
block: genFullBlock(t, util.DefaultBlockGenConfig(), 1 /*slot*/),
},
check: func(t *testing.T, s *Service) {
// Hacky sleep, should use a better way to be able to resolve the race
// between event being sent out and processed.
time.Sleep(100 * time.Millisecond)
if recvd := len(s.cfg.StateNotifier.(*blockchainTesting.MockStateNotifier).ReceivedEvents()); recvd < 1 {
t.Errorf("Received %d state notifications, expected at least 1", recvd)
}
@@ -119,6 +123,10 @@ func TestService_ReceiveBlock(t *testing.T) {
for _, tt := range tests {
wg.Add(1)
t.Run(tt.name, func(t *testing.T) {
defer func() {
wg.Done()
}()
genesis = genesis.Copy()
s, tr := minimalTestService(t,
WithFinalizedStateAtStartUp(genesis),
WithExitPool(voluntaryexits.NewPool()),
@@ -139,10 +147,9 @@ func TestService_ReceiveBlock(t *testing.T) {
if tt.wantedErr != "" {
assert.ErrorContains(t, tt.wantedErr, err)
} else {
assert.NoError(t, err)
require.NoError(t, err)
tt.check(t, s)
}
wg.Done()
})
}
wg.Wait()
@@ -173,6 +180,7 @@ func TestService_ReceiveBlockUpdateHead(t *testing.T) {
wg.Done()
}()
wg.Wait()
time.Sleep(100 * time.Millisecond)
if recvd := len(s.cfg.StateNotifier.(*blockchainTesting.MockStateNotifier).ReceivedEvents()); recvd < 1 {
t.Errorf("Received %d state notifications, expected at least 1", recvd)
}
@@ -215,6 +223,7 @@ func TestService_ReceiveBlockBatch(t *testing.T) {
block: genFullBlock(t, util.DefaultBlockGenConfig(), 1 /*slot*/),
},
check: func(t *testing.T, s *Service) {
time.Sleep(100 * time.Millisecond)
if recvd := len(s.cfg.StateNotifier.(*blockchainTesting.MockStateNotifier).ReceivedEvents()); recvd < 1 {
t.Errorf("Received %d state notifications, expected at least 1", recvd)
}

View File

@@ -606,10 +606,12 @@ func (s *ChainService) UnrealizedJustifiedPayloadBlockHash() [32]byte {
return [32]byte{}
}
// SendNewBlobEvent mocks the same method in the chain service
func (*ChainService) SendNewBlobEvent(_ [32]byte, _ uint64) {}
// BlockBeingSynced mocks the same method in the chain service
func (c *ChainService) BlockBeingSynced(root [32]byte) bool {
return root == c.SyncingRoot
}
// ReceiveBlob implements the same method in the chain service
func (*ChainService) ReceiveBlob(_ context.Context, _ *ethpb.BlobSidecar) error {
return nil
}

View File

@@ -51,6 +51,10 @@ func ProcessVoluntaryExits(
beaconState state.BeaconState,
exits []*ethpb.SignedVoluntaryExit,
) (state.BeaconState, error) {
// Avoid calculating the epoch churn if no exits exist.
if len(exits) == 0 {
return beaconState, nil
}
maxExitEpoch, churn := v.ValidatorsMaxExitEpochAndChurn(beaconState)
var exitEpoch primitives.Epoch
for idx, exit := range exits {

View File

@@ -137,9 +137,10 @@ func ProcessRegistryUpdates(ctx context.Context, state state.BeaconState) (state
return nil, errors.Wrap(err, "could not get active validator count")
}
churnLimit, err := helpers.ValidatorChurnLimit(activeValidatorCount)
if err != nil {
return nil, errors.Wrap(err, "could not get churn limit")
churnLimit := helpers.ValidatorActivationChurnLimit(activeValidatorCount)
if state.Version() >= version.Deneb {
churnLimit = helpers.ValidatorActivationChurnLimitDeneb(activeValidatorCount)
}
// Prevent churn limit cause index out of bound.

View File

@@ -311,8 +311,7 @@ func TestProcessRegistryUpdates_EligibleToActivate(t *testing.T) {
Slot: 5 * params.BeaconConfig().SlotsPerEpoch,
FinalizedCheckpoint: &ethpb.Checkpoint{Epoch: 6, Root: make([]byte, fieldparams.RootLength)},
}
limit, err := helpers.ValidatorChurnLimit(0)
require.NoError(t, err)
limit := helpers.ValidatorActivationChurnLimit(0)
for i := uint64(0); i < limit+10; i++ {
base.Validators = append(base.Validators, &ethpb.Validator{
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
@@ -338,6 +337,42 @@ func TestProcessRegistryUpdates_EligibleToActivate(t *testing.T) {
}
}
func TestProcessRegistryUpdates_EligibleToActivate_Cancun(t *testing.T) {
base := &ethpb.BeaconStateDeneb{
Slot: 5 * params.BeaconConfig().SlotsPerEpoch,
FinalizedCheckpoint: &ethpb.Checkpoint{Epoch: 6, Root: make([]byte, fieldparams.RootLength)},
}
cfg := params.BeaconConfig()
cfg.MinPerEpochChurnLimit = 10
cfg.ChurnLimitQuotient = 1
params.OverrideBeaconConfig(cfg)
for i := uint64(0); i < 10; i++ {
base.Validators = append(base.Validators, &ethpb.Validator{
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
ActivationEpoch: params.BeaconConfig().FarFutureEpoch,
})
}
beaconState, err := state_native.InitializeFromProtoDeneb(base)
require.NoError(t, err)
currentEpoch := time.CurrentEpoch(beaconState)
newState, err := epoch.ProcessRegistryUpdates(context.Background(), beaconState)
require.NoError(t, err)
for i, validator := range newState.Validators() {
assert.Equal(t, currentEpoch+1, validator.ActivationEligibilityEpoch, "Could not update registry %d, unexpected activation eligibility epoch", i)
// Note: In Deneb, only validators indices before `MaxPerEpochActivationChurnLimit` should be activated.
if uint64(i) < params.BeaconConfig().MaxPerEpochActivationChurnLimit && validator.ActivationEpoch != helpers.ActivationExitEpoch(currentEpoch) {
t.Errorf("Could not update registry %d, validators failed to activate: wanted activation epoch %d, got %d",
i, helpers.ActivationExitEpoch(currentEpoch), validator.ActivationEpoch)
}
if uint64(i) >= params.BeaconConfig().MaxPerEpochActivationChurnLimit && validator.ActivationEpoch != params.BeaconConfig().FarFutureEpoch {
t.Errorf("Could not update registry %d, validators should not have been activated, wanted activation epoch: %d, got %d",
i, params.BeaconConfig().FarFutureEpoch, validator.ActivationEpoch)
}
}
}
func TestProcessRegistryUpdates_ActivationCompletes(t *testing.T) {
base := &ethpb.BeaconState{
Slot: 5 * params.BeaconConfig().SlotsPerEpoch,

View File

@@ -206,10 +206,7 @@ func ActivationExitEpoch(epoch primitives.Epoch) primitives.Epoch {
return epoch + 1 + params.BeaconConfig().MaxSeedLookahead
}
// ValidatorChurnLimit returns the number of validators that are allowed to
// enter and exit validator pool for an epoch.
//
// Spec pseudocode definition:
// calculateChurnLimit based on the formula in the spec.
//
// def get_validator_churn_limit(state: BeaconState) -> uint64:
// """
@@ -217,12 +214,32 @@ func ActivationExitEpoch(epoch primitives.Epoch) primitives.Epoch {
// """
// active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
// return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT)
func ValidatorChurnLimit(activeValidatorCount uint64) (uint64, error) {
func calculateChurnLimit(activeValidatorCount uint64) uint64 {
churnLimit := activeValidatorCount / params.BeaconConfig().ChurnLimitQuotient
if churnLimit < params.BeaconConfig().MinPerEpochChurnLimit {
churnLimit = params.BeaconConfig().MinPerEpochChurnLimit
return params.BeaconConfig().MinPerEpochChurnLimit
}
return churnLimit, nil
return churnLimit
}
// ValidatorActivationChurnLimit returns the maximum number of validators that can be activated in a slot.
func ValidatorActivationChurnLimit(activeValidatorCount uint64) uint64 {
return calculateChurnLimit(activeValidatorCount)
}
// ValidatorExitChurnLimit returns the maximum number of validators that can be exited in a slot.
func ValidatorExitChurnLimit(activeValidatorCount uint64) uint64 {
return calculateChurnLimit(activeValidatorCount)
}
// ValidatorActivationChurnLimitDeneb returns the maximum number of validators that can be activated in a slot post Deneb.
func ValidatorActivationChurnLimitDeneb(activeValidatorCount uint64) uint64 {
limit := calculateChurnLimit(activeValidatorCount)
// New in Deneb.
if limit > params.BeaconConfig().MaxPerEpochActivationChurnLimit {
return params.BeaconConfig().MaxPerEpochActivationChurnLimit
}
return limit
}
// BeaconProposerIndex returns proposer index of a current slot.

View File

@@ -367,9 +367,50 @@ func TestChurnLimit_OK(t *testing.T) {
require.NoError(t, err)
validatorCount, err := ActiveValidatorCount(context.Background(), beaconState, time.CurrentEpoch(beaconState))
require.NoError(t, err)
resultChurn, err := ValidatorChurnLimit(validatorCount)
resultChurn := ValidatorActivationChurnLimit(validatorCount)
assert.Equal(t, test.wantedChurn, resultChurn, "ValidatorActivationChurnLimit(%d)", test.validatorCount)
}
}
func TestChurnLimitDeneb_OK(t *testing.T) {
tests := []struct {
validatorCount int
wantedChurn uint64
}{
{1000, 4},
{100000, 4},
{1000000, params.BeaconConfig().MaxPerEpochActivationChurnLimit},
{2000000, params.BeaconConfig().MaxPerEpochActivationChurnLimit},
}
defer ClearCache()
for _, test := range tests {
ClearCache()
// Create validators
validators := make([]*ethpb.Validator, test.validatorCount)
for i := range validators {
validators[i] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
// Initialize beacon state
beaconState, err := state_native.InitializeFromProtoPhase0(&ethpb.BeaconState{
Slot: 1,
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
})
require.NoError(t, err)
assert.Equal(t, test.wantedChurn, resultChurn, "ValidatorChurnLimit(%d)", test.validatorCount)
// Get active validator count
validatorCount, err := ActiveValidatorCount(context.Background(), beaconState, time.CurrentEpoch(beaconState))
require.NoError(t, err)
// Test churn limit calculation
resultChurn := ValidatorActivationChurnLimitDeneb(validatorCount)
assert.Equal(t, test.wantedChurn, resultChurn)
}
}

View File

@@ -80,10 +80,7 @@ func ComputeWeakSubjectivityPeriod(ctx context.Context, st state.ReadOnlyBeaconS
T := cfg.MaxEffectiveBalance / cfg.GweiPerEth
// Validator churn limit.
delta, err := ValidatorChurnLimit(N)
if err != nil {
return 0, fmt.Errorf("cannot obtain active validator churn limit: %w", err)
}
delta := ValidatorExitChurnLimit(N)
// Balance top-ups.
Delta := uint64(cfg.SlotsPerEpoch.Mul(cfg.MaxDeposits))

View File

@@ -82,10 +82,7 @@ func InitiateValidatorExit(ctx context.Context, s state.BeaconState, idx primiti
if err != nil {
return nil, 0, errors.Wrap(err, "could not get active validator count")
}
currentChurn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
if err != nil {
return nil, 0, errors.Wrap(err, "could not get churn limit")
}
currentChurn := helpers.ValidatorExitChurnLimit(activeValidatorCount)
if churn >= currentChurn {
exitQueueEpoch, err = exitQueueEpoch.SafeAdd(1)
@@ -235,10 +232,7 @@ func ExitedValidatorIndices(epoch primitives.Epoch, validators []*ethpb.Validato
exitQueueChurn++
}
}
churn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
if err != nil {
return nil, errors.Wrap(err, "could not get churn limit")
}
churn := helpers.ValidatorExitChurnLimit(activeValidatorCount)
if churn < exitQueueChurn {
exitQueueEpoch++
}
@@ -276,10 +270,7 @@ func EjectedValidatorIndices(epoch primitives.Epoch, validators []*ethpb.Validat
exitQueueChurn++
}
}
churn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
if err != nil {
return nil, errors.Wrap(err, "could not get churn limit")
}
churn := helpers.ValidatorExitChurnLimit(activeValidatorCount)
if churn < exitQueueChurn {
exitQueueEpoch++
}

View File

@@ -13,6 +13,7 @@ go_library(
"error.go",
"execution_chain.go",
"finalized_block_roots.go",
"flags.go",
"genesis.go",
"key.go",
"kv.go",
@@ -38,6 +39,7 @@ go_library(
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/genesis:go_default_library",
"//beacon-chain/state/state-native: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",
@@ -65,6 +67,7 @@ go_library(
"@com_github_prysmaticlabs_prombbolt//:go_default_library",
"@com_github_schollz_progressbar_v3//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
"@io_etcd_go_bbolt//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
@@ -83,6 +86,7 @@ go_test(
"encoding_test.go",
"execution_chain_test.go",
"finalized_block_roots_test.go",
"flags_test.go",
"genesis_test.go",
"init_test.go",
"kv_test.go",
@@ -103,6 +107,7 @@ go_test(
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/genesis:go_default_library",
"//beacon-chain/state/state-native: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",
@@ -119,6 +124,7 @@ go_test(
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_golang_snappy//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
"@io_etcd_go_bbolt//:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",

View File

@@ -289,7 +289,6 @@ func blobSidecarKey(blob *ethpb.BlobSidecar) blobRotatingKey {
func slotKey(slot types.Slot) []byte {
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
maxEpochsToPersistBlobs := params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest
maxSlotsToPersistBlobs := types.Slot(maxEpochsToPersistBlobs.Mul(uint64(slotsPerEpoch)))
return bytesutil.SlotToBytesBigEndian(slot.ModSlot(maxSlotsToPersistBlobs))
}
@@ -299,14 +298,14 @@ func checkEpochsForBlobSidecarsRequestBucket(db *bolt.DB) error {
b := tx.Bucket(chainMetadataBucket)
v := b.Get(blobRetentionEpochsKey)
if v == nil {
if err := b.Put(blobRetentionEpochsKey, bytesutil.Uint64ToBytesBigEndian(uint64(params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest))); err != nil {
if err := b.Put(blobRetentionEpochsKey, bytesutil.Uint64ToBytesBigEndian(uint64(maxEpochsToPersistBlobs))); err != nil {
return err
}
return nil
}
e := bytesutil.BytesToUint64BigEndian(v)
if e != uint64(params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest) {
return fmt.Errorf("epochs for blobs request value in DB %d does not match config value %d", e, params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest)
if e != uint64(maxEpochsToPersistBlobs) {
return fmt.Errorf("epochs for blobs request value in DB %d does not match config value %d", e, maxEpochsToPersistBlobs)
}
return nil
}); err != nil {

View File

@@ -3,10 +3,13 @@ package kv
import (
"context"
"crypto/rand"
"flag"
"fmt"
"strconv"
"testing"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/cmd/beacon-chain/flags"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
@@ -15,6 +18,7 @@ import (
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/assertions"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/urfave/cli/v2"
bolt "go.etcd.io/bbolt"
)
@@ -332,7 +336,6 @@ func generateBlobSidecar(t *testing.T, index uint64) *ethpb.BlobSidecar {
kzgProof := make([]byte, 48)
_, err = rand.Read(kzgProof)
require.NoError(t, err)
return &ethpb.BlobSidecar{
BlockRoot: bytesutil.PadTo([]byte{'a'}, 32),
Index: index,
@@ -516,9 +519,12 @@ func Test_checkEpochsForBlobSidecarsRequestBucket(t *testing.T) {
require.NoError(t, checkEpochsForBlobSidecarsRequestBucket(dbStore.db)) // First write
require.NoError(t, checkEpochsForBlobSidecarsRequestBucket(dbStore.db)) // First check
nConfig := params.BeaconNetworkConfig()
nConfig.MinEpochsForBlobsSidecarsRequest = 42069
params.OverrideBeaconNetworkConfig(nConfig)
params.SetupTestConfigCleanup(t)
set := flag.NewFlagSet("test", 0)
set.Uint64(flags.BlobRetentionEpoch.Name, 0, "")
require.NoError(t, set.Set(flags.BlobRetentionEpoch.Name, strconv.FormatUint(42069, 10)))
cliCtx := cli.NewContext(&cli.App{}, set, nil)
require.NoError(t, ConfigureBlobRetentionEpoch(cliCtx))
require.ErrorContains(t, "epochs for blobs request value in DB 4096 does not match config value 42069", checkEpochsForBlobSidecarsRequestBucket(dbStore.db))
}

View File

@@ -0,0 +1,33 @@
package kv
import (
"fmt"
"github.com/prysmaticlabs/prysm/v4/cmd/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/urfave/cli/v2"
)
var maxEpochsToPersistBlobs = params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest
// ConfigureBlobRetentionEpoch sets the for blob retention based on command-line context. It sets the local config `maxEpochsToPersistBlobs`.
// If the flag is not set, the spec default `MinEpochsForBlobsSidecarsRequest` is used.
// An error if the input epoch is smaller than the spec default value.
func ConfigureBlobRetentionEpoch(cliCtx *cli.Context) error {
// Check if the blob retention epoch flag is set.
if cliCtx.IsSet(flags.BlobRetentionEpoch.Name) {
// Retrieve and cast the epoch value.
epochValue := cliCtx.Uint64(flags.BlobRetentionEpoch.Name)
e := primitives.Epoch(epochValue)
// Validate the epoch value against the spec default.
if e < params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest {
return fmt.Errorf("%s smaller than spec default, %d < %d", flags.BlobRetentionEpoch.Name, e, params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest)
}
maxEpochsToPersistBlobs = e
}
return nil
}

View File

@@ -0,0 +1,39 @@
package kv
import (
"flag"
"strconv"
"testing"
"github.com/prysmaticlabs/prysm/v4/cmd/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/urfave/cli/v2"
)
func TestConfigureBlobRetentionEpoch(t *testing.T) {
maxEpochsToPersistBlobs = params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest
params.SetupTestConfigCleanup(t)
app := cli.App{}
set := flag.NewFlagSet("test", 0)
// Test case: Spec default.
require.NoError(t, ConfigureBlobRetentionEpoch(cli.NewContext(&app, set, nil)))
require.Equal(t, params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest, maxEpochsToPersistBlobs)
set.Uint64(flags.BlobRetentionEpoch.Name, 0, "")
minEpochsForSidecarRequest := uint64(params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest)
require.NoError(t, set.Set(flags.BlobRetentionEpoch.Name, strconv.FormatUint(2*minEpochsForSidecarRequest, 10)))
cliCtx := cli.NewContext(&app, set, nil)
// Test case: Input epoch is greater than or equal to spec value.
require.NoError(t, ConfigureBlobRetentionEpoch(cliCtx))
require.Equal(t, primitives.Epoch(2*minEpochsForSidecarRequest), maxEpochsToPersistBlobs)
// Test case: Input epoch is less than spec value.
require.NoError(t, set.Set(flags.BlobRetentionEpoch.Name, strconv.FormatUint(minEpochsForSidecarRequest-1, 10)))
cliCtx = cli.NewContext(&app, set, nil)
err := ConfigureBlobRetentionEpoch(cliCtx)
require.ErrorContains(t, "extend-blob-retention-epoch smaller than spec default", err)
}

View File

@@ -3,6 +3,7 @@ package execution
import (
"context"
"fmt"
"net/http"
"strings"
"time"
@@ -106,28 +107,26 @@ func (s *Service) retryExecutionClientConnection(ctx context.Context, err error)
// Initializes an RPC connection with authentication headers.
func (s *Service) newRPCClientWithAuth(ctx context.Context, endpoint network.Endpoint) (*gethRPC.Client, error) {
client, err := network.NewExecutionRPCClient(ctx, endpoint)
if err != nil {
return nil, err
}
headers := http.Header{}
if endpoint.Auth.Method != authorization.None {
header, err := endpoint.Auth.ToHeaderValue()
if err != nil {
return nil, err
}
client.SetHeader("Authorization", header)
headers.Set("Authorization", header)
}
for _, h := range s.cfg.headers {
if h != "" {
keyValue := strings.Split(h, "=")
if len(keyValue) < 2 {
log.Warnf("Incorrect HTTP header flag format. Skipping %v", keyValue[0])
continue
}
client.SetHeader(keyValue[0], strings.Join(keyValue[1:], "="))
if h == "" {
continue
}
keyValue := strings.Split(h, "=")
if len(keyValue) < 2 {
log.Warnf("Incorrect HTTP header flag format. Skipping %v", keyValue[0])
continue
}
headers.Set(keyValue[0], strings.Join(keyValue[1:], "="))
}
return client, nil
return network.NewExecutionRPCClient(ctx, endpoint, headers)
}
// Checks the chain ID of the execution client to ensure

View File

@@ -6,6 +6,7 @@ go_library(
"doc.go",
"errors.go",
"forkchoice.go",
"last_root.go",
"metrics.go",
"node.go",
"on_tick.go",
@@ -49,6 +50,7 @@ go_test(
srcs = [
"ffg_update_test.go",
"forkchoice_test.go",
"last_root_test.go",
"no_vote_test.go",
"node_test.go",
"on_tick_test.go",

View File

@@ -0,0 +1,26 @@
package doublylinkedtree
import (
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/time/slots"
)
// LastRoot returns the last canonical block root in the given epoch
func (f *ForkChoice) LastRoot(epoch primitives.Epoch) [32]byte {
head := f.store.headNode
headEpoch := slots.ToEpoch(head.slot)
epochEnd, err := slots.EpochEnd(epoch)
if err != nil {
return [32]byte{}
}
if headEpoch <= epoch {
return head.root
}
for head != nil && head.slot > epochEnd {
head = head.parent
}
if head == nil {
return [32]byte{}
}
return head.root
}

View File

@@ -0,0 +1,39 @@
package doublylinkedtree
import (
"context"
"testing"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/testing/require"
)
func TestLastRoot(t *testing.T) {
f := setup(0, 0)
ctx := context.Background()
st, root, err := prepareForkchoiceState(ctx, 1, [32]byte{'1'}, params.BeaconConfig().ZeroHash, [32]byte{'1'}, 0, 0)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, st, root))
st, root, err = prepareForkchoiceState(ctx, 2, [32]byte{'2'}, [32]byte{'1'}, [32]byte{'2'}, 0, 0)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, st, root))
st, root, err = prepareForkchoiceState(ctx, 3, [32]byte{'3'}, [32]byte{'1'}, [32]byte{'3'}, 0, 0)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, st, root))
st, root, err = prepareForkchoiceState(ctx, 32, [32]byte{'4'}, [32]byte{'3'}, [32]byte{'4'}, 0, 0)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, st, root))
st, root, err = prepareForkchoiceState(ctx, 33, [32]byte{'5'}, [32]byte{'2'}, [32]byte{'5'}, 0, 0)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, st, root))
st, root, err = prepareForkchoiceState(ctx, 34, [32]byte{'6'}, [32]byte{'5'}, [32]byte{'6'}, 0, 0)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, st, root))
headNode, _ := f.store.nodeByRoot[[32]byte{'6'}]
f.store.headNode = headNode
require.Equal(t, [32]byte{'6'}, f.store.headNode.root)
require.Equal(t, [32]byte{'2'}, f.LastRoot(0))
require.Equal(t, [32]byte{'6'}, f.LastRoot(1))
require.Equal(t, [32]byte{'6'}, f.LastRoot(2))
}

View File

@@ -49,6 +49,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
slot := primitives.Slot(1)
driftGenesisTime(f, slot, 0)
newRoot := indexToHash(1)
f.store.proposerBoostRoot = [32]byte{}
state, blkRoot, err := prepareForkchoiceState(
ctx,
slot,
@@ -74,6 +75,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
slot = primitives.Slot(2)
driftGenesisTime(f, slot, 0)
newRoot = indexToHash(2)
f.store.proposerBoostRoot = [32]byte{}
state, blkRoot, err = prepareForkchoiceState(
ctx,
slot,
@@ -101,6 +103,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
slot = primitives.Slot(3)
driftGenesisTime(f, slot, 0)
newRoot = indexToHash(3)
f.store.proposerBoostRoot = [32]byte{}
state, blkRoot, err = prepareForkchoiceState(
ctx,
slot,
@@ -129,6 +132,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
slot = primitives.Slot(4)
driftGenesisTime(f, slot, 0)
newRoot = indexToHash(4)
f.store.proposerBoostRoot = [32]byte{}
state, blkRoot, err = prepareForkchoiceState(
ctx,
slot,
@@ -335,6 +339,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
cSlot := primitives.Slot(2)
driftGenesisTime(f, cSlot, 0)
c := indexToHash(2)
f.store.proposerBoostRoot = [32]byte{}
state, blkRoot, err := prepareForkchoiceState(
ctx,
cSlot,
@@ -354,6 +359,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
bSlot := primitives.Slot(1)
b := indexToHash(1)
f.store.proposerBoostRoot = [32]byte{}
state, blkRoot, err = prepareForkchoiceState(
ctx,
bSlot,
@@ -378,6 +384,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
// A block D, building on B, is received at slot N+3. It should not be able to win without boosting.
dSlot := primitives.Slot(3)
d := indexToHash(3)
f.store.proposerBoostRoot = [32]byte{}
state, blkRoot, err = prepareForkchoiceState(
ctx,
dSlot,
@@ -398,6 +405,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
// If the same block arrives with boosting then it becomes head:
driftGenesisTime(f, dSlot, 0)
d2 := indexToHash(30)
f.store.proposerBoostRoot = [32]byte{}
state, blkRoot, err = prepareForkchoiceState(
ctx,
dSlot,

View File

@@ -109,7 +109,8 @@ func (s *Store) insert(ctx context.Context,
secondsIntoSlot := (timeNow - s.genesisTime) % params.BeaconConfig().SecondsPerSlot
currentSlot := slots.CurrentSlot(s.genesisTime)
boostThreshold := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot
if currentSlot == slot && secondsIntoSlot < boostThreshold {
isFirstBlock := s.proposerBoostRoot == [32]byte{}
if currentSlot == slot && secondsIntoSlot < boostThreshold && isFirstBlock {
s.proposerBoostRoot = root
}

View File

@@ -68,6 +68,7 @@ type Getter interface {
IsOptimistic(root [32]byte) (bool, error)
ShouldOverrideFCU() bool
Slot([32]byte) (primitives.Slot, error)
LastRoot(primitives.Epoch) [32]byte
}
// Setter allows to set forkchoice information

View File

@@ -8,6 +8,7 @@ go_library(
"node.go",
"options.go",
"prometheus.go",
"router.go",
],
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/node",
visibility = [
@@ -40,6 +41,7 @@ go_library(
"//beacon-chain/p2p:go_default_library",
"//beacon-chain/rpc:go_default_library",
"//beacon-chain/rpc/apimiddleware:go_default_library",
"//beacon-chain/rpc/eth/helpers:go_default_library",
"//beacon-chain/slasher:go_default_library",
"//beacon-chain/startup:go_default_library",
"//beacon-chain/state:go_default_library",

View File

@@ -153,6 +153,9 @@ func New(cliCtx *cli.Context, opts ...Option) (*BeaconNode, error) {
if err := configureExecutionSetting(cliCtx); err != nil {
return nil, err
}
if err := kv.ConfigureBlobRetentionEpoch(cliCtx); err != nil {
return nil, err
}
configureFastSSZHashingAlgorithm()
// Initializes any forks here.
@@ -268,6 +271,7 @@ func New(cliCtx *cli.Context, opts ...Option) (*BeaconNode, error) {
log.Debugln("Registering RPC Service")
router := mux.NewRouter()
router.Use(middleware)
if err := beacon.registerRPCService(router); err != nil {
return nil, err
}

View File

@@ -0,0 +1,17 @@
package node
import (
"net/http"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers"
)
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
helpers.NormalizeQueryValues(query)
r.URL.RawQuery = query.Encode()
next.ServeHTTP(w, r)
})
}

View File

@@ -70,19 +70,6 @@ func handleGetBlindedBeaconBlockSSZ(
return handleGetSSZ(m, endpoint, w, req, config)
}
func handleSubmitBlockSSZ(m *apimiddleware.ApiProxyMiddleware, endpoint apimiddleware.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
return handlePostSSZ(m, endpoint, w, req)
}
func handleSubmitBlindedBlockSSZ(
m *apimiddleware.ApiProxyMiddleware,
endpoint apimiddleware.Endpoint,
w http.ResponseWriter,
req *http.Request,
) (handled bool) {
return handlePostSSZ(m, endpoint, w, req)
}
func handleProduceBlockSSZ(m *apimiddleware.ApiProxyMiddleware, endpoint apimiddleware.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
config := sszConfig{
fileName: "produce_beacon_block.ssz",
@@ -111,11 +98,7 @@ func handleGetSSZ(
req *http.Request,
config sszConfig,
) (handled bool) {
ssz, err := http2.SszRequested(req)
if err != nil {
apimiddleware.WriteError(w, apimiddleware.InternalServerError(err), nil)
return true
}
ssz := http2.SszRequested(req)
if !ssz {
return false
}
@@ -163,58 +146,6 @@ func handleGetSSZ(
return true
}
func handlePostSSZ(m *apimiddleware.ApiProxyMiddleware, endpoint apimiddleware.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
if !sszPosted(req) {
return false
}
if errJson := prepareSSZRequestForProxying(m, endpoint, req); errJson != nil {
apimiddleware.WriteError(w, errJson, nil)
return true
}
prepareCustomHeaders(req)
if errJson := preparePostedSSZData(req); errJson != nil {
apimiddleware.WriteError(w, errJson, nil)
return true
}
grpcResponse, errJson := m.ProxyRequest(req)
if errJson != nil {
apimiddleware.WriteError(w, errJson, nil)
return true
}
grpcResponseBody, errJson := apimiddleware.ReadGrpcResponseBody(grpcResponse.Body)
if errJson != nil {
apimiddleware.WriteError(w, errJson, nil)
return true
}
respHasError, errJson := apimiddleware.HandleGrpcResponseError(endpoint.Err, grpcResponse, grpcResponseBody, w)
if errJson != nil {
apimiddleware.WriteError(w, errJson, nil)
return true
}
if respHasError {
return true
}
if errJson := apimiddleware.Cleanup(grpcResponse.Body); errJson != nil {
apimiddleware.WriteError(w, errJson, nil)
return true
}
return true
}
func sszPosted(req *http.Request) bool {
ct, ok := req.Header["Content-Type"]
if !ok {
return false
}
if len(ct) != 1 {
return false
}
return ct[0] == api.OctetStreamMediaType
}
func prepareSSZRequestForProxying(m *apimiddleware.ApiProxyMiddleware, endpoint apimiddleware.Endpoint, req *http.Request) apimiddleware.ErrorJson {
req.URL.Scheme = "http"
req.URL.Host = m.GatewayAddress
@@ -230,14 +161,6 @@ func prepareSSZRequestForProxying(m *apimiddleware.ApiProxyMiddleware, endpoint
return nil
}
func prepareCustomHeaders(req *http.Request) {
ver := req.Header.Get(api.VersionHeader)
if ver != "" {
req.Header.Del(api.VersionHeader)
req.Header.Add(grpc.WithPrefix(api.VersionHeader), ver)
}
}
func preparePostedSSZData(req *http.Request) apimiddleware.ErrorJson {
buf, err := io.ReadAll(req.Body)
if err != nil {

View File

@@ -19,7 +19,6 @@ func (_ *BeaconEndpointFactory) Paths() []string {
"/eth/v1/beacon/states/{state_id}/root",
"/eth/v1/beacon/states/{state_id}/sync_committees",
"/eth/v1/beacon/states/{state_id}/randao",
"/eth/v1/beacon/blocks",
"/eth/v1/beacon/blinded_blocks",
"/eth/v1/beacon/blocks/{block_id}",
"/eth/v2/beacon/blocks/{block_id}",
@@ -65,18 +64,6 @@ func (_ *BeaconEndpointFactory) Create(path string) (*apimiddleware.Endpoint, er
case "/eth/v1/beacon/states/{state_id}/randao":
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "epoch"}}
endpoint.GetResponse = &RandaoResponseJson{}
case "/eth/v1/beacon/blocks":
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: setInitialPublishBlockPostRequest,
OnPostDeserializeRequestBodyIntoContainer: preparePublishedBlock,
}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleSubmitBlockSSZ}
case "/eth/v1/beacon/blinded_blocks":
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: setInitialPublishBlindedBlockPostRequest,
OnPostDeserializeRequestBodyIntoContainer: preparePublishedBlindedBlock,
}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleSubmitBlindedBlockSSZ}
case "/eth/v1/beacon/blocks/{block_id}":
endpoint.GetResponse = &BlockResponseJson{}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleGetBeaconBlockSSZ}

View File

@@ -459,12 +459,12 @@ func (s *Service) GetAttestationData(
// SubmitSyncMessage submits the sync committee message to the network.
// It also saves the sync committee message into the pending pool for block inclusion.
func (s *Service) SubmitSyncMessage(ctx context.Context, msg *ethpb.SyncCommitteeMessage) error {
func (s *Service) SubmitSyncMessage(ctx context.Context, msg *ethpb.SyncCommitteeMessage) *RpcError {
errs, ctx := errgroup.WithContext(ctx)
headSyncCommitteeIndices, err := s.HeadFetcher.HeadSyncCommitteeIndices(ctx, msg.ValidatorIndex, msg.Slot)
if err != nil {
return err
return &RpcError{Reason: Internal, Err: errors.Wrap(err, "could not get head sync committee indices")}
}
// Broadcasting and saving message into the pool in parallel. As one fail should not affect another.
// This broadcasts for all subnets.
@@ -477,9 +477,12 @@ func (s *Service) SubmitSyncMessage(ctx context.Context, msg *ethpb.SyncCommitte
}
if err := s.SyncCommitteePool.SaveSyncCommitteeMessage(msg); err != nil {
return err
return &RpcError{Reason: Internal, Err: errors.Wrap(err, "could not save sync committee message")}
}
// Wait for p2p broadcast to complete and return the first error (if any)
return errs.Wait()
if err = errs.Wait(); err != nil {
return &RpcError{Reason: Internal, Err: errors.Wrap(err, "could not broadcast sync committee message")}
}
return nil
}

View File

@@ -41,7 +41,6 @@ go_library(
"//beacon-chain/rpc/eth/helpers:go_default_library",
"//beacon-chain/rpc/eth/shared:go_default_library",
"//beacon-chain/rpc/lookup:go_default_library",
"//beacon-chain/rpc/prysm/v1alpha1/validator:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
@@ -56,7 +55,6 @@ go_library(
"//consensus-types/validator:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/bytesutil:go_default_library",
"//encoding/ssz/detect:go_default_library",
"//network/forks:go_default_library",
"//network/http:go_default_library",
"//proto/eth/v1:go_default_library",
@@ -66,7 +64,6 @@ go_library(
"//runtime/version:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_go_playground_validator_v10//:go_default_library",
"@com_github_gorilla_mux//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
@@ -154,7 +151,6 @@ go_test(
"@com_github_stretchr_testify//mock:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//metadata:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
],

View File

@@ -2,30 +2,21 @@ package beacon
import (
"context"
"strings"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/api"
rpchelpers "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/prysm/v1alpha1/validator"
"github.com/prysmaticlabs/prysm/v4/config/params"
consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/encoding/ssz/detect"
"github.com/prysmaticlabs/prysm/v4/network/forks"
ethpbv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v4/proto/migration"
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"go.opencensus.io/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
)
// GetBlindedBlock retrieves blinded block for given block id.
@@ -34,7 +25,7 @@ func (bs *Server) GetBlindedBlock(ctx context.Context, req *ethpbv1.BlockRequest
defer span.End()
blk, err := bs.Blocker.Block(ctx, req.BlockId)
err = handleGetBlockError(blk, err)
err = rpchelpers.HandleGetBlockError(blk, err)
if err != nil {
return nil, err
}
@@ -100,7 +91,7 @@ func (bs *Server) GetBlindedBlockSSZ(ctx context.Context, req *ethpbv1.BlockRequ
defer span.End()
blk, err := bs.Blocker.Block(ctx, req.BlockId)
err = handleGetBlockError(blk, err)
err = rpchelpers.HandleGetBlockError(blk, err)
if err != nil {
return nil, err
}
@@ -158,217 +149,6 @@ func (bs *Server) GetBlindedBlockSSZ(ctx context.Context, req *ethpbv1.BlockRequ
return nil, status.Errorf(codes.Internal, "Unknown block type %T", blk)
}
// SubmitBlindedBlock instructs the beacon node to use the components of the `SignedBlindedBeaconBlock` to construct
// and publish a `ReadOnlySignedBeaconBlock` by swapping out the `transactions_root` for the corresponding full list of `transactions`.
// The beacon node should broadcast a newly constructed `ReadOnlySignedBeaconBlock` to the beacon network,
// to be included in the beacon chain. The beacon node is not required to validate the signed
// `ReadOnlyBeaconBlock`, and a successful response (20X) only indicates that the broadcast has been
// successful. The beacon node is expected to integrate the new block into its state, and
// therefore validate the block internally, however blocks which fail the validation are still
// broadcast but a different status code is returned (202).
func (bs *Server) SubmitBlindedBlock(ctx context.Context, req *ethpbv2.SignedBlindedBeaconBlockContentsContainer) (*emptypb.Empty, error) {
ctx, span := trace.StartSpan(ctx, "beacon.SubmitBlindedBlock")
defer span.End()
if err := rpchelpers.ValidateSyncGRPC(ctx, bs.SyncChecker, bs.HeadFetcher, bs.TimeFetcher, bs.OptimisticModeFetcher); err != nil {
// We simply return the error because it's already a gRPC error.
return nil, err
}
switch blkContainer := req.Message.(type) {
case *ethpbv2.SignedBlindedBeaconBlockContentsContainer_DenebContents:
if err := bs.submitBlindedDenebContents(ctx, blkContainer.DenebContents); err != nil {
return nil, err
}
case *ethpbv2.SignedBlindedBeaconBlockContentsContainer_CapellaBlock:
if err := bs.submitBlindedCapellaBlock(ctx, blkContainer.CapellaBlock.Message, blkContainer.CapellaBlock.Signature); err != nil {
return nil, err
}
case *ethpbv2.SignedBlindedBeaconBlockContentsContainer_BellatrixBlock:
if err := bs.submitBlindedBellatrixBlock(ctx, blkContainer.BellatrixBlock.Message, blkContainer.BellatrixBlock.Signature); err != nil {
return nil, err
}
case *ethpbv2.SignedBlindedBeaconBlockContentsContainer_Phase0Block:
if err := bs.submitPhase0Block(ctx, blkContainer.Phase0Block.Block, blkContainer.Phase0Block.Signature); err != nil {
return nil, err
}
case *ethpbv2.SignedBlindedBeaconBlockContentsContainer_AltairBlock:
if err := bs.submitAltairBlock(ctx, blkContainer.AltairBlock.Message, blkContainer.AltairBlock.Signature); err != nil {
return nil, err
}
default:
return nil, status.Errorf(codes.InvalidArgument, "Unsupported block container type %T", blkContainer)
}
return &emptypb.Empty{}, nil
}
// SubmitBlindedBlockSSZ instructs the beacon node to use the components of the `SignedBlindedBeaconBlock` to construct
// and publish a `ReadOnlySignedBeaconBlock` by swapping out the `transactions_root` for the corresponding full list of `transactions`.
// The beacon node should broadcast a newly constructed `ReadOnlySignedBeaconBlock` to the beacon network,
// to be included in the beacon chain. The beacon node is not required to validate the signed
// `ReadOnlyBeaconBlock`, and a successful response (20X) only indicates that the broadcast has been
// successful. The beacon node is expected to integrate the new block into its state, and
// therefore validate the block internally, however blocks which fail the validation are still
// broadcast but a different status code is returned (202).
//
// The provided block must be SSZ-serialized.
func (bs *Server) SubmitBlindedBlockSSZ(ctx context.Context, req *ethpbv2.SSZContainer) (*emptypb.Empty, error) {
ctx, span := trace.StartSpan(ctx, "beacon.SubmitBlindedBlockSSZ")
defer span.End()
if err := rpchelpers.ValidateSyncGRPC(ctx, bs.SyncChecker, bs.HeadFetcher, bs.TimeFetcher, bs.OptimisticModeFetcher); err != nil {
// We simply return the error because it's already a gRPC error.
return nil, err
}
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not read"+api.VersionHeader+" header")
}
ver := md.Get(api.VersionHeader)
if len(ver) == 0 {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not read"+api.VersionHeader+" header")
}
schedule := forks.NewOrderedSchedule(params.BeaconConfig())
forkVer, err := schedule.VersionForName(ver[0])
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not determine fork version: %v", err)
}
unmarshaler, err := detect.FromForkVersion(forkVer)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not create unmarshaler: %v", err)
}
switch forkVer {
case bytesutil.ToBytes4(params.BeaconConfig().DenebForkVersion):
blkContent := &ethpbv2.SignedBlindedBeaconBlockContentsDeneb{}
if err := blkContent.UnmarshalSSZ(req.Data); err != nil {
return &emptypb.Empty{}, status.Errorf(codes.InvalidArgument, "Could not unmarshal ssz signed blinded block contents: %v", err)
}
blindedBlock, err := migration.BlindedDenebToV1Alpha1SignedBlock(blkContent.SignedBlindedBlock)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not convert signed blinded block to v1alpha1: %v", err)
}
block, err := blocks.NewSignedBeaconBlock(blindedBlock)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not init block: %v", err)
}
b, err := block.PbBlindedDenebBlock()
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err)
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_BlindedDeneb{
BlindedDeneb: &eth.SignedBlindedBeaconBlockAndBlobsDeneb{
SignedBlindedBlock: b,
SignedBlindedBlobSidecars: migration.SignedBlindedBlobsToV1Alpha1SignedBlindedBlobs(blkContent.SignedBlindedBlobSidecars),
},
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error())
}
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return &emptypb.Empty{}, nil
case bytesutil.ToBytes4(params.BeaconConfig().CapellaForkVersion):
block, err := unmarshaler.UnmarshalBlindedBeaconBlock(req.Data)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not unmarshal request data into block: %v", err)
}
if !block.IsBlinded() {
return nil, status.Error(codes.InvalidArgument, "Submitted block is not blinded")
}
b, err := block.PbBlindedCapellaBlock()
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err)
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_BlindedCapella{
BlindedCapella: b,
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error())
}
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return &emptypb.Empty{}, nil
case bytesutil.ToBytes4(params.BeaconConfig().BellatrixForkVersion):
block, err := unmarshaler.UnmarshalBlindedBeaconBlock(req.Data)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not unmarshal request data into block: %v", err)
}
if !block.IsBlinded() {
return nil, status.Error(codes.InvalidArgument, "Submitted block is not blinded")
}
b, err := block.PbBlindedBellatrixBlock()
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err)
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_BlindedBellatrix{
BlindedBellatrix: b,
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error())
}
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return &emptypb.Empty{}, nil
case bytesutil.ToBytes4(params.BeaconConfig().AltairForkVersion):
block, err := unmarshaler.UnmarshalBlindedBeaconBlock(req.Data)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not unmarshal request data into block: %v", err)
}
b, err := block.PbAltairBlock()
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err)
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Altair{
Altair: b,
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error())
}
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return &emptypb.Empty{}, nil
case bytesutil.ToBytes4(params.BeaconConfig().GenesisForkVersion):
block, err := unmarshaler.UnmarshalBlindedBeaconBlock(req.Data)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not unmarshal request data into block: %v", err)
}
b, err := block.PbPhase0Block()
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err)
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Phase0{
Phase0: b,
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error())
}
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return &emptypb.Empty{}, nil
default:
return &emptypb.Empty{}, status.Errorf(codes.InvalidArgument, "Unsupported fork %s", string(forkVer[:]))
}
}
func getBlindedBlockPhase0(blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.BlindedBlockResponse, error) {
phase0Blk, err := blk.PbPhase0Block()
if err != nil {
@@ -848,73 +628,3 @@ func (bs *Server) getBlindedSSZBlockDeneb(ctx context.Context, blk interfaces.Re
}
return &ethpbv2.SSZContainer{Version: ethpbv2.Version_DENEB, ExecutionOptimistic: isOptimistic, Data: sszData}, nil
}
func (bs *Server) submitBlindedBellatrixBlock(ctx context.Context, blindedBellatrixBlk *ethpbv2.BlindedBeaconBlockBellatrix, sig []byte) error {
b, err := migration.BlindedBellatrixToV1Alpha1SignedBlock(&ethpbv2.SignedBlindedBeaconBlockBellatrix{
Message: blindedBellatrixBlk,
Signature: sig,
})
if err != nil {
return status.Errorf(codes.Internal, "Could not convert block: %v", err)
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_BlindedBellatrix{
BlindedBellatrix: b,
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return status.Error(codes.InvalidArgument, err.Error())
}
return status.Errorf(codes.Internal, "Could not propose blinded block: %v", err)
}
return nil
}
func (bs *Server) submitBlindedCapellaBlock(ctx context.Context, blindedCapellaBlk *ethpbv2.BlindedBeaconBlockCapella, sig []byte) error {
b, err := migration.BlindedCapellaToV1Alpha1SignedBlock(&ethpbv2.SignedBlindedBeaconBlockCapella{
Message: blindedCapellaBlk,
Signature: sig,
})
if err != nil {
return status.Errorf(codes.Internal, "Could not convert block: %v", err)
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_BlindedCapella{
BlindedCapella: b,
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return status.Error(codes.InvalidArgument, err.Error())
}
return status.Errorf(codes.Internal, "Could not propose blinded block: %v", err)
}
return nil
}
func (bs *Server) submitBlindedDenebContents(ctx context.Context, blindedDenebContents *ethpbv2.SignedBlindedBeaconBlockContentsDeneb) error {
blk, err := migration.BlindedDenebToV1Alpha1SignedBlock(&ethpbv2.SignedBlindedBeaconBlockDeneb{
Message: blindedDenebContents.SignedBlindedBlock.Message,
Signature: blindedDenebContents.SignedBlindedBlock.Signature,
})
if err != nil {
return status.Errorf(codes.Internal, "Could not get blinded block: %v", err)
}
blobs := migration.SignedBlindedBlobsToV1Alpha1SignedBlindedBlobs(blindedDenebContents.SignedBlindedBlobSidecars)
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_BlindedDeneb{
BlindedDeneb: &eth.SignedBlindedBeaconBlockAndBlobsDeneb{
SignedBlindedBlock: blk,
SignedBlindedBlobSidecars: blobs,
},
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return status.Error(codes.InvalidArgument, err.Error())
}
return status.Errorf(codes.Internal, "Could not propose blinded block: %v", err)
}
return nil
}

View File

@@ -4,24 +4,17 @@ import (
"context"
"testing"
"github.com/golang/mock/gomock"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/prysmaticlabs/prysm/v4/api"
mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil"
mockSync "github.com/prysmaticlabs/prysm/v4/beacon-chain/sync/initial-sync/testing"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
ethpbv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v4/proto/migration"
"github.com/prysmaticlabs/prysm/v4/testing/assert"
mock2 "github.com/prysmaticlabs/prysm/v4/testing/mock"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
func TestServer_GetBlindedBlock(t *testing.T) {
@@ -347,273 +340,3 @@ func TestServer_GetBlindedBlockSSZ(t *testing.T) {
assert.Equal(t, false, resp.Finalized)
})
}
func TestServer_SubmitBlindedBlockSSZ(t *testing.T) {
ctrl := gomock.NewController(t)
ctx := context.Background()
t.Run("Phase 0", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b := util.NewBeaconBlock()
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "phase0")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlindedBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err)
})
t.Run("Altair", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b := util.NewBeaconBlockAltair()
b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().AltairForkEpoch))
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "altair")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlindedBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err)
})
t.Run("Bellatrix", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b := util.NewBlindedBeaconBlockBellatrix()
b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().BellatrixForkEpoch))
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "bellatrix")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlindedBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err)
})
t.Run("Bellatrix full", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b := util.NewBeaconBlockBellatrix()
b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().BellatrixForkEpoch))
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "bellatrix")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlindedBlockSSZ(sszCtx, blockReq)
assert.NotNil(t, err)
})
t.Run("Capella", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b := util.NewBlindedBeaconBlockCapella()
b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().CapellaForkEpoch))
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "capella")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlindedBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err)
})
t.Run("Capella full", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b := util.NewBeaconBlockCapella()
b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().CapellaForkEpoch))
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "capella")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlindedBlockSSZ(sszCtx, blockReq)
assert.NotNil(t, err)
})
t.Run("Deneb", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b, err := util.NewBlindedBeaconBlockContentsDeneb(fieldparams.MaxBlobsPerBlock)
require.NoError(t, err)
// TODO: replace when deneb fork epoch is known
b.SignedBlindedBlock.Message.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().CapellaForkEpoch))
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "deneb")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlindedBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err)
})
t.Run("Deneb full", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b, err := util.NewBeaconBlockContentsDeneb(fieldparams.MaxBlobsPerBlock)
require.NoError(t, err)
// TODO: replace when deneb fork epoch is known
b.SignedBlock.Message.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().CapellaForkEpoch))
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "deneb")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlindedBlockSSZ(sszCtx, blockReq)
assert.NotNil(t, err)
})
t.Run("sync not ready", func(t *testing.T) {
chainService := &mock.ChainService{}
v1Server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: true},
HeadFetcher: chainService,
TimeFetcher: chainService,
OptimisticModeFetcher: chainService,
}
_, err := v1Server.SubmitBlindedBlockSSZ(context.Background(), nil)
require.ErrorContains(t, "Syncing to latest head", err)
})
}
func TestSubmitBlindedBlock(t *testing.T) {
ctrl := gomock.NewController(t)
t.Run("Phase 0", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
blockReq := &ethpbv2.SignedBlindedBeaconBlockContentsContainer{
Message: &ethpbv2.SignedBlindedBeaconBlockContentsContainer_Phase0Block{Phase0Block: &ethpbv1.SignedBeaconBlock{}},
}
_, err := server.SubmitBlindedBlock(context.Background(), blockReq)
assert.NoError(t, err)
})
t.Run("Altair", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
blockReq := &ethpbv2.SignedBlindedBeaconBlockContentsContainer{
Message: &ethpbv2.SignedBlindedBeaconBlockContentsContainer_AltairBlock{AltairBlock: &ethpbv2.SignedBeaconBlockAltair{}},
}
_, err := server.SubmitBlindedBlock(context.Background(), blockReq)
assert.NoError(t, err)
})
t.Run("Bellatrix", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
blockReq := &ethpbv2.SignedBlindedBeaconBlockContentsContainer{
Message: &ethpbv2.SignedBlindedBeaconBlockContentsContainer_BellatrixBlock{BellatrixBlock: &ethpbv2.SignedBlindedBeaconBlockBellatrix{}},
}
_, err := server.SubmitBlindedBlock(context.Background(), blockReq)
assert.NoError(t, err)
})
t.Run("Capella", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
blockReq := &ethpbv2.SignedBlindedBeaconBlockContentsContainer{
Message: &ethpbv2.SignedBlindedBeaconBlockContentsContainer_CapellaBlock{CapellaBlock: &ethpbv2.SignedBlindedBeaconBlockCapella{}},
}
_, err := server.SubmitBlindedBlock(context.Background(), blockReq)
assert.NoError(t, err)
})
t.Run("Deneb", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
blockReq := &ethpbv2.SignedBlindedBeaconBlockContentsContainer{
Message: &ethpbv2.SignedBlindedBeaconBlockContentsContainer_DenebContents{
DenebContents: &ethpbv2.SignedBlindedBeaconBlockContentsDeneb{
SignedBlindedBlock: &ethpbv2.SignedBlindedBeaconBlockDeneb{},
SignedBlindedBlobSidecars: []*ethpbv2.SignedBlindedBlobSidecar{},
},
},
}
_, err := server.SubmitBlindedBlock(context.Background(), blockReq)
assert.NoError(t, err)
})
t.Run("sync not ready", func(t *testing.T) {
chainService := &mock.ChainService{}
v1Server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: true},
HeadFetcher: chainService,
TimeFetcher: chainService,
OptimisticModeFetcher: chainService,
}
_, err := v1Server.SubmitBlindedBlock(context.Background(), nil)
require.ErrorContains(t, "Syncing to latest head", err)
})
}

View File

@@ -3,26 +3,18 @@ package beacon
import (
"context"
"fmt"
"strings"
"github.com/golang/protobuf/ptypes/empty"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/api"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
rpchelpers "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/prysm/v1alpha1/validator"
"github.com/prysmaticlabs/prysm/v4/config/params"
consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/encoding/ssz/detect"
"github.com/prysmaticlabs/prysm/v4/network/forks"
ethpbv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v4/proto/migration"
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"go.opencensus.io/trace"
@@ -30,7 +22,6 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
)
var (
@@ -79,210 +70,6 @@ func (bs *Server) GetWeakSubjectivity(ctx context.Context, _ *empty.Empty) (*eth
}, nil
}
// SubmitBlock instructs the beacon node to broadcast a newly signed beacon block to the beacon network, to be
// included in the beacon chain. The beacon node is not required to validate the signed ReadOnlyBeaconBlock, and a successful
// response (20X) only indicates that the broadcast has been successful. The beacon node is expected to integrate the
// new block into its state, and therefore validate the block internally, however blocks which fail the validation are
// still broadcast but a different status code is returned (202).
func (bs *Server) SubmitBlock(ctx context.Context, req *ethpbv2.SignedBeaconBlockContentsContainer) (*emptypb.Empty, error) {
ctx, span := trace.StartSpan(ctx, "beacon.SubmitBlock")
defer span.End()
if err := rpchelpers.ValidateSyncGRPC(ctx, bs.SyncChecker, bs.HeadFetcher, bs.TimeFetcher, bs.OptimisticModeFetcher); err != nil {
// We simply return the error because it's already a gRPC error.
return nil, err
}
switch blkContainer := req.Message.(type) {
case *ethpbv2.SignedBeaconBlockContentsContainer_Phase0Block:
if err := bs.submitPhase0Block(ctx, blkContainer.Phase0Block.Block, blkContainer.Phase0Block.Signature); err != nil {
return nil, err
}
case *ethpbv2.SignedBeaconBlockContentsContainer_AltairBlock:
if err := bs.submitAltairBlock(ctx, blkContainer.AltairBlock.Message, blkContainer.AltairBlock.Signature); err != nil {
return nil, err
}
case *ethpbv2.SignedBeaconBlockContentsContainer_BellatrixBlock:
if err := bs.submitBellatrixBlock(ctx, blkContainer.BellatrixBlock.Message, blkContainer.BellatrixBlock.Signature); err != nil {
return nil, err
}
case *ethpbv2.SignedBeaconBlockContentsContainer_CapellaBlock:
if err := bs.submitCapellaBlock(ctx, blkContainer.CapellaBlock.Message, blkContainer.CapellaBlock.Signature); err != nil {
return nil, err
}
case *ethpbv2.SignedBeaconBlockContentsContainer_DenebContents:
if err := bs.submitDenebContents(ctx, blkContainer.DenebContents); err != nil {
return nil, err
}
default:
return nil, status.Errorf(codes.InvalidArgument, "Unsupported block container type %T", blkContainer)
}
return &emptypb.Empty{}, nil
}
// SubmitBlockSSZ instructs the beacon node to broadcast a newly signed beacon block to the beacon network, to be
// included in the beacon chain. The beacon node is not required to validate the signed ReadOnlyBeaconBlock, and a successful
// response (20X) only indicates that the broadcast has been successful. The beacon node is expected to integrate the
// new block into its state, and therefore validate the block internally, however blocks which fail the validation are
// still broadcast but a different status code is returned (202).
//
// The provided block must be SSZ-serialized.
func (bs *Server) SubmitBlockSSZ(ctx context.Context, req *ethpbv2.SSZContainer) (*emptypb.Empty, error) {
ctx, span := trace.StartSpan(ctx, "beacon.SubmitBlockSSZ")
defer span.End()
if err := rpchelpers.ValidateSyncGRPC(ctx, bs.SyncChecker, bs.HeadFetcher, bs.TimeFetcher, bs.OptimisticModeFetcher); err != nil {
// We simply return the error because it's already a gRPC error.
return nil, err
}
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not read "+api.VersionHeader+" header")
}
ver := md.Get(api.VersionHeader)
if len(ver) == 0 {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not read "+api.VersionHeader+" header")
}
schedule := forks.NewOrderedSchedule(params.BeaconConfig())
forkVer, err := schedule.VersionForName(ver[0])
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not determine fork version: %v", err)
}
unmarshaler, err := detect.FromForkVersion(forkVer)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not create unmarshaler: %v", err)
}
switch forkVer {
case bytesutil.ToBytes4(params.BeaconConfig().DenebForkVersion):
blkContent := &ethpbv2.SignedBeaconBlockContentsDeneb{}
if err := blkContent.UnmarshalSSZ(req.Data); err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not unmarshal ssz block contents: %v", err)
}
v1block, err := migration.DenebToV1Alpha1SignedBlock(blkContent.SignedBlock)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Submitted block is not valid: %v", err)
}
block, err := blocks.NewSignedBeaconBlock(v1block)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not init block: %v", err)
}
b, err := block.PbDenebBlock()
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err)
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Deneb{
Deneb: &eth.SignedBeaconBlockAndBlobsDeneb{
Block: b,
Blobs: migration.SignedBlobsToV1Alpha1SignedBlobs(blkContent.SignedBlobSidecars),
},
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error())
}
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return &emptypb.Empty{}, nil
case bytesutil.ToBytes4(params.BeaconConfig().CapellaForkVersion):
block, err := unmarshaler.UnmarshalBeaconBlock(req.Data)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not unmarshal request data into block: %v", err)
}
if block.IsBlinded() {
return nil, status.Error(codes.InvalidArgument, "Submitted block is blinded")
}
b, err := block.PbCapellaBlock()
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err)
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Capella{
Capella: b,
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error())
}
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return &emptypb.Empty{}, nil
case bytesutil.ToBytes4(params.BeaconConfig().BellatrixForkVersion):
block, err := unmarshaler.UnmarshalBeaconBlock(req.Data)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not unmarshal request data into block: %v", err)
}
if block.IsBlinded() {
return nil, status.Error(codes.InvalidArgument, "Submitted block is blinded")
}
b, err := block.PbBellatrixBlock()
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err)
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Bellatrix{
Bellatrix: b,
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error())
}
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return &emptypb.Empty{}, nil
case bytesutil.ToBytes4(params.BeaconConfig().AltairForkVersion):
block, err := unmarshaler.UnmarshalBeaconBlock(req.Data)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not unmarshal request data into block: %v", err)
}
b, err := block.PbAltairBlock()
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err)
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Altair{
Altair: b,
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error())
}
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return &emptypb.Empty{}, nil
case bytesutil.ToBytes4(params.BeaconConfig().GenesisForkVersion):
block, err := unmarshaler.UnmarshalBeaconBlock(req.Data)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not unmarshal request data into block: %v", err)
}
b, err := block.PbPhase0Block()
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err)
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Phase0{
Phase0: b,
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error())
}
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return &emptypb.Empty{}, nil
default:
return &emptypb.Empty{}, status.Errorf(codes.InvalidArgument, "Unsupported fork %s", string(forkVer[:]))
}
}
// GetBlock retrieves block details for given block ID.
// DEPRECATED: please use GetBlockV2 instead
func (bs *Server) GetBlock(ctx context.Context, req *ethpbv1.BlockRequest) (*ethpbv1.BlockResponse, error) {
@@ -290,7 +77,7 @@ func (bs *Server) GetBlock(ctx context.Context, req *ethpbv1.BlockRequest) (*eth
defer span.End()
blk, err := bs.Blocker.Block(ctx, req.BlockId)
err = handleGetBlockError(blk, err)
err = rpchelpers.HandleGetBlockError(blk, err)
if err != nil {
return nil, err
}
@@ -314,7 +101,7 @@ func (bs *Server) GetBlockSSZ(ctx context.Context, req *ethpbv1.BlockRequest) (*
defer span.End()
blk, err := bs.Blocker.Block(ctx, req.BlockId)
err = handleGetBlockError(blk, err)
err = rpchelpers.HandleGetBlockError(blk, err)
if err != nil {
return nil, err
}
@@ -336,7 +123,7 @@ func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) (
defer span.End()
blk, err := bs.Blocker.Block(ctx, req.BlockId)
err = handleGetBlockError(blk, err)
err = rpchelpers.HandleGetBlockError(blk, err)
if err != nil {
return nil, err
}
@@ -404,7 +191,7 @@ func (bs *Server) GetBlockSSZV2(ctx context.Context, req *ethpbv2.BlockRequestV2
defer span.End()
blk, err := bs.Blocker.Block(ctx, req.BlockId)
err = handleGetBlockError(blk, err)
err = rpchelpers.HandleGetBlockError(blk, err)
if err != nil {
return nil, err
}
@@ -469,7 +256,7 @@ func (bs *Server) ListBlockAttestations(ctx context.Context, req *ethpbv1.BlockR
defer span.End()
blk, err := bs.Blocker.Block(ctx, req.BlockId)
err = handleGetBlockError(blk, err)
err = rpchelpers.HandleGetBlockError(blk, err)
if err != nil {
return nil, err
}
@@ -495,19 +282,6 @@ func (bs *Server) ListBlockAttestations(ctx context.Context, req *ethpbv1.BlockR
}, nil
}
func handleGetBlockError(blk interfaces.ReadOnlySignedBeaconBlock, err error) error {
if invalidBlockIdErr, ok := err.(*lookup.BlockIdParseError); ok {
return status.Errorf(codes.InvalidArgument, "Invalid block ID: %v", invalidBlockIdErr)
}
if err != nil {
return status.Errorf(codes.Internal, "Could not get block from block ID: %v", err)
}
if err := blocks.BeaconBlockIsNil(blk); err != nil {
return status.Errorf(codes.NotFound, "Could not find requested block: %v", err)
}
return nil
}
func getBlockPhase0(blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.BlockResponseV2, error) {
phase0Blk, err := blk.PbPhase0Block()
if err != nil {
@@ -1033,105 +807,3 @@ func (bs *Server) getSSZBlockDeneb(ctx context.Context, blk interfaces.ReadOnlyS
}
return &ethpbv2.SSZContainer{Version: ethpbv2.Version_DENEB, ExecutionOptimistic: isOptimistic, Data: sszData}, nil
}
func (bs *Server) submitPhase0Block(ctx context.Context, phase0Blk *ethpbv1.BeaconBlock, sig []byte) error {
v1alpha1Blk, err := migration.V1ToV1Alpha1SignedBlock(&ethpbv1.SignedBeaconBlock{Block: phase0Blk, Signature: sig})
if err != nil {
return status.Errorf(codes.InvalidArgument, "Could not convert block: %v", err)
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Phase0{
Phase0: v1alpha1Blk,
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return status.Error(codes.InvalidArgument, err.Error())
}
return status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return nil
}
func (bs *Server) submitAltairBlock(ctx context.Context, altairBlk *ethpbv2.BeaconBlockAltair, sig []byte) error {
v1alpha1Blk, err := migration.AltairToV1Alpha1SignedBlock(&ethpbv2.SignedBeaconBlockAltair{Message: altairBlk, Signature: sig})
if err != nil {
return status.Errorf(codes.InvalidArgument, "Could not convert block %v", err)
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Altair{
Altair: v1alpha1Blk,
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return status.Error(codes.InvalidArgument, err.Error())
}
return status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return nil
}
func (bs *Server) submitBellatrixBlock(ctx context.Context, bellatrixBlk *ethpbv2.BeaconBlockBellatrix, sig []byte) error {
v1alpha1Blk, err := migration.BellatrixToV1Alpha1SignedBlock(&ethpbv2.SignedBeaconBlockBellatrix{Message: bellatrixBlk, Signature: sig})
if err != nil {
return status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block")
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Bellatrix{
Bellatrix: v1alpha1Blk,
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return status.Error(codes.InvalidArgument, err.Error())
}
return status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return nil
}
func (bs *Server) submitCapellaBlock(ctx context.Context, capellaBlk *ethpbv2.BeaconBlockCapella, sig []byte) error {
v1alpha1Blk, err := migration.CapellaToV1Alpha1SignedBlock(&ethpbv2.SignedBeaconBlockCapella{Message: capellaBlk, Signature: sig})
if err != nil {
return status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block")
}
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Capella{
Capella: v1alpha1Blk,
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return status.Error(codes.InvalidArgument, err.Error())
}
return status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return nil
}
func (bs *Server) submitDenebContents(ctx context.Context, denebContents *ethpbv2.SignedBeaconBlockContentsDeneb) error {
blk, err := migration.DenebToV1Alpha1SignedBlock(&ethpbv2.SignedBeaconBlockDeneb{
Message: denebContents.SignedBlock.Message,
Signature: denebContents.SignedBlock.Signature,
})
if err != nil {
return status.Errorf(codes.Internal, "Could not get block: %v", err)
}
blobs := migration.SignedBlobsToV1Alpha1SignedBlobs(denebContents.SignedBlobSidecars)
_, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Deneb{
Deneb: &eth.SignedBeaconBlockAndBlobsDeneb{
Block: blk,
Blobs: blobs,
},
},
})
if err != nil {
if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) {
return status.Error(codes.InvalidArgument, err.Error())
}
return status.Errorf(codes.Internal, "Could not propose block: %v", err)
}
return nil
}

View File

@@ -4,16 +4,11 @@ import (
"context"
"testing"
"github.com/golang/mock/gomock"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/v4/api"
mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil"
mockSync "github.com/prysmaticlabs/prysm/v4/beacon-chain/sync/initial-sync/testing"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
@@ -23,11 +18,9 @@ import (
"github.com/prysmaticlabs/prysm/v4/proto/migration"
ethpbalpha "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/assert"
mock2 "github.com/prysmaticlabs/prysm/v4/testing/mock"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
func fillDBTestBlocks(ctx context.Context, t *testing.T, beaconDB db.Database) (*ethpbalpha.SignedBeaconBlock, []*ethpbalpha.BeaconBlockContainer) {
@@ -66,276 +59,6 @@ func fillDBTestBlocks(ctx context.Context, t *testing.T, beaconDB db.Database) (
return genBlk, blkContainers
}
func TestServer_SubmitBlock(t *testing.T) {
ctrl := gomock.NewController(t)
t.Run("Phase 0", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
blockReq := &ethpbv2.SignedBeaconBlockContentsContainer{
Message: &ethpbv2.SignedBeaconBlockContentsContainer_Phase0Block{Phase0Block: &ethpbv1.SignedBeaconBlock{}},
}
_, err := server.SubmitBlock(context.Background(), blockReq)
assert.NoError(t, err)
})
t.Run("Altair", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
blockReq := &ethpbv2.SignedBeaconBlockContentsContainer{
Message: &ethpbv2.SignedBeaconBlockContentsContainer_AltairBlock{AltairBlock: &ethpbv2.SignedBeaconBlockAltair{}},
}
_, err := server.SubmitBlock(context.Background(), blockReq)
assert.NoError(t, err)
})
t.Run("Bellatrix", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
blockReq := &ethpbv2.SignedBeaconBlockContentsContainer{
Message: &ethpbv2.SignedBeaconBlockContentsContainer_BellatrixBlock{BellatrixBlock: &ethpbv2.SignedBeaconBlockBellatrix{}},
}
_, err := server.SubmitBlock(context.Background(), blockReq)
assert.NoError(t, err)
})
t.Run("Capella", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
blockReq := &ethpbv2.SignedBeaconBlockContentsContainer{
Message: &ethpbv2.SignedBeaconBlockContentsContainer_CapellaBlock{CapellaBlock: &ethpbv2.SignedBeaconBlockCapella{}},
}
_, err := server.SubmitBlock(context.Background(), blockReq)
assert.NoError(t, err)
})
t.Run("Deneb", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
blockReq := &ethpbv2.SignedBeaconBlockContentsContainer{
Message: &ethpbv2.SignedBeaconBlockContentsContainer_DenebContents{
DenebContents: &ethpbv2.SignedBeaconBlockContentsDeneb{
SignedBlock: &ethpbv2.SignedBeaconBlockDeneb{},
SignedBlobSidecars: []*ethpbv2.SignedBlobSidecar{},
},
},
}
_, err := server.SubmitBlock(context.Background(), blockReq)
assert.NoError(t, err)
})
t.Run("sync not ready", func(t *testing.T) {
chainService := &mock.ChainService{}
v1Server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: true},
HeadFetcher: chainService,
TimeFetcher: chainService,
OptimisticModeFetcher: chainService,
}
_, err := v1Server.SubmitBlock(context.Background(), nil)
require.ErrorContains(t, "Syncing to latest head", err)
})
}
func TestServer_SubmitBlockSSZ(t *testing.T) {
ctrl := gomock.NewController(t)
ctx := context.Background()
t.Run("Phase 0", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b := util.NewBeaconBlock()
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "phase0")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err)
})
t.Run("Altair", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b := util.NewBeaconBlockAltair()
b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().AltairForkEpoch))
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "altair")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err)
})
t.Run("Bellatrix", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b := util.NewBeaconBlockBellatrix()
b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().BellatrixForkEpoch))
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "bellatrix")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err)
})
t.Run("Bellatrix blinded", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b := util.NewBlindedBeaconBlockBellatrix()
b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().BellatrixForkEpoch))
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "bellatrix")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlockSSZ(sszCtx, blockReq)
assert.NotNil(t, err)
})
t.Run("Capella", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b := util.NewBeaconBlockCapella()
b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().CapellaForkEpoch))
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "capella")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err)
})
t.Run("Capella blinded", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b := util.NewBlindedBeaconBlockCapella()
b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().CapellaForkEpoch))
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "capella")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlockSSZ(sszCtx, blockReq)
assert.NotNil(t, err)
})
t.Run("Deneb", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any())
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b, err := util.NewBeaconBlockContentsDeneb(fieldparams.MaxBlobsPerBlock)
require.NoError(t, err)
// TODO: update to deneb fork epoch
b.SignedBlock.Message.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().CapellaForkEpoch))
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "deneb")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err)
})
t.Run("Deneb blinded", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
b, err := util.NewBlindedBeaconBlockContentsDeneb(fieldparams.MaxBlobsPerBlock)
require.NoError(t, err)
// TODO: update to deneb fork epoch
b.SignedBlindedBlock.Message.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().CapellaForkEpoch))
ssz, err := b.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: ssz,
}
md := metadata.MD{}
md.Set(api.VersionHeader, "deneb")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = server.SubmitBlockSSZ(sszCtx, blockReq)
assert.NotNil(t, err)
})
t.Run("sync not ready", func(t *testing.T) {
chainService := &mock.ChainService{}
v1Server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: true},
HeadFetcher: chainService,
TimeFetcher: chainService,
OptimisticModeFetcher: chainService,
}
_, err := v1Server.SubmitBlockSSZ(context.Background(), nil)
require.ErrorContains(t, "Syncing to latest head", err)
})
}
func TestServer_GetBlock(t *testing.T) {
ctx := context.Background()
b := util.NewBeaconBlock()

View File

@@ -141,7 +141,7 @@ func TestGetSpec(t *testing.T) {
resp, err := server.GetSpec(context.Background(), &emptypb.Empty{})
require.NoError(t, err)
assert.Equal(t, 111, len(resp.Data))
assert.Equal(t, 112, len(resp.Data))
for k, v := range resp.Data {
switch k {
case "CONFIG_NAME":
@@ -380,6 +380,8 @@ func TestGetSpec(t *testing.T) {
assert.Equal(t, "20", v)
case "REORG_PARENT_WEIGHT_THRESHOLD":
assert.Equal(t, "160", v)
case "MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT":
assert.Equal(t, "8", v)
case "SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY":
default:
t.Errorf("Incorrect key: %s", k)

View File

@@ -38,6 +38,28 @@ const (
broadcastValidationConsensusAndEquivocation = "consensus_and_equivocation"
)
// PublishBlindedBlock instructs the beacon node to use the components of the `SignedBlindedBeaconBlock` to construct
// and publish a SignedBeaconBlock by swapping out the transactions_root for the corresponding full list of `transactions`.
// The beacon node should broadcast a newly constructed SignedBeaconBlock to the beacon network, to be included in the
// beacon chain. The beacon node is not required to validate the signed BeaconBlock, and a successful response (20X)
// only indicates that the broadcast has been successful. The beacon node is expected to integrate the new block into
// its state, and therefore validate the block internally, however blocks which fail the validation are still broadcast
// but a different status code is returned (202). Pre-Bellatrix, this endpoint will accept a SignedBeaconBlock. After
// Deneb, this additionally instructs the beacon node to broadcast all given signed blobs.
func (s *Server) PublishBlindedBlock(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.PublishBlindedBlock")
defer span.End()
if shared.IsSyncing(r.Context(), w, s.SyncChecker, s.HeadFetcher, s.TimeFetcher, s.OptimisticModeFetcher) {
return
}
isSSZ := http2.SszRequested(r)
if isSSZ {
s.publishBlindedBlockSSZ(ctx, w, r)
} else {
s.publishBlindedBlock(ctx, w, r)
}
}
// PublishBlindedBlockV2 instructs the beacon node to use the components of the `SignedBlindedBeaconBlock` to construct and publish a
// `SignedBeaconBlock` by swapping out the `transactions_root` for the corresponding full list of `transactions`.
// The beacon node should broadcast a newly constructed `SignedBeaconBlock` to the beacon network,
@@ -46,23 +68,24 @@ const (
// successful. The beacon node is expected to integrate the new block into its state, and
// therefore validate the block internally, however blocks which fail the validation are still
// broadcast but a different status code is returned (202). Pre-Bellatrix, this endpoint will accept
// a `SignedBeaconBlock`. The broadcast behaviour may be adjusted via the `broadcast_validation`
// a `SignedBeaconBlock`. After Deneb, this additionally instructs the beacon node to broadcast all given signed blobs.
// The broadcast behaviour may be adjusted via the `broadcast_validation`
// query parameter.
func (bs *Server) PublishBlindedBlockV2(w http.ResponseWriter, r *http.Request) {
func (s *Server) PublishBlindedBlockV2(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.PublishBlindedBlockV2")
defer span.End()
if shared.IsSyncing(r.Context(), w, bs.SyncChecker, bs.HeadFetcher, bs.TimeFetcher, bs.OptimisticModeFetcher) {
if shared.IsSyncing(r.Context(), w, s.SyncChecker, s.HeadFetcher, s.TimeFetcher, s.OptimisticModeFetcher) {
return
}
isSSZ, err := http2.SszRequested(r)
if isSSZ && err == nil {
publishBlindedBlockV2SSZ(ctx, bs, w, r)
isSSZ := http2.SszRequested(r)
if isSSZ {
s.publishBlindedBlockSSZ(ctx, w, r)
} else {
publishBlindedBlockV2(ctx, bs, w, r)
s.publishBlindedBlock(ctx, w, r)
}
}
func publishBlindedBlockV2SSZ(ctx context.Context, bs *Server, w http.ResponseWriter, r *http.Request) {
func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http2.HandleError(w, "Could not read request body: "+err.Error(), http.StatusInternalServerError)
@@ -75,11 +98,11 @@ func publishBlindedBlockV2SSZ(ctx context.Context, bs *Server, w http.ResponseWr
BlindedDeneb: denebBlockContents,
},
}
if err = bs.validateBroadcast(ctx, r, genericBlock); err != nil {
if err = s.validateBroadcast(ctx, r, genericBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, genericBlock)
s.proposeBlock(ctx, w, genericBlock)
return
}
capellaBlock := &eth.SignedBlindedBeaconBlockCapella{}
@@ -89,11 +112,11 @@ func publishBlindedBlockV2SSZ(ctx context.Context, bs *Server, w http.ResponseWr
BlindedCapella: capellaBlock,
},
}
if err = bs.validateBroadcast(ctx, r, genericBlock); err != nil {
if err = s.validateBroadcast(ctx, r, genericBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, genericBlock)
s.proposeBlock(ctx, w, genericBlock)
return
}
bellatrixBlock := &eth.SignedBlindedBeaconBlockBellatrix{}
@@ -103,11 +126,11 @@ func publishBlindedBlockV2SSZ(ctx context.Context, bs *Server, w http.ResponseWr
BlindedBellatrix: bellatrixBlock,
},
}
if err = bs.validateBroadcast(ctx, r, genericBlock); err != nil {
if err = s.validateBroadcast(ctx, r, genericBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, genericBlock)
s.proposeBlock(ctx, w, genericBlock)
return
}
@@ -119,11 +142,11 @@ func publishBlindedBlockV2SSZ(ctx context.Context, bs *Server, w http.ResponseWr
Altair: altairBlock,
},
}
if err = bs.validateBroadcast(ctx, r, genericBlock); err != nil {
if err = s.validateBroadcast(ctx, r, genericBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, genericBlock)
s.proposeBlock(ctx, w, genericBlock)
return
}
phase0Block := &eth.SignedBeaconBlock{}
@@ -133,17 +156,17 @@ func publishBlindedBlockV2SSZ(ctx context.Context, bs *Server, w http.ResponseWr
Phase0: phase0Block,
},
}
if err = bs.validateBroadcast(ctx, r, genericBlock); err != nil {
if err = s.validateBroadcast(ctx, r, genericBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, genericBlock)
s.proposeBlock(ctx, w, genericBlock)
return
}
http2.HandleError(w, "Body does not represent a valid block type", http.StatusBadRequest)
}
func publishBlindedBlockV2(ctx context.Context, bs *Server, w http.ResponseWriter, r *http.Request) {
func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http2.HandleError(w, "Could not read request body", http.StatusInternalServerError)
@@ -155,11 +178,11 @@ func publishBlindedBlockV2(ctx context.Context, bs *Server, w http.ResponseWrite
if err = unmarshalStrict(body, &denebBlockContents); err == nil {
consensusBlock, err := denebBlockContents.ToGeneric()
if err == nil {
if err = bs.validateBroadcast(ctx, r, consensusBlock); err != nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, consensusBlock)
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Deneb) {
@@ -171,11 +194,11 @@ func publishBlindedBlockV2(ctx context.Context, bs *Server, w http.ResponseWrite
if err = unmarshalStrict(body, &capellaBlock); err == nil {
consensusBlock, err := capellaBlock.ToGeneric()
if err == nil {
if err = bs.validateBroadcast(ctx, r, consensusBlock); err != nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, consensusBlock)
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Capella) {
@@ -187,11 +210,11 @@ func publishBlindedBlockV2(ctx context.Context, bs *Server, w http.ResponseWrite
if err = unmarshalStrict(body, &bellatrixBlock); err == nil {
consensusBlock, err := bellatrixBlock.ToGeneric()
if err == nil {
if err = bs.validateBroadcast(ctx, r, consensusBlock); err != nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, consensusBlock)
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Bellatrix) {
@@ -202,11 +225,11 @@ func publishBlindedBlockV2(ctx context.Context, bs *Server, w http.ResponseWrite
if err = unmarshalStrict(body, &altairBlock); err == nil {
consensusBlock, err := altairBlock.ToGeneric()
if err == nil {
if err = bs.validateBroadcast(ctx, r, consensusBlock); err != nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, consensusBlock)
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Altair) {
@@ -217,11 +240,11 @@ func publishBlindedBlockV2(ctx context.Context, bs *Server, w http.ResponseWrite
if err = unmarshalStrict(body, &phase0Block); err == nil {
consensusBlock, err := phase0Block.ToGeneric()
if err == nil {
if err = bs.validateBroadcast(ctx, r, consensusBlock); err != nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, consensusBlock)
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Phase0) {
@@ -234,29 +257,52 @@ func publishBlindedBlockV2(ctx context.Context, bs *Server, w http.ResponseWrite
http2.HandleError(w, "Body does not represent a valid block type: "+blockVersionError, http.StatusBadRequest)
}
// PublishBlock instructs the beacon node to broadcast a newly signed beacon block to the beacon network,
// to be included in the beacon chain. A success response (20x) indicates that the block
// passed gossip validation and was successfully broadcast onto the network.
// The beacon node is also expected to integrate the block into state, but may broadcast it
// before doing so, so as to aid timely delivery of the block. Should the block fail full
// validation, a separate success response code (202) is used to indicate that the block was
// successfully broadcast but failed integration. After Deneb, this additionally instructs the
// beacon node to broadcast all given signed blobs.
func (s *Server) PublishBlock(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.PublishBlock")
defer span.End()
if shared.IsSyncing(r.Context(), w, s.SyncChecker, s.HeadFetcher, s.TimeFetcher, s.OptimisticModeFetcher) {
return
}
isSSZ := http2.SszRequested(r)
if isSSZ {
s.publishBlockSSZ(ctx, w, r)
} else {
s.publishBlock(ctx, w, r)
}
}
// PublishBlockV2 instructs the beacon node to broadcast a newly signed beacon block to the beacon network,
// to be included in the beacon chain. A success response (20x) indicates that the block
// passed gossip validation and was successfully broadcast onto the network.
// The beacon node is also expected to integrate the block into the state, but may broadcast it
// before doing so, so as to aid timely delivery of the block. Should the block fail full
// validation, a separate success response code (202) is used to indicate that the block was
// successfully broadcast but failed integration. The broadcast behaviour may be adjusted via the
// successfully broadcast but failed integration. After Deneb, this additionally instructs the beacon node to
// broadcast all given signed blobs. The broadcast behaviour may be adjusted via the
// `broadcast_validation` query parameter.
func (bs *Server) PublishBlockV2(w http.ResponseWriter, r *http.Request) {
func (s *Server) PublishBlockV2(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.PublishBlockV2")
defer span.End()
if shared.IsSyncing(r.Context(), w, bs.SyncChecker, bs.HeadFetcher, bs.TimeFetcher, bs.OptimisticModeFetcher) {
if shared.IsSyncing(r.Context(), w, s.SyncChecker, s.HeadFetcher, s.TimeFetcher, s.OptimisticModeFetcher) {
return
}
isSSZ, err := http2.SszRequested(r)
if isSSZ && err == nil {
publishBlockV2SSZ(ctx, bs, w, r)
isSSZ := http2.SszRequested(r)
if isSSZ {
s.publishBlockSSZ(ctx, w, r)
} else {
publishBlockV2(ctx, bs, w, r)
s.publishBlock(ctx, w, r)
}
}
func publishBlockV2SSZ(ctx context.Context, bs *Server, w http.ResponseWriter, r *http.Request) {
func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http2.HandleError(w, "Could not read request body", http.StatusInternalServerError)
@@ -269,11 +315,11 @@ func publishBlockV2SSZ(ctx context.Context, bs *Server, w http.ResponseWriter, r
Deneb: denebBlockContents,
},
}
if err = bs.validateBroadcast(ctx, r, genericBlock); err != nil {
if err = s.validateBroadcast(ctx, r, genericBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, genericBlock)
s.proposeBlock(ctx, w, genericBlock)
return
}
capellaBlock := &eth.SignedBeaconBlockCapella{}
@@ -283,11 +329,11 @@ func publishBlockV2SSZ(ctx context.Context, bs *Server, w http.ResponseWriter, r
Capella: capellaBlock,
},
}
if err = bs.validateBroadcast(ctx, r, genericBlock); err != nil {
if err = s.validateBroadcast(ctx, r, genericBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, genericBlock)
s.proposeBlock(ctx, w, genericBlock)
return
}
bellatrixBlock := &eth.SignedBeaconBlockBellatrix{}
@@ -297,11 +343,11 @@ func publishBlockV2SSZ(ctx context.Context, bs *Server, w http.ResponseWriter, r
Bellatrix: bellatrixBlock,
},
}
if err = bs.validateBroadcast(ctx, r, genericBlock); err != nil {
if err = s.validateBroadcast(ctx, r, genericBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, genericBlock)
s.proposeBlock(ctx, w, genericBlock)
return
}
altairBlock := &eth.SignedBeaconBlockAltair{}
@@ -311,11 +357,11 @@ func publishBlockV2SSZ(ctx context.Context, bs *Server, w http.ResponseWriter, r
Altair: altairBlock,
},
}
if err = bs.validateBroadcast(ctx, r, genericBlock); err != nil {
if err = s.validateBroadcast(ctx, r, genericBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, genericBlock)
s.proposeBlock(ctx, w, genericBlock)
return
}
phase0Block := &eth.SignedBeaconBlock{}
@@ -325,17 +371,17 @@ func publishBlockV2SSZ(ctx context.Context, bs *Server, w http.ResponseWriter, r
Phase0: phase0Block,
},
}
if err = bs.validateBroadcast(ctx, r, genericBlock); err != nil {
if err = s.validateBroadcast(ctx, r, genericBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, genericBlock)
s.proposeBlock(ctx, w, genericBlock)
return
}
http2.HandleError(w, "Body does not represent a valid block type", http.StatusBadRequest)
}
func publishBlockV2(ctx context.Context, bs *Server, w http.ResponseWriter, r *http.Request) {
func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http2.HandleError(w, "Could not read request body", http.StatusInternalServerError)
@@ -347,11 +393,11 @@ func publishBlockV2(ctx context.Context, bs *Server, w http.ResponseWriter, r *h
if err = unmarshalStrict(body, &denebBlockContents); err == nil {
consensusBlock, err := denebBlockContents.ToGeneric()
if err == nil {
if err = bs.validateBroadcast(ctx, r, consensusBlock); err != nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, consensusBlock)
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Deneb) {
@@ -362,11 +408,11 @@ func publishBlockV2(ctx context.Context, bs *Server, w http.ResponseWriter, r *h
if err = unmarshalStrict(body, &capellaBlock); err == nil {
consensusBlock, err := capellaBlock.ToGeneric()
if err == nil {
if err = bs.validateBroadcast(ctx, r, consensusBlock); err != nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, consensusBlock)
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Capella) {
@@ -377,11 +423,11 @@ func publishBlockV2(ctx context.Context, bs *Server, w http.ResponseWriter, r *h
if err = unmarshalStrict(body, &bellatrixBlock); err == nil {
consensusBlock, err := bellatrixBlock.ToGeneric()
if err == nil {
if err = bs.validateBroadcast(ctx, r, consensusBlock); err != nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, consensusBlock)
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Bellatrix) {
@@ -392,11 +438,11 @@ func publishBlockV2(ctx context.Context, bs *Server, w http.ResponseWriter, r *h
if err = unmarshalStrict(body, &altairBlock); err == nil {
consensusBlock, err := altairBlock.ToGeneric()
if err == nil {
if err = bs.validateBroadcast(ctx, r, consensusBlock); err != nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, consensusBlock)
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Altair) {
@@ -407,11 +453,11 @@ func publishBlockV2(ctx context.Context, bs *Server, w http.ResponseWriter, r *h
if err = unmarshalStrict(body, &phase0Block); err == nil {
consensusBlock, err := phase0Block.ToGeneric()
if err == nil {
if err = bs.validateBroadcast(ctx, r, consensusBlock); err != nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
bs.proposeBlock(ctx, w, consensusBlock)
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Phase0) {
@@ -746,10 +792,10 @@ func (*Server) GetDepositContract(w http.ResponseWriter, r *http.Request) {
http2.WriteJson(w, &DepositContractResponse{
Data: &struct {
ChainId uint64 `json:"chain_id"`
ChainId string `json:"chain_id"`
Address string `json:"address"`
}{
ChainId: params.BeaconConfig().DepositChainID,
ChainId: strconv.FormatUint(params.BeaconConfig().DepositChainID, 10),
Address: params.BeaconConfig().DepositContractAddress,
},
})

View File

@@ -8,12 +8,12 @@ import (
"strconv"
"strings"
"github.com/go-playground/validator/v10"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/operation"
corehelpers "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/core"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
@@ -89,11 +89,6 @@ func (s *Server) SubmitAttestations(w http.ResponseWriter, r *http.Request) {
http2.HandleError(w, "No data submitted", http.StatusBadRequest)
return
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
var validAttestations []*ethpbalpha.Attestation
var attFailures []*shared.IndexedVerificationFailure
@@ -209,11 +204,6 @@ func (s *Server) SubmitVoluntaryExit(w http.ResponseWriter, r *http.Request) {
http2.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
return
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
exit, err := req.ToConsensus()
if err != nil {
@@ -292,8 +282,8 @@ func (s *Server) SubmitSyncCommitteeSignatures(w http.ResponseWriter, r *http.Re
}
for _, msg := range validMessages {
if err = s.CoreService.SubmitSyncMessage(ctx, msg); err != nil {
http2.HandleError(w, "Could not submit message: "+err.Error(), http.StatusInternalServerError)
if rpcerr := s.CoreService.SubmitSyncMessage(ctx, msg); rpcerr != nil {
http2.HandleError(w, "Could not submit message: "+rpcerr.Err.Error(), core.ErrorReasonToHTTP(rpcerr.Reason))
return
}
}

View File

@@ -43,6 +43,518 @@ import (
"github.com/stretchr/testify/mock"
)
func TestPublishBlock(t *testing.T) {
ctrl := gomock.NewController(t)
t.Run("Phase 0", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Phase0)
converted, err := shared.BeaconBlockFromConsensus(block.Phase0.Block)
require.NoError(t, err)
var signedblock *shared.SignedBeaconBlock
err = json.Unmarshal([]byte(rpctesting.Phase0Block), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.Phase0Block)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Altair", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Altair)
converted, err := shared.BeaconBlockAltairFromConsensus(block.Altair.Block)
require.NoError(t, err)
var signedblock *shared.SignedBeaconBlockAltair
err = json.Unmarshal([]byte(rpctesting.AltairBlock), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.AltairBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Bellatrix", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Bellatrix)
converted, err := shared.BeaconBlockBellatrixFromConsensus(block.Bellatrix.Block)
require.NoError(t, err)
var signedblock *shared.SignedBeaconBlockBellatrix
err = json.Unmarshal([]byte(rpctesting.BellatrixBlock), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BellatrixBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Capella", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Capella)
converted, err := shared.BeaconBlockCapellaFromConsensus(block.Capella.Block)
require.NoError(t, err)
var signedblock *shared.SignedBeaconBlockCapella
err = json.Unmarshal([]byte(rpctesting.CapellaBlock), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.CapellaBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Deneb", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Deneb)
converted, err := shared.BeaconBlockDenebFromConsensus(block.Deneb.Block.Block)
require.NoError(t, err)
var signedblock *shared.SignedBeaconBlockContentsDeneb
err = json.Unmarshal([]byte(rpctesting.DenebBlockContents), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.SignedBlock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.DenebBlockContents)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("invalid block", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BlindedBellatrixBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlock(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
assert.Equal(t, true, strings.Contains(writer.Body.String(), "please add the api header"))
assert.Equal(t, true, strings.Contains(writer.Body.String(), "Body does not represent a valid block type"))
})
t.Run("invalid block with version header", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BadCapellaBlock)))
request.Header.Set(api.VersionHeader, version.String(version.Capella))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlock(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
body := writer.Body.String()
assert.Equal(t, true, strings.Contains(body, "Body does not represent a valid block type"))
assert.Equal(t, true, strings.Contains(body, fmt.Sprintf("could not decode %s request body into consensus block:", version.String(version.Capella))))
})
t.Run("syncing", func(t *testing.T) {
chainService := &chainMock.ChainService{}
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: true},
HeadFetcher: chainService,
TimeFetcher: chainService,
OptimisticModeFetcher: chainService,
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte("foo")))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlock(writer, request)
assert.Equal(t, http.StatusServiceUnavailable, writer.Code)
assert.Equal(t, true, strings.Contains(writer.Body.String(), "Beacon node is currently syncing and not serving request on that endpoint"))
})
}
func TestPublishBlockSSZ(t *testing.T) {
ctrl := gomock.NewController(t)
t.Run("Bellatrix", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_Bellatrix)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
var bellablock shared.SignedBeaconBlockBellatrix
err := json.Unmarshal([]byte(rpctesting.BellatrixBlock), &bellablock)
require.NoError(t, err)
genericBlock, err := bellablock.ToGeneric()
require.NoError(t, err)
sszvalue, err := genericBlock.GetBellatrix().MarshalSSZ()
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(sszvalue))
request.Header.Set("Accept", "application/octet-stream")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Capella", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_Capella)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
var cblock shared.SignedBeaconBlockCapella
err := json.Unmarshal([]byte(rpctesting.CapellaBlock), &cblock)
require.NoError(t, err)
genericBlock, err := cblock.ToGeneric()
require.NoError(t, err)
sszvalue, err := genericBlock.GetCapella().MarshalSSZ()
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(sszvalue))
request.Header.Set("Accept", "application/octet-stream")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Deneb", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_Deneb)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
var dblock shared.SignedBeaconBlockContentsDeneb
err := json.Unmarshal([]byte(rpctesting.DenebBlockContents), &dblock)
require.NoError(t, err)
genericBlock, err := dblock.ToGeneric()
require.NoError(t, err)
v2block, err := migration.V1Alpha1SignedBeaconBlockDenebAndBlobsToV2(genericBlock.GetDeneb())
require.NoError(t, err)
sszvalue, err := v2block.MarshalSSZ()
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(sszvalue))
request.Header.Set("Accept", "application/octet-stream")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("invalid block", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BlindedBellatrixBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlock(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
assert.Equal(t, true, strings.Contains(writer.Body.String(), "Body does not represent a valid block type"))
})
}
func TestPublishBlindedBlock(t *testing.T) {
ctrl := gomock.NewController(t)
t.Run("Phase 0", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Phase0)
converted, err := shared.BeaconBlockFromConsensus(block.Phase0.Block)
require.NoError(t, err)
var signedblock *shared.SignedBeaconBlock
err = json.Unmarshal([]byte(rpctesting.Phase0Block), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.Phase0Block)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Altair", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Altair)
converted, err := shared.BeaconBlockAltairFromConsensus(block.Altair.Block)
require.NoError(t, err)
var signedblock *shared.SignedBeaconBlockAltair
err = json.Unmarshal([]byte(rpctesting.AltairBlock), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.AltairBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Blinded Bellatrix", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedBellatrix)
converted, err := shared.BlindedBeaconBlockBellatrixFromConsensus(block.BlindedBellatrix.Block)
require.NoError(t, err)
var signedblock *shared.SignedBlindedBeaconBlockBellatrix
err = json.Unmarshal([]byte(rpctesting.BlindedBellatrixBlock), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BlindedBellatrixBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Blinded Capella", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedCapella)
converted, err := shared.BlindedBeaconBlockCapellaFromConsensus(block.BlindedCapella.Block)
require.NoError(t, err)
var signedblock *shared.SignedBlindedBeaconBlockCapella
err = json.Unmarshal([]byte(rpctesting.BlindedCapellaBlock), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BlindedCapellaBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Blinded Deneb", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedDeneb)
converted, err := shared.BlindedBeaconBlockDenebFromConsensus(block.BlindedDeneb.SignedBlindedBlock.Message)
require.NoError(t, err)
var signedblock *shared.SignedBlindedBeaconBlockContentsDeneb
err = json.Unmarshal([]byte(rpctesting.BlindedDenebBlockContents), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.SignedBlindedBlock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BlindedDenebBlockContents)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("invalid block", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BellatrixBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlock(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
assert.Equal(t, true, strings.Contains(writer.Body.String(), "please add the api header"))
assert.Equal(t, true, strings.Contains(writer.Body.String(), "Body does not represent a valid block type"))
})
t.Run("invalid block with version header", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BadBlindedBellatrixBlock)))
request.Header.Set(api.VersionHeader, version.String(version.Bellatrix))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlock(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
body := writer.Body.String()
assert.Equal(t, true, strings.Contains(body, "Body does not represent a valid block type"))
assert.Equal(t, true, strings.Contains(body, fmt.Sprintf("could not decode %s request body into consensus block:", version.String(version.Bellatrix))))
})
t.Run("syncing", func(t *testing.T) {
chainService := &chainMock.ChainService{}
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: true},
HeadFetcher: chainService,
TimeFetcher: chainService,
OptimisticModeFetcher: chainService,
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte("foo")))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlock(writer, request)
assert.Equal(t, http.StatusServiceUnavailable, writer.Code)
assert.Equal(t, true, strings.Contains(writer.Body.String(), "Beacon node is currently syncing and not serving request on that endpoint"))
})
}
func TestPublishBlindedBlockSSZ(t *testing.T) {
ctrl := gomock.NewController(t)
t.Run("Bellatrix", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedBellatrix)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
var bellablock shared.SignedBlindedBeaconBlockBellatrix
err := json.Unmarshal([]byte(rpctesting.BlindedBellatrixBlock), &bellablock)
require.NoError(t, err)
genericBlock, err := bellablock.ToGeneric()
require.NoError(t, err)
sszvalue, err := genericBlock.GetBlindedBellatrix().MarshalSSZ()
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(sszvalue))
request.Header.Set("Accept", "application/octet-stream")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Capella", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedCapella)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
var cblock shared.SignedBlindedBeaconBlockCapella
err := json.Unmarshal([]byte(rpctesting.BlindedCapellaBlock), &cblock)
require.NoError(t, err)
genericBlock, err := cblock.ToGeneric()
require.NoError(t, err)
sszvalue, err := genericBlock.GetBlindedCapella().MarshalSSZ()
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(sszvalue))
request.Header.Set("Accept", "application/octet-stream")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Deneb", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedDeneb)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
var cblock shared.SignedBlindedBeaconBlockContentsDeneb
err := json.Unmarshal([]byte(rpctesting.BlindedDenebBlockContents), &cblock)
require.NoError(t, err)
genericBlock, err := cblock.ToGeneric()
require.NoError(t, err)
v1block, err := migration.V1Alpha1SignedBlindedBlockAndBlobsDenebToV2Blinded(genericBlock.GetBlindedDeneb())
require.NoError(t, err)
sszvalue, err := v1block.MarshalSSZ()
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(sszvalue))
request.Header.Set("Accept", "application/octet-stream")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlock(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("invalid block", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BellatrixBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlock(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
assert.Equal(t, true, strings.Contains(writer.Body.String(), "Body does not represent a valid block type"))
})
}
func TestPublishBlockV2(t *testing.T) {
ctrl := gomock.NewController(t)
@@ -1654,3 +2166,23 @@ func TestGetGenesis(t *testing.T) {
assert.StringContains(t, "Chain genesis info is not yet known", e.Message)
})
}
func TestGetDepositContract(t *testing.T) {
params.SetupTestConfigCleanup(t)
config := params.BeaconConfig().Copy()
config.DepositChainID = uint64(10)
config.DepositContractAddress = "0x4242424242424242424242424242424242424242"
params.OverrideBeaconConfig(config)
request := httptest.NewRequest(http.MethodGet, "/eth/v1/beacon/states/{state_id}/finality_checkpoints", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s := &Server{}
s.GetDepositContract(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
response := DepositContractResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), &response))
assert.Equal(t, "10", response.Data.ChainId)
assert.Equal(t, "0x4242424242424242424242424242424242424242", response.Data.Address)
}

View File

@@ -20,7 +20,7 @@ type GetCommitteesResponse struct {
type DepositContractResponse struct {
Data *struct {
ChainId uint64 `json:"chain_id"`
ChainId string `json:"chain_id"`
Address string `json:"address"`
} `json:"data"`
}

View File

@@ -12,7 +12,6 @@ go_library(
deps = [
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/rpc/eth/helpers:go_default_library",
"//beacon-chain/rpc/lookup:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",

View File

@@ -8,7 +8,6 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup"
field_params "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
@@ -98,12 +97,7 @@ func (s *Server) Blobs(w http.ResponseWriter, r *http.Request) {
return
}
ssz, err := http2.SszRequested(r)
if err != nil {
http2.HandleError(w, err.Error(), http.StatusInternalServerError)
return
}
ssz := http2.SszRequested(r)
if ssz {
v2sidecars, err := migration.V1Alpha1BlobSidecarsToV2(sidecars)
if err != nil {
@@ -127,9 +121,7 @@ func (s *Server) Blobs(w http.ResponseWriter, r *http.Request) {
// parseIndices filters out invalid and duplicate blob indices
func parseIndices(url *url.URL) []uint64 {
query := url.Query()
helpers.NormalizeQueryValues(query)
rawIndices := query["indices"]
rawIndices := url.Query()["indices"]
indices := make([]uint64, 0, field_params.MaxBlobsPerBlock)
loop:
for _, raw := range rawIndices {

View File

@@ -23,7 +23,7 @@ import (
)
func TestParseIndices(t *testing.T) {
assert.DeepEqual(t, []uint64{1, 2, 3}, parseIndices(&url.URL{RawQuery: "indices=1,2,foo,1&indices=3,1&bar=bar"}))
assert.DeepEqual(t, []uint64{1, 2, 3}, parseIndices(&url.URL{RawQuery: "indices=1&indices=2&indices=foo&indices=1&indices=3&bar=bar"}))
}
func TestBlobs(t *testing.T) {

View File

@@ -20,10 +20,13 @@ go_library(
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/sync:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//consensus-types/validator:go_default_library",
"//encoding/bytesutil:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
@@ -60,6 +63,7 @@ go_test(
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_grpc_ecosystem_grpc_gateway_v2//runtime:go_default_library",
"@org_golang_google_grpc//:go_default_library",
],

View File

@@ -5,6 +5,8 @@ import (
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stategen"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
@@ -34,3 +36,16 @@ type SingleIndexedVerificationFailure struct {
Index int `json:"index"`
Message string `json:"message"`
}
func HandleGetBlockError(blk interfaces.ReadOnlySignedBeaconBlock, err error) error {
if invalidBlockIdErr, ok := err.(*lookup.BlockIdParseError); ok {
return status.Errorf(codes.InvalidArgument, "Invalid block ID: %v", invalidBlockIdErr)
}
if err != nil {
return status.Errorf(codes.Internal, "Could not get block from block ID: %v", err)
}
if err := blocks.BeaconBlockIsNil(blk); err != nil {
return status.Errorf(codes.NotFound, "Could not find requested block: %v", err)
}
return nil
}

View File

@@ -6,6 +6,7 @@ import (
"strconv"
"strings"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/api/grpc"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
@@ -95,7 +96,13 @@ func IsOptimistic(
}
return optimisticModeFetcher.IsOptimisticForRoot(ctx, bytesutil.ToBytes32(jcp.Root))
default:
if len(stateId) == 32 {
if len(stateIdString) >= 2 && stateIdString[:2] == "0x" {
id, err := hexutil.Decode(stateIdString)
if err != nil {
return false, err
}
return isStateRootOptimistic(ctx, id, optimisticModeFetcher, stateFetcher, chainInfo, database)
} else if len(stateId) == 32 {
return isStateRootOptimistic(ctx, stateId, optimisticModeFetcher, stateFetcher, chainInfo, database)
} else {
optimistic, err := optimisticModeFetcher.IsOptimistic(ctx)

View File

@@ -6,6 +6,7 @@ import (
"strings"
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
grpcutil "github.com/prysmaticlabs/prysm/v4/api/grpc"
chainmock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
@@ -204,6 +205,78 @@ func TestIsOptimistic(t *testing.T) {
assert.Equal(t, true, o)
})
})
t.Run("hex", func(t *testing.T) {
t.Run("is head and head is optimistic", func(t *testing.T) {
st, err := util.NewBeaconState()
require.NoError(t, err)
cs := &chainmock.ChainService{Optimistic: true}
mf := &testutil.MockStater{BeaconState: st}
o, err := IsOptimistic(ctx, []byte(hexutil.Encode(bytesutil.PadTo([]byte("root"), 32))), cs, mf, cs, nil)
require.NoError(t, err)
assert.Equal(t, true, o)
})
t.Run("is head and head is not optimistic", func(t *testing.T) {
st, err := util.NewBeaconState()
require.NoError(t, err)
cs := &chainmock.ChainService{Optimistic: false}
mf := &testutil.MockStater{BeaconState: st}
o, err := IsOptimistic(ctx, []byte(hexutil.Encode(bytesutil.PadTo([]byte("root"), 32))), cs, mf, cs, nil)
require.NoError(t, err)
assert.Equal(t, false, o)
})
t.Run("root is optimistic", func(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
b.SetStateRoot(bytesutil.PadTo([]byte("root"), 32))
db := dbtest.SetupDB(t)
require.NoError(t, db.SaveBlock(ctx, b))
fetcherSt, err := util.NewBeaconState()
require.NoError(t, err)
chainSt, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, chainSt.SetSlot(fieldparams.SlotsPerEpoch))
bRoot, err := b.Block().HashTreeRoot()
require.NoError(t, err)
cs := &chainmock.ChainService{State: chainSt, OptimisticRoots: map[[32]byte]bool{bRoot: true}}
mf := &testutil.MockStater{BeaconState: fetcherSt}
o, err := IsOptimistic(ctx, []byte(hexutil.Encode(bytesutil.PadTo([]byte("root"), 32))), cs, mf, cs, db)
require.NoError(t, err)
assert.Equal(t, true, o)
})
t.Run("root is not optimistic", func(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
b.SetStateRoot(bytesutil.PadTo([]byte("root"), 32))
db := dbtest.SetupDB(t)
require.NoError(t, db.SaveBlock(ctx, b))
fetcherSt, err := util.NewBeaconState()
require.NoError(t, err)
chainSt, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, chainSt.SetSlot(fieldparams.SlotsPerEpoch))
cs := &chainmock.ChainService{State: chainSt}
mf := &testutil.MockStater{BeaconState: fetcherSt}
o, err := IsOptimistic(ctx, []byte(hexutil.Encode(bytesutil.PadTo([]byte("root"), 32))), cs, mf, cs, db)
require.NoError(t, err)
assert.Equal(t, false, o)
})
t.Run("no canonical blocks", func(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
db := dbtest.SetupDB(t)
require.NoError(t, db.SaveBlock(ctx, b))
fetcherSt, err := util.NewBeaconState()
require.NoError(t, err)
chainSt, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, chainSt.SetSlot(fieldparams.SlotsPerEpoch))
cs := &chainmock.ChainService{Optimistic: false, State: chainSt, CanonicalRoots: map[[32]byte]bool{}}
mf := &testutil.MockStater{BeaconState: fetcherSt}
o, err := IsOptimistic(ctx, []byte(hexutil.Encode(bytesutil.PadTo([]byte("root"), 32))), nil, mf, cs, db)
require.NoError(t, err)
assert.Equal(t, true, o)
})
})
t.Run("slot", func(t *testing.T) {
t.Run("head is not optimistic", func(t *testing.T) {
cs := &chainmock.ChainService{Optimistic: false}

View File

@@ -15,18 +15,16 @@ go_library(
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/epoch/precompute:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/rpc/eth/shared:go_default_library",
"//beacon-chain/rpc/lookup:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//network/http:go_default_library",
"//runtime/version:go_default_library",
"//time/slots:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_wealdtech_go_bytesutil//:go_default_library",
],
)

View File

@@ -7,17 +7,14 @@ import (
"strconv"
"strings"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair"
coreblocks "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/epoch/precompute"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
http2 "github.com/prysmaticlabs/prysm/v4/network/http"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
@@ -31,16 +28,11 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
blockId := segments[len(segments)-1]
blk, err := s.Blocker.Block(r.Context(), []byte(blockId))
if errJson := handleGetBlockError(blk, err); errJson != nil {
http2.WriteError(w, errJson)
if !shared.WriteBlockFetchError(w, blk, err) {
return
}
if blk.Version() == version.Phase0 {
errJson := &http2.DefaultErrorJson{
Message: "Block rewards are not supported for Phase 0 blocks",
Code: http.StatusBadRequest,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Block rewards are not supported for Phase 0 blocks", http.StatusBadRequest)
return
}
@@ -49,114 +41,66 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
// To do this, we replay the state up to the block's slot, but before processing the block.
st, err := s.ReplayerBuilder.ReplayerForSlot(blk.Block().Slot()-1).ReplayToSlot(r.Context(), blk.Block().Slot())
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get state: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get state: "+err.Error(), http.StatusInternalServerError)
return
}
proposerIndex := blk.Block().ProposerIndex()
initBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get proposer's balance: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get proposer's balance: "+err.Error(), http.StatusInternalServerError)
return
}
st, err = altair.ProcessAttestationsNoVerifySignature(r.Context(), st, blk)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get attestation rewards" + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get attestation rewards"+err.Error(), http.StatusInternalServerError)
return
}
attBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get proposer's balance: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get proposer's balance: "+err.Error(), http.StatusInternalServerError)
return
}
st, err = coreblocks.ProcessAttesterSlashings(r.Context(), st, blk.Block().Body().AttesterSlashings(), validators.SlashValidator)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get attester slashing rewards: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get attester slashing rewards: "+err.Error(), http.StatusInternalServerError)
return
}
attSlashingsBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get proposer's balance: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get proposer's balance: "+err.Error(), http.StatusInternalServerError)
return
}
st, err = coreblocks.ProcessProposerSlashings(r.Context(), st, blk.Block().Body().ProposerSlashings(), validators.SlashValidator)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get proposer slashing rewards" + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get proposer slashing rewards"+err.Error(), http.StatusInternalServerError)
return
}
proposerSlashingsBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get proposer's balance: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get proposer's balance: "+err.Error(), http.StatusInternalServerError)
return
}
sa, err := blk.Block().Body().SyncAggregate()
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get sync aggregate: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get sync aggregate: "+err.Error(), http.StatusInternalServerError)
return
}
var syncCommitteeReward uint64
_, syncCommitteeReward, err = altair.ProcessSyncAggregate(r.Context(), st, sa)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get sync aggregate rewards: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get sync aggregate rewards: "+err.Error(), http.StatusInternalServerError)
return
}
optimistic, err := s.OptimisticModeFetcher.IsOptimistic(r.Context())
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get optimistic mode info: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get optimistic mode info: "+err.Error(), http.StatusInternalServerError)
return
}
blkRoot, err := blk.Block().HashTreeRoot()
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get block root: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get block root: "+err.Error(), http.StatusInternalServerError)
return
}
@@ -198,20 +142,12 @@ func (s *Server) AttestationRewards(w http.ResponseWriter, r *http.Request) {
optimistic, err := s.OptimisticModeFetcher.IsOptimistic(r.Context())
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get optimistic mode info: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get optimistic mode info: "+err.Error(), http.StatusInternalServerError)
return
}
blkRoot, err := st.LatestBlockHeader().HashTreeRoot()
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get block root: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get block root: "+err.Error(), http.StatusInternalServerError)
return
}
@@ -233,34 +169,21 @@ func (s *Server) SyncCommitteeRewards(w http.ResponseWriter, r *http.Request) {
blockId := segments[len(segments)-1]
blk, err := s.Blocker.Block(r.Context(), []byte(blockId))
if errJson := handleGetBlockError(blk, err); errJson != nil {
http2.WriteError(w, errJson)
if !shared.WriteBlockFetchError(w, blk, err) {
return
}
if blk.Version() == version.Phase0 {
errJson := &http2.DefaultErrorJson{
Message: "Sync committee rewards are not supported for Phase 0",
Code: http.StatusBadRequest,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Sync committee rewards are not supported for Phase 0", http.StatusBadRequest)
return
}
st, err := s.ReplayerBuilder.ReplayerForSlot(blk.Block().Slot()-1).ReplayToSlot(r.Context(), blk.Block().Slot())
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get state: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get state: "+err.Error(), http.StatusInternalServerError)
return
}
sa, err := blk.Block().Body().SyncAggregate()
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get sync aggregate: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get sync aggregate: "+err.Error(), http.StatusInternalServerError)
return
}
@@ -272,22 +195,14 @@ func (s *Server) SyncCommitteeRewards(w http.ResponseWriter, r *http.Request) {
for i, valIdx := range valIndices {
preProcessBals[i], err = st.BalanceAtIndex(valIdx)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get validator's balance: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get validator's balance: "+err.Error(), http.StatusInternalServerError)
return
}
}
_, proposerReward, err := altair.ProcessSyncAggregate(r.Context(), st, sa)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get sync aggregate rewards: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get sync aggregate rewards: "+err.Error(), http.StatusInternalServerError)
return
}
@@ -296,11 +211,7 @@ func (s *Server) SyncCommitteeRewards(w http.ResponseWriter, r *http.Request) {
for i, valIdx := range valIndices {
bal, err := st.BalanceAtIndex(valIdx)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get validator's balance: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get validator's balance: "+err.Error(), http.StatusInternalServerError)
return
}
rewards[i] = int(bal - preProcessBals[i]) // lint:ignore uintcast
@@ -311,20 +222,12 @@ func (s *Server) SyncCommitteeRewards(w http.ResponseWriter, r *http.Request) {
optimistic, err := s.OptimisticModeFetcher.IsOptimistic(r.Context())
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get optimistic mode info: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get optimistic mode info: "+err.Error(), http.StatusInternalServerError)
return
}
blkRoot, err := blk.Block().HashTreeRoot()
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get block root: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get block root: "+err.Error(), http.StatusInternalServerError)
return
}
@@ -347,46 +250,28 @@ func (s *Server) attRewardsState(w http.ResponseWriter, r *http.Request) (state.
segments := strings.Split(r.URL.Path, "/")
requestedEpoch, err := strconv.ParseUint(segments[len(segments)-1], 10, 64)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not decode epoch: " + err.Error(),
Code: http.StatusBadRequest,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not decode epoch: "+err.Error(), http.StatusBadRequest)
return nil, false
}
if primitives.Epoch(requestedEpoch) < params.BeaconConfig().AltairForkEpoch {
errJson := &http2.DefaultErrorJson{
Message: "Attestation rewards are not supported for Phase 0",
Code: http.StatusNotFound,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Attestation rewards are not supported for Phase 0", http.StatusNotFound)
return nil, false
}
currentEpoch := uint64(slots.ToEpoch(s.TimeFetcher.CurrentSlot()))
if requestedEpoch+1 >= currentEpoch {
errJson := &http2.DefaultErrorJson{
Code: http.StatusNotFound,
Message: "Attestation rewards are available after two epoch transitions to ensure all attestations have a chance of inclusion",
}
http2.WriteError(w, errJson)
http2.HandleError(w,
"Attestation rewards are available after two epoch transitions to ensure all attestations have a chance of inclusion",
http.StatusNotFound)
return nil, false
}
nextEpochEnd, err := slots.EpochEnd(primitives.Epoch(requestedEpoch + 1))
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get next epoch's ending slot: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get next epoch's ending slot: "+err.Error(), http.StatusInternalServerError)
return nil, false
}
st, err := s.Stater.StateBySlot(r.Context(), nextEpochEnd)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get state for epoch's starting slot: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get state for epoch's starting slot: "+err.Error(), http.StatusInternalServerError)
return nil, false
}
return st, true
@@ -399,20 +284,12 @@ func attRewardsBalancesAndVals(
) (*precompute.Balance, []*precompute.Validator, []primitives.ValidatorIndex, bool) {
allVals, bal, err := altair.InitializePrecomputeValidators(r.Context(), st)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not initialize precompute validators: " + err.Error(),
Code: http.StatusBadRequest,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not initialize precompute validators: "+err.Error(), http.StatusBadRequest)
return nil, nil, nil, false
}
allVals, bal, err = altair.ProcessEpochParticipation(r.Context(), st, bal, allVals)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not process epoch participation: " + err.Error(),
Code: http.StatusBadRequest,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not process epoch participation: "+err.Error(), http.StatusBadRequest)
return nil, nil, nil, false
}
valIndices, ok := requestedValIndices(w, r, st, allVals)
@@ -463,11 +340,7 @@ func idealAttRewards(
}
deltas, err := altair.AttestationsDelta(st, bal, idealVals)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get attestations delta: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get attestations delta: "+err.Error(), http.StatusInternalServerError)
return nil, false
}
for i, d := range deltas {
@@ -499,11 +372,7 @@ func totalAttRewards(
}
deltas, err := altair.AttestationsDelta(st, bal, vals)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get attestations delta: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get attestations delta: "+err.Error(), http.StatusInternalServerError)
return nil, false
}
for i, d := range deltas {
@@ -529,11 +398,7 @@ func syncRewardsVals(
) ([]*precompute.Validator, []primitives.ValidatorIndex, bool) {
allVals, _, err := altair.InitializePrecomputeValidators(r.Context(), st)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not initialize precompute validators: " + err.Error(),
Code: http.StatusBadRequest,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not initialize precompute validators: "+err.Error(), http.StatusBadRequest)
return nil, nil, false
}
valIndices, ok := requestedValIndices(w, r, st, allVals)
@@ -543,22 +408,14 @@ func syncRewardsVals(
sc, err := st.CurrentSyncCommittee()
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get current sync committee: " + err.Error(),
Code: http.StatusBadRequest,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not get current sync committee: "+err.Error(), http.StatusBadRequest)
return nil, nil, false
}
allScIndices := make([]primitives.ValidatorIndex, len(sc.Pubkeys))
for i, pk := range sc.Pubkeys {
valIdx, ok := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(pk))
if !ok {
errJson := &http2.DefaultErrorJson{
Message: fmt.Sprintf("No validator index found for pubkey %#x", pk),
Code: http.StatusBadRequest,
}
http2.WriteError(w, errJson)
http2.HandleError(w, fmt.Sprintf("No validator index found for pubkey %#x", pk), http.StatusBadRequest)
return nil, nil, false
}
allScIndices[i] = valIdx
@@ -583,11 +440,7 @@ func requestedValIndices(w http.ResponseWriter, r *http.Request, st state.Beacon
var rawValIds []string
if r.Body != http.NoBody {
if err := json.NewDecoder(r.Body).Decode(&rawValIds); err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not decode validators: " + err.Error(),
Code: http.StatusBadRequest,
}
http2.WriteError(w, errJson)
http2.HandleError(w, "Could not decode validators: "+err.Error(), http.StatusBadRequest)
return nil, false
}
}
@@ -597,30 +450,18 @@ func requestedValIndices(w http.ResponseWriter, r *http.Request, st state.Beacon
if err != nil {
pubkey, err := bytesutil.FromHexString(v)
if err != nil || len(pubkey) != fieldparams.BLSPubkeyLength {
errJson := &http2.DefaultErrorJson{
Message: fmt.Sprintf("%s is not a validator index or pubkey", v),
Code: http.StatusBadRequest,
}
http2.WriteError(w, errJson)
http2.HandleError(w, fmt.Sprintf("%s is not a validator index or pubkey", v), http.StatusBadRequest)
return nil, false
}
var ok bool
valIndices[i], ok = st.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubkey))
if !ok {
errJson := &http2.DefaultErrorJson{
Message: fmt.Sprintf("No validator index found for pubkey %#x", pubkey),
Code: http.StatusBadRequest,
}
http2.WriteError(w, errJson)
http2.HandleError(w, fmt.Sprintf("No validator index found for pubkey %#x", pubkey), http.StatusBadRequest)
return nil, false
}
} else {
if index >= uint64(st.NumValidators()) {
errJson := &http2.DefaultErrorJson{
Message: fmt.Sprintf("Validator index %d is too large. Maximum allowed index is %d", index, st.NumValidators()-1),
Code: http.StatusBadRequest,
}
http2.WriteError(w, errJson)
http2.HandleError(w, fmt.Sprintf("Validator index %d is too large. Maximum allowed index is %d", index, st.NumValidators()-1), http.StatusBadRequest)
return nil, false
}
valIndices[i] = primitives.ValidatorIndex(index)
@@ -635,25 +476,3 @@ func requestedValIndices(w http.ResponseWriter, r *http.Request, st state.Beacon
return valIndices, true
}
func handleGetBlockError(blk interfaces.ReadOnlySignedBeaconBlock, err error) *http2.DefaultErrorJson {
if errors.Is(err, lookup.BlockIdParseError{}) {
return &http2.DefaultErrorJson{
Message: "Invalid block ID: " + err.Error(),
Code: http.StatusBadRequest,
}
}
if err != nil {
return &http2.DefaultErrorJson{
Message: "Could not get block from block ID: " + err.Error(),
Code: http.StatusInternalServerError,
}
}
if err := blocks.BeaconBlockIsNil(blk); err != nil {
return &http2.DefaultErrorJson{
Message: "Could not find requested block: " + err.Error(),
Code: http.StatusNotFound,
}
}
return nil
}

View File

@@ -5,6 +5,7 @@ import (
"strconv"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/consensus-types/validator"
@@ -12,22 +13,22 @@ import (
)
type Attestation struct {
AggregationBits string `json:"aggregation_bits" validate:"required,hexadecimal"`
Data *AttestationData `json:"data" validate:"required"`
Signature string `json:"signature" validate:"required,hexadecimal"`
AggregationBits string `json:"aggregation_bits"`
Data *AttestationData `json:"data"`
Signature string `json:"signature"`
}
type AttestationData struct {
Slot string `json:"slot" validate:"required,number,gte=0"`
CommitteeIndex string `json:"index" validate:"required,number,gte=0"`
BeaconBlockRoot string `json:"beacon_block_root" validate:"required,hexadecimal"`
Source *Checkpoint `json:"source" validate:"required"`
Target *Checkpoint `json:"target" validate:"required"`
Slot string `json:"slot"`
CommitteeIndex string `json:"index"`
BeaconBlockRoot string `json:"beacon_block_root"`
Source *Checkpoint `json:"source"`
Target *Checkpoint `json:"target"`
}
type Checkpoint struct {
Epoch string `json:"epoch" validate:"required,number,gte=0"`
Root string `json:"root" validate:"required,hexadecimal"`
Epoch string `json:"epoch"`
Root string `json:"root"`
}
type Committee struct {
@@ -37,59 +38,59 @@ type Committee struct {
}
type SignedContributionAndProof struct {
Message *ContributionAndProof `json:"message" validate:"required"`
Signature string `json:"signature" validate:"required,hexadecimal"`
Message *ContributionAndProof `json:"message"`
Signature string `json:"signature"`
}
type ContributionAndProof struct {
AggregatorIndex string `json:"aggregator_index" validate:"required,number,gte=0"`
Contribution *SyncCommitteeContribution `json:"contribution" validate:"required"`
SelectionProof string `json:"selection_proof" validate:"required,hexadecimal"`
AggregatorIndex string `json:"aggregator_index"`
Contribution *SyncCommitteeContribution `json:"contribution"`
SelectionProof string `json:"selection_proof"`
}
type SyncCommitteeContribution struct {
Slot string `json:"slot" validate:"required,number,gte=0"`
BeaconBlockRoot string `json:"beacon_block_root" hex:"true" validate:"required,hexadecimal"`
SubcommitteeIndex string `json:"subcommittee_index" validate:"required,number,gte=0"`
AggregationBits string `json:"aggregation_bits" hex:"true" validate:"required,hexadecimal"`
Signature string `json:"signature" hex:"true" validate:"required,hexadecimal"`
Slot string `json:"slot"`
BeaconBlockRoot string `json:"beacon_block_root"`
SubcommitteeIndex string `json:"subcommittee_index"`
AggregationBits string `json:"aggregation_bits"`
Signature string `json:"signature"`
}
type SignedAggregateAttestationAndProof struct {
Message *AggregateAttestationAndProof `json:"message" validate:"required"`
Signature string `json:"signature" validate:"required,hexadecimal"`
Message *AggregateAttestationAndProof `json:"message"`
Signature string `json:"signature"`
}
type AggregateAttestationAndProof struct {
AggregatorIndex string `json:"aggregator_index" validate:"required,number,gte=0"`
Aggregate *Attestation `json:"aggregate" validate:"required"`
SelectionProof string `json:"selection_proof" validate:"required,hexadecimal"`
AggregatorIndex string `json:"aggregator_index"`
Aggregate *Attestation `json:"aggregate"`
SelectionProof string `json:"selection_proof"`
}
type SyncCommitteeSubscription struct {
ValidatorIndex string `json:"validator_index" validate:"required,number,gte=0"`
SyncCommitteeIndices []string `json:"sync_committee_indices" validate:"required,dive,number,gte=0"`
UntilEpoch string `json:"until_epoch" validate:"required,number,gte=0"`
ValidatorIndex string `json:"validator_index"`
SyncCommitteeIndices []string `json:"sync_committee_indices"`
UntilEpoch string `json:"until_epoch"`
}
type BeaconCommitteeSubscription struct {
ValidatorIndex string `json:"validator_index" validate:"required,number,gte=0"`
CommitteeIndex string `json:"committee_index" validate:"required,number,gte=0"`
CommitteesAtSlot string `json:"committees_at_slot" validate:"required,number,gte=0"`
Slot string `json:"slot" validate:"required,number,gte=0"`
ValidatorIndex string `json:"validator_index"`
CommitteeIndex string `json:"committee_index"`
CommitteesAtSlot string `json:"committees_at_slot"`
Slot string `json:"slot"`
IsAggregator bool `json:"is_aggregator"`
}
type ValidatorRegistration struct {
FeeRecipient string `json:"fee_recipient" validate:"required,hexadecimal"`
GasLimit string `json:"gas_limit" validate:"required,number,gte=0"`
Timestamp string `json:"timestamp" validate:"required,number,gte=0"`
Pubkey string `json:"pubkey" validate:"required,hexadecimal"`
FeeRecipient string `json:"fee_recipient"`
GasLimit string `json:"gas_limit"`
Timestamp string `json:"timestamp"`
Pubkey string `json:"pubkey"`
}
type SignedValidatorRegistration struct {
Message *ValidatorRegistration `json:"message" validate:"required"`
Signature string `json:"signature" validate:"required,hexadecimal"`
Message *ValidatorRegistration `json:"message"`
Signature string `json:"signature"`
}
type FeeRecipient struct {
@@ -98,13 +99,13 @@ type FeeRecipient struct {
}
type SignedVoluntaryExit struct {
Message *VoluntaryExit `json:"message" validate:"required"`
Signature string `json:"signature" validate:"required,hexadecimal"`
Message *VoluntaryExit `json:"message"`
Signature string `json:"signature"`
}
type VoluntaryExit struct {
Epoch string `json:"epoch" validate:"required,number,gte=0"`
ValidatorIndex string `json:"validator_index" validate:"required,number,gte=0"`
Epoch string `json:"epoch"`
ValidatorIndex string `json:"validator_index"`
}
type Fork struct {
@@ -134,10 +135,10 @@ func (s *Fork) ToConsensus() (*eth.Fork, error) {
}
type SyncCommitteeMessage struct {
Slot string `json:"slot" validate:"required,number,gte=0"`
BeaconBlockRoot string `json:"beacon_block_root" validate:"required,hexadecimal"`
ValidatorIndex string `json:"validator_index" validate:"required,number,gte=0"`
Signature string `json:"signature" validate:"required,hexadecimal"`
Slot string `json:"slot"`
BeaconBlockRoot string `json:"beacon_block_root"`
ValidatorIndex string `json:"validator_index"`
Signature string `json:"signature"`
}
func (s *SignedValidatorRegistration) ToConsensus() (*eth.SignedValidatorRegistrationV1, error) {
@@ -189,6 +190,32 @@ func (s *ValidatorRegistration) ToConsensus() (*eth.ValidatorRegistrationV1, err
}, nil
}
func ValidatorRegistrationFromConsensus(vr *eth.ValidatorRegistrationV1) (*ValidatorRegistration, error) {
if vr == nil {
return nil, errors.New("ValidatorRegistrationV1 is empty")
}
return &ValidatorRegistration{
FeeRecipient: hexutil.Encode(vr.FeeRecipient),
GasLimit: strconv.FormatUint(vr.GasLimit, 10),
Timestamp: strconv.FormatUint(vr.Timestamp, 10),
Pubkey: hexutil.Encode(vr.Pubkey),
}, nil
}
func SignedValidatorRegistrationFromConsensus(vr *eth.SignedValidatorRegistrationV1) (*SignedValidatorRegistration, error) {
if vr == nil {
return nil, errors.New("SignedValidatorRegistrationV1 is empty")
}
v, err := ValidatorRegistrationFromConsensus(vr.Message)
if err != nil {
return nil, err
}
return &SignedValidatorRegistration{
Message: v,
Signature: hexutil.Encode(vr.Signature),
}, nil
}
func (s *SignedContributionAndProof) ToConsensus() (*eth.SignedContributionAndProof, error) {
msg, err := s.Message.ToConsensus()
if err != nil {

View File

@@ -2162,6 +2162,17 @@ func BlindedBeaconBlockBellatrixFromConsensus(b *eth.BlindedBeaconBlockBellatrix
}, nil
}
func SignedBlindedBeaconBlockBellatrixFromConsensus(b *eth.SignedBlindedBeaconBlockBellatrix) (*SignedBlindedBeaconBlockBellatrix, error) {
blindedBlock, err := BlindedBeaconBlockBellatrixFromConsensus(b.Block)
if err != nil {
return nil, err
}
return &SignedBlindedBeaconBlockBellatrix{
Message: blindedBlock,
Signature: hexutil.Encode(b.Signature),
}, nil
}
func BeaconBlockBellatrixFromConsensus(b *eth.BeaconBlockBellatrix) (*BeaconBlockBellatrix, error) {
proposerSlashings, err := ProposerSlashingsFromConsensus(b.Body.ProposerSlashings)
if err != nil {
@@ -2307,6 +2318,17 @@ func BlindedBeaconBlockCapellaFromConsensus(b *eth.BlindedBeaconBlockCapella) (*
}, nil
}
func SignedBlindedBeaconBlockCapellaFromConsensus(b *eth.SignedBlindedBeaconBlockCapella) (*SignedBlindedBeaconBlockCapella, error) {
blindedBlock, err := BlindedBeaconBlockCapellaFromConsensus(b.Block)
if err != nil {
return nil, err
}
return &SignedBlindedBeaconBlockCapella{
Message: blindedBlock,
Signature: hexutil.Encode(b.Signature),
}, nil
}
func BeaconBlockCapellaFromConsensus(b *eth.BeaconBlockCapella) (*BeaconBlockCapella, error) {
proposerSlashings, err := ProposerSlashingsFromConsensus(b.Body.ProposerSlashings)
if err != nil {

View File

@@ -45,7 +45,6 @@ go_library(
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_go_playground_validator_v10//:go_default_library",
"@com_github_gorilla_mux//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",

View File

@@ -13,7 +13,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/go-playground/validator/v10"
"github.com/gorilla/mux"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/builder"
@@ -50,6 +49,7 @@ func (s *Server) GetAggregateAttestation(w http.ResponseWriter, r *http.Request)
if !valid {
return
}
rawSlot := r.URL.Query().Get("slot")
slot, valid := shared.ValidateUint(w, "Slot", rawSlot)
if !valid {
@@ -122,11 +122,6 @@ func (s *Server) SubmitContributionAndProofs(w http.ResponseWriter, r *http.Requ
http2.HandleError(w, "No data submitted", http.StatusBadRequest)
return
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
for _, item := range req.Data {
consensusItem, err := item.ToConsensus()
@@ -160,11 +155,6 @@ func (s *Server) SubmitAggregateAndProofs(w http.ResponseWriter, r *http.Request
http2.HandleError(w, "No data submitted", http.StatusBadRequest)
return
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
broadcastFailed := false
for _, item := range req.Data {
@@ -220,11 +210,6 @@ func (s *Server) SubmitSyncCommitteeSubscription(w http.ResponseWriter, r *http.
http2.HandleError(w, "No data submitted", http.StatusBadRequest)
return
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
st, err := s.HeadFetcher.HeadStateReadOnly(ctx)
if err != nil {
@@ -335,11 +320,6 @@ func (s *Server) SubmitBeaconCommitteeSubscription(w http.ResponseWriter, r *htt
http2.HandleError(w, "No data submitted", http.StatusBadRequest)
return
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
st, err := s.HeadFetcher.HeadStateReadOnly(ctx)
if err != nil {
@@ -557,13 +537,8 @@ func (s *Server) RegisterValidator(w http.ResponseWriter, r *http.Request) {
return
}
validate := validator.New()
registrations := make([]*ethpbalpha.SignedValidatorRegistrationV1, len(jsonRegistrations))
for i, registration := range jsonRegistrations {
if err := validate.Struct(registration); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
reg, err := registration.ToConsensus()
if err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
@@ -855,6 +830,7 @@ func (s *Server) GetProposerDuties(w http.ResponseWriter, r *http.Request) {
pubkey48 := val.PublicKey()
pubkey := pubkey48[:]
for _, slot := range proposalSlots {
s.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, index, [8]byte{} /* payloadID */, [32]byte{} /* head root */)
duties = append(duties, &ProposerDuty{
Pubkey: hexutil.Encode(pubkey),
ValidatorIndex: strconv.FormatUint(uint64(index), 10),
@@ -863,6 +839,8 @@ func (s *Server) GetProposerDuties(w http.ResponseWriter, r *http.Request) {
}
}
s.ProposerSlotIndexCache.PrunePayloadIDs(epochStartSlot)
dependentRoot, err := proposalDependentRoot(st, requestedEpoch)
if err != nil {
http2.HandleError(w, "Could not get dependent root: "+err.Error(), http.StatusInternalServerError)

View File

@@ -30,6 +30,7 @@ import (
func (s *Server) ProduceBlockV3(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "validator.ProduceBlockV3")
defer span.End()
if shared.IsSyncing(r.Context(), w, s.SyncChecker, s.HeadFetcher, s.TimeFetcher, s.OptimisticModeFetcher) {
return
}
@@ -74,10 +75,9 @@ func (s *Server) ProduceBlockV3(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) produceBlockV3(ctx context.Context, w http.ResponseWriter, r *http.Request, v1alpha1req *eth.BlockRequest) {
isSSZ, err := http2.SszRequested(r)
if err != nil {
log.WithError(err).Error("Checking for SSZ failed, defaulting to JSON")
isSSZ = false
isSSZ := http2.SszRequested(r)
if !isSSZ {
log.Error("Checking for SSZ failed, defaulting to JSON")
}
v1alpha1resp, err := s.V1Alpha1Server.GetBeaconBlock(ctx, v1alpha1req)
if err != nil {

View File

@@ -1856,6 +1856,9 @@ func TestGetProposerDuties(t *testing.T) {
expectedDuty = duty
}
}
vid, _, has := s.ProposerSlotIndexCache.GetProposerPayloadIDs(11, [32]byte{})
require.Equal(t, true, has)
require.Equal(t, primitives.ValidatorIndex(12289), vid)
require.NotNil(t, expectedDuty, "Expected duty for slot 11 not found")
assert.Equal(t, "12289", expectedDuty.ValidatorIndex)
assert.Equal(t, hexutil.Encode(pubKeys[12289]), expectedDuty.Pubkey)
@@ -1895,10 +1898,52 @@ func TestGetProposerDuties(t *testing.T) {
expectedDuty = duty
}
}
vid, _, has := s.ProposerSlotIndexCache.GetProposerPayloadIDs(43, [32]byte{})
require.Equal(t, true, has)
require.Equal(t, primitives.ValidatorIndex(1360), vid)
require.NotNil(t, expectedDuty, "Expected duty for slot 43 not found")
assert.Equal(t, "1360", expectedDuty.ValidatorIndex)
assert.Equal(t, hexutil.Encode(pubKeys[1360]), expectedDuty.Pubkey)
})
t.Run("prune payload ID cache", func(t *testing.T) {
bs, err := transition.GenesisBeaconState(context.Background(), deposits, 0, eth1Data)
require.NoError(t, err, "Could not set up genesis state")
require.NoError(t, bs.SetSlot(params.BeaconConfig().SlotsPerEpoch))
require.NoError(t, bs.SetBlockRoots(roots))
chainSlot := params.BeaconConfig().SlotsPerEpoch
chain := &mockChain.ChainService{
State: bs, Root: genesisRoot[:], Slot: &chainSlot,
}
s := &Server{
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{params.BeaconConfig().SlotsPerEpoch: bs}},
HeadFetcher: chain,
TimeFetcher: chain,
OptimisticModeFetcher: chain,
SyncChecker: &mockSync.Sync{IsSyncing: false},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
}
s.ProposerSlotIndexCache.SetProposerAndPayloadIDs(1, 1, [8]byte{1}, [32]byte{2})
s.ProposerSlotIndexCache.SetProposerAndPayloadIDs(31, 2, [8]byte{2}, [32]byte{3})
s.ProposerSlotIndexCache.SetProposerAndPayloadIDs(32, 4309, [8]byte{3}, [32]byte{4})
request := httptest.NewRequest(http.MethodGet, "http://www.example.com/eth/v1/validator/duties/proposer/{epoch}", nil)
request = mux.SetURLVars(request, map[string]string{"epoch": "1"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetProposerDuties(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
vid, _, has := s.ProposerSlotIndexCache.GetProposerPayloadIDs(1, [32]byte{})
require.Equal(t, false, has)
require.Equal(t, primitives.ValidatorIndex(0), vid)
vid, _, has = s.ProposerSlotIndexCache.GetProposerPayloadIDs(2, [32]byte{})
require.Equal(t, false, has)
require.Equal(t, primitives.ValidatorIndex(0), vid)
vid, _, has = s.ProposerSlotIndexCache.GetProposerPayloadIDs(32, [32]byte{})
require.Equal(t, true, has)
require.Equal(t, primitives.ValidatorIndex(10565), vid)
})
t.Run("epoch out of bounds", func(t *testing.T) {
bs, err := transition.GenesisBeaconState(context.Background(), deposits, 0, eth1Data)
require.NoError(t, err, "Could not set up genesis state")

View File

@@ -11,19 +11,19 @@ type AggregateAttestationResponse struct {
}
type SubmitContributionAndProofsRequest struct {
Data []*shared.SignedContributionAndProof `json:"data" validate:"required,dive"`
Data []*shared.SignedContributionAndProof `json:"data"`
}
type SubmitAggregateAndProofsRequest struct {
Data []*shared.SignedAggregateAttestationAndProof `json:"data" validate:"required,dive"`
Data []*shared.SignedAggregateAttestationAndProof `json:"data"`
}
type SubmitSyncCommitteeSubscriptionsRequest struct {
Data []*shared.SyncCommitteeSubscription `json:"data" validate:"required,dive"`
Data []*shared.SyncCommitteeSubscription `json:"data"`
}
type SubmitBeaconCommitteeSubscriptionsRequest struct {
Data []*shared.BeaconCommitteeSubscription `json:"data" validate:"required,dive"`
Data []*shared.BeaconCommitteeSubscription `json:"data"`
}
type GetAttestationDataResponse struct {

View File

@@ -19,6 +19,7 @@ go_library(
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
],

View File

@@ -3,7 +3,9 @@ package lookup
import (
"context"
"strconv"
"strings"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
@@ -47,6 +49,7 @@ type BeaconDbBlocker struct {
// - "justified"
// - <slot>
// - <hex encoded block root with '0x' prefix>
// - <block root>
func (p *BeaconDbBlocker) Block(ctx context.Context, id []byte) (interfaces.ReadOnlySignedBeaconBlock, error) {
var err error
var blk interfaces.ReadOnlySignedBeaconBlock
@@ -69,7 +72,18 @@ func (p *BeaconDbBlocker) Block(ctx context.Context, id []byte) (interfaces.Read
return nil, errors.Wrap(err, "could not retrieve genesis block")
}
default:
if len(id) == 32 {
stringId := strings.ToLower(string(id))
if len(stringId) >= 2 && stringId[:2] == "0x" {
decoded, err := hexutil.Decode(string(id))
if err != nil {
e := NewBlockIdParseError(err)
return nil, &e
}
blk, err = p.BeaconDB.Block(ctx, bytesutil.ToBytes32(decoded))
if err != nil {
return nil, errors.Wrap(err, "could not retrieve block")
}
} else if len(id) == 32 {
blk, err = p.BeaconDB.Block(ctx, bytesutil.ToBytes32(id))
if err != nil {
return nil, errors.Wrap(err, "could not retrieve block")

View File

@@ -6,6 +6,7 @@ import (
"reflect"
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
dbtesting "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil"
@@ -116,6 +117,11 @@ func TestGetBlock(t *testing.T) {
blockID: bytesutil.PadTo([]byte("hi there"), 32),
want: nil,
},
{
name: "hex",
blockID: []byte(hexutil.Encode(blkContainers[20].BlockRoot)),
want: blkContainers[20].Block.(*ethpbalpha.BeaconBlockContainer_Phase0Block).Phase0Block,
},
{
name: "no block",
blockID: []byte("105"),

View File

@@ -7,6 +7,7 @@ import (
"strconv"
"strings"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
@@ -94,6 +95,7 @@ type BeaconDbStater struct {
// - "justified"
// - <slot>
// - <hex encoded state root with '0x' prefix>
// - <state root>
func (p *BeaconDbStater) State(ctx context.Context, stateId []byte) (state.BeaconState, error) {
var (
s state.BeaconState
@@ -141,7 +143,15 @@ func (p *BeaconDbStater) State(ctx context.Context, stateId []byte) (state.Beaco
return nil, errors.Wrap(err, "could not get justified state")
}
default:
if len(stateId) == 32 {
stringId := strings.ToLower(string(stateId))
if len(stringId) >= 2 && stringId[:2] == "0x" {
decoded, parseErr := hexutil.Decode(string(stateId))
if parseErr != nil {
e := NewStateIdParseError(parseErr)
return nil, &e
}
s, err = p.stateByRoot(ctx, decoded)
} else if len(stateId) == 32 {
s, err = p.stateByRoot(ctx, stateId)
} else {
slotNumber, parseErr := strconv.ParseUint(stateIdString, 10, 64)

View File

@@ -140,7 +140,26 @@ func TestGetState(t *testing.T) {
assert.DeepEqual(t, stateRoot, sRoot)
})
t.Run("hex_root", func(t *testing.T) {
t.Run("hex", func(t *testing.T) {
hex := "0x" + strings.Repeat("0", 63) + "1"
root, err := hexutil.Decode(hex)
require.NoError(t, err)
stateGen := mockstategen.NewMockService()
stateGen.StatesByRoot[bytesutil.ToBytes32(root)] = newBeaconState
p := BeaconDbStater{
ChainInfoFetcher: &chainMock.ChainService{State: newBeaconState},
StateGenService: stateGen,
}
s, err := p.State(ctx, []byte(hex))
require.NoError(t, err)
sRoot, err := s.HashTreeRoot(ctx)
require.NoError(t, err)
assert.DeepEqual(t, stateRoot, sRoot)
})
t.Run("root", func(t *testing.T) {
stateId, err := hexutil.Decode("0x" + strings.Repeat("0", 63) + "1")
require.NoError(t, err)
stateGen := mockstategen.NewMockService()
@@ -158,7 +177,7 @@ func TestGetState(t *testing.T) {
assert.DeepEqual(t, stateRoot, sRoot)
})
t.Run("hex_root_not_found", func(t *testing.T) {
t.Run("root not found", func(t *testing.T) {
p := BeaconDbStater{
ChainInfoFetcher: &chainMock.ChainService{State: newBeaconState},
}

View File

@@ -601,10 +601,6 @@ func (bs *Server) GetValidatorQueue(
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get active validator count: %v", err)
}
churnLimit, err := helpers.ValidatorChurnLimit(activeValidatorCount)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not compute churn limit: %v", err)
}
exitQueueEpoch := primitives.Epoch(0)
for _, i := range exitEpochs {
@@ -619,7 +615,8 @@ func (bs *Server) GetValidatorQueue(
}
}
// Prevent churn limit from causing index out of bound issues.
if churnLimit < exitQueueChurn {
exitChurnLimit := helpers.ValidatorExitChurnLimit(activeValidatorCount)
if exitChurnLimit < exitQueueChurn {
// If we are above the churn limit, we simply increase the churn by one.
exitQueueEpoch++
}
@@ -645,6 +642,10 @@ func (bs *Server) GetValidatorQueue(
exitQueueKeys[i] = vals[idx].PublicKey
}
churnLimit := helpers.ValidatorActivationChurnLimit(activeValidatorCount)
if headState.Version() >= version.Deneb {
churnLimit = helpers.ValidatorActivationChurnLimitDeneb(activeValidatorCount)
}
return &ethpb.ValidatorQueue{
ChurnLimit: churnLimit,
ActivationPublicKeys: activationQueueKeys,

View File

@@ -28,6 +28,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
@@ -422,10 +423,11 @@ func (is *infostream) calculateActivationTimeForPendingValidators(res []*ethpb.V
// Loop over epochs, roughly simulating progression.
for curEpoch := epoch + 1; len(sortedIndices) > 0 && len(pendingValidators) > 0; curEpoch++ {
toProcess, err := helpers.ValidatorChurnLimit(numAttestingValidators)
if err != nil {
log.WithError(err).Error("Could not determine validator churn limit")
toProcess := helpers.ValidatorActivationChurnLimit(numAttestingValidators)
if headState.Version() >= version.Deneb {
toProcess = helpers.ValidatorActivationChurnLimitDeneb(numAttestingValidators)
}
if toProcess > uint64(len(sortedIndices)) {
toProcess = uint64(len(sortedIndices))
}

View File

@@ -1370,8 +1370,7 @@ func TestServer_GetValidatorQueue_PendingActivation(t *testing.T) {
}
activeValidatorCount, err := helpers.ActiveValidatorCount(context.Background(), headState, coreTime.CurrentEpoch(headState))
require.NoError(t, err)
wantChurn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
require.NoError(t, err)
wantChurn := helpers.ValidatorActivationChurnLimit(activeValidatorCount)
assert.Equal(t, wantChurn, res.ChurnLimit)
assert.DeepEqual(t, wanted, res.ActivationPublicKeys)
wantedActiveIndices := []primitives.ValidatorIndex{2, 1, 0}
@@ -1412,8 +1411,7 @@ func TestServer_GetValidatorQueue_ExitedValidatorLeavesQueue(t *testing.T) {
}
activeValidatorCount, err := helpers.ActiveValidatorCount(context.Background(), headState, coreTime.CurrentEpoch(headState))
require.NoError(t, err)
wantChurn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
require.NoError(t, err)
wantChurn := helpers.ValidatorExitChurnLimit(activeValidatorCount)
assert.Equal(t, wantChurn, res.ChurnLimit)
assert.DeepEqual(t, wanted, res.ExitPublicKeys)
wantedExitIndices := []primitives.ValidatorIndex{1}
@@ -1472,8 +1470,7 @@ func TestServer_GetValidatorQueue_PendingExit(t *testing.T) {
}
activeValidatorCount, err := helpers.ActiveValidatorCount(context.Background(), headState, coreTime.CurrentEpoch(headState))
require.NoError(t, err)
wantChurn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
require.NoError(t, err)
wantChurn := helpers.ValidatorExitChurnLimit(activeValidatorCount)
assert.Equal(t, wantChurn, res.ChurnLimit)
assert.DeepEqual(t, wanted, res.ExitPublicKeys)
}

View File

@@ -1,34 +0,0 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"attestations.go",
"blocks.go",
"server.go",
],
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/prysm/v1alpha1/slasher",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/slasher:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"attestations_test.go",
"server_test.go",
],
embed = [":go_default_library"],
deps = [
"//beacon-chain/slasher/mock:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/require:go_default_library",
],
)

View File

@@ -1,43 +0,0 @@
package slasher
import (
"context"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// IsSlashableAttestation returns an attester slashing if an input
// attestation is found to be slashable.
func (s *Server) IsSlashableAttestation(
ctx context.Context, req *ethpb.IndexedAttestation,
) (*ethpb.AttesterSlashingResponse, error) {
attesterSlashings, err := s.SlashingChecker.IsSlashableAttestation(ctx, req)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not determine if attestation is slashable: %v", err)
}
if len(attesterSlashings) > 0 {
return &ethpb.AttesterSlashingResponse{
AttesterSlashings: attesterSlashings,
}, nil
}
return &ethpb.AttesterSlashingResponse{}, nil
}
// HighestAttestations returns the highest source and target epochs attested for
// validator indices that have been observed by slasher.
func (s *Server) HighestAttestations(
ctx context.Context, req *ethpb.HighestAttestationRequest,
) (*ethpb.HighestAttestationResponse, error) {
valIndices := make([]primitives.ValidatorIndex, len(req.ValidatorIndices))
for i, valIdx := range req.ValidatorIndices {
valIndices[i] = primitives.ValidatorIndex(valIdx)
}
atts, err := s.SlashingChecker.HighestAttestations(ctx, valIndices)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get highest attestations: %v", err)
}
return &ethpb.HighestAttestationResponse{Attestations: atts}, nil
}

View File

@@ -1,63 +0,0 @@
package slasher
import (
"context"
"testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/slasher/mock"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/require"
)
func TestServer_HighestAttestations(t *testing.T) {
highestAtts := map[primitives.ValidatorIndex]*ethpb.HighestAttestation{
0: {
ValidatorIndex: 0,
HighestSourceEpoch: 1,
HighestTargetEpoch: 2,
},
1: {
ValidatorIndex: 1,
HighestSourceEpoch: 2,
HighestTargetEpoch: 3,
},
}
mockSlasher := &mock.MockSlashingChecker{
HighestAtts: highestAtts,
}
s := Server{SlashingChecker: mockSlasher}
ctx := context.Background()
t.Run("single index found", func(t *testing.T) {
resp, err := s.HighestAttestations(ctx, &ethpb.HighestAttestationRequest{
ValidatorIndices: []uint64{0},
})
require.NoError(t, err)
require.Equal(t, 1, len(resp.Attestations))
require.DeepEqual(t, highestAtts[0], resp.Attestations[0])
})
t.Run("single index not found", func(t *testing.T) {
resp, err := s.HighestAttestations(ctx, &ethpb.HighestAttestationRequest{
ValidatorIndices: []uint64{3},
})
require.NoError(t, err)
require.Equal(t, 0, len(resp.Attestations))
})
t.Run("multiple indices all found", func(t *testing.T) {
resp, err := s.HighestAttestations(ctx, &ethpb.HighestAttestationRequest{
ValidatorIndices: []uint64{0, 1},
})
require.NoError(t, err)
require.Equal(t, 2, len(resp.Attestations))
require.DeepEqual(t, highestAtts[0], resp.Attestations[0])
require.DeepEqual(t, highestAtts[1], resp.Attestations[1])
})
t.Run("multiple indices some not found", func(t *testing.T) {
resp, err := s.HighestAttestations(ctx, &ethpb.HighestAttestationRequest{
ValidatorIndices: []uint64{0, 3},
})
require.NoError(t, err)
require.Equal(t, 1, len(resp.Attestations))
require.DeepEqual(t, highestAtts[0], resp.Attestations[0])
})
}

View File

@@ -1,28 +0,0 @@
package slasher
import (
"context"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// IsSlashableBlock returns a proposer slashing if an input
// signed beacon block header is found to be slashable.
func (s *Server) IsSlashableBlock(
ctx context.Context, req *ethpb.SignedBeaconBlockHeader,
) (*ethpb.ProposerSlashingResponse, error) {
proposerSlashing, err := s.SlashingChecker.IsSlashableBlock(ctx, req)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not determine if block is slashable: %v", err)
}
if proposerSlashing == nil {
return &ethpb.ProposerSlashingResponse{
ProposerSlashings: []*ethpb.ProposerSlashing{},
}, nil
}
return &ethpb.ProposerSlashingResponse{
ProposerSlashings: []*ethpb.ProposerSlashing{proposerSlashing},
}, nil
}

View File

@@ -1,12 +0,0 @@
// Package slasher defines a gRPC server implementation of a slasher service
// which allows for checking if attestations or blocks are slashable.
package slasher
import (
slasherservice "github.com/prysmaticlabs/prysm/v4/beacon-chain/slasher"
)
// Server defines a server implementation of the gRPC slasher service.
type Server struct {
SlashingChecker slasherservice.SlashingChecker
}

View File

@@ -1,54 +0,0 @@
package slasher
import (
"context"
"testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/slasher/mock"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/require"
)
func TestServer_IsSlashableAttestation_SlashingFound(t *testing.T) {
mockSlasher := &mock.MockSlashingChecker{
AttesterSlashingFound: true,
}
s := Server{SlashingChecker: mockSlasher}
ctx := context.Background()
slashing, err := s.IsSlashableAttestation(ctx, &ethpb.IndexedAttestation{})
require.NoError(t, err)
require.Equal(t, true, len(slashing.AttesterSlashings) > 0)
}
func TestServer_IsSlashableAttestation_SlashingNotFound(t *testing.T) {
mockSlasher := &mock.MockSlashingChecker{
AttesterSlashingFound: false,
}
s := Server{SlashingChecker: mockSlasher}
ctx := context.Background()
slashing, err := s.IsSlashableAttestation(ctx, &ethpb.IndexedAttestation{})
require.NoError(t, err)
require.Equal(t, true, len(slashing.AttesterSlashings) == 0)
}
func TestServer_IsSlashableBlock_SlashingFound(t *testing.T) {
mockSlasher := &mock.MockSlashingChecker{
ProposerSlashingFound: true,
}
s := Server{SlashingChecker: mockSlasher}
ctx := context.Background()
slashing, err := s.IsSlashableBlock(ctx, &ethpb.SignedBeaconBlockHeader{})
require.NoError(t, err)
require.Equal(t, true, len(slashing.ProposerSlashings) > 0)
}
func TestServer_IsSlashableBlock_SlashingNotFound(t *testing.T) {
mockSlasher := &mock.MockSlashingChecker{
ProposerSlashingFound: false,
}
s := Server{SlashingChecker: mockSlasher}
ctx := context.Background()
slashing, err := s.IsSlashableBlock(ctx, &ethpb.SignedBeaconBlockHeader{})
require.NoError(t, err)
require.Equal(t, true, len(slashing.ProposerSlashings) == 0)
}

View File

@@ -7,6 +7,7 @@ go_library(
"assignments.go",
"attester.go",
"blocks.go",
"construct_generic_block.go",
"exit.go",
"log.go",
"proposer.go",

View File

@@ -191,8 +191,6 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
for _, slot := range nextProposerIndexToSlots[idx] {
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, idx, [8]byte{} /* payloadID */, [32]byte{} /* head root */)
}
// Prune payload ID cache for any slots before request slot.
vs.ProposerSlotIndexCache.PrunePayloadIDs(epochStartSlot)
} else {
// If the validator isn't in the beacon state, try finding their deposit to determine their status.
// We don't need the lastActiveValidatorFn because we don't use the response in this.
@@ -237,6 +235,8 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
core.AssignValidatorToSubnetProto(pubKey, assignment.Status)
core.AssignValidatorToSubnetProto(pubKey, nextAssignment.Status)
}
// Prune payload ID cache for any slots before request slot.
vs.ProposerSlotIndexCache.PrunePayloadIDs(epochStartSlot)
return &ethpb.DutiesResponse{
Duties: validatorAssignments,

View File

@@ -0,0 +1,74 @@
package validator
import (
"fmt"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"google.golang.org/protobuf/proto"
)
// constructGenericBeaconBlock constructs a `GenericBeaconBlock` based on the block version and other parameters.
func (vs *Server) constructGenericBeaconBlock(sBlk interfaces.SignedBeaconBlock, blindBlobs []*ethpb.BlindedBlobSidecar, fullBlobs []*ethpb.BlobSidecar) (*ethpb.GenericBeaconBlock, error) {
if sBlk == nil || sBlk.Block() == nil {
return nil, fmt.Errorf("block cannot be nil")
}
blockProto, err := sBlk.Block().Proto()
if err != nil {
return nil, err
}
isBlinded := sBlk.IsBlinded()
payloadValue := sBlk.ValueInGwei()
switch sBlk.Version() {
case version.Deneb:
return vs.constructDenebBlock(blockProto, isBlinded, payloadValue, blindBlobs, fullBlobs), nil
case version.Capella:
return vs.constructCapellaBlock(blockProto, isBlinded, payloadValue), nil
case version.Bellatrix:
return vs.constructBellatrixBlock(blockProto, isBlinded, payloadValue), nil
case version.Altair:
return vs.constructAltairBlock(blockProto), nil
case version.Phase0:
return vs.constructPhase0Block(blockProto), nil
default:
return nil, fmt.Errorf("unknown block version: %d", sBlk.Version())
}
}
// Helper functions for constructing blocks for each version
func (vs *Server) constructDenebBlock(blockProto proto.Message, isBlinded bool, payloadValue uint64, blindBlobs []*ethpb.BlindedBlobSidecar, fullBlobs []*ethpb.BlobSidecar) *ethpb.GenericBeaconBlock {
if isBlinded {
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedDeneb{BlindedDeneb: &ethpb.BlindedBeaconBlockAndBlobsDeneb{Block: blockProto.(*ethpb.BlindedBeaconBlockDeneb), Blobs: blindBlobs}}, IsBlinded: true, PayloadValue: payloadValue}
}
blockAndBlobs := &ethpb.BeaconBlockAndBlobsDeneb{
Block: blockProto.(*ethpb.BeaconBlockDeneb),
Blobs: fullBlobs,
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Deneb{Deneb: blockAndBlobs}, IsBlinded: false, PayloadValue: payloadValue}
}
func (vs *Server) constructCapellaBlock(pb proto.Message, isBlinded bool, payloadValue uint64) *ethpb.GenericBeaconBlock {
if isBlinded {
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedCapella{BlindedCapella: pb.(*ethpb.BlindedBeaconBlockCapella)}, IsBlinded: true, PayloadValue: payloadValue}
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Capella{Capella: pb.(*ethpb.BeaconBlockCapella)}, IsBlinded: false, PayloadValue: payloadValue}
}
func (vs *Server) constructBellatrixBlock(pb proto.Message, isBlinded bool, payloadValue uint64) *ethpb.GenericBeaconBlock {
if isBlinded {
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedBellatrix{BlindedBellatrix: pb.(*ethpb.BlindedBeaconBlockBellatrix)}, IsBlinded: true, PayloadValue: payloadValue}
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Bellatrix{Bellatrix: pb.(*ethpb.BeaconBlockBellatrix)}, IsBlinded: false, PayloadValue: payloadValue}
}
func (vs *Server) constructAltairBlock(pb proto.Message) *ethpb.GenericBeaconBlock {
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Altair{Altair: pb.(*ethpb.BeaconBlockAltair)}}
}
func (vs *Server) constructPhase0Block(pb proto.Message) *ethpb.GenericBeaconBlock {
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Phase0{Phase0: pb.(*ethpb.BeaconBlock)}}
}

View File

@@ -0,0 +1,130 @@
package validator
import (
"testing"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/util"
"github.com/stretchr/testify/require"
)
func TestConstructGenericBeaconBlock(t *testing.T) {
vs := &Server{}
// Test when sBlk or sBlk.Block() is nil
t.Run("NilBlock", func(t *testing.T) {
_, err := vs.constructGenericBeaconBlock(nil, nil, nil)
require.ErrorContains(t, err, "block cannot be nil")
})
// Test for Deneb version
t.Run("deneb block", func(t *testing.T) {
eb := util.NewBeaconBlockDeneb()
b, err := blocks.NewSignedBeaconBlock(eb)
require.NoError(t, err)
r1, err := b.Block().HashTreeRoot()
require.NoError(t, err)
scs := []*ethpb.BlobSidecar{
util.GenerateTestDenebBlobSidecar(r1, eb, 0, []byte{}),
util.GenerateTestDenebBlobSidecar(r1, eb, 1, []byte{}),
util.GenerateTestDenebBlobSidecar(r1, eb, 2, []byte{}),
util.GenerateTestDenebBlobSidecar(r1, eb, 3, []byte{}),
util.GenerateTestDenebBlobSidecar(r1, eb, 4, []byte{}),
util.GenerateTestDenebBlobSidecar(r1, eb, 5, []byte{}),
}
result, err := vs.constructGenericBeaconBlock(b, nil, scs)
require.NoError(t, err)
r2, err := result.GetDeneb().Block.HashTreeRoot()
require.NoError(t, err)
require.Equal(t, r1, r2)
require.Equal(t, len(result.GetDeneb().Blobs), len(scs))
require.Equal(t, result.IsBlinded, false)
})
// Test for blind Deneb version
t.Run("blind deneb block", func(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockDeneb())
require.NoError(t, err)
r1, err := b.Block().HashTreeRoot()
require.NoError(t, err)
scs := []*ethpb.BlindedBlobSidecar{{}, {}, {}, {}, {}, {}}
result, err := vs.constructGenericBeaconBlock(b, scs, nil)
require.NoError(t, err)
r2, err := result.GetBlindedDeneb().Block.HashTreeRoot()
require.NoError(t, err)
require.Equal(t, r1, r2)
require.Equal(t, len(result.GetBlindedDeneb().Blobs), len(scs))
require.Equal(t, result.IsBlinded, true)
})
// Test for Capella version
t.Run("capella block", func(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
require.NoError(t, err)
result, err := vs.constructGenericBeaconBlock(b, nil, nil)
require.NoError(t, err)
r1, err := result.GetCapella().HashTreeRoot()
require.NoError(t, err)
r2, err := b.Block().HashTreeRoot()
require.NoError(t, err)
require.Equal(t, r1, r2)
require.Equal(t, result.IsBlinded, false)
})
// Test for blind Capella version
t.Run("blind capella block", func(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockCapella())
require.NoError(t, err)
result, err := vs.constructGenericBeaconBlock(b, nil, nil)
require.NoError(t, err)
r1, err := result.GetBlindedCapella().HashTreeRoot()
require.NoError(t, err)
r2, err := b.Block().HashTreeRoot()
require.NoError(t, err)
require.Equal(t, r1, r2)
require.Equal(t, result.IsBlinded, true)
})
// Test for Bellatrix version
t.Run("bellatrix block", func(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockBellatrix())
require.NoError(t, err)
result, err := vs.constructGenericBeaconBlock(b, nil, nil)
require.NoError(t, err)
r1, err := result.GetBellatrix().HashTreeRoot()
require.NoError(t, err)
r2, err := b.Block().HashTreeRoot()
require.NoError(t, err)
require.Equal(t, r1, r2)
require.Equal(t, result.IsBlinded, false)
})
// Test for Altair version
t.Run("altair block", func(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockAltair())
require.NoError(t, err)
result, err := vs.constructGenericBeaconBlock(b, nil, nil)
require.NoError(t, err)
r1, err := result.GetAltair().HashTreeRoot()
require.NoError(t, err)
r2, err := b.Block().HashTreeRoot()
require.NoError(t, err)
require.Equal(t, r1, r2)
require.Equal(t, result.IsBlinded, false)
})
// Test for phase0 version
t.Run("phase0 block", func(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
result, err := vs.constructGenericBeaconBlock(b, nil, nil)
require.NoError(t, err)
r1, err := result.GetPhase0().HashTreeRoot()
require.NoError(t, err)
r2, err := b.Block().HashTreeRoot()
require.NoError(t, err)
require.Equal(t, r1, r2)
require.Equal(t, result.IsBlinded, false)
})
}

View File

@@ -110,7 +110,7 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
var blobBundle *enginev1.BlobsBundle
var blindBlobBundle *enginev1.BlindedBlobsBundle
if features.Get().BuildBlockParallel {
blindBlobBundle, blobBundle, err = vs.BuildBlockParallel(ctx, sBlk, head)
blindBlobBundle, blobBundle, err = vs.BuildBlockParallel(ctx, sBlk, head, req.SkipMevBoost)
if err != nil {
return nil, errors.Wrap(err, "could not build block in parallel")
}
@@ -154,6 +154,8 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
}
// There's no reason to try to get a builder bid if local override is true.
var builderPayload interfaces.ExecutionData
overrideBuilder = req.SkipMevBoost || overrideBuilder // Skip using mev-boost if requested by the caller.
if !overrideBuilder {
builderPayload, blindBlobBundle, err = vs.getBuilderPayloadAndBlobs(ctx, sBlk.Block().Slot(), sBlk.Block().ProposerIndex())
if err != nil {
@@ -194,45 +196,10 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
"validator": sBlk.Block().ProposerIndex(),
}).Info("Finished building block")
pb, err := sBlk.Block().Proto()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not convert block to proto: %v", err)
}
if slots.ToEpoch(req.Slot) >= params.BeaconConfig().DenebForkEpoch {
if sBlk.IsBlinded() {
blockAndBlobs := &ethpb.BlindedBeaconBlockAndBlobsDeneb{
Block: pb.(*ethpb.BlindedBeaconBlockDeneb),
Blobs: blindBlobs,
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedDeneb{BlindedDeneb: blockAndBlobs}}, nil
}
blockAndBlobs := &ethpb.BeaconBlockAndBlobsDeneb{
Block: pb.(*ethpb.BeaconBlockDeneb),
Blobs: fullBlobs,
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Deneb{Deneb: blockAndBlobs}}, nil
}
if slots.ToEpoch(req.Slot) >= params.BeaconConfig().CapellaForkEpoch {
if sBlk.IsBlinded() {
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedCapella{BlindedCapella: pb.(*ethpb.BlindedBeaconBlockCapella)}, IsBlinded: true, PayloadValue: sBlk.ValueInGwei()}, nil
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Capella{Capella: pb.(*ethpb.BeaconBlockCapella)}, IsBlinded: false, PayloadValue: sBlk.ValueInGwei()}, nil
}
if slots.ToEpoch(req.Slot) >= params.BeaconConfig().BellatrixForkEpoch {
if sBlk.IsBlinded() {
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedBellatrix{BlindedBellatrix: pb.(*ethpb.BlindedBeaconBlockBellatrix)}, IsBlinded: true, PayloadValue: sBlk.ValueInGwei()}, nil
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Bellatrix{Bellatrix: pb.(*ethpb.BeaconBlockBellatrix)}, IsBlinded: false, PayloadValue: sBlk.ValueInGwei()}, nil
}
if slots.ToEpoch(req.Slot) >= params.BeaconConfig().AltairForkEpoch {
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Altair{Altair: pb.(*ethpb.BeaconBlockAltair)}, IsBlinded: false, PayloadValue: 0}, nil
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Phase0{Phase0: pb.(*ethpb.BeaconBlock)}, IsBlinded: false, PayloadValue: 0}, nil
return vs.constructGenericBeaconBlock(sBlk, blindBlobs, fullBlobs)
}
func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.SignedBeaconBlock, head state.BeaconState) (*enginev1.BlindedBlobsBundle, *enginev1.BlobsBundle, error) {
func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.SignedBeaconBlock, head state.BeaconState, skipMevBoost bool) (*enginev1.BlindedBlobsBundle, *enginev1.BlobsBundle, error) {
// Build consensus fields in background
var wg sync.WaitGroup
wg.Add(1)
@@ -281,6 +248,7 @@ func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.Signed
// There's no reason to try to get a builder bid if local override is true.
var builderPayload interfaces.ExecutionData
var blindBlobsBundle *enginev1.BlindedBlobsBundle
overrideBuilder = overrideBuilder || skipMevBoost // Skip using mev-boost if requested by the caller.
if !overrideBuilder {
builderPayload, blindBlobsBundle, err = vs.getBuilderPayloadAndBlobs(ctx, sBlk.Block().Slot(), sBlk.Block().ProposerIndex())
if err != nil {

View File

@@ -91,42 +91,16 @@ func (vs *Server) getLocalPayloadAndBlobs(ctx context.Context, blk interfaces.Re
}
}
var parentHash []byte
var hasTerminalBlock bool
mergeComplete, err := blocks.IsMergeTransitionComplete(st)
if err != nil {
return nil, nil, false, err
}
t, err := slots.ToTime(st.GenesisTime(), slot)
if err != nil {
return nil, nil, false, err
}
if mergeComplete {
header, err := st.LatestExecutionPayloadHeader()
parentHash, err := vs.getParentBlockHash(ctx, st, slot)
switch {
case errors.Is(err, errActivationNotReached) || errors.Is(err, errNoTerminalBlockHash):
p, err := consensusblocks.WrappedExecutionPayload(emptyPayload())
if err != nil {
return nil, nil, false, err
}
parentHash = header.BlockHash()
} else {
if activationEpochNotReached(slot) {
p, err := consensusblocks.WrappedExecutionPayload(emptyPayload())
if err != nil {
return nil, nil, false, err
}
return p, nil, false, nil
}
parentHash, hasTerminalBlock, err = vs.getTerminalBlockHashIfExists(ctx, uint64(t.Unix()))
if err != nil {
return nil, nil, false, err
}
if !hasTerminalBlock {
p, err := consensusblocks.WrappedExecutionPayload(emptyPayload())
if err != nil {
return nil, nil, false, err
}
return p, nil, false, nil
}
return p, nil, false, nil
case err != nil:
return nil, nil, false, err
}
payloadIDCacheMiss.Inc()
@@ -148,6 +122,11 @@ func (vs *Server) getLocalPayloadAndBlobs(ctx context.Context, blk interfaces.Re
SafeBlockHash: justifiedBlockHash[:],
FinalizedBlockHash: finalizedBlockHash[:],
}
t, err := slots.ToTime(st.GenesisTime(), slot)
if err != nil {
return nil, nil, false, err
}
var attr payloadattribute.Attributer
switch st.Version() {
case version.Deneb:
@@ -271,6 +250,74 @@ func (vs *Server) getBuilderPayloadAndBlobs(ctx context.Context,
return vs.getPayloadHeaderFromBuilder(ctx, slot, vIdx)
}
var errActivationNotReached = errors.New("activation epoch not reached")
var errNoTerminalBlockHash = errors.New("no terminal block hash")
// getParentBlockHash retrieves the parent block hash of the block at the given slot.
// The function's behavior varies depending on the state version and whether the merge has been completed.
//
// For states of version Capella or later, the block hash is directly retrieved from the state's latest execution payload header.
//
// If the merge transition has been completed, the parent block hash is also retrieved from the state's latest execution payload header.
//
// If the activation epoch has not been reached, an errActivationNotReached error is returned.
//
// Otherwise, the terminal block hash is fetched based on the slot's time, and an error is returned if it doesn't exist.
func (vs *Server) getParentBlockHash(ctx context.Context, st state.BeaconState, slot primitives.Slot) ([]byte, error) {
if st.Version() >= version.Capella {
return getParentBlockHashPostCapella(st)
}
mergeComplete, err := blocks.IsMergeTransitionComplete(st)
if err != nil {
return nil, err
}
if mergeComplete {
return getParentBlockHashPostMerge(st)
}
if activationEpochNotReached(slot) {
return nil, errActivationNotReached
}
return getParentBlockHashPreMerge(ctx, vs, st, slot)
}
// getParentBlockHashPostCapella retrieves the parent block hash for states of version Capella or later.
func getParentBlockHashPostCapella(st state.BeaconState) ([]byte, error) {
header, err := st.LatestExecutionPayloadHeader()
if err != nil {
return nil, errors.Wrap(err, "could not get post capella payload header")
}
return header.BlockHash(), nil
}
// getParentBlockHashPostMerge retrieves the parent block hash after the merge has completed.
func getParentBlockHashPostMerge(st state.BeaconState) ([]byte, error) {
header, err := st.LatestExecutionPayloadHeader()
if err != nil {
return nil, errors.Wrap(err, "could not get post merge payload header")
}
return header.ParentHash(), nil
}
// getParentBlockHashPreMerge retrieves the parent block hash before the merge has completed.
func getParentBlockHashPreMerge(ctx context.Context, vs *Server, st state.BeaconState, slot primitives.Slot) ([]byte, error) {
t, err := slots.ToTime(st.GenesisTime(), slot)
if err != nil {
return nil, err
}
parentHash, hasTerminalBlock, err := vs.getTerminalBlockHashIfExists(ctx, uint64(t.Unix()))
if err != nil {
return nil, err
}
if !hasTerminalBlock {
return nil, errNoTerminalBlockHash
}
return parentHash, nil
}
// activationEpochNotReached returns true if activation epoch has not been reach.
// Which satisfy the following conditions in spec:
//

View File

@@ -35,7 +35,10 @@ func (vs *Server) GetSyncMessageBlockRoot(
// SubmitSyncMessage submits the sync committee message to the network.
// It also saves the sync committee message into the pending pool for block inclusion.
func (vs *Server) SubmitSyncMessage(ctx context.Context, msg *ethpb.SyncCommitteeMessage) (*emptypb.Empty, error) {
return &emptypb.Empty{}, vs.CoreService.SubmitSyncMessage(ctx, msg)
if err := vs.CoreService.SubmitSyncMessage(ctx, msg); err != nil {
return &emptypb.Empty{}, status.Errorf(core.ErrorReasonToGRPC(err.Reason), err.Err.Error())
}
return &emptypb.Empty{}, nil
}
// GetSyncSubcommitteeIndex is called by a sync committee participant to get

View File

@@ -432,6 +432,8 @@ func (s *Service) Start() {
s.cfg.Router.HandleFunc("/eth/v1/beacon/states/{state_id}/validator_count", httpServer.GetValidatorCount).Methods(http.MethodGet)
s.cfg.Router.HandleFunc("/eth/v1/beacon/states/{state_id}/committees", beaconChainServerV1.GetCommittees).Methods(http.MethodGet)
s.cfg.Router.HandleFunc("/eth/v1/beacon/states/{state_id}/fork", beaconChainServerV1.GetStateFork).Methods(http.MethodGet)
s.cfg.Router.HandleFunc("/eth/v1/beacon/blocks", beaconChainServerV1.PublishBlock).Methods(http.MethodPost)
s.cfg.Router.HandleFunc("/eth/v1/beacon/blinded_blocks", beaconChainServerV1.PublishBlindedBlock).Methods(http.MethodPost)
s.cfg.Router.HandleFunc("/eth/v2/beacon/blocks", beaconChainServerV1.PublishBlockV2).Methods(http.MethodPost)
s.cfg.Router.HandleFunc("/eth/v2/beacon/blinded_blocks", beaconChainServerV1.PublishBlindedBlockV2).Methods(http.MethodPost)
s.cfg.Router.HandleFunc("/eth/v1/beacon/blocks/{block_id}/root", beaconChainServerV1.GetBlockRoot).Methods(http.MethodGet)

View File

@@ -74,7 +74,6 @@ go_test(
"//beacon-chain/db/testing:go_default_library",
"//beacon-chain/forkchoice/doubly-linked-tree:go_default_library",
"//beacon-chain/operations/slashings/mock:go_default_library",
"//beacon-chain/slasher/mock:go_default_library",
"//beacon-chain/slasher/types:go_default_library",
"//beacon-chain/startup:go_default_library",
"//beacon-chain/state/stategen:go_default_library",

View File

@@ -1,13 +0,0 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["mock_slashing_checker.go"],
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/slasher/mock",
visibility = ["//visibility:public"],
deps = [
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
],
)

View File

@@ -1,73 +0,0 @@
package mock
import (
"context"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
)
type MockSlashingChecker struct {
AttesterSlashingFound bool
ProposerSlashingFound bool
HighestAtts map[primitives.ValidatorIndex]*ethpb.HighestAttestation
}
func (s *MockSlashingChecker) HighestAttestations(
_ context.Context, indices []primitives.ValidatorIndex,
) ([]*ethpb.HighestAttestation, error) {
atts := make([]*ethpb.HighestAttestation, 0, len(indices))
for _, valIdx := range indices {
att, ok := s.HighestAtts[valIdx]
if !ok {
continue
}
atts = append(atts, att)
}
return atts, nil
}
func (s *MockSlashingChecker) IsSlashableBlock(_ context.Context, _ *ethpb.SignedBeaconBlockHeader) (*ethpb.ProposerSlashing, error) {
if s.ProposerSlashingFound {
return &ethpb.ProposerSlashing{
Header_1: &ethpb.SignedBeaconBlockHeader{
Header: &ethpb.BeaconBlockHeader{
Slot: 0,
ProposerIndex: 0,
ParentRoot: params.BeaconConfig().ZeroHash[:],
StateRoot: params.BeaconConfig().ZeroHash[:],
BodyRoot: params.BeaconConfig().ZeroHash[:],
},
Signature: params.BeaconConfig().EmptySignature[:],
},
Header_2: &ethpb.SignedBeaconBlockHeader{
Header: &ethpb.BeaconBlockHeader{
Slot: 0,
ProposerIndex: 0,
ParentRoot: params.BeaconConfig().ZeroHash[:],
StateRoot: params.BeaconConfig().ZeroHash[:],
BodyRoot: params.BeaconConfig().ZeroHash[:],
},
Signature: params.BeaconConfig().EmptySignature[:],
},
}, nil
}
return nil, nil
}
func (s *MockSlashingChecker) IsSlashableAttestation(_ context.Context, _ *ethpb.IndexedAttestation) ([]*ethpb.AttesterSlashing, error) {
if s.AttesterSlashingFound {
return []*ethpb.AttesterSlashing{
{
Attestation_1: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{},
},
Attestation_2: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{},
},
},
}, nil
}
return nil, nil
}

View File

@@ -9,7 +9,6 @@ import (
"github.com/prysmaticlabs/prysm/v4/async/event"
mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
dbtest "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
mockslasher "github.com/prysmaticlabs/prysm/v4/beacon-chain/slasher/mock"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/startup"
mockSync "github.com/prysmaticlabs/prysm/v4/beacon-chain/sync/initial-sync/testing"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
@@ -21,7 +20,6 @@ import (
)
var _ = SlashingChecker(&Service{})
var _ = SlashingChecker(&mockslasher.MockSlashingChecker{})
func TestMain(m *testing.M) {
logrus.SetLevel(logrus.DebugLevel)

View File

@@ -86,6 +86,10 @@ go_test(
"readonly_validator_test.go",
"references_test.go",
"setters_attestation_test.go",
"setters_eth1_test.go",
"setters_misc_test.go",
"setters_participation_test.go",
"setters_validator_test.go",
"setters_withdrawal_test.go",
"state_fuzz_test.go",
"state_test.go",

View File

@@ -157,12 +157,12 @@ func (b *BeaconState) NumValidators() int {
//
// WARNING: This method is potentially unsafe, as it exposes the actual validator registry.
func (b *BeaconState) ReadFromEveryValidator(f func(idx int, val state.ReadOnlyValidator) error) error {
b.lock.RLock()
defer b.lock.RUnlock()
if b.validators == nil {
return errors.New("nil validators in state")
}
b.lock.RLock()
validators := b.validators
b.lock.RUnlock()
for i, v := range validators {
v, err := NewValidator(v)

View File

@@ -90,8 +90,8 @@ func (b *BeaconState) AppendPreviousEpochAttestations(val *ethpb.PendingAttestat
}
if b.sharedFieldReferences[types.PreviousEpochAttestations].Refs() > 1 {
atts = make([]*ethpb.PendingAttestation, len(b.previousEpochAttestations))
copy(atts, b.previousEpochAttestations)
atts = make([]*ethpb.PendingAttestation, 0, len(b.previousEpochAttestations)+1)
atts = append(atts, b.previousEpochAttestations...)
b.sharedFieldReferences[types.PreviousEpochAttestations].MinusRef()
b.sharedFieldReferences[types.PreviousEpochAttestations] = stateutil.NewRef(1)
}

View File

@@ -74,3 +74,27 @@ func TestAppendBeyondIndicesLimit(t *testing.T) {
assert.Equal(t, true, s.rebuildTrie[types.Validators])
assert.Equal(t, len(s.dirtyIndices[types.Validators]), 0)
}
func BenchmarkAppendPreviousEpochAttestations(b *testing.B) {
st, err := InitializeFromProtoPhase0(&ethpb.BeaconState{})
require.NoError(b, err)
max := uint64(params.BeaconConfig().PreviousEpochAttestationsLength())
if max < 2 {
b.Fatalf("previous epoch attestations length is less than 2: %d", max)
}
for i := uint64(0); i < max-2; i++ {
err := st.AppendPreviousEpochAttestations(&ethpb.PendingAttestation{Data: &ethpb.AttestationData{Slot: primitives.Slot(i)}})
require.NoError(b, err)
}
b.ResetTimer()
ref := st.Copy()
for i := 0; i < b.N; i++ {
err := ref.AppendPreviousEpochAttestations(&ethpb.PendingAttestation{Data: &ethpb.AttestationData{Slot: primitives.Slot(i)}})
require.NoError(b, err)
ref = st.Copy()
}
}

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