mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-11 06:18:05 -05:00
Compare commits
299 Commits
checkpoint
...
log-elapse
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af367a3c16 | ||
|
|
7529ceda2f | ||
|
|
37baf27b5b | ||
|
|
c6bfefa0bc | ||
|
|
3446eaa5f2 | ||
|
|
4d278de34d | ||
|
|
49e8b9f1fe | ||
|
|
193866c731 | ||
|
|
b90ce1b60f | ||
|
|
2716184852 | ||
|
|
d056b21f09 | ||
|
|
8449d24ed0 | ||
|
|
b92b01f379 | ||
|
|
bcd180ee4d | ||
|
|
5f10e51a49 | ||
|
|
c4f03868ce | ||
|
|
d55c0d74dd | ||
|
|
807b71244b | ||
|
|
3ea8b79697 | ||
|
|
2744eba391 | ||
|
|
5566b2bb15 | ||
|
|
5507558678 | ||
|
|
52171da8c8 | ||
|
|
8cecd4e8bf | ||
|
|
ab9ece5263 | ||
|
|
29296de277 | ||
|
|
1d4477c6d4 | ||
|
|
69a9388515 | ||
|
|
649a345664 | ||
|
|
bd308b6d73 | ||
|
|
9abea200a5 | ||
|
|
8d13ed12e1 | ||
|
|
ce86bfae66 | ||
|
|
730678bf21 | ||
|
|
7423c61292 | ||
|
|
8029648849 | ||
|
|
ce0bd748eb | ||
|
|
9fe7d5833c | ||
|
|
3ae6dc9068 | ||
|
|
c489687336 | ||
|
|
a37e0f19c3 | ||
|
|
ee1ee623c0 | ||
|
|
e62cdcea16 | ||
|
|
546939dd33 | ||
|
|
f6eb6cd6bf | ||
|
|
a08c809073 | ||
|
|
ef0435493d | ||
|
|
6f15d2b0b2 | ||
|
|
5b4a87c512 | ||
|
|
5d7704e3a9 | ||
|
|
c4093f8adb | ||
|
|
4724b8430f | ||
|
|
55c8922f51 | ||
|
|
7f8d66c919 | ||
|
|
e2e5a0d86c | ||
|
|
4acc40ffed | ||
|
|
acc528ff75 | ||
|
|
87395141e8 | ||
|
|
a69901bd7c | ||
|
|
72d2bc7ce1 | ||
|
|
aecd34a1ea | ||
|
|
1daae0f5cf | ||
|
|
1583c77bdf | ||
|
|
8baf2179b3 | ||
|
|
d41947c60d | ||
|
|
d8f9ecbd4d | ||
|
|
9da43e4170 | ||
|
|
d3756ea4ea | ||
|
|
66418ec0ff | ||
|
|
e3963094d4 | ||
|
|
a5240cf4b8 | ||
|
|
78a90af679 | ||
|
|
b280e796da | ||
|
|
1e32cd5596 | ||
|
|
72c1720704 | ||
|
|
b6fd9e5315 | ||
|
|
fa1509c970 | ||
|
|
f9fbda80c2 | ||
|
|
9a56a5d101 | ||
|
|
5e8c49c871 | ||
|
|
4c7daf7a1f | ||
|
|
c4454cae78 | ||
|
|
1cedf4ba9a | ||
|
|
4ce3da7ecc | ||
|
|
70a6fc4222 | ||
|
|
bb126a9829 | ||
|
|
99deee57d1 | ||
|
|
4c23401a3b | ||
|
|
9636fde1eb | ||
|
|
a424f523a1 | ||
|
|
68e75d5851 | ||
|
|
176ea137ee | ||
|
|
b15cd763b6 | ||
|
|
8e78eae897 | ||
|
|
f6883f2aa9 | ||
|
|
19782d2563 | ||
|
|
032cf433c5 | ||
|
|
fa656a86a5 | ||
|
|
5f414b3e82 | ||
|
|
41b8b1a0f8 | ||
|
|
1d36ecb98d | ||
|
|
80cd539297 | ||
|
|
f47b6af910 | ||
|
|
443df77bb3 | ||
|
|
94fe3884a0 | ||
|
|
7d6046276d | ||
|
|
4f77ad20c8 | ||
|
|
1b5a6d4195 | ||
|
|
eae0db383f | ||
|
|
b56bd9e9d8 | ||
|
|
481d8847c2 | ||
|
|
42d5416658 | ||
|
|
a1d8833749 | ||
|
|
695389b7bb | ||
|
|
a67b8610f0 | ||
|
|
29eceba4d2 | ||
|
|
4c34e5d424 | ||
|
|
569375286e | ||
|
|
924758a557 | ||
|
|
eedcb529fd | ||
|
|
30e796a4f1 | ||
|
|
ea6ca456e6 | ||
|
|
4b75b991dd | ||
|
|
8eaf391918 | ||
|
|
cbdb3c9e86 | ||
|
|
12754adddc | ||
|
|
08a5155ee3 | ||
|
|
f99a0419ef | ||
|
|
4ad31f9c05 | ||
|
|
26876d64d7 | ||
|
|
3450923661 | ||
|
|
aba628b56b | ||
|
|
5effb92d11 | ||
|
|
2b55368c99 | ||
|
|
327903b7bb | ||
|
|
77f815a39f | ||
|
|
80dc725412 | ||
|
|
263c18992e | ||
|
|
9e220f9052 | ||
|
|
99878d104c | ||
|
|
a870bf7a74 | ||
|
|
dc42ff382f | ||
|
|
53b78a38a3 | ||
|
|
b45826e731 | ||
|
|
7b59ecac5e | ||
|
|
9149178a9c | ||
|
|
51ef502b04 | ||
|
|
8d891821ee | ||
|
|
762863ce6a | ||
|
|
41f5fa7524 | ||
|
|
09744bac70 | ||
|
|
f5db847237 | ||
|
|
8600f70b0b | ||
|
|
6fe430de44 | ||
|
|
42a5f96d3f | ||
|
|
e7f0fcf202 | ||
|
|
5ae564f1bf | ||
|
|
719109c219 | ||
|
|
64533a4b0c | ||
|
|
9fecd761d7 | ||
|
|
f84c95667c | ||
|
|
9af081797e | ||
|
|
e4e9f12c8b | ||
|
|
2f4e8beae6 | ||
|
|
81c7b90d26 | ||
|
|
dd3d65ff18 | ||
|
|
ac5a227aeb | ||
|
|
33f4d5c3cc | ||
|
|
67d7f8baee | ||
|
|
3c54aef7b1 | ||
|
|
938c28c42e | ||
|
|
8ddb2c26c4 | ||
|
|
cf0e78c2f6 | ||
|
|
4c0b262fdc | ||
|
|
33e675e204 | ||
|
|
e599f6a8a1 | ||
|
|
49c9ab9fda | ||
|
|
f90dec287b | ||
|
|
12c36cff9d | ||
|
|
bc565d9ee6 | ||
|
|
db67d5bad8 | ||
|
|
3bc0c2be54 | ||
|
|
1bed9ef749 | ||
|
|
ec772beeaf | ||
|
|
56407dde02 | ||
|
|
445f17881e | ||
|
|
183d40d8f1 | ||
|
|
87bc6aa5e5 | ||
|
|
5b5065b01d | ||
|
|
ee1c567561 | ||
|
|
ff1416c98d | ||
|
|
471c94031f | ||
|
|
9863fb3d6a | ||
|
|
f3c2d1a00b | ||
|
|
5d8879a4df | ||
|
|
abea0a11bc | ||
|
|
80ce1603bd | ||
|
|
ca478244e0 | ||
|
|
8a864b66a1 | ||
|
|
72f3b9e84b | ||
|
|
493e95060f | ||
|
|
e7e1ecd72f | ||
|
|
c286ac8b87 | ||
|
|
bde315224c | ||
|
|
00520705bc | ||
|
|
c7fcd804d7 | ||
|
|
985ac2e848 | ||
|
|
f4a0e98926 | ||
|
|
5f93ff10ea | ||
|
|
544248f60f | ||
|
|
3b41968510 | ||
|
|
7fc418042a | ||
|
|
9a03946706 | ||
|
|
33dd6dd5f2 | ||
|
|
56542e1958 | ||
|
|
e82d7b4c0b | ||
|
|
6cb69d8ff0 | ||
|
|
70b55a0191 | ||
|
|
50f4951194 | ||
|
|
1a14f2368d | ||
|
|
bb8cad58f1 | ||
|
|
05412c1f0e | ||
|
|
b03441fed8 | ||
|
|
fa7d7cef69 | ||
|
|
1caa6c969f | ||
|
|
eeb7d5bbfb | ||
|
|
d7c7d150b1 | ||
|
|
63c4d2eb2b | ||
|
|
9de1f694a0 | ||
|
|
8a79d06cbd | ||
|
|
5290ad93b8 | ||
|
|
2128208ef7 | ||
|
|
296323719c | ||
|
|
5e9583ea85 | ||
|
|
17196e0f80 | ||
|
|
c50d54000d | ||
|
|
85b3061d1b | ||
|
|
0146c5317a | ||
|
|
fcbc48ffd9 | ||
|
|
76ee51af9d | ||
|
|
370b0b97ed | ||
|
|
990ebd3fe3 | ||
|
|
54449c72e8 | ||
|
|
1dbd0b98eb | ||
|
|
09c3896c6b | ||
|
|
d494845e19 | ||
|
|
4d0c0f7234 | ||
|
|
bfe570b1aa | ||
|
|
56db696823 | ||
|
|
d312e15db8 | ||
|
|
907d4cf7e6 | ||
|
|
891353d6ad | ||
|
|
0adc08660c | ||
|
|
de31425dcd | ||
|
|
2094e0f21f | ||
|
|
2c6f554500 | ||
|
|
18a1e07711 | ||
|
|
5e432f5aaa | ||
|
|
284e2696cb | ||
|
|
7547aaa6ce | ||
|
|
953315c2cc | ||
|
|
9662d06b08 | ||
|
|
ecaea26ace | ||
|
|
63819e2690 | ||
|
|
a6d0cd06b3 | ||
|
|
2dbe4f5e67 | ||
|
|
2689d6814d | ||
|
|
69a681ddc0 | ||
|
|
7f9f1fd36c | ||
|
|
57c97eb561 | ||
|
|
f0f94a8193 | ||
|
|
87b0bf2c2a | ||
|
|
d8ad317dec | ||
|
|
ab5f488cf4 | ||
|
|
296d7464ad | ||
|
|
221c542e4f | ||
|
|
7ad32aaa96 | ||
|
|
3dc0969c0c | ||
|
|
0e18e835c3 | ||
|
|
8adfbfc382 | ||
|
|
68b0b5e0ce | ||
|
|
eede309e0f | ||
|
|
b11628dc53 | ||
|
|
ea3ae22d3b | ||
|
|
02bb39ddeb | ||
|
|
1618c1f55d | ||
|
|
73c8493fd7 | ||
|
|
a4f59a4f15 | ||
|
|
3c497efdb8 | ||
|
|
9f5daafbb7 | ||
|
|
11d7ffdfa8 | ||
|
|
c26b3305e6 | ||
|
|
38d8b63fbf | ||
|
|
aea67405c8 | ||
|
|
57d830f8b3 | ||
|
|
ac4b1ef4ea | ||
|
|
1d32119f5a | ||
|
|
3540cc7b05 | ||
|
|
191e7767a6 |
10
WORKSPACE
10
WORKSPACE
@@ -215,7 +215,7 @@ filegroup(
|
||||
url = "https://github.com/eth-clients/slashing-protection-interchange-tests/archive/b8413ca42dc92308019d0d4db52c87e9e125c4e9.tar.gz",
|
||||
)
|
||||
|
||||
consensus_spec_version = "v1.1.9"
|
||||
consensus_spec_version = "v1.1.10"
|
||||
|
||||
bls_test_version = "v0.1.1"
|
||||
|
||||
@@ -231,7 +231,7 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "207d9c326ba4fa1f34bab7b6169201c32f2611755db030909a3405873445e0ba",
|
||||
sha256 = "28043009cc2f6fc9804e73c8c1fc2cb27062f1591e6884f3015ae1dd7a276883",
|
||||
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/general.tar.gz" % consensus_spec_version,
|
||||
)
|
||||
|
||||
@@ -247,7 +247,7 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "a3995b39f412db236b2f1db909f288218da53cb53b9923b71dda9d144d68f40a",
|
||||
sha256 = "bc1a283ca068f310f04d70c4f6a8eaa0b8f7e9318073a8bdc2ee233111b4e339",
|
||||
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/minimal.tar.gz" % consensus_spec_version,
|
||||
)
|
||||
|
||||
@@ -263,7 +263,7 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "76cea7a4c8e32d458ad456b54bfbb30bc772481a91954a4cd97e229aa3023b1d",
|
||||
sha256 = "bbabb482c229ff9d4e2c7b77c992edb452f9d0af7c6d8dd4f922f06a7b101e81",
|
||||
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/mainnet.tar.gz" % consensus_spec_version,
|
||||
)
|
||||
|
||||
@@ -278,7 +278,7 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "0fc429684775f943250dce1f9c485ac25e26c6395d7f585c8d1317becec2ace7",
|
||||
sha256 = "408a5524548ad3fcf387f65ac7ec52781d9ee899499720bb12451b48a15818d4",
|
||||
strip_prefix = "consensus-specs-" + consensus_spec_version[1:],
|
||||
url = "https://github.com/ethereum/consensus-specs/archive/refs/tags/%s.tar.gz" % consensus_spec_version,
|
||||
)
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"checkpoint.go",
|
||||
"client.go",
|
||||
"doc.go",
|
||||
"errors.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/api/client/beacon",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/rpc/apimiddleware:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/detect:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/block: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",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@org_golang_x_mod//semver:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"checkpoint_test.go",
|
||||
"client_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//testing/require:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,279 +0,0 @@
|
||||
package beacon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/proto/detect"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
// WeakSubjectivityData represents the state root, block root and epoch of the BeaconState + SignedBeaconBlock
|
||||
// that falls at the beginning of the current weak subjectivity period. These values can be used to construct
|
||||
// a weak subjectivity checkpoint, or to download a BeaconState+SignedBeaconBlock pair that can be used to bootstrap
|
||||
// a new Beacon Node using Checkpoint Sync.
|
||||
type WeakSubjectivityData struct {
|
||||
BlockRoot [32]byte
|
||||
StateRoot [32]byte
|
||||
Epoch types.Epoch
|
||||
}
|
||||
|
||||
// CheckpointString returns the standard string representation of a Checkpoint for the block root and epoch to which the
|
||||
// WeakSubjectivityData value refers.
|
||||
// The format is a a hex-encoded block root, followed by the epoch of the block, separated by a colon. For example:
|
||||
// "0x1c35540cac127315fabb6bf29181f2ae0de1a3fc909d2e76ba771e61312cc49a:74888"
|
||||
func (wsd *WeakSubjectivityData) CheckpointString() string {
|
||||
return fmt.Sprintf("%#x:%d", wsd.BlockRoot, wsd.Epoch)
|
||||
}
|
||||
|
||||
// OriginData represents the BeaconState and SignedBeaconBlock necessary to start an empty Beacon Node
|
||||
// using Checkpoint Sync.
|
||||
type OriginData struct {
|
||||
wsd *WeakSubjectivityData
|
||||
sb []byte
|
||||
bb []byte
|
||||
st state.BeaconState
|
||||
b block.SignedBeaconBlock
|
||||
cf *detect.ConfigFork
|
||||
}
|
||||
|
||||
// WeakSubjectivity returns the WeakSubjectivityData determined by DownloadOriginData.
|
||||
func (od *OriginData) WeakSubjectivity() *WeakSubjectivityData {
|
||||
return od.wsd
|
||||
}
|
||||
|
||||
// SaveBlock saves the downloaded block to a unique file in the given path.
|
||||
// For readability and collision avoidance, the file name includes: type, config name, slot and root
|
||||
func (od *OriginData) SaveBlock(dir string) (string, error) {
|
||||
statePath := path.Join(dir, fname("state", od.cf, od.st.Slot(), od.wsd.BlockRoot))
|
||||
return statePath, os.WriteFile(statePath, od.sb, 0600)
|
||||
}
|
||||
|
||||
// SaveState saves the downloaded state to a unique file in the given path.
|
||||
// For readability and collision avoidance, the file name includes: type, config name, slot and root
|
||||
func (od *OriginData) SaveState(dir string) (string, error) {
|
||||
statePath := path.Join(dir, fname("state", od.cf, od.st.Slot(), od.wsd.StateRoot))
|
||||
return statePath, os.WriteFile(statePath, od.sb, 0600)
|
||||
}
|
||||
|
||||
// StateBytes returns the ssz-encoded bytes of the downloaded BeaconState value.
|
||||
func (od *OriginData) StateBytes() []byte {
|
||||
return od.sb
|
||||
}
|
||||
|
||||
// BlockBytes returns the ssz-encoded bytes of the downloaded SignedBeaconBlock value.
|
||||
func (od *OriginData) BlockBytes() []byte {
|
||||
return od.bb
|
||||
}
|
||||
|
||||
func fname(prefix string, cf *detect.ConfigFork, slot types.Slot, root [32]byte) string {
|
||||
return fmt.Sprintf("%s_%s_%s_%d-%#x.ssz", prefix, cf.ConfigName.String(), cf.Fork.String(), slot, root)
|
||||
}
|
||||
|
||||
// this method downloads the head state, which can be used to find the correct chain config
|
||||
// and use prysm's helper methods to compute the latest weak subjectivity epoch.
|
||||
func getWeakSubjectivityEpochFromHead(ctx context.Context, client *Client) (types.Epoch, error) {
|
||||
headBytes, err := client.GetState(ctx, IdHead)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cf, err := detect.ByState(headBytes)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "error detecting chain config for beacon state")
|
||||
}
|
||||
log.Printf("detected supported config in remote head state, name=%s, fork=%s", cf.ConfigName.String(), cf.Fork)
|
||||
headState, err := cf.UnmarshalBeaconState(headBytes)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "error unmarshaling state to correct version")
|
||||
}
|
||||
|
||||
// LatestWeakSubjectivityEpoch uses package-level vars from the params package, so we need to override it
|
||||
params.OverrideBeaconConfig(cf.Config)
|
||||
epoch, err := helpers.LatestWeakSubjectivityEpoch(ctx, headState)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "error computing the weak subjectivity epoch from head state")
|
||||
}
|
||||
|
||||
log.Printf("(computed client-side) weak subjectivity epoch = %d", epoch)
|
||||
return epoch, nil
|
||||
}
|
||||
|
||||
const (
|
||||
prysmMinimumVersion = "v2.0.7"
|
||||
prysmImplementationName = "Prysm"
|
||||
)
|
||||
|
||||
// ErrUnsupportedPrysmCheckpointVersion indicates remote beacon node can't be used for checkpoint retrieval.
|
||||
var ErrUnsupportedPrysmCheckpointVersion = errors.New("node does not meet minimum version requirements for checkpoint retrieval")
|
||||
|
||||
// for older endpoints or clients that do not support the weak_subjectivity api method (only prysm at release time)
|
||||
// we gather the necessary data for a checkpoint sync by:
|
||||
// - inspecting the remote server's head state and computing the weak subjectivity epoch locally
|
||||
// - requesting the state at the first slot of the epoch
|
||||
// - using hash_tree_root(state.latest_block_header) to compute the block the state integrates
|
||||
// - requesting that block by its root
|
||||
func downloadBackwardsCompatible(ctx context.Context, client *Client) (*OriginData, error) {
|
||||
log.Print("falling back to generic checkpoint derivation, weak_subjectivity API not supported by server")
|
||||
nv, err := client.GetNodeVersion(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to proceed with fallback method without confirming node version")
|
||||
}
|
||||
if nv.implementation == prysmImplementationName && semver.Compare(nv.semver, prysmMinimumVersion) < 0 {
|
||||
return nil, errors.Wrapf(ErrUnsupportedPrysmCheckpointVersion, "%s < minimum (%s)", nv.semver, prysmMinimumVersion)
|
||||
}
|
||||
epoch, err := getWeakSubjectivityEpochFromHead(ctx, client)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error computing weak subjectivity epoch via head state inspection")
|
||||
}
|
||||
|
||||
// use first slot of the epoch for the state slot
|
||||
slot, err := slots.EpochStart(epoch)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Error computing first slot of epoch=%d", epoch)
|
||||
}
|
||||
|
||||
log.Printf("requesting checkpoint state at slot %d", slot)
|
||||
// get the state at the first slot of the epoch
|
||||
stateBytes, err := client.GetState(ctx, IdFromSlot(slot))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to request state by slot from api, slot=%d", slot)
|
||||
}
|
||||
|
||||
// ConfigFork is used to unmarshal the BeaconState so we can read the block root in latest_block_header
|
||||
cf, err := detect.ByState(stateBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error detecting chain config for beacon state")
|
||||
}
|
||||
log.Printf("detected supported config in checkpoint state, name=%s, fork=%s", cf.ConfigName.String(), cf.Fork)
|
||||
|
||||
st, err := cf.UnmarshalBeaconState(stateBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error using detected config fork to unmarshal state bytes")
|
||||
}
|
||||
|
||||
// compute state and block roots
|
||||
stateRoot, err := st.HashTreeRoot(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error computing hash_tree_root of state")
|
||||
}
|
||||
|
||||
header := st.LatestBlockHeader()
|
||||
header.StateRoot = stateRoot[:]
|
||||
computedBlockRoot, err := header.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error while computing block root using state data")
|
||||
}
|
||||
|
||||
blockBytes, err := client.GetBlock(ctx, IdFromRoot(computedBlockRoot))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error requesting block by slot = %d", slot)
|
||||
}
|
||||
block, err := cf.UnmarshalBeaconBlock(blockBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to unmarshal block to a supported type using the detected fork schedule")
|
||||
}
|
||||
blockRoot, err := block.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error computing hash_tree_root for block obtained via root")
|
||||
}
|
||||
|
||||
log.Printf("BeaconState slot=%d, Block slot=%d", st.Slot(), block.Block().Slot())
|
||||
log.Printf("BeaconState htr=%#xd, Block state_root=%#x", stateRoot, block.Block().StateRoot())
|
||||
log.Printf("BeaconBlock root computed from state=%#x, Block HTR=%#x", computedBlockRoot, blockRoot)
|
||||
|
||||
return &OriginData{
|
||||
wsd: &WeakSubjectivityData{
|
||||
BlockRoot: blockRoot,
|
||||
StateRoot: stateRoot,
|
||||
Epoch: epoch,
|
||||
},
|
||||
st: st,
|
||||
sb: stateBytes,
|
||||
b: block,
|
||||
bb: blockBytes,
|
||||
cf: cf,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DownloadOriginData attempts to use the proposed weak_subjectivity beacon node api
|
||||
// to obtain the weak_subjectivity metadata (epoch, block_root, state_root) needed to sync
|
||||
// a beacon node from the canonical weak subjectivity checkpoint. As this is a proposed API
|
||||
// that will only be supported by prysm at first, in the event of a 404 we fallback to using a
|
||||
// different technique where we first download the head state which can be used to compute the
|
||||
// weak subjectivity epoch on the client side.
|
||||
func DownloadOriginData(ctx context.Context, client *Client) (*OriginData, error) {
|
||||
ws, err := client.GetWeakSubjectivity(ctx)
|
||||
if err != nil {
|
||||
// a 404 is expected if querying an endpoint that doesn't support the weak subjectivity checkpoint api
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
return nil, errors.Wrap(err, "unexpected API response for prysm-only weak subjectivity checkpoint API")
|
||||
}
|
||||
// ok so it's a 404, use the head state method
|
||||
return downloadBackwardsCompatible(ctx, client)
|
||||
}
|
||||
log.Printf("server weak subjectivity checkpoint response - epoch=%d, block_root=%#x, state_root=%#x", ws.Epoch, ws.BlockRoot, ws.StateRoot)
|
||||
|
||||
// use first slot of the epoch for the block slot
|
||||
slot, err := slots.EpochStart(ws.Epoch)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Error computing first slot of epoch=%d", ws.Epoch)
|
||||
}
|
||||
log.Printf("requesting checkpoint state at slot %d", slot)
|
||||
|
||||
stateBytes, err := client.GetState(ctx, IdFromSlot(slot))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to request state by slot from api, slot=%d", slot)
|
||||
}
|
||||
cf, err := detect.ByState(stateBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error detecting chain config for beacon state")
|
||||
}
|
||||
log.Printf("detected supported config in checkpoint state, name=%s, fork=%s", cf.ConfigName.String(), cf.Fork)
|
||||
|
||||
state, err := cf.UnmarshalBeaconState(stateBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error using detected config fork to unmarshal state bytes")
|
||||
}
|
||||
stateRoot, err := state.HashTreeRoot(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to compute htr for state at slot=%d", slot)
|
||||
}
|
||||
|
||||
blockRoot, err := state.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error computing hash_tree_root of latest_block_header")
|
||||
}
|
||||
blockBytes, err := client.GetBlock(ctx, IdFromRoot(ws.BlockRoot))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error requesting block by slot = %d", slot)
|
||||
}
|
||||
block, err := cf.UnmarshalBeaconBlock(blockBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to unmarshal block to a supported type using the detected fork schedule")
|
||||
}
|
||||
realBlockRoot, err := block.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error computing hash_tree_root of retrieved block")
|
||||
}
|
||||
log.Printf("BeaconState slot=%d, Block slot=%d", state.Slot(), block.Block().Slot())
|
||||
log.Printf("BeaconState htr=%#xd, Block state_root=%#x", stateRoot, block.Block().StateRoot())
|
||||
log.Printf("BeaconState latest_block_header htr=%#xd, block htr=%#x", blockRoot, realBlockRoot)
|
||||
return &OriginData{
|
||||
wsd: ws,
|
||||
st: state,
|
||||
b: block,
|
||||
sb: stateBytes,
|
||||
bb: blockBytes,
|
||||
cf: cf,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package beacon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
type testRT struct {
|
||||
rt func(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
func (rt *testRT) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if rt.rt != nil {
|
||||
return rt.rt(req)
|
||||
}
|
||||
return nil, errors.New("RoundTripper not implemented")
|
||||
}
|
||||
|
||||
var _ http.RoundTripper = &testRT{}
|
||||
|
||||
func marshalToEnvelope(val interface{}) ([]byte, error) {
|
||||
raw, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error marsaling value to place in data envelope")
|
||||
}
|
||||
env := struct {
|
||||
Data json.RawMessage `json:"data"`
|
||||
}{
|
||||
Data: raw,
|
||||
}
|
||||
return json.Marshal(env)
|
||||
}
|
||||
|
||||
func TestMarshalToEnvelope(t *testing.T) {
|
||||
d := struct {
|
||||
Version string `json:"version"`
|
||||
}{
|
||||
Version: "Prysm/v2.0.5 (linux amd64)",
|
||||
}
|
||||
encoded, err := marshalToEnvelope(d)
|
||||
require.NoError(t, err)
|
||||
expected := `{"data":{"version":"Prysm/v2.0.5 (linux amd64)"}}`
|
||||
require.Equal(t, expected, string(encoded))
|
||||
}
|
||||
|
||||
func TestFallbackVersionCheck(t *testing.T) {
|
||||
c := &Client{
|
||||
hc: &http.Client{},
|
||||
host: "localhost:3500",
|
||||
scheme: "http",
|
||||
}
|
||||
c.hc.Transport = &testRT{rt: func(req *http.Request) (*http.Response, error) {
|
||||
res := &http.Response{Request: req}
|
||||
switch req.URL.Path {
|
||||
case get_node_version_path:
|
||||
res.StatusCode = http.StatusOK
|
||||
b := bytes.NewBuffer(nil)
|
||||
d := struct {
|
||||
Version string `json:"version"`
|
||||
}{
|
||||
Version: "Prysm/v2.0.5 (linux amd64)",
|
||||
}
|
||||
encoded, err := marshalToEnvelope(d)
|
||||
require.NoError(t, err)
|
||||
b.Write(encoded)
|
||||
res.Body = io.NopCloser(b)
|
||||
case get_weak_subjectivity_path:
|
||||
res.StatusCode = http.StatusNotFound
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}}
|
||||
|
||||
ctx := context.Background()
|
||||
_, err := DownloadOriginData(ctx, c)
|
||||
require.ErrorIs(t, err, ErrUnsupportedPrysmCheckpointVersion)
|
||||
}
|
||||
@@ -1,418 +0,0 @@
|
||||
package beacon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
get_signed_block_path = "/eth/v2/beacon/blocks"
|
||||
get_block_root_path = "/eth/v1/beacon/blocks/{{.Id}}/root"
|
||||
get_fork_for_state_path = "/eth/v1/beacon/states/{{.Id}}/fork"
|
||||
get_weak_subjectivity_path = "/eth/v1/beacon/weak_subjectivity"
|
||||
get_fork_schedule_path = "/eth/v1/config/fork_schedule"
|
||||
get_state_path = "/eth/v2/debug/beacon/states"
|
||||
get_node_version_path = "/eth/v1/node/version"
|
||||
)
|
||||
|
||||
// StateOrBlockId represents the block_id / state_id parameters that several of the Eth Beacon API methods accept.
|
||||
// StateOrBlockId constants are defined for named identifiers, and helper methods are provided
|
||||
// for slot and root identifiers. Example text from the Eth Beacon Node API documentation:
|
||||
//
|
||||
// "Block identifier can be one of: "head" (canonical head in node's view), "genesis", "finalized",
|
||||
// <slot>, <hex encoded blockRoot with 0x prefix>."
|
||||
type StateOrBlockId string
|
||||
|
||||
const (
|
||||
IdFinalized StateOrBlockId = "finalized"
|
||||
IdGenesis StateOrBlockId = "genesis"
|
||||
IdHead StateOrBlockId = "head"
|
||||
IdJustified StateOrBlockId = "finalized"
|
||||
)
|
||||
|
||||
// IdFromRoot encodes a block root in the format expected by the API in places where a root can be used to identify
|
||||
// a BeaconState or SignedBeaconBlock.
|
||||
func IdFromRoot(r [32]byte) StateOrBlockId {
|
||||
return StateOrBlockId(fmt.Sprintf("%#x", r))
|
||||
}
|
||||
|
||||
// IdFromRoot encodes a Slot in the format expected by the API in places where a slot can be used to identify
|
||||
// a BeaconState or SignedBeaconBlock.
|
||||
func IdFromSlot(s types.Slot) StateOrBlockId {
|
||||
return StateOrBlockId(strconv.FormatUint(uint64(s), 10))
|
||||
}
|
||||
|
||||
// idTemplate is used to create template functions that can interpolate StateOrBlockId values.
|
||||
func idTemplate(ts string) func(StateOrBlockId) string {
|
||||
t := template.Must(template.New("").Parse(ts))
|
||||
f := func(id StateOrBlockId) string {
|
||||
b := bytes.NewBuffer(nil)
|
||||
err := t.Execute(b, struct{ Id string }{Id: string(id)})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("invalid idTemplate: %s", ts))
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
// run the template to ensure that it is valid
|
||||
// this should happen load time (using package scoped vars) to ensure runtime errors aren't possible
|
||||
_ = f(IdGenesis)
|
||||
return f
|
||||
}
|
||||
|
||||
// ClientOpt is a functional option for the Client type (http.Client wrapper)
|
||||
type ClientOpt func(*Client)
|
||||
|
||||
// WithTimeout sets the .Timeout attribute of the wrapped http.Client.
|
||||
func WithTimeout(timeout time.Duration) ClientOpt {
|
||||
return func(c *Client) {
|
||||
c.hc.Timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
// Client provides a collection of helper methods for calling the Eth Beacon Node API endpoints.
|
||||
type Client struct {
|
||||
hc *http.Client
|
||||
host string
|
||||
scheme string
|
||||
}
|
||||
|
||||
// NewClient constructs a new client with the provided options (ex WithTimeout).
|
||||
// `host` is the base host + port used to construct request urls. This value can be
|
||||
// a URL string, or NewClient will assume an http endpoint if just `host:port` is used.
|
||||
func NewClient(host string, opts ...ClientOpt) (*Client, error) {
|
||||
host, err := validHostname(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := &Client{
|
||||
hc: &http.Client{},
|
||||
scheme: "http",
|
||||
host: host,
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(c)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func validHostname(h string) (string, error) {
|
||||
// try to parse as url (being permissive)
|
||||
u, err := url.Parse(h)
|
||||
if err == nil && u.Host != "" {
|
||||
return u.Host, nil
|
||||
}
|
||||
// try to parse as host:port
|
||||
host, port, err := net.SplitHostPort(h)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%s:%s", host, port), nil
|
||||
}
|
||||
|
||||
func (c *Client) urlForPath(methodPath string) *url.URL {
|
||||
u := &url.URL{
|
||||
Scheme: c.scheme,
|
||||
Host: c.host,
|
||||
}
|
||||
u.Path = path.Join(u.Path, methodPath)
|
||||
return u
|
||||
}
|
||||
|
||||
type reqOption func(*http.Request)
|
||||
|
||||
func withSSZEncoding() reqOption {
|
||||
return func(req *http.Request) {
|
||||
req.Header.Set("Accept", "application/octet-stream")
|
||||
}
|
||||
}
|
||||
|
||||
// get is a generic, opinionated GET function to reduce boilerplate amongst the getters in this package.
|
||||
func (c *Client) get(ctx context.Context, path string, opts ...reqOption) ([]byte, error) {
|
||||
u := c.urlForPath(path)
|
||||
log.Printf("requesting %s", u.String())
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(req)
|
||||
}
|
||||
r, err := c.hc.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err = r.Body.Close()
|
||||
}()
|
||||
if r.StatusCode != http.StatusOK {
|
||||
return nil, non200Err(r)
|
||||
}
|
||||
b, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error reading http response body from GetBlock")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// GetBlock retrieves the SignedBeaconBlock for the given block id.
|
||||
// Block identifier can be one of: "head" (canonical head in node's view), "genesis", "finalized",
|
||||
// <slot>, <hex encoded blockRoot with 0x prefix>. Variables of type StateOrBlockId are exported by this package
|
||||
// for the named identifiers.
|
||||
// The return value contains the ssz-encoded bytes.
|
||||
func (c *Client) GetBlock(ctx context.Context, blockId StateOrBlockId) ([]byte, error) {
|
||||
blockPath := path.Join(get_signed_block_path, string(blockId))
|
||||
b, err := c.get(ctx, blockPath, withSSZEncoding())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error requesting state by id = %s", blockId)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
var getBlockRootTpl = idTemplate(get_block_root_path)
|
||||
|
||||
// GetBlockRoot retrieves the hash_tree_root of the BeaconBlock for the given block id.
|
||||
// Block identifier can be one of: "head" (canonical head in node's view), "genesis", "finalized",
|
||||
// <slot>, <hex encoded blockRoot with 0x prefix>. Variables of type StateOrBlockId are exported by this package
|
||||
// for the named identifiers.
|
||||
func (c *Client) GetBlockRoot(ctx context.Context, blockId StateOrBlockId) ([32]byte, error) {
|
||||
rootPath := getBlockRootTpl(blockId)
|
||||
b, err := c.get(ctx, rootPath)
|
||||
if err != nil {
|
||||
return [32]byte{}, errors.Wrapf(err, "error requesting block root by id = %s", blockId)
|
||||
}
|
||||
jsonr := &struct{ Data struct{ Root string } }{}
|
||||
err = json.Unmarshal(b, jsonr)
|
||||
if err != nil {
|
||||
return [32]byte{}, errors.Wrap(err, "error decoding json data from get block root response")
|
||||
}
|
||||
rs, err := hexutil.Decode(jsonr.Data.Root)
|
||||
if err != nil {
|
||||
return [32]byte{}, errors.Wrap(err, fmt.Sprintf("error decoding hex-encoded value %s", jsonr.Data.Root))
|
||||
}
|
||||
return bytesutil.ToBytes32(rs), nil
|
||||
}
|
||||
|
||||
var getForkTpl = idTemplate(get_fork_for_state_path)
|
||||
|
||||
// GetFork queries the Beacon Node API for the Fork from the state identified by stateId.
|
||||
// Block identifier can be one of: "head" (canonical head in node's view), "genesis", "finalized",
|
||||
// <slot>, <hex encoded blockRoot with 0x prefix>. Variables of type StateOrBlockId are exported by this package
|
||||
// for the named identifiers.
|
||||
func (c *Client) GetFork(ctx context.Context, stateId StateOrBlockId) (*ethpb.Fork, error) {
|
||||
body, err := c.get(ctx, getForkTpl(stateId))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error requesting fork by state id = %s", stateId)
|
||||
}
|
||||
fr := &forkResponse{}
|
||||
dataWrapper := &struct{ Data *forkResponse }{Data: fr}
|
||||
err = json.Unmarshal(body, dataWrapper)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error decoding json response in GetFork")
|
||||
}
|
||||
|
||||
return fr.Fork()
|
||||
}
|
||||
|
||||
// GetForkSchedule retrieve all forks, past present and future, of which this node is aware.
|
||||
func (c *Client) GetForkSchedule(ctx context.Context) (params.OrderedForkSchedule, error) {
|
||||
body, err := c.get(ctx, get_fork_schedule_path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error requesting fork schedule")
|
||||
}
|
||||
fsr := &forkScheduleResponse{}
|
||||
err = json.Unmarshal(body, fsr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ofs, err := fsr.OrderedForkSchedule()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("problem unmarshaling %s response", get_fork_schedule_path))
|
||||
}
|
||||
return ofs, nil
|
||||
}
|
||||
|
||||
type NodeVersion struct {
|
||||
implementation string
|
||||
semver string
|
||||
systemInfo string
|
||||
}
|
||||
|
||||
var versionRE = regexp.MustCompile(`^(\w+)\/(v\d+\.\d+\.\d+) \((.*)\)$`)
|
||||
|
||||
func parseNodeVersion(v string) (*NodeVersion, error) {
|
||||
groups := versionRE.FindStringSubmatch(v)
|
||||
if len(groups) != 4 {
|
||||
return nil, errors.Wrapf(ErrInvalidNodeVersion, "could not be parsed: %s", v)
|
||||
}
|
||||
return &NodeVersion{
|
||||
implementation: groups[1],
|
||||
semver: groups[2],
|
||||
systemInfo: groups[3],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetNodeVersion requests that the beacon node identify information about its implementation in a format
|
||||
// similar to a HTTP User-Agent field. ex: Lighthouse/v0.1.5 (Linux x86_64)
|
||||
func (c *Client) GetNodeVersion(ctx context.Context) (*NodeVersion, error) {
|
||||
b, err := c.get(ctx, get_node_version_path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error requesting node version")
|
||||
}
|
||||
d := struct {
|
||||
Data struct {
|
||||
Version string `json:"version"`
|
||||
} `json:"data"`
|
||||
}{}
|
||||
err = json.Unmarshal(b, &d)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error unmarshaling response body: %s", string(b))
|
||||
}
|
||||
return parseNodeVersion(d.Data.Version)
|
||||
}
|
||||
|
||||
// GetState retrieves the BeaconState for the given state id.
|
||||
// State identifier can be one of: "head" (canonical head in node's view), "genesis", "finalized",
|
||||
// <slot>, <hex encoded stateRoot with 0x prefix>. Variables of type StateOrBlockId are exported by this package
|
||||
// for the named identifiers.
|
||||
// The return value contains the ssz-encoded bytes.
|
||||
func (c *Client) GetState(ctx context.Context, stateId StateOrBlockId) ([]byte, error) {
|
||||
statePath := path.Join(get_state_path, string(stateId))
|
||||
b, err := c.get(ctx, statePath, withSSZEncoding())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error requesting state by id = %s", stateId)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// GetWeakSubjectivity calls a proposed API endpoint that is unique to prysm
|
||||
// This api method does the following:
|
||||
// - computes weak subjectivity epoch
|
||||
// - finds the highest non-skipped block preceding the epoch
|
||||
// - returns the htr of the found block and returns this + the value of state_root from the block
|
||||
func (c *Client) GetWeakSubjectivity(ctx context.Context) (*WeakSubjectivityData, error) {
|
||||
body, err := c.get(ctx, get_weak_subjectivity_path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v := &apimiddleware.WeakSubjectivityResponse{}
|
||||
err = json.Unmarshal(body, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
epoch, err := strconv.ParseUint(v.Data.Checkpoint.Epoch, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockRoot, err := hexutil.Decode(v.Data.Checkpoint.Root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stateRoot, err := hexutil.Decode(v.Data.StateRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &WeakSubjectivityData{
|
||||
Epoch: types.Epoch(epoch),
|
||||
BlockRoot: bytesutil.ToBytes32(blockRoot),
|
||||
StateRoot: bytesutil.ToBytes32(stateRoot),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func non200Err(response *http.Response) error {
|
||||
bodyBytes, err := ioutil.ReadAll(response.Body)
|
||||
var body string
|
||||
if err != nil {
|
||||
body = "(Unable to read response body.)"
|
||||
} else {
|
||||
body = "response body:\n" + string(bodyBytes)
|
||||
}
|
||||
msg := fmt.Sprintf("code=%d, url=%s, body=%s", response.StatusCode, response.Request.URL, body)
|
||||
switch response.StatusCode {
|
||||
case 404:
|
||||
return errors.Wrap(ErrNotFound, msg)
|
||||
default:
|
||||
return errors.Wrap(ErrNotOK, msg)
|
||||
}
|
||||
}
|
||||
|
||||
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 ðpb.Fork{
|
||||
CurrentVersion: cSlice,
|
||||
PreviousVersion: pSlice,
|
||||
Epoch: types.Epoch(epoch),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type forkScheduleResponse struct {
|
||||
Data []forkResponse
|
||||
}
|
||||
|
||||
func (fsr *forkScheduleResponse) OrderedForkSchedule() (params.OrderedForkSchedule, error) {
|
||||
ofs := make(params.OrderedForkSchedule, 0)
|
||||
for _, d := range fsr.Data {
|
||||
epoch, err := strconv.Atoi(d.Epoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vSlice, err := hexutil.Decode(d.CurrentVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vSlice) != 4 {
|
||||
return nil, fmt.Errorf("got %d byte version, expected 4 bytes. version hex=%s", len(vSlice), d.CurrentVersion)
|
||||
}
|
||||
version := bytesutil.ToBytes4(vSlice)
|
||||
ofs = append(ofs, params.ForkScheduleEntry{
|
||||
Version: version,
|
||||
Epoch: types.Epoch(uint64(epoch)),
|
||||
})
|
||||
}
|
||||
sort.Sort(ofs)
|
||||
return ofs, nil
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package beacon
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestParseNodeVersion(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
v string
|
||||
err error
|
||||
nv *NodeVersion
|
||||
}{
|
||||
{
|
||||
name: "empty string",
|
||||
v: "",
|
||||
err: ErrInvalidNodeVersion,
|
||||
},
|
||||
{
|
||||
name: "Prysm as the version string",
|
||||
v: "Prysm",
|
||||
err: ErrInvalidNodeVersion,
|
||||
},
|
||||
{
|
||||
name: "semver only",
|
||||
v: "v2.0.6",
|
||||
err: ErrInvalidNodeVersion,
|
||||
},
|
||||
{
|
||||
name: "implementation and semver only",
|
||||
v: "Prysm/v2.0.6",
|
||||
err: ErrInvalidNodeVersion,
|
||||
},
|
||||
{
|
||||
name: "complete version",
|
||||
v: "Prysm/v2.0.6 (linux amd64)",
|
||||
nv: &NodeVersion{
|
||||
implementation: "Prysm",
|
||||
semver: "v2.0.6",
|
||||
systemInfo: "linux amd64",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
nv, err := parseNodeVersion(c.v)
|
||||
if c.err != nil {
|
||||
require.ErrorIs(t, err, c.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, c.nv, nv)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/*
|
||||
Package beacon provides a client for interacting with the standard Eth Beacon Node API.
|
||||
Interactive swagger documentation for the API is available here: https://ethereum.github.io/beacon-APIs/
|
||||
|
||||
*/
|
||||
package beacon
|
||||
@@ -1,12 +0,0 @@
|
||||
package beacon
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
// ErrNotOK is used to indicate when an HTTP request to the Beacon Node API failed with any non-2xx response code.
|
||||
// More specific errors may be returned, but an error in reaction to a non-2xx response will always wrap ErrNotOK.
|
||||
var ErrNotOK = errors.New("did not receive 2xx response from API")
|
||||
|
||||
// ErrNotFound specifically means that a '404 - NOT FOUND' response was received from the API.
|
||||
var ErrNotFound = errors.Wrap(ErrNotOK, "recv 404 NotFound response from API")
|
||||
|
||||
var ErrInvalidNodeVersion = errors.New("invalid node version response")
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice"
|
||||
doublylinkedtree "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/doubly-linked-tree"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
@@ -323,13 +325,44 @@ func (s *Service) IsOptimistic(ctx context.Context) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return s.cfg.ForkChoiceStore.IsOptimistic(ctx, s.head.root)
|
||||
return s.IsOptimisticForRoot(ctx, s.head.root)
|
||||
}
|
||||
|
||||
// IsOptimisticForRoot takes the root and slot as aguments instead of the current head
|
||||
// and returns true if it is optimistic.
|
||||
func (s *Service) IsOptimisticForRoot(ctx context.Context, root [32]byte) (bool, error) {
|
||||
return s.cfg.ForkChoiceStore.IsOptimistic(ctx, root)
|
||||
optimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(ctx, root)
|
||||
if err == nil {
|
||||
return optimistic, nil
|
||||
}
|
||||
if err != protoarray.ErrUnknownNodeRoot && err != doublylinkedtree.ErrNilNode {
|
||||
return false, err
|
||||
}
|
||||
ss, err := s.cfg.BeaconDB.StateSummary(ctx, root)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if ss == nil {
|
||||
return false, errInvalidNilSummary
|
||||
}
|
||||
|
||||
validatedCheckpoint, err := s.cfg.BeaconDB.LastValidatedCheckpoint(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if slots.ToEpoch(ss.Slot) > validatedCheckpoint.Epoch {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if slots.ToEpoch(ss.Slot)+1 < validatedCheckpoint.Epoch {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
lastValidated, err := s.cfg.BeaconDB.StateSummary(ctx, bytesutil.ToBytes32(validatedCheckpoint.Root))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return ss.Slot > lastValidated.Slot, nil
|
||||
}
|
||||
|
||||
// SetGenesisTime sets the genesis time of beacon chain.
|
||||
|
||||
@@ -437,3 +437,79 @@ func TestService_IsOptimisticForRoot_DoublyLinkedTree(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, opt)
|
||||
}
|
||||
|
||||
func TestService_IsOptimisticForRoot_DB_ProtoArray(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
c := &Service{cfg: &config{BeaconDB: beaconDB, ForkChoiceStore: protoarray.New(0, 0, [32]byte{})}, head: &head{slot: 101, root: [32]byte{'b'}}}
|
||||
c.head = &head{root: params.BeaconConfig().ZeroHash}
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block.Slot = 10
|
||||
br, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(b)))
|
||||
require.NoError(t, beaconDB.SaveStateSummary(context.Background(), ðpb.StateSummary{Root: br[:], Slot: 10}))
|
||||
|
||||
optimisticBlock := util.NewBeaconBlock()
|
||||
optimisticBlock.Block.Slot = 11
|
||||
optimisticRoot, err := optimisticBlock.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(optimisticBlock)))
|
||||
|
||||
validatedBlock := util.NewBeaconBlock()
|
||||
validatedBlock.Block.Slot = 9
|
||||
validatedRoot, err := validatedBlock.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(validatedBlock)))
|
||||
|
||||
validatedCheckpoint := ðpb.Checkpoint{Root: br[:]}
|
||||
require.NoError(t, beaconDB.SaveLastValidatedCheckpoint(ctx, validatedCheckpoint))
|
||||
|
||||
require.NoError(t, beaconDB.SaveStateSummary(context.Background(), ðpb.StateSummary{Root: optimisticRoot[:], Slot: 11}))
|
||||
optimistic, err := c.IsOptimisticForRoot(ctx, optimisticRoot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, optimistic)
|
||||
|
||||
require.NoError(t, beaconDB.SaveStateSummary(context.Background(), ðpb.StateSummary{Root: validatedRoot[:], Slot: 9}))
|
||||
validated, err := c.IsOptimisticForRoot(ctx, validatedRoot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, validated)
|
||||
}
|
||||
|
||||
func TestService_IsOptimisticForRoot__DB_DoublyLinkedTree(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
c := &Service{cfg: &config{BeaconDB: beaconDB, ForkChoiceStore: doublylinkedtree.New(0, 0)}, head: &head{slot: 101, root: [32]byte{'b'}}}
|
||||
c.head = &head{root: params.BeaconConfig().ZeroHash}
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block.Slot = 10
|
||||
br, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(b)))
|
||||
require.NoError(t, beaconDB.SaveStateSummary(context.Background(), ðpb.StateSummary{Root: br[:], Slot: 10}))
|
||||
|
||||
optimisticBlock := util.NewBeaconBlock()
|
||||
optimisticBlock.Block.Slot = 11
|
||||
optimisticRoot, err := optimisticBlock.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(optimisticBlock)))
|
||||
|
||||
validatedBlock := util.NewBeaconBlock()
|
||||
validatedBlock.Block.Slot = 9
|
||||
validatedRoot, err := validatedBlock.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(validatedBlock)))
|
||||
|
||||
validatedCheckpoint := ðpb.Checkpoint{Root: br[:]}
|
||||
require.NoError(t, beaconDB.SaveLastValidatedCheckpoint(ctx, validatedCheckpoint))
|
||||
|
||||
require.NoError(t, beaconDB.SaveStateSummary(context.Background(), ðpb.StateSummary{Root: optimisticRoot[:], Slot: 11}))
|
||||
optimistic, err := c.IsOptimisticForRoot(ctx, optimisticRoot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, optimistic)
|
||||
|
||||
require.NoError(t, beaconDB.SaveStateSummary(context.Background(), ðpb.StateSummary{Root: validatedRoot[:], Slot: 9}))
|
||||
validated, err := c.IsOptimisticForRoot(ctx, validatedRoot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, validated)
|
||||
}
|
||||
|
||||
@@ -9,4 +9,8 @@ var (
|
||||
errNilBestJustifiedInStore = errors.New("nil best justified checkpoint returned from store")
|
||||
// errNilFinalizedInStore is returned when a nil finalized checkpt is returned from store.
|
||||
errNilFinalizedInStore = errors.New("nil finalized checkpoint returned from store")
|
||||
// errInvalidNilSummary is returned when a nil summary is returned from the DB.
|
||||
errInvalidNilSummary = errors.New("nil summary returned from the DB")
|
||||
// errNilParentInDB is returned when a nil parent block is returned from the DB.
|
||||
errNilParentInDB = errors.New("nil parent block in DB")
|
||||
)
|
||||
|
||||
@@ -86,11 +86,6 @@ func (s *Service) updateHead(ctx context.Context, balances []uint64) error {
|
||||
if err := s.insertBlockToForkChoiceStore(ctx, jb.Block(), headStartRoot, f, j); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(10261) send optimistic status
|
||||
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, headStartRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
headRoot, err := s.cfg.ForkChoiceStore.Head(ctx, j.Epoch, headStartRoot, balances, f.Epoch)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/runtime/version"
|
||||
@@ -17,7 +18,7 @@ import (
|
||||
var log = logrus.WithField("prefix", "blockchain")
|
||||
|
||||
// logs state transition related data every slot.
|
||||
func logStateTransitionData(b block.BeaconBlock) {
|
||||
func logStateTransitionData(b block.BeaconBlock) error {
|
||||
log := log.WithField("slot", b.Slot())
|
||||
if len(b.Body().Attestations()) > 0 {
|
||||
log = log.WithField("attestations", len(b.Body().Attestations()))
|
||||
@@ -34,13 +35,23 @@ func logStateTransitionData(b block.BeaconBlock) {
|
||||
if len(b.Body().VoluntaryExits()) > 0 {
|
||||
log = log.WithField("voluntaryExits", len(b.Body().VoluntaryExits()))
|
||||
}
|
||||
if b.Version() == version.Altair {
|
||||
if b.Version() == version.Altair || b.Version() == version.Bellatrix {
|
||||
agg, err := b.Body().SyncAggregate()
|
||||
if err == nil {
|
||||
log = log.WithField("syncBitsCount", agg.SyncCommitteeBits.Count())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log = log.WithField("syncBitsCount", agg.SyncCommitteeBits.Count())
|
||||
}
|
||||
if b.Version() == version.Bellatrix {
|
||||
p, err := b.Body().ExecutionPayload()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log = log.WithField("payloadHash", fmt.Sprintf("%#x", bytesutil.Trunc(p.BlockHash)))
|
||||
log = log.WithField("txCount", len(p.Transactions))
|
||||
}
|
||||
log.Info("Finished applying state transition")
|
||||
return nil
|
||||
}
|
||||
|
||||
func logBlockSyncStatus(block block.BeaconBlock, blockRoot [32]byte, finalized *ethpb.Checkpoint, receivedTime time.Time, genesisTime uint64) error {
|
||||
|
||||
@@ -3,6 +3,7 @@ package blockchain
|
||||
import (
|
||||
"testing"
|
||||
|
||||
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
@@ -11,6 +12,17 @@ import (
|
||||
)
|
||||
|
||||
func Test_logStateTransitionData(t *testing.T) {
|
||||
payloadBlk := ðpb.BeaconBlockBellatrix{
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
SyncAggregate: ðpb.SyncAggregate{},
|
||||
ExecutionPayload: &enginev1.ExecutionPayload{
|
||||
BlockHash: []byte{1, 2, 3},
|
||||
Transactions: [][]byte{{}, {}},
|
||||
},
|
||||
},
|
||||
}
|
||||
wrappedPayloadBlk, err := wrapper.WrappedBeaconBlock(payloadBlk)
|
||||
require.NoError(t, err)
|
||||
tests := []struct {
|
||||
name string
|
||||
b block.BeaconBlock
|
||||
@@ -55,11 +67,15 @@ func Test_logStateTransitionData(t *testing.T) {
|
||||
VoluntaryExits: []*ethpb.SignedVoluntaryExit{{}}}}),
|
||||
want: "\"Finished applying state transition\" attestations=1 attesterSlashings=1 deposits=1 prefix=blockchain proposerSlashings=1 slot=0 voluntaryExits=1",
|
||||
},
|
||||
{name: "has payload",
|
||||
b: wrappedPayloadBlk,
|
||||
want: "\"Finished applying state transition\" payloadHash=0x010203 prefix=blockchain slot=0 syncBitsCount=0 txCount=2",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
hook := logTest.NewGlobal()
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
logStateTransitionData(tt.b)
|
||||
require.NoError(t, logStateTransitionData(tt.b))
|
||||
require.LogsContain(t, hook, tt.want)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
// notifyForkchoiceUpdate signals execution engine the fork choice updates. Execution engine should:
|
||||
// 1. Re-organizes the execution payload chain and corresponding state to make head_block_hash the head.
|
||||
// 2. Applies finality to the execution state: it irreversibly persists the chain of all execution payloads and corresponding state, up to and including finalized_block_hash.
|
||||
func (s *Service) notifyForkchoiceUpdate(ctx context.Context, headBlk block.BeaconBlock, finalizedRoot [32]byte) (*enginev1.PayloadIDBytes, error) {
|
||||
func (s *Service) notifyForkchoiceUpdate(ctx context.Context, headBlk block.BeaconBlock, headRoot [32]byte, finalizedRoot [32]byte) (*enginev1.PayloadIDBytes, error) {
|
||||
if headBlk == nil || headBlk.IsNil() || headBlk.Body().IsNil() {
|
||||
return nil, errors.New("nil head block")
|
||||
}
|
||||
@@ -76,7 +76,7 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, headBlk block.Beac
|
||||
return nil, errors.Wrap(err, "could not notify forkchoice update from execution engine")
|
||||
}
|
||||
}
|
||||
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, s.headRoot()); err != nil {
|
||||
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, headRoot); err != nil {
|
||||
return nil, errors.Wrap(err, "could not set block to valid")
|
||||
}
|
||||
return payloadID, nil
|
||||
@@ -122,7 +122,9 @@ func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion int, hea
|
||||
|
||||
// During the transition event, the transition block should be verified for sanity.
|
||||
if isPreBellatrix(preStateVersion) {
|
||||
return nil
|
||||
// Handle case where pre-state is Altair but block contains payload.
|
||||
// To reach here, the block must have contained a valid payload.
|
||||
return s.validateMergeBlock(ctx, blk)
|
||||
}
|
||||
atTransition, err := blocks.IsMergeTransitionBlockUsingPayloadHeader(header, body)
|
||||
if err != nil {
|
||||
@@ -143,14 +145,38 @@ func isPreBellatrix(v int) bool {
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def is_optimistic_candidate_block(opt_store: OptimisticStore, current_slot: Slot, block: BeaconBlock) -> bool:
|
||||
// justified_root = opt_store.block_states[opt_store.head_block_root].current_justified_checkpoint.root
|
||||
// justified_is_execution_block = is_execution_block(opt_store.blocks[justified_root])
|
||||
// block_is_deep = block.slot + SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY <= current_slot
|
||||
// return justified_is_execution_block or block_is_deep
|
||||
// if is_execution_block(opt_store.blocks[block.parent_root]):
|
||||
// return True
|
||||
//
|
||||
// justified_root = opt_store.block_states[opt_store.head_block_root].current_justified_checkpoint.root
|
||||
// if is_execution_block(opt_store.blocks[justified_root]):
|
||||
// return True
|
||||
//
|
||||
// if block.slot + SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY <= current_slot:
|
||||
// return True
|
||||
//
|
||||
// return False
|
||||
func (s *Service) optimisticCandidateBlock(ctx context.Context, blk block.BeaconBlock) (bool, error) {
|
||||
if blk.Slot()+params.BeaconConfig().SafeSlotsToImportOptimistically <= s.CurrentSlot() {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
parent, err := s.cfg.BeaconDB.Block(ctx, bytesutil.ToBytes32(blk.ParentRoot()))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if parent == nil {
|
||||
return false, errNilParentInDB
|
||||
}
|
||||
|
||||
parentIsExecutionBlock, err := blocks.ExecutionBlock(parent.Block().Body())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if parentIsExecutionBlock {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
j := s.store.JustifiedCheckpt()
|
||||
if j == nil {
|
||||
return false, errNilJustifiedInStore
|
||||
|
||||
@@ -155,7 +155,7 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
engine := &mockEngineService{forkchoiceError: tt.newForkchoiceErr}
|
||||
service.cfg.ExecutionEngineCaller = engine
|
||||
_, err := service.notifyForkchoiceUpdate(ctx, tt.blk, tt.finalizedRoot)
|
||||
_, err := service.notifyForkchoiceUpdate(ctx, tt.blk, service.headRoot(), tt.finalizedRoot)
|
||||
if tt.errString != "" {
|
||||
require.ErrorContains(t, tt.errString, err)
|
||||
} else {
|
||||
@@ -188,6 +188,13 @@ func Test_NotifyNewPayload(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
a := ðpb.SignedBeaconBlockAltair{
|
||||
Block: ðpb.BeaconBlockAltair{
|
||||
Body: ðpb.BeaconBlockBodyAltair{},
|
||||
},
|
||||
}
|
||||
altairBlk, err := wrapper.WrappedSignedBeaconBlock(a)
|
||||
require.NoError(t, err)
|
||||
bellatrixBlk, err := wrapper.WrappedSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
service, err := NewService(ctx, opts...)
|
||||
@@ -238,10 +245,29 @@ func Test_NotifyNewPayload(t *testing.T) {
|
||||
errString: "could not validate execution payload from execution engine: payload status is INVALID",
|
||||
},
|
||||
{
|
||||
name: "altair pre state",
|
||||
name: "altair pre state, altair block",
|
||||
postState: bellatrixState,
|
||||
preState: altairState,
|
||||
blk: bellatrixBlk,
|
||||
blk: altairBlk,
|
||||
},
|
||||
{
|
||||
name: "altair pre state, happy case",
|
||||
postState: bellatrixState,
|
||||
preState: altairState,
|
||||
blk: func() block.SignedBeaconBlock {
|
||||
blk := ðpb.SignedBeaconBlockBellatrix{
|
||||
Block: ðpb.BeaconBlockBellatrix{
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
ExecutionPayload: &v1.ExecutionPayload{
|
||||
ParentHash: bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
b, err := wrapper.WrappedSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
return b
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "could not get merge block",
|
||||
@@ -304,27 +330,29 @@ func Test_NotifyNewPayload(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
engine := &mockEngineService{newPayloadError: tt.newPayloadErr, blks: map[[32]byte]*v1.ExecutionBlock{}}
|
||||
engine.blks[[32]byte{'a'}] = &v1.ExecutionBlock{
|
||||
ParentHash: bytesutil.PadTo([]byte{'b'}, fieldparams.RootLength),
|
||||
TotalDifficulty: "0x2",
|
||||
}
|
||||
engine.blks[[32]byte{'b'}] = &v1.ExecutionBlock{
|
||||
ParentHash: bytesutil.PadTo([]byte{'3'}, fieldparams.RootLength),
|
||||
TotalDifficulty: "0x1",
|
||||
}
|
||||
service.cfg.ExecutionEngineCaller = engine
|
||||
var payload *ethpb.ExecutionPayloadHeader
|
||||
if tt.preState.Version() == version.Bellatrix {
|
||||
payload, err = tt.preState.LatestExecutionPayloadHeader()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
err := service.notifyNewPayload(ctx, tt.preState.Version(), payload, tt.postState, tt.blk)
|
||||
if tt.errString != "" {
|
||||
require.ErrorContains(t, tt.errString, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
engine := &mockEngineService{newPayloadError: tt.newPayloadErr, blks: map[[32]byte]*v1.ExecutionBlock{}}
|
||||
engine.blks[[32]byte{'a'}] = &v1.ExecutionBlock{
|
||||
ParentHash: bytesutil.PadTo([]byte{'b'}, fieldparams.RootLength),
|
||||
TotalDifficulty: "0x2",
|
||||
}
|
||||
engine.blks[[32]byte{'b'}] = &v1.ExecutionBlock{
|
||||
ParentHash: bytesutil.PadTo([]byte{'3'}, fieldparams.RootLength),
|
||||
TotalDifficulty: "0x1",
|
||||
}
|
||||
service.cfg.ExecutionEngineCaller = engine
|
||||
var payload *ethpb.ExecutionPayloadHeader
|
||||
if tt.preState.Version() == version.Bellatrix {
|
||||
payload, err = tt.preState.LatestExecutionPayloadHeader()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
err := service.notifyNewPayload(ctx, tt.preState.Version(), payload, tt.postState, tt.blk)
|
||||
if tt.errString != "" {
|
||||
require.ErrorContains(t, tt.errString, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +375,12 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
|
||||
params.BeaconConfig().SafeSlotsToImportOptimistically = 128
|
||||
service.genesisTime = time.Now().Add(-time.Second * 12 * 2 * 128)
|
||||
|
||||
parentBlk := util.NewBeaconBlockBellatrix()
|
||||
wrappedParentBlock, err := wrapper.WrappedBellatrixSignedBeaconBlock(parentBlk)
|
||||
require.NoError(t, err)
|
||||
parentRoot, err := wrappedParentBlock.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
blk block.BeaconBlock
|
||||
@@ -358,6 +392,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
|
||||
blk: func(tt *testing.T) block.BeaconBlock {
|
||||
blk := util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Slot = 1
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
wr, err := wrapper.WrappedBellatrixBeaconBlock(blk.Block)
|
||||
require.NoError(tt, err)
|
||||
return wr
|
||||
@@ -365,6 +400,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
|
||||
justified: func(tt *testing.T) block.SignedBeaconBlock {
|
||||
blk := util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Slot = 32
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
wr, err := wrapper.WrappedBellatrixSignedBeaconBlock(blk)
|
||||
require.NoError(tt, err)
|
||||
return wr
|
||||
@@ -376,6 +412,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
|
||||
blk: func(tt *testing.T) block.BeaconBlock {
|
||||
blk := util.NewBeaconBlockAltair()
|
||||
blk.Block.Slot = 200
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
wr, err := wrapper.WrappedAltairBeaconBlock(blk.Block)
|
||||
require.NoError(tt, err)
|
||||
return wr
|
||||
@@ -383,6 +420,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
|
||||
justified: func(tt *testing.T) block.SignedBeaconBlock {
|
||||
blk := util.NewBeaconBlockAltair()
|
||||
blk.Block.Slot = 32
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
wr, err := wrapper.WrappedAltairSignedBeaconBlock(blk)
|
||||
require.NoError(tt, err)
|
||||
return wr
|
||||
@@ -394,6 +432,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
|
||||
blk: func(tt *testing.T) block.BeaconBlock {
|
||||
blk := util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Slot = 200
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
wr, err := wrapper.WrappedBellatrixBeaconBlock(blk.Block)
|
||||
require.NoError(tt, err)
|
||||
return wr
|
||||
@@ -401,6 +440,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
|
||||
justified: func(tt *testing.T) block.SignedBeaconBlock {
|
||||
blk := util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Slot = 32
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
wr, err := wrapper.WrappedBellatrixSignedBeaconBlock(blk)
|
||||
require.NoError(tt, err)
|
||||
return wr
|
||||
@@ -412,6 +452,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
|
||||
blk: func(tt *testing.T) block.BeaconBlock {
|
||||
blk := util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Slot = 200
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
wr, err := wrapper.WrappedBellatrixBeaconBlock(blk.Block)
|
||||
require.NoError(tt, err)
|
||||
return wr
|
||||
@@ -419,6 +460,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
|
||||
justified: func(tt *testing.T) block.SignedBeaconBlock {
|
||||
blk := util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Slot = 32
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
blk.Block.Body.ExecutionPayload.ParentHash = bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
|
||||
blk.Block.Body.ExecutionPayload.FeeRecipient = bytesutil.PadTo([]byte{'a'}, fieldparams.FeeRecipientLength)
|
||||
blk.Block.Body.ExecutionPayload.StateRoot = bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
|
||||
@@ -443,8 +485,59 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
|
||||
Root: jroot[:],
|
||||
Epoch: slots.ToEpoch(tt.justified.Block().Slot()),
|
||||
})
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wrappedParentBlock))
|
||||
|
||||
candidate, err := service.optimisticCandidateBlock(ctx, tt.blk)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.want, candidate, tt.name)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_IsOptimisticShallowExecutionParent(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
params.OverrideBeaconConfig(params.MainnetConfig())
|
||||
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
}
|
||||
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
|
||||
params.BeaconConfig().SafeSlotsToImportOptimistically = 128
|
||||
service.genesisTime = time.Now().Add(-time.Second * 12 * 2 * 128)
|
||||
payload := &v1.ExecutionPayload{
|
||||
ParentHash: make([]byte, 32),
|
||||
FeeRecipient: make([]byte, 20),
|
||||
StateRoot: make([]byte, 32),
|
||||
ReceiptsRoot: make([]byte, 32),
|
||||
LogsBloom: make([]byte, 256),
|
||||
PrevRandao: make([]byte, 32),
|
||||
BaseFeePerGas: bytesutil.PadTo([]byte{1, 2, 3, 4}, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, 32),
|
||||
BlockNumber: 100,
|
||||
}
|
||||
body := ðpb.BeaconBlockBodyBellatrix{ExecutionPayload: payload}
|
||||
block := ðpb.BeaconBlockBellatrix{Body: body, Slot: 200}
|
||||
rawSigned := ðpb.SignedBeaconBlockBellatrix{Block: block}
|
||||
blk := util.HydrateSignedBeaconBlockBellatrix(rawSigned)
|
||||
wr, err := wrapper.WrappedBellatrixSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wr))
|
||||
blkRoot, err := wr.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
childBlock := util.NewBeaconBlockBellatrix()
|
||||
childBlock.Block.ParentRoot = blkRoot[:]
|
||||
// shallow block
|
||||
childBlock.Block.Slot = 201
|
||||
wrappedChild, err := wrapper.WrappedBellatrixSignedBeaconBlock(childBlock)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wrappedChild))
|
||||
candidate, err := service.optimisticCandidateBlock(ctx, wrappedChild.Block())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, candidate)
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ var initialSyncBlockCacheSize = uint64(2 * params.BeaconConfig().SlotsPerEpoch)
|
||||
func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, blockRoot [32]byte) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.onBlock")
|
||||
defer span.End()
|
||||
startOnBlock := time.Now()
|
||||
if err := helpers.BeaconBlockIsNil(signed); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -103,17 +104,25 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
startStateTransition := time.Now()
|
||||
postState, err := transition.ExecuteStateTransition(ctx, preState, signed)
|
||||
if err != nil {
|
||||
|
||||
return err
|
||||
}
|
||||
log.WithField("elapsed", time.Since(startStateTransition)).Info("Done running state transition")
|
||||
|
||||
startNotifyNewPayload := time.Now()
|
||||
if err := s.notifyNewPayload(ctx, preStateVersion, preStateHeader, postState, signed); err != nil {
|
||||
return errors.Wrap(err, "could not verify new payload")
|
||||
}
|
||||
log.WithField("elapsed", time.Since(startNotifyNewPayload)).Info("Done running notify new payload")
|
||||
|
||||
startSavePostData := time.Now()
|
||||
if err := s.savePostStateInfo(ctx, blockRoot, signed, postState, false /* reg sync */); err != nil {
|
||||
return err
|
||||
}
|
||||
log.WithField("elapsed", time.Since(startSavePostData)).Info("Done saving post state data")
|
||||
|
||||
// We add a proposer score boost to fork choice for the block root if applicable, right after
|
||||
// running a successful state transition for the block.
|
||||
@@ -179,12 +188,18 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
|
||||
msg := fmt.Sprintf("could not read balances for state w/ justified checkpoint %#x", justified.Root)
|
||||
return errors.Wrap(err, msg)
|
||||
}
|
||||
|
||||
startUpdateHead := time.Now()
|
||||
if err := s.updateHead(ctx, balances); err != nil {
|
||||
log.WithError(err).Warn("Could not update head")
|
||||
}
|
||||
if _, err := s.notifyForkchoiceUpdate(ctx, s.headBlock().Block(), bytesutil.ToBytes32(finalized.Root)); err != nil {
|
||||
log.WithField("elapsed", time.Since(startUpdateHead)).Info("Done updating head")
|
||||
|
||||
startNotifyFCU := time.Now()
|
||||
if _, err := s.notifyForkchoiceUpdate(ctx, s.headBlock().Block(), s.headRoot(), bytesutil.ToBytes32(finalized.Root)); err != nil {
|
||||
return err
|
||||
}
|
||||
log.WithField("elapsed", time.Since(startNotifyFCU)).Info("Done notifying forkchoice update")
|
||||
|
||||
if err := s.pruneCanonicalAttsFromPool(ctx, blockRoot, signed); err != nil {
|
||||
return err
|
||||
@@ -253,7 +268,7 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
|
||||
}
|
||||
|
||||
defer reportAttestationInclusion(b)
|
||||
|
||||
log.WithField("elapsed", time.Since(startOnBlock)).Info("Done running on block method")
|
||||
return s.handleEpochBoundary(ctx, postState)
|
||||
}
|
||||
|
||||
@@ -307,10 +322,17 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []block.SignedBeaconBlo
|
||||
var set *bls.SignatureBatch
|
||||
boundaries := make(map[[32]byte]state.BeaconState)
|
||||
for i, b := range blks {
|
||||
preStateVersion, preStateHeader, err := getStateVersionAndPayload(preState)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
set, preState, err = transition.ExecuteStateTransitionNoVerifyAnySig(ctx, preState, b)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := s.notifyNewPayload(ctx, preStateVersion, preStateHeader, preState, b); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Save potential boundary states.
|
||||
if slots.IsEpochStart(preState.Slot()) {
|
||||
boundaries[blockRoots[i]] = preState.Copy()
|
||||
@@ -356,8 +378,7 @@ func (s *Service) handleBlockAfterBatchVerify(ctx context.Context, signed block.
|
||||
if err := s.insertBlockToForkChoiceStore(ctx, b, blockRoot, fCheckpoint, jCheckpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO(10261) send optimistic status
|
||||
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, blockRoot); err != nil {
|
||||
if _, err := s.notifyForkchoiceUpdate(ctx, b, blockRoot, bytesutil.ToBytes32(fCheckpoint.Root)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -475,14 +496,10 @@ func (s *Service) insertBlockToForkChoiceStore(ctx context.Context, blk block.Be
|
||||
return err
|
||||
}
|
||||
// Feed in block to fork choice store.
|
||||
if err := s.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx,
|
||||
return s.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx,
|
||||
blk.Slot(), root, bytesutil.ToBytes32(blk.ParentRoot()),
|
||||
jCheckpoint.Epoch,
|
||||
fCheckpoint.Epoch); err != nil {
|
||||
return errors.Wrap(err, "could not process block for proto array fork choice")
|
||||
}
|
||||
// TODO(10261) send optimistic status
|
||||
return s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, root)
|
||||
fCheckpoint.Epoch)
|
||||
}
|
||||
|
||||
// This saves post state info to DB or cache. This also saves post state info to fork choice store.
|
||||
|
||||
@@ -372,10 +372,6 @@ func (s *Service) fillInForkChoiceMissingBlocks(ctx context.Context, blk block.B
|
||||
fCheckpoint.Epoch); err != nil {
|
||||
return errors.Wrap(err, "could not process block for proto array fork choice")
|
||||
}
|
||||
// TODO(10261) send optimistic status
|
||||
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -351,6 +351,56 @@ func TestStore_OnBlockBatch_DoublyLinkedTree(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestStore_OnBlockBatch_NotifyNewPayload(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
}
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
genesis := blocks.NewGenesisBlock(params.BeaconConfig().ZeroHash[:])
|
||||
assert.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(genesis)))
|
||||
gRoot, err := genesis.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
service.store.SetFinalizedCheckpt(ðpb.Checkpoint{Root: gRoot[:]})
|
||||
service.cfg.ForkChoiceStore = doublylinkedtree.New(0, 0)
|
||||
service.saveInitSyncBlock(gRoot, wrapper.WrappedPhase0SignedBeaconBlock(genesis))
|
||||
st, keys := util.DeterministicGenesisState(t, 64)
|
||||
bState := st.Copy()
|
||||
|
||||
var blks []block.SignedBeaconBlock
|
||||
var blkRoots [][32]byte
|
||||
var firstState state.BeaconState
|
||||
blkCount := 4
|
||||
for i := 1; i <= blkCount; i++ {
|
||||
b, err := util.GenerateFullBlock(bState, keys, util.DefaultBlockGenConfig(), types.Slot(i))
|
||||
require.NoError(t, err)
|
||||
bState, err = transition.ExecuteStateTransition(ctx, bState, wrapper.WrappedPhase0SignedBeaconBlock(b))
|
||||
require.NoError(t, err)
|
||||
if i == 1 {
|
||||
firstState = bState.Copy()
|
||||
}
|
||||
root, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
service.saveInitSyncBlock(root, wrapper.WrappedPhase0SignedBeaconBlock(b))
|
||||
blks = append(blks, wrapper.WrappedPhase0SignedBeaconBlock(b))
|
||||
blkRoots = append(blkRoots, root)
|
||||
}
|
||||
|
||||
rBlock, err := blks[0].PbPhase0Block()
|
||||
assert.NoError(t, err)
|
||||
rBlock.Block.ParentRoot = gRoot[:]
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), blks[0]))
|
||||
require.NoError(t, service.cfg.StateGen.SaveState(ctx, blkRoots[0], firstState))
|
||||
cp1, cp2, err := service.onBlockBatch(ctx, blks[1:], blkRoots[1:])
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, blkCount-1, len(cp1))
|
||||
require.Equal(t, blkCount-1, len(cp2))
|
||||
}
|
||||
|
||||
func TestRemoveStateSinceLastFinalized_EmptyStartSlot(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
params.SetupTestConfigCleanup(t)
|
||||
|
||||
@@ -64,7 +64,9 @@ func (s *Service) ReceiveBlock(ctx context.Context, block block.SignedBeaconBloc
|
||||
return err
|
||||
}
|
||||
// Log state transition data.
|
||||
logStateTransitionData(blockCopy.Block())
|
||||
if err := logStateTransitionData(blockCopy.Block()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ func (s *Service) Start() {
|
||||
saved := s.cfg.FinalizedStateAtStartUp
|
||||
|
||||
if saved != nil && !saved.IsNil() {
|
||||
if err := s.startFromSavedState(saved); err != nil {
|
||||
if err := s.StartFromSavedState(saved); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
@@ -164,7 +164,7 @@ func (s *Service) Status() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) startFromSavedState(saved state.BeaconState) error {
|
||||
func (s *Service) StartFromSavedState(saved state.BeaconState) error {
|
||||
log.Info("Blockchain data already exists in DB, initializing...")
|
||||
s.genesisTime = time.Unix(int64(saved.GenesisTime()), 0) // lint:ignore uintcast -- Genesis time will not exceed int64 in your lifetime.
|
||||
s.cfg.AttService.SetGenesisTime(saved.GenesisTime())
|
||||
@@ -233,7 +233,7 @@ func (s *Service) startFromSavedState(saved state.BeaconState) error {
|
||||
|
||||
func (s *Service) originRootFromSavedState(ctx context.Context) ([32]byte, error) {
|
||||
// first check if we have started from checkpoint sync and have a root
|
||||
originRoot, err := s.cfg.BeaconDB.OriginCheckpointBlockRoot(ctx)
|
||||
originRoot, err := s.cfg.BeaconDB.OriginBlockRoot(ctx)
|
||||
if err == nil {
|
||||
return originRoot, nil
|
||||
}
|
||||
@@ -241,7 +241,7 @@ func (s *Service) originRootFromSavedState(ctx context.Context) ([32]byte, error
|
||||
return originRoot, errors.Wrap(err, "could not retrieve checkpoint sync chain origin data from db")
|
||||
}
|
||||
|
||||
// we got here because OriginCheckpointBlockRoot gave us an ErrNotFound. this means the node was started from a genesis state,
|
||||
// we got here because OriginBlockRoot gave us an ErrNotFound. this means the node was started from a genesis state,
|
||||
// so we should have a value for GenesisBlock
|
||||
genesisBlock, err := s.cfg.BeaconDB.GenesisBlock(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -290,7 +290,7 @@ func TestChainService_InitializeChainInfo(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, c.startFromSavedState(headState))
|
||||
require.NoError(t, c.StartFromSavedState(headState))
|
||||
headBlk, err := c.HeadBlock(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, headBlock, headBlk.Proto(), "Head block incorrect")
|
||||
@@ -333,7 +333,7 @@ func TestChainService_InitializeChainInfo_SetHeadAtGenesis(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, c.startFromSavedState(headState))
|
||||
require.NoError(t, c.StartFromSavedState(headState))
|
||||
s, err := c.HeadState(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.DeepSSZEqual(t, headState.InnerStateUnsafe(), s.InnerStateUnsafe(), "Head state incorrect")
|
||||
@@ -393,7 +393,7 @@ func TestChainService_InitializeChainInfo_HeadSync(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, c.startFromSavedState(headState))
|
||||
require.NoError(t, c.StartFromSavedState(headState))
|
||||
s, err := c.HeadState(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.DeepSSZEqual(t, headState.InnerStateUnsafe(), s.InnerStateUnsafe(), "Head state incorrect")
|
||||
|
||||
@@ -36,7 +36,6 @@ func NewWeakSubjectivityVerifier(wsc *ethpb.Checkpoint, db weakSubjectivityDB) (
|
||||
// per 7342, a nil checkpoint, zero-root or zero-epoch should all fail validation
|
||||
// and return an error instead of creating a WeakSubjectivityVerifier that permits any chain history.
|
||||
if wsc == nil || len(wsc.Root) == 0 || wsc.Epoch == 0 {
|
||||
log.Warn("No valid weak subjectivity checkpoint specified, running without weak subjectivity verification")
|
||||
return &WeakSubjectivityVerifier{
|
||||
enabled: false,
|
||||
}, nil
|
||||
@@ -82,14 +81,13 @@ func (v *WeakSubjectivityVerifier) VerifyWeakSubjectivity(ctx context.Context, f
|
||||
}
|
||||
filter := filters.NewFilter().SetStartSlot(v.slot).SetEndSlot(v.slot + params.BeaconConfig().SlotsPerEpoch)
|
||||
// A node should have the weak subjectivity block corresponds to the correct epoch in the DB.
|
||||
log.Infof("Searching block roots index for weak subjectivity root=%#x", v.root)
|
||||
roots, err := v.db.BlockRoots(ctx, filter)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error while retrieving block roots to verify weak subjectivity")
|
||||
}
|
||||
for _, root := range roots {
|
||||
if v.root == root {
|
||||
log.Info("Weak subjectivity check has passed!!")
|
||||
log.Info("Weak subjectivity check has passed")
|
||||
v.verified = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -14,62 +14,58 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/testing/util"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
)
|
||||
|
||||
func TestService_VerifyWeakSubjectivityRoot(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block.Slot = 1792480
|
||||
blockEpoch := slots.ToEpoch(b.Block.Slot)
|
||||
b.Block.Slot = 32
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(b)))
|
||||
r, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
tests := []struct {
|
||||
wsVerified bool
|
||||
disabled bool
|
||||
wantErr error
|
||||
checkpt *ethpb.Checkpoint
|
||||
finalizedEpoch types.Epoch
|
||||
name string
|
||||
}{
|
||||
{
|
||||
name: "nil root and epoch",
|
||||
disabled: true,
|
||||
name: "nil root and epoch",
|
||||
},
|
||||
{
|
||||
name: "already verified",
|
||||
checkpt: ðpb.Checkpoint{Epoch: 2},
|
||||
finalizedEpoch: 2,
|
||||
wsVerified: true,
|
||||
},
|
||||
{
|
||||
name: "not yet to verify, ws epoch higher than finalized epoch",
|
||||
checkpt: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte{'a'}, 32), Epoch: blockEpoch},
|
||||
finalizedEpoch: blockEpoch - 1,
|
||||
checkpt: ðpb.Checkpoint{Epoch: 2},
|
||||
finalizedEpoch: 1,
|
||||
},
|
||||
{
|
||||
name: "can't find the block in DB",
|
||||
checkpt: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength), Epoch: 1},
|
||||
finalizedEpoch: blockEpoch + 1,
|
||||
finalizedEpoch: 3,
|
||||
wantErr: errWSBlockNotFound,
|
||||
},
|
||||
{
|
||||
name: "can't find the block corresponds to ws epoch in DB",
|
||||
checkpt: ðpb.Checkpoint{Root: r[:], Epoch: blockEpoch - 2}, // Root belongs in epoch 1.
|
||||
finalizedEpoch: blockEpoch - 1,
|
||||
checkpt: ðpb.Checkpoint{Root: r[:], Epoch: 2}, // Root belongs in epoch 1.
|
||||
finalizedEpoch: 3,
|
||||
wantErr: errWSBlockNotFoundInEpoch,
|
||||
},
|
||||
{
|
||||
name: "can verify and pass",
|
||||
checkpt: ðpb.Checkpoint{Root: r[:], Epoch: blockEpoch},
|
||||
finalizedEpoch: blockEpoch + 1,
|
||||
},
|
||||
{
|
||||
name: "equal epoch",
|
||||
checkpt: ðpb.Checkpoint{Root: r[:], Epoch: blockEpoch},
|
||||
finalizedEpoch: blockEpoch,
|
||||
checkpt: ðpb.Checkpoint{Root: r[:], Epoch: 1},
|
||||
finalizedEpoch: 3,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
wv, err := NewWeakSubjectivityVerifier(tt.checkpt, beaconDB)
|
||||
require.Equal(t, !tt.disabled, wv.enabled)
|
||||
require.NoError(t, err)
|
||||
s := &Service{
|
||||
cfg: &config{BeaconDB: beaconDB, WeakSubjectivityCheckpt: tt.checkpt},
|
||||
|
||||
@@ -15,7 +15,25 @@ go_library(
|
||||
"weak_subjectivity.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers",
|
||||
visibility = ["//visibility:public"],
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//cmd/beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__pkg__",
|
||||
"//crypto/keystore:__pkg__",
|
||||
"//network/forks:__pkg__",
|
||||
"//proto/prysm/v1alpha1:__subpackages__",
|
||||
"//proto/prysm/v1alpha1/attestation:__pkg__",
|
||||
"//runtime/interop:__pkg__",
|
||||
"//slasher:__subpackages__",
|
||||
"//testing/altair:__pkg__",
|
||||
"//testing/benchmark/benchmark_files:__subpackages__",
|
||||
"//testing/endtoend/evaluators:__pkg__",
|
||||
"//testing/slasher/simulator:__pkg__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
"//tools:__subpackages__",
|
||||
"//validator:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/core/time:go_default_library",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
@@ -12,9 +13,8 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/time"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/math"
|
||||
v1alpha1 "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
)
|
||||
|
||||
@@ -122,20 +122,20 @@ func ComputeWeakSubjectivityPeriod(ctx context.Context, st state.ReadOnlyBeaconS
|
||||
// current_epoch = compute_epoch_at_slot(get_current_slot(store))
|
||||
// return current_epoch <= ws_state_epoch + ws_period
|
||||
func IsWithinWeakSubjectivityPeriod(
|
||||
ctx context.Context, currentEpoch types.Epoch, wsState state.ReadOnlyBeaconState, wsStateRoot [32]byte, wsEpoch types.Epoch) (bool, error) {
|
||||
ctx context.Context, currentEpoch types.Epoch, wsState state.ReadOnlyBeaconState, wsCheckpoint *eth.WeakSubjectivityCheckpoint) (bool, error) {
|
||||
// Make sure that incoming objects are not nil.
|
||||
if wsState == nil || wsState.IsNil() || wsState.LatestBlockHeader() == nil {
|
||||
if wsState == nil || wsState.IsNil() || wsState.LatestBlockHeader() == nil || wsCheckpoint == nil {
|
||||
return false, errors.New("invalid weak subjectivity state or checkpoint")
|
||||
}
|
||||
|
||||
// Assert that state and checkpoint have the same root and epoch.
|
||||
if bytesutil.ToBytes32(wsState.LatestBlockHeader().StateRoot) != wsStateRoot {
|
||||
if !bytes.Equal(wsState.LatestBlockHeader().StateRoot, wsCheckpoint.StateRoot) {
|
||||
return false, fmt.Errorf("state (%#x) and checkpoint (%#x) roots do not match",
|
||||
wsState.LatestBlockHeader().StateRoot, wsStateRoot)
|
||||
wsState.LatestBlockHeader().StateRoot, wsCheckpoint.StateRoot)
|
||||
}
|
||||
if slots.ToEpoch(wsState.Slot()) != wsEpoch {
|
||||
if slots.ToEpoch(wsState.Slot()) != wsCheckpoint.Epoch {
|
||||
return false, fmt.Errorf("state (%v) and checkpoint (%v) epochs do not match",
|
||||
slots.ToEpoch(wsState.Slot()), wsEpoch)
|
||||
slots.ToEpoch(wsState.Slot()), wsCheckpoint.Epoch)
|
||||
}
|
||||
|
||||
// Compare given epoch to state epoch + weak subjectivity period.
|
||||
@@ -164,7 +164,7 @@ func LatestWeakSubjectivityEpoch(ctx context.Context, st state.ReadOnlyBeaconSta
|
||||
}
|
||||
|
||||
// ParseWeakSubjectivityInputString parses "blocks_root:epoch_number" string into a checkpoint.
|
||||
func ParseWeakSubjectivityInputString(wsCheckpointString string) (*v1alpha1.Checkpoint, error) {
|
||||
func ParseWeakSubjectivityInputString(wsCheckpointString string) (*eth.Checkpoint, error) {
|
||||
if wsCheckpointString == "" {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -197,7 +197,7 @@ func ParseWeakSubjectivityInputString(wsCheckpointString string) (*v1alpha1.Chec
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &v1alpha1.Checkpoint{
|
||||
return ð.Checkpoint{
|
||||
Epoch: types.Epoch(epoch),
|
||||
Root: bRoot,
|
||||
}, nil
|
||||
|
||||
@@ -54,15 +54,12 @@ func TestWeakSubjectivity_ComputeWeakSubjectivityPeriod(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockWsCheckpoint func() (stateRoot [32]byte, blockRoot [32]byte, e types.Epoch)
|
||||
|
||||
func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
epoch types.Epoch
|
||||
genWsState func() state.ReadOnlyBeaconState
|
||||
genWsCheckpoint mockWsCheckpoint
|
||||
genWsCheckpoint func() *ethpb.WeakSubjectivityCheckpoint
|
||||
want bool
|
||||
wantedErr string
|
||||
}{
|
||||
@@ -71,8 +68,22 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
genWsState: func() state.ReadOnlyBeaconState {
|
||||
return nil
|
||||
},
|
||||
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
|
||||
return [32]byte{}, [32]byte{}, 42
|
||||
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
|
||||
return ðpb.WeakSubjectivityCheckpoint{
|
||||
BlockRoot: make([]byte, 32),
|
||||
StateRoot: make([]byte, 32),
|
||||
Epoch: 42,
|
||||
}
|
||||
},
|
||||
wantedErr: "invalid weak subjectivity state or checkpoint",
|
||||
},
|
||||
{
|
||||
name: "nil weak subjectivity checkpoint",
|
||||
genWsState: func() state.ReadOnlyBeaconState {
|
||||
return genState(t, 128, 32)
|
||||
},
|
||||
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
|
||||
return nil
|
||||
},
|
||||
wantedErr: "invalid weak subjectivity state or checkpoint",
|
||||
},
|
||||
@@ -88,10 +99,11 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return beaconState
|
||||
},
|
||||
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
|
||||
var sr [32]byte
|
||||
copy(sr[:], bytesutil.PadTo([]byte("stateroot2"), 32))
|
||||
return sr, [32]byte{}, 42
|
||||
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
|
||||
return ðpb.WeakSubjectivityCheckpoint{
|
||||
StateRoot: bytesutil.PadTo([]byte("stateroot2"), 32),
|
||||
Epoch: 42,
|
||||
}
|
||||
},
|
||||
wantedErr: fmt.Sprintf("state (%#x) and checkpoint (%#x) roots do not match",
|
||||
bytesutil.PadTo([]byte("stateroot1"), 32), bytesutil.PadTo([]byte("stateroot2"), 32)),
|
||||
@@ -108,10 +120,11 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return beaconState
|
||||
},
|
||||
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
|
||||
var sr [32]byte
|
||||
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
|
||||
return sr, [32]byte{}, 43
|
||||
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
|
||||
return ðpb.WeakSubjectivityCheckpoint{
|
||||
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
|
||||
Epoch: 43,
|
||||
}
|
||||
},
|
||||
wantedErr: "state (42) and checkpoint (43) epochs do not match",
|
||||
},
|
||||
@@ -127,10 +140,11 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return beaconState
|
||||
},
|
||||
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
|
||||
var sr [32]byte
|
||||
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
|
||||
return sr, [32]byte{}, 42
|
||||
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
|
||||
return ðpb.WeakSubjectivityCheckpoint{
|
||||
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
|
||||
Epoch: 42,
|
||||
}
|
||||
},
|
||||
wantedErr: "cannot compute weak subjectivity period: no active validators found",
|
||||
},
|
||||
@@ -147,10 +161,11 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return beaconState
|
||||
},
|
||||
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
|
||||
var sr [32]byte
|
||||
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
|
||||
return sr, [32]byte{}, 42
|
||||
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
|
||||
return ðpb.WeakSubjectivityCheckpoint{
|
||||
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
|
||||
Epoch: 42,
|
||||
}
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
@@ -167,18 +182,18 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return beaconState
|
||||
},
|
||||
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
|
||||
var sr [32]byte
|
||||
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
|
||||
return sr, [32]byte{}, 42
|
||||
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
|
||||
return ðpb.WeakSubjectivityCheckpoint{
|
||||
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
|
||||
Epoch: 42,
|
||||
}
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sr, _, e := tt.genWsCheckpoint()
|
||||
got, err := helpers.IsWithinWeakSubjectivityPeriod(context.Background(), tt.epoch, tt.genWsState(), sr, e)
|
||||
got, err := helpers.IsWithinWeakSubjectivityPeriod(context.Background(), tt.epoch, tt.genWsState(), tt.genWsCheckpoint())
|
||||
if tt.wantedErr != "" {
|
||||
assert.Equal(t, false, got)
|
||||
assert.ErrorContains(t, tt.wantedErr, err)
|
||||
|
||||
@@ -245,7 +245,7 @@ func createFullBellatrixBlockWithOperations(t *testing.T) (state.BeaconState,
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: bytesutil.PadTo([]byte{1, 2, 3, 4}, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
Transactions: make([][]byte, 0),
|
||||
ExtraData: make([]byte, 0),
|
||||
|
||||
@@ -45,6 +45,7 @@ type ReadOnlyDatabase interface {
|
||||
HasArchivedPoint(ctx context.Context, slot types.Slot) bool
|
||||
LastArchivedRoot(ctx context.Context) [32]byte
|
||||
LastArchivedSlot(ctx context.Context) (types.Slot, error)
|
||||
LastValidatedCheckpoint(ctx context.Context) (*ethpb.Checkpoint, error)
|
||||
// Deposit contract related handlers.
|
||||
DepositContractAddress(ctx context.Context) ([]byte, error)
|
||||
// Powchain operations.
|
||||
@@ -52,8 +53,7 @@ type ReadOnlyDatabase interface {
|
||||
// Fee reicipients operations.
|
||||
FeeRecipientByValidatorID(ctx context.Context, id types.ValidatorIndex) (common.Address, error)
|
||||
// origin checkpoint sync support
|
||||
OriginCheckpointBlockRoot(ctx context.Context) ([32]byte, error)
|
||||
LowestSyncedBlockSlot(ctx context.Context) (types.Slot, error)
|
||||
OriginBlockRoot(ctx context.Context) ([32]byte, error)
|
||||
}
|
||||
|
||||
// NoHeadAccessDatabase defines a struct without access to chain head data.
|
||||
@@ -75,6 +75,7 @@ type NoHeadAccessDatabase interface {
|
||||
// Checkpoint operations.
|
||||
SaveJustifiedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error
|
||||
SaveFinalizedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error
|
||||
SaveLastValidatedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error
|
||||
// Deposit contract related handlers.
|
||||
SaveDepositContractAddress(ctx context.Context, addr common.Address) error
|
||||
// Powchain operations.
|
||||
@@ -101,7 +102,7 @@ type HeadAccessDatabase interface {
|
||||
EnsureEmbeddedGenesis(ctx context.Context) error
|
||||
|
||||
// initialization method needed for origin checkpoint sync
|
||||
SaveOrigin(ctx context.Context, state []byte, block []byte) error
|
||||
SaveOrigin(ctx context.Context, state io.Reader, block io.Reader) error
|
||||
}
|
||||
|
||||
// SlasherDatabase interface for persisting data related to detecting slashable offenses on Ethereum.
|
||||
|
||||
@@ -25,6 +25,7 @@ go_library(
|
||||
"state_summary.go",
|
||||
"state_summary_cache.go",
|
||||
"utils.go",
|
||||
"validated_checkpoint.go",
|
||||
"wss.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db/kv",
|
||||
@@ -49,7 +50,6 @@ go_library(
|
||||
"//io/file:go_default_library",
|
||||
"//monitoring/progress:go_default_library",
|
||||
"//monitoring/tracing:go_default_library",
|
||||
"//proto/detect:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/block:go_default_library",
|
||||
"//proto/prysm/v1alpha1/wrapper:go_default_library",
|
||||
@@ -92,6 +92,7 @@ go_test(
|
||||
"state_summary_test.go",
|
||||
"state_test.go",
|
||||
"utils_test.go",
|
||||
"validated_checkpoint_test.go",
|
||||
],
|
||||
data = glob(["testdata/**"]),
|
||||
embed = [":go_default_library"],
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
|
||||
// used to represent errors for inconsistent slot ranges.
|
||||
var errInvalidSlotRange = errors.New("invalid end slot and start slot provided")
|
||||
var errEmptyBucket = errors.New("unexpectedly empty bucket")
|
||||
|
||||
// Block retrieval by root.
|
||||
func (s *Store) Block(ctx context.Context, blockRoot [32]byte) (block.SignedBeaconBlock, error) {
|
||||
@@ -48,18 +47,18 @@ func (s *Store) Block(ctx context.Context, blockRoot [32]byte) (block.SignedBeac
|
||||
return blk, err
|
||||
}
|
||||
|
||||
// OriginCheckpointBlockRoot returns the value written to the db in SaveOriginCheckpointBlockRoot
|
||||
// OriginBlockRoot returns the value written to the db in SaveOriginBlockRoot
|
||||
// This is the root of a finalized block within the weak subjectivity period
|
||||
// at the time the chain was started, used to initialize the database and chain
|
||||
// without syncing from genesis.
|
||||
func (s *Store) OriginCheckpointBlockRoot(ctx context.Context) ([32]byte, error) {
|
||||
_, span := trace.StartSpan(ctx, "BeaconDB.OriginCheckpointBlockRoot")
|
||||
func (s *Store) OriginBlockRoot(ctx context.Context) ([32]byte, error) {
|
||||
_, span := trace.StartSpan(ctx, "BeaconDB.OriginBlockRoot")
|
||||
defer span.End()
|
||||
|
||||
var root [32]byte
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(blocksBucket)
|
||||
rootSlice := bkt.Get(originCheckpointBlockRootKey)
|
||||
rootSlice := bkt.Get(originBlockRootKey)
|
||||
if rootSlice == nil {
|
||||
return ErrNotFoundOriginBlockRoot
|
||||
}
|
||||
@@ -70,26 +69,6 @@ func (s *Store) OriginCheckpointBlockRoot(ctx context.Context) ([32]byte, error)
|
||||
return root, err
|
||||
}
|
||||
|
||||
// LowestSyncedBlockSlot returns the lowest slot number in the block root index
|
||||
func (s *Store) LowestSyncedBlockSlot(ctx context.Context) (types.Slot, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.LowestSyncedBlockSlot")
|
||||
defer span.End()
|
||||
// start scan at zero, which will yield the lowest key in the collection.
|
||||
var slot types.Slot
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
// Iterate through the index, which is in byte sorted order.
|
||||
bkt := tx.Bucket(blockSlotIndicesBucket)
|
||||
c := bkt.Cursor()
|
||||
k, _ := c.First()
|
||||
if k == nil {
|
||||
return errors.Wrapf(errEmptyBucket, "'%s' bucket is empty", string(blockSlotIndicesBucket))
|
||||
}
|
||||
slot = bytesutil.BytesToSlotBigEndian(k)
|
||||
return nil
|
||||
})
|
||||
return slot, err
|
||||
}
|
||||
|
||||
// HeadBlock returns the latest canonical block in the Ethereum Beacon Chain.
|
||||
func (s *Store) HeadBlock(ctx context.Context) (block.SignedBeaconBlock, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.HeadBlock")
|
||||
@@ -355,16 +334,16 @@ func (s *Store) SaveGenesisBlockRoot(ctx context.Context, blockRoot [32]byte) er
|
||||
})
|
||||
}
|
||||
|
||||
// SaveOriginCheckpointBlockRoot is used to keep track of the block root used for syncing from a checkpoint origin.
|
||||
// SaveOriginBlockRoot is used to keep track of the block root used for origin sync.
|
||||
// This should be a finalized block from within the current weak subjectivity period.
|
||||
// This value is used by a running beacon chain node to locate the state at the beginning
|
||||
// of the chain history, in places where genesis would typically be used.
|
||||
func (s *Store) SaveOriginCheckpointBlockRoot(ctx context.Context, blockRoot [32]byte) error {
|
||||
_, span := trace.StartSpan(ctx, "BeaconDB.SaveOriginCheckpointBlockRoot")
|
||||
func (s *Store) SaveOriginBlockRoot(ctx context.Context, blockRoot [32]byte) error {
|
||||
_, span := trace.StartSpan(ctx, "BeaconDB.SaveOriginBlockRoot")
|
||||
defer span.End()
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(blocksBucket)
|
||||
return bucket.Put(originCheckpointBlockRootKey, blockRoot[:])
|
||||
return bucket.Put(originBlockRootKey, blockRoot[:])
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -58,30 +58,6 @@ var blockTests = []struct {
|
||||
},
|
||||
}
|
||||
|
||||
func TestStore_LowestSyncedBlockSlot(t *testing.T) {
|
||||
// test case: make sure expected error is returned w/ an empty bucket
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
_, err := db.LowestSyncedBlockSlot(ctx)
|
||||
require.ErrorIs(t, err, errEmptyBucket)
|
||||
|
||||
// test case: make sure that the right value is returned using a non-zero key
|
||||
block := util.NewBeaconBlock()
|
||||
block.Block.Slot = types.Slot(1 << 32)
|
||||
require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(block)))
|
||||
slot, err := db.LowestSyncedBlockSlot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, slot, block.Block.Slot)
|
||||
|
||||
// test case: write a lower value and make sure that it is returned instead of the existing value
|
||||
zero := util.NewBeaconBlock()
|
||||
zero.Block.Slot = types.Slot(0)
|
||||
require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(zero)))
|
||||
zslot, err := db.LowestSyncedBlockSlot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, zslot, zero.Block.Slot)
|
||||
}
|
||||
|
||||
func TestStore_SaveBlock_NoDuplicates(t *testing.T) {
|
||||
BlockCacheSize = 1
|
||||
slot := types.Slot(20)
|
||||
|
||||
@@ -46,7 +46,6 @@ func (s *Store) updateFinalizedBlockRoots(ctx context.Context, tx *bolt.Tx, chec
|
||||
root := checkpoint.Root
|
||||
var previousRoot []byte
|
||||
genesisRoot := tx.Bucket(blocksBucket).Get(genesisBlockRootKey)
|
||||
initCheckpointRoot := tx.Bucket(blocksBucket).Get(originCheckpointBlockRootKey)
|
||||
|
||||
// De-index recent finalized block roots, to be re-indexed.
|
||||
previousFinalizedCheckpoint := ðpb.Checkpoint{}
|
||||
@@ -75,7 +74,7 @@ func (s *Store) updateFinalizedBlockRoots(ctx context.Context, tx *bolt.Tx, chec
|
||||
// Walk up the ancestry chain until we reach a block root present in the finalized block roots
|
||||
// index bucket or genesis block root.
|
||||
for {
|
||||
if bytes.Equal(root, genesisRoot) || bytes.Equal(root, initCheckpointRoot) {
|
||||
if bytes.Equal(root, genesisRoot) {
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,6 @@ func NewKVStore(ctx context.Context, dirPath string, config *Config) (*Store, er
|
||||
powchainBucket,
|
||||
stateSummaryBucket,
|
||||
stateValidatorsBucket,
|
||||
validatedTips,
|
||||
// Indices buckets.
|
||||
attestationHeadBlockRootBucket,
|
||||
attestationSourceRootIndicesBucket,
|
||||
|
||||
@@ -19,7 +19,6 @@ var (
|
||||
powchainBucket = []byte("powchain")
|
||||
stateValidatorsBucket = []byte("state-validators")
|
||||
feeRecipientBucket = []byte("fee-recipient")
|
||||
validatedTips = []byte("validated-synced-tips")
|
||||
|
||||
// Deprecated: This bucket was migrated in PR 6461. Do not use, except for migrations.
|
||||
slotsHasObjectBucket = []byte("slots-has-objects")
|
||||
@@ -39,19 +38,20 @@ var (
|
||||
blockRootValidatorHashesBucket = []byte("block-root-validator-hashes")
|
||||
|
||||
// Specific item keys.
|
||||
headBlockRootKey = []byte("head-root")
|
||||
genesisBlockRootKey = []byte("genesis-root")
|
||||
depositContractAddressKey = []byte("deposit-contract")
|
||||
justifiedCheckpointKey = []byte("justified-checkpoint")
|
||||
finalizedCheckpointKey = []byte("finalized-checkpoint")
|
||||
powchainDataKey = []byte("powchain-data")
|
||||
headBlockRootKey = []byte("head-root")
|
||||
genesisBlockRootKey = []byte("genesis-root")
|
||||
depositContractAddressKey = []byte("deposit-contract")
|
||||
justifiedCheckpointKey = []byte("justified-checkpoint")
|
||||
finalizedCheckpointKey = []byte("finalized-checkpoint")
|
||||
powchainDataKey = []byte("powchain-data")
|
||||
lastValidatedCheckpointKey = []byte("last-validated-checkpoint")
|
||||
|
||||
// Below keys are used to identify objects are to be fork compatible.
|
||||
// Objects that are only compatible with specific forks should be prefixed with such keys.
|
||||
altairKey = []byte("altair")
|
||||
bellatrixKey = []byte("merge")
|
||||
// block root included in the beacon state used by weak subjectivity initial sync
|
||||
originCheckpointBlockRootKey = []byte("origin-checkpoint-block-root")
|
||||
originBlockRootKey = []byte("origin-block-root")
|
||||
|
||||
// Deprecated: This index key was migrated in PR 6461. Do not use, except for migrations.
|
||||
lastArchivedIndexKey = []byte("last-archived")
|
||||
|
||||
49
beacon-chain/db/kv/validated_checkpoint.go
Normal file
49
beacon-chain/db/kv/validated_checkpoint.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// LastValidatedCheckpoint returns the latest fully validated checkpoint in beacon chain.
|
||||
func (s *Store) LastValidatedCheckpoint(ctx context.Context) (*ethpb.Checkpoint, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.LastValidatedCheckpoint")
|
||||
defer span.End()
|
||||
var checkpoint *ethpb.Checkpoint
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(checkpointBucket)
|
||||
enc := bkt.Get(lastValidatedCheckpointKey)
|
||||
if enc == nil {
|
||||
var finErr error
|
||||
checkpoint, finErr = s.FinalizedCheckpoint(ctx)
|
||||
return finErr
|
||||
}
|
||||
checkpoint = ðpb.Checkpoint{}
|
||||
return decode(ctx, enc, checkpoint)
|
||||
})
|
||||
return checkpoint, err
|
||||
}
|
||||
|
||||
// SaveLastValidatedCheckpoint saves the last validated checkpoint in beacon chain.
|
||||
func (s *Store) SaveLastValidatedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveLastValidatedCheckpoint")
|
||||
defer span.End()
|
||||
|
||||
enc, err := encode(ctx, checkpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(checkpointBucket)
|
||||
hasStateSummary := s.hasStateSummaryBytes(tx, bytesutil.ToBytes32(checkpoint.Root))
|
||||
hasStateInDB := tx.Bucket(stateBucket).Get(checkpoint.Root) != nil
|
||||
if !(hasStateInDB || hasStateSummary) {
|
||||
return errMissingStateForCheckpoint
|
||||
}
|
||||
return bucket.Put(lastValidatedCheckpointKey, enc)
|
||||
})
|
||||
}
|
||||
67
beacon-chain/db/kv/validated_checkpoint_test.go
Normal file
67
beacon-chain/db/kv/validated_checkpoint_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/testing/util"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestStore_LastValidatedCheckpoint_CanSaveRetrieve(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
root := bytesutil.ToBytes32([]byte{'A'})
|
||||
cp := ðpb.Checkpoint{
|
||||
Epoch: 10,
|
||||
Root: root[:],
|
||||
}
|
||||
st, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, st.SetSlot(1))
|
||||
require.NoError(t, db.SaveState(ctx, st, root))
|
||||
require.NoError(t, db.SaveLastValidatedCheckpoint(ctx, cp))
|
||||
|
||||
retrieved, err := db.LastValidatedCheckpoint(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, proto.Equal(cp, retrieved), "Wanted %v, received %v", cp, retrieved)
|
||||
}
|
||||
|
||||
func TestStore_LastValidatedCheckpoint_DefaultIsFinalized(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
genesis := bytesutil.ToBytes32([]byte{'G', 'E', 'N', 'E', 'S', 'I', 'S'})
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesis))
|
||||
|
||||
blk := util.NewBeaconBlock()
|
||||
blk.Block.ParentRoot = genesis[:]
|
||||
blk.Block.Slot = 40
|
||||
|
||||
root, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
cp := ðpb.Checkpoint{
|
||||
Epoch: 5,
|
||||
Root: root[:],
|
||||
}
|
||||
|
||||
// a valid chain is required to save finalized checkpoint.
|
||||
require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(blk)))
|
||||
st, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, st.SetSlot(1))
|
||||
// a state is required to save checkpoint
|
||||
require.NoError(t, db.SaveState(ctx, st, root))
|
||||
|
||||
require.NoError(t, db.SaveFinalizedCheckpoint(ctx, cp))
|
||||
|
||||
retrieved, err := db.LastValidatedCheckpoint(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, proto.Equal(cp, retrieved), "Wanted %v, received %v", cp, retrieved)
|
||||
}
|
||||
@@ -2,58 +2,56 @@ package kv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
statev2 "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/proto/detect"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
)
|
||||
|
||||
// SaveOrigin loads an ssz serialized Block & BeaconState from an io.Reader
|
||||
// (ex: an open file) prepares the database so that the beacon node can begin
|
||||
// syncing, using the provided values as their point of origin. This is an alternative
|
||||
// to syncing from genesis, and should only be run on an empty database.
|
||||
func (s *Store) SaveOrigin(ctx context.Context, serState, serBlock []byte) error {
|
||||
cf, err := detect.ByState(serState)
|
||||
func (s *Store) SaveOrigin(ctx context.Context, stateReader, blockReader io.Reader) error {
|
||||
// unmarshal both block and state before trying to save anything
|
||||
// so that we fail early if there is any issue with the ssz data
|
||||
blk := ðpb.SignedBeaconBlockAltair{}
|
||||
bb, err := ioutil.ReadAll(blockReader)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not sniff config+fork for origin state bytes")
|
||||
return errors.Wrap(err, "error reading block given to SaveOrigin")
|
||||
}
|
||||
_, ok := params.BeaconConfig().ForkVersionSchedule[cf.Version]
|
||||
if !ok {
|
||||
return fmt.Errorf("config mismatch, beacon node configured to connect to %s, detected state is for %s", params.BeaconConfig().ConfigName, cf.ConfigName.String())
|
||||
if err := blk.UnmarshalSSZ(bb); err != nil {
|
||||
return errors.Wrap(err, "could not unmarshal checkpoint block")
|
||||
}
|
||||
|
||||
log.Printf("detected supported config for state & block version detection, name=%s, fork=%s", cf.ConfigName.String(), cf.Fork)
|
||||
state, err := cf.UnmarshalBeaconState(serState)
|
||||
wblk, err := wrapper.WrappedAltairSignedBeaconBlock(blk)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to initialize origin state w/ bytes + config+fork")
|
||||
return errors.Wrap(err, "could not wrap checkpoint block")
|
||||
}
|
||||
|
||||
wblk, err := cf.UnmarshalBeaconBlock(serBlock)
|
||||
bs, err := statev2.InitializeFromSSZReader(stateReader)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to initialize origin block w/ bytes + config+fork")
|
||||
return errors.Wrap(err, "could not initialize checkpoint state from reader")
|
||||
}
|
||||
blk := wblk.Block()
|
||||
|
||||
// save block
|
||||
blockRoot, err := blk.HashTreeRoot()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not compute HashTreeRoot of checkpoint block")
|
||||
}
|
||||
log.Infof("saving checkpoint block to db, w/ root=%#x", blockRoot)
|
||||
if err := s.SaveBlock(ctx, wblk); err != nil {
|
||||
return errors.Wrap(err, "could not save checkpoint block")
|
||||
}
|
||||
blockRoot, err := blk.Block.HashTreeRoot()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not compute HashTreeRoot of checkpoint block")
|
||||
}
|
||||
|
||||
// save state
|
||||
log.Infof("calling SaveState w/ blockRoot=%x", blockRoot)
|
||||
if err = s.SaveState(ctx, state, blockRoot); err != nil {
|
||||
if err = s.SaveState(ctx, bs, blockRoot); err != nil {
|
||||
return errors.Wrap(err, "could not save state")
|
||||
}
|
||||
if err = s.SaveStateSummary(ctx, ðpb.StateSummary{
|
||||
Slot: state.Slot(),
|
||||
Slot: bs.Slot(),
|
||||
Root: blockRoot[:],
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "could not save state summary")
|
||||
@@ -61,7 +59,7 @@ func (s *Store) SaveOrigin(ctx context.Context, serState, serBlock []byte) error
|
||||
|
||||
// save origin block root in special key, to be used when the canonical
|
||||
// origin (start of chain, ie alternative to genesis) block or state is needed
|
||||
if err = s.SaveOriginCheckpointBlockRoot(ctx, blockRoot); err != nil {
|
||||
if err = s.SaveOriginBlockRoot(ctx, blockRoot); err != nil {
|
||||
return errors.Wrap(err, "could not save origin block root")
|
||||
}
|
||||
|
||||
@@ -72,7 +70,7 @@ func (s *Store) SaveOrigin(ctx context.Context, serState, serBlock []byte) error
|
||||
|
||||
// rebuild the checkpoint from the block
|
||||
// use it to mark the block as justified and finalized
|
||||
slotEpoch, err := blk.Slot().SafeDivSlot(params.BeaconConfig().SlotsPerEpoch)
|
||||
slotEpoch, err := blk.Block.Slot.SafeDivSlot(params.BeaconConfig().SlotsPerEpoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package doublylinkedtree
|
||||
|
||||
import "errors"
|
||||
|
||||
var errNilNode = errors.New("invalid nil or unknown node")
|
||||
var ErrNilNode = errors.New("invalid nil or unknown node")
|
||||
var errInvalidBalance = errors.New("invalid node balance")
|
||||
var errInvalidProposerBoostRoot = errors.New("invalid proposer boost root")
|
||||
var errUnknownFinalizedRoot = errors.New("unknown finalized root")
|
||||
|
||||
@@ -174,7 +174,7 @@ func (f *ForkChoice) IsOptimistic(_ context.Context, root [32]byte) (bool, error
|
||||
|
||||
node, ok := f.store.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return false, errNilNode
|
||||
return false, ErrNilNode
|
||||
}
|
||||
|
||||
return node.optimistic, nil
|
||||
@@ -190,7 +190,7 @@ func (f *ForkChoice) AncestorRoot(ctx context.Context, root [32]byte, slot types
|
||||
|
||||
node, ok := f.store.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return nil, errNilNode
|
||||
return nil, ErrNilNode
|
||||
}
|
||||
|
||||
n := node
|
||||
@@ -202,7 +202,7 @@ func (f *ForkChoice) AncestorRoot(ctx context.Context, root [32]byte, slot types
|
||||
}
|
||||
|
||||
if n == nil {
|
||||
return nil, errNilNode
|
||||
return nil, ErrNilNode
|
||||
}
|
||||
|
||||
return n.root[:], nil
|
||||
@@ -237,7 +237,7 @@ func (f *ForkChoice) updateBalances(newBalances []uint64) error {
|
||||
if ok && vote.nextRoot != params.BeaconConfig().ZeroHash {
|
||||
// Protection against nil node
|
||||
if nextNode == nil {
|
||||
return errNilNode
|
||||
return ErrNilNode
|
||||
}
|
||||
nextNode.balance += newBalance
|
||||
}
|
||||
@@ -246,7 +246,7 @@ func (f *ForkChoice) updateBalances(newBalances []uint64) error {
|
||||
if ok && vote.currentRoot != params.BeaconConfig().ZeroHash {
|
||||
// Protection against nil node
|
||||
if currentNode == nil {
|
||||
return errNilNode
|
||||
return ErrNilNode
|
||||
}
|
||||
if currentNode.balance < oldBalance {
|
||||
return errInvalidBalance
|
||||
@@ -279,7 +279,7 @@ func (f *ForkChoice) SetOptimisticToValid(ctx context.Context, root [fieldparams
|
||||
defer f.store.nodesLock.Unlock()
|
||||
node, ok := f.store.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return errNilNode
|
||||
return ErrNilNode
|
||||
}
|
||||
return node.setNodeAndParentValidated(ctx)
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ func TestForkChoice_AncestorRoot(t *testing.T) {
|
||||
assert.Equal(t, bytesutil.ToBytes32(r), indexToHash(3))
|
||||
|
||||
_, err = f.AncestorRoot(ctx, indexToHash(3), 0)
|
||||
assert.ErrorContains(t, errNilNode.Error(), err)
|
||||
assert.ErrorContains(t, ErrNilNode.Error(), err)
|
||||
|
||||
root, err := f.AncestorRoot(ctx, indexToHash(3), 5)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -55,7 +55,7 @@ func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finaliz
|
||||
hasViableDescendant := false
|
||||
for _, child := range n.children {
|
||||
if child == nil {
|
||||
return errNilNode
|
||||
return ErrNilNode
|
||||
}
|
||||
if err := child.updateBestDescendant(ctx, justifiedEpoch, finalizedEpoch); err != nil {
|
||||
return err
|
||||
|
||||
@@ -12,7 +12,7 @@ func (s *Store) removeNode(ctx context.Context, root [32]byte) error {
|
||||
|
||||
node, ok := s.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return errNilNode
|
||||
return ErrNilNode
|
||||
}
|
||||
if !node.optimistic || node.parent == nil {
|
||||
return errInvalidOptimisticStatus
|
||||
|
||||
@@ -5,7 +5,7 @@ import "errors"
|
||||
var errUnknownFinalizedRoot = errors.New("unknown finalized root")
|
||||
var errUnknownJustifiedRoot = errors.New("unknown justified root")
|
||||
var errInvalidNodeIndex = errors.New("node index is invalid")
|
||||
var errUnknownNodeRoot = errors.New("unknown block root")
|
||||
var ErrUnknownNodeRoot = errors.New("unknown block root")
|
||||
var errInvalidJustifiedIndex = errors.New("justified index is invalid")
|
||||
var errInvalidBestChildIndex = errors.New("best child index is invalid")
|
||||
var errInvalidBestDescendantIndex = errors.New("best descendant index is invalid")
|
||||
|
||||
@@ -40,7 +40,7 @@ func (f *ForkChoice) IsOptimistic(ctx context.Context, root [32]byte) (bool, err
|
||||
index, ok := f.store.nodesIndices[root]
|
||||
if !ok {
|
||||
f.store.nodesLock.RUnlock()
|
||||
return false, errUnknownNodeRoot
|
||||
return false, ErrUnknownNodeRoot
|
||||
}
|
||||
node := f.store.nodes[index]
|
||||
slot := node.slot
|
||||
|
||||
@@ -147,10 +147,10 @@ func TestOptimistic(t *testing.T) {
|
||||
|
||||
// We test first nodes outside the Fork Choice store
|
||||
_, err := f.IsOptimistic(ctx, root0)
|
||||
require.ErrorIs(t, errUnknownNodeRoot, err)
|
||||
require.ErrorIs(t, ErrUnknownNodeRoot, err)
|
||||
|
||||
_, err = f.IsOptimistic(ctx, root1)
|
||||
require.ErrorIs(t, errUnknownNodeRoot, err)
|
||||
require.ErrorIs(t, ErrUnknownNodeRoot, err)
|
||||
|
||||
// We check all nodes in the Fork Choice store.
|
||||
op, err := f.IsOptimistic(ctx, nodeA.root)
|
||||
|
||||
@@ -41,7 +41,6 @@ go_library(
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//beacon-chain/sync:go_default_library",
|
||||
"//beacon-chain/sync/checkpoint:go_default_library",
|
||||
"//beacon-chain/sync/initial-sync:go_default_library",
|
||||
"//cmd:go_default_library",
|
||||
"//cmd/beacon-chain/flags:go_default_library",
|
||||
|
||||
@@ -25,7 +25,7 @@ func configureTracing(cliCtx *cli.Context) error {
|
||||
func configureChainConfig(cliCtx *cli.Context) {
|
||||
if cliCtx.IsSet(cmd.ChainConfigFileFlag.Name) {
|
||||
chainConfigFileName := cliCtx.String(cmd.ChainConfigFileFlag.Name)
|
||||
params.LoadChainConfigFile(chainConfigFileName, nil)
|
||||
params.LoadChainConfigFile(chainConfigFileName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
regularsync "github.com/prysmaticlabs/prysm/beacon-chain/sync"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/sync/checkpoint"
|
||||
initialsync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync"
|
||||
"github.com/prysmaticlabs/prysm/cmd"
|
||||
"github.com/prysmaticlabs/prysm/cmd/beacon-chain/flags"
|
||||
@@ -102,8 +101,6 @@ type BeaconNode struct {
|
||||
slasherAttestationsFeed *event.Feed
|
||||
finalizedStateAtStartUp state.BeaconState
|
||||
serviceFlagOpts *serviceFlagOpts
|
||||
blockchainFlagOpts []blockchain.Option
|
||||
CheckpointInitializer checkpoint.Initializer
|
||||
}
|
||||
|
||||
// New creates a new node instance, sets up configuration options, and registers
|
||||
@@ -242,7 +239,7 @@ func New(cliCtx *cli.Context, opts ...Option) (*BeaconNode, error) {
|
||||
|
||||
// db.DatabasePath is the path to the containing directory
|
||||
// db.NewDBFilename expands that to the canonical full path using
|
||||
// the same construction as NewDB()
|
||||
// the same constuction as NewDB()
|
||||
c, err := newBeaconNodePromCollector(db.NewDBFilename(beacon.db.DatabasePath()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -399,13 +396,6 @@ func (b *BeaconNode) startDB(cliCtx *cli.Context, depositAddress string) error {
|
||||
if err := b.db.EnsureEmbeddedGenesis(b.ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if b.CheckpointInitializer != nil {
|
||||
if err := b.CheckpointInitializer.Initialize(b.ctx, d); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
knownContract, err := b.db.DepositContractAddress(b.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -474,29 +464,7 @@ func (b *BeaconNode) startSlasherDB(cliCtx *cli.Context) error {
|
||||
}
|
||||
|
||||
func (b *BeaconNode) startStateGen() error {
|
||||
o := make([]stategen.StateGenOption, 0)
|
||||
_, err := b.db.OriginCheckpointBlockRoot(b.ctx)
|
||||
if err == nil {
|
||||
// we'll get a db.ErrNotFound if the db was not initialized with a checkpoint
|
||||
// so if err == nil, the node was initialized with checkpoint init.
|
||||
o = append(o, stategen.WithInitType(stategen.BeaconDBInitTypeCheckpoint))
|
||||
lowestSlot, err := b.db.LowestSyncedBlockSlot(b.ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error searching for lowest block slot on checkpoint-initialized node")
|
||||
}
|
||||
o = append(o, stategen.WithMinimumSlot(lowestSlot))
|
||||
} else {
|
||||
if errors.Is(err, db.ErrNotFound) {
|
||||
// error is ErrNotFound, so we can assume the node was initialized with a genesis state (or via powchain)
|
||||
o = append(o, stategen.WithInitType(stategen.BeaconDBInitTypeGenesisState))
|
||||
} else {
|
||||
// if the error is not an ErrNotFound, we failed to query the db for some reason,
|
||||
// can't be confident how it was initialized.
|
||||
return errors.Wrap(err, "could not retrieve checkpoint sync chain origin data from db")
|
||||
}
|
||||
}
|
||||
|
||||
sg := stategen.New(b.db, o...)
|
||||
b.stateGen = stategen.New(b.db)
|
||||
|
||||
cp, err := b.db.FinalizedCheckpoint(b.ctx)
|
||||
if err != nil {
|
||||
@@ -506,12 +474,6 @@ func (b *BeaconNode) startStateGen() error {
|
||||
r := bytesutil.ToBytes32(cp.Root)
|
||||
// Consider edge case where finalized root are zeros instead of genesis root hash.
|
||||
if r == params.BeaconConfig().ZeroHash {
|
||||
if sg.MinimumSlot() > 0 {
|
||||
// using checkpoint sync the minimum slot will be > 0, but the checkpoint block is marked as finalized,
|
||||
// so if it is equal to ZeroHash, this is likely an error (and the genesis block won't be available).
|
||||
msg := fmt.Sprintf("unable to retrieve genesis block, no slots before %d in db", sg.MinimumSlot())
|
||||
return errors.Wrap(err, msg)
|
||||
}
|
||||
genesisBlock, err := b.db.GenesisBlock(b.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -524,12 +486,10 @@ func (b *BeaconNode) startStateGen() error {
|
||||
}
|
||||
}
|
||||
|
||||
b.finalizedStateAtStartUp, err = sg.StateByRoot(b.ctx, r)
|
||||
b.finalizedStateAtStartUp, err = b.stateGen.StateByRoot(b.ctx, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.stateGen = sg
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -596,7 +596,7 @@ func fixtures() map[string]interface{} {
|
||||
foo := bytesutil.ToBytes32([]byte("foo"))
|
||||
bar := bytesutil.PadTo([]byte("bar"), 20)
|
||||
baz := bytesutil.PadTo([]byte("baz"), 256)
|
||||
baseFeePerGas := big.NewInt(6)
|
||||
baseFeePerGas := big.NewInt(12345)
|
||||
executionPayloadFixture := &pb.ExecutionPayload{
|
||||
ParentHash: foo[:],
|
||||
FeeRecipient: bar,
|
||||
|
||||
@@ -10,7 +10,7 @@ go_library(
|
||||
"structs_marshalling.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/rpc/apimiddleware",
|
||||
visibility = ["//visibility:public"],
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//api/gateway/apimiddleware:go_default_library",
|
||||
"//api/grpc:go_default_library",
|
||||
|
||||
@@ -17,6 +17,29 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
)
|
||||
|
||||
// https://ethereum.github.io/beacon-apis/#/Validator/prepareBeaconProposer expects posting a top-level array.
|
||||
// We make it more proto-friendly by wrapping it in a struct.
|
||||
func wrapFeeRecipientsArray(
|
||||
endpoint *apimiddleware.Endpoint,
|
||||
_ http.ResponseWriter,
|
||||
req *http.Request,
|
||||
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
||||
if _, ok := endpoint.PostRequest.(*feeRecipientsRequestJSON); !ok {
|
||||
return true, nil
|
||||
}
|
||||
recipients := make([]*feeRecipientJson, 0)
|
||||
if err := json.NewDecoder(req.Body).Decode(&recipients); err != nil {
|
||||
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
|
||||
}
|
||||
j := &feeRecipientsRequestJSON{Recipients: recipients}
|
||||
b, err := json.Marshal(j)
|
||||
if err != nil {
|
||||
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
|
||||
}
|
||||
req.Body = ioutil.NopCloser(bytes.NewReader(b))
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// https://ethereum.github.io/beacon-apis/#/Beacon/submitPoolAttestations expects posting a top-level array.
|
||||
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
|
||||
func wrapAttestationsArray(
|
||||
@@ -412,6 +435,11 @@ type altairProduceBlockResponseJson struct {
|
||||
Data *beaconBlockAltairJson `json:"data"`
|
||||
}
|
||||
|
||||
type bellatrixProduceBlockResponseJson struct {
|
||||
Version string `json:"version"`
|
||||
Data *beaconBlockBellatrixJson `json:"data"`
|
||||
}
|
||||
|
||||
func serializeProducedV2Block(response interface{}) (apimiddleware.RunDefault, []byte, apimiddleware.ErrorJson) {
|
||||
respContainer, ok := response.(*produceBlockResponseV2Json)
|
||||
if !ok {
|
||||
@@ -429,6 +457,11 @@ func serializeProducedV2Block(response interface{}) (apimiddleware.RunDefault, [
|
||||
Version: respContainer.Version,
|
||||
Data: respContainer.Data.AltairBlock,
|
||||
}
|
||||
} else if strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_BELLATRIX.String())) {
|
||||
actualRespContainer = &bellatrixProduceBlockResponseJson{
|
||||
Version: respContainer.Version,
|
||||
Data: respContainer.Data.BellatrixBlock,
|
||||
}
|
||||
} else {
|
||||
return false, nil, apimiddleware.InternalServerError(fmt.Errorf("unsupported block version '%s'", respContainer.Version))
|
||||
}
|
||||
|
||||
@@ -671,7 +671,7 @@ func TestSerializeV2State(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSerializeProduceV2Block(t *testing.T) {
|
||||
func TestSerializeProducedV2Block(t *testing.T) {
|
||||
t.Run("Phase 0", func(t *testing.T) {
|
||||
response := &produceBlockResponseV2Json{
|
||||
Version: ethpbv2.Version_PHASE0.String(),
|
||||
@@ -683,7 +683,6 @@ func TestSerializeProduceV2Block(t *testing.T) {
|
||||
StateRoot: "root",
|
||||
Body: &beaconBlockBodyJson{},
|
||||
},
|
||||
AltairBlock: nil,
|
||||
},
|
||||
}
|
||||
runDefault, j, errJson := serializeProducedV2Block(response)
|
||||
@@ -706,7 +705,6 @@ func TestSerializeProduceV2Block(t *testing.T) {
|
||||
response := &produceBlockResponseV2Json{
|
||||
Version: ethpbv2.Version_ALTAIR.String(),
|
||||
Data: &beaconBlockContainerV2Json{
|
||||
Phase0Block: nil,
|
||||
AltairBlock: &beaconBlockAltairJson{
|
||||
Slot: "1",
|
||||
ProposerIndex: "1",
|
||||
@@ -732,6 +730,35 @@ func TestSerializeProduceV2Block(t *testing.T) {
|
||||
require.NotNil(t, beaconBlock.Body)
|
||||
})
|
||||
|
||||
t.Run("Bellatrix", func(t *testing.T) {
|
||||
response := &produceBlockResponseV2Json{
|
||||
Version: ethpbv2.Version_BELLATRIX.String(),
|
||||
Data: &beaconBlockContainerV2Json{
|
||||
BellatrixBlock: &beaconBlockBellatrixJson{
|
||||
Slot: "1",
|
||||
ProposerIndex: "1",
|
||||
ParentRoot: "root",
|
||||
StateRoot: "root",
|
||||
Body: &beaconBlockBodyBellatrixJson{},
|
||||
},
|
||||
},
|
||||
}
|
||||
runDefault, j, errJson := serializeProducedV2Block(response)
|
||||
require.Equal(t, nil, errJson)
|
||||
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
|
||||
require.NotNil(t, j)
|
||||
resp := &bellatrixProduceBlockResponseJson{}
|
||||
require.NoError(t, json.Unmarshal(j, resp))
|
||||
require.NotNil(t, resp.Data)
|
||||
require.NotNil(t, resp.Data)
|
||||
beaconBlock := resp.Data
|
||||
assert.Equal(t, "1", beaconBlock.Slot)
|
||||
assert.Equal(t, "1", beaconBlock.ProposerIndex)
|
||||
assert.Equal(t, "root", beaconBlock.ParentRoot)
|
||||
assert.Equal(t, "root", beaconBlock.StateRoot)
|
||||
require.NotNil(t, beaconBlock.Body)
|
||||
})
|
||||
|
||||
t.Run("incorrect response type", func(t *testing.T) {
|
||||
response := &types.Empty{}
|
||||
runDefault, j, errJson := serializeProducedV2Block(response)
|
||||
|
||||
@@ -37,7 +37,6 @@ func (_ *BeaconEndpointFactory) Paths() []string {
|
||||
"/eth/v1/beacon/pool/proposer_slashings",
|
||||
"/eth/v1/beacon/pool/voluntary_exits",
|
||||
"/eth/v1/beacon/pool/sync_committees",
|
||||
"/eth/v1/beacon/weak_subjectivity",
|
||||
"/eth/v1/node/identity",
|
||||
"/eth/v1/node/peers",
|
||||
"/eth/v1/node/peers/{peer_id}",
|
||||
@@ -64,6 +63,7 @@ func (_ *BeaconEndpointFactory) Paths() []string {
|
||||
"/eth/v1/validator/aggregate_and_proofs",
|
||||
"/eth/v1/validator/sync_committee_contribution",
|
||||
"/eth/v1/validator/contribution_and_proofs",
|
||||
"/eth/v1/validator/prepare_beacon_proposer",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,8 +142,6 @@ func (_ *BeaconEndpointFactory) Create(path string) (*apimiddleware.Endpoint, er
|
||||
endpoint.Hooks = apimiddleware.HookCollection{
|
||||
OnPreDeserializeRequestBodyIntoContainer: wrapSyncCommitteeSignaturesArray,
|
||||
}
|
||||
case "/eth/v1/beacon/weak_subjectivity":
|
||||
endpoint.GetResponse = &WeakSubjectivityResponse{}
|
||||
case "/eth/v1/node/identity":
|
||||
endpoint.GetResponse = &identityResponseJson{}
|
||||
case "/eth/v1/node/peers":
|
||||
@@ -241,6 +239,11 @@ func (_ *BeaconEndpointFactory) Create(path string) (*apimiddleware.Endpoint, er
|
||||
endpoint.Hooks = apimiddleware.HookCollection{
|
||||
OnPreDeserializeRequestBodyIntoContainer: wrapSignedContributionAndProofsArray,
|
||||
}
|
||||
case "/eth/v1/validator/prepare_beacon_proposer":
|
||||
endpoint.PostRequest = &feeRecipientsRequestJSON{}
|
||||
endpoint.Hooks = apimiddleware.HookCollection{
|
||||
OnPreDeserializeRequestBodyIntoContainer: wrapFeeRecipientsArray,
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("invalid path")
|
||||
}
|
||||
|
||||
@@ -19,13 +19,9 @@ type genesisResponse_GenesisJson struct {
|
||||
GenesisForkVersion string `json:"genesis_fork_version" hex:"true"`
|
||||
}
|
||||
|
||||
// WeakSubjectivityResponse is used to marshal/unmarshal the response for the
|
||||
// /eth/v1/beacon/weak_subjectivity endpoint.
|
||||
type WeakSubjectivityResponse struct {
|
||||
Data *struct {
|
||||
Checkpoint *checkpointJson `json:"ws_checkpoint"`
|
||||
StateRoot string `json:"state_root" hex:"true"`
|
||||
} `json:"data"`
|
||||
// feeRecipientsRequestJson is used in /validator/prepare_beacon_proposers API endpoint.
|
||||
type feeRecipientsRequestJSON struct {
|
||||
Recipients []*feeRecipientJson `json:"recipients"`
|
||||
}
|
||||
|
||||
// stateRootResponseJson is used in /beacon/states/{state_id}/root API endpoint.
|
||||
@@ -480,6 +476,11 @@ type indexedAttestationJson struct {
|
||||
Signature string `json:"signature" hex:"true"`
|
||||
}
|
||||
|
||||
type feeRecipientJson struct {
|
||||
ValidatorIndex uint64 `json:"validator_index"`
|
||||
FeeRecipient string `json:"fee_recipient" hex:"true"`
|
||||
}
|
||||
|
||||
type attestationJson struct {
|
||||
AggregationBits string `json:"aggregation_bits" hex:"true"`
|
||||
Data *attestationDataJson `json:"data"`
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
|
||||
@@ -18,7 +17,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/proto/migration"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
"go.opencensus.io/trace"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
@@ -42,43 +40,6 @@ func (e *blockIdParseError) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
// GetWeakSubjectivity computes the starting epoch of the current weak subjectivity period, and then also
|
||||
// determines the best block root and state root to use for a Checkpoint Sync starting from that point.
|
||||
func (bs *Server) GetWeakSubjectivity(ctx context.Context, _ *empty.Empty) (*ethpbv1.WeakSubjectivityResponse, error) {
|
||||
hs, err := bs.HeadFetcher.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "could not get head state")
|
||||
}
|
||||
wsEpoch, err := helpers.LatestWeakSubjectivityEpoch(ctx, hs)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not get weak subjectivity epoch: %v", err)
|
||||
}
|
||||
wsSlot, err := slots.EpochStart(wsEpoch)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not get weak subjectivity slot: %v", err)
|
||||
}
|
||||
blks, err := bs.BeaconDB.HighestSlotBlocksBelow(ctx, wsSlot+1)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, fmt.Sprintf("could not find highest block below slot %d", wsSlot))
|
||||
}
|
||||
block := blks[0]
|
||||
blockRoot, err := block.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, fmt.Sprintf("failed to compute hash_tree_root for block at slot=%d", block.Block().Slot()))
|
||||
}
|
||||
stateRoot := bytesutil.ToBytes32(block.Block().StateRoot())
|
||||
log.Printf("weak subjectivity checkpoint reported as epoch=%d, block root=%#x, state root=%#x", wsEpoch, blockRoot, stateRoot)
|
||||
return ðpbv1.WeakSubjectivityResponse{
|
||||
Data: ðpbv1.WeakSubjectivityData{
|
||||
WsCheckpoint: ðpbv1.Checkpoint{
|
||||
Epoch: wsEpoch,
|
||||
Root: blockRoot[:],
|
||||
},
|
||||
StateRoot: stateRoot[:],
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetBlockHeader retrieves block header for given block id.
|
||||
func (bs *Server) GetBlockHeader(ctx context.Context, req *ethpbv1.BlockRequest) (*ethpbv1.BlockHeaderResponse, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon.GetBlockHeader")
|
||||
@@ -87,7 +48,7 @@ func (bs *Server) GetBlockHeader(ctx context.Context, req *ethpbv1.BlockRequest)
|
||||
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
|
||||
err = handleGetBlockError(blk, err)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBlockHeader")
|
||||
return nil, err
|
||||
}
|
||||
v1alpha1Header, err := blk.Header()
|
||||
if err != nil {
|
||||
@@ -306,7 +267,7 @@ func (bs *Server) GetBlock(ctx context.Context, req *ethpbv1.BlockRequest) (*eth
|
||||
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
|
||||
err = handleGetBlockError(blk, err)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBlock")
|
||||
return nil, err
|
||||
}
|
||||
signedBeaconBlock, err := migration.SignedBeaconBlock(blk)
|
||||
if err != nil {
|
||||
@@ -329,7 +290,7 @@ func (bs *Server) GetBlockSSZ(ctx context.Context, req *ethpbv1.BlockRequest) (*
|
||||
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
|
||||
err = handleGetBlockError(blk, err)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBlockSSZ")
|
||||
return nil, err
|
||||
}
|
||||
signedBeaconBlock, err := migration.SignedBeaconBlock(blk)
|
||||
if err != nil {
|
||||
@@ -351,7 +312,7 @@ func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) (
|
||||
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
|
||||
err = handleGetBlockError(blk, err)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBlockV2")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = blk.PbPhase0Block()
|
||||
@@ -428,7 +389,7 @@ func (bs *Server) GetBlockSSZV2(ctx context.Context, req *ethpbv2.BlockRequestV2
|
||||
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
|
||||
err = handleGetBlockError(blk, err)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBlockSSZV2")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = blk.PbPhase0Block()
|
||||
|
||||
@@ -117,7 +117,12 @@ func (bs *Server) GetFinalityCheckpoints(ctx context.Context, req *ethpb.StateRe
|
||||
|
||||
st, err = bs.StateFetcher.State(ctx, req.StateId)
|
||||
if err != nil {
|
||||
return nil, helpers.PrepareStateFetchGRPCError(err)
|
||||
if stateNotFoundErr, ok := err.(*statefetcher.StateNotFoundError); ok {
|
||||
return nil, status.Errorf(codes.NotFound, "State not found: %v", stateNotFoundErr)
|
||||
} else if parseErr, ok := err.(*statefetcher.StateIdParseError); ok {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Invalid state ID: %v", parseErr)
|
||||
}
|
||||
return nil, status.Errorf(codes.Internal, "Could not get state: %v", err)
|
||||
}
|
||||
|
||||
return ðpb.StateFinalityCheckpointResponse{
|
||||
|
||||
@@ -61,6 +61,20 @@ func TestGetBeaconStateV2(t *testing.T) {
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, ethpbv2.Version_ALTAIR, resp.Version)
|
||||
})
|
||||
t.Run("Bellatrix", func(t *testing.T) {
|
||||
fakeState, _ := util.DeterministicGenesisStateBellatrix(t, 1)
|
||||
server := &Server{
|
||||
StateFetcher: &testutil.MockFetcher{
|
||||
BeaconState: fakeState,
|
||||
},
|
||||
}
|
||||
resp, err := server.GetBeaconStateV2(context.Background(), ðpbv2.StateRequestV2{
|
||||
StateId: make([]byte, 0),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, ethpbv2.Version_BELLATRIX, resp.Version)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetBeaconStateSSZ(t *testing.T) {
|
||||
@@ -119,6 +133,24 @@ func TestGetBeaconStateSSZV2(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
|
||||
assert.DeepEqual(t, sszState, resp.Data)
|
||||
})
|
||||
t.Run("Bellatrix", func(t *testing.T) {
|
||||
fakeState, _ := util.DeterministicGenesisStateBellatrix(t, 1)
|
||||
sszState, err := fakeState.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
|
||||
server := &Server{
|
||||
StateFetcher: &testutil.MockFetcher{
|
||||
BeaconState: fakeState,
|
||||
},
|
||||
}
|
||||
resp, err := server.GetBeaconStateSSZV2(context.Background(), ðpbv2.StateRequestV2{
|
||||
StateId: make([]byte, 0),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
|
||||
assert.DeepEqual(t, sszState, resp.Data)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ go_library(
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/rpc/statefetcher:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//beacon-chain/sync:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//proto/eth/v1:go_default_library",
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/rpc/statefetcher"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
@@ -12,13 +9,9 @@ import (
|
||||
// PrepareStateFetchGRPCError returns an appropriate gRPC error based on the supplied argument.
|
||||
// The argument error should be a result of fetching state.
|
||||
func PrepareStateFetchGRPCError(err error) error {
|
||||
if errors.Is(err, stategen.ErrSlotBeforeOrigin) {
|
||||
return status.Errorf(codes.NotFound, "lacking historical data needed to fulfill request")
|
||||
}
|
||||
if stateNotFoundErr, ok := err.(*statefetcher.StateNotFoundError); ok {
|
||||
return status.Errorf(codes.NotFound, "State not found: %v", stateNotFoundErr)
|
||||
}
|
||||
if parseErr, ok := err.(*statefetcher.StateIdParseError); ok {
|
||||
} else if parseErr, ok := err.(*statefetcher.StateIdParseError); ok {
|
||||
return status.Errorf(codes.InvalidArgument, "Invalid state ID: %v", parseErr)
|
||||
}
|
||||
return status.Errorf(codes.Internal, "Invalid state ID: %v", err)
|
||||
|
||||
@@ -30,6 +30,8 @@ go_library(
|
||||
"//proto/migration:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_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_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
@@ -61,6 +63,7 @@ go_test(
|
||||
"//beacon-chain/operations/voluntaryexits:go_default_library",
|
||||
"//beacon-chain/p2p/testing:go_default_library",
|
||||
"//beacon-chain/p2p/types:go_default_library",
|
||||
"//beacon-chain/powchain/engine-api-client/v1/mocks:go_default_library",
|
||||
"//beacon-chain/powchain/testing:go_default_library",
|
||||
"//beacon-chain/rpc/prysm/v1alpha1/validator:go_default_library",
|
||||
"//beacon-chain/rpc/testutil:go_default_library",
|
||||
@@ -71,6 +74,7 @@ go_test(
|
||||
"//config/params:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//encoding/bytesutil: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",
|
||||
@@ -80,6 +84,7 @@ go_test(
|
||||
"//testing/require:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
|
||||
@@ -3,10 +3,13 @@ package validator
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
@@ -252,6 +255,11 @@ func (vs *Server) ProduceBlock(ctx context.Context, req *ethpbv1.ProduceBlockReq
|
||||
ctx, span := trace.StartSpan(ctx, "validator.ProduceBlock")
|
||||
defer span.End()
|
||||
|
||||
if err := rpchelpers.ValidateSync(ctx, vs.SyncChecker, vs.HeadFetcher, vs.TimeFetcher); err != nil {
|
||||
// We simply return the error because it's already a gRPC error.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := vs.v1BeaconBlock(ctx, req)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not get block: %v", err)
|
||||
@@ -263,18 +271,9 @@ func (vs *Server) ProduceBlockV2(ctx context.Context, req *ethpbv1.ProduceBlockR
|
||||
_, span := trace.StartSpan(ctx, "validator.ProduceBlockV2")
|
||||
defer span.End()
|
||||
|
||||
epoch := slots.ToEpoch(req.Slot)
|
||||
if epoch < params.BeaconConfig().AltairForkEpoch {
|
||||
block, err := vs.v1BeaconBlock(ctx, req)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
|
||||
}
|
||||
return ðpbv2.ProduceBlockResponseV2{
|
||||
Version: ethpbv2.Version_PHASE0,
|
||||
Data: ðpbv2.BeaconBlockContainerV2{
|
||||
Block: ðpbv2.BeaconBlockContainerV2_Phase0Block{Phase0Block: block},
|
||||
},
|
||||
}, nil
|
||||
if err := rpchelpers.ValidateSync(ctx, vs.SyncChecker, vs.HeadFetcher, vs.TimeFetcher); err != nil {
|
||||
// We simply return the error because it's already a gRPC error.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v1alpha1req := ðpbalpha.BlockRequest{
|
||||
@@ -287,27 +286,71 @@ func (vs *Server) ProduceBlockV2(ctx context.Context, req *ethpbv1.ProduceBlockR
|
||||
// We simply return err because it's already of a gRPC error type.
|
||||
return nil, err
|
||||
}
|
||||
phase0Block, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_Phase0)
|
||||
if ok {
|
||||
block, err := migration.V1Alpha1ToV1Block(phase0Block.Phase0)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
|
||||
}
|
||||
return ðpbv2.ProduceBlockResponseV2{
|
||||
Version: ethpbv2.Version_PHASE0,
|
||||
Data: ðpbv2.BeaconBlockContainerV2{
|
||||
Block: ðpbv2.BeaconBlockContainerV2_Phase0Block{Phase0Block: block},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
altairBlock, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_Altair)
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.Internal, "Could not get Altair block: %v", err)
|
||||
if ok {
|
||||
block, err := migration.V1Alpha1BeaconBlockAltairToV2(altairBlock.Altair)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
|
||||
}
|
||||
return ðpbv2.ProduceBlockResponseV2{
|
||||
Version: ethpbv2.Version_ALTAIR,
|
||||
Data: ðpbv2.BeaconBlockContainerV2{
|
||||
Block: ðpbv2.BeaconBlockContainerV2_AltairBlock{AltairBlock: block},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
block, err := migration.V1Alpha1BeaconBlockAltairToV2(altairBlock.Altair)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
|
||||
bellatrixBlock, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_Bellatrix)
|
||||
if ok {
|
||||
block, err := migration.V1Alpha1BeaconBlockBellatrixToV2(bellatrixBlock.Bellatrix)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
|
||||
}
|
||||
return ðpbv2.ProduceBlockResponseV2{
|
||||
Version: ethpbv2.Version_BELLATRIX,
|
||||
Data: ðpbv2.BeaconBlockContainerV2{
|
||||
Block: ðpbv2.BeaconBlockContainerV2_BellatrixBlock{BellatrixBlock: block},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return ðpbv2.ProduceBlockResponseV2{
|
||||
Version: ethpbv2.Version_ALTAIR,
|
||||
Data: ðpbv2.BeaconBlockContainerV2{
|
||||
Block: ðpbv2.BeaconBlockContainerV2_AltairBlock{AltairBlock: block},
|
||||
},
|
||||
}, nil
|
||||
return nil, status.Error(codes.InvalidArgument, "Unsupported block type")
|
||||
}
|
||||
|
||||
// PrepareBeaconProposer --
|
||||
// PrepareBeaconProposer caches and updates the fee recipient for the given proposer.
|
||||
func (vs *Server) PrepareBeaconProposer(
|
||||
_ context.Context, _ *ethpbv1.PrepareBeaconProposerRequest,
|
||||
ctx context.Context, request *ethpbv1.PrepareBeaconProposerRequest,
|
||||
) (*emptypb.Empty, error) {
|
||||
return &emptypb.Empty{}, status.Error(codes.Unimplemented, "Unimplemented")
|
||||
_, span := trace.StartSpan(ctx, "validator.PrepareBeaconProposer")
|
||||
defer span.End()
|
||||
var feeRecipients []common.Address
|
||||
var validatorIndices []types.ValidatorIndex
|
||||
for _, recipientContainer := range request.Recipients {
|
||||
recipient := hexutil.Encode(recipientContainer.FeeRecipient)
|
||||
if !common.IsHexAddress(recipient) {
|
||||
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("Invalid fee recipient address: %v", recipient))
|
||||
}
|
||||
feeRecipients = append(feeRecipients, common.BytesToAddress(recipientContainer.FeeRecipient))
|
||||
validatorIndices = append(validatorIndices, recipientContainer.ValidatorIndex)
|
||||
}
|
||||
if err := vs.V1Alpha1Server.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, validatorIndices, feeRecipients); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not save fee recipients: %v", err)
|
||||
}
|
||||
log.WithFields(log.Fields{
|
||||
"validatorIndices": validatorIndices,
|
||||
}).Info("Updated fee recipient addresses for validator indices")
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
// ProduceAttestationData requests that the beacon node produces attestation data for
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
@@ -24,6 +25,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
|
||||
p2pmock "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
|
||||
p2pType "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/powchain/engine-api-client/v1/mocks"
|
||||
mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
|
||||
v1alpha1validator "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/validator"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/rpc/testutil"
|
||||
@@ -34,6 +36,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
|
||||
ethpbv1 "github.com/prysmaticlabs/prysm/proto/eth/v1"
|
||||
ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2"
|
||||
"github.com/prysmaticlabs/prysm/proto/migration"
|
||||
@@ -610,6 +613,7 @@ func TestProduceBlock(t *testing.T) {
|
||||
}
|
||||
|
||||
v1Server := &Server{
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
V1Alpha1Server: v1Alpha1Server,
|
||||
}
|
||||
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
|
||||
@@ -641,6 +645,17 @@ func TestProduceBlock(t *testing.T) {
|
||||
assert.DeepEqual(t, expectedAttSlashings, resp.Data.Body.AttesterSlashings)
|
||||
}
|
||||
|
||||
func TestProduceBlock_SyncNotReady(t *testing.T) {
|
||||
chainService := &mockChain.ChainService{}
|
||||
vs := &Server{
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: true},
|
||||
HeadFetcher: chainService,
|
||||
TimeFetcher: chainService,
|
||||
}
|
||||
_, err := vs.ProduceBlock(context.Background(), ðpbv1.ProduceBlockRequest{})
|
||||
assert.ErrorContains(t, "Syncing to latest head, not ready to respond", err)
|
||||
}
|
||||
|
||||
func TestProduceBlockV2(t *testing.T) {
|
||||
t.Run("Phase 0", func(t *testing.T) {
|
||||
db := dbutil.SetupDB(t)
|
||||
@@ -701,6 +716,7 @@ func TestProduceBlockV2(t *testing.T) {
|
||||
|
||||
v1Server := &Server{
|
||||
V1Alpha1Server: v1Alpha1Server,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
}
|
||||
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
|
||||
require.NoError(t, err)
|
||||
@@ -834,6 +850,7 @@ func TestProduceBlockV2(t *testing.T) {
|
||||
|
||||
v1Server := &Server{
|
||||
V1Alpha1Server: v1Alpha1Server,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
}
|
||||
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
|
||||
require.NoError(t, err)
|
||||
@@ -874,6 +891,166 @@ func TestProduceBlockV2(t *testing.T) {
|
||||
assert.DeepEqual(t, expectedBits, blk.Body.SyncAggregate.SyncCommitteeBits)
|
||||
assert.DeepEqual(t, aggregatedSig, blk.Body.SyncAggregate.SyncCommitteeSignature)
|
||||
})
|
||||
|
||||
t.Run("Bellatrix", func(t *testing.T) {
|
||||
db := dbutil.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
params.SetupTestConfigCleanup(t)
|
||||
bc := params.BeaconConfig()
|
||||
bc.AltairForkEpoch = types.Epoch(0)
|
||||
bc.BellatrixForkEpoch = types.Epoch(1)
|
||||
params.OverrideBeaconConfig(bc)
|
||||
|
||||
beaconState, privKeys := util.DeterministicGenesisStateBellatrix(t, params.BeaconConfig().SyncCommitteeSize)
|
||||
require.NoError(t, beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch))
|
||||
syncCommittee, err := altair.NextSyncCommittee(context.Background(), beaconState)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconState.SetCurrentSyncCommittee(syncCommittee))
|
||||
require.NoError(t, beaconState.SetNextSyncCommittee(syncCommittee))
|
||||
|
||||
stateRoot, err := beaconState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err, "Could not hash genesis state")
|
||||
genesisBlock := util.NewBeaconBlockBellatrix()
|
||||
genesisBlock.Block.StateRoot = stateRoot[:]
|
||||
wrappedBellatrixBlock, err := wrapper.WrappedBellatrixSignedBeaconBlock(genesisBlock)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, wrappedBellatrixBlock))
|
||||
parentRoot, err := genesisBlock.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, db.SaveState(ctx, beaconState, parentRoot), "Could not save genesis state")
|
||||
require.NoError(t, db.SaveHeadBlockRoot(ctx, parentRoot), "Could not save genesis state")
|
||||
|
||||
v1Alpha1Server := &v1alpha1validator.Server{
|
||||
ExecutionEngineCaller: &mocks.EngineClient{
|
||||
ExecutionBlock: &enginev1.ExecutionBlock{
|
||||
TotalDifficulty: "0x1",
|
||||
},
|
||||
},
|
||||
TimeFetcher: &mockChain.ChainService{},
|
||||
HeadFetcher: &mockChain.ChainService{State: beaconState, Root: parentRoot[:]},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
BlockReceiver: &mockChain.ChainService{},
|
||||
ChainStartFetcher: &mockPOW.POWChain{},
|
||||
Eth1InfoFetcher: &mockPOW.POWChain{},
|
||||
Eth1BlockFetcher: &mockPOW.POWChain{},
|
||||
MockEth1Votes: true,
|
||||
AttPool: attestations.NewPool(),
|
||||
SlashingsPool: slashings.NewPool(),
|
||||
ExitPool: voluntaryexits.NewPool(),
|
||||
StateGen: stategen.New(db),
|
||||
SyncCommitteePool: synccommittee.NewStore(),
|
||||
}
|
||||
|
||||
proposerSlashings := make([]*ethpbalpha.ProposerSlashing, params.BeaconConfig().MaxProposerSlashings)
|
||||
for i := types.ValidatorIndex(0); uint64(i) < params.BeaconConfig().MaxProposerSlashings; i++ {
|
||||
proposerSlashing, err := util.GenerateProposerSlashingForValidator(
|
||||
beaconState,
|
||||
privKeys[i],
|
||||
i,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
proposerSlashings[i] = proposerSlashing
|
||||
err = v1Alpha1Server.SlashingsPool.InsertProposerSlashing(context.Background(), beaconState, proposerSlashing)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
attSlashings := make([]*ethpbalpha.AttesterSlashing, params.BeaconConfig().MaxAttesterSlashings)
|
||||
for i := uint64(0); i < params.BeaconConfig().MaxAttesterSlashings; i++ {
|
||||
attesterSlashing, err := util.GenerateAttesterSlashingForValidator(
|
||||
beaconState,
|
||||
privKeys[i+params.BeaconConfig().MaxProposerSlashings],
|
||||
types.ValidatorIndex(i+params.BeaconConfig().MaxProposerSlashings), /* validator index */
|
||||
)
|
||||
require.NoError(t, err)
|
||||
attSlashings[i] = attesterSlashing
|
||||
err = v1Alpha1Server.SlashingsPool.InsertAttesterSlashing(context.Background(), beaconState, attesterSlashing)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
aggregationBits := bitfield.NewBitvector128()
|
||||
for i := range aggregationBits {
|
||||
aggregationBits[i] = 0xAA
|
||||
}
|
||||
|
||||
syncCommitteeIndices, err := altair.NextSyncCommitteeIndices(context.Background(), beaconState)
|
||||
require.NoError(t, err)
|
||||
sigs := make([]bls.Signature, 0, len(syncCommitteeIndices))
|
||||
for i, indice := range syncCommitteeIndices {
|
||||
if aggregationBits.BitAt(uint64(i)) {
|
||||
b := p2pType.SSZBytes(parentRoot[:])
|
||||
sb, err := signing.ComputeDomainAndSign(beaconState, coreTime.CurrentEpoch(beaconState), &b, params.BeaconConfig().DomainSyncCommittee, privKeys[indice])
|
||||
require.NoError(t, err)
|
||||
sig, err := bls.SignatureFromBytes(sb)
|
||||
require.NoError(t, err)
|
||||
sigs = append(sigs, sig)
|
||||
}
|
||||
}
|
||||
aggregatedSig := bls.AggregateSignatures(sigs).Marshal()
|
||||
contribution := ðpbalpha.SyncCommitteeContribution{
|
||||
Slot: params.BeaconConfig().SlotsPerEpoch,
|
||||
BlockRoot: parentRoot[:],
|
||||
SubcommitteeIndex: 0,
|
||||
AggregationBits: aggregationBits,
|
||||
Signature: aggregatedSig,
|
||||
}
|
||||
require.NoError(t, v1Alpha1Server.SyncCommitteePool.SaveSyncCommitteeContribution(contribution))
|
||||
|
||||
v1Server := &Server{
|
||||
V1Alpha1Server: v1Alpha1Server,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
}
|
||||
randaoReveal, err := util.RandaoReveal(beaconState, 1, privKeys)
|
||||
require.NoError(t, err)
|
||||
graffiti := bytesutil.ToBytes32([]byte("eth2"))
|
||||
|
||||
req := ðpbv1.ProduceBlockRequest{
|
||||
Slot: params.BeaconConfig().SlotsPerEpoch + 1,
|
||||
RandaoReveal: randaoReveal,
|
||||
Graffiti: graffiti[:],
|
||||
}
|
||||
resp, err := v1Server.ProduceBlockV2(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ethpbv2.Version_BELLATRIX, resp.Version)
|
||||
|
||||
containerBlock, ok := resp.Data.Block.(*ethpbv2.BeaconBlockContainerV2_BellatrixBlock)
|
||||
require.Equal(t, true, ok)
|
||||
blk := containerBlock.BellatrixBlock
|
||||
assert.Equal(t, req.Slot, blk.Slot, "Expected block to have slot of 1")
|
||||
assert.DeepEqual(t, parentRoot[:], blk.ParentRoot, "Expected block to have correct parent root")
|
||||
assert.DeepEqual(t, randaoReveal, blk.Body.RandaoReveal, "Expected block to have correct randao reveal")
|
||||
assert.DeepEqual(t, req.Graffiti, blk.Body.Graffiti, "Expected block to have correct graffiti")
|
||||
assert.Equal(t, params.BeaconConfig().MaxProposerSlashings, uint64(len(blk.Body.ProposerSlashings)))
|
||||
expectedPropSlashings := make([]*ethpbv1.ProposerSlashing, len(proposerSlashings))
|
||||
for i, slash := range proposerSlashings {
|
||||
expectedPropSlashings[i] = migration.V1Alpha1ProposerSlashingToV1(slash)
|
||||
}
|
||||
assert.DeepEqual(t, expectedPropSlashings, blk.Body.ProposerSlashings)
|
||||
assert.Equal(t, params.BeaconConfig().MaxAttesterSlashings, uint64(len(blk.Body.AttesterSlashings)))
|
||||
expectedAttSlashings := make([]*ethpbv1.AttesterSlashing, len(attSlashings))
|
||||
for i, slash := range attSlashings {
|
||||
expectedAttSlashings[i] = migration.V1Alpha1AttSlashingToV1(slash)
|
||||
}
|
||||
assert.DeepEqual(t, expectedAttSlashings, blk.Body.AttesterSlashings)
|
||||
expectedBits := bitfield.NewBitvector512()
|
||||
for i := 0; i <= 15; i++ {
|
||||
expectedBits[i] = 0xAA
|
||||
}
|
||||
assert.DeepEqual(t, expectedBits, blk.Body.SyncAggregate.SyncCommitteeBits)
|
||||
assert.DeepEqual(t, aggregatedSig, blk.Body.SyncAggregate.SyncCommitteeSignature)
|
||||
})
|
||||
}
|
||||
|
||||
func TestProduceBlockV2_SyncNotReady(t *testing.T) {
|
||||
chainService := &mockChain.ChainService{}
|
||||
vs := &Server{
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: true},
|
||||
HeadFetcher: chainService,
|
||||
TimeFetcher: chainService,
|
||||
}
|
||||
_, err := vs.ProduceBlockV2(context.Background(), ðpbv1.ProduceBlockRequest{})
|
||||
assert.ErrorContains(t, "Syncing to latest head, not ready to respond", err)
|
||||
}
|
||||
|
||||
func TestProduceAttestationData(t *testing.T) {
|
||||
@@ -1835,3 +2012,65 @@ func TestSubmitContributionAndProofs(t *testing.T) {
|
||||
require.DeepEqual(t, expectedContributions, savedMsgs)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPrepareBeaconProposer(t *testing.T) {
|
||||
type args struct {
|
||||
request *ethpbv1.PrepareBeaconProposerRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "Happy Path",
|
||||
args: args{
|
||||
request: ðpbv1.PrepareBeaconProposerRequest{
|
||||
Recipients: []*ethpbv1.PrepareBeaconProposerRequest_FeeRecipientContainer{
|
||||
{
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "invalid fee recipient length",
|
||||
args: args{
|
||||
request: ðpbv1.PrepareBeaconProposerRequest{
|
||||
Recipients: []*ethpbv1.PrepareBeaconProposerRequest_FeeRecipientContainer{
|
||||
{
|
||||
FeeRecipient: make([]byte, fieldparams.BLSPubkeyLength),
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: "Invalid fee recipient address",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db := dbutil.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
v1Server := &v1alpha1validator.Server{
|
||||
BeaconDB: db,
|
||||
}
|
||||
server := &Server{
|
||||
V1Alpha1Server: v1Server,
|
||||
}
|
||||
_, err := server.PrepareBeaconProposer(ctx, tt.args.request)
|
||||
if tt.wantErr != "" {
|
||||
require.ErrorContains(t, tt.wantErr, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
address, err := server.V1Alpha1Server.BeaconDB.FeeRecipientByValidatorID(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, common.BytesToAddress(tt.args.request.Recipients[0].FeeRecipient), address)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package beacon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -396,6 +397,11 @@ func (bs *Server) chainHeadRetrieval(ctx context.Context) (*ethpb.ChainHead, err
|
||||
isGenesis := func(cp *ethpb.Checkpoint) bool {
|
||||
return bytesutil.ToBytes32(cp.Root) == params.BeaconConfig().ZeroHash && cp.Epoch == 0
|
||||
}
|
||||
// Retrieve genesis block in the event we have genesis checkpoints.
|
||||
genBlock, err := bs.BeaconDB.GenesisBlock(ctx)
|
||||
if err != nil || genBlock == nil || genBlock.IsNil() || genBlock.Block().IsNil() {
|
||||
return nil, status.Error(codes.Internal, "Could not get genesis block")
|
||||
}
|
||||
|
||||
finalizedCheckpoint := bs.FinalizationFetcher.FinalizedCheckpt()
|
||||
if !isGenesis(finalizedCheckpoint) {
|
||||
@@ -457,3 +463,40 @@ func (bs *Server) chainHeadRetrieval(ctx context.Context) (*ethpb.ChainHead, err
|
||||
PreviousJustifiedBlockRoot: prevJustifiedCheckpoint.Root,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetWeakSubjectivityCheckpoint retrieves weak subjectivity state root, block root, and epoch.
|
||||
func (bs *Server) GetWeakSubjectivityCheckpoint(ctx context.Context, _ *emptypb.Empty) (*ethpb.WeakSubjectivityCheckpoint, error) {
|
||||
hs, err := bs.HeadFetcher.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get head state")
|
||||
}
|
||||
wsEpoch, err := helpers.LatestWeakSubjectivityEpoch(ctx, hs)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get weak subjectivity epoch")
|
||||
}
|
||||
wsSlot, err := slots.EpochStart(wsEpoch)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get weak subjectivity slot")
|
||||
}
|
||||
|
||||
wsState, err := bs.ReplayerBuilder.ForSlot(wsSlot).ReplayBlocks(ctx)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("error replaying blocks for state at slot %d: %v", wsSlot, err)
|
||||
return nil, status.Error(codes.Internal, msg)
|
||||
}
|
||||
|
||||
stateRoot, err := wsState.HashTreeRoot(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get weak subjectivity state root")
|
||||
}
|
||||
blkRoot, err := wsState.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get weak subjectivity block root")
|
||||
}
|
||||
|
||||
return ðpb.WeakSubjectivityCheckpoint{
|
||||
BlockRoot: blkRoot[:],
|
||||
StateRoot: stateRoot[:],
|
||||
Epoch: wsEpoch,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ go_library(
|
||||
"//runtime/version:go_default_library",
|
||||
"//time:go_default_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_ferranbt_fastssz//:go_default_library",
|
||||
"@com_github_holiman_uint256//:go_default_library",
|
||||
|
||||
@@ -6,8 +6,11 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
emptypb "github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
|
||||
blockfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/block"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
|
||||
@@ -116,11 +119,29 @@ func (vs *Server) ProposeBlock(ctx context.Context, rBlk *ethpb.SignedBeaconBloc
|
||||
return vs.proposeGenericBeaconBlock(ctx, blk)
|
||||
}
|
||||
|
||||
// PrepareBeaconProposer --
|
||||
// PrepareBeaconProposer caches and updates the fee recipient for the given proposer.
|
||||
func (vs *Server) PrepareBeaconProposer(
|
||||
_ context.Context, _ *ethpb.PrepareBeaconProposerRequest,
|
||||
ctx context.Context, request *ethpb.PrepareBeaconProposerRequest,
|
||||
) (*emptypb.Empty, error) {
|
||||
return &emptypb.Empty{}, status.Error(codes.Unimplemented, "Unimplemented")
|
||||
_, span := trace.StartSpan(ctx, "validator.PrepareBeaconProposer")
|
||||
defer span.End()
|
||||
var feeRecipients []common.Address
|
||||
var validatorIndices []types.ValidatorIndex
|
||||
for _, recipientContainer := range request.Recipients {
|
||||
recipient := hexutil.Encode(recipientContainer.FeeRecipient)
|
||||
if !common.IsHexAddress(recipient) {
|
||||
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("Invalid fee recipient address: %v", recipient))
|
||||
}
|
||||
feeRecipients = append(feeRecipients, common.BytesToAddress(recipientContainer.FeeRecipient))
|
||||
validatorIndices = append(validatorIndices, recipientContainer.ValidatorIndex)
|
||||
}
|
||||
if err := vs.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, validatorIndices, feeRecipients); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not save fee recipients: %v", err)
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"validatorIndices": validatorIndices,
|
||||
}).Info("Updated fee recipient addresses for validator indices")
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk block.SignedBeaconBlock) (*ethpb.ProposeResponse, error) {
|
||||
|
||||
@@ -115,6 +115,9 @@ func (vs *Server) canonicalEth1Data(
|
||||
canonicalEth1Data = beaconState.Eth1Data()
|
||||
eth1BlockHash = bytesutil.ToBytes32(beaconState.Eth1Data().BlockHash)
|
||||
}
|
||||
if eth1BlockHash == [32]byte{} {
|
||||
return canonicalEth1Data, big.NewInt(0), nil
|
||||
}
|
||||
_, canonicalEth1DataHeight, err := vs.Eth1BlockFetcher.BlockExists(ctx, eth1BlockHash)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not fetch eth1data height")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
@@ -98,14 +99,19 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx
|
||||
|
||||
feeRecipient := params.BeaconConfig().DefaultFeeRecipient
|
||||
recipient, err := vs.BeaconDB.FeeRecipientByValidatorID(ctx, vIdx)
|
||||
burnAddr := bytesutil.PadTo([]byte{}, fieldparams.FeeRecipientLength)
|
||||
switch err == nil {
|
||||
case true:
|
||||
feeRecipient = recipient
|
||||
case errors.As(err, kv.ErrNotFoundFeeRecipient): // If fee recipient is not found, use the default fee recipient.
|
||||
logrus.WithError(err).WithFields(logrus.Fields{
|
||||
"validatorIndex": vIdx,
|
||||
"defaultFeeRecipient": feeRecipient,
|
||||
}).Error("Fee recipient not found. Using default fee recipient.")
|
||||
case errors.As(err, kv.ErrNotFoundFeeRecipient):
|
||||
// If fee recipient is not found in DB and not set from beacon node CLI,
|
||||
// use the burn address.
|
||||
if bytes.Equal(feeRecipient.Bytes(), burnAddr) {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"validatorIndex": vIdx,
|
||||
"burnAddress": burnAddr,
|
||||
}).Error("Fee recipient not set. Using burn address")
|
||||
}
|
||||
default:
|
||||
return nil, errors.Wrap(err, "could not get fee recipient in db")
|
||||
}
|
||||
|
||||
@@ -2349,10 +2349,6 @@ func TestProposer_GetBeaconBlock_BellatrixEpoch(t *testing.T) {
|
||||
Graffiti: graffiti[:],
|
||||
}
|
||||
|
||||
proposerIndex := types.ValidatorIndex(40)
|
||||
addr := common.Address{'a'}
|
||||
require.NoError(t, proposerServer.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, []types.ValidatorIndex{proposerIndex}, []common.Address{addr}))
|
||||
|
||||
block, err := proposerServer.GetBeaconBlock(ctx, req)
|
||||
require.NoError(t, err)
|
||||
bellatrixBlk, ok := block.GetBlock().(*ethpb.GenericBeaconBlock_Bellatrix)
|
||||
@@ -2363,8 +2359,18 @@ func TestProposer_GetBeaconBlock_BellatrixEpoch(t *testing.T) {
|
||||
assert.DeepEqual(t, randaoReveal, bellatrixBlk.Bellatrix.Body.RandaoReveal, "Expected block to have correct randao reveal")
|
||||
assert.DeepEqual(t, req.Graffiti, bellatrixBlk.Bellatrix.Body.Graffiti, "Expected block to have correct Graffiti")
|
||||
|
||||
require.LogsDoNotContain(t, hook, "Fee recipient not found. Using default fee recipient.")
|
||||
require.LogsContain(t, hook, "Fee recipient not set. Using burn address")
|
||||
require.DeepEqual(t, payload, bellatrixBlk.Bellatrix.Body.ExecutionPayload) // Payload should equal.
|
||||
|
||||
// Operator sets default fee recipient to not be burned through beacon node cli.
|
||||
newHook := logTest.NewGlobal()
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg = params.MainnetConfig().Copy()
|
||||
cfg.DefaultFeeRecipient = common.Address{'b'}
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
_, err = proposerServer.GetBeaconBlock(ctx, req)
|
||||
require.NoError(t, err)
|
||||
require.LogsDoNotContain(t, newHook, "Fee recipient not set. Using burn address")
|
||||
}
|
||||
|
||||
func TestProposer_GetBeaconBlock_Optimistic(t *testing.T) {
|
||||
@@ -2431,6 +2437,63 @@ func TestProposer_GetSyncAggregate_OK(t *testing.T) {
|
||||
require.DeepEqual(t, bitfield.NewBitvector512(), aggregate.SyncCommitteeBits)
|
||||
}
|
||||
|
||||
func TestProposer_PrepareBeaconProposer(t *testing.T) {
|
||||
type args struct {
|
||||
request *ethpb.PrepareBeaconProposerRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "Happy Path",
|
||||
args: args{
|
||||
request: ðpb.PrepareBeaconProposerRequest{
|
||||
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
|
||||
{
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "invalid fee recipient length",
|
||||
args: args{
|
||||
request: ðpb.PrepareBeaconProposerRequest{
|
||||
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
|
||||
{
|
||||
FeeRecipient: make([]byte, fieldparams.BLSPubkeyLength),
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: "Invalid fee recipient address",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db := dbutil.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
proposerServer := &Server{BeaconDB: db}
|
||||
_, err := proposerServer.PrepareBeaconProposer(ctx, tt.args.request)
|
||||
if tt.wantErr != "" {
|
||||
require.ErrorContains(t, tt.wantErr, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
address, err := proposerServer.BeaconDB.FeeRecipientByValidatorID(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, common.BytesToAddress(tt.args.request.Recipients[0].FeeRecipient), address)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func majorityVoteBoundaryTime(slot types.Slot) (uint64, uint64) {
|
||||
slots := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().EpochsPerEth1VotingPeriod))
|
||||
slotStartTime := uint64(mockPOW.GenesisTime) + uint64((slot - (slot % (slots))).Mul(params.BeaconConfig().SecondsPerSlot))
|
||||
|
||||
@@ -110,7 +110,7 @@ func (p *StateProvider) State(ctx context.Context, stateId []byte) (state.Beacon
|
||||
return nil, errors.Wrap(err, "could not get head state")
|
||||
}
|
||||
case "genesis":
|
||||
s, err = p.StateBySlot(ctx, 0)
|
||||
s, err = p.BeaconDB.GenesisState(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get genesis state")
|
||||
}
|
||||
|
||||
@@ -73,11 +73,8 @@ func TestGetState(t *testing.T) {
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, r))
|
||||
require.NoError(t, db.SaveState(ctx, bs, r))
|
||||
|
||||
cc := &mockstategen.MockCanonicalChecker{Is: true}
|
||||
cs := &mockstategen.MockCurrentSlotter{Slot: bs.Slot() + 1}
|
||||
p := StateProvider{
|
||||
BeaconDB: db,
|
||||
ReplayerBuilder: stategen.NewCanonicalBuilder(db, cc, cs),
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
s, err := p.State(ctx, []byte("genesis"))
|
||||
|
||||
@@ -10,7 +10,21 @@ go_library(
|
||||
"prometheus.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state",
|
||||
visibility = ["//visibility:public"],
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__subpackages__",
|
||||
"//proto/migration:__pkg__",
|
||||
"//proto/prysm/v1alpha1:__subpackages__",
|
||||
"//proto/testing:__subpackages__",
|
||||
"//slasher/rpc:__subpackages__",
|
||||
"//testing/benchmark:__pkg__",
|
||||
"//testing/slasher/simulator:__pkg__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
"//tools/benchmark-files-gen:__pkg__",
|
||||
"//tools/exploredb:__pkg__",
|
||||
"//tools/pcli:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
|
||||
@@ -3,75 +3,43 @@ package v1
|
||||
import (
|
||||
"testing"
|
||||
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestBeaconState_LatestBlockHeader(t *testing.T) {
|
||||
s, err := InitializeFromProto(ðpb.BeaconState{})
|
||||
require.NoError(t, err)
|
||||
got := s.LatestBlockHeader()
|
||||
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
|
||||
|
||||
want := ðpb.BeaconBlockHeader{Slot: 100}
|
||||
s, err = InitializeFromProto(ðpb.BeaconState{LatestBlockHeader: want})
|
||||
require.NoError(t, err)
|
||||
got = s.LatestBlockHeader()
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
// Test copy does not mutate.
|
||||
got.Slot = 101
|
||||
require.DeepNotEqual(t, want, got)
|
||||
testtmpl.VerifyBeaconStateLatestBlockHeader(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{})
|
||||
},
|
||||
func(BH *ethpb.BeaconBlockHeader) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{LatestBlockHeader: BH})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestBeaconState_BlockRoots(t *testing.T) {
|
||||
s, err := InitializeFromProto(ðpb.BeaconState{})
|
||||
require.NoError(t, err)
|
||||
got := s.BlockRoots()
|
||||
want := make([][]byte, fieldparams.BlockRootsLength)
|
||||
for i := range want {
|
||||
want[i] = make([]byte, 32)
|
||||
}
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
want = make([][]byte, fieldparams.BlockRootsLength)
|
||||
for i := range want {
|
||||
if i == 0 {
|
||||
want[i] = bytesutil.PadTo([]byte{'a'}, 32)
|
||||
} else {
|
||||
want[i] = make([]byte, 32)
|
||||
}
|
||||
|
||||
}
|
||||
s, err = InitializeFromProto(ðpb.BeaconState{BlockRoots: want})
|
||||
require.NoError(t, err)
|
||||
got = s.BlockRoots()
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
// Test copy does not mutate.
|
||||
got[0][0] = 'b'
|
||||
require.DeepNotEqual(t, want, got)
|
||||
testtmpl.VerifyBeaconStateBlockRootsNative(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{})
|
||||
},
|
||||
func(BR [][]byte) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{BlockRoots: BR})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestBeaconState_BlockRootAtIndex(t *testing.T) {
|
||||
s, err := InitializeFromProto(ðpb.BeaconState{})
|
||||
require.NoError(t, err)
|
||||
got, err := s.BlockRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, bytesutil.PadTo([]byte{}, 32), got)
|
||||
|
||||
r := [fieldparams.BlockRootsLength][32]byte{{'a'}}
|
||||
bRoots := make([][]byte, len(r))
|
||||
for i, root := range r {
|
||||
tmp := root
|
||||
bRoots[i] = tmp[:]
|
||||
}
|
||||
s, err = InitializeFromProto(ðpb.BeaconState{BlockRoots: bRoots})
|
||||
require.NoError(t, err)
|
||||
got, err = s.BlockRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
want := bytesutil.PadTo([]byte{'a'}, 32)
|
||||
require.DeepSSZEqual(t, want, got)
|
||||
testtmpl.VerifyBeaconStateBlockRootAtIndexNative(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{})
|
||||
},
|
||||
func(BR [][]byte) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{BlockRoots: BR})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ import (
|
||||
)
|
||||
|
||||
func TestBeaconState_SlotDataRace(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_SlotDataRace(t, func() (state.BeaconState, error) {
|
||||
testtmpl.VerifyBeaconStateSlotDataRace(t, func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{Slot: 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_MatchCurrentJustifiedCheckptNative(
|
||||
testtmpl.VerifyBeaconStateMatchCurrentJustifiedCheckptNative(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{CurrentJustifiedCheckpoint: cp})
|
||||
@@ -24,7 +24,7 @@ func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_MatchPreviousJustifiedCheckptNative(
|
||||
testtmpl.VerifyBeaconStateMatchPreviousJustifiedCheckptNative(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{PreviousJustifiedCheckpoint: cp})
|
||||
@@ -33,7 +33,7 @@ func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeaconState_ValidatorByPubkey(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_ValidatorByPubkey(t, func() (state.BeaconState, error) {
|
||||
testtmpl.VerifyBeaconStateValidatorByPubkey(t, func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t, func() (state.BeaconState, error) {
|
||||
testtmpl.VerifyBeaconStateValidatorAtIndexReadOnlyHandlesNilSlice(t, func() (state.BeaconState, error) {
|
||||
return v1.InitializeFromProtoUnsafe(ðpb.BeaconState{
|
||||
Validators: nil,
|
||||
})
|
||||
|
||||
@@ -86,6 +86,7 @@ go_test(
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/stateutil:go_default_library",
|
||||
"//beacon-chain/state/testing:go_default_library",
|
||||
"//beacon-chain/state/types:go_default_library",
|
||||
"//beacon-chain/state/v2:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
|
||||
@@ -3,74 +3,43 @@ package v2
|
||||
import (
|
||||
"testing"
|
||||
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestBeaconState_LatestBlockHeader(t *testing.T) {
|
||||
s, err := InitializeFromProto(ðpb.BeaconStateAltair{})
|
||||
require.NoError(t, err)
|
||||
got := s.LatestBlockHeader()
|
||||
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
|
||||
|
||||
want := ðpb.BeaconBlockHeader{Slot: 100}
|
||||
s, err = InitializeFromProto(ðpb.BeaconStateAltair{LatestBlockHeader: want})
|
||||
require.NoError(t, err)
|
||||
got = s.LatestBlockHeader()
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
// Test copy does not mutate.
|
||||
got.Slot = 101
|
||||
require.DeepNotEqual(t, want, got)
|
||||
testtmpl.VerifyBeaconStateLatestBlockHeader(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateAltair{})
|
||||
},
|
||||
func(BH *ethpb.BeaconBlockHeader) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateAltair{LatestBlockHeader: BH})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestBeaconState_BlockRoots(t *testing.T) {
|
||||
s, err := InitializeFromProto(ðpb.BeaconStateAltair{})
|
||||
require.NoError(t, err)
|
||||
got := s.BlockRoots()
|
||||
want := make([][]byte, fieldparams.BlockRootsLength)
|
||||
for i := range want {
|
||||
want[i] = make([]byte, fieldparams.RootLength)
|
||||
}
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
want = make([][]byte, fieldparams.BlockRootsLength)
|
||||
for i := range want {
|
||||
if i == 0 {
|
||||
want[i] = bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
|
||||
} else {
|
||||
want[i] = make([]byte, fieldparams.RootLength)
|
||||
}
|
||||
}
|
||||
s, err = InitializeFromProto(ðpb.BeaconStateAltair{BlockRoots: want})
|
||||
require.NoError(t, err)
|
||||
got = s.BlockRoots()
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
// Test copy does not mutate.
|
||||
got[0][0] = 'b'
|
||||
require.DeepNotEqual(t, want, got)
|
||||
testtmpl.VerifyBeaconStateBlockRootsNative(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateAltair{})
|
||||
},
|
||||
func(BR [][]byte) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateAltair{BlockRoots: BR})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestBeaconState_BlockRootAtIndex(t *testing.T) {
|
||||
s, err := InitializeFromProto(ðpb.BeaconStateAltair{})
|
||||
require.NoError(t, err)
|
||||
got, err := s.BlockRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, bytesutil.PadTo([]byte{}, fieldparams.RootLength), got)
|
||||
|
||||
r := [fieldparams.BlockRootsLength][fieldparams.RootLength]byte{{'a'}}
|
||||
bRoots := make([][]byte, len(r))
|
||||
for i, root := range r {
|
||||
tmp := root
|
||||
bRoots[i] = tmp[:]
|
||||
}
|
||||
s, err = InitializeFromProto(ðpb.BeaconStateAltair{BlockRoots: bRoots})
|
||||
require.NoError(t, err)
|
||||
got, err = s.BlockRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
want := bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
|
||||
require.DeepSSZEqual(t, want, got)
|
||||
testtmpl.VerifyBeaconStateBlockRootAtIndexNative(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateAltair{})
|
||||
},
|
||||
func(BR [][]byte) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateAltair{BlockRoots: BR})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ import (
|
||||
)
|
||||
|
||||
func TestBeaconState_SlotDataRace(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_SlotDataRace(t, func() (state.BeaconState, error) {
|
||||
testtmpl.VerifyBeaconStateSlotDataRace(t, func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateAltair{Slot: 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_MatchCurrentJustifiedCheckptNative(
|
||||
testtmpl.VerifyBeaconStateMatchCurrentJustifiedCheckptNative(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateAltair{CurrentJustifiedCheckpoint: cp})
|
||||
@@ -24,7 +24,7 @@ func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_MatchPreviousJustifiedCheckptNative(
|
||||
testtmpl.VerifyBeaconStateMatchPreviousJustifiedCheckptNative(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateAltair{PreviousJustifiedCheckpoint: cp})
|
||||
@@ -33,7 +33,7 @@ func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeaconState_ValidatorByPubkey(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_ValidatorByPubkey(t, func() (state.BeaconState, error) {
|
||||
testtmpl.VerifyBeaconStateValidatorByPubkey(t, func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateAltair{})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t, func() (state.BeaconState, error) {
|
||||
testtmpl.VerifyBeaconStateValidatorAtIndexReadOnlyHandlesNilSlice(t, func() (state.BeaconState, error) {
|
||||
return v2.InitializeFromProtoUnsafe(ðpb.BeaconStateAltair{
|
||||
Validators: nil,
|
||||
})
|
||||
|
||||
223
beacon-chain/state/state-native/v2/references_test.go
Normal file
223
beacon-chain/state/state-native/v2/references_test.go
Normal file
@@ -0,0 +1,223 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestStateReferenceSharing_Finalizer(t *testing.T) {
|
||||
// This test showcases the logic on a the RandaoMixes field with the GC finalizer.
|
||||
|
||||
s, err := InitializeFromProtoUnsafe(ðpb.BeaconStateAltair{RandaoMixes: [][]byte{[]byte("foo")}})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, uint(1), s.sharedFieldReferences[randaoMixes].Refs(), "Expected a single reference for RANDAO mixes")
|
||||
|
||||
func() {
|
||||
// Create object in a different scope for GC
|
||||
b := s.Copy()
|
||||
assert.Equal(t, uint(2), s.sharedFieldReferences[randaoMixes].Refs(), "Expected 2 references to RANDAO mixes")
|
||||
_ = b
|
||||
}()
|
||||
|
||||
runtime.GC() // Should run finalizer on object b
|
||||
assert.Equal(t, uint(1), s.sharedFieldReferences[randaoMixes].Refs(), "Expected 1 shared reference to RANDAO mixes!")
|
||||
|
||||
copied := s.Copy()
|
||||
b, ok := copied.(*BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
assert.Equal(t, uint(2), b.sharedFieldReferences[randaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes")
|
||||
require.NoError(t, b.UpdateRandaoMixesAtIndex(0, []byte("bar")))
|
||||
if b.sharedFieldReferences[randaoMixes].Refs() != 1 || s.sharedFieldReferences[randaoMixes].Refs() != 1 {
|
||||
t.Error("Expected 1 shared reference to RANDAO mix for both a and b")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateReferenceCopy_NoUnexpectedRootsMutation(t *testing.T) {
|
||||
root1, root2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
|
||||
s, err := InitializeFromProtoUnsafe(ðpb.BeaconStateAltair{
|
||||
BlockRoots: [][]byte{
|
||||
root1[:],
|
||||
},
|
||||
StateRoots: [][]byte{
|
||||
root1[:],
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assertRefCount(t, s, blockRoots, 1)
|
||||
assertRefCount(t, s, stateRoots, 1)
|
||||
|
||||
// Copy, increases reference count.
|
||||
copied := s.Copy()
|
||||
b, ok := copied.(*BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
assertRefCount(t, s, blockRoots, 2)
|
||||
assertRefCount(t, s, stateRoots, 2)
|
||||
assertRefCount(t, b, blockRoots, 2)
|
||||
assertRefCount(t, b, stateRoots, 2)
|
||||
assert.Equal(t, 8192, len(b.BlockRoots()), "Wrong number of block roots found")
|
||||
assert.Equal(t, 8192, len(b.StateRoots()), "Wrong number of state roots found")
|
||||
|
||||
// Assert shared state.
|
||||
blockRootsA := s.BlockRoots()
|
||||
stateRootsA := s.StateRoots()
|
||||
blockRootsB := b.BlockRoots()
|
||||
stateRootsB := b.StateRoots()
|
||||
if len(blockRootsA) != len(blockRootsB) || len(blockRootsA) < 1 {
|
||||
t.Errorf("Unexpected number of block roots, want: %v", 1)
|
||||
}
|
||||
if len(stateRootsA) != len(stateRootsB) || len(stateRootsA) < 1 {
|
||||
t.Errorf("Unexpected number of state roots, want: %v", 1)
|
||||
}
|
||||
assertValFound(t, blockRootsA, root1[:])
|
||||
assertValFound(t, blockRootsB, root1[:])
|
||||
assertValFound(t, stateRootsA, root1[:])
|
||||
assertValFound(t, stateRootsB, root1[:])
|
||||
|
||||
// Mutator should only affect calling state: a.
|
||||
require.NoError(t, s.UpdateBlockRootAtIndex(0, root2))
|
||||
require.NoError(t, s.UpdateStateRootAtIndex(0, root2))
|
||||
|
||||
// Assert no shared state mutation occurred only on state a (copy on write).
|
||||
assertValNotFound(t, s.BlockRoots(), root1[:])
|
||||
assertValNotFound(t, s.StateRoots(), root1[:])
|
||||
assertValFound(t, s.BlockRoots(), root2[:])
|
||||
assertValFound(t, s.StateRoots(), root2[:])
|
||||
assertValFound(t, b.BlockRoots(), root1[:])
|
||||
assertValFound(t, b.StateRoots(), root1[:])
|
||||
if len(blockRootsA) != len(blockRootsB) || len(blockRootsA) < 1 {
|
||||
t.Errorf("Unexpected number of block roots, want: %v", 1)
|
||||
}
|
||||
if len(stateRootsA) != len(stateRootsB) || len(stateRootsA) < 1 {
|
||||
t.Errorf("Unexpected number of state roots, want: %v", 1)
|
||||
}
|
||||
assert.DeepEqual(t, root2[:], s.BlockRoots()[0], "Expected mutation not found")
|
||||
assert.DeepEqual(t, root2[:], s.StateRoots()[0], "Expected mutation not found")
|
||||
assert.DeepEqual(t, root1[:], blockRootsB[0], "Unexpected mutation found")
|
||||
assert.DeepEqual(t, root1[:], stateRootsB[0], "Unexpected mutation found")
|
||||
|
||||
// Copy on write happened, reference counters are reset.
|
||||
assertRefCount(t, s, blockRoots, 1)
|
||||
assertRefCount(t, s, stateRoots, 1)
|
||||
assertRefCount(t, b, blockRoots, 1)
|
||||
assertRefCount(t, b, stateRoots, 1)
|
||||
}
|
||||
|
||||
func TestStateReferenceCopy_NoUnexpectedRandaoMutation(t *testing.T) {
|
||||
|
||||
val1, val2 := bytesutil.PadTo([]byte("foo"), 32), bytesutil.PadTo([]byte("bar"), 32)
|
||||
s, err := InitializeFromProtoUnsafe(ðpb.BeaconStateAltair{
|
||||
RandaoMixes: [][]byte{
|
||||
val1,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assertRefCount(t, s, randaoMixes, 1)
|
||||
|
||||
// Copy, increases reference count.
|
||||
copied := s.Copy()
|
||||
b, ok := copied.(*BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
assertRefCount(t, s, randaoMixes, 2)
|
||||
assertRefCount(t, b, randaoMixes, 2)
|
||||
|
||||
// Assert shared state.
|
||||
mixesA := s.RandaoMixes()
|
||||
mixesB := b.RandaoMixes()
|
||||
if len(mixesA) != len(mixesB) || len(mixesA) < 1 {
|
||||
t.Errorf("Unexpected number of mix values, want: %v", 1)
|
||||
}
|
||||
assertValFound(t, mixesA, val1)
|
||||
assertValFound(t, mixesB, val1)
|
||||
|
||||
// Mutator should only affect calling state: a.
|
||||
require.NoError(t, s.UpdateRandaoMixesAtIndex(0, val2))
|
||||
|
||||
// Assert no shared state mutation occurred only on state a (copy on write).
|
||||
if len(mixesA) != len(mixesB) || len(mixesA) < 1 {
|
||||
t.Errorf("Unexpected number of mix values, want: %v", 1)
|
||||
}
|
||||
assertValFound(t, s.RandaoMixes(), val2)
|
||||
assertValNotFound(t, s.RandaoMixes(), val1)
|
||||
assertValFound(t, b.RandaoMixes(), val1)
|
||||
assertValNotFound(t, b.RandaoMixes(), val2)
|
||||
assertValFound(t, mixesB, val1)
|
||||
assertValNotFound(t, mixesB, val2)
|
||||
assert.DeepEqual(t, val2, s.RandaoMixes()[0], "Expected mutation not found")
|
||||
assert.DeepEqual(t, val1, mixesB[0], "Unexpected mutation found")
|
||||
|
||||
// Copy on write happened, reference counters are reset.
|
||||
assertRefCount(t, s, randaoMixes, 1)
|
||||
assertRefCount(t, b, randaoMixes, 1)
|
||||
}
|
||||
|
||||
func TestValidatorReferences_RemainsConsistent(t *testing.T) {
|
||||
a, err := InitializeFromProtoUnsafe(ðpb.BeaconStateAltair{
|
||||
Validators: []*ethpb.Validator{
|
||||
{PublicKey: []byte{'A'}},
|
||||
{PublicKey: []byte{'B'}},
|
||||
{PublicKey: []byte{'C'}},
|
||||
{PublicKey: []byte{'D'}},
|
||||
{PublicKey: []byte{'E'}},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a second state.
|
||||
copied := a.Copy()
|
||||
b, ok := copied.(*BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
|
||||
// Update First Validator.
|
||||
assert.NoError(t, a.UpdateValidatorAtIndex(0, ðpb.Validator{PublicKey: []byte{'Z'}}))
|
||||
|
||||
assert.DeepNotEqual(t, a.Validators()[0], b.Validators()[0], "validators are equal when they are supposed to be different")
|
||||
// Modify all validators from copied state.
|
||||
assert.NoError(t, b.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
|
||||
return true, ðpb.Validator{PublicKey: []byte{'V'}}, nil
|
||||
}))
|
||||
|
||||
// Ensure reference is properly accounted for.
|
||||
assert.NoError(t, a.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
|
||||
assert.NotEqual(t, bytesutil.ToBytes48([]byte{'V'}), val.PublicKey())
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
// assertRefCount checks whether reference count for a given state
|
||||
// at a given index is equal to expected amount.
|
||||
func assertRefCount(t *testing.T, b *BeaconState, idx types.FieldIndex, want uint) {
|
||||
if cnt := b.sharedFieldReferences[idx].Refs(); cnt != want {
|
||||
t.Errorf("Unexpected count of references for index %d, want: %v, got: %v", idx, want, cnt)
|
||||
}
|
||||
}
|
||||
|
||||
// assertValFound checks whether item with a given value exists in list.
|
||||
func assertValFound(t *testing.T, vals [][]byte, val []byte) {
|
||||
for i := range vals {
|
||||
if reflect.DeepEqual(vals[i], val) {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Log(string(debug.Stack()))
|
||||
t.Fatalf("Expected value not found (%v), want: %v", vals, val)
|
||||
}
|
||||
|
||||
// assertValNotFound checks whether item with a given value doesn't exist in list.
|
||||
func assertValNotFound(t *testing.T, vals [][]byte, val []byte) {
|
||||
for i := range vals {
|
||||
if reflect.DeepEqual(vals[i], val) {
|
||||
t.Log(string(debug.Stack()))
|
||||
t.Errorf("Unexpected value found (%v),: %v", vals, val)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"sort"
|
||||
|
||||
@@ -28,6 +30,27 @@ func InitializeFromProto(st *ethpb.BeaconStateAltair) (state.BeaconStateAltair,
|
||||
return InitializeFromProtoUnsafe(proto.Clone(st).(*ethpb.BeaconStateAltair))
|
||||
}
|
||||
|
||||
// InitializeFromSSZReader can be used when the source for a serialized BeaconState object
|
||||
// is an io.Reader. This allows client code to remain agnostic about whether the data comes
|
||||
// from the network or a file without needing to read the entire state into mem as a large byte slice.
|
||||
func InitializeFromSSZReader(r io.Reader) (state.BeaconStateAltair, error) {
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return InitializeFromSSZBytes(b)
|
||||
}
|
||||
|
||||
// InitializeFromSSZBytes is a convenience method to obtain a BeaconState by unmarshaling
|
||||
// a slice of bytes containing the ssz-serialized representation of the state.
|
||||
func InitializeFromSSZBytes(marshaled []byte) (*BeaconState, error) {
|
||||
st := ðpb.BeaconStateAltair{}
|
||||
if err := st.UnmarshalSSZ(marshaled); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return InitializeFromProtoUnsafe(st)
|
||||
}
|
||||
|
||||
// InitializeFromProtoUnsafe directly uses the beacon state protobuf fields
|
||||
// and sets them as fields of the BeaconState type.
|
||||
func InitializeFromProtoUnsafe(st *ethpb.BeaconStateAltair) (*BeaconState, error) {
|
||||
|
||||
@@ -87,6 +87,7 @@ go_test(
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/stateutil:go_default_library",
|
||||
"//beacon-chain/state/testing:go_default_library",
|
||||
"//beacon-chain/state/types:go_default_library",
|
||||
"//beacon-chain/state/v3:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
|
||||
@@ -3,75 +3,43 @@ package v3
|
||||
import (
|
||||
"testing"
|
||||
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestBeaconState_LatestBlockHeader(t *testing.T) {
|
||||
s, err := InitializeFromProto(ðpb.BeaconStateBellatrix{})
|
||||
require.NoError(t, err)
|
||||
got := s.LatestBlockHeader()
|
||||
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
|
||||
|
||||
want := ðpb.BeaconBlockHeader{Slot: 100}
|
||||
s, err = InitializeFromProto(ðpb.BeaconStateBellatrix{LatestBlockHeader: want})
|
||||
require.NoError(t, err)
|
||||
got = s.LatestBlockHeader()
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
// Test copy does not mutate.
|
||||
got.Slot = 101
|
||||
require.DeepNotEqual(t, want, got)
|
||||
testtmpl.VerifyBeaconStateLatestBlockHeader(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateBellatrix{})
|
||||
},
|
||||
func(BH *ethpb.BeaconBlockHeader) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateBellatrix{LatestBlockHeader: BH})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestBeaconState_BlockRoots(t *testing.T) {
|
||||
s, err := InitializeFromProto(ðpb.BeaconStateBellatrix{})
|
||||
require.NoError(t, err)
|
||||
got := s.BlockRoots()
|
||||
want := make([][]byte, fieldparams.BlockRootsLength)
|
||||
for i := range want {
|
||||
want[i] = make([]byte, fieldparams.RootLength)
|
||||
}
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
want = make([][]byte, fieldparams.BlockRootsLength)
|
||||
for i := range want {
|
||||
if i == 0 {
|
||||
want[i] = bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
|
||||
} else {
|
||||
want[i] = make([]byte, fieldparams.RootLength)
|
||||
}
|
||||
|
||||
}
|
||||
s, err = InitializeFromProto(ðpb.BeaconStateBellatrix{BlockRoots: want})
|
||||
require.NoError(t, err)
|
||||
got = s.BlockRoots()
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
// Test copy does not mutate.
|
||||
got[0][0] = 'b'
|
||||
require.DeepNotEqual(t, want, got)
|
||||
testtmpl.VerifyBeaconStateBlockRootsNative(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateBellatrix{})
|
||||
},
|
||||
func(BR [][]byte) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateBellatrix{BlockRoots: BR})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestBeaconState_BlockRootAtIndex(t *testing.T) {
|
||||
s, err := InitializeFromProto(ðpb.BeaconStateBellatrix{})
|
||||
require.NoError(t, err)
|
||||
got, err := s.BlockRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, bytesutil.PadTo([]byte{}, fieldparams.RootLength), got)
|
||||
|
||||
r := [fieldparams.BlockRootsLength][fieldparams.RootLength]byte{{'a'}}
|
||||
bRoots := make([][]byte, len(r))
|
||||
for i, root := range r {
|
||||
tmp := root
|
||||
bRoots[i] = tmp[:]
|
||||
}
|
||||
s, err = InitializeFromProto(ðpb.BeaconStateBellatrix{BlockRoots: bRoots})
|
||||
require.NoError(t, err)
|
||||
got, err = s.BlockRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
want := bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
|
||||
require.DeepSSZEqual(t, want, got)
|
||||
testtmpl.VerifyBeaconStateBlockRootAtIndexNative(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateBellatrix{})
|
||||
},
|
||||
func(BR [][]byte) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateBellatrix{BlockRoots: BR})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ import (
|
||||
)
|
||||
|
||||
func TestBeaconState_SlotDataRace(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_SlotDataRace(t, func() (state.BeaconState, error) {
|
||||
testtmpl.VerifyBeaconStateSlotDataRace(t, func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateBellatrix{Slot: 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_MatchCurrentJustifiedCheckptNative(
|
||||
testtmpl.VerifyBeaconStateMatchCurrentJustifiedCheckptNative(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateBellatrix{CurrentJustifiedCheckpoint: cp})
|
||||
@@ -24,7 +24,7 @@ func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_MatchPreviousJustifiedCheckptNative(
|
||||
testtmpl.VerifyBeaconStateMatchPreviousJustifiedCheckptNative(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconStateBellatrix{PreviousJustifiedCheckpoint: cp})
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t, func() (state.BeaconState, error) {
|
||||
testtmpl.VerifyBeaconStateValidatorAtIndexReadOnlyHandlesNilSlice(t, func() (state.BeaconState, error) {
|
||||
return v3.InitializeFromProtoUnsafe(ðpb.BeaconStateBellatrix{
|
||||
Validators: nil,
|
||||
})
|
||||
|
||||
229
beacon-chain/state/state-native/v3/references_test.go
Normal file
229
beacon-chain/state/state-native/v3/references_test.go
Normal file
@@ -0,0 +1,229 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestStateReferenceSharing_Finalizer(t *testing.T) {
|
||||
// This test showcases the logic on a the RandaoMixes field with the GC finalizer.
|
||||
|
||||
a, err := InitializeFromProtoUnsafe(ðpb.BeaconStateBellatrix{RandaoMixes: [][]byte{[]byte("foo")}})
|
||||
require.NoError(t, err)
|
||||
s, ok := a.(*BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
assert.Equal(t, uint(1), s.sharedFieldReferences[randaoMixes].Refs(), "Expected a single reference for RANDAO mixes")
|
||||
|
||||
func() {
|
||||
// Create object in a different scope for GC
|
||||
b := a.Copy()
|
||||
assert.Equal(t, uint(2), s.sharedFieldReferences[randaoMixes].Refs(), "Expected 2 references to RANDAO mixes")
|
||||
_ = b
|
||||
}()
|
||||
|
||||
runtime.GC() // Should run finalizer on object b
|
||||
assert.Equal(t, uint(1), s.sharedFieldReferences[randaoMixes].Refs(), "Expected 1 shared reference to RANDAO mixes!")
|
||||
|
||||
copied := a.Copy()
|
||||
b, ok := copied.(*BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
assert.Equal(t, uint(2), b.sharedFieldReferences[randaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes")
|
||||
require.NoError(t, b.UpdateRandaoMixesAtIndex(0, []byte("bar")))
|
||||
if b.sharedFieldReferences[randaoMixes].Refs() != 1 || s.sharedFieldReferences[randaoMixes].Refs() != 1 {
|
||||
t.Error("Expected 1 shared reference to RANDAO mix for both a and b")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateReferenceCopy_NoUnexpectedRootsMutation(t *testing.T) {
|
||||
root1, root2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
|
||||
a, err := InitializeFromProtoUnsafe(ðpb.BeaconStateBellatrix{
|
||||
BlockRoots: [][]byte{
|
||||
root1[:],
|
||||
},
|
||||
StateRoots: [][]byte{
|
||||
root1[:],
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
s, ok := a.(*BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
assertRefCount(t, s, blockRoots, 1)
|
||||
assertRefCount(t, s, stateRoots, 1)
|
||||
|
||||
// Copy, increases reference count.
|
||||
copied := a.Copy()
|
||||
b, ok := copied.(*BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
assertRefCount(t, s, blockRoots, 2)
|
||||
assertRefCount(t, s, stateRoots, 2)
|
||||
assertRefCount(t, b, blockRoots, 2)
|
||||
assertRefCount(t, b, stateRoots, 2)
|
||||
assert.Equal(t, 8192, len(b.BlockRoots()), "Wrong number of block roots found")
|
||||
assert.Equal(t, 8192, len(b.StateRoots()), "Wrong number of state roots found")
|
||||
|
||||
// Assert shared state.
|
||||
blockRootsA := a.BlockRoots()
|
||||
stateRootsA := a.StateRoots()
|
||||
blockRootsB := b.BlockRoots()
|
||||
stateRootsB := b.StateRoots()
|
||||
if len(blockRootsA) != len(blockRootsB) || len(blockRootsA) < 1 {
|
||||
t.Errorf("Unexpected number of block roots, want: %v", 1)
|
||||
}
|
||||
if len(stateRootsA) != len(stateRootsB) || len(stateRootsA) < 1 {
|
||||
t.Errorf("Unexpected number of state roots, want: %v", 1)
|
||||
}
|
||||
assertValFound(t, blockRootsA, root1[:])
|
||||
assertValFound(t, blockRootsB, root1[:])
|
||||
assertValFound(t, stateRootsA, root1[:])
|
||||
assertValFound(t, stateRootsB, root1[:])
|
||||
|
||||
// Mutator should only affect calling state: a.
|
||||
require.NoError(t, a.UpdateBlockRootAtIndex(0, root2))
|
||||
require.NoError(t, a.UpdateStateRootAtIndex(0, root2))
|
||||
|
||||
// Assert no shared state mutation occurred only on state a (copy on write).
|
||||
assertValNotFound(t, a.BlockRoots(), root1[:])
|
||||
assertValNotFound(t, a.StateRoots(), root1[:])
|
||||
assertValFound(t, a.BlockRoots(), root2[:])
|
||||
assertValFound(t, a.StateRoots(), root2[:])
|
||||
assertValFound(t, b.BlockRoots(), root1[:])
|
||||
assertValFound(t, b.StateRoots(), root1[:])
|
||||
if len(blockRootsA) != len(blockRootsB) || len(blockRootsA) < 1 {
|
||||
t.Errorf("Unexpected number of block roots, want: %v", 1)
|
||||
}
|
||||
if len(stateRootsA) != len(stateRootsB) || len(stateRootsA) < 1 {
|
||||
t.Errorf("Unexpected number of state roots, want: %v", 1)
|
||||
}
|
||||
assert.DeepEqual(t, root2[:], a.BlockRoots()[0], "Expected mutation not found")
|
||||
assert.DeepEqual(t, root2[:], a.StateRoots()[0], "Expected mutation not found")
|
||||
assert.DeepEqual(t, root1[:], blockRootsB[0], "Unexpected mutation found")
|
||||
assert.DeepEqual(t, root1[:], stateRootsB[0], "Unexpected mutation found")
|
||||
|
||||
// Copy on write happened, reference counters are reset.
|
||||
assertRefCount(t, s, blockRoots, 1)
|
||||
assertRefCount(t, s, stateRoots, 1)
|
||||
assertRefCount(t, b, blockRoots, 1)
|
||||
assertRefCount(t, b, stateRoots, 1)
|
||||
}
|
||||
|
||||
func TestStateReferenceCopy_NoUnexpectedRandaoMutation(t *testing.T) {
|
||||
|
||||
val1, val2 := bytesutil.PadTo([]byte("foo"), 32), bytesutil.PadTo([]byte("bar"), 32)
|
||||
a, err := InitializeFromProtoUnsafe(ðpb.BeaconStateBellatrix{
|
||||
RandaoMixes: [][]byte{
|
||||
val1,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
s, ok := a.(*BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
assertRefCount(t, s, randaoMixes, 1)
|
||||
|
||||
// Copy, increases reference count.
|
||||
copied := a.Copy()
|
||||
b, ok := copied.(*BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
assertRefCount(t, s, randaoMixes, 2)
|
||||
assertRefCount(t, b, randaoMixes, 2)
|
||||
|
||||
// Assert shared state.
|
||||
mixesA := a.RandaoMixes()
|
||||
mixesB := b.RandaoMixes()
|
||||
if len(mixesA) != len(mixesB) || len(mixesA) < 1 {
|
||||
t.Errorf("Unexpected number of mix values, want: %v", 1)
|
||||
}
|
||||
assertValFound(t, mixesA, val1)
|
||||
assertValFound(t, mixesB, val1)
|
||||
|
||||
// Mutator should only affect calling state: a.
|
||||
require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2))
|
||||
|
||||
// Assert no shared state mutation occurred only on state a (copy on write).
|
||||
if len(mixesA) != len(mixesB) || len(mixesA) < 1 {
|
||||
t.Errorf("Unexpected number of mix values, want: %v", 1)
|
||||
}
|
||||
assertValFound(t, a.RandaoMixes(), val2)
|
||||
assertValNotFound(t, a.RandaoMixes(), val1)
|
||||
assertValFound(t, b.RandaoMixes(), val1)
|
||||
assertValNotFound(t, b.RandaoMixes(), val2)
|
||||
assertValFound(t, mixesB, val1)
|
||||
assertValNotFound(t, mixesB, val2)
|
||||
assert.DeepEqual(t, val2, a.RandaoMixes()[0], "Expected mutation not found")
|
||||
assert.DeepEqual(t, val1, mixesB[0], "Unexpected mutation found")
|
||||
|
||||
// Copy on write happened, reference counters are reset.
|
||||
assertRefCount(t, s, randaoMixes, 1)
|
||||
assertRefCount(t, b, randaoMixes, 1)
|
||||
}
|
||||
|
||||
func TestValidatorReferences_RemainsConsistent(t *testing.T) {
|
||||
a, err := InitializeFromProtoUnsafe(ðpb.BeaconStateBellatrix{
|
||||
Validators: []*ethpb.Validator{
|
||||
{PublicKey: []byte{'A'}},
|
||||
{PublicKey: []byte{'B'}},
|
||||
{PublicKey: []byte{'C'}},
|
||||
{PublicKey: []byte{'D'}},
|
||||
{PublicKey: []byte{'E'}},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a second state.
|
||||
copied := a.Copy()
|
||||
b, ok := copied.(*BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
|
||||
// Update First Validator.
|
||||
assert.NoError(t, a.UpdateValidatorAtIndex(0, ðpb.Validator{PublicKey: []byte{'Z'}}))
|
||||
|
||||
assert.DeepNotEqual(t, a.Validators()[0], b.Validators()[0], "validators are equal when they are supposed to be different")
|
||||
// Modify all validators from copied state.
|
||||
assert.NoError(t, b.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
|
||||
return true, ðpb.Validator{PublicKey: []byte{'V'}}, nil
|
||||
}))
|
||||
|
||||
// Ensure reference is properly accounted for.
|
||||
assert.NoError(t, a.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
|
||||
assert.NotEqual(t, bytesutil.ToBytes48([]byte{'V'}), val.PublicKey())
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
// assertRefCount checks whether reference count for a given state
|
||||
// at a given index is equal to expected amount.
|
||||
func assertRefCount(t *testing.T, b *BeaconState, idx types.FieldIndex, want uint) {
|
||||
if cnt := b.sharedFieldReferences[idx].Refs(); cnt != want {
|
||||
t.Errorf("Unexpected count of references for index %d, want: %v, got: %v", idx, want, cnt)
|
||||
}
|
||||
}
|
||||
|
||||
// assertValFound checks whether item with a given value exists in list.
|
||||
func assertValFound(t *testing.T, vals [][]byte, val []byte) {
|
||||
for i := range vals {
|
||||
if reflect.DeepEqual(vals[i], val) {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Log(string(debug.Stack()))
|
||||
t.Fatalf("Expected value not found (%v), want: %v", vals, val)
|
||||
}
|
||||
|
||||
// assertValNotFound checks whether item with a given value doesn't exist in list.
|
||||
func assertValNotFound(t *testing.T, vals [][]byte, val []byte) {
|
||||
for i := range vals {
|
||||
if reflect.DeepEqual(vals[i], val) {
|
||||
t.Log(string(debug.Stack()))
|
||||
t.Errorf("Unexpected value found (%v),: %v", vals, val)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func TestBeaconState_AppendBalanceWithTrie(t *testing.T) {
|
||||
ReceiptRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, 256),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: bytesutil.PadTo([]byte{1, 2, 3, 4}, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
TransactionsRoot: make([]byte, fieldparams.RootLength),
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package stategen
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
@@ -14,8 +13,6 @@ import (
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var ErrSlotBeforeOrigin = errors.New("cannot retrieve data for slots before sync origin")
|
||||
|
||||
// HasState returns true if the state exists in cache or in DB.
|
||||
func (s *State) HasState(ctx context.Context, blockRoot [32]byte) (bool, error) {
|
||||
has, err := s.HasStateInCache(ctx, blockRoot)
|
||||
@@ -56,9 +53,6 @@ func (s *State) StateByRoot(ctx context.Context, blockRoot [32]byte) (state.Beac
|
||||
|
||||
// Genesis case. If block root is zero hash, short circuit to use genesis cachedState stored in DB.
|
||||
if blockRoot == params.BeaconConfig().ZeroHash {
|
||||
if s.MinimumSlot() > 0 {
|
||||
return nil, errors.Wrapf(ErrSlotBeforeOrigin, "cannot retrieve genesis state, lowest block in db is slot %d", s.MinimumSlot())
|
||||
}
|
||||
return s.beaconDB.GenesisState(ctx)
|
||||
}
|
||||
return s.loadStateByRoot(ctx, blockRoot)
|
||||
@@ -72,9 +66,6 @@ func (s *State) StateByRoot(ctx context.Context, blockRoot [32]byte) (state.Beac
|
||||
func (s *State) StateByRootInitialSync(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error) {
|
||||
// Genesis case. If block root is zero hash, short circuit to use genesis state stored in DB.
|
||||
if blockRoot == params.BeaconConfig().ZeroHash {
|
||||
if s.beaconDBInitType == BeaconDBInitTypeCheckpoint {
|
||||
return nil, fmt.Errorf("node initialized through checkpoint sync received invalid (zero value) block root")
|
||||
}
|
||||
return s.beaconDB.GenesisState(ctx)
|
||||
}
|
||||
|
||||
@@ -222,10 +213,6 @@ func (s *State) loadStateBySlot(ctx context.Context, slot types.Slot) (state.Bea
|
||||
ctx, span := trace.StartSpan(ctx, "stateGen.loadStateBySlot")
|
||||
defer span.End()
|
||||
|
||||
if slot < s.MinimumSlot() {
|
||||
return nil, errors.Wrapf(ErrSlotBeforeOrigin, "no data from before slot %d", s.MinimumSlot())
|
||||
}
|
||||
|
||||
// Return genesis state if slot is 0.
|
||||
if slot == 0 {
|
||||
return s.beaconDB.GenesisState(ctx)
|
||||
@@ -279,11 +266,6 @@ func (s *State) LastAncestorState(ctx context.Context, root [32]byte) (state.Bea
|
||||
if ctx.Err() != nil {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
// return an error if we have rewound to before the checkpoint sync slot
|
||||
if (b.Block().Slot() - 1) < s.MinimumSlot() {
|
||||
return nil, errors.Wrapf(ErrSlotBeforeOrigin, "no blocks in db prior to slot %d", s.MinimumSlot())
|
||||
}
|
||||
// Is the state a genesis state.
|
||||
parentRoot := bytesutil.ToBytes32(b.Block().ParentRoot())
|
||||
if parentRoot == params.BeaconConfig().ZeroHash {
|
||||
|
||||
@@ -242,17 +242,9 @@ func (s *State) lastSavedBlock(ctx context.Context, slot types.Slot) ([32]byte,
|
||||
ctx, span := trace.StartSpan(ctx, "stateGen.lastSavedBlock")
|
||||
defer span.End()
|
||||
|
||||
if slot < s.MinimumSlot() {
|
||||
return [32]byte{}, 0, errors.Wrapf(ErrSlotBeforeOrigin, "no data from before slot %d", s.MinimumSlot())
|
||||
}
|
||||
|
||||
// Handle the genesis case where the input slot is 0.
|
||||
if slot == 0 {
|
||||
gb, err := s.beaconDB.GenesisBlock(ctx)
|
||||
if err != nil {
|
||||
return [32]byte{}, 0, err
|
||||
}
|
||||
gRoot, err := gb.Block().HashTreeRoot()
|
||||
gRoot, err := s.genesisRoot(ctx)
|
||||
if err != nil {
|
||||
return [32]byte{}, 0, err
|
||||
}
|
||||
@@ -286,10 +278,6 @@ func (s *State) lastSavedState(ctx context.Context, slot types.Slot) (state.Read
|
||||
ctx, span := trace.StartSpan(ctx, "stateGen.lastSavedState")
|
||||
defer span.End()
|
||||
|
||||
if slot < s.MinimumSlot() {
|
||||
return nil, errors.Wrapf(ErrSlotBeforeOrigin, "no data from before slot %d", s.MinimumSlot())
|
||||
}
|
||||
|
||||
// Handle the genesis case where the input slot is 0.
|
||||
if slot == 0 {
|
||||
return s.beaconDB.GenesisState(ctx)
|
||||
@@ -311,6 +299,15 @@ func (s *State) lastSavedState(ctx context.Context, slot types.Slot) (state.Read
|
||||
return lastSaved[0], nil
|
||||
}
|
||||
|
||||
// This returns the genesis root.
|
||||
func (s *State) genesisRoot(ctx context.Context) ([32]byte, error) {
|
||||
b, err := s.beaconDB.GenesisBlock(ctx)
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
return b.Block().HashTreeRoot()
|
||||
}
|
||||
|
||||
// Given the start slot and the end slot, this returns the finalized beacon blocks in between.
|
||||
// Since hot states don't have finalized blocks, this should ONLY be used for replaying cold state.
|
||||
func (s *State) loadFinalizedBlocks(ctx context.Context, startSlot, endSlot types.Slot) ([]block.SignedBeaconBlock, error) {
|
||||
|
||||
@@ -6,7 +6,6 @@ package stategen
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
@@ -50,8 +49,6 @@ type State struct {
|
||||
finalizedInfo *finalizedInfo
|
||||
epochBoundaryStateCache *epochBoundaryState
|
||||
saveHotStateDB *saveHotStateDbConfig
|
||||
minimumSlot types.Slot
|
||||
beaconDBInitType BeaconDBInitType
|
||||
}
|
||||
|
||||
// This tracks the config in the event of long non-finality,
|
||||
@@ -73,31 +70,9 @@ type finalizedInfo struct {
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func WithMinimumSlot(min types.Slot) StateGenOption {
|
||||
return func(s *State) {
|
||||
s.minimumSlot = min
|
||||
}
|
||||
}
|
||||
|
||||
type BeaconDBInitType uint
|
||||
|
||||
const (
|
||||
BeaconDBInitTypeGenesisState = iota
|
||||
BeaconDBInitTypeCheckpoint
|
||||
)
|
||||
|
||||
func WithInitType(t BeaconDBInitType) StateGenOption {
|
||||
return func(s *State) {
|
||||
s.beaconDBInitType = t
|
||||
}
|
||||
}
|
||||
|
||||
// StateGenOption is a functional option for controlling the initialization of a *State value
|
||||
type StateGenOption func(*State)
|
||||
|
||||
// New returns a new state management object.
|
||||
func New(beaconDB db.NoHeadAccessDatabase, opts ...StateGenOption) *State {
|
||||
s := &State{
|
||||
func New(beaconDB db.NoHeadAccessDatabase) *State {
|
||||
return &State{
|
||||
beaconDB: beaconDB,
|
||||
hotStateCache: newHotStateCache(),
|
||||
finalizedInfo: &finalizedInfo{slot: 0, root: params.BeaconConfig().ZeroHash},
|
||||
@@ -106,13 +81,7 @@ func New(beaconDB db.NoHeadAccessDatabase, opts ...StateGenOption) *State {
|
||||
saveHotStateDB: &saveHotStateDbConfig{
|
||||
duration: defaultHotStateDBInterval,
|
||||
},
|
||||
// defaults to minimumSlot of zero (genesis), overridden by checkpoint sync
|
||||
minimumSlot: types.Slot(0),
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Resume resumes a new state management object from previously saved finalized check point in DB.
|
||||
@@ -127,9 +96,6 @@ func (s *State) Resume(ctx context.Context, fState state.BeaconState) (state.Bea
|
||||
fRoot := bytesutil.ToBytes32(c.Root)
|
||||
// Resume as genesis state if last finalized root is zero hashes.
|
||||
if fRoot == params.BeaconConfig().ZeroHash {
|
||||
if s.MinimumSlot() > 0 {
|
||||
return nil, fmt.Errorf("no finalized checkpoint, and history before slot %d not currently available", s.MinimumSlot())
|
||||
}
|
||||
return s.beaconDB.GenesisState(ctx)
|
||||
}
|
||||
|
||||
@@ -172,7 +138,3 @@ func (s *State) finalizedState() state.BeaconState {
|
||||
defer s.finalizedInfo.lock.RUnlock()
|
||||
return s.finalizedInfo.state.Copy()
|
||||
}
|
||||
|
||||
func (s *State) MinimumSlot() types.Slot {
|
||||
return s.minimumSlot
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ go_library(
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"getters.go",
|
||||
"getters_block.go",
|
||||
"getters_validator.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/testing",
|
||||
@@ -12,6 +13,7 @@ go_library(
|
||||
deps = [
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func VerifyBeaconState_SlotDataRace(t *testing.T, factory getState) {
|
||||
func VerifyBeaconStateSlotDataRace(t *testing.T, factory getState) {
|
||||
headState, err := factory()
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -33,7 +33,7 @@ func VerifyBeaconState_SlotDataRace(t *testing.T, factory getState) {
|
||||
type getStateWithCurrentJustifiedCheckpoint func(*ethpb.Checkpoint) (state.BeaconState, error)
|
||||
type clearInternalState func(state.BeaconState)
|
||||
|
||||
func VerifyBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint, clear clearInternalState) {
|
||||
func VerifyBeaconStateMatchCurrentJustifiedCheckpt(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint, clear clearInternalState) {
|
||||
c1 := ðpb.Checkpoint{Epoch: 1}
|
||||
c2 := ðpb.Checkpoint{Epoch: 2}
|
||||
beaconState, err := factory(c1)
|
||||
@@ -46,7 +46,7 @@ func VerifyBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T, factory getSta
|
||||
require.Equal(t, false, beaconState.MatchCurrentJustifiedCheckpoint(c1))
|
||||
}
|
||||
|
||||
func VerifyBeaconState_MatchCurrentJustifiedCheckptNative(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint) {
|
||||
func VerifyBeaconStateMatchCurrentJustifiedCheckptNative(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint) {
|
||||
c1 := ðpb.Checkpoint{Epoch: 1}
|
||||
c2 := ðpb.Checkpoint{Epoch: 2}
|
||||
beaconState, err := factory(c1)
|
||||
@@ -57,7 +57,7 @@ func VerifyBeaconState_MatchCurrentJustifiedCheckptNative(t *testing.T, factory
|
||||
require.Equal(t, false, beaconState.MatchPreviousJustifiedCheckpoint(c2))
|
||||
}
|
||||
|
||||
func VerifyBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint, clear clearInternalState) {
|
||||
func VerifyBeaconStateMatchPreviousJustifiedCheckpt(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint, clear clearInternalState) {
|
||||
c1 := ðpb.Checkpoint{Epoch: 1}
|
||||
c2 := ðpb.Checkpoint{Epoch: 2}
|
||||
beaconState, err := factory(c1)
|
||||
@@ -70,7 +70,7 @@ func VerifyBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T, factory getSt
|
||||
require.Equal(t, false, beaconState.MatchPreviousJustifiedCheckpoint(c1))
|
||||
}
|
||||
|
||||
func VerifyBeaconState_MatchPreviousJustifiedCheckptNative(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint) {
|
||||
func VerifyBeaconStateMatchPreviousJustifiedCheckptNative(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint) {
|
||||
c1 := ðpb.Checkpoint{Epoch: 1}
|
||||
c2 := ðpb.Checkpoint{Epoch: 2}
|
||||
beaconState, err := factory(c1)
|
||||
@@ -81,7 +81,7 @@ func VerifyBeaconState_MatchPreviousJustifiedCheckptNative(t *testing.T, factory
|
||||
require.Equal(t, false, beaconState.MatchPreviousJustifiedCheckpoint(c2))
|
||||
}
|
||||
|
||||
func VerifyBeaconState_MarshalSSZ_NilState(t *testing.T, factory getState, clear clearInternalState) {
|
||||
func VerifyBeaconStateMarshalSSZNilState(t *testing.T, factory getState, clear clearInternalState) {
|
||||
s, err := factory()
|
||||
require.NoError(t, err)
|
||||
clear(s)
|
||||
@@ -89,7 +89,7 @@ func VerifyBeaconState_MarshalSSZ_NilState(t *testing.T, factory getState, clear
|
||||
require.ErrorContains(t, "nil beacon state", err)
|
||||
}
|
||||
|
||||
func VerifyBeaconState_ValidatorByPubkey(t *testing.T, factory getState) {
|
||||
func VerifyBeaconStateValidatorByPubkey(t *testing.T, factory getState) {
|
||||
keyCreator := func(input []byte) [fieldparams.BLSPubkeyLength]byte {
|
||||
nKey := [fieldparams.BLSPubkeyLength]byte{}
|
||||
copy(nKey[:1], input)
|
||||
|
||||
135
beacon-chain/state/testing/getters_block.go
Normal file
135
beacon-chain/state/testing/getters_block.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
type getStateWithLatestBlockHeader func(*ethpb.BeaconBlockHeader) (state.BeaconState, error)
|
||||
|
||||
func VerifyBeaconStateLatestBlockHeader(
|
||||
t *testing.T,
|
||||
factory getState,
|
||||
factoryLBH getStateWithLatestBlockHeader,
|
||||
) {
|
||||
s, err := factory()
|
||||
require.NoError(t, err)
|
||||
got := s.LatestBlockHeader()
|
||||
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
|
||||
|
||||
want := ðpb.BeaconBlockHeader{Slot: 100}
|
||||
s, err = factoryLBH(want)
|
||||
require.NoError(t, err)
|
||||
got = s.LatestBlockHeader()
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
// Test copy does not mutate.
|
||||
got.Slot = 101
|
||||
require.DeepNotEqual(t, want, got)
|
||||
}
|
||||
|
||||
type getStateWithLBlockRoots func([][]byte) (state.BeaconState, error)
|
||||
|
||||
func VerifyBeaconStateBlockRoots(
|
||||
t *testing.T,
|
||||
factory getState,
|
||||
factoryBR getStateWithLBlockRoots,
|
||||
) {
|
||||
s, err := factory()
|
||||
require.NoError(t, err)
|
||||
got := s.BlockRoots()
|
||||
require.DeepEqual(t, ([][]byte)(nil), got)
|
||||
|
||||
want := [][]byte{{'a'}}
|
||||
s, err = factoryBR(want)
|
||||
require.NoError(t, err)
|
||||
got = s.BlockRoots()
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
// Test copy does not mutate.
|
||||
got[0][0] = 'b'
|
||||
require.DeepNotEqual(t, want, got)
|
||||
}
|
||||
|
||||
func VerifyBeaconStateBlockRootsNative(
|
||||
t *testing.T,
|
||||
factory getState,
|
||||
factoryBR getStateWithLBlockRoots,
|
||||
) {
|
||||
s, err := factory()
|
||||
require.NoError(t, err)
|
||||
got := s.BlockRoots()
|
||||
want := make([][]byte, fieldparams.BlockRootsLength)
|
||||
for i := range want {
|
||||
want[i] = make([]byte, 32)
|
||||
}
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
want = make([][]byte, fieldparams.BlockRootsLength)
|
||||
for i := range want {
|
||||
if i == 0 {
|
||||
want[i] = bytesutil.PadTo([]byte{'a'}, 32)
|
||||
} else {
|
||||
want[i] = make([]byte, 32)
|
||||
}
|
||||
|
||||
}
|
||||
s, err = factoryBR(want)
|
||||
require.NoError(t, err)
|
||||
got = s.BlockRoots()
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
// Test copy does not mutate.
|
||||
got[0][0] = 'b'
|
||||
require.DeepNotEqual(t, want, got)
|
||||
}
|
||||
|
||||
func VerifyBeaconStateBlockRootAtIndex(
|
||||
t *testing.T,
|
||||
factory getState,
|
||||
factoryBR getStateWithLBlockRoots,
|
||||
) {
|
||||
s, err := factory()
|
||||
require.NoError(t, err)
|
||||
got, err := s.BlockRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, ([]byte)(nil), got)
|
||||
|
||||
r := [][]byte{{'a'}}
|
||||
s, err = factoryBR(r)
|
||||
require.NoError(t, err)
|
||||
got, err = s.BlockRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
want := bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
|
||||
require.DeepSSZEqual(t, want, got)
|
||||
}
|
||||
|
||||
func VerifyBeaconStateBlockRootAtIndexNative(
|
||||
t *testing.T,
|
||||
factory getState,
|
||||
factoryBR getStateWithLBlockRoots,
|
||||
) {
|
||||
s, err := factory()
|
||||
require.NoError(t, err)
|
||||
got, err := s.BlockRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, bytesutil.PadTo([]byte{}, 32), got)
|
||||
|
||||
r := [fieldparams.BlockRootsLength][32]byte{{'a'}}
|
||||
bRoots := make([][]byte, len(r))
|
||||
for i, root := range r {
|
||||
tmp := root
|
||||
bRoots[i] = tmp[:]
|
||||
}
|
||||
s, err = factoryBR(bRoots)
|
||||
require.NoError(t, err)
|
||||
got, err = s.BlockRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
want := bytesutil.PadTo([]byte{'a'}, 32)
|
||||
require.DeepSSZEqual(t, want, got)
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
type getState func() (state.BeaconState, error)
|
||||
|
||||
func VerifyBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T, factory getState) {
|
||||
func VerifyBeaconStateValidatorAtIndexReadOnlyHandlesNilSlice(t *testing.T, factory getState) {
|
||||
st, err := factory()
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -29,7 +29,20 @@ go_library(
|
||||
"unsupported_setters.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/v1",
|
||||
visibility = ["//visibility:public"],
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__subpackages__",
|
||||
"//proto/migration:__subpackages__",
|
||||
"//proto/prysm/v1alpha1:__subpackages__",
|
||||
"//proto/testing:__subpackages__",
|
||||
"//runtime/interop:__subpackages__",
|
||||
"//slasher/rpc:__subpackages__",
|
||||
"//testing/benchmark:__pkg__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
"//tools/benchmark-files-gen:__pkg__",
|
||||
"//tools/pcli:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/fieldtrie:go_default_library",
|
||||
|
||||
@@ -3,57 +3,43 @@ package v1
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestBeaconState_LatestBlockHeader(t *testing.T) {
|
||||
s, err := InitializeFromProto(ðpb.BeaconState{})
|
||||
require.NoError(t, err)
|
||||
got := s.LatestBlockHeader()
|
||||
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
|
||||
|
||||
want := ðpb.BeaconBlockHeader{Slot: 100}
|
||||
s, err = InitializeFromProto(ðpb.BeaconState{LatestBlockHeader: want})
|
||||
require.NoError(t, err)
|
||||
got = s.LatestBlockHeader()
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
// Test copy does not mutate.
|
||||
got.Slot = 101
|
||||
require.DeepNotEqual(t, want, got)
|
||||
testtmpl.VerifyBeaconStateLatestBlockHeader(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{})
|
||||
},
|
||||
func(BH *ethpb.BeaconBlockHeader) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{LatestBlockHeader: BH})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestBeaconState_BlockRoots(t *testing.T) {
|
||||
s, err := InitializeFromProto(ðpb.BeaconState{})
|
||||
require.NoError(t, err)
|
||||
got := s.BlockRoots()
|
||||
require.DeepEqual(t, ([][]byte)(nil), got)
|
||||
|
||||
want := [][]byte{{'a'}}
|
||||
s, err = InitializeFromProto(ðpb.BeaconState{BlockRoots: want})
|
||||
require.NoError(t, err)
|
||||
got = s.BlockRoots()
|
||||
require.DeepEqual(t, want, got)
|
||||
|
||||
// Test copy does not mutate.
|
||||
got[0][0] = 'b'
|
||||
require.DeepNotEqual(t, want, got)
|
||||
testtmpl.VerifyBeaconStateBlockRoots(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{})
|
||||
},
|
||||
func(BR [][]byte) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{BlockRoots: BR})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestBeaconState_BlockRootAtIndex(t *testing.T) {
|
||||
s, err := InitializeFromProto(ðpb.BeaconState{})
|
||||
require.NoError(t, err)
|
||||
got, err := s.BlockRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, ([]byte)(nil), got)
|
||||
|
||||
r := [][]byte{{'a'}}
|
||||
s, err = InitializeFromProto(ðpb.BeaconState{BlockRoots: r})
|
||||
require.NoError(t, err)
|
||||
got, err = s.BlockRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
want := bytesutil.PadTo([]byte{'a'}, 32)
|
||||
require.DeepSSZEqual(t, want, got)
|
||||
testtmpl.VerifyBeaconStateBlockRootAtIndex(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{})
|
||||
},
|
||||
func(BR [][]byte) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{BlockRoots: BR})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func TestBeaconState_SlotDataRace(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_SlotDataRace(t, func() (state.BeaconState, error) {
|
||||
testtmpl.VerifyBeaconStateSlotDataRace(t, func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{Slot: 1})
|
||||
})
|
||||
}
|
||||
@@ -66,7 +66,7 @@ func TestNilState_NoPanic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_MatchCurrentJustifiedCheckpt(
|
||||
testtmpl.VerifyBeaconStateMatchCurrentJustifiedCheckpt(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{CurrentJustifiedCheckpoint: cp})
|
||||
@@ -82,7 +82,7 @@ func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_MatchPreviousJustifiedCheckpt(
|
||||
testtmpl.VerifyBeaconStateMatchPreviousJustifiedCheckpt(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{PreviousJustifiedCheckpoint: cp})
|
||||
@@ -98,7 +98,7 @@ func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeaconState_MarshalSSZ_NilState(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_MarshalSSZ_NilState(
|
||||
testtmpl.VerifyBeaconStateMarshalSSZNilState(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{})
|
||||
@@ -114,7 +114,7 @@ func TestBeaconState_MarshalSSZ_NilState(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeaconState_ValidatorByPubkey(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_ValidatorByPubkey(t, func() (state.BeaconState, error) {
|
||||
testtmpl.VerifyBeaconStateValidatorByPubkey(t, func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(ðpb.BeaconState{})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T) {
|
||||
testtmpl.VerifyBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t, func() (state.BeaconState, error) {
|
||||
testtmpl.VerifyBeaconStateValidatorAtIndexReadOnlyHandlesNilSlice(t, func() (state.BeaconState, error) {
|
||||
return v1.InitializeFromProtoUnsafe(ðpb.BeaconState{
|
||||
Validators: nil,
|
||||
})
|
||||
|
||||
@@ -31,16 +31,6 @@ func InitializeFromProto(st *ethpb.BeaconState) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(proto.Clone(st).(*ethpb.BeaconState))
|
||||
}
|
||||
|
||||
// InitializeFromSSZBytes is a convenience method to obtain a BeaconState by unmarshaling
|
||||
// a slice of bytes containing the ssz-serialized representation of the state.
|
||||
func InitializeFromSSZBytes(marshaled []byte) (state.BeaconState, error) {
|
||||
st := ðpb.BeaconState{}
|
||||
if err := st.UnmarshalSSZ(marshaled); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return InitializeFromProtoUnsafe(st)
|
||||
}
|
||||
|
||||
// InitializeFromProtoUnsafe directly uses the beacon state protobuf pointer
|
||||
// and sets it as the inner state of the BeaconState type.
|
||||
func InitializeFromProtoUnsafe(st *ethpb.BeaconState) (state.BeaconState, error) {
|
||||
|
||||
@@ -29,7 +29,12 @@ go_library(
|
||||
"types.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/v2",
|
||||
visibility = ["//visibility:public"],
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//proto/migration:__subpackages__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/fieldtrie:go_default_library",
|
||||
@@ -63,6 +68,7 @@ go_test(
|
||||
"getters_test.go",
|
||||
"getters_validator_test.go",
|
||||
"proofs_test.go",
|
||||
"references_test.go",
|
||||
"setters_test.go",
|
||||
"state_trie_test.go",
|
||||
],
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user