Compare commits

...

15 Commits

Author SHA1 Message Date
Raul Jordan
1dec9eb912 Remove Unnecessary Stack Traces for Engine Errors (#10765)
* cleaner rpc responses

* clean up unecessary stack trace

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-05-26 22:09:33 +00:00
terencechain
a2951ec37d Update Ropsten ttd to 100000000000000000000000 (#10762)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-05-26 21:12:59 +00:00
terencechain
216fdb48cf Add back ttd override flags (#10763)
* Add back ttd override flags

* Add warning log for overrides

* Print has hex

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: prestonvanloon <preston@prysmaticlabs.com>
2022-05-26 19:12:02 +00:00
Preston Van Loon
61c5e2a443 Fix error message with incorrect flag names (#10761) 2022-05-26 17:47:00 +00:00
Nishant Das
da9f72360d Make Common Dep Set For E2E (#10758) 2022-05-26 09:33:04 +02:00
Preston Van Loon
1be46aa16e Add a fuzz test for fieldtrie (#10757)
* Add a fuzz test for fieldtrie

* gofmt
2022-05-26 13:17:34 +08:00
kasey
a1a12243be Sync from finalized (#10723)
* checkpoint sync use finalized state+block

instead of finding the block at the beginning of the weak subjectivity
epoch.

* happy path test for sync-from-finalized

* gofmt

* functional opts for the minimal e2e

* add TestCheckpointSync option

* wip: pushing for CI

* include conn index in log for debugging

* lint

* block until regular sync test finishes

* restore TestSync->testDoppelGangerProtection link

* update bazel deps for all the test targets

* updating to match current checksum from github

Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2022-05-25 22:52:43 +00:00
kasey
a1dd2e6b8c updating to match current checksum from github (#10756)
Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2022-05-25 18:29:17 +00:00
Nishant Das
ecb605814e Add in Separate Directories per Test (#10753)
Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
2022-05-25 15:57:13 +00:00
terencechain
61cbe3709b Ignore subset aggregates (#10674)
* Ignore subset aggregates

* Add test

* Update BUILD.bazel

* Update validate_sync_contribution_proof_test.go

* Update validate_sync_contribution_proof_test.go

* Don't utilize pooled objects. Use direct caches

* Handle mainnet/minimal better

* Revert att changes

* Check overlaps before set

* Feedbacks

* Fixed a copy bug

* Fixed the same copy bug in committee indices cache

* Use SafeCopyBytes

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-05-25 05:40:06 +00:00
Raul Jordan
7039c382bf Update Golang X Tools to Support Generics in Prysm (#10752)
* update tools to enable generics support

* tidy

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-05-25 05:10:05 +00:00
Nishant Das
4f00984ab1 fix (#10751) 2022-05-24 23:10:51 -04:00
terencechain
edb03328ea Sync: use copied bits for seen cache (#10747)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-05-24 21:11:33 +00:00
Sammy Rosso
c922eb9bfc Add new DomainType for application usage (#10740)
* Add new DomainType for application usage

* Add DomainApplicationMask to config_test

* Add func to convert uint32 to 4 byte array

We add a convenience function Uint32ToBytes4 that takes a uint32,
converts it to big endian order and return a 4 byte array.

* Add unit test for Uint32ToBytes4

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: terencechain <terence@prysmaticlabs.com>
2022-05-24 20:16:05 +00:00
terencechain
051a83a83d Engine metrics: help text typos (#10746)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-05-24 19:17:17 +00:00
51 changed files with 913 additions and 348 deletions

View File

@@ -231,7 +231,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "57120e8e2d5aaec956fc6a25ddc58fae2477f5b3ac7789174cf5ac1106dcc151",
sha256 = "9c93f87378aaa6d6fe1c67b396eac2aacc9594af2a83f028cb99c95dea5b81df",
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 = "aa46676b26c173274ec8ea8756ae3072474b73ef7ccc7414d4026884810d8de2",
sha256 = "52f2c52415228cee8a4de5a09abff785f439a77dfef8f03e834e4e16857673c1",
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 = "d7dba93110cf35d9575ce21af6b7c3989f4aba621a9749bc090bca216e0345f7",
sha256 = "022dcc0d6de7dd27b337a0d1b945077eaf5ee47000700395a693fc25e12f96df",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/mainnet.tar.gz" % consensus_spec_version,
)

View File

@@ -26,69 +26,126 @@ type OriginData struct {
bb []byte
st state.BeaconState
b interfaces.SignedBeaconBlock
cf *detect.VersionedUnmarshaler
}
// CheckpointString returns the standard string representation of a Checkpoint for the block root and epoch for the
// SignedBeaconBlock value found by DownloadOriginData.
// 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 (od *OriginData) CheckpointString() string {
return fmt.Sprintf("%#x:%d", od.wsd.BlockRoot, od.wsd.Epoch)
vu *detect.VersionedUnmarshaler
br [32]byte
sr [32]byte
}
// 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) {
blockPath := path.Join(dir, fname("block", od.cf, od.b.Block().Slot(), od.wsd.BlockRoot))
return blockPath, file.WriteFile(blockPath, od.BlockBytes())
func (o *OriginData) SaveBlock(dir string) (string, error) {
blockPath := path.Join(dir, fname("block", o.vu, o.b.Block().Slot(), o.br))
return blockPath, file.WriteFile(blockPath, o.BlockBytes())
}
// 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, file.WriteFile(statePath, od.StateBytes())
func (o *OriginData) SaveState(dir string) (string, error) {
statePath := path.Join(dir, fname("state", o.vu, o.st.Slot(), o.sr))
return statePath, file.WriteFile(statePath, o.StateBytes())
}
// StateBytes returns the ssz-encoded bytes of the downloaded BeaconState value.
func (od *OriginData) StateBytes() []byte {
return od.sb
func (o *OriginData) StateBytes() []byte {
return o.sb
}
// BlockBytes returns the ssz-encoded bytes of the downloaded SignedBeaconBlock value.
func (od *OriginData) BlockBytes() []byte {
return od.bb
func (o *OriginData) BlockBytes() []byte {
return o.bb
}
func fname(prefix string, cf *detect.VersionedUnmarshaler, slot types.Slot, root [32]byte) string {
return fmt.Sprintf("%s_%s_%s_%d-%#x.ssz", prefix, cf.Config.ConfigName, version.String(cf.Fork), slot, root)
func fname(prefix string, vu *detect.VersionedUnmarshaler, slot types.Slot, root [32]byte) string {
return fmt.Sprintf("%s_%s_%s_%d-%#x.ssz", prefix, vu.Config.ConfigName, version.String(vu.Fork), 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)
// DownloadFinalizedData downloads the most recently finalized state, and the block most recently applied to that state.
// This pair can be used to initialize a new beacon node via checkpoint sync.
func DownloadFinalizedData(ctx context.Context, client *Client) (*OriginData, error) {
sb, err := client.GetState(ctx, IdFinalized)
if err != nil {
return 0, err
return nil, err
}
cf, err := detect.FromState(headBytes)
vu, err := detect.FromState(sb)
if err != nil {
return 0, errors.Wrap(err, "error detecting chain config for beacon state")
return nil, errors.Wrap(err, "error detecting chain config for finalized state")
}
log.Printf("detected supported config in remote head state, name=%s, fork=%s", cf.Config.ConfigName, version.String(cf.Fork))
headState, err := cf.UnmarshalBeaconState(headBytes)
log.Printf("detected supported config in remote finalized state, name=%s, fork=%s", vu.Config.ConfigName, version.String(vu.Fork))
s, err := vu.UnmarshalBeaconState(sb)
if err != nil {
return 0, errors.Wrap(err, "error unmarshaling state to correct version")
return nil, errors.Wrap(err, "error unmarshaling finalized state to correct version")
}
epoch, err := helpers.LatestWeakSubjectivityEpoch(ctx, headState, cf.Config)
sr, err := s.HashTreeRoot(ctx)
if err != nil {
return 0, errors.Wrap(err, "error computing the weak subjectivity epoch from head state")
return nil, errors.Wrapf(err, "failed to compute htr for finalized state at slot=%d", s.Slot())
}
header := s.LatestBlockHeader()
header.StateRoot = sr[:]
br, err := header.HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "error while computing block root using state data")
}
log.Printf("(computed client-side) weak subjectivity epoch = %d", epoch)
return epoch, nil
bb, err := client.GetBlock(ctx, IdFromRoot(br))
if err != nil {
return nil, errors.Wrapf(err, "error requesting block by root = %#x", br)
}
b, err := vu.UnmarshalBeaconBlock(bb)
if err != nil {
return nil, errors.Wrap(err, "unable to unmarshal block to a supported type using the detected fork schedule")
}
realBlockRoot, err := b.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "error computing hash_tree_root of retrieved block")
}
log.Printf("BeaconState slot=%d, Block slot=%d", s.Slot(), b.Block().Slot())
log.Printf("BeaconState htr=%#xd, Block state_root=%#x", sr, b.Block().StateRoot())
log.Printf("BeaconState latest_block_header htr=%#xd, block htr=%#x", br, realBlockRoot)
return &OriginData{
st: s,
b: b,
sb: sb,
bb: bb,
vu: vu,
br: br,
sr: sr,
}, nil
}
// 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 beacon node flag to be used for validation.
type WeakSubjectivityData struct {
BlockRoot [32]byte
StateRoot [32]byte
Epoch types.Epoch
}
// CheckpointString returns the standard string representation of a Checkpoint.
// 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)
}
// ComputeWeakSubjectivityCheckpoint attempts to use the prysm weak_subjectivity api
// to obtain the current weak_subjectivity checkpoint.
// For non-prysm nodes, the same computation will be performed with extra steps,
// using the head state downloaded from the beacon node api.
func ComputeWeakSubjectivityCheckpoint(ctx context.Context, client *Client) (*WeakSubjectivityData, error) {
ws, err := client.GetWeakSubjectivity(ctx)
if err != nil {
// a 404/405 is expected if querying an endpoint that doesn't support the weak subjectivity checkpoint api
if !errors.Is(err, ErrNotOK) {
return nil, errors.Wrap(err, "unexpected API response for prysm-only weak subjectivity checkpoint API")
}
// fall back to vanilla Beacon Node API method
return computeBackwardsCompatible(ctx, client)
}
log.Printf("server weak subjectivity checkpoint response - epoch=%d, block_root=%#x, state_root=%#x", ws.Epoch, ws.BlockRoot, ws.StateRoot)
return ws, nil
}
const (
@@ -96,8 +153,8 @@ const (
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")
// 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
// we gather the necessary data for a checkpoint sync by:
@@ -105,14 +162,14 @@ var ErrUnsupportedPrysmCheckpointVersion = errors.New("node does not meet minimu
// - 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) {
func computeBackwardsCompatible(ctx context.Context, client *Client) (*WeakSubjectivityData, 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)
return nil, errors.Wrapf(errUnsupportedPrysmCheckpointVersion, "%s < minimum (%s)", nv.semver, prysmMinimumVersion)
}
epoch, err := getWeakSubjectivityEpochFromHead(ctx, client)
if err != nil {
@@ -127,136 +184,78 @@ func downloadBackwardsCompatible(ctx context.Context, client *Client) (*OriginDa
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))
sb, 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.FromState(stateBytes)
vu, err := detect.FromState(sb)
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.Config.ConfigName, version.String(cf.Fork))
log.Printf("detected supported config in checkpoint state, name=%s, fork=%s", vu.Config.ConfigName, version.String(vu.Fork))
st, err := cf.UnmarshalBeaconState(stateBytes)
s, err := vu.UnmarshalBeaconState(sb)
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)
sr, err := s.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()
h := s.LatestBlockHeader()
h.StateRoot = sr[:]
br, err := h.HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "error while computing block root using state data")
}
blockBytes, err := client.GetBlock(ctx, IdFromRoot(computedBlockRoot))
bb, err := client.GetBlock(ctx, IdFromRoot(br))
if err != nil {
return nil, errors.Wrapf(err, "error requesting block by root = %d", computedBlockRoot)
return nil, errors.Wrapf(err, "error requesting block by root = %d", br)
}
block, err := cf.UnmarshalBeaconBlock(blockBytes)
b, err := vu.UnmarshalBeaconBlock(bb)
if err != nil {
return nil, errors.Wrap(err, "unable to unmarshal block to a supported type using the detected fork schedule")
}
blockRoot, err := block.Block().HashTreeRoot()
br, err = b.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,
return &WeakSubjectivityData{
Epoch: epoch,
BlockRoot: br,
StateRoot: sr,
}, 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)
// 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 {
// a 404/405 is expected if querying an endpoint that doesn't support the weak subjectivity checkpoint api
if !errors.Is(err, ErrNotOK) {
return nil, errors.Wrap(err, "unexpected API response for prysm-only weak subjectivity checkpoint API")
}
// fall back to vanilla Beacon Node API method
return downloadBackwardsCompatible(ctx, client)
return 0, err
}
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)
vu, err := detect.FromState(headBytes)
if err != nil {
return nil, errors.Wrapf(err, "error computing first slot of epoch=%d", ws.Epoch)
return 0, errors.Wrap(err, "error detecting chain config for beacon state")
}
log.Printf("requesting checkpoint state at slot %d", slot)
stateBytes, err := client.GetState(ctx, IdFromSlot(slot))
log.Printf("detected supported config in remote head state, name=%s, fork=%s", vu.Config.ConfigName, version.String(vu.Fork))
headState, err := vu.UnmarshalBeaconState(headBytes)
if err != nil {
return nil, errors.Wrapf(err, "failed to request state by slot from api, slot=%d", slot)
}
cf, err := detect.FromState(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.Config.ConfigName, version.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)
return 0, errors.Wrap(err, "error unmarshaling state to correct version")
}
blockRoot, err := state.LatestBlockHeader().HashTreeRoot()
epoch, err := helpers.LatestWeakSubjectivityEpoch(ctx, headState, vu.Config)
if err != nil {
return nil, errors.Wrap(err, "error computing hash_tree_root of latest_block_header")
return 0, errors.Wrap(err, "error computing the weak subjectivity epoch from head state")
}
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
log.Printf("(computed client-side) weak subjectivity epoch = %d", epoch)
return epoch, nil
}

View File

@@ -93,8 +93,8 @@ func TestFallbackVersionCheck(t *testing.T) {
}}
ctx := context.Background()
_, err := DownloadOriginData(ctx, c)
require.ErrorIs(t, err, ErrUnsupportedPrysmCheckpointVersion)
_, err := ComputeWeakSubjectivityCheckpoint(ctx, c)
require.ErrorIs(t, err, errUnsupportedPrysmCheckpointVersion)
}
func TestFname(t *testing.T) {
@@ -120,9 +120,9 @@ func TestFname(t *testing.T) {
require.Equal(t, expected, actual)
}
func TestDownloadOriginData(t *testing.T) {
func TestDownloadWeakSubjectivityCheckpoint(t *testing.T) {
ctx := context.Background()
cfg := params.MainnetConfig()
cfg := params.MainnetConfig().Copy()
epoch := cfg.AltairForkEpoch - 1
// set up checkpoint state, using the epoch that will be computed as the ws checkpoint state based on the head state
@@ -204,19 +204,15 @@ func TestDownloadOriginData(t *testing.T) {
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
}
od, err := DownloadOriginData(ctx, c)
wsd, err := ComputeWeakSubjectivityCheckpoint(ctx, c)
require.NoError(t, err)
require.Equal(t, expectedWSD.Epoch, od.wsd.Epoch)
require.Equal(t, expectedWSD.StateRoot, od.wsd.StateRoot)
require.Equal(t, expectedWSD.BlockRoot, od.wsd.BlockRoot)
require.DeepEqual(t, wsSerialized, od.sb)
require.DeepEqual(t, serBlock, od.bb)
require.DeepEqual(t, wst.Fork().CurrentVersion, od.cf.Version[:])
require.DeepEqual(t, version.Phase0, od.cf.Fork)
require.Equal(t, expectedWSD.Epoch, wsd.Epoch)
require.Equal(t, expectedWSD.StateRoot, wsd.StateRoot)
require.Equal(t, expectedWSD.BlockRoot, wsd.BlockRoot)
}
// runs downloadBackwardsCompatible directly
// and via DownloadOriginData with a round tripper that triggers the backwards compatible code path
// runs computeBackwardsCompatible directly
// and via ComputeWeakSubjectivityCheckpoint with a round tripper that triggers the backwards compatible code path
func TestDownloadBackwardsCompatibleCombined(t *testing.T) {
ctx := context.Background()
cfg := params.MainnetConfig()
@@ -297,16 +293,12 @@ func TestDownloadBackwardsCompatibleCombined(t *testing.T) {
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
}
odPub, err := DownloadOriginData(ctx, c)
wsPub, err := ComputeWeakSubjectivityCheckpoint(ctx, c)
require.NoError(t, err)
odPriv, err := downloadBackwardsCompatible(ctx, c)
wsPriv, err := computeBackwardsCompatible(ctx, c)
require.NoError(t, err)
require.DeepEqual(t, odPriv.wsd, odPub.wsd)
require.DeepEqual(t, odPriv.sb, odPub.sb)
require.DeepEqual(t, odPriv.bb, odPub.bb)
require.DeepEqual(t, odPriv.cf.Fork, odPub.cf.Fork)
require.DeepEqual(t, odPriv.cf.Version, odPub.cf.Version)
require.DeepEqual(t, wsPriv, wsPub)
}
func TestGetWeakSubjectivityEpochFromHead(t *testing.T) {
@@ -402,3 +394,94 @@ func populateValidators(cfg *params.BeaconChainConfig, st state.BeaconState, val
return nil
}
func TestDownloadFinalizedData(t *testing.T) {
ctx := context.Background()
cfg := params.MainnetConfig().Copy()
// avoid the altair zone because genesis tests are easier to set up
epoch := cfg.AltairForkEpoch - 1
// set up checkpoint state, using the epoch that will be computed as the ws checkpoint state based on the head state
slot, err := slots.EpochStart(epoch)
require.NoError(t, err)
st, err := util.NewBeaconState()
require.NoError(t, err)
fork, err := forkForEpoch(cfg, epoch)
require.NoError(t, st.SetFork(fork))
// set up checkpoint block
b, err := wrapper.WrappedSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, wrapper.SetBlockParentRoot(b, cfg.ZeroHash))
require.NoError(t, wrapper.SetBlockSlot(b, slot))
require.NoError(t, wrapper.SetProposerIndex(b, 0))
// set up state header pointing at checkpoint block - this is how the block is downloaded by root
header, err := b.Header()
require.NoError(t, err)
require.NoError(t, st.SetLatestBlockHeader(header.Header))
// order of operations can be confusing here:
// - when computing the state root, make sure block header is complete, EXCEPT the state root should be zero-value
// - before computing the block root (to match the request route), the block should include the state root
// *computed from the state with a header that does not have a state root set yet*
sr, err := st.HashTreeRoot(ctx)
require.NoError(t, err)
require.NoError(t, wrapper.SetBlockStateRoot(b, sr))
mb, err := b.MarshalSSZ()
require.NoError(t, err)
br, err := b.Block().HashTreeRoot()
require.NoError(t, err)
ms, err := st.MarshalSSZ()
require.NoError(t, err)
hc := &http.Client{
Transport: &testRT{rt: func(req *http.Request) (*http.Response, error) {
res := &http.Response{Request: req}
switch req.URL.Path {
case renderGetStatePath(IdFinalized):
res.StatusCode = http.StatusOK
res.Body = io.NopCloser(bytes.NewBuffer(ms))
case renderGetBlockPath(IdFromRoot(br)):
res.StatusCode = http.StatusOK
res.Body = io.NopCloser(bytes.NewBuffer(mb))
default:
res.StatusCode = http.StatusInternalServerError
res.Body = io.NopCloser(bytes.NewBufferString(""))
}
return res, nil
}},
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
}
// sanity check before we go through checkpoint
// make sure we can download the state and unmarshal it with the VersionedUnmarshaler
sb, err := c.GetState(ctx, IdFinalized)
require.NoError(t, err)
require.Equal(t, true, bytes.Equal(sb, ms))
vu, err := detect.FromState(sb)
require.NoError(t, err)
us, err := vu.UnmarshalBeaconState(sb)
require.NoError(t, err)
ushtr, err := us.HashTreeRoot(ctx)
require.NoError(t, err)
require.Equal(t, sr, ushtr)
expected := &OriginData{
sb: ms,
bb: mb,
br: br,
sr: sr,
}
od, err := DownloadFinalizedData(ctx, c)
require.NoError(t, err)
require.Equal(t, true, bytes.Equal(expected.sb, od.sb))
require.Equal(t, true, bytes.Equal(expected.bb, od.bb))
require.Equal(t, expected.br, od.br)
require.Equal(t, expected.sr, od.sr)
}

View File

@@ -46,8 +46,9 @@ const (
type StateOrBlockId string
const (
IdGenesis StateOrBlockId = "genesis"
IdHead StateOrBlockId = "head"
IdGenesis StateOrBlockId = "genesis"
IdHead StateOrBlockId = "head"
IdFinalized StateOrBlockId = "finalized"
)
var ErrMalformedHostname = errors.New("hostname must include port, separated by one colon, like example.com:3500")
@@ -344,16 +345,6 @@ func (c *Client) GetWeakSubjectivity(ctx context.Context) (*WeakSubjectivityData
}, nil
}
// 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
}
func non200Err(response *http.Response) error {
bodyBytes, err := io.ReadAll(response.Body)
var body string

View File

@@ -118,7 +118,7 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
}
isValidPayload, err := s.notifyNewPayload(ctx, postStateVersion, postStateHeader, signed)
if err != nil {
return errors.Wrap(err, "could not verify new payload")
return fmt.Errorf("could not verify new payload: %v", err)
}
if isValidPayload {
if err := s.validateMergeTransitionBlock(ctx, preStateVersion, preStateHeader, signed); err != nil {

View File

@@ -129,6 +129,25 @@ func configureInteropConfig(cliCtx *cli.Context) error {
}
func configureExecutionSetting(cliCtx *cli.Context) error {
if cliCtx.IsSet(flags.TerminalTotalDifficultyOverride.Name) {
c := params.BeaconConfig()
c.TerminalTotalDifficulty = cliCtx.String(flags.TerminalTotalDifficultyOverride.Name)
log.WithField("terminal block difficult", c.TerminalTotalDifficulty).Warn("Terminal block difficult overridden")
params.OverrideBeaconConfig(c)
}
if cliCtx.IsSet(flags.TerminalBlockHashOverride.Name) {
c := params.BeaconConfig()
c.TerminalBlockHash = common.HexToHash(cliCtx.String(flags.TerminalBlockHashOverride.Name))
log.WithField("terminal block hash", c.TerminalBlockHash.Hex()).Warn("Terminal block hash overridden")
params.OverrideBeaconConfig(c)
}
if cliCtx.IsSet(flags.TerminalBlockHashActivationEpochOverride.Name) {
c := params.BeaconConfig()
c.TerminalBlockHashActivationEpoch = types.Epoch(cliCtx.Uint64(flags.TerminalBlockHashActivationEpochOverride.Name))
log.WithField("terminal block hash activation epoch", c.TerminalBlockHashActivationEpoch).Warn("Terminal block hash activation epoch overridden")
params.OverrideBeaconConfig(c)
}
if !cliCtx.IsSet(flags.SuggestedFeeRecipient.Name) {
return nil
}

View File

@@ -92,6 +92,13 @@ func TestConfigureExecutionSetting(t *testing.T) {
app := cli.App{}
set := flag.NewFlagSet("test", 0)
set.String(flags.SuggestedFeeRecipient.Name, "", "")
set.Uint64(flags.TerminalTotalDifficultyOverride.Name, 0, "")
set.String(flags.TerminalBlockHashOverride.Name, "", "")
set.Uint64(flags.TerminalBlockHashActivationEpochOverride.Name, 0, "")
require.NoError(t, set.Set(flags.TerminalTotalDifficultyOverride.Name, strconv.Itoa(100)))
require.NoError(t, set.Set(flags.TerminalBlockHashOverride.Name, "0xA"))
require.NoError(t, set.Set(flags.TerminalBlockHashActivationEpochOverride.Name, strconv.Itoa(200)))
require.NoError(t, set.Set(flags.SuggestedFeeRecipient.Name, "0xB"))
cliCtx := cli.NewContext(&app, set, nil)
err := configureExecutionSetting(cliCtx)
@@ -112,6 +119,10 @@ func TestConfigureExecutionSetting(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, common.HexToAddress("0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa"), params.BeaconConfig().DefaultFeeRecipient)
assert.Equal(t, "100", params.BeaconConfig().TerminalTotalDifficulty)
assert.Equal(t, common.HexToHash("0xA"), params.BeaconConfig().TerminalBlockHash)
assert.Equal(t, types.Epoch(200), params.BeaconConfig().TerminalBlockHashActivationEpoch)
}
func TestConfigureNetwork(t *testing.T) {

View File

@@ -296,7 +296,7 @@ func handleRPCError(err error) error {
return nil
}
if isTimeout(err) {
return errors.Wrapf(ErrHTTPTimeout, "%s", err)
return ErrHTTPTimeout
}
e, ok := err.(rpc.Error)
if !ok {
@@ -307,7 +307,7 @@ func handleRPCError(err error) error {
"here https://docs.prylabs.network/docs/execution-node/authentication")
return fmt.Errorf("could not authenticate connection to execution client: %v", err)
}
return errors.Wrap(err, "got an unexpected error")
return fmt.Errorf("got an unexpected error in JSON-RPC response: %v", err)
}
switch e.ErrorCode() {
case -32700:
@@ -330,7 +330,7 @@ func handleRPCError(err error) error {
// Only -32000 status codes are data errors in the RPC specification.
errWithData, ok := err.(rpc.DataError)
if !ok {
return errors.Wrap(err, "got an unexpected error")
return fmt.Errorf("got an unexpected error in JSON-RPC response: %v", err)
}
return errors.Wrapf(ErrServer, "%v", errWithData.ErrorData())
default:

View File

@@ -16,14 +16,14 @@ var (
getPayloadLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "get_payload_v1_latency_milliseconds",
Help: "Captures RPC latency for newPayloadV1 in milliseconds",
Help: "Captures RPC latency for getPayloadV1 in milliseconds",
Buckets: []float64{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000},
},
)
forkchoiceUpdatedLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "forkchoice_updated_v1_latency_milliseconds",
Help: "Captures RPC latency for newPayloadV1 in milliseconds",
Help: "Captures RPC latency for forkchoiceUpdatedV1 in milliseconds",
Buckets: []float64{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000},
},
)

View File

@@ -123,6 +123,9 @@ func TestGetSpec(t *testing.T) {
var daap [4]byte
copy(daap[:], []byte{'0', '0', '0', '7'})
config.DomainAggregateAndProof = daap
var dam [4]byte
copy(dam[:], []byte{'1', '0', '0', '0'})
config.DomainApplicationMask = dam
params.OverrideBeaconConfig(config)
@@ -130,7 +133,7 @@ func TestGetSpec(t *testing.T) {
resp, err := server.GetSpec(context.Background(), &emptypb.Empty{})
require.NoError(t, err)
assert.Equal(t, 98, len(resp.Data))
assert.Equal(t, 99, len(resp.Data))
for k, v := range resp.Data {
switch k {
case "CONFIG_NAME":
@@ -315,6 +318,8 @@ func TestGetSpec(t *testing.T) {
assert.Equal(t, "0x30303036", v)
case "DOMAIN_AGGREGATE_AND_PROOF":
assert.Equal(t, "0x30303037", v)
case "DOMAIN_APPLICATION_MASK":
assert.Equal(t, "0x31303030", v)
case "DOMAIN_SYNC_COMMITTEE":
assert.Equal(t, "0x07000000", v)
case "DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF":

View File

@@ -29,6 +29,7 @@ go_test(
"field_trie_test.go",
"helpers_test.go",
],
data = glob(["testdata/**"]),
embed = [":go_default_library"],
deps = [
"//beacon-chain/state/stateutil:go_default_library",

View File

@@ -10,6 +10,11 @@ import (
pmath "github.com/prysmaticlabs/prysm/math"
)
var (
ErrInvalidFieldTrie = errors.New("invalid field trie")
ErrEmptyFieldTrie = errors.New("empty field trie")
)
// FieldTrie is the representation of the representative
// trie of the particular field.
type FieldTrie struct {
@@ -188,6 +193,12 @@ func (f *FieldTrie) CopyTrie() *FieldTrie {
// TrieRoot returns the corresponding root of the trie.
func (f *FieldTrie) TrieRoot() ([32]byte, error) {
if f.Empty() {
return [32]byte{}, ErrEmptyFieldTrie
}
if len(f.fieldLayers[len(f.fieldLayers)-1]) == 0 {
return [32]byte{}, ErrInvalidFieldTrie
}
switch f.dataType {
case types.BasicArray:
return *f.fieldLayers[len(f.fieldLayers)-1][0], nil

View File

@@ -27,6 +27,13 @@ func TestFieldTrie_NewTrie(t *testing.T) {
assert.Equal(t, root, newRoot)
}
func TestFieldTrie_NewTrie_NilElements(t *testing.T) {
trie, err := fieldtrie.NewFieldTrie(stateTypes.FieldIndex(5), stateTypes.BasicArray, nil, 8234)
require.NoError(t, err)
_, err = trie.TrieRoot()
require.ErrorIs(t, err, fieldtrie.ErrEmptyFieldTrie)
}
func TestFieldTrie_RecomputeTrie(t *testing.T) {
newState, _ := util.DeterministicGenesisState(t, 32)
// 10 represents the enum value of validators
@@ -77,3 +84,27 @@ func TestFieldTrie_CopyTrieImmutable(t *testing.T) {
t.Errorf("Wanted roots to be different, but they are the same: %#x", root)
}
}
func FuzzFieldTrie(f *testing.F) {
newState, _ := util.DeterministicGenesisState(f, 40)
var data []byte
for _, root := range newState.StateRoots() {
data = append(data, root...)
}
f.Add(5, int(stateTypes.BasicArray), data, uint64(params.BeaconConfig().SlotsPerHistoricalRoot))
f.Fuzz(func(t *testing.T, idx, typ int, data []byte, slotsPerHistRoot uint64) {
var roots [][]byte
for i := 32; i < len(data); i += 32 {
roots = append(roots, data[i-32:i])
}
trie, err := fieldtrie.NewFieldTrie(stateTypes.FieldIndex(idx), stateTypes.DataType(typ), roots, slotsPerHistRoot)
if err != nil {
return // invalid inputs
}
_, err = trie.TrieRoot()
if err != nil {
return
}
})
}

View File

@@ -0,0 +1,5 @@
go test fuzz v1
int(5)
int(0)
[]byte("")
uint64(8234)

View File

@@ -27,7 +27,7 @@ func NewAPIInitializer(beaconNodeHost string) (*APIInitializer, error) {
// Initialize downloads origin state and block for checkpoint sync and initializes database records to
// prepare the node to begin syncing from that point.
func (dl *APIInitializer) Initialize(ctx context.Context, d db.Database) error {
od, err := beacon.DownloadOriginData(ctx, dl.c)
od, err := beacon.DownloadFinalizedData(ctx, dl.c)
if err != nil {
return errors.Wrap(err, "Error retrieving checkpoint origin state and block")
}

View File

@@ -132,6 +132,8 @@ type Service struct {
seenSyncContributionCache *lru.Cache
badBlockCache *lru.Cache
badBlockLock sync.RWMutex
syncContributionBitsOverlapLock sync.RWMutex
syncContributionBitsOverlapCache *lru.Cache
signatureChan chan *signatureVerifier
}
@@ -221,6 +223,7 @@ func (s *Service) initCaches() {
s.seenUnAggregatedAttestationCache = lruwrpr.New(seenUnaggregatedAttSize)
s.seenSyncMessageCache = lruwrpr.New(seenSyncMsgSize)
s.seenSyncContributionCache = lruwrpr.New(seenSyncContributionSize)
s.syncContributionBitsOverlapCache = lruwrpr.New(seenSyncContributionSize)
s.seenExitCache = lruwrpr.New(seenExitSize)
s.seenAttesterSlashingCache = make(map[uint64]bool)
s.seenProposerSlashingCache = lruwrpr.New(seenProposerSlashingSize)

View File

@@ -252,7 +252,7 @@ func (s *Service) setSeenCommitteeIndicesSlot(slot types.Slot, committeeID types
s.seenUnAggregatedAttestationLock.Lock()
defer s.seenUnAggregatedAttestationLock.Unlock()
b := append(bytesutil.Bytes32(uint64(slot)), bytesutil.Bytes32(uint64(committeeID))...)
b = append(b, aggregateBits...)
b = append(b, bytesutil.SafeCopyBytes(aggregateBits)...)
s.seenUnAggregatedAttestationCache.Add(string(b), true)
}

View File

@@ -339,3 +339,30 @@ func TestServiceValidateCommitteeIndexBeaconAttestation_Optimistic(t *testing.T)
valid := res == pubsub.ValidationIgnore
assert.Equal(t, true, valid, "Should have ignore this message")
}
func TestService_setSeenCommitteeIndicesSlot(t *testing.T) {
chainService := &mockChain.ChainService{
Genesis: time.Now(),
ValidatorsRoot: [32]byte{'A'},
}
s := NewService(context.Background(), WithP2P(p2ptest.NewTestP2P(t)), WithStateNotifier(chainService.StateNotifier()))
s.initCaches()
// Empty cache
b0 := []byte{9} // 1001
require.Equal(t, false, s.hasSeenCommitteeIndicesSlot(0, 0, b0))
// Cache some entries but same key
s.setSeenCommitteeIndicesSlot(0, 0, b0)
require.Equal(t, true, s.hasSeenCommitteeIndicesSlot(0, 0, b0))
b1 := []byte{14} // 1110
s.setSeenCommitteeIndicesSlot(0, 0, b1)
require.Equal(t, true, s.hasSeenCommitteeIndicesSlot(0, 0, b0))
require.Equal(t, true, s.hasSeenCommitteeIndicesSlot(0, 0, b1))
// Cache some entries with diff keys
s.setSeenCommitteeIndicesSlot(1, 2, b1)
require.Equal(t, false, s.hasSeenCommitteeIndicesSlot(1, 0, b1))
require.Equal(t, false, s.hasSeenCommitteeIndicesSlot(0, 2, b1))
require.Equal(t, true, s.hasSeenCommitteeIndicesSlot(1, 2, b1))
}

View File

@@ -88,7 +88,11 @@ func (s *Service) validateSyncContributionAndProof(ctx context.Context, pid peer
return result, err
}
s.setSyncContributionIndexSlotSeen(m.Message.Contribution.Slot, m.Message.AggregatorIndex, types.CommitteeIndex(m.Message.Contribution.SubcommitteeIndex))
con := m.Message.Contribution
if err := s.setSyncContributionBits(con); err != nil {
return pubsub.ValidationIgnore, err
}
s.setSyncContributionIndexSlotSeen(con.Slot, m.Message.AggregatorIndex, types.CommitteeIndex(con.SubcommitteeIndex))
msg.ValidatorData = m
@@ -149,7 +153,15 @@ func rejectEmptyContribution(m *ethpb.SignedContributionAndProof) validationFn {
func (s *Service) ignoreSeenSyncContribution(m *ethpb.SignedContributionAndProof) validationFn {
return func(ctx context.Context) (pubsub.ValidationResult, error) {
seen := s.hasSeenSyncContributionIndexSlot(m.Message.Contribution.Slot, m.Message.AggregatorIndex, types.CommitteeIndex(m.Message.Contribution.SubcommitteeIndex))
c := m.Message.Contribution
seen, err := s.hasSeenSyncContributionBits(c)
if err != nil {
return pubsub.ValidationIgnore, err
}
if seen {
return pubsub.ValidationIgnore, nil
}
seen = s.hasSeenSyncContributionIndexSlot(c.Slot, m.Message.AggregatorIndex, types.CommitteeIndex(c.SubcommitteeIndex))
if seen {
return pubsub.ValidationIgnore, nil
}
@@ -318,6 +330,68 @@ func (s *Service) setSyncContributionIndexSlotSeen(slot types.Slot, aggregatorIn
s.seenSyncContributionCache.Add(string(b), true)
}
// Set sync contribution's slot, root, committee index and bits.
func (s *Service) setSyncContributionBits(c *ethpb.SyncCommitteeContribution) error {
s.syncContributionBitsOverlapLock.Lock()
defer s.syncContributionBitsOverlapLock.Unlock()
// Copying due to how pb unmarshalling is carried out, prevent mutation.
b := append(bytesutil.SafeCopyBytes(c.BlockRoot), bytesutil.Bytes32(uint64(c.Slot))...)
b = append(b, bytesutil.Bytes32(c.SubcommitteeIndex)...)
v, ok := s.syncContributionBitsOverlapCache.Get(string(b))
if !ok {
s.syncContributionBitsOverlapCache.Add(string(b), [][]byte{c.AggregationBits.Bytes()})
return nil
}
bitsList, ok := v.([][]byte)
if !ok {
return errors.New("could not covert cached value to []bitfield.Bitvector")
}
has, err := bitListOverlaps(bitsList, c.AggregationBits)
if err != nil {
return err
}
if has {
return nil
}
s.syncContributionBitsOverlapCache.Add(string(b), append(bitsList, c.AggregationBits.Bytes()))
return nil
}
// Check sync contribution bits don't have an overlap with one's in cache.
func (s *Service) hasSeenSyncContributionBits(c *ethpb.SyncCommitteeContribution) (bool, error) {
s.syncContributionBitsOverlapLock.RLock()
defer s.syncContributionBitsOverlapLock.RUnlock()
b := append(c.BlockRoot, bytesutil.Bytes32(uint64(c.Slot))...)
b = append(b, bytesutil.Bytes32(c.SubcommitteeIndex)...)
v, ok := s.syncContributionBitsOverlapCache.Get(string(b))
if !ok {
return false, nil
}
bitsList, ok := v.([][]byte)
if !ok {
return false, errors.New("could not covert cached value to []bitfield.Bitvector128")
}
return bitListOverlaps(bitsList, c.AggregationBits.Bytes())
}
// bitListOverlaps returns true if there's an overlap between two bitlists.
func bitListOverlaps(bitLists [][]byte, b []byte) (bool, error) {
for _, bitList := range bitLists {
if bitList == nil {
return false, errors.New("nil bitfield")
}
bl := ethpb.ConvertToSyncContributionBitVector(bitList)
overlaps, err := bl.Overlaps(ethpb.ConvertToSyncContributionBitVector(b))
if err != nil {
return false, err
}
if overlaps {
return true, nil
}
}
return false, nil
}
// verifySyncSelectionData verifies that the provided sync contribution has a valid
// selection proof.
func (s *Service) verifySyncSelectionData(ctx context.Context, m *ethpb.ContributionAndProof) error {

View File

@@ -1095,3 +1095,121 @@ func syncSelectionProofSigningRoot(st state.BeaconState, slot types.Slot, comIdx
selectionData := &ethpb.SyncAggregatorSelectionData{Slot: slot, SubcommitteeIndex: uint64(comIdx)}
return signing.ComputeSigningRoot(selectionData, dom)
}
func TestService_setSyncContributionIndexSlotSeen(t *testing.T) {
chainService := &mockChain.ChainService{
Genesis: time.Now(),
ValidatorsRoot: [32]byte{'A'},
}
s := NewService(context.Background(), WithP2P(mockp2p.NewTestP2P(t)), WithStateNotifier(chainService.StateNotifier()))
s.initCaches()
// Empty cache
b0 := bitfield.NewBitvector128()
b0.SetBitAt(0, true)
has, err := s.hasSeenSyncContributionBits(&ethpb.SyncCommitteeContribution{
AggregationBits: b0,
})
require.NoError(t, err)
require.Equal(t, false, has)
// Cache with entries but same key
require.NoError(t, s.setSyncContributionBits(&ethpb.SyncCommitteeContribution{
AggregationBits: b0,
}))
has, err = s.hasSeenSyncContributionBits(&ethpb.SyncCommitteeContribution{
AggregationBits: b0,
})
require.NoError(t, err)
require.Equal(t, true, has)
b1 := bitfield.NewBitvector128()
b1.SetBitAt(1, true)
has, err = s.hasSeenSyncContributionBits(&ethpb.SyncCommitteeContribution{
AggregationBits: b1,
})
require.NoError(t, err)
require.Equal(t, false, has)
b2 := bitfield.NewBitvector128()
b2.SetBitAt(1, true)
b2.SetBitAt(2, true)
has, err = s.hasSeenSyncContributionBits(&ethpb.SyncCommitteeContribution{
AggregationBits: b2,
})
require.NoError(t, err)
require.Equal(t, false, has)
b2.SetBitAt(0, true)
has, err = s.hasSeenSyncContributionBits(&ethpb.SyncCommitteeContribution{
AggregationBits: b2,
})
require.NoError(t, err)
require.Equal(t, true, has)
// Make sure set doesn't contain existing overlaps
require.Equal(t, 1, s.syncContributionBitsOverlapCache.Len())
// Cache with entries but different key
has, err = s.hasSeenSyncContributionBits(&ethpb.SyncCommitteeContribution{
Slot: 1,
SubcommitteeIndex: 2,
BlockRoot: []byte{'A'},
AggregationBits: b0,
})
require.NoError(t, err)
require.Equal(t, false, has)
require.NoError(t, s.setSyncContributionBits(&ethpb.SyncCommitteeContribution{
Slot: 1,
SubcommitteeIndex: 2,
BlockRoot: []byte{'A'},
AggregationBits: b2,
}))
has, err = s.hasSeenSyncContributionBits(&ethpb.SyncCommitteeContribution{
Slot: 1,
SubcommitteeIndex: 2,
BlockRoot: []byte{'A'},
AggregationBits: b0,
})
require.NoError(t, err)
require.Equal(t, true, has)
has, err = s.hasSeenSyncContributionBits(&ethpb.SyncCommitteeContribution{
Slot: 1,
SubcommitteeIndex: 2,
BlockRoot: []byte{'A'},
AggregationBits: b1,
})
require.NoError(t, err)
require.Equal(t, true, has)
has, err = s.hasSeenSyncContributionBits(&ethpb.SyncCommitteeContribution{
Slot: 1,
SubcommitteeIndex: 2,
BlockRoot: []byte{'A'},
AggregationBits: b2,
})
require.NoError(t, err)
require.Equal(t, true, has)
// Check invariant with the keys
has, err = s.hasSeenSyncContributionBits(&ethpb.SyncCommitteeContribution{
Slot: 2,
SubcommitteeIndex: 2,
BlockRoot: []byte{'A'},
AggregationBits: b0,
})
require.NoError(t, err)
require.Equal(t, false, has)
has, err = s.hasSeenSyncContributionBits(&ethpb.SyncCommitteeContribution{
Slot: 1,
SubcommitteeIndex: 2,
BlockRoot: []byte{'B'},
AggregationBits: b0,
})
require.NoError(t, err)
require.Equal(t, false, has)
has, err = s.hasSeenSyncContributionBits(&ethpb.SyncCommitteeContribution{
Slot: 1,
SubcommitteeIndex: 3,
BlockRoot: []byte{'A'},
AggregationBits: b0,
})
require.NoError(t, err)
require.Equal(t, false, has)
}

View File

@@ -211,4 +211,25 @@ var (
Usage: "Post bellatrix, this address will receive the transaction fees produced by any blocks from this node. Default to junk whilst bellatrix is in development state. Validator client can override this value through the preparebeaconproposer api.",
Value: fieldparams.EthBurnAddressHex,
}
// TerminalTotalDifficultyOverride specifies the total difficulty to manual overrides the `TERMINAL_TOTAL_DIFFICULTY` parameter.
TerminalTotalDifficultyOverride = &cli.StringFlag{
Name: "terminal-total-difficulty-override",
Usage: "Sets the total difficulty to manual overrides the default TERMINAL_TOTAL_DIFFICULTY value. " +
"WARNING: This flag should be used only if you have a clear understanding that community has decided to override the terminal difficulty. " +
"Incorrect usage will result in your node experience consensus failure.",
}
// TerminalBlockHashOverride specifies the terminal block hash to manual overrides the `TERMINAL_BLOCK_HASH` parameter.
TerminalBlockHashOverride = &cli.StringFlag{
Name: "terminal-block-hash-override",
Usage: "Sets the block hash to manual overrides the default TERMINAL_BLOCK_HASH value. " +
"WARNING: This flag should be used only if you have a clear understanding that community has decided to override the terminal block hash. " +
"Incorrect usage will result in your node experience consensus failure.",
}
// TerminalBlockHashActivationEpochOverride specifies the terminal block hash epoch to manual overrides the `TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH` parameter.
TerminalBlockHashActivationEpochOverride = &cli.Uint64Flag{
Name: "terminal-block-hash-epoch-override",
Usage: "Sets the block hash epoch to manual overrides the default TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH value. " +
"WARNING: This flag should be used only if you have a clear understanding that community has decided to override the terminal block hash activation epoch. " +
"Incorrect usage will result in your node experience consensus failure.",
}
)

View File

@@ -69,6 +69,9 @@ var appFlags = []cli.Flag{
flags.Eth1HeaderReqLimit,
flags.MinPeersPerSubnet,
flags.SuggestedFeeRecipient,
flags.TerminalTotalDifficultyOverride,
flags.TerminalBlockHashOverride,
flags.TerminalBlockHashActivationEpochOverride,
cmd.EnableBackupWebhookFlag,
cmd.BackupWebhookOutputDir,
cmd.MinimalConfigFlag,

View File

@@ -137,6 +137,9 @@ var appHelpFlagGroups = []flagGroup{
Name: "merge",
Flags: []cli.Flag{
flags.SuggestedFeeRecipient,
flags.TerminalTotalDifficultyOverride,
flags.TerminalBlockHashOverride,
flags.TerminalBlockHashActivationEpochOverride,
},
},
{

View File

@@ -44,12 +44,12 @@ func cliActionLatest(_ *cli.Context) error {
return err
}
od, err := beacon.DownloadOriginData(ctx, client)
ws, err := beacon.ComputeWeakSubjectivityCheckpoint(ctx, client)
if err != nil {
return err
}
fmt.Println("\nUse the following flag when starting a prysm Beacon Node to ensure the chain history " +
"includes the Weak Subjectivity Checkpoint: ")
fmt.Printf("--weak-subjectivity-checkpoint=%s\n\n", od.CheckpointString())
fmt.Printf("--weak-subjectivity-checkpoint=%s\n\n", ws.CheckpointString())
return nil
}

View File

@@ -17,7 +17,7 @@ var saveFlags = struct {
var saveCmd = &cli.Command{
Name: "save",
Usage: "Query for the current weak subjectivity period epoch, then download the corresponding state and block. To be used for checkpoint sync.",
Usage: "Save the latest finalized header and the most recent block it integrates. To be used for checkpoint sync.",
Action: cliActionSave,
Flags: []cli.Flag{
&cli.StringFlag{
@@ -50,7 +50,7 @@ func cliActionSave(_ *cli.Context) error {
return err
}
od, err := beacon.DownloadOriginData(ctx, client)
od, err := beacon.DownloadFinalizedData(ctx, client)
if err != nil {
return err
}

View File

@@ -110,6 +110,7 @@ type BeaconChainConfig struct {
DomainSyncCommittee [4]byte `yaml:"DOMAIN_SYNC_COMMITTEE" spec:"true"` // DomainVoluntaryExit defines the BLS signature domain for sync committee.
DomainSyncCommitteeSelectionProof [4]byte `yaml:"DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF" spec:"true"` // DomainSelectionProof defines the BLS signature domain for sync committee selection proof.
DomainContributionAndProof [4]byte `yaml:"DOMAIN_CONTRIBUTION_AND_PROOF" spec:"true"` // DomainAggregateAndProof defines the BLS signature domain for contribution and proof.
DomainApplicationMask [4]byte `yaml:"DOMAIN_APPLICATION_MASK" spec:"true"` // DomainApplicationMask defines the BLS signature domain for application mask.
// Prysm constants.
GweiPerEth uint64 // GweiPerEth is the amount of gwei corresponding to 1 eth.

View File

@@ -155,16 +155,17 @@ var mainnetBeaconConfig = &BeaconChainConfig{
MaxVoluntaryExits: 16,
// BLS domain values.
DomainBeaconProposer: bytesutil.ToBytes4(bytesutil.Bytes4(0)),
DomainBeaconAttester: bytesutil.ToBytes4(bytesutil.Bytes4(1)),
DomainRandao: bytesutil.ToBytes4(bytesutil.Bytes4(2)),
DomainDeposit: bytesutil.ToBytes4(bytesutil.Bytes4(3)),
DomainVoluntaryExit: bytesutil.ToBytes4(bytesutil.Bytes4(4)),
DomainSelectionProof: bytesutil.ToBytes4(bytesutil.Bytes4(5)),
DomainAggregateAndProof: bytesutil.ToBytes4(bytesutil.Bytes4(6)),
DomainSyncCommittee: bytesutil.ToBytes4(bytesutil.Bytes4(7)),
DomainSyncCommitteeSelectionProof: bytesutil.ToBytes4(bytesutil.Bytes4(8)),
DomainContributionAndProof: bytesutil.ToBytes4(bytesutil.Bytes4(9)),
DomainBeaconProposer: bytesutil.Uint32ToBytes4(0x00000000),
DomainBeaconAttester: bytesutil.Uint32ToBytes4(0x01000000),
DomainRandao: bytesutil.Uint32ToBytes4(0x02000000),
DomainDeposit: bytesutil.Uint32ToBytes4(0x03000000),
DomainVoluntaryExit: bytesutil.Uint32ToBytes4(0x04000000),
DomainSelectionProof: bytesutil.Uint32ToBytes4(0x05000000),
DomainAggregateAndProof: bytesutil.Uint32ToBytes4(0x06000000),
DomainSyncCommittee: bytesutil.Uint32ToBytes4(0x07000000),
DomainSyncCommitteeSelectionProof: bytesutil.Uint32ToBytes4(0x08000000),
DomainContributionAndProof: bytesutil.Uint32ToBytes4(0x09000000),
DomainApplicationMask: bytesutil.Uint32ToBytes4(0x00000001),
// Prysm constants.
GweiPerEth: 1000000000,

View File

@@ -32,7 +32,7 @@ func RopstenConfig() *BeaconChainConfig {
cfg.AltairForkVersion = []byte{0x80, 0x00, 0x00, 0x70}
cfg.BellatrixForkEpoch = 750
cfg.BellatrixForkVersion = []byte{0x80, 0x00, 0x00, 0x71}
cfg.TerminalTotalDifficulty = "43531756765713534"
cfg.TerminalTotalDifficulty = "100000000000000000000000"
cfg.DepositContractAddress = "0x6f22fFbC56eFF051aECF839396DD1eD9aD6BBA9D"
cfg.InitializeForkSchedule()
return cfg

View File

@@ -1247,7 +1247,7 @@ def prysm_deps():
go_repository(
name = "com_github_gogo_protobuf",
importpath = "github.com/gogo/protobuf",
commit = "b03c65ea87cdc3521ede29f62fe3ce239267c1bc",
sum = "h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=",
version = "v1.3.2",
)
@@ -4504,8 +4504,8 @@ def prysm_deps():
go_repository(
name = "org_golang_x_mod",
importpath = "golang.org/x/mod",
sum = "h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=",
version = "v0.6.0-dev.0.20220106191415-9b9b3d81d5e3",
sum = "h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=",
version = "v0.6.0-dev.0.20220419223038-86c51ed26bb4",
)
go_repository(
@@ -4561,8 +4561,8 @@ def prysm_deps():
go_repository(
name = "org_golang_x_tools",
importpath = "golang.org/x/tools",
sum = "h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=",
version = "v0.1.10",
sum = "h1:FsZrnczqPMLkWZqyqBPGYJTko7xFkWqyypB7a4Xldkw=",
version = "v0.1.11-0.20220523181440-ccb10502d1a5",
)
go_repository(

View File

@@ -339,6 +339,14 @@ func HighestBitIndexAt(b []byte, index int) (int, error) {
return 0, nil
}
// Uint32ToBytes4 is a convenience method for converting uint32 to a fix
// sized 4 byte array in big endian order. Returns 4 byte array.
func Uint32ToBytes4(i uint32) [4]byte {
buf := make([]byte, 4)
binary.BigEndian.PutUint32(buf, i)
return ToBytes4(buf)
}
// Uint64ToBytesLittleEndian conversion.
func Uint64ToBytesLittleEndian(i uint64) []byte {
buf := make([]byte, 8)

View File

@@ -2,6 +2,7 @@ package bytesutil_test
import (
"bytes"
"fmt"
"reflect"
"testing"
@@ -582,3 +583,26 @@ func TestIsValidRoot(t *testing.T) {
})
}
}
func TestUint32ToBytes4(t *testing.T) {
tests := []struct {
value uint32
want [4]byte
}{
{
value: 0x01000000,
want: [4]byte{1, 0, 0, 0},
},
{
value: 0x00000001,
want: [4]byte{0, 0, 0, 1},
},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("0x%08x", tt.value), func(t *testing.T) {
if got := bytesutil.Uint32ToBytes4(tt.value); !bytes.Equal(got[:], tt.want[:]) {
t.Errorf("Uint32ToBytes4() = %v, want %v", got, tt.want)
}
})
}
}

5
go.mod
View File

@@ -87,9 +87,9 @@ require (
go.uber.org/automaxprocs v1.3.0
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064
golang.org/x/exp v0.0.0-20200513190911-00229845015e
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/tools v0.1.10
golang.org/x/tools v0.1.11-0.20220523181440-ccb10502d1a5
google.golang.org/genproto v0.0.0-20210426193834-eac7f76ac494
google.golang.org/grpc v1.40.0
google.golang.org/protobuf v1.28.0
@@ -242,7 +242,6 @@ require (
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect

8
go.sum
View File

@@ -1534,8 +1534,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1823,8 +1823,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.11-0.20220523181440-ccb10502d1a5 h1:FsZrnczqPMLkWZqyqBPGYJTko7xFkWqyypB7a4Xldkw=
golang.org/x/tools v0.1.11-0.20220523181440-ccb10502d1a5/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -10,3 +10,7 @@ import (
func NewSyncCommitteeAggregationBits() bitfield.Bitvector128 {
return bitfield.NewBitvector128()
}
func ConvertToSyncContributionBitVector(b []byte) bitfield.Bitvector128 {
return b
}

View File

@@ -10,3 +10,7 @@ import (
func NewSyncCommitteeAggregationBits() bitfield.Bitvector8 {
return bitfield.NewBitvector8()
}
func ConvertToSyncContributionBitVector(b []byte) bitfield.Bitvector8 {
return b
}

View File

@@ -5,6 +5,42 @@ load("@prysm//tools/go:def.bzl", "go_test")
# gazelle:exclude mainnet_scenario_e2e_test.go
# gazelle:exclude minimal_scenario_e2e_test.go
common_deps = [
"//api/client/beacon:go_default_library",
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/db/testing:go_default_library",
"//beacon-chain/operations/slashings/mock:go_default_library",
"//beacon-chain/state/stategen/mock:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/bytesutil:go_default_library",
"//io/file:go_default_library",
"//proto/eth/service:go_default_library",
"//proto/eth/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/endtoend/components:go_default_library",
"//testing/endtoend/components/eth1:go_default_library",
"//testing/endtoend/evaluators:go_default_library",
"//testing/endtoend/helpers:go_default_library",
"//testing/endtoend/params:go_default_library",
"//testing/endtoend/types:go_default_library",
"//testing/require:go_default_library",
"//testing/slasher/simulator:go_default_library",
"//testing/util:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
"@org_golang_x_sync//errgroup:go_default_library",
]
# gazelle:ignore
go_test(
name = "go_default_test",
size = "large",
@@ -35,33 +71,7 @@ go_test(
"minimal",
"requires-network",
],
deps = [
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/db/testing:go_default_library",
"//beacon-chain/operations/slashings/mock:go_default_library",
"//beacon-chain/state/stategen/mock:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//crypto/bls:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/endtoend/components:go_default_library",
"//testing/endtoend/components/eth1:go_default_library",
"//testing/endtoend/evaluators:go_default_library",
"//testing/endtoend/helpers:go_default_library",
"//testing/endtoend/params:go_default_library",
"//testing/endtoend/types:go_default_library",
"//testing/require:go_default_library",
"//testing/slasher/simulator:go_default_library",
"//testing/util:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
"@org_golang_x_sync//errgroup:go_default_library",
],
deps = common_deps,
)
go_test(
@@ -93,34 +103,7 @@ go_test(
"manual",
"requires-network",
],
deps = [
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/db/testing:go_default_library",
"//beacon-chain/operations/slashings:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//build/bazel:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//crypto/bls:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/endtoend/components:go_default_library",
"//testing/endtoend/components/eth1:go_default_library",
"//testing/endtoend/evaluators:go_default_library",
"//testing/endtoend/helpers:go_default_library",
"//testing/endtoend/params:go_default_library",
"//testing/endtoend/types:go_default_library",
"//testing/require:go_default_library",
"//testing/slasher/simulator:go_default_library",
"//testing/util:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
"@org_golang_x_sync//errgroup:go_default_library",
],
deps = common_deps,
)
go_test(
@@ -152,34 +135,7 @@ go_test(
"requires-network",
"scenario",
],
deps = [
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/db/testing:go_default_library",
"//beacon-chain/operations/slashings:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//build/bazel:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//crypto/bls:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/endtoend/components:go_default_library",
"//testing/endtoend/components/eth1:go_default_library",
"//testing/endtoend/evaluators:go_default_library",
"//testing/endtoend/helpers:go_default_library",
"//testing/endtoend/params:go_default_library",
"//testing/endtoend/types:go_default_library",
"//testing/require:go_default_library",
"//testing/slasher/simulator:go_default_library",
"//testing/util:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
"@org_golang_x_sync//errgroup:go_default_library",
],
deps = common_deps,
)
go_test(
@@ -211,32 +167,5 @@ go_test(
"requires-network",
"scenario",
],
deps = [
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/db/testing:go_default_library",
"//beacon-chain/operations/slashings:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//build/bazel:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//crypto/bls:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/endtoend/components:go_default_library",
"//testing/endtoend/components/eth1:go_default_library",
"//testing/endtoend/evaluators:go_default_library",
"//testing/endtoend/helpers:go_default_library",
"//testing/endtoend/params:go_default_library",
"//testing/endtoend/types:go_default_library",
"//testing/require:go_default_library",
"//testing/slasher/simulator:go_default_library",
"//testing/util:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
"@org_golang_x_sync//errgroup:go_default_library",
],
deps = common_deps,
)

View File

@@ -129,6 +129,9 @@ func (c *componentHandler) setup() {
return nil
})
if config.TestCheckpointSync {
appendDebugEndpoints(config)
}
// Beacon nodes.
beaconNodes := components.NewBeaconNodes(config)
g.Go(func() error {
@@ -201,3 +204,11 @@ func (c *componentHandler) required() []e2etypes.ComponentRunner {
}
return requiredComponents
}
func appendDebugEndpoints(cfg *e2etypes.E2EConfig) {
debug := []string{
"--enable-debug-rpc-endpoints",
"--grpc-max-msg-size=65568081",
}
cfg.BeaconFlags = append(cfg.BeaconFlags, debug...)
}

View File

@@ -175,6 +175,9 @@ func (node *BeaconNode) Start(ctx context.Context) error {
if node.config.TestSync {
expectedNumOfPeers += 1
}
if node.config.TestCheckpointSync {
expectedNumOfPeers += 1
}
jwtPath := path.Join(e2e.TestParams.TestPath, "eth1data/"+strconv.Itoa(node.index)+"/")
if index == 0 {
jwtPath = path.Join(e2e.TestParams.TestPath, "eth1data/miner/")

View File

@@ -12,7 +12,7 @@ import (
)
func TestWeb3RemoteSigner_StartsAndReturnsPublicKeys(t *testing.T) {
require.NoError(t, e2eparams.Init(0))
require.NoError(t, e2eparams.Init(t, 0))
wsc := components.NewWeb3RemoteSigner()

View File

@@ -14,10 +14,10 @@ import (
"github.com/prysmaticlabs/prysm/testing/require"
)
func e2eMinimal(t *testing.T, useWeb3RemoteSigner bool, extraEpochs uint64) *testRunner {
func e2eMinimal(t *testing.T, cfgo ...types.E2EConfigOpt) *testRunner {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.E2ETestConfig().Copy())
require.NoError(t, e2eParams.Init(e2eParams.StandardBeaconCount))
require.NoError(t, e2eParams.Init(t, e2eParams.StandardBeaconCount))
// Run for 12 epochs if not in long-running to confirm long-running has no issues.
var err error
@@ -74,12 +74,13 @@ func e2eMinimal(t *testing.T, useWeb3RemoteSigner bool, extraEpochs uint64) *tes
TestDeposits: true,
UsePrysmShValidator: false,
UsePprof: !longRunning,
UseWeb3RemoteSigner: useWeb3RemoteSigner,
TracingSinkEndpoint: tracingEndpoint,
Evaluators: evals,
EvalInterceptor: defaultInterceptor,
Seed: int64(seed),
ExtraEpochs: extraEpochs,
}
for _, o := range cfgo {
o(testConfig)
}
return newTestRunner(t, testConfig)
@@ -89,9 +90,9 @@ func e2eMainnet(t *testing.T, usePrysmSh, useMultiClient bool) *testRunner {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.E2EMainnetTestConfig())
if useMultiClient {
require.NoError(t, e2eParams.InitMultiClient(e2eParams.StandardBeaconCount, e2eParams.StandardLighthouseNodeCount))
require.NoError(t, e2eParams.InitMultiClient(t, e2eParams.StandardBeaconCount, e2eParams.StandardLighthouseNodeCount))
} else {
require.NoError(t, e2eParams.Init(e2eParams.StandardBeaconCount))
require.NoError(t, e2eParams.Init(t, e2eParams.StandardBeaconCount))
}
// Run for 10 epochs if not in long-running to confirm long-running has no issues.
var err error
@@ -165,7 +166,6 @@ func scenarioEvals() []types.Evaluator {
ev.MetricsCheck,
ev.ValidatorsParticipatingAtEpoch(2),
ev.FinalizationOccurs(3),
ev.PeersCheck,
ev.VerifyBlockGraffiti,
ev.ProposeVoluntaryExit,
ev.ValidatorHasExited,
@@ -179,3 +179,22 @@ func scenarioEvals() []types.Evaluator {
ev.ValidatorSyncParticipation,
}
}
func scenarioEvalsMulti() []types.Evaluator {
return []types.Evaluator{
ev.PeersConnect,
ev.HealthzCheck,
ev.MetricsCheck,
ev.ValidatorsParticipatingAtEpoch(2),
ev.FinalizationOccurs(3),
ev.ProposeVoluntaryExit,
ev.ValidatorHasExited,
ev.ColdStateCheckpoint,
ev.AltairForkTransition,
ev.BellatrixForkTransition,
ev.APIMiddlewareVerifyIntegrity,
ev.APIGatewayV1Alpha1VerifyIntegrity,
ev.FinishedSyncing,
ev.AllNodesHaveSameHead,
}
}

View File

@@ -14,6 +14,14 @@ import (
"testing"
"time"
"github.com/prysmaticlabs/prysm/api/client/beacon"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/io/file"
"github.com/prysmaticlabs/prysm/proto/eth/service"
v1 "github.com/prysmaticlabs/prysm/proto/eth/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/config/params"
@@ -211,9 +219,101 @@ func (r *testRunner) testTxGeneration(ctx context.Context, g *errgroup.Group, ke
})
}
func (r *testRunner) waitForMatchingHead(ctx context.Context, check, ref *grpc.ClientConn) error {
// sleep hack copied from testBeaconChainSync
// Sleep a second for every 4 blocks that need to be synced for the newly started node.
secondsPerEpoch := uint64(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot))
extraSecondsToSync := (r.config.EpochsToRun) * secondsPerEpoch
deadline := time.Now().Add(time.Second * time.Duration(extraSecondsToSync))
dctx, cancel := context.WithDeadline(ctx, deadline)
defer cancel()
checkClient := service.NewBeaconChainClient(check)
refClient := service.NewBeaconChainClient(ref)
for {
select {
case <-dctx.Done():
// deadline ensures that the test eventually exits when beacon node fails to sync in a resonable timeframe
return fmt.Errorf("deadline exceeded waiting for known good block to appear in checkpoint-synced node")
default:
cResp, err := checkClient.GetBlockRoot(ctx, &v1.BlockRequest{BlockId: []byte("head")})
if err != nil {
errStatus, ok := status.FromError(err)
// in the happy path we expect NotFound results until the node has synced
if ok && errStatus.Code() == codes.NotFound {
continue
}
return fmt.Errorf("error requesting head from 'check' beacon node")
}
rResp, err := refClient.GetBlockRoot(ctx, &v1.BlockRequest{BlockId: []byte("head")})
if err != nil {
return errors.Wrap(err, "unexpected error requesting head block root from 'ref' beacon node")
}
if bytesutil.ToBytes32(cResp.Data.Root) == bytesutil.ToBytes32(rResp.Data.Root) {
return nil
}
}
}
}
func (r *testRunner) testCheckpointSync(ctx context.Context, g *errgroup.Group, i int, conns []*grpc.ClientConn, bnAPI, enr, minerEnr string) error {
ethNode := eth1.NewNode(i, minerEnr)
g.Go(func() error {
return ethNode.Start(ctx)
})
if err := helpers.ComponentsStarted(ctx, []e2etypes.ComponentRunner{ethNode}); err != nil {
return fmt.Errorf("sync beacon node not ready: %w", err)
}
client, err := beacon.NewClient(bnAPI)
if err != nil {
return err
}
gb, err := client.GetState(ctx, beacon.IdGenesis)
if err != nil {
return err
}
genPath := path.Join(e2e.TestParams.TestPath, "genesis.ssz")
err = file.WriteFile(genPath, gb)
if err != nil {
return err
}
flags := append([]string{}, r.config.BeaconFlags...)
flags = append(flags, fmt.Sprintf("--checkpoint-sync-url=%s", bnAPI))
flags = append(flags, fmt.Sprintf("--genesis-beacon-api-url=%s", bnAPI))
cfgcp := new(e2etypes.E2EConfig)
*cfgcp = *r.config
cfgcp.BeaconFlags = flags
cpsyncer := components.NewBeaconNode(cfgcp, i, enr)
g.Go(func() error {
return cpsyncer.Start(ctx)
})
if err := helpers.ComponentsStarted(ctx, []e2etypes.ComponentRunner{cpsyncer}); err != nil {
return fmt.Errorf("checkpoint sync beacon node not ready: %w", err)
}
c, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", e2e.TestParams.Ports.PrysmBeaconNodeRPCPort+i), grpc.WithInsecure())
require.NoError(r.t, err, "Failed to dial")
// this is so that the syncEvaluators checks can run on the checkpoint sync'd node
conns = append(conns, c)
err = r.waitForMatchingHead(ctx, c, conns[0])
if err != nil {
return err
}
syncEvaluators := []e2etypes.Evaluator{ev.FinishedSyncing, ev.AllNodesHaveSameHead}
for _, evaluator := range syncEvaluators {
r.t.Run(evaluator.Name, func(t *testing.T) {
assert.NoError(t, evaluator.Evaluation(conns...), "Evaluation failed for sync node")
})
}
return nil
}
// testBeaconChainSync creates another beacon node, and tests whether it can sync to head using previous nodes.
func (r *testRunner) testBeaconChainSync(ctx context.Context, g *errgroup.Group,
conns []*grpc.ClientConn, tickingStartTime time.Time, bootnodeEnr, minerEnr string) (*grpc.ClientConn, error) {
conns []*grpc.ClientConn, tickingStartTime time.Time, bootnodeEnr, minerEnr string) error {
t, config := r.t, r.config
index := e2e.TestParams.BeaconNodeCount + e2e.TestParams.LighthouseBeaconNodeCount
ethNode := eth1.NewNode(index, minerEnr)
@@ -221,14 +321,14 @@ func (r *testRunner) testBeaconChainSync(ctx context.Context, g *errgroup.Group,
return ethNode.Start(ctx)
})
if err := helpers.ComponentsStarted(ctx, []e2etypes.ComponentRunner{ethNode}); err != nil {
return nil, fmt.Errorf("sync beacon node not ready: %w", err)
return fmt.Errorf("sync beacon node not ready: %w", err)
}
syncBeaconNode := components.NewBeaconNode(config, index, bootnodeEnr)
g.Go(func() error {
return syncBeaconNode.Start(ctx)
})
if err := helpers.ComponentsStarted(ctx, []e2etypes.ComponentRunner{syncBeaconNode}); err != nil {
return nil, fmt.Errorf("sync beacon node not ready: %w", err)
return fmt.Errorf("sync beacon node not ready: %w", err)
}
syncConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", e2e.TestParams.Ports.PrysmBeaconNodeRPCPort+index), grpc.WithInsecure())
require.NoError(t, err, "Failed to dial")
@@ -247,7 +347,7 @@ func (r *testRunner) testBeaconChainSync(ctx context.Context, g *errgroup.Group,
assert.NoError(t, helpers.WaitForTextInFile(syncLogFile, "Synced up to"), "Failed to sync")
})
if t.Failed() {
return nil, errors.New("cannot sync beacon node")
return errors.New("cannot sync beacon node")
}
// Sleep a slot to make sure the synced state is made.
@@ -258,7 +358,7 @@ func (r *testRunner) testBeaconChainSync(ctx context.Context, g *errgroup.Group,
assert.NoError(t, evaluator.Evaluation(conns...), "Evaluation failed for sync node")
})
}
return syncConn, nil
return nil
}
func (r *testRunner) testDoppelGangerProtection(ctx context.Context) error {
@@ -365,17 +465,24 @@ func (r *testRunner) defaultEndToEndRun() error {
return errors.Wrap(err, "one or more evaluators failed")
}
// If requested, run sync test.
if !config.TestSync {
return nil
index := e2e.TestParams.BeaconNodeCount + e2e.TestParams.LighthouseBeaconNodeCount
if config.TestSync {
if err := r.testBeaconChainSync(ctx, g, conns, tickingStartTime, bootNode.ENR(), eth1Miner.ENR()); err != nil {
return errors.Wrap(err, "beacon chain sync test failed")
}
index += 1
if err := r.testDoppelGangerProtection(ctx); err != nil {
return errors.Wrap(err, "doppel ganger protection check failed")
}
}
syncConn, err := r.testBeaconChainSync(ctx, g, conns, tickingStartTime, bootNode.ENR(), eth1Miner.ENR())
if err != nil {
return errors.Wrap(err, "beacon chain sync test failed")
}
conns = append(conns, syncConn)
if err := r.testDoppelGangerProtection(ctx); err != nil {
return errors.Wrap(err, "doppel ganger protection check failed")
if config.TestCheckpointSync {
httpEndpoints := helpers.BeaconAPIHostnames(e2e.TestParams.BeaconNodeCount)
menr := eth1Miner.ENR()
benr := bootNode.ENR()
if err := r.testCheckpointSync(ctx, g, index, conns, httpEndpoints[0], benr, menr); err != nil {
return errors.Wrap(err, "checkpoint sync test failed")
}
index += 1
}
if config.ExtraEpochs > 0 {

View File

@@ -136,7 +136,7 @@ func allNodesHaveSameHead(conns ...*grpc.ClientConn) error {
beaconClient := eth.NewBeaconChainClient(conn)
chainHead, err := beaconClient.GetChainHead(context.Background(), &emptypb.Empty{})
if err != nil {
return err
return errors.Wrapf(err, "connection number=%d", i)
}
headEpochs[i] = chainHead.HeadEpoch
justifiedRoots[i] = chainHead.JustifiedBlockRoot

View File

@@ -7,10 +7,12 @@ import (
"context"
"fmt"
"io"
"net"
"net/http"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"testing"
"time"
@@ -269,6 +271,16 @@ func NewLocalConnections(ctx context.Context, numConns int) ([]*grpc.ClientConn,
}, nil
}
// BeaconAPIHostnames constructs a hostname:port string for the
func BeaconAPIHostnames(numConns int) []string {
hostnames := make([]string, 0)
for i := 0; i < numConns; i++ {
port := e2e.TestParams.Ports.PrysmBeaconNodeGatewayPort + i
hostnames = append(hostnames, net.JoinHostPort("127.0.0.1", strconv.Itoa(port)))
}
return hostnames
}
// ComponentsStarted checks, sequentially, each provided component, blocks until all of the components are ready.
func ComponentsStarted(ctx context.Context, comps []e2etypes.ComponentRunner) error {
for _, comp := range comps {

View File

@@ -10,8 +10,7 @@ func TestEndToEnd_MainnetConfig_MultiClient(t *testing.T) {
func TestEndToEnd_ScenarioRun_BeaconOffline_Multiclient(t *testing.T) {
runner := e2eMainnet(t, false /*usePrysmSh*/, true /*useMultiClient*/)
runner.config.Evaluators = scenarioEvals()
runner.config.Evaluators = scenarioEvalsMulti()
runner.config.EvalInterceptor = runner.singleNodeOffline
runner.scenarioRunner()
}

View File

@@ -2,12 +2,18 @@ package endtoend
import (
"testing"
"github.com/prysmaticlabs/prysm/testing/endtoend/types"
)
func TestEndToEnd_MinimalConfig(t *testing.T) {
e2eMinimal(t, false, 3).run()
e2eMinimal(t).run()
}
func TestEndToEnd_MinimalConfig_Web3Signer(t *testing.T) {
e2eMinimal(t, true, 0).run()
e2eMinimal(t, types.WithRemoteSigner()).run()
}
func TestEndToEnd_MinimalConfig_CheckpointSync(t *testing.T) {
e2eMinimal(t, types.WithCheckpointSync()).run()
}

View File

@@ -3,7 +3,7 @@ package endtoend
import "testing"
func TestEndToEnd_ScenarioRun_BeaconOffline(t *testing.T) {
runner := e2eMinimal(t, false, 0)
runner := e2eMinimal(t)
runner.config.Evaluators = scenarioEvals()
runner.config.EvalInterceptor = runner.singleNodeOffline
@@ -11,7 +11,7 @@ func TestEndToEnd_ScenarioRun_BeaconOffline(t *testing.T) {
}
func TestEndToEnd_ScenarioRun_AllvalidatorsOffline(t *testing.T) {
runner := e2eMinimal(t, false, 0)
runner := e2eMinimal(t)
runner.config.Evaluators = scenarioEvals()
runner.config.EvalInterceptor = runner.allValidatorsOffline
@@ -20,7 +20,7 @@ func TestEndToEnd_ScenarioRun_AllvalidatorsOffline(t *testing.T) {
func TestEndToEnd_ScenarioRun_EEOffline(t *testing.T) {
t.Skip("TODO(#10242) Prysm is current unable to handle an offline e2e")
runner := e2eMinimal(t, false, 0)
runner := e2eMinimal(t)
runner.config.Evaluators = scenarioEvals()
runner.config.EvalInterceptor = runner.eeOffline

View File

@@ -14,7 +14,7 @@ import (
func TestEndToEnd_Slasher_MinimalConfig(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.E2ETestConfig().Copy())
require.NoError(t, e2eParams.Init(e2eParams.StandardBeaconCount))
require.NoError(t, e2eParams.Init(t, e2eParams.StandardBeaconCount))
tracingPort := e2eParams.TestParams.Ports.JaegerTracingPort
tracingEndpoint := fmt.Sprintf("127.0.0.1:%d", tracingPort)

View File

@@ -7,6 +7,7 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/testing/endtoend/params",
visibility = ["//testing/endtoend:__subpackages__"],
deps = [
"//io/file:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
],

View File

@@ -6,11 +6,14 @@ import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"strconv"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/ethereum/go-ethereum/common"
"github.com/prysmaticlabs/prysm/io/file"
)
// params struct defines the parameters needed for running E2E tests to properly handle test sharding.
@@ -105,12 +108,16 @@ const (
)
// Init initializes the E2E config, properly handling test sharding.
func Init(beaconNodeCount int) error {
func Init(t *testing.T, beaconNodeCount int) error {
testPath := bazel.TestTmpDir()
logPath, ok := os.LookupEnv("TEST_UNDECLARED_OUTPUTS_DIR")
if !ok {
return errors.New("expected TEST_UNDECLARED_OUTPUTS_DIR to be defined")
}
logPath = path.Join(logPath, t.Name())
if err := file.MkdirAll(logPath); err != nil {
return err
}
testTotalShardsStr, ok := os.LookupEnv("TEST_TOTAL_SHARDS")
if !ok {
testTotalShardsStr = "1"
@@ -146,12 +153,16 @@ func Init(beaconNodeCount int) error {
}
// InitMultiClient initializes the multiclient E2E config, properly handling test sharding.
func InitMultiClient(beaconNodeCount int, lighthouseNodeCount int) error {
func InitMultiClient(t *testing.T, beaconNodeCount int, lighthouseNodeCount int) error {
testPath := bazel.TestTmpDir()
logPath, ok := os.LookupEnv("TEST_UNDECLARED_OUTPUTS_DIR")
if !ok {
return errors.New("expected TEST_UNDECLARED_OUTPUTS_DIR to be defined")
}
logPath = path.Join(logPath, t.Name())
if err := file.MkdirAll(logPath); err != nil {
return err
}
testTotalShardsStr, ok := os.LookupEnv("TEST_TOTAL_SHARDS")
if !ok {
testTotalShardsStr = "1"

View File

@@ -9,8 +9,29 @@ import (
"google.golang.org/grpc"
)
type E2EConfigOpt func(*E2EConfig)
func WithExtraEpochs(extra uint64) E2EConfigOpt {
return func(cfg *E2EConfig) {
cfg.ExtraEpochs = extra
}
}
func WithRemoteSigner() E2EConfigOpt {
return func(cfg *E2EConfig) {
cfg.UseWeb3RemoteSigner = true
}
}
func WithCheckpointSync() E2EConfigOpt {
return func(cfg *E2EConfig) {
cfg.TestCheckpointSync = true
}
}
// E2EConfig defines the struct for all configurations needed for E2E testing.
type E2EConfig struct {
TestCheckpointSync bool
TestSync bool
TestFeature bool
UsePrysmShValidator bool

View File

@@ -473,7 +473,7 @@ func web3SignerConfig(cliCtx *cli.Context) (*remote_web3signer.SetupConfig, erro
func feeRecipientConfig(cliCtx *cli.Context) (*validatorServiceConfig.FeeRecipientConfig, error) {
var fileConfig *validatorServiceConfig.FeeRecipientFileConfig
if cliCtx.IsSet(flags.FeeRecipientConfigFileFlag.Name) && cliCtx.IsSet(flags.FeeRecipientConfigURLFlag.Name) {
return nil, errors.New("cannot specify both --validators-proposer-fileConfig-dir and --validators-proposer-fileConfig-url")
return nil, fmt.Errorf("cannot specify both --%s and --%s", flags.FeeRecipientConfigFileFlag.Name, flags.FeeRecipientConfigURLFlag.Name)
}
if cliCtx.IsSet(flags.FeeRecipientConfigFileFlag.Name) {
if err := unmarshalFromFile(cliCtx.Context, cliCtx.String(flags.FeeRecipientConfigFileFlag.Name), &fileConfig); err != nil {