mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
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>
This commit is contained in:
@@ -26,69 +26,126 @@ type OriginData struct {
|
|||||||
bb []byte
|
bb []byte
|
||||||
st state.BeaconState
|
st state.BeaconState
|
||||||
b interfaces.SignedBeaconBlock
|
b interfaces.SignedBeaconBlock
|
||||||
cf *detect.VersionedUnmarshaler
|
vu *detect.VersionedUnmarshaler
|
||||||
}
|
br [32]byte
|
||||||
|
sr [32]byte
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveBlock saves the downloaded block to a unique file in the given path.
|
// 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
|
// For readability and collision avoidance, the file name includes: type, config name, slot and root
|
||||||
func (od *OriginData) SaveBlock(dir string) (string, error) {
|
func (o *OriginData) SaveBlock(dir string) (string, error) {
|
||||||
blockPath := path.Join(dir, fname("block", od.cf, od.b.Block().Slot(), od.wsd.BlockRoot))
|
blockPath := path.Join(dir, fname("block", o.vu, o.b.Block().Slot(), o.br))
|
||||||
return blockPath, file.WriteFile(blockPath, od.BlockBytes())
|
return blockPath, file.WriteFile(blockPath, o.BlockBytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveState saves the downloaded state to a unique file in the given path.
|
// 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
|
// For readability and collision avoidance, the file name includes: type, config name, slot and root
|
||||||
func (od *OriginData) SaveState(dir string) (string, error) {
|
func (o *OriginData) SaveState(dir string) (string, error) {
|
||||||
statePath := path.Join(dir, fname("state", od.cf, od.st.Slot(), od.wsd.StateRoot))
|
statePath := path.Join(dir, fname("state", o.vu, o.st.Slot(), o.sr))
|
||||||
return statePath, file.WriteFile(statePath, od.StateBytes())
|
return statePath, file.WriteFile(statePath, o.StateBytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateBytes returns the ssz-encoded bytes of the downloaded BeaconState value.
|
// StateBytes returns the ssz-encoded bytes of the downloaded BeaconState value.
|
||||||
func (od *OriginData) StateBytes() []byte {
|
func (o *OriginData) StateBytes() []byte {
|
||||||
return od.sb
|
return o.sb
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockBytes returns the ssz-encoded bytes of the downloaded SignedBeaconBlock value.
|
// BlockBytes returns the ssz-encoded bytes of the downloaded SignedBeaconBlock value.
|
||||||
func (od *OriginData) BlockBytes() []byte {
|
func (o *OriginData) BlockBytes() []byte {
|
||||||
return od.bb
|
return o.bb
|
||||||
}
|
}
|
||||||
|
|
||||||
func fname(prefix string, cf *detect.VersionedUnmarshaler, slot types.Slot, root [32]byte) string {
|
func fname(prefix string, vu *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)
|
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
|
// DownloadFinalizedData downloads the most recently finalized state, and the block most recently applied to that state.
|
||||||
// and use prysm's helper methods to compute the latest weak subjectivity epoch.
|
// This pair can be used to initialize a new beacon node via checkpoint sync.
|
||||||
func getWeakSubjectivityEpochFromHead(ctx context.Context, client *Client) (types.Epoch, error) {
|
func DownloadFinalizedData(ctx context.Context, client *Client) (*OriginData, error) {
|
||||||
headBytes, err := client.GetState(ctx, IdHead)
|
sb, err := client.GetState(ctx, IdFinalized)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cf, err := detect.FromState(headBytes)
|
vu, err := detect.FromState(sb)
|
||||||
if err != nil {
|
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))
|
log.Printf("detected supported config in remote finalized state, name=%s, fork=%s", vu.Config.ConfigName, version.String(vu.Fork))
|
||||||
headState, err := cf.UnmarshalBeaconState(headBytes)
|
s, err := vu.UnmarshalBeaconState(sb)
|
||||||
if err != nil {
|
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 {
|
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)
|
bb, err := client.GetBlock(ctx, IdFromRoot(br))
|
||||||
return epoch, nil
|
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 (
|
const (
|
||||||
@@ -96,8 +153,8 @@ const (
|
|||||||
prysmImplementationName = "Prysm"
|
prysmImplementationName = "Prysm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrUnsupportedPrysmCheckpointVersion indicates remote beacon node can't be used 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")
|
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
|
// for older endpoints or clients that do not support the weak_subjectivity api method
|
||||||
// we gather the necessary data for a checkpoint sync by:
|
// 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
|
// - 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
|
// - using hash_tree_root(state.latest_block_header) to compute the block the state integrates
|
||||||
// - requesting that block by its root
|
// - 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")
|
log.Print("falling back to generic checkpoint derivation, weak_subjectivity API not supported by server")
|
||||||
nv, err := client.GetNodeVersion(ctx)
|
nv, err := client.GetNodeVersion(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "unable to proceed with fallback method without confirming node version")
|
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 {
|
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)
|
epoch, err := getWeakSubjectivityEpochFromHead(ctx, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -127,136 +184,78 @@ func downloadBackwardsCompatible(ctx context.Context, client *Client) (*OriginDa
|
|||||||
|
|
||||||
log.Printf("requesting checkpoint state at slot %d", slot)
|
log.Printf("requesting checkpoint state at slot %d", slot)
|
||||||
// get the state at the first slot of the epoch
|
// 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 {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to request state by slot from api, slot=%d", slot)
|
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
|
// 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 {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error detecting chain config for beacon state")
|
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 {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error using detected config fork to unmarshal state bytes")
|
return nil, errors.Wrap(err, "error using detected config fork to unmarshal state bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute state and block roots
|
// compute state and block roots
|
||||||
stateRoot, err := st.HashTreeRoot(ctx)
|
sr, err := s.HashTreeRoot(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error computing hash_tree_root of state")
|
return nil, errors.Wrap(err, "error computing hash_tree_root of state")
|
||||||
}
|
}
|
||||||
|
|
||||||
header := st.LatestBlockHeader()
|
h := s.LatestBlockHeader()
|
||||||
header.StateRoot = stateRoot[:]
|
h.StateRoot = sr[:]
|
||||||
computedBlockRoot, err := header.HashTreeRoot()
|
br, err := h.HashTreeRoot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error while computing block root using state data")
|
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 {
|
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 {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "unable to unmarshal block to a supported type using the detected fork schedule")
|
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 {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error computing hash_tree_root for block obtained via root")
|
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())
|
return &WeakSubjectivityData{
|
||||||
log.Printf("BeaconState htr=%#xd, Block state_root=%#x", stateRoot, block.Block().StateRoot())
|
Epoch: epoch,
|
||||||
log.Printf("BeaconBlock root computed from state=%#x, Block htr=%#x", computedBlockRoot, blockRoot)
|
BlockRoot: br,
|
||||||
|
StateRoot: sr,
|
||||||
return &OriginData{
|
|
||||||
wsd: &WeakSubjectivityData{
|
|
||||||
BlockRoot: blockRoot,
|
|
||||||
StateRoot: stateRoot,
|
|
||||||
Epoch: epoch,
|
|
||||||
},
|
|
||||||
st: st,
|
|
||||||
sb: stateBytes,
|
|
||||||
b: block,
|
|
||||||
bb: blockBytes,
|
|
||||||
cf: cf,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownloadOriginData attempts to use the proposed weak_subjectivity beacon node api
|
// this method downloads the head state, which can be used to find the correct chain config
|
||||||
// to obtain the weak_subjectivity metadata (epoch, block_root, state_root) needed to sync
|
// and use prysm's helper methods to compute the latest weak subjectivity epoch.
|
||||||
// a beacon node from the canonical weak subjectivity checkpoint. As this is a proposed API
|
func getWeakSubjectivityEpochFromHead(ctx context.Context, client *Client) (types.Epoch, error) {
|
||||||
// that will only be supported by prysm at first, in the event of a 404 we fallback to using a
|
headBytes, err := client.GetState(ctx, IdHead)
|
||||||
// different technique where we first download the head state which can be used to compute the
|
|
||||||
// weak subjectivity epoch on the client side.
|
|
||||||
func DownloadOriginData(ctx context.Context, client *Client) (*OriginData, error) {
|
|
||||||
ws, err := client.GetWeakSubjectivity(ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// a 404/405 is expected if querying an endpoint that doesn't support the weak subjectivity checkpoint api
|
return 0, err
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
log.Printf("server weak subjectivity checkpoint response - epoch=%d, block_root=%#x, state_root=%#x", ws.Epoch, ws.BlockRoot, ws.StateRoot)
|
vu, err := detect.FromState(headBytes)
|
||||||
|
|
||||||
// use first slot of the epoch for the block slot
|
|
||||||
slot, err := slots.EpochStart(ws.Epoch)
|
|
||||||
if err != nil {
|
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)
|
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)
|
||||||
stateBytes, err := client.GetState(ctx, IdFromSlot(slot))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to request state by slot from api, slot=%d", slot)
|
return 0, errors.Wrap(err, "error unmarshaling state to correct version")
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blockRoot, err := state.LatestBlockHeader().HashTreeRoot()
|
epoch, err := helpers.LatestWeakSubjectivityEpoch(ctx, headState, vu.Config)
|
||||||
if err != nil {
|
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 {
|
log.Printf("(computed client-side) weak subjectivity epoch = %d", epoch)
|
||||||
return nil, errors.Wrapf(err, "error requesting block by slot = %d", slot)
|
return epoch, nil
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,8 +93,8 @@ func TestFallbackVersionCheck(t *testing.T) {
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
_, err := DownloadOriginData(ctx, c)
|
_, err := ComputeWeakSubjectivityCheckpoint(ctx, c)
|
||||||
require.ErrorIs(t, err, ErrUnsupportedPrysmCheckpointVersion)
|
require.ErrorIs(t, err, errUnsupportedPrysmCheckpointVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFname(t *testing.T) {
|
func TestFname(t *testing.T) {
|
||||||
@@ -120,9 +120,9 @@ func TestFname(t *testing.T) {
|
|||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDownloadOriginData(t *testing.T) {
|
func TestDownloadWeakSubjectivityCheckpoint(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
cfg := params.MainnetConfig()
|
cfg := params.MainnetConfig().Copy()
|
||||||
|
|
||||||
epoch := cfg.AltairForkEpoch - 1
|
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
|
// 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"},
|
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
|
||||||
}
|
}
|
||||||
|
|
||||||
od, err := DownloadOriginData(ctx, c)
|
wsd, err := ComputeWeakSubjectivityCheckpoint(ctx, c)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expectedWSD.Epoch, od.wsd.Epoch)
|
require.Equal(t, expectedWSD.Epoch, wsd.Epoch)
|
||||||
require.Equal(t, expectedWSD.StateRoot, od.wsd.StateRoot)
|
require.Equal(t, expectedWSD.StateRoot, wsd.StateRoot)
|
||||||
require.Equal(t, expectedWSD.BlockRoot, od.wsd.BlockRoot)
|
require.Equal(t, expectedWSD.BlockRoot, 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// runs downloadBackwardsCompatible directly
|
// runs computeBackwardsCompatible directly
|
||||||
// and via DownloadOriginData with a round tripper that triggers the backwards compatible code path
|
// and via ComputeWeakSubjectivityCheckpoint with a round tripper that triggers the backwards compatible code path
|
||||||
func TestDownloadBackwardsCompatibleCombined(t *testing.T) {
|
func TestDownloadBackwardsCompatibleCombined(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
cfg := params.MainnetConfig()
|
cfg := params.MainnetConfig()
|
||||||
@@ -297,16 +293,12 @@ func TestDownloadBackwardsCompatibleCombined(t *testing.T) {
|
|||||||
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
|
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
|
||||||
}
|
}
|
||||||
|
|
||||||
odPub, err := DownloadOriginData(ctx, c)
|
wsPub, err := ComputeWeakSubjectivityCheckpoint(ctx, c)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
odPriv, err := downloadBackwardsCompatible(ctx, c)
|
wsPriv, err := computeBackwardsCompatible(ctx, c)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.DeepEqual(t, odPriv.wsd, odPub.wsd)
|
require.DeepEqual(t, wsPriv, wsPub)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetWeakSubjectivityEpochFromHead(t *testing.T) {
|
func TestGetWeakSubjectivityEpochFromHead(t *testing.T) {
|
||||||
@@ -402,3 +394,94 @@ func populateValidators(cfg *params.BeaconChainConfig, st state.BeaconState, val
|
|||||||
|
|
||||||
return nil
|
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
|
type StateOrBlockId string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
IdGenesis StateOrBlockId = "genesis"
|
IdGenesis StateOrBlockId = "genesis"
|
||||||
IdHead StateOrBlockId = "head"
|
IdHead StateOrBlockId = "head"
|
||||||
|
IdFinalized StateOrBlockId = "finalized"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrMalformedHostname = errors.New("hostname must include port, separated by one colon, like example.com:3500")
|
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
|
}, 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 {
|
func non200Err(response *http.Response) error {
|
||||||
bodyBytes, err := io.ReadAll(response.Body)
|
bodyBytes, err := io.ReadAll(response.Body)
|
||||||
var body string
|
var body string
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func NewAPIInitializer(beaconNodeHost string) (*APIInitializer, error) {
|
|||||||
// Initialize downloads origin state and block for checkpoint sync and initializes database records to
|
// Initialize downloads origin state and block for checkpoint sync and initializes database records to
|
||||||
// prepare the node to begin syncing from that point.
|
// prepare the node to begin syncing from that point.
|
||||||
func (dl *APIInitializer) Initialize(ctx context.Context, d db.Database) error {
|
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 {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Error retrieving checkpoint origin state and block")
|
return errors.Wrap(err, "Error retrieving checkpoint origin state and block")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,12 +44,12 @@ func cliActionLatest(_ *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
od, err := beacon.DownloadOriginData(ctx, client)
|
ws, err := beacon.ComputeWeakSubjectivityCheckpoint(ctx, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("\nUse the following flag when starting a prysm Beacon Node to ensure the chain history " +
|
fmt.Println("\nUse the following flag when starting a prysm Beacon Node to ensure the chain history " +
|
||||||
"includes the Weak Subjectivity Checkpoint: ")
|
"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
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ var saveFlags = struct {
|
|||||||
|
|
||||||
var saveCmd = &cli.Command{
|
var saveCmd = &cli.Command{
|
||||||
Name: "save",
|
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,
|
Action: cliActionSave,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
@@ -50,7 +50,7 @@ func cliActionSave(_ *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
od, err := beacon.DownloadOriginData(ctx, client)
|
od, err := beacon.DownloadFinalizedData(ctx, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ go_test(
|
|||||||
"requires-network",
|
"requires-network",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//api/client/beacon:go_default_library",
|
||||||
"//beacon-chain/blockchain/testing:go_default_library",
|
"//beacon-chain/blockchain/testing:go_default_library",
|
||||||
"//beacon-chain/core/transition:go_default_library",
|
"//beacon-chain/core/transition:go_default_library",
|
||||||
"//beacon-chain/db/testing:go_default_library",
|
"//beacon-chain/db/testing:go_default_library",
|
||||||
@@ -44,6 +45,10 @@ go_test(
|
|||||||
"//config/params:go_default_library",
|
"//config/params:go_default_library",
|
||||||
"//consensus-types/primitives:go_default_library",
|
"//consensus-types/primitives:go_default_library",
|
||||||
"//crypto/bls: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",
|
"//proto/prysm/v1alpha1:go_default_library",
|
||||||
"//testing/assert:go_default_library",
|
"//testing/assert:go_default_library",
|
||||||
"//testing/endtoend/components:go_default_library",
|
"//testing/endtoend/components:go_default_library",
|
||||||
@@ -59,6 +64,8 @@ go_test(
|
|||||||
"@com_github_sirupsen_logrus//:go_default_library",
|
"@com_github_sirupsen_logrus//:go_default_library",
|
||||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||||
"@org_golang_google_grpc//: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_google_protobuf//types/known/emptypb:go_default_library",
|
||||||
"@org_golang_x_sync//errgroup:go_default_library",
|
"@org_golang_x_sync//errgroup:go_default_library",
|
||||||
],
|
],
|
||||||
@@ -94,6 +101,7 @@ go_test(
|
|||||||
"requires-network",
|
"requires-network",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//api/client/beacon:go_default_library",
|
||||||
"//beacon-chain/blockchain/testing:go_default_library",
|
"//beacon-chain/blockchain/testing:go_default_library",
|
||||||
"//beacon-chain/core/transition:go_default_library",
|
"//beacon-chain/core/transition:go_default_library",
|
||||||
"//beacon-chain/db/testing:go_default_library",
|
"//beacon-chain/db/testing:go_default_library",
|
||||||
@@ -103,6 +111,10 @@ go_test(
|
|||||||
"//config/params:go_default_library",
|
"//config/params:go_default_library",
|
||||||
"//consensus-types/primitives:go_default_library",
|
"//consensus-types/primitives:go_default_library",
|
||||||
"//crypto/bls: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",
|
"//proto/prysm/v1alpha1:go_default_library",
|
||||||
"//testing/assert:go_default_library",
|
"//testing/assert:go_default_library",
|
||||||
"//testing/endtoend/components:go_default_library",
|
"//testing/endtoend/components:go_default_library",
|
||||||
@@ -118,6 +130,8 @@ go_test(
|
|||||||
"@com_github_sirupsen_logrus//:go_default_library",
|
"@com_github_sirupsen_logrus//:go_default_library",
|
||||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||||
"@org_golang_google_grpc//: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_google_protobuf//types/known/emptypb:go_default_library",
|
||||||
"@org_golang_x_sync//errgroup:go_default_library",
|
"@org_golang_x_sync//errgroup:go_default_library",
|
||||||
],
|
],
|
||||||
@@ -153,6 +167,7 @@ go_test(
|
|||||||
"scenario",
|
"scenario",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//api/client/beacon:go_default_library",
|
||||||
"//beacon-chain/blockchain/testing:go_default_library",
|
"//beacon-chain/blockchain/testing:go_default_library",
|
||||||
"//beacon-chain/core/transition:go_default_library",
|
"//beacon-chain/core/transition:go_default_library",
|
||||||
"//beacon-chain/db/testing:go_default_library",
|
"//beacon-chain/db/testing:go_default_library",
|
||||||
@@ -162,6 +177,10 @@ go_test(
|
|||||||
"//config/params:go_default_library",
|
"//config/params:go_default_library",
|
||||||
"//consensus-types/primitives:go_default_library",
|
"//consensus-types/primitives:go_default_library",
|
||||||
"//crypto/bls: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",
|
"//proto/prysm/v1alpha1:go_default_library",
|
||||||
"//testing/assert:go_default_library",
|
"//testing/assert:go_default_library",
|
||||||
"//testing/endtoend/components:go_default_library",
|
"//testing/endtoend/components:go_default_library",
|
||||||
@@ -177,6 +196,8 @@ go_test(
|
|||||||
"@com_github_sirupsen_logrus//:go_default_library",
|
"@com_github_sirupsen_logrus//:go_default_library",
|
||||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||||
"@org_golang_google_grpc//: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_google_protobuf//types/known/emptypb:go_default_library",
|
||||||
"@org_golang_x_sync//errgroup:go_default_library",
|
"@org_golang_x_sync//errgroup:go_default_library",
|
||||||
],
|
],
|
||||||
@@ -212,6 +233,7 @@ go_test(
|
|||||||
"scenario",
|
"scenario",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//api/client/beacon:go_default_library",
|
||||||
"//beacon-chain/blockchain/testing:go_default_library",
|
"//beacon-chain/blockchain/testing:go_default_library",
|
||||||
"//beacon-chain/core/transition:go_default_library",
|
"//beacon-chain/core/transition:go_default_library",
|
||||||
"//beacon-chain/db/testing:go_default_library",
|
"//beacon-chain/db/testing:go_default_library",
|
||||||
@@ -221,6 +243,10 @@ go_test(
|
|||||||
"//config/params:go_default_library",
|
"//config/params:go_default_library",
|
||||||
"//consensus-types/primitives:go_default_library",
|
"//consensus-types/primitives:go_default_library",
|
||||||
"//crypto/bls: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",
|
"//proto/prysm/v1alpha1:go_default_library",
|
||||||
"//testing/assert:go_default_library",
|
"//testing/assert:go_default_library",
|
||||||
"//testing/endtoend/components:go_default_library",
|
"//testing/endtoend/components:go_default_library",
|
||||||
@@ -236,6 +262,8 @@ go_test(
|
|||||||
"@com_github_sirupsen_logrus//:go_default_library",
|
"@com_github_sirupsen_logrus//:go_default_library",
|
||||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||||
"@org_golang_google_grpc//: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_google_protobuf//types/known/emptypb:go_default_library",
|
||||||
"@org_golang_x_sync//errgroup:go_default_library",
|
"@org_golang_x_sync//errgroup:go_default_library",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -129,6 +129,9 @@ func (c *componentHandler) setup() {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if config.TestCheckpointSync {
|
||||||
|
appendDebugEndpoints(config)
|
||||||
|
}
|
||||||
// Beacon nodes.
|
// Beacon nodes.
|
||||||
beaconNodes := components.NewBeaconNodes(config)
|
beaconNodes := components.NewBeaconNodes(config)
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
@@ -201,3 +204,11 @@ func (c *componentHandler) required() []e2etypes.ComponentRunner {
|
|||||||
}
|
}
|
||||||
return requiredComponents
|
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 {
|
if node.config.TestSync {
|
||||||
expectedNumOfPeers += 1
|
expectedNumOfPeers += 1
|
||||||
}
|
}
|
||||||
|
if node.config.TestCheckpointSync {
|
||||||
|
expectedNumOfPeers += 1
|
||||||
|
}
|
||||||
jwtPath := path.Join(e2e.TestParams.TestPath, "eth1data/"+strconv.Itoa(node.index)+"/")
|
jwtPath := path.Join(e2e.TestParams.TestPath, "eth1data/"+strconv.Itoa(node.index)+"/")
|
||||||
if index == 0 {
|
if index == 0 {
|
||||||
jwtPath = path.Join(e2e.TestParams.TestPath, "eth1data/miner/")
|
jwtPath = path.Join(e2e.TestParams.TestPath, "eth1data/miner/")
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
"github.com/prysmaticlabs/prysm/testing/require"
|
"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.SetupTestConfigCleanup(t)
|
||||||
params.OverrideBeaconConfig(params.E2ETestConfig().Copy())
|
params.OverrideBeaconConfig(params.E2ETestConfig().Copy())
|
||||||
require.NoError(t, e2eParams.Init(t, e2eParams.StandardBeaconCount))
|
require.NoError(t, e2eParams.Init(t, e2eParams.StandardBeaconCount))
|
||||||
@@ -74,12 +74,13 @@ func e2eMinimal(t *testing.T, useWeb3RemoteSigner bool, extraEpochs uint64) *tes
|
|||||||
TestDeposits: true,
|
TestDeposits: true,
|
||||||
UsePrysmShValidator: false,
|
UsePrysmShValidator: false,
|
||||||
UsePprof: !longRunning,
|
UsePprof: !longRunning,
|
||||||
UseWeb3RemoteSigner: useWeb3RemoteSigner,
|
|
||||||
TracingSinkEndpoint: tracingEndpoint,
|
TracingSinkEndpoint: tracingEndpoint,
|
||||||
Evaluators: evals,
|
Evaluators: evals,
|
||||||
EvalInterceptor: defaultInterceptor,
|
EvalInterceptor: defaultInterceptor,
|
||||||
Seed: int64(seed),
|
Seed: int64(seed),
|
||||||
ExtraEpochs: extraEpochs,
|
}
|
||||||
|
for _, o := range cfgo {
|
||||||
|
o(testConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
return newTestRunner(t, testConfig)
|
return newTestRunner(t, testConfig)
|
||||||
|
|||||||
@@ -14,6 +14,14 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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/pkg/errors"
|
||||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
|
||||||
"github.com/prysmaticlabs/prysm/config/params"
|
"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.
|
// 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,
|
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
|
t, config := r.t, r.config
|
||||||
index := e2e.TestParams.BeaconNodeCount + e2e.TestParams.LighthouseBeaconNodeCount
|
index := e2e.TestParams.BeaconNodeCount + e2e.TestParams.LighthouseBeaconNodeCount
|
||||||
ethNode := eth1.NewNode(index, minerEnr)
|
ethNode := eth1.NewNode(index, minerEnr)
|
||||||
@@ -221,14 +321,14 @@ func (r *testRunner) testBeaconChainSync(ctx context.Context, g *errgroup.Group,
|
|||||||
return ethNode.Start(ctx)
|
return ethNode.Start(ctx)
|
||||||
})
|
})
|
||||||
if err := helpers.ComponentsStarted(ctx, []e2etypes.ComponentRunner{ethNode}); err != nil {
|
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)
|
syncBeaconNode := components.NewBeaconNode(config, index, bootnodeEnr)
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
return syncBeaconNode.Start(ctx)
|
return syncBeaconNode.Start(ctx)
|
||||||
})
|
})
|
||||||
if err := helpers.ComponentsStarted(ctx, []e2etypes.ComponentRunner{syncBeaconNode}); err != nil {
|
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())
|
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")
|
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")
|
assert.NoError(t, helpers.WaitForTextInFile(syncLogFile, "Synced up to"), "Failed to sync")
|
||||||
})
|
})
|
||||||
if t.Failed() {
|
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.
|
// 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")
|
assert.NoError(t, evaluator.Evaluation(conns...), "Evaluation failed for sync node")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return syncConn, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *testRunner) testDoppelGangerProtection(ctx context.Context) error {
|
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")
|
return errors.Wrap(err, "one or more evaluators failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If requested, run sync test.
|
index := e2e.TestParams.BeaconNodeCount + e2e.TestParams.LighthouseBeaconNodeCount
|
||||||
if !config.TestSync {
|
if config.TestSync {
|
||||||
return nil
|
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 config.TestCheckpointSync {
|
||||||
if err != nil {
|
httpEndpoints := helpers.BeaconAPIHostnames(e2e.TestParams.BeaconNodeCount)
|
||||||
return errors.Wrap(err, "beacon chain sync test failed")
|
menr := eth1Miner.ENR()
|
||||||
}
|
benr := bootNode.ENR()
|
||||||
conns = append(conns, syncConn)
|
if err := r.testCheckpointSync(ctx, g, index, conns, httpEndpoints[0], benr, menr); err != nil {
|
||||||
if err := r.testDoppelGangerProtection(ctx); err != nil {
|
return errors.Wrap(err, "checkpoint sync test failed")
|
||||||
return errors.Wrap(err, "doppel ganger protection check failed")
|
}
|
||||||
|
index += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.ExtraEpochs > 0 {
|
if config.ExtraEpochs > 0 {
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ func allNodesHaveSameHead(conns ...*grpc.ClientConn) error {
|
|||||||
beaconClient := eth.NewBeaconChainClient(conn)
|
beaconClient := eth.NewBeaconChainClient(conn)
|
||||||
chainHead, err := beaconClient.GetChainHead(context.Background(), &emptypb.Empty{})
|
chainHead, err := beaconClient.GetChainHead(context.Background(), &emptypb.Empty{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrapf(err, "connection number=%d", i)
|
||||||
}
|
}
|
||||||
headEpochs[i] = chainHead.HeadEpoch
|
headEpochs[i] = chainHead.HeadEpoch
|
||||||
justifiedRoots[i] = chainHead.JustifiedBlockRoot
|
justifiedRoots[i] = chainHead.JustifiedBlockRoot
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -269,6 +271,16 @@ func NewLocalConnections(ctx context.Context, numConns int) ([]*grpc.ClientConn,
|
|||||||
}, nil
|
}, 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.
|
// ComponentsStarted checks, sequentially, each provided component, blocks until all of the components are ready.
|
||||||
func ComponentsStarted(ctx context.Context, comps []e2etypes.ComponentRunner) error {
|
func ComponentsStarted(ctx context.Context, comps []e2etypes.ComponentRunner) error {
|
||||||
for _, comp := range comps {
|
for _, comp := range comps {
|
||||||
|
|||||||
@@ -2,12 +2,18 @@ package endtoend
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/testing/endtoend/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEndToEnd_MinimalConfig(t *testing.T) {
|
func TestEndToEnd_MinimalConfig(t *testing.T) {
|
||||||
e2eMinimal(t, false, 3).run()
|
e2eMinimal(t).run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEndToEnd_MinimalConfig_Web3Signer(t *testing.T) {
|
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"
|
import "testing"
|
||||||
|
|
||||||
func TestEndToEnd_ScenarioRun_BeaconOffline(t *testing.T) {
|
func TestEndToEnd_ScenarioRun_BeaconOffline(t *testing.T) {
|
||||||
runner := e2eMinimal(t, false, 0)
|
runner := e2eMinimal(t)
|
||||||
|
|
||||||
runner.config.Evaluators = scenarioEvals()
|
runner.config.Evaluators = scenarioEvals()
|
||||||
runner.config.EvalInterceptor = runner.singleNodeOffline
|
runner.config.EvalInterceptor = runner.singleNodeOffline
|
||||||
@@ -11,7 +11,7 @@ func TestEndToEnd_ScenarioRun_BeaconOffline(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEndToEnd_ScenarioRun_AllvalidatorsOffline(t *testing.T) {
|
func TestEndToEnd_ScenarioRun_AllvalidatorsOffline(t *testing.T) {
|
||||||
runner := e2eMinimal(t, false, 0)
|
runner := e2eMinimal(t)
|
||||||
|
|
||||||
runner.config.Evaluators = scenarioEvals()
|
runner.config.Evaluators = scenarioEvals()
|
||||||
runner.config.EvalInterceptor = runner.allValidatorsOffline
|
runner.config.EvalInterceptor = runner.allValidatorsOffline
|
||||||
@@ -20,7 +20,7 @@ func TestEndToEnd_ScenarioRun_AllvalidatorsOffline(t *testing.T) {
|
|||||||
|
|
||||||
func TestEndToEnd_ScenarioRun_EEOffline(t *testing.T) {
|
func TestEndToEnd_ScenarioRun_EEOffline(t *testing.T) {
|
||||||
t.Skip("TODO(#10242) Prysm is current unable to handle an offline e2e")
|
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.Evaluators = scenarioEvals()
|
||||||
runner.config.EvalInterceptor = runner.eeOffline
|
runner.config.EvalInterceptor = runner.eeOffline
|
||||||
|
|||||||
@@ -9,8 +9,29 @@ import (
|
|||||||
"google.golang.org/grpc"
|
"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.
|
// E2EConfig defines the struct for all configurations needed for E2E testing.
|
||||||
type E2EConfig struct {
|
type E2EConfig struct {
|
||||||
|
TestCheckpointSync bool
|
||||||
TestSync bool
|
TestSync bool
|
||||||
TestFeature bool
|
TestFeature bool
|
||||||
UsePrysmShValidator bool
|
UsePrysmShValidator bool
|
||||||
|
|||||||
Reference in New Issue
Block a user