mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 22:07:59 -05:00
Compare commits
23 Commits
v2.1.3-rc.
...
rm-client-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e98d9c0b3b | ||
|
|
da9f72360d | ||
|
|
1be46aa16e | ||
|
|
a1a12243be | ||
|
|
a1dd2e6b8c | ||
|
|
ecb605814e | ||
|
|
61cbe3709b | ||
|
|
7039c382bf | ||
|
|
4f00984ab1 | ||
|
|
edb03328ea | ||
|
|
c922eb9bfc | ||
|
|
0841857c53 | ||
|
|
460a5a4115 | ||
|
|
21e569fd90 | ||
|
|
594782d941 | ||
|
|
18e04a2743 | ||
|
|
3207d481cb | ||
|
|
e069c55a9a | ||
|
|
9b28abb99e | ||
|
|
402facfdf1 | ||
|
|
8ab5d3461e | ||
|
|
9bec48ea47 | ||
|
|
8a84a86a85 |
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -111,7 +111,6 @@ func TestConfigureExecutionSetting(t *testing.T) {
|
||||
err = configureExecutionSetting(cliCtx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, common.HexToAddress("0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa"), params.BeaconConfig().DefaultFeeRecipient)
|
||||
|
||||
}
|
||||
|
||||
func TestConfigureNetwork(t *testing.T) {
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
go test fuzz v1
|
||||
int(5)
|
||||
int(0)
|
||||
[]byte("")
|
||||
uint64(8234)
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1095,3 +1095,121 @@ func syncSelectionProofSigningRoot(st state.BeaconState, slot types.Slot, comIdx
|
||||
selectionData := ðpb.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(ðpb.SyncCommitteeContribution{
|
||||
AggregationBits: b0,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, has)
|
||||
|
||||
// Cache with entries but same key
|
||||
require.NoError(t, s.setSyncContributionBits(ðpb.SyncCommitteeContribution{
|
||||
AggregationBits: b0,
|
||||
}))
|
||||
has, err = s.hasSeenSyncContributionBits(ðpb.SyncCommitteeContribution{
|
||||
AggregationBits: b0,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, has)
|
||||
b1 := bitfield.NewBitvector128()
|
||||
b1.SetBitAt(1, true)
|
||||
has, err = s.hasSeenSyncContributionBits(ðpb.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(ðpb.SyncCommitteeContribution{
|
||||
AggregationBits: b2,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, has)
|
||||
b2.SetBitAt(0, true)
|
||||
has, err = s.hasSeenSyncContributionBits(ðpb.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(ðpb.SyncCommitteeContribution{
|
||||
Slot: 1,
|
||||
SubcommitteeIndex: 2,
|
||||
BlockRoot: []byte{'A'},
|
||||
AggregationBits: b0,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, has)
|
||||
require.NoError(t, s.setSyncContributionBits(ðpb.SyncCommitteeContribution{
|
||||
Slot: 1,
|
||||
SubcommitteeIndex: 2,
|
||||
BlockRoot: []byte{'A'},
|
||||
AggregationBits: b2,
|
||||
}))
|
||||
has, err = s.hasSeenSyncContributionBits(ðpb.SyncCommitteeContribution{
|
||||
Slot: 1,
|
||||
SubcommitteeIndex: 2,
|
||||
BlockRoot: []byte{'A'},
|
||||
AggregationBits: b0,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, has)
|
||||
has, err = s.hasSeenSyncContributionBits(ðpb.SyncCommitteeContribution{
|
||||
Slot: 1,
|
||||
SubcommitteeIndex: 2,
|
||||
BlockRoot: []byte{'A'},
|
||||
AggregationBits: b1,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, has)
|
||||
has, err = s.hasSeenSyncContributionBits(ðpb.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(ðpb.SyncCommitteeContribution{
|
||||
Slot: 2,
|
||||
SubcommitteeIndex: 2,
|
||||
BlockRoot: []byte{'A'},
|
||||
AggregationBits: b0,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, has)
|
||||
has, err = s.hasSeenSyncContributionBits(ðpb.SyncCommitteeContribution{
|
||||
Slot: 1,
|
||||
SubcommitteeIndex: 2,
|
||||
BlockRoot: []byte{'B'},
|
||||
AggregationBits: b0,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, has)
|
||||
has, err = s.hasSeenSyncContributionBits(ðpb.SyncCommitteeContribution{
|
||||
Slot: 1,
|
||||
SubcommitteeIndex: 3,
|
||||
BlockRoot: []byte{'A'},
|
||||
AggregationBits: b0,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, has)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
10
deps.bzl
10
deps.bzl
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
5
go.mod
@@ -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
8
go.sum
@@ -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=
|
||||
|
||||
@@ -10,3 +10,7 @@ import (
|
||||
func NewSyncCommitteeAggregationBits() bitfield.Bitvector128 {
|
||||
return bitfield.NewBitvector128()
|
||||
}
|
||||
|
||||
func ConvertToSyncContributionBitVector(b []byte) bitfield.Bitvector128 {
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -10,3 +10,7 @@ import (
|
||||
func NewSyncCommitteeAggregationBits() bitfield.Bitvector8 {
|
||||
return bitfield.NewBitvector8()
|
||||
}
|
||||
|
||||
func ConvertToSyncContributionBitVector(b []byte) bitfield.Bitvector8 {
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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...)
|
||||
}
|
||||
|
||||
@@ -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/")
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user