mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 13:58:09 -05:00
Compare commits
28 Commits
kiln
...
consensus-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c245c55dc5 | ||
|
|
7f53700306 | ||
|
|
3b69f7a196 | ||
|
|
72562dcf3a | ||
|
|
cc23b8311a | ||
|
|
cbe54fe3f9 | ||
|
|
1b6adca3ca | ||
|
|
1651649e5a | ||
|
|
56187edb98 | ||
|
|
ecad5bbffc | ||
|
|
407182387b | ||
|
|
ad0b0b503d | ||
|
|
58f4ba758c | ||
|
|
64f64f06bf | ||
|
|
e70055733f | ||
|
|
36e4f49af0 | ||
|
|
d98428dec4 | ||
|
|
00b92e01d3 | ||
|
|
ca5adbf7e4 | ||
|
|
a083b7a0a5 | ||
|
|
dd5995b665 | ||
|
|
6903d52dde | ||
|
|
ac8d27bcf1 | ||
|
|
8d6afb3afd | ||
|
|
d51f716675 | ||
|
|
0411b7eceb | ||
|
|
8dfc80187d | ||
|
|
3833f78803 |
22
.github/workflows/horusec.yaml
vendored
Normal file
22
.github/workflows/horusec.yaml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Horusec Security Scan
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Runs cron at 16.00 UTC on
|
||||
- cron: '0 0 * * SUN'
|
||||
|
||||
jobs:
|
||||
Horusec_Scan:
|
||||
name: horusec-Scan
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/develop'
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with: # Required when commit authors is enabled
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Running Security Scan
|
||||
run: |
|
||||
curl -fsSL https://raw.githubusercontent.com/ZupIT/horusec/main/deployments/scripts/install.sh | bash -s latest
|
||||
horusec start -t="10000" -p="./" -e="true" -i="**/crypto/bls/herumi/**, **/**/*_test.go, **/third_party/afl/**, **/crypto/keystore/key.go"
|
||||
13
BUILD.bazel
13
BUILD.bazel
@@ -115,18 +115,19 @@ nogo(
|
||||
"@org_golang_x_tools//go/analysis/passes/assign:go_default_library",
|
||||
"@org_golang_x_tools//go/analysis/passes/inspect:go_default_library",
|
||||
"@org_golang_x_tools//go/analysis/passes/asmdecl:go_default_library",
|
||||
"//tools/analyzers/maligned:go_default_library",
|
||||
"//tools/analyzers/comparesame:go_default_library",
|
||||
"//tools/analyzers/cryptorand:go_default_library",
|
||||
"//tools/analyzers/errcheck:go_default_library",
|
||||
"//tools/analyzers/featureconfig:go_default_library",
|
||||
"//tools/analyzers/comparesame:go_default_library",
|
||||
"//tools/analyzers/shadowpredecl:go_default_library",
|
||||
"//tools/analyzers/nop:go_default_library",
|
||||
"//tools/analyzers/slicedirect:go_default_library",
|
||||
"//tools/analyzers/interfacechecker:go_default_library",
|
||||
"//tools/analyzers/gocognit:go_default_library",
|
||||
"//tools/analyzers/ineffassign:go_default_library",
|
||||
"//tools/analyzers/interfacechecker:go_default_library",
|
||||
"//tools/analyzers/maligned:go_default_library",
|
||||
"//tools/analyzers/nop:go_default_library",
|
||||
"//tools/analyzers/properpermissions:go_default_library",
|
||||
"//tools/analyzers/recursivelock:go_default_library",
|
||||
"//tools/analyzers/shadowpredecl:go_default_library",
|
||||
"//tools/analyzers/slicedirect:go_default_library",
|
||||
"//tools/analyzers/uintcast:go_default_library",
|
||||
] + select({
|
||||
# nogo checks that fail with coverage enabled.
|
||||
|
||||
@@ -176,7 +176,7 @@ load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_depe
|
||||
go_rules_dependencies()
|
||||
|
||||
go_register_toolchains(
|
||||
go_version = "1.17.6",
|
||||
go_version = "1.17.9",
|
||||
nogo = "@//:nogo",
|
||||
)
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ go_library(
|
||||
"//beacon-chain/core/transition:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/db/filters:go_default_library",
|
||||
"//beacon-chain/db/kv:go_default_library",
|
||||
"//beacon-chain/forkchoice:go_default_library",
|
||||
"//beacon-chain/forkchoice/doubly-linked-tree:go_default_library",
|
||||
"//beacon-chain/forkchoice/protoarray:go_default_library",
|
||||
@@ -65,6 +66,7 @@ go_library(
|
||||
"//config/params:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//math:go_default_library",
|
||||
"//monitoring/tracing:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/eth/v1:go_default_library",
|
||||
|
||||
@@ -273,13 +273,13 @@ func (s *Service) CurrentFork() *ethpb.Fork {
|
||||
|
||||
// IsCanonical returns true if the input block root is part of the canonical chain.
|
||||
func (s *Service) IsCanonical(ctx context.Context, blockRoot [32]byte) (bool, error) {
|
||||
// If the block has been finalized, the block will always be part of the canonical chain.
|
||||
if s.cfg.BeaconDB.IsFinalizedBlock(ctx, blockRoot) {
|
||||
return true, nil
|
||||
// If the block has not been finalized, check fork choice store to see if the block is canonical
|
||||
if s.cfg.ForkChoiceStore.HasNode(blockRoot) {
|
||||
return s.cfg.ForkChoiceStore.IsCanonical(blockRoot), nil
|
||||
}
|
||||
|
||||
// If the block has not been finalized, check fork choice store to see if the block is canonical
|
||||
return s.cfg.ForkChoiceStore.IsCanonical(blockRoot), nil
|
||||
// If the block has been finalized, the block will always be part of the canonical chain.
|
||||
return s.cfg.BeaconDB.IsFinalizedBlock(ctx, blockRoot), nil
|
||||
}
|
||||
|
||||
// ChainHeads returns all possible chain heads (leaves of fork choice tree).
|
||||
|
||||
@@ -19,10 +19,11 @@ func TestHeadSlot_DataRace(t *testing.T) {
|
||||
}
|
||||
b, err := wrapper.WrappedSignedBeaconBlock(util.NewBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
wait := make(chan struct{})
|
||||
go func() {
|
||||
defer close(wait)
|
||||
require.NoError(t, s.saveHead(context.Background(), [32]byte{}, b))
|
||||
require.NoError(t, s.saveHead(context.Background(), [32]byte{}, b, st))
|
||||
}()
|
||||
s.HeadSlot()
|
||||
<-wait
|
||||
@@ -37,9 +38,11 @@ func TestHeadRoot_DataRace(t *testing.T) {
|
||||
b, err := wrapper.WrappedSignedBeaconBlock(util.NewBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
wait := make(chan struct{})
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
go func() {
|
||||
defer close(wait)
|
||||
require.NoError(t, s.saveHead(context.Background(), [32]byte{}, b))
|
||||
require.NoError(t, s.saveHead(context.Background(), [32]byte{}, b, st))
|
||||
|
||||
}()
|
||||
_, err = s.HeadRoot(context.Background())
|
||||
require.NoError(t, err)
|
||||
@@ -57,9 +60,11 @@ func TestHeadBlock_DataRace(t *testing.T) {
|
||||
b, err := wrapper.WrappedSignedBeaconBlock(util.NewBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
wait := make(chan struct{})
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
go func() {
|
||||
defer close(wait)
|
||||
require.NoError(t, s.saveHead(context.Background(), [32]byte{}, b))
|
||||
require.NoError(t, s.saveHead(context.Background(), [32]byte{}, b, st))
|
||||
|
||||
}()
|
||||
_, err = s.HeadBlock(context.Background())
|
||||
require.NoError(t, err)
|
||||
@@ -74,9 +79,11 @@ func TestHeadState_DataRace(t *testing.T) {
|
||||
b, err := wrapper.WrappedSignedBeaconBlock(util.NewBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
wait := make(chan struct{})
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
go func() {
|
||||
defer close(wait)
|
||||
require.NoError(t, s.saveHead(context.Background(), [32]byte{}, b))
|
||||
require.NoError(t, s.saveHead(context.Background(), [32]byte{}, b, st))
|
||||
|
||||
}()
|
||||
_, err = s.HeadState(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -44,7 +44,11 @@ func (s *Service) UpdateAndSaveHeadWithBalances(ctx context.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.saveHead(ctx, headRoot, headBlock)
|
||||
headState, err := s.cfg.StateGen.StateByRoot(ctx, headRoot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not retrieve head state in DB")
|
||||
}
|
||||
return s.saveHead(ctx, headRoot, headBlock, headState)
|
||||
}
|
||||
|
||||
// This defines the current chain service's view of head.
|
||||
@@ -101,7 +105,7 @@ func (s *Service) updateHead(ctx context.Context, balances []uint64) ([32]byte,
|
||||
|
||||
// This saves head info to the local service cache, it also saves the
|
||||
// new head root to the DB.
|
||||
func (s *Service) saveHead(ctx context.Context, headRoot [32]byte, headBlock block.SignedBeaconBlock) error {
|
||||
func (s *Service) saveHead(ctx context.Context, headRoot [32]byte, headBlock block.SignedBeaconBlock, headState state.BeaconState) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.saveHead")
|
||||
defer span.End()
|
||||
|
||||
@@ -116,6 +120,9 @@ func (s *Service) saveHead(ctx context.Context, headRoot [32]byte, headBlock blo
|
||||
if err := helpers.BeaconBlockIsNil(headBlock); err != nil {
|
||||
return err
|
||||
}
|
||||
if headState == nil || headState.IsNil() {
|
||||
return errors.New("cannot save nil head state")
|
||||
}
|
||||
|
||||
// If the head state is not available, just return nil.
|
||||
// There's nothing to cache
|
||||
@@ -123,15 +130,6 @@ func (s *Service) saveHead(ctx context.Context, headRoot [32]byte, headBlock blo
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the new head state from cached state or DB.
|
||||
newHeadState, err := s.cfg.StateGen.StateByRoot(ctx, headRoot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not retrieve head state in DB")
|
||||
}
|
||||
if newHeadState == nil || newHeadState.IsNil() {
|
||||
return errors.New("cannot save nil head state")
|
||||
}
|
||||
|
||||
// A chain re-org occurred, so we fire an event notifying the rest of the services.
|
||||
headSlot := s.HeadSlot()
|
||||
newHeadSlot := headBlock.Block().Slot()
|
||||
@@ -170,7 +168,7 @@ func (s *Service) saveHead(ctx context.Context, headRoot [32]byte, headBlock blo
|
||||
}
|
||||
|
||||
// Cache the new head info.
|
||||
s.setHead(headRoot, headBlock, newHeadState)
|
||||
s.setHead(headRoot, headBlock, headState)
|
||||
|
||||
// Save the new head root to DB.
|
||||
if err := s.cfg.BeaconDB.SaveHeadBlockRoot(ctx, headRoot); err != nil {
|
||||
@@ -180,7 +178,7 @@ func (s *Service) saveHead(ctx context.Context, headRoot [32]byte, headBlock blo
|
||||
// Forward an event capturing a new chain head over a common event feed
|
||||
// done in a goroutine to avoid blocking the critical runtime main routine.
|
||||
go func() {
|
||||
if err := s.notifyNewHeadEvent(ctx, newHeadSlot, newHeadState, newStateRoot, headRoot[:]); err != nil {
|
||||
if err := s.notifyNewHeadEvent(ctx, newHeadSlot, headState, newStateRoot, headRoot[:]); err != nil {
|
||||
log.WithError(err).Error("Could not notify event feed of new chain head")
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -31,7 +31,8 @@ func TestSaveHead_Same(t *testing.T) {
|
||||
service.head = &head{slot: 0, root: r}
|
||||
b, err := wrapper.WrappedSignedBeaconBlock(util.NewBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.saveHead(context.Background(), r, b))
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
require.NoError(t, service.saveHead(context.Background(), r, b, st))
|
||||
assert.Equal(t, types.Slot(0), service.headSlot(), "Head did not stay the same")
|
||||
assert.Equal(t, r, service.headRoot(), "Head did not stay the same")
|
||||
}
|
||||
@@ -69,7 +70,7 @@ func TestSaveHead_Different(t *testing.T) {
|
||||
require.NoError(t, headState.SetSlot(1))
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(context.Background(), ðpb.StateSummary{Slot: 1, Root: newRoot[:]}))
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(context.Background(), headState, newRoot))
|
||||
require.NoError(t, service.saveHead(context.Background(), newRoot, wsb))
|
||||
require.NoError(t, service.saveHead(context.Background(), newRoot, wsb, headState))
|
||||
|
||||
assert.Equal(t, types.Slot(1), service.HeadSlot(), "Head did not change")
|
||||
|
||||
@@ -115,7 +116,7 @@ func TestSaveHead_Different_Reorg(t *testing.T) {
|
||||
require.NoError(t, headState.SetSlot(1))
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(context.Background(), ðpb.StateSummary{Slot: 1, Root: newRoot[:]}))
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(context.Background(), headState, newRoot))
|
||||
require.NoError(t, service.saveHead(context.Background(), newRoot, wsb))
|
||||
require.NoError(t, service.saveHead(context.Background(), newRoot, wsb, headState))
|
||||
|
||||
assert.Equal(t, types.Slot(1), service.HeadSlot(), "Head did not change")
|
||||
|
||||
@@ -159,7 +160,8 @@ func TestUpdateHead_MissingJustifiedRoot(t *testing.T) {
|
||||
service.store.SetBestJustifiedCheckpt(ðpb.Checkpoint{})
|
||||
headRoot, err := service.updateHead(context.Background(), []uint64{})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.saveHead(context.Background(), headRoot, wsb))
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
require.NoError(t, service.saveHead(context.Background(), headRoot, wsb, st))
|
||||
}
|
||||
|
||||
func Test_notifyNewHeadEvent(t *testing.T) {
|
||||
|
||||
@@ -138,6 +138,26 @@ var (
|
||||
Name: "state_balance_cache_miss",
|
||||
Help: "Count the number of state balance cache hits.",
|
||||
})
|
||||
newPayloadValidNodeCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "new_payload_valid_node_count",
|
||||
Help: "Count the number of valid nodes after newPayload EE call",
|
||||
})
|
||||
newPayloadOptimisticNodeCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "new_payload_optimistic_node_count",
|
||||
Help: "Count the number of optimistic nodes after newPayload EE call",
|
||||
})
|
||||
newPayloadInvalidNodeCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "new_payload_invalid_node_count",
|
||||
Help: "Count the number of invalid nodes after newPayload EE call",
|
||||
})
|
||||
forkchoiceUpdatedValidNodeCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "forkchoice_updated_valid_node_count",
|
||||
Help: "Count the number of valid nodes after forkchoiceUpdated EE call",
|
||||
})
|
||||
forkchoiceUpdatedOptimisticNodeCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "forkchoice_updated_optimistic_node_count",
|
||||
Help: "Count the number of optimistic nodes after forkchoiceUpdated EE call",
|
||||
})
|
||||
)
|
||||
|
||||
// reportSlotMetrics reports slot related metrics.
|
||||
|
||||
@@ -5,14 +5,21 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/time"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
@@ -20,7 +27,7 @@ import (
|
||||
// notifyForkchoiceUpdate signals execution engine the fork choice updates. Execution engine should:
|
||||
// 1. Re-organizes the execution payload chain and corresponding state to make head_block_hash the head.
|
||||
// 2. Applies finality to the execution state: it irreversibly persists the chain of all execution payloads and corresponding state, up to and including finalized_block_hash.
|
||||
func (s *Service) notifyForkchoiceUpdate(ctx context.Context, headBlk block.BeaconBlock, headRoot [32]byte, finalizedRoot [32]byte) (*enginev1.PayloadIDBytes, error) {
|
||||
func (s *Service) notifyForkchoiceUpdate(ctx context.Context, headState state.BeaconState, headBlk block.BeaconBlock, headRoot [32]byte, finalizedRoot [32]byte) (*enginev1.PayloadIDBytes, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.notifyForkchoiceUpdate")
|
||||
defer span.End()
|
||||
|
||||
@@ -66,24 +73,36 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, headBlk block.Beac
|
||||
FinalizedBlockHash: finalizedHash,
|
||||
}
|
||||
|
||||
// payload attribute is only required when requesting payload, here we are just updating fork choice, so it is nil.
|
||||
payloadID, _, err := s.cfg.ExecutionEngineCaller.ForkchoiceUpdated(ctx, fcs, nil /*payload attribute*/)
|
||||
nextSlot := s.CurrentSlot() + 1 // Cache payload ID for next slot proposer.
|
||||
hasAttr, attr, proposerId, err := s.getPayloadAttribute(ctx, headState, nextSlot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get payload attribute")
|
||||
}
|
||||
|
||||
payloadID, _, err := s.cfg.ExecutionEngineCaller.ForkchoiceUpdated(ctx, fcs, attr)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case powchain.ErrAcceptedSyncingPayloadStatus:
|
||||
forkchoiceUpdatedOptimisticNodeCount.Inc()
|
||||
log.WithFields(logrus.Fields{
|
||||
"headSlot": headBlk.Slot(),
|
||||
"headHash": fmt.Sprintf("%#x", bytesutil.Trunc(headPayload.BlockHash)),
|
||||
"finalizedHash": fmt.Sprintf("%#x", bytesutil.Trunc(finalizedHash)),
|
||||
"headSlot": headBlk.Slot(),
|
||||
"headPayloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(headPayload.BlockHash)),
|
||||
"finalizedPayloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(finalizedHash)),
|
||||
}).Info("Called fork choice updated with optimistic block")
|
||||
return payloadID, nil
|
||||
default:
|
||||
return nil, errors.Wrap(err, "could not notify forkchoice update from execution engine")
|
||||
}
|
||||
}
|
||||
forkchoiceUpdatedValidNodeCount.Inc()
|
||||
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, headRoot); err != nil {
|
||||
return nil, errors.Wrap(err, "could not set block to valid")
|
||||
}
|
||||
if hasAttr { // If the forkchoice update call has an attribute, update the proposer payload ID cache.
|
||||
var pId [8]byte
|
||||
copy(pId[:], payloadID[:])
|
||||
s.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(nextSlot, proposerId, pId)
|
||||
}
|
||||
return payloadID, nil
|
||||
}
|
||||
|
||||
@@ -114,20 +133,35 @@ func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion, postSta
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "could not get execution payload")
|
||||
}
|
||||
_, err = s.cfg.ExecutionEngineCaller.NewPayload(ctx, payload)
|
||||
lastValidHash, err := s.cfg.ExecutionEngineCaller.NewPayload(ctx, payload)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case powchain.ErrAcceptedSyncingPayloadStatus:
|
||||
newPayloadOptimisticNodeCount.Inc()
|
||||
log.WithFields(logrus.Fields{
|
||||
"slot": blk.Block().Slot(),
|
||||
"blockHash": fmt.Sprintf("%#x", bytesutil.Trunc(payload.BlockHash)),
|
||||
"slot": blk.Block().Slot(),
|
||||
"payloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(payload.BlockHash)),
|
||||
}).Info("Called new payload with optimistic block")
|
||||
return false, nil
|
||||
case powchain.ErrInvalidPayloadStatus:
|
||||
newPayloadInvalidNodeCount.Inc()
|
||||
root, err := blk.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
invalidRoots, err := s.ForkChoicer().SetOptimisticToInvalid(ctx, root, bytesutil.ToBytes32(lastValidHash))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := s.removeInvalidBlockAndState(ctx, invalidRoots); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return false, errors.New("could not validate an INVALID payload from execution engine")
|
||||
default:
|
||||
return false, errors.Wrap(err, "could not validate execution payload from execution engine")
|
||||
}
|
||||
}
|
||||
|
||||
newPayloadValidNodeCount.Inc()
|
||||
// During the transition event, the transition block should be verified for sanity.
|
||||
if blocks.IsPreBellatrixVersion(preStateVersion) {
|
||||
// Handle case where pre-state is Altair but block contains payload.
|
||||
@@ -174,3 +208,69 @@ func (s *Service) optimisticCandidateBlock(ctx context.Context, blk block.Beacon
|
||||
}
|
||||
return parentIsExecutionBlock, nil
|
||||
}
|
||||
|
||||
// getPayloadAttributes returns the payload attributes for the given state and slot.
|
||||
// The attribute is required to initiate a payload build process in the context of an `engine_forkchoiceUpdated` call.
|
||||
func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState, slot types.Slot) (bool, *enginev1.PayloadAttributes, types.ValidatorIndex, error) {
|
||||
proposerID, _, ok := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(slot)
|
||||
if !ok { // There's no need to build attribute if there is no proposer for slot.
|
||||
return false, nil, 0, nil
|
||||
}
|
||||
|
||||
// Get previous randao.
|
||||
st = st.Copy()
|
||||
st, err := transition.ProcessSlotsIfPossible(ctx, st, slot)
|
||||
if err != nil {
|
||||
return false, nil, 0, err
|
||||
}
|
||||
prevRando, err := helpers.RandaoMix(st, time.CurrentEpoch(st))
|
||||
if err != nil {
|
||||
return false, nil, 0, nil
|
||||
}
|
||||
|
||||
// Get fee recipient.
|
||||
feeRecipient := params.BeaconConfig().DefaultFeeRecipient
|
||||
recipient, err := s.cfg.BeaconDB.FeeRecipientByValidatorID(ctx, proposerID)
|
||||
switch {
|
||||
case errors.Is(err, kv.ErrNotFoundFeeRecipient):
|
||||
if feeRecipient.String() == fieldparams.EthBurnAddressHex {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"validatorIndex": proposerID,
|
||||
"burnAddress": fieldparams.EthBurnAddressHex,
|
||||
}).Error("Fee recipient not set. Using burn address")
|
||||
}
|
||||
case err != nil:
|
||||
return false, nil, 0, errors.Wrap(err, "could not get fee recipient in db")
|
||||
default:
|
||||
feeRecipient = recipient
|
||||
}
|
||||
|
||||
// Get timestamp.
|
||||
t, err := slots.ToTime(uint64(s.genesisTime.Unix()), slot)
|
||||
if err != nil {
|
||||
return false, nil, 0, err
|
||||
}
|
||||
attr := &enginev1.PayloadAttributes{
|
||||
Timestamp: uint64(t.Unix()),
|
||||
PrevRandao: prevRando,
|
||||
SuggestedFeeRecipient: feeRecipient.Bytes(),
|
||||
}
|
||||
return true, attr, proposerID, nil
|
||||
}
|
||||
|
||||
// removeInvalidBlockAndState removes the invalid block and its corresponding state from the cache and DB.
|
||||
func (s *Service) removeInvalidBlockAndState(ctx context.Context, blkRoots [][32]byte) error {
|
||||
for _, root := range blkRoots {
|
||||
if err := s.cfg.StateGen.DeleteStateFromCaches(ctx, root); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete block also deletes the state as well.
|
||||
if err := s.cfg.BeaconDB.DeleteBlock(ctx, root); err != nil {
|
||||
// TODO(10487): If a caller requests to delete a root that's justified and finalized. We should gracefully shutdown.
|
||||
// This is an irreparable condition, it would me a justified or finalized block has become invalid.
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray"
|
||||
@@ -24,6 +27,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/testing/util"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
||||
@@ -44,8 +48,13 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
WithForkChoiceStore(fcs),
|
||||
WithProposerIdsCache(cache.NewProposerPayloadIDsCache()),
|
||||
}
|
||||
service, err := NewService(ctx, opts...)
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
service.head = &head{
|
||||
state: st,
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
|
||||
@@ -157,7 +166,8 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
service.cfg.ExecutionEngineCaller = &mockPOW.EngineClient{ErrForkchoiceUpdated: tt.newForkchoiceErr}
|
||||
_, err := service.notifyForkchoiceUpdate(ctx, tt.blk, service.headRoot(), tt.finalizedRoot)
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
_, err := service.notifyForkchoiceUpdate(ctx, st, tt.blk, service.headRoot(), tt.finalizedRoot)
|
||||
if tt.errString != "" {
|
||||
require.ErrorContains(t, tt.errString, err)
|
||||
} else {
|
||||
@@ -183,13 +193,6 @@ func Test_NotifyNewPayload(t *testing.T) {
|
||||
phase0State, _ := util.DeterministicGenesisState(t, 1)
|
||||
altairState, _ := util.DeterministicGenesisStateAltair(t, 1)
|
||||
bellatrixState, _ := util.DeterministicGenesisStateBellatrix(t, 2)
|
||||
blk := ðpb.SignedBeaconBlockBellatrix{
|
||||
Block: ðpb.BeaconBlockBellatrix{
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
ExecutionPayload: &v1.ExecutionPayload{},
|
||||
},
|
||||
},
|
||||
}
|
||||
a := ðpb.SignedBeaconBlockAltair{
|
||||
Block: ðpb.BeaconBlockAltair{
|
||||
Body: ðpb.BeaconBlockBodyAltair{},
|
||||
@@ -197,10 +200,32 @@ func Test_NotifyNewPayload(t *testing.T) {
|
||||
}
|
||||
altairBlk, err := wrapper.WrappedSignedBeaconBlock(a)
|
||||
require.NoError(t, err)
|
||||
bellatrixBlk, err := wrapper.WrappedSignedBeaconBlock(blk)
|
||||
blk := ðpb.SignedBeaconBlockBellatrix{
|
||||
Block: ðpb.BeaconBlockBellatrix{
|
||||
Slot: 1,
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
ExecutionPayload: &v1.ExecutionPayload{
|
||||
BlockNumber: 1,
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
bellatrixBlk, err := wrapper.WrappedSignedBeaconBlock(util.HydrateSignedBeaconBlockBellatrix(blk))
|
||||
require.NoError(t, err)
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
r, err := bellatrixBlk.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 1, r, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -244,7 +269,7 @@ func Test_NotifyNewPayload(t *testing.T) {
|
||||
preState: bellatrixState,
|
||||
blk: bellatrixBlk,
|
||||
newPayloadErr: powchain.ErrInvalidPayloadStatus,
|
||||
errString: "could not validate execution payload from execution engine: payload status is INVALID",
|
||||
errString: "could not validate an INVALID payload from execution engine",
|
||||
isValidPayload: false,
|
||||
},
|
||||
{
|
||||
@@ -573,6 +598,47 @@ func Test_IsOptimisticShallowExecutionParent(t *testing.T) {
|
||||
require.Equal(t, true, candidate)
|
||||
}
|
||||
|
||||
func Test_GetPayloadAttribute(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
WithProposerIdsCache(cache.NewProposerPayloadIDsCache()),
|
||||
}
|
||||
|
||||
// Cache miss
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
hasPayload, _, vId, err := service.getPayloadAttribute(ctx, nil, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, hasPayload)
|
||||
require.Equal(t, types.ValidatorIndex(0), vId)
|
||||
|
||||
// Cache hit, advance state, no fee recipient
|
||||
suggestedVid := types.ValidatorIndex(1)
|
||||
slot := types.Slot(1)
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, suggestedVid, [8]byte{})
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
hook := logTest.NewGlobal()
|
||||
hasPayload, attr, vId, err := service.getPayloadAttribute(ctx, st, slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, hasPayload)
|
||||
require.Equal(t, suggestedVid, vId)
|
||||
require.Equal(t, fieldparams.EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient).String())
|
||||
require.LogsContain(t, hook, "Fee recipient not set. Using burn address")
|
||||
|
||||
// Cache hit, advance state, has fee recipient
|
||||
suggestedAddr := common.HexToAddress("123")
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, []types.ValidatorIndex{suggestedVid}, []common.Address{suggestedAddr}))
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, suggestedVid, [8]byte{})
|
||||
hasPayload, attr, vId, err = service.getPayloadAttribute(ctx, st, slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, hasPayload)
|
||||
require.Equal(t, suggestedVid, vId)
|
||||
require.Equal(t, suggestedAddr, common.BytesToAddress(attr.SuggestedFeeRecipient))
|
||||
}
|
||||
|
||||
func Test_UpdateLastValidatedCheckpoint(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
params.OverrideBeaconConfig(params.MainnetConfig())
|
||||
@@ -588,7 +654,6 @@ func Test_UpdateLastValidatedCheckpoint(t *testing.T) {
|
||||
}
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
|
||||
genesisStateRoot := [32]byte{}
|
||||
genesisBlk := blocks.NewGenesisBlock(genesisStateRoot[:])
|
||||
wr, err := wrapper.WrappedSignedBeaconBlock(genesisBlk)
|
||||
@@ -671,3 +736,59 @@ func Test_UpdateLastValidatedCheckpoint(t *testing.T) {
|
||||
require.DeepEqual(t, validCheckpoint.Root, cp.Root)
|
||||
require.Equal(t, validCheckpoint.Epoch, cp.Epoch)
|
||||
}
|
||||
|
||||
func TestService_removeInvalidBlockAndState(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
WithForkChoiceStore(protoarray.New(0, 0, [32]byte{})),
|
||||
}
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Deleting unknown block should not error.
|
||||
require.NoError(t, service.removeInvalidBlockAndState(ctx, [][32]byte{{'a'}, {'b'}, {'c'}}))
|
||||
|
||||
// Happy case
|
||||
b1 := util.NewBeaconBlock()
|
||||
b1.Block.Slot = 1
|
||||
blk1, err := wrapper.WrappedSignedBeaconBlock(b1)
|
||||
require.NoError(t, err)
|
||||
r1, err := blk1.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, blk1))
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(ctx, ðpb.StateSummary{
|
||||
Slot: 1,
|
||||
Root: r1[:],
|
||||
}))
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, st, r1))
|
||||
|
||||
b2 := util.NewBeaconBlock()
|
||||
b2.Block.Slot = 2
|
||||
blk2, err := wrapper.WrappedSignedBeaconBlock(b2)
|
||||
require.NoError(t, err)
|
||||
r2, err := blk2.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, blk2))
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(ctx, ðpb.StateSummary{
|
||||
Slot: 2,
|
||||
Root: r2[:],
|
||||
}))
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, st, r2))
|
||||
|
||||
require.NoError(t, service.removeInvalidBlockAndState(ctx, [][32]byte{r1, r2}))
|
||||
|
||||
require.Equal(t, false, service.hasBlock(ctx, r1))
|
||||
require.Equal(t, false, service.hasBlock(ctx, r2))
|
||||
require.Equal(t, false, service.cfg.BeaconDB.HasStateSummary(ctx, r1))
|
||||
require.Equal(t, false, service.cfg.BeaconDB.HasStateSummary(ctx, r2))
|
||||
has, err := service.cfg.StateGen.HasState(ctx, r1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, has)
|
||||
has, err = service.cfg.StateGen.HasState(ctx, r2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, has)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package blockchain
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/async/event"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
@@ -66,6 +67,14 @@ func WithDepositCache(c *depositcache.DepositCache) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithProposerIdsCache for proposer id cache.
|
||||
func WithProposerIdsCache(c *cache.ProposerPayloadIDsCache) Option {
|
||||
return func(s *Service) error {
|
||||
s.cfg.ProposerSlotIndexCache = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithAttestationPool for attestation lifecycle after chain inclusion.
|
||||
func WithAttestationPool(p attestations.Pool) Option {
|
||||
return func(s *Service) error {
|
||||
|
||||
@@ -127,8 +127,8 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.savePostStateInfo(ctx, blockRoot, signed, postState, false /* reg sync */); err != nil {
|
||||
return err
|
||||
if err := s.insertBlockAndAttestationsToForkChoiceStore(ctx, signed.Block(), blockRoot, postState); err != nil {
|
||||
return errors.Wrapf(err, "could not insert block %d to fork choice store", signed.Block().Slot())
|
||||
}
|
||||
if isValidPayload {
|
||||
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, blockRoot); err != nil {
|
||||
@@ -148,6 +148,9 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.savePostStateInfo(ctx, blockRoot, signed, postState, false /* reg sync */); err != nil {
|
||||
return err
|
||||
}
|
||||
// If slasher is configured, forward the attestations in the block via
|
||||
// an event feed for processing.
|
||||
if features.Get().EnableSlasher {
|
||||
@@ -212,10 +215,14 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := s.notifyForkchoiceUpdate(ctx, headBlock.Block(), headRoot, bytesutil.ToBytes32(finalized.Root)); err != nil {
|
||||
headState, err := s.cfg.StateGen.StateByRoot(ctx, headRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.saveHead(ctx, headRoot, headBlock); err != nil {
|
||||
if _, err := s.notifyForkchoiceUpdate(ctx, headState, headBlock.Block(), headRoot, bytesutil.ToBytes32(finalized.Root)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.saveHead(ctx, headRoot, headBlock, headState); err != nil {
|
||||
return errors.Wrap(err, "could not save head")
|
||||
}
|
||||
|
||||
@@ -428,7 +435,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []block.SignedBeaconBlo
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := s.notifyForkchoiceUpdate(ctx, b.Block(), blockRoots[i], bytesutil.ToBytes32(fCheckpoints[i].Root)); err != nil {
|
||||
if _, err := s.notifyForkchoiceUpdate(ctx, preState, b.Block(), blockRoots[i], bytesutil.ToBytes32(fCheckpoints[i].Root)); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
@@ -605,9 +612,6 @@ func (s *Service) savePostStateInfo(ctx context.Context, r [32]byte, b block.Sig
|
||||
if err := s.cfg.StateGen.SaveState(ctx, r, st); err != nil {
|
||||
return errors.Wrap(err, "could not save state")
|
||||
}
|
||||
if err := s.insertBlockAndAttestationsToForkChoiceStore(ctx, b.Block(), r, st); err != nil {
|
||||
return errors.Wrapf(err, "could not insert block %d to fork choice store", b.Block().Slot())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
mathutil "github.com/prysmaticlabs/prysm/math"
|
||||
"github.com/prysmaticlabs/prysm/monitoring/tracing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
@@ -402,10 +403,17 @@ func (s *Service) insertFinalizedDeposits(ctx context.Context, fRoot [32]byte) e
|
||||
// We update the cache up to the last deposit index in the finalized block's state.
|
||||
// We can be confident that these deposits will be included in some block
|
||||
// because the Eth1 follow distance makes such long-range reorgs extremely unlikely.
|
||||
eth1DepositIndex := int64(finalizedState.Eth1Data().DepositCount - 1)
|
||||
s.cfg.DepositCache.InsertFinalizedDeposits(ctx, eth1DepositIndex)
|
||||
eth1DepositIndex, err := mathutil.Int(finalizedState.Eth1DepositIndex())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not cast eth1 deposit index")
|
||||
}
|
||||
// The deposit index in the state is always the index of the next deposit
|
||||
// to be included(rather than the last one to be processed). This was most likely
|
||||
// done as the state cannot represent signed integers.
|
||||
eth1DepositIndex -= 1
|
||||
s.cfg.DepositCache.InsertFinalizedDeposits(ctx, int64(eth1DepositIndex))
|
||||
// Deposit proofs are only used during state transition and can be safely removed to save space.
|
||||
if err = s.cfg.DepositCache.PruneProofs(ctx, eth1DepositIndex); err != nil {
|
||||
if err = s.cfg.DepositCache.PruneProofs(ctx, int64(eth1DepositIndex)); err != nil {
|
||||
return errors.Wrap(err, "could not prune deposit proofs")
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -1516,6 +1516,7 @@ func TestInsertFinalizedDeposits(t *testing.T) {
|
||||
service.store.SetFinalizedCheckpt(ðpb.Checkpoint{Root: gRoot[:]})
|
||||
gs = gs.Copy()
|
||||
assert.NoError(t, gs.SetEth1Data(ðpb.Eth1Data{DepositCount: 10}))
|
||||
assert.NoError(t, gs.SetEth1DepositIndex(8))
|
||||
assert.NoError(t, service.cfg.StateGen.SaveState(ctx, [32]byte{'m', 'o', 'c', 'k'}, gs))
|
||||
zeroSig := [96]byte{}
|
||||
for i := uint64(0); i < uint64(4*params.BeaconConfig().SlotsPerEpoch); i++ {
|
||||
@@ -1529,8 +1530,64 @@ func TestInsertFinalizedDeposits(t *testing.T) {
|
||||
}
|
||||
assert.NoError(t, service.insertFinalizedDeposits(ctx, [32]byte{'m', 'o', 'c', 'k'}))
|
||||
fDeposits := depositCache.FinalizedDeposits(ctx)
|
||||
assert.Equal(t, 9, int(fDeposits.MerkleTrieIndex), "Finalized deposits not inserted correctly")
|
||||
deps := depositCache.AllDeposits(ctx, big.NewInt(109))
|
||||
assert.Equal(t, 7, int(fDeposits.MerkleTrieIndex), "Finalized deposits not inserted correctly")
|
||||
deps := depositCache.AllDeposits(ctx, big.NewInt(107))
|
||||
for _, d := range deps {
|
||||
assert.DeepEqual(t, [][]byte(nil), d.Proof, "Proofs are not empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInsertFinalizedDeposits_MultipleFinalizedRoutines(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
opts := testServiceOptsWithDB(t)
|
||||
depositCache, err := depositcache.New()
|
||||
require.NoError(t, err)
|
||||
opts = append(opts, WithDepositCache(depositCache))
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
|
||||
gs, _ := util.DeterministicGenesisState(t, 32)
|
||||
require.NoError(t, service.saveGenesisData(ctx, gs))
|
||||
gBlk, err := service.cfg.BeaconDB.GenesisBlock(ctx)
|
||||
require.NoError(t, err)
|
||||
gRoot, err := gBlk.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
service.store.SetFinalizedCheckpt(ðpb.Checkpoint{Root: gRoot[:]})
|
||||
gs = gs.Copy()
|
||||
assert.NoError(t, gs.SetEth1Data(ðpb.Eth1Data{DepositCount: 7}))
|
||||
assert.NoError(t, gs.SetEth1DepositIndex(6))
|
||||
assert.NoError(t, service.cfg.StateGen.SaveState(ctx, [32]byte{'m', 'o', 'c', 'k'}, gs))
|
||||
gs2 := gs.Copy()
|
||||
assert.NoError(t, gs2.SetEth1Data(ðpb.Eth1Data{DepositCount: 15}))
|
||||
assert.NoError(t, gs2.SetEth1DepositIndex(13))
|
||||
assert.NoError(t, service.cfg.StateGen.SaveState(ctx, [32]byte{'m', 'o', 'c', 'k', '2'}, gs2))
|
||||
zeroSig := [96]byte{}
|
||||
for i := uint64(0); i < uint64(4*params.BeaconConfig().SlotsPerEpoch); i++ {
|
||||
root := []byte(strconv.Itoa(int(i)))
|
||||
assert.NoError(t, depositCache.InsertDeposit(ctx, ðpb.Deposit{Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.FromBytes48([fieldparams.BLSPubkeyLength]byte{}),
|
||||
WithdrawalCredentials: params.BeaconConfig().ZeroHash[:],
|
||||
Amount: 0,
|
||||
Signature: zeroSig[:],
|
||||
}, Proof: [][]byte{root}}, 100+i, int64(i), bytesutil.ToBytes32(root)))
|
||||
}
|
||||
// Insert 3 deposits before hand.
|
||||
depositCache.InsertFinalizedDeposits(ctx, 2)
|
||||
|
||||
assert.NoError(t, service.insertFinalizedDeposits(ctx, [32]byte{'m', 'o', 'c', 'k'}))
|
||||
fDeposits := depositCache.FinalizedDeposits(ctx)
|
||||
assert.Equal(t, 5, int(fDeposits.MerkleTrieIndex), "Finalized deposits not inserted correctly")
|
||||
|
||||
deps := depositCache.AllDeposits(ctx, big.NewInt(105))
|
||||
for _, d := range deps {
|
||||
assert.DeepEqual(t, [][]byte(nil), d.Proof, "Proofs are not empty")
|
||||
}
|
||||
|
||||
// Insert New Finalized State with higher deposit count.
|
||||
assert.NoError(t, service.insertFinalizedDeposits(ctx, [32]byte{'m', 'o', 'c', 'k', '2'}))
|
||||
fDeposits = depositCache.FinalizedDeposits(ctx)
|
||||
assert.Equal(t, 12, int(fDeposits.MerkleTrieIndex), "Finalized deposits not inserted correctly")
|
||||
deps = depositCache.AllDeposits(ctx, big.NewInt(112))
|
||||
for _, d := range deps {
|
||||
assert.DeepEqual(t, [][]byte(nil), d.Proof, "Proofs are not empty")
|
||||
}
|
||||
|
||||
@@ -83,7 +83,8 @@ func (s *Service) VerifyLmdFfgConsistency(ctx context.Context, a *ethpb.Attestat
|
||||
func (s *Service) VerifyFinalizedConsistency(ctx context.Context, root []byte) error {
|
||||
// A canonical root implies the root to has an ancestor that aligns with finalized check point.
|
||||
// In this case, we could exit early to save on additional computation.
|
||||
if s.cfg.ForkChoiceStore.IsCanonical(bytesutil.ToBytes32(root)) {
|
||||
blockRoot := bytesutil.ToBytes32(root)
|
||||
if s.cfg.ForkChoiceStore.HasNode(blockRoot) && s.cfg.ForkChoiceStore.IsCanonical(blockRoot) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -186,7 +187,13 @@ func (s *Service) notifyEngineIfChangedHead(ctx context.Context, newHeadRoot [32
|
||||
log.WithError(err).Error("Could not get block from db")
|
||||
return
|
||||
}
|
||||
headState, err := s.cfg.StateGen.StateByRoot(ctx, newHeadRoot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get state from db")
|
||||
return
|
||||
}
|
||||
_, err = s.notifyForkchoiceUpdate(s.ctx,
|
||||
headState,
|
||||
newHeadBlock.Block(),
|
||||
newHeadRoot,
|
||||
bytesutil.ToBytes32(finalized.Root),
|
||||
@@ -194,7 +201,7 @@ func (s *Service) notifyEngineIfChangedHead(ctx context.Context, newHeadRoot [32
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not notify forkchoice update")
|
||||
}
|
||||
if err := s.saveHead(ctx, newHeadRoot, newHeadBlock); err != nil {
|
||||
if err := s.saveHead(ctx, newHeadRoot, newHeadBlock, headState); err != nil {
|
||||
log.WithError(err).Error("could not save head")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
|
||||
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
@@ -133,7 +134,7 @@ func TestNotifyEngineIfChangedHead(t *testing.T) {
|
||||
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
|
||||
service.cfg.ProposerSlotIndexCache = cache.NewProposerPayloadIDsCache()
|
||||
service.notifyEngineIfChangedHead(ctx, service.headRoot())
|
||||
hookErr := "could not notify forkchoice update"
|
||||
finalizedErr := "could not get finalized checkpoint"
|
||||
@@ -156,9 +157,20 @@ func TestNotifyEngineIfChangedHead(t *testing.T) {
|
||||
r1, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
finalized := ðpb.Checkpoint{Root: r1[:], Epoch: 0}
|
||||
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
service.head = &head{
|
||||
slot: 1,
|
||||
root: r1,
|
||||
block: wsb,
|
||||
state: st,
|
||||
}
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(2, 1, [8]byte{1})
|
||||
service.store.SetFinalizedCheckpt(finalized)
|
||||
service.notifyEngineIfChangedHead(ctx, r1)
|
||||
require.LogsDoNotContain(t, hook, finalizedErr)
|
||||
require.LogsDoNotContain(t, hook, hookErr)
|
||||
vId, payloadID, has := service.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(2)
|
||||
require.Equal(t, true, has)
|
||||
require.Equal(t, types.ValidatorIndex(1), vId)
|
||||
require.Equal(t, [8]byte{1}, payloadID)
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ type config struct {
|
||||
ChainStartFetcher powchain.ChainStartFetcher
|
||||
BeaconDB db.HeadAccessDatabase
|
||||
DepositCache *depositcache.DepositCache
|
||||
ProposerSlotIndexCache *cache.ProposerPayloadIDsCache
|
||||
AttPool attestations.Pool
|
||||
ExitPool voluntaryexits.PoolManager
|
||||
SlashingPool slashings.PoolManager
|
||||
|
||||
@@ -23,9 +23,10 @@ func TestChainService_SaveHead_DataRace(t *testing.T) {
|
||||
cfg: &config{BeaconDB: beaconDB},
|
||||
}
|
||||
b, err := wrapper.WrappedSignedBeaconBlock(util.NewBeaconBlock())
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
require.NoError(t, err)
|
||||
go func() {
|
||||
require.NoError(t, s.saveHead(context.Background(), [32]byte{}, b))
|
||||
require.NoError(t, s.saveHead(context.Background(), [32]byte{}, b, st))
|
||||
}()
|
||||
require.NoError(t, s.saveHead(context.Background(), [32]byte{}, b))
|
||||
require.NoError(t, s.saveHead(context.Background(), [32]byte{}, b, st))
|
||||
}
|
||||
|
||||
3
beacon-chain/cache/BUILD.bazel
vendored
3
beacon-chain/cache/BUILD.bazel
vendored
@@ -13,6 +13,7 @@ go_library(
|
||||
"common.go",
|
||||
"doc.go",
|
||||
"error.go",
|
||||
"payload_id.go",
|
||||
"proposer_indices.go",
|
||||
"proposer_indices_disabled.go", # keep
|
||||
"proposer_indices_type.go",
|
||||
@@ -26,6 +27,7 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/cache",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//tools:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
@@ -61,6 +63,7 @@ go_test(
|
||||
"checkpoint_state_test.go",
|
||||
"committee_fuzz_test.go",
|
||||
"committee_test.go",
|
||||
"payload_id_test.go",
|
||||
"proposer_indices_test.go",
|
||||
"skip_slot_cache_test.go",
|
||||
"subnet_ids_test.go",
|
||||
|
||||
@@ -36,7 +36,7 @@ type DepositFetcher interface {
|
||||
DepositByPubkey(ctx context.Context, pubKey []byte) (*ethpb.Deposit, *big.Int)
|
||||
DepositsNumberAndRootAtHeight(ctx context.Context, blockHeight *big.Int) (uint64, [32]byte)
|
||||
FinalizedDeposits(ctx context.Context) *FinalizedDeposits
|
||||
NonFinalizedDeposits(ctx context.Context, untilBlk *big.Int) []*ethpb.Deposit
|
||||
NonFinalizedDeposits(ctx context.Context, lastFinalizedIndex int64, untilBlk *big.Int) []*ethpb.Deposit
|
||||
}
|
||||
|
||||
// FinalizedDeposits stores the trie of deposits that have been included
|
||||
@@ -137,6 +137,22 @@ func (dc *DepositCache) InsertFinalizedDeposits(ctx context.Context, eth1Deposit
|
||||
|
||||
depositTrie := dc.finalizedDeposits.Deposits
|
||||
insertIndex := int(dc.finalizedDeposits.MerkleTrieIndex + 1)
|
||||
|
||||
// Don't insert into finalized trie if there is no deposit to
|
||||
// insert.
|
||||
if len(dc.deposits) == 0 {
|
||||
return
|
||||
}
|
||||
// In the event we have less deposits than we need to
|
||||
// finalize we finalize till the index on which we do have it.
|
||||
if len(dc.deposits) <= int(eth1DepositIndex) {
|
||||
eth1DepositIndex = int64(len(dc.deposits)) - 1
|
||||
}
|
||||
// If we finalize to some lower deposit index, we
|
||||
// ignore it.
|
||||
if int(eth1DepositIndex) < insertIndex {
|
||||
return
|
||||
}
|
||||
for _, d := range dc.deposits {
|
||||
if d.Index <= dc.finalizedDeposits.MerkleTrieIndex {
|
||||
continue
|
||||
@@ -246,7 +262,7 @@ func (dc *DepositCache) FinalizedDeposits(ctx context.Context) *FinalizedDeposit
|
||||
|
||||
// NonFinalizedDeposits returns the list of non-finalized deposits until the given block number (inclusive).
|
||||
// If no block is specified then this method returns all non-finalized deposits.
|
||||
func (dc *DepositCache) NonFinalizedDeposits(ctx context.Context, untilBlk *big.Int) []*ethpb.Deposit {
|
||||
func (dc *DepositCache) NonFinalizedDeposits(ctx context.Context, lastFinalizedIndex int64, untilBlk *big.Int) []*ethpb.Deposit {
|
||||
ctx, span := trace.StartSpan(ctx, "DepositsCache.NonFinalizedDeposits")
|
||||
defer span.End()
|
||||
dc.depositsLock.RLock()
|
||||
@@ -256,10 +272,9 @@ func (dc *DepositCache) NonFinalizedDeposits(ctx context.Context, untilBlk *big.
|
||||
return dc.allDeposits(untilBlk)
|
||||
}
|
||||
|
||||
lastFinalizedDepositIndex := dc.finalizedDeposits.MerkleTrieIndex
|
||||
var deposits []*ethpb.Deposit
|
||||
for _, d := range dc.deposits {
|
||||
if (d.Index > lastFinalizedDepositIndex) && (untilBlk == nil || untilBlk.Uint64() >= d.Eth1BlockHeight) {
|
||||
if (d.Index > lastFinalizedIndex) && (untilBlk == nil || untilBlk.Uint64() >= d.Eth1BlockHeight) {
|
||||
deposits = append(deposits, d.Deposit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,7 +459,7 @@ func TestFinalizedDeposits_UtilizesPreviouslyCachedDeposits(t *testing.T) {
|
||||
Index: 1,
|
||||
},
|
||||
}
|
||||
newFinalizedDeposit := ethpb.DepositContainer{
|
||||
newFinalizedDeposit := ðpb.DepositContainer{
|
||||
Deposit: ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.PadTo([]byte{2}, 48),
|
||||
@@ -471,17 +471,17 @@ func TestFinalizedDeposits_UtilizesPreviouslyCachedDeposits(t *testing.T) {
|
||||
}
|
||||
dc.deposits = oldFinalizedDeposits
|
||||
dc.InsertFinalizedDeposits(context.Background(), 1)
|
||||
// Artificially exclude old deposits so that they can only be retrieved from previously finalized deposits.
|
||||
dc.deposits = []*ethpb.DepositContainer{&newFinalizedDeposit}
|
||||
|
||||
dc.InsertFinalizedDeposits(context.Background(), 2)
|
||||
|
||||
dc.deposits = append(dc.deposits, []*ethpb.DepositContainer{newFinalizedDeposit}...)
|
||||
|
||||
cachedDeposits := dc.FinalizedDeposits(context.Background())
|
||||
require.NotNil(t, cachedDeposits, "Deposits not cached")
|
||||
assert.Equal(t, int64(2), cachedDeposits.MerkleTrieIndex)
|
||||
assert.Equal(t, int64(1), cachedDeposits.MerkleTrieIndex)
|
||||
|
||||
var deps [][]byte
|
||||
for _, d := range append(oldFinalizedDeposits, &newFinalizedDeposit) {
|
||||
for _, d := range oldFinalizedDeposits {
|
||||
hash, err := d.Deposit.Data.HashTreeRoot()
|
||||
require.NoError(t, err, "Could not hash deposit data")
|
||||
deps = append(deps, hash[:])
|
||||
@@ -491,6 +491,140 @@ func TestFinalizedDeposits_UtilizesPreviouslyCachedDeposits(t *testing.T) {
|
||||
assert.Equal(t, trie.HashTreeRoot(), cachedDeposits.Deposits.HashTreeRoot())
|
||||
}
|
||||
|
||||
func TestFinalizedDeposits_HandleZeroDeposits(t *testing.T) {
|
||||
dc, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
dc.InsertFinalizedDeposits(context.Background(), 2)
|
||||
|
||||
cachedDeposits := dc.FinalizedDeposits(context.Background())
|
||||
require.NotNil(t, cachedDeposits, "Deposits not cached")
|
||||
assert.Equal(t, int64(-1), cachedDeposits.MerkleTrieIndex)
|
||||
}
|
||||
|
||||
func TestFinalizedDeposits_HandleSmallerThanExpectedDeposits(t *testing.T) {
|
||||
dc, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
finalizedDeposits := []*ethpb.DepositContainer{
|
||||
{
|
||||
Deposit: ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.PadTo([]byte{0}, 48),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
},
|
||||
},
|
||||
Index: 0,
|
||||
},
|
||||
{
|
||||
Deposit: ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.PadTo([]byte{1}, 48),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
},
|
||||
},
|
||||
Index: 1,
|
||||
},
|
||||
{
|
||||
Deposit: ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.PadTo([]byte{2}, 48),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
},
|
||||
},
|
||||
Index: 2,
|
||||
},
|
||||
}
|
||||
dc.deposits = finalizedDeposits
|
||||
|
||||
dc.InsertFinalizedDeposits(context.Background(), 5)
|
||||
|
||||
cachedDeposits := dc.FinalizedDeposits(context.Background())
|
||||
require.NotNil(t, cachedDeposits, "Deposits not cached")
|
||||
assert.Equal(t, int64(2), cachedDeposits.MerkleTrieIndex)
|
||||
}
|
||||
|
||||
func TestFinalizedDeposits_HandleLowerEth1DepositIndex(t *testing.T) {
|
||||
dc, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
finalizedDeposits := []*ethpb.DepositContainer{
|
||||
{
|
||||
Deposit: ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.PadTo([]byte{0}, 48),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
},
|
||||
},
|
||||
Index: 0,
|
||||
},
|
||||
{
|
||||
Deposit: ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.PadTo([]byte{1}, 48),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
},
|
||||
},
|
||||
Index: 1,
|
||||
},
|
||||
{
|
||||
Deposit: ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.PadTo([]byte{2}, 48),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
},
|
||||
},
|
||||
Index: 2,
|
||||
},
|
||||
{
|
||||
Deposit: ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.PadTo([]byte{3}, 48),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
},
|
||||
},
|
||||
Index: 3,
|
||||
},
|
||||
{
|
||||
Deposit: ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.PadTo([]byte{4}, 48),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
},
|
||||
},
|
||||
Index: 4,
|
||||
},
|
||||
{
|
||||
Deposit: ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.PadTo([]byte{5}, 48),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
},
|
||||
},
|
||||
Index: 5,
|
||||
},
|
||||
}
|
||||
dc.deposits = finalizedDeposits
|
||||
|
||||
dc.InsertFinalizedDeposits(context.Background(), 5)
|
||||
|
||||
// Reinsert finalized deposits with a lower index.
|
||||
dc.InsertFinalizedDeposits(context.Background(), 2)
|
||||
|
||||
cachedDeposits := dc.FinalizedDeposits(context.Background())
|
||||
require.NotNil(t, cachedDeposits, "Deposits not cached")
|
||||
assert.Equal(t, int64(5), cachedDeposits.MerkleTrieIndex)
|
||||
}
|
||||
|
||||
func TestFinalizedDeposits_InitializedCorrectly(t *testing.T) {
|
||||
dc, err := New()
|
||||
require.NoError(t, err)
|
||||
@@ -554,7 +688,7 @@ func TestNonFinalizedDeposits_ReturnsAllNonFinalizedDeposits(t *testing.T) {
|
||||
})
|
||||
dc.InsertFinalizedDeposits(context.Background(), 1)
|
||||
|
||||
deps := dc.NonFinalizedDeposits(context.Background(), nil)
|
||||
deps := dc.NonFinalizedDeposits(context.Background(), 1, nil)
|
||||
assert.Equal(t, 2, len(deps))
|
||||
}
|
||||
|
||||
@@ -611,10 +745,89 @@ func TestNonFinalizedDeposits_ReturnsNonFinalizedDepositsUpToBlockNumber(t *test
|
||||
})
|
||||
dc.InsertFinalizedDeposits(context.Background(), 1)
|
||||
|
||||
deps := dc.NonFinalizedDeposits(context.Background(), big.NewInt(10))
|
||||
deps := dc.NonFinalizedDeposits(context.Background(), 1, big.NewInt(10))
|
||||
assert.Equal(t, 1, len(deps))
|
||||
}
|
||||
|
||||
func TestFinalizedDeposits_ReturnsTrieCorrectly(t *testing.T) {
|
||||
dc, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
generateCtr := func(height uint64, index int64) *ethpb.DepositContainer {
|
||||
return ðpb.DepositContainer{
|
||||
Eth1BlockHeight: height,
|
||||
Deposit: ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.PadTo([]byte{uint8(index)}, 48),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
},
|
||||
},
|
||||
Index: index,
|
||||
}
|
||||
}
|
||||
|
||||
finalizedDeposits := []*ethpb.DepositContainer{
|
||||
generateCtr(10, 0),
|
||||
generateCtr(11, 1),
|
||||
generateCtr(12, 2),
|
||||
generateCtr(12, 3),
|
||||
generateCtr(13, 4),
|
||||
generateCtr(13, 5),
|
||||
generateCtr(13, 6),
|
||||
generateCtr(14, 7),
|
||||
}
|
||||
dc.deposits = append(finalizedDeposits,
|
||||
generateCtr(15, 8),
|
||||
generateCtr(15, 9),
|
||||
generateCtr(30, 10))
|
||||
trieItems := make([][]byte, 0, len(dc.deposits))
|
||||
for _, dep := range dc.allDeposits(big.NewInt(30)) {
|
||||
depHash, err := dep.Data.HashTreeRoot()
|
||||
assert.NoError(t, err)
|
||||
trieItems = append(trieItems, depHash[:])
|
||||
}
|
||||
depositTrie, err := trie.GenerateTrieFromItems(trieItems, params.BeaconConfig().DepositContractTreeDepth)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Perform this in a non-sensical ordering
|
||||
dc.InsertFinalizedDeposits(context.Background(), 10)
|
||||
dc.InsertFinalizedDeposits(context.Background(), 2)
|
||||
dc.InsertFinalizedDeposits(context.Background(), 3)
|
||||
dc.InsertFinalizedDeposits(context.Background(), 4)
|
||||
|
||||
// Mimick finalized deposit trie fetch.
|
||||
fd := dc.FinalizedDeposits(context.Background())
|
||||
deps := dc.NonFinalizedDeposits(context.Background(), fd.MerkleTrieIndex, big.NewInt(14))
|
||||
insertIndex := fd.MerkleTrieIndex + 1
|
||||
|
||||
for _, dep := range deps {
|
||||
depHash, err := dep.Data.HashTreeRoot()
|
||||
assert.NoError(t, err)
|
||||
if err = fd.Deposits.Insert(depHash[:], int(insertIndex)); err != nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
insertIndex++
|
||||
}
|
||||
dc.InsertFinalizedDeposits(context.Background(), 15)
|
||||
dc.InsertFinalizedDeposits(context.Background(), 15)
|
||||
dc.InsertFinalizedDeposits(context.Background(), 14)
|
||||
|
||||
fd = dc.FinalizedDeposits(context.Background())
|
||||
deps = dc.NonFinalizedDeposits(context.Background(), fd.MerkleTrieIndex, big.NewInt(30))
|
||||
insertIndex = fd.MerkleTrieIndex + 1
|
||||
|
||||
for _, dep := range deps {
|
||||
depHash, err := dep.Data.HashTreeRoot()
|
||||
assert.NoError(t, err)
|
||||
if err = fd.Deposits.Insert(depHash[:], int(insertIndex)); err != nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
insertIndex++
|
||||
}
|
||||
assert.Equal(t, fd.Deposits.NumOfItems(), depositTrie.NumOfItems())
|
||||
}
|
||||
|
||||
func TestPruneProofs_Ok(t *testing.T) {
|
||||
dc, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
73
beacon-chain/cache/payload_id.go
vendored
Normal file
73
beacon-chain/cache/payload_id.go
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
)
|
||||
|
||||
const vIdLength = 8
|
||||
const pIdLength = 8
|
||||
const vpIdsLength = vIdLength + pIdLength
|
||||
|
||||
// ProposerPayloadIDsCache is a cache of proposer payload IDs.
|
||||
// The key is the slot. The value is the concatenation of the proposer and payload IDs. 8 bytes each.
|
||||
type ProposerPayloadIDsCache struct {
|
||||
slotToProposerAndPayloadIDs map[types.Slot][vpIdsLength]byte
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// NewProposerPayloadIDsCache creates a new proposer payload IDs cache.
|
||||
func NewProposerPayloadIDsCache() *ProposerPayloadIDsCache {
|
||||
return &ProposerPayloadIDsCache{
|
||||
slotToProposerAndPayloadIDs: make(map[types.Slot][vpIdsLength]byte),
|
||||
}
|
||||
}
|
||||
|
||||
// GetProposerPayloadIDs returns the proposer and payload IDs for the given slot.
|
||||
func (f *ProposerPayloadIDsCache) GetProposerPayloadIDs(slot types.Slot) (types.ValidatorIndex, [8]byte, bool) {
|
||||
f.RLock()
|
||||
defer f.RUnlock()
|
||||
ids, ok := f.slotToProposerAndPayloadIDs[slot]
|
||||
if !ok {
|
||||
return 0, [8]byte{}, false
|
||||
}
|
||||
vId := ids[:vIdLength]
|
||||
|
||||
b := ids[vIdLength:]
|
||||
var pId [pIdLength]byte
|
||||
copy(pId[:], b)
|
||||
|
||||
return types.ValidatorIndex(bytesutil.BytesToUint64BigEndian(vId)), pId, true
|
||||
}
|
||||
|
||||
// SetProposerAndPayloadIDs sets the proposer and payload IDs for the given slot.
|
||||
func (f *ProposerPayloadIDsCache) SetProposerAndPayloadIDs(slot types.Slot, vId types.ValidatorIndex, pId [8]byte) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
var vIdBytes [vIdLength]byte
|
||||
copy(vIdBytes[:], bytesutil.Uint64ToBytesBigEndian(uint64(vId)))
|
||||
|
||||
var bytes [vpIdsLength]byte
|
||||
copy(bytes[:], append(vIdBytes[:], pId[:]...))
|
||||
|
||||
_, ok := f.slotToProposerAndPayloadIDs[slot]
|
||||
// Ok to overwrite if the slot is already set but the payload ID is not set.
|
||||
// This combats the re-org case where payload assignment could change the epoch of.
|
||||
if !ok || (ok && pId != [pIdLength]byte{}) {
|
||||
f.slotToProposerAndPayloadIDs[slot] = bytes
|
||||
}
|
||||
}
|
||||
|
||||
// PrunePayloadIDs removes the payload id entries that's current than input slot.
|
||||
func (f *ProposerPayloadIDsCache) PrunePayloadIDs(slot types.Slot) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
for s := range f.slotToProposerAndPayloadIDs {
|
||||
if slot > s {
|
||||
delete(f.slotToProposerAndPayloadIDs, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
60
beacon-chain/cache/payload_id_test.go
vendored
Normal file
60
beacon-chain/cache/payload_id_test.go
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestValidatorPayloadIDsCache_GetAndSaveValidatorPayloadIDs(t *testing.T) {
|
||||
cache := NewProposerPayloadIDsCache()
|
||||
i, p, ok := cache.GetProposerPayloadIDs(0)
|
||||
require.Equal(t, false, ok)
|
||||
require.Equal(t, types.ValidatorIndex(0), i)
|
||||
require.Equal(t, [pIdLength]byte{}, p)
|
||||
|
||||
slot := types.Slot(1234)
|
||||
vid := types.ValidatorIndex(34234324)
|
||||
pid := [8]byte{1, 2, 3, 3, 7, 8, 7, 8}
|
||||
cache.SetProposerAndPayloadIDs(slot, vid, pid)
|
||||
i, p, ok = cache.GetProposerPayloadIDs(slot)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, vid, i)
|
||||
require.Equal(t, pid, p)
|
||||
|
||||
slot = types.Slot(9456456)
|
||||
vid = types.ValidatorIndex(6786745)
|
||||
cache.SetProposerAndPayloadIDs(slot, vid, [pIdLength]byte{})
|
||||
i, p, ok = cache.GetProposerPayloadIDs(slot)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, vid, i)
|
||||
require.Equal(t, [pIdLength]byte{}, p)
|
||||
|
||||
// reset cache without pid
|
||||
slot = types.Slot(9456456)
|
||||
vid = types.ValidatorIndex(11111)
|
||||
pid = [8]byte{3, 2, 3, 33, 72, 8, 7, 8}
|
||||
cache.SetProposerAndPayloadIDs(slot, vid, pid)
|
||||
i, p, ok = cache.GetProposerPayloadIDs(slot)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, vid, i)
|
||||
require.Equal(t, pid, p)
|
||||
|
||||
// reset cache with existing pid
|
||||
slot = types.Slot(9456456)
|
||||
vid = types.ValidatorIndex(11111)
|
||||
newPid := [8]byte{1, 2, 3, 33, 72, 8, 7, 1}
|
||||
cache.SetProposerAndPayloadIDs(slot, vid, newPid)
|
||||
i, p, ok = cache.GetProposerPayloadIDs(slot)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, vid, i)
|
||||
require.Equal(t, newPid, p)
|
||||
|
||||
// remove cache entry
|
||||
cache.PrunePayloadIDs(slot + 1)
|
||||
i, p, ok = cache.GetProposerPayloadIDs(slot)
|
||||
require.Equal(t, false, ok)
|
||||
require.Equal(t, types.ValidatorIndex(0), i)
|
||||
require.Equal(t, [pIdLength]byte{}, p)
|
||||
}
|
||||
@@ -270,6 +270,7 @@ func AttestationsDelta(beaconState state.BeaconState, bal *precompute.Balance, v
|
||||
// Modified in Altair and Bellatrix.
|
||||
var inactivityDenominator uint64
|
||||
bias := cfg.InactivityScoreBias
|
||||
inactivityDenominator := bias * beaconState.InactivityPenaltyQuotient()
|
||||
switch beaconState.Version() {
|
||||
case version.Altair:
|
||||
inactivityDenominator = bias * cfg.InactivityPenaltyQuotientAltair
|
||||
|
||||
@@ -175,9 +175,7 @@ func CommitteeAssignments(
|
||||
return nil, nil, err
|
||||
}
|
||||
proposerIndexToSlots := make(map[types.ValidatorIndex][]types.Slot, params.BeaconConfig().SlotsPerEpoch)
|
||||
// Proposal epochs do not have a look ahead, so we skip them over here.
|
||||
validProposalEpoch := epoch < nextEpoch
|
||||
for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch && validProposalEpoch; slot++ {
|
||||
for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ {
|
||||
// Skip proposer assignment for genesis slot.
|
||||
if slot == 0 {
|
||||
continue
|
||||
@@ -192,6 +190,15 @@ func CommitteeAssignments(
|
||||
proposerIndexToSlots[i] = append(proposerIndexToSlots[i], slot)
|
||||
}
|
||||
|
||||
// If previous proposer indices computation is outside if current proposal epoch range,
|
||||
// we need to reset state slot back to start slot so that we can compute the correct committees.
|
||||
currentProposalEpoch := epoch < nextEpoch
|
||||
if !currentProposalEpoch {
|
||||
if err := state.SetSlot(state.Slot() - params.BeaconConfig().SlotsPerEpoch); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
activeValidatorIndices, err := ActiveValidatorIndices(ctx, state, epoch)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
@@ -232,7 +232,7 @@ func TestCommitteeAssignments_CannotRetrieveFuture(t *testing.T) {
|
||||
|
||||
_, proposerIndxs, err = CommitteeAssignments(context.Background(), state, time.CurrentEpoch(state)+1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(proposerIndxs), "wanted empty proposer index set")
|
||||
require.NotEqual(t, 0, len(proposerIndxs), "wanted non-zero proposer index set")
|
||||
}
|
||||
|
||||
func TestCommitteeAssignments_EverySlotHasMin1Proposer(t *testing.T) {
|
||||
|
||||
@@ -233,13 +233,17 @@ func (s *Store) DeleteBlock(ctx context.Context, root [32]byte) error {
|
||||
defer span.End()
|
||||
|
||||
if err := s.DeleteState(ctx, root); err != nil {
|
||||
return errDeleteFinalized
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.deleteStateSummary(root); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(finalizedBlockRootsIndexBucket)
|
||||
if b := bkt.Get(root[:]); b != nil {
|
||||
return errDeleteFinalized
|
||||
return ErrDeleteJustifiedAndFinalized
|
||||
}
|
||||
|
||||
if err := tx.Bucket(blocksBucket).Delete(root[:]); err != nil {
|
||||
|
||||
@@ -191,6 +191,16 @@ func TestStore_DeleteBlock(t *testing.T) {
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesisBlockRoot))
|
||||
blks := makeBlocks(t, 0, slotsPerEpoch*4, genesisBlockRoot)
|
||||
require.NoError(t, db.SaveBlocks(ctx, blks))
|
||||
ss := make([]*ethpb.StateSummary, len(blks))
|
||||
for i, blk := range blks {
|
||||
r, err := blk.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
ss[i] = ðpb.StateSummary{
|
||||
Slot: blk.Block().Slot(),
|
||||
Root: r[:],
|
||||
}
|
||||
}
|
||||
require.NoError(t, db.SaveStateSummaries(ctx, ss))
|
||||
|
||||
root, err := blks[slotsPerEpoch].Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
@@ -216,11 +226,50 @@ func TestStore_DeleteBlock(t *testing.T) {
|
||||
b, err = db.Block(ctx, root2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, b, nil)
|
||||
require.Equal(t, false, db.HasStateSummary(ctx, root2))
|
||||
|
||||
require.ErrorIs(t, db.DeleteBlock(ctx, root), errDeleteFinalized)
|
||||
|
||||
require.ErrorIs(t, db.DeleteBlock(ctx, root), ErrDeleteJustifiedAndFinalized)
|
||||
}
|
||||
|
||||
func TestStore_DeleteJustifiedBlock(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block.Slot = 1
|
||||
root, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
cp := ðpb.Checkpoint{
|
||||
Root: root[:],
|
||||
}
|
||||
st, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
blk, err := wrapper.WrappedSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, blk))
|
||||
require.NoError(t, db.SaveState(ctx, st, root))
|
||||
require.NoError(t, db.SaveJustifiedCheckpoint(ctx, cp))
|
||||
require.ErrorIs(t, db.DeleteBlock(ctx, root), ErrDeleteJustifiedAndFinalized)
|
||||
}
|
||||
|
||||
func TestStore_DeleteFinalizedBlock(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
b := util.NewBeaconBlock()
|
||||
root, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
cp := ðpb.Checkpoint{
|
||||
Root: root[:],
|
||||
}
|
||||
st, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
blk, err := wrapper.WrappedSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, blk))
|
||||
require.NoError(t, db.SaveState(ctx, st, root))
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
|
||||
require.NoError(t, db.SaveFinalizedCheckpoint(ctx, cp))
|
||||
require.ErrorIs(t, db.DeleteBlock(ctx, root), ErrDeleteJustifiedAndFinalized)
|
||||
}
|
||||
func TestStore_GenesisBlock(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -2,8 +2,8 @@ package kv
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
// errDeleteFinalized is raised when we attempt to delete a finalized block/state
|
||||
var errDeleteFinalized = errors.New("cannot delete finalized block or state")
|
||||
// ErrDeleteJustifiedAndFinalized is raised when we attempt to delete a finalized block/state
|
||||
var ErrDeleteJustifiedAndFinalized = errors.New("cannot delete finalized block or state")
|
||||
|
||||
// ErrNotFound can be used directly, or as a wrapped DBError, whenever a db method needs to
|
||||
// indicate that a value couldn't be found.
|
||||
|
||||
@@ -346,19 +346,31 @@ func (s *Store) DeleteState(ctx context.Context, blockRoot [32]byte) error {
|
||||
|
||||
bkt = tx.Bucket(checkpointBucket)
|
||||
enc := bkt.Get(finalizedCheckpointKey)
|
||||
checkpoint := ðpb.Checkpoint{}
|
||||
finalized := ðpb.Checkpoint{}
|
||||
if enc == nil {
|
||||
checkpoint = ðpb.Checkpoint{Root: genesisBlockRoot}
|
||||
} else if err := decode(ctx, enc, checkpoint); err != nil {
|
||||
finalized = ðpb.Checkpoint{Root: genesisBlockRoot}
|
||||
} else if err := decode(ctx, enc, finalized); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
enc = bkt.Get(justifiedCheckpointKey)
|
||||
justified := ðpb.Checkpoint{}
|
||||
if enc == nil {
|
||||
justified = ðpb.Checkpoint{Root: genesisBlockRoot}
|
||||
} else if err := decode(ctx, enc, justified); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blockBkt := tx.Bucket(blocksBucket)
|
||||
headBlkRoot := blockBkt.Get(headBlockRootKey)
|
||||
bkt = tx.Bucket(stateBucket)
|
||||
// Safe guard against deleting genesis, finalized, head state.
|
||||
if bytes.Equal(blockRoot[:], checkpoint.Root) || bytes.Equal(blockRoot[:], genesisBlockRoot) || bytes.Equal(blockRoot[:], headBlkRoot) {
|
||||
return errors.New("cannot delete genesis, finalized, or head state")
|
||||
// Safeguard against deleting genesis, finalized, head state.
|
||||
if bytes.Equal(blockRoot[:], finalized.Root) || bytes.Equal(blockRoot[:], genesisBlockRoot) || bytes.Equal(blockRoot[:], justified.Root) {
|
||||
return ErrDeleteJustifiedAndFinalized
|
||||
}
|
||||
|
||||
// Nothing to delete if state doesn't exist.
|
||||
enc = bkt.Get(blockRoot[:])
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
slot, err := s.slotByBlockRoot(ctx, tx, blockRoot[:])
|
||||
|
||||
@@ -110,3 +110,12 @@ func (s *Store) saveCachedStateSummariesDB(ctx context.Context) error {
|
||||
s.stateSummaryCache.clear()
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteStateSummary deletes a state summary object from the db using input block root.
|
||||
func (s *Store) deleteStateSummary(blockRoot [32]byte) error {
|
||||
s.stateSummaryCache.delete(blockRoot)
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(stateSummaryBucket)
|
||||
return bucket.Delete(blockRoot[:])
|
||||
})
|
||||
}
|
||||
|
||||
@@ -37,6 +37,13 @@ func (c *stateSummaryCache) has(r [32]byte) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// delete state summary in cache.
|
||||
func (c *stateSummaryCache) delete(r [32]byte) {
|
||||
c.initSyncStateSummariesLock.Lock()
|
||||
defer c.initSyncStateSummariesLock.Unlock()
|
||||
delete(c.initSyncStateSummaries, r)
|
||||
}
|
||||
|
||||
// get retrieves a state summary from the initial sync state summaries cache using the root of
|
||||
// the block.
|
||||
func (c *stateSummaryCache) get(r [32]byte) *ethpb.StateSummary {
|
||||
|
||||
@@ -62,3 +62,17 @@ func TestStateSummary_CacheToDB(t *testing.T) {
|
||||
require.Equal(t, true, db.HasStateSummary(context.Background(), bytesutil.ToBytes32(r)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateSummary_CanDelete(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
r1 := bytesutil.ToBytes32([]byte{'A'})
|
||||
s1 := ðpb.StateSummary{Slot: 1, Root: r1[:]}
|
||||
|
||||
require.Equal(t, false, db.HasStateSummary(ctx, r1), "State summary should not be saved")
|
||||
require.NoError(t, db.SaveStateSummary(ctx, s1))
|
||||
require.Equal(t, true, db.HasStateSummary(ctx, r1), "State summary should be saved")
|
||||
|
||||
require.NoError(t, db.deleteStateSummary(r1))
|
||||
require.Equal(t, false, db.HasStateSummary(ctx, r1), "State summary should not be saved")
|
||||
}
|
||||
|
||||
@@ -412,7 +412,7 @@ func TestStore_DeleteGenesisState(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, st.SetSlot(100))
|
||||
require.NoError(t, db.SaveState(ctx, st, genesisBlockRoot))
|
||||
wantedErr := "cannot delete genesis, finalized, or head state"
|
||||
wantedErr := "cannot delete finalized block or state"
|
||||
assert.ErrorContains(t, wantedErr, db.DeleteState(ctx, genesisBlockRoot))
|
||||
}
|
||||
|
||||
@@ -440,7 +440,7 @@ func TestStore_DeleteFinalizedState(t *testing.T) {
|
||||
require.NoError(t, db.SaveState(ctx, finalizedState, finalizedBlockRoot))
|
||||
finalizedCheckpoint := ðpb.Checkpoint{Root: finalizedBlockRoot[:]}
|
||||
require.NoError(t, db.SaveFinalizedCheckpoint(ctx, finalizedCheckpoint))
|
||||
wantedErr := "cannot delete genesis, finalized, or head state"
|
||||
wantedErr := "cannot delete finalized block or state"
|
||||
assert.ErrorContains(t, wantedErr, db.DeleteState(ctx, finalizedBlockRoot))
|
||||
}
|
||||
|
||||
@@ -465,8 +465,7 @@ func TestStore_DeleteHeadState(t *testing.T) {
|
||||
require.NoError(t, st.SetSlot(100))
|
||||
require.NoError(t, db.SaveState(ctx, st, headBlockRoot))
|
||||
require.NoError(t, db.SaveHeadBlockRoot(ctx, headBlockRoot))
|
||||
wantedErr := "cannot delete genesis, finalized, or head state"
|
||||
assert.ErrorContains(t, wantedErr, db.DeleteState(ctx, headBlockRoot))
|
||||
require.NoError(t, db.DeleteState(ctx, headBlockRoot)) // Ok to delete head state if it's optimistic.
|
||||
}
|
||||
|
||||
func TestStore_SaveDeleteState_CanGetHighestBelow(t *testing.T) {
|
||||
|
||||
@@ -155,7 +155,7 @@ func (_ *Service) FinalizedDeposits(_ context.Context) *depositcache.FinalizedDe
|
||||
}
|
||||
|
||||
// NonFinalizedDeposits mocks out the deposit cache functionality for interop.
|
||||
func (_ *Service) NonFinalizedDeposits(_ context.Context, _ *big.Int) []*ethpb.Deposit {
|
||||
func (_ *Service) NonFinalizedDeposits(_ context.Context, _ int64, _ *big.Int) []*ethpb.Deposit {
|
||||
return []*ethpb.Deposit{}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,13 @@ go_library(
|
||||
"//beacon-chain/forkchoice/types:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -3,7 +3,6 @@ package doublylinkedtree
|
||||
import "errors"
|
||||
|
||||
var ErrNilNode = errors.New("invalid nil or unknown node")
|
||||
var errInvalidBalance = errors.New("invalid node balance")
|
||||
var errInvalidProposerBoostRoot = errors.New("invalid proposer boost root")
|
||||
var errUnknownFinalizedRoot = errors.New("unknown finalized root")
|
||||
var errUnknownJustifiedRoot = errors.New("unknown justified root")
|
||||
|
||||
@@ -2,12 +2,15 @@ package doublylinkedtree
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
pbrpc "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
@@ -250,9 +253,21 @@ func (f *ForkChoice) updateBalances(newBalances []uint64) error {
|
||||
return ErrNilNode
|
||||
}
|
||||
if currentNode.balance < oldBalance {
|
||||
return errInvalidBalance
|
||||
f.store.proposerBoostLock.RLock()
|
||||
log.WithFields(logrus.Fields{
|
||||
"nodeRoot": fmt.Sprintf("%#x", bytesutil.Trunc(vote.currentRoot[:])),
|
||||
"oldBalance": oldBalance,
|
||||
"nodeBalance": currentNode.balance,
|
||||
"nodeWeight": currentNode.weight,
|
||||
"proposerBoostRoot": fmt.Sprintf("%#x", bytesutil.Trunc(f.store.proposerBoostRoot[:])),
|
||||
"previousProposerBoostRoot": fmt.Sprintf("%#x", bytesutil.Trunc(f.store.previousProposerBoostRoot[:])),
|
||||
"previousProposerBoostScore": f.store.previousProposerBoostScore,
|
||||
}).Warning("node with invalid balance, setting it to zero")
|
||||
f.store.proposerBoostLock.RUnlock()
|
||||
currentNode.balance = 0
|
||||
} else {
|
||||
currentNode.balance -= oldBalance
|
||||
}
|
||||
currentNode.balance -= oldBalance
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,30 @@ func TestForkChoice_UpdateBalancesNegativeChange(t *testing.T) {
|
||||
assert.Equal(t, uint64(30), s.nodeByRoot[indexToHash(3)].balance)
|
||||
}
|
||||
|
||||
func TestForkChoice_UpdateBalancesUnderflow(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
s := f.store
|
||||
s.nodeByRoot[indexToHash(1)].balance = 100
|
||||
s.nodeByRoot[indexToHash(2)].balance = 100
|
||||
s.nodeByRoot[indexToHash(3)].balance = 100
|
||||
|
||||
f.balances = []uint64{125, 125, 125}
|
||||
f.votes = []Vote{
|
||||
{indexToHash(1), indexToHash(1), 0},
|
||||
{indexToHash(2), indexToHash(2), 0},
|
||||
{indexToHash(3), indexToHash(3), 0},
|
||||
}
|
||||
|
||||
require.NoError(t, f.updateBalances([]uint64{10, 20, 30}))
|
||||
assert.Equal(t, uint64(0), s.nodeByRoot[indexToHash(1)].balance)
|
||||
assert.Equal(t, uint64(0), s.nodeByRoot[indexToHash(2)].balance)
|
||||
assert.Equal(t, uint64(5), s.nodeByRoot[indexToHash(3)].balance)
|
||||
}
|
||||
|
||||
func TestForkChoice_IsCanonical(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -3,9 +3,12 @@ package doublylinkedtree
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
log = logrus.WithField("prefix", "forkchoice-doublylinkedtree")
|
||||
|
||||
headSlotNumber = promauto.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "doublylinkedtree_head_slot",
|
||||
@@ -48,10 +51,4 @@ var (
|
||||
Help: "The number of times pruning happened.",
|
||||
},
|
||||
)
|
||||
validatedCount = promauto.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "doublylinkedtree_validated_count",
|
||||
Help: "The number of blocks that have been fully validated.",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
@@ -120,7 +120,6 @@ func (n *Node) setNodeAndParentValidated(ctx context.Context) error {
|
||||
}
|
||||
|
||||
n.optimistic = false
|
||||
validatedCount.Inc()
|
||||
return n.parent.setNodeAndParentValidated(ctx)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,12 +22,14 @@ go_library(
|
||||
"//beacon-chain/forkchoice/types:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//math:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -3,9 +3,12 @@ package protoarray
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
log = logrus.WithField("prefix", "forkchoice-protoarray")
|
||||
|
||||
headSlotNumber = promauto.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "proto_array_head_slot",
|
||||
@@ -48,10 +51,4 @@ var (
|
||||
Help: "The number of times pruning happened.",
|
||||
},
|
||||
)
|
||||
validatedNodesCount = promauto.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Name: "proto_array_validated_nodes_count",
|
||||
Help: "The number of nodes that have been fully validated.",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
@@ -43,7 +43,6 @@ func (f *ForkChoice) SetOptimisticToValid(ctx context.Context, root [32]byte) er
|
||||
if index == NonExistentNode {
|
||||
break
|
||||
}
|
||||
validatedNodesCount.Inc()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@ import (
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
pmath "github.com/prysmaticlabs/prysm/math"
|
||||
pbrpc "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
@@ -426,6 +428,16 @@ func (s *Store) applyWeightChanges(
|
||||
if nodeDelta < 0 {
|
||||
d := uint64(-nodeDelta)
|
||||
if n.weight < d {
|
||||
s.proposerBoostLock.RLock()
|
||||
log.WithFields(logrus.Fields{
|
||||
"nodeDelta": d,
|
||||
"nodeRoot": fmt.Sprintf("%#x", bytesutil.Trunc(n.root[:])),
|
||||
"nodeWeight": n.weight,
|
||||
"proposerBoostRoot": fmt.Sprintf("%#x", bytesutil.Trunc(s.proposerBoostRoot[:])),
|
||||
"previousProposerBoostRoot": fmt.Sprintf("%#x", bytesutil.Trunc(s.previousProposerBoostRoot[:])),
|
||||
"previousProposerBoostScore": s.previousProposerBoostScore,
|
||||
}).Warning("node with invalid weight, setting it to zero")
|
||||
s.proposerBoostLock.RUnlock()
|
||||
n.weight = 0
|
||||
} else {
|
||||
n.weight -= d
|
||||
@@ -602,16 +614,22 @@ func (s *Store) prune(ctx context.Context, finalizedRoot [32]byte) error {
|
||||
node := copyNode(s.nodes[idx])
|
||||
parentIdx, ok := canonicalNodesMap[node.parent]
|
||||
if ok {
|
||||
s.nodesIndices[node.root] = uint64(len(canonicalNodes))
|
||||
canonicalNodesMap[idx] = uint64(len(canonicalNodes))
|
||||
currentIndex := uint64(len(canonicalNodes))
|
||||
s.nodesIndices[node.root] = currentIndex
|
||||
s.payloadIndices[node.payloadHash] = currentIndex
|
||||
canonicalNodesMap[idx] = currentIndex
|
||||
node.parent = parentIdx
|
||||
canonicalNodes = append(canonicalNodes, node)
|
||||
} else {
|
||||
// Remove node and synced tip that is not part of finalized branch.
|
||||
// Remove node that is not part of finalized branch.
|
||||
delete(s.nodesIndices, node.root)
|
||||
delete(s.canonicalNodes, node.root)
|
||||
delete(s.payloadIndices, node.payloadHash)
|
||||
}
|
||||
}
|
||||
s.nodesIndices[finalizedRoot] = uint64(0)
|
||||
s.canonicalNodes[finalizedRoot] = true
|
||||
s.payloadIndices[finalizedNode.payloadHash] = uint64(0)
|
||||
|
||||
// Recompute the best child and descendant for each canonical nodes.
|
||||
for _, node := range canonicalNodes {
|
||||
|
||||
@@ -375,7 +375,7 @@ func TestStore_Prune_MoreThanThreshold(t *testing.T) {
|
||||
parent: uint64(numOfNodes - 2),
|
||||
})
|
||||
indices[indexToHash(uint64(numOfNodes-1))] = uint64(numOfNodes - 1)
|
||||
s := &Store{nodes: nodes, nodesIndices: indices}
|
||||
s := &Store{nodes: nodes, nodesIndices: indices, canonicalNodes: map[[32]byte]bool{}, payloadIndices: map[[32]byte]uint64{}}
|
||||
|
||||
// Finalized root is at index 99 so everything before 99 should be pruned.
|
||||
require.NoError(t, s.prune(context.Background(), indexToHash(99)))
|
||||
@@ -413,7 +413,7 @@ func TestStore_Prune_MoreThanOnce(t *testing.T) {
|
||||
parent: uint64(numOfNodes - 2),
|
||||
})
|
||||
|
||||
s := &Store{nodes: nodes, nodesIndices: indices}
|
||||
s := &Store{nodes: nodes, nodesIndices: indices, canonicalNodes: map[[32]byte]bool{}, payloadIndices: map[[32]byte]uint64{}}
|
||||
|
||||
// Finalized root is at index 11 so everything before 11 should be pruned.
|
||||
require.NoError(t, s.prune(context.Background(), indexToHash(10)))
|
||||
@@ -441,6 +441,7 @@ func TestStore_Prune_NoDanglingBranch(t *testing.T) {
|
||||
bestDescendant: 1,
|
||||
root: indexToHash(uint64(0)),
|
||||
parent: NonExistentNode,
|
||||
payloadHash: [32]byte{'A'},
|
||||
},
|
||||
{
|
||||
slot: 101,
|
||||
@@ -448,6 +449,7 @@ func TestStore_Prune_NoDanglingBranch(t *testing.T) {
|
||||
bestChild: NonExistentNode,
|
||||
bestDescendant: NonExistentNode,
|
||||
parent: 0,
|
||||
payloadHash: [32]byte{'B'},
|
||||
},
|
||||
{
|
||||
slot: 101,
|
||||
@@ -455,6 +457,7 @@ func TestStore_Prune_NoDanglingBranch(t *testing.T) {
|
||||
parent: 0,
|
||||
bestChild: NonExistentNode,
|
||||
bestDescendant: NonExistentNode,
|
||||
payloadHash: [32]byte{'C'},
|
||||
},
|
||||
}
|
||||
s := &Store{
|
||||
@@ -465,9 +468,22 @@ func TestStore_Prune_NoDanglingBranch(t *testing.T) {
|
||||
indexToHash(uint64(1)): 1,
|
||||
indexToHash(uint64(2)): 2,
|
||||
},
|
||||
canonicalNodes: map[[32]byte]bool{
|
||||
indexToHash(uint64(0)): true,
|
||||
indexToHash(uint64(1)): true,
|
||||
indexToHash(uint64(2)): true,
|
||||
},
|
||||
payloadIndices: map[[32]byte]uint64{
|
||||
[32]byte{'A'}: 0,
|
||||
[32]byte{'B'}: 1,
|
||||
[32]byte{'C'}: 2,
|
||||
},
|
||||
}
|
||||
require.NoError(t, s.prune(context.Background(), indexToHash(uint64(1))))
|
||||
require.Equal(t, len(s.nodes), 1)
|
||||
require.Equal(t, 1, len(s.nodes))
|
||||
require.Equal(t, 1, len(s.nodesIndices))
|
||||
require.Equal(t, 1, len(s.canonicalNodes))
|
||||
require.Equal(t, 1, len(s.payloadIndices))
|
||||
}
|
||||
|
||||
// This test starts with the following branching diagram
|
||||
@@ -482,25 +498,74 @@ func TestStore_Prune_NoDanglingBranch(t *testing.T) {
|
||||
// J -- K -- L
|
||||
//
|
||||
//
|
||||
func TestStore_PruneSyncedTips(t *testing.T) {
|
||||
func TestStore_PruneBranched(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := setup(1, 1)
|
||||
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
f.store.pruneThreshold = 0
|
||||
require.NoError(t, f.Prune(ctx, [32]byte{'f'}))
|
||||
require.Equal(t, 1, f.NodeCount())
|
||||
tests := []struct {
|
||||
finalizedRoot [32]byte
|
||||
wantedCanonical [32]byte
|
||||
wantedNonCanonical [32]byte
|
||||
canonicalCount int
|
||||
payloadHash [32]byte
|
||||
payloadIndex uint64
|
||||
nonExistentPayload [32]byte
|
||||
}{
|
||||
{
|
||||
[32]byte{'f'},
|
||||
[32]byte{'f'},
|
||||
[32]byte{'a'},
|
||||
1,
|
||||
[32]byte{'F'},
|
||||
0,
|
||||
[32]byte{'H'},
|
||||
},
|
||||
{
|
||||
[32]byte{'d'},
|
||||
[32]byte{'e'},
|
||||
[32]byte{'i'},
|
||||
3,
|
||||
[32]byte{'E'},
|
||||
1,
|
||||
[32]byte{'C'},
|
||||
},
|
||||
{
|
||||
[32]byte{'b'},
|
||||
[32]byte{'f'},
|
||||
[32]byte{'h'},
|
||||
5,
|
||||
[32]byte{'D'},
|
||||
3,
|
||||
[32]byte{'A'},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
f := setup(1, 1)
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, [32]byte{'J'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, [32]byte{'D'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, [32]byte{'E'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, [32]byte{'G'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, [32]byte{'F'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, [32]byte{'H'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, [32]byte{'K'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, [32]byte{'I'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, [32]byte{'L'}, 1, 1))
|
||||
f.store.pruneThreshold = 0
|
||||
require.NoError(t, f.store.updateCanonicalNodes(ctx, [32]byte{'f'}))
|
||||
require.Equal(t, true, f.IsCanonical([32]byte{'a'}))
|
||||
require.Equal(t, true, f.IsCanonical([32]byte{'f'}))
|
||||
|
||||
require.NoError(t, f.Prune(ctx, tc.finalizedRoot))
|
||||
require.Equal(t, tc.canonicalCount, len(f.store.canonicalNodes))
|
||||
require.Equal(t, true, f.IsCanonical(tc.wantedCanonical))
|
||||
require.Equal(t, false, f.IsCanonical(tc.wantedNonCanonical))
|
||||
require.Equal(t, tc.payloadIndex, f.store.payloadIndices[tc.payloadHash])
|
||||
_, ok := f.store.payloadIndices[tc.nonExistentPayload]
|
||||
require.Equal(t, false, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_LeadsToViableHead(t *testing.T) {
|
||||
|
||||
@@ -18,6 +18,7 @@ go_library(
|
||||
"//api/gateway:go_default_library",
|
||||
"//async/event:go_default_library",
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/cache/depositcache:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/db/kv:go_default_library",
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
apigateway "github.com/prysmaticlabs/prysm/api/gateway"
|
||||
"github.com/prysmaticlabs/prysm/async/event"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
|
||||
@@ -94,6 +95,7 @@ type BeaconNode struct {
|
||||
slashingsPool slashings.PoolManager
|
||||
syncCommitteePool synccommittee.Pool
|
||||
depositCache *depositcache.DepositCache
|
||||
proposerIdsCache *cache.ProposerPayloadIDsCache
|
||||
stateFeed *event.Feed
|
||||
blockFeed *event.Feed
|
||||
opFeed *event.Feed
|
||||
@@ -152,6 +154,7 @@ func New(cliCtx *cli.Context, opts ...Option) (*BeaconNode, error) {
|
||||
slasherBlockHeadersFeed: new(event.Feed),
|
||||
slasherAttestationsFeed: new(event.Feed),
|
||||
serviceFlagOpts: &serviceFlagOpts{},
|
||||
proposerIdsCache: cache.NewProposerPayloadIDsCache(),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
@@ -585,6 +588,7 @@ func (b *BeaconNode) registerBlockchainService() error {
|
||||
blockchain.WithStateGen(b.stateGen),
|
||||
blockchain.WithSlasherAttestationsFeed(b.slasherAttestationsFeed),
|
||||
blockchain.WithFinalizedStateAtStartUp(b.finalizedStateAtStartUp),
|
||||
blockchain.WithProposerIdsCache(b.proposerIdsCache),
|
||||
)
|
||||
blockchainService, err := blockchain.NewService(b.ctx, opts...)
|
||||
if err != nil {
|
||||
@@ -801,6 +805,7 @@ func (b *BeaconNode) registerRPCService() error {
|
||||
StateGen: b.stateGen,
|
||||
EnableDebugRPCEndpoints: enableDebugRPCEndpoints,
|
||||
MaxMsgSize: maxMsgSize,
|
||||
ProposerIdsCache: b.proposerIdsCache,
|
||||
ExecutionEngineCaller: web3Service,
|
||||
})
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"auth.go",
|
||||
"block_cache.go",
|
||||
"block_reader.go",
|
||||
"check_transition_config.go",
|
||||
@@ -16,6 +15,7 @@ go_library(
|
||||
"options.go",
|
||||
"prometheus.go",
|
||||
"provider.go",
|
||||
"rpc_connection.go",
|
||||
"service.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/powchain",
|
||||
@@ -59,7 +59,6 @@ go_library(
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
"@com_github_golang_jwt_jwt_v4//:go_default_library",
|
||||
"@com_github_holiman_uint256//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
@@ -74,7 +73,6 @@ go_test(
|
||||
name = "go_default_test",
|
||||
size = "medium",
|
||||
srcs = [
|
||||
"auth_test.go",
|
||||
"block_cache_test.go",
|
||||
"block_reader_test.go",
|
||||
"check_transition_config_test.go",
|
||||
@@ -125,7 +123,6 @@ go_test(
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//trie:go_default_library",
|
||||
"@com_github_golang_jwt_jwt_v4//:go_default_library",
|
||||
"@com_github_holiman_uint256//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
|
||||
statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/network"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -81,7 +82,7 @@ func (s *Service) checkTransitionConfiguration(
|
||||
return
|
||||
}
|
||||
case tm := <-ticker.C:
|
||||
ctx, cancel := context.WithDeadline(ctx, tm.Add(DefaultRPCHTTPTimeout))
|
||||
ctx, cancel := context.WithDeadline(ctx, tm.Add(network.DefaultRPCHTTPTimeout))
|
||||
err = s.ExchangeTransitionConfiguration(ctx, cfg)
|
||||
s.handleExchangeConfigurationError(err)
|
||||
if !hasTtdReached {
|
||||
@@ -119,11 +120,16 @@ func (s *Service) handleExchangeConfigurationError(err error) {
|
||||
// Logs the terminal total difficulty status.
|
||||
func (s *Service) logTtdStatus(ctx context.Context, ttd *uint256.Int) (bool, error) {
|
||||
latest, err := s.LatestExecutionBlock(ctx)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, hexutil.ErrEmptyString):
|
||||
return false, nil
|
||||
case err != nil:
|
||||
return false, err
|
||||
}
|
||||
if latest == nil {
|
||||
case latest == nil:
|
||||
return false, errors.New("latest block is nil")
|
||||
case latest.TotalDifficulty == "":
|
||||
return false, nil
|
||||
default:
|
||||
}
|
||||
latestTtd, err := hexutil.DecodeBig(latest.TotalDifficulty)
|
||||
if err != nil {
|
||||
|
||||
@@ -190,6 +190,38 @@ func TestService_logTtdStatus(t *testing.T) {
|
||||
require.Equal(t, false, reached)
|
||||
}
|
||||
|
||||
func TestService_logTtdStatus_NotSyncedClient(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
defer func() {
|
||||
require.NoError(t, r.Body.Close())
|
||||
}()
|
||||
|
||||
resp := (*pb.ExecutionBlock)(nil) // Nil response when a client is not synced
|
||||
respJSON := map[string]interface{}{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": resp,
|
||||
}
|
||||
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
rpcClient, err := rpc.DialHTTP(srv.URL)
|
||||
require.NoError(t, err)
|
||||
defer rpcClient.Close()
|
||||
|
||||
service := &Service{
|
||||
cfg: &config{},
|
||||
}
|
||||
service.rpcClient = rpcClient
|
||||
|
||||
ttd := new(uint256.Int)
|
||||
reached, err := service.logTtdStatus(context.Background(), ttd.SetUint64(24343))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, reached)
|
||||
}
|
||||
|
||||
func emptyPayload() *pb.ExecutionPayload {
|
||||
return &pb.ExecutionPayload{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
|
||||
@@ -10,9 +10,12 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
@@ -49,8 +52,8 @@ type EngineCaller interface {
|
||||
ExchangeTransitionConfiguration(
|
||||
ctx context.Context, cfg *pb.TransitionConfiguration,
|
||||
) error
|
||||
LatestExecutionBlock(ctx context.Context) (*pb.ExecutionBlock, error)
|
||||
ExecutionBlockByHash(ctx context.Context, hash common.Hash) (*pb.ExecutionBlock, error)
|
||||
GetTerminalBlockHash(ctx context.Context) ([]byte, bool, error)
|
||||
}
|
||||
|
||||
// NewPayload calls the engine_newPayloadV1 method via JSON-RPC.
|
||||
@@ -174,6 +177,78 @@ func (s *Service) ExchangeTransitionConfiguration(
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTerminalBlockHash returns the valid terminal block hash based on total difficulty.
|
||||
//
|
||||
// Spec code:
|
||||
// def get_pow_block_at_terminal_total_difficulty(pow_chain: Dict[Hash32, PowBlock]) -> Optional[PowBlock]:
|
||||
// # `pow_chain` abstractly represents all blocks in the PoW chain
|
||||
// for block in pow_chain:
|
||||
// parent = pow_chain[block.parent_hash]
|
||||
// block_reached_ttd = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||
// parent_reached_ttd = parent.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||
// if block_reached_ttd and not parent_reached_ttd:
|
||||
// return block
|
||||
//
|
||||
// return None
|
||||
func (s *Service) GetTerminalBlockHash(ctx context.Context) ([]byte, bool, error) {
|
||||
ttd := new(big.Int)
|
||||
ttd.SetString(params.BeaconConfig().TerminalTotalDifficulty, 10)
|
||||
terminalTotalDifficulty, overflows := uint256.FromBig(ttd)
|
||||
if overflows {
|
||||
return nil, false, errors.New("could not convert terminal total difficulty to uint256")
|
||||
}
|
||||
blk, err := s.LatestExecutionBlock(ctx)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "could not get latest execution block")
|
||||
}
|
||||
if blk == nil {
|
||||
return nil, false, errors.New("latest execution block is nil")
|
||||
}
|
||||
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return nil, false, ctx.Err()
|
||||
}
|
||||
currentTotalDifficulty, err := tDStringToUint256(blk.TotalDifficulty)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "could not convert total difficulty to uint256")
|
||||
}
|
||||
blockReachedTTD := currentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
|
||||
|
||||
parentHash := bytesutil.ToBytes32(blk.ParentHash)
|
||||
if len(blk.ParentHash) == 0 || parentHash == params.BeaconConfig().ZeroHash {
|
||||
return nil, false, nil
|
||||
}
|
||||
parentBlk, err := s.ExecutionBlockByHash(ctx, parentHash)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "could not get parent execution block")
|
||||
}
|
||||
if parentBlk == nil {
|
||||
return nil, false, errors.New("parent execution block is nil")
|
||||
}
|
||||
if blockReachedTTD {
|
||||
parentTotalDifficulty, err := tDStringToUint256(parentBlk.TotalDifficulty)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "could not convert total difficulty to uint256")
|
||||
}
|
||||
parentReachedTTD := parentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
|
||||
if !parentReachedTTD {
|
||||
log.WithFields(logrus.Fields{
|
||||
"number": blk.Number,
|
||||
"hash": fmt.Sprintf("%#x", bytesutil.Trunc(blk.Hash)),
|
||||
"td": blk.TotalDifficulty,
|
||||
"parentTd": parentBlk.TotalDifficulty,
|
||||
"ttd": terminalTotalDifficulty,
|
||||
}).Info("Retrieved terminal block hash")
|
||||
return blk.Hash, true, nil
|
||||
}
|
||||
} else {
|
||||
return nil, false, nil
|
||||
}
|
||||
blk = parentBlk
|
||||
}
|
||||
}
|
||||
|
||||
// LatestExecutionBlock fetches the latest execution engine block by calling
|
||||
// eth_blockByNumber via JSON-RPC.
|
||||
func (s *Service) LatestExecutionBlock(ctx context.Context) (*pb.ExecutionBlock, error) {
|
||||
@@ -251,3 +326,15 @@ func isTimeout(e error) bool {
|
||||
t, ok := e.(httpTimeoutError)
|
||||
return ok && t.Timeout()
|
||||
}
|
||||
|
||||
func tDStringToUint256(td string) (*uint256.Int, error) {
|
||||
b, err := hexutil.DecodeBig(td)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i, overflows := uint256.FromBig(b)
|
||||
if overflows {
|
||||
return nil, errors.New("total difficulty overflowed")
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
@@ -418,6 +418,155 @@ func TestClient_HTTP(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_getPowBlockHashAtTerminalTotalDifficulty(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
paramsTd string
|
||||
currentPowBlock *pb.ExecutionBlock
|
||||
parentPowBlock *pb.ExecutionBlock
|
||||
errLatestExecutionBlk error
|
||||
wantTerminalBlockHash []byte
|
||||
wantExists bool
|
||||
errString string
|
||||
}{
|
||||
{
|
||||
name: "config td overflows",
|
||||
paramsTd: "1115792089237316195423570985008687907853269984665640564039457584007913129638912",
|
||||
errString: "could not convert terminal total difficulty to uint256",
|
||||
},
|
||||
{
|
||||
name: "could not get latest execution block",
|
||||
paramsTd: "1",
|
||||
errLatestExecutionBlk: errors.New("blah"),
|
||||
errString: "could not get latest execution block",
|
||||
},
|
||||
{
|
||||
name: "nil latest execution block",
|
||||
paramsTd: "1",
|
||||
errString: "latest execution block is nil",
|
||||
},
|
||||
{
|
||||
name: "current execution block invalid TD",
|
||||
paramsTd: "1",
|
||||
currentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'a'},
|
||||
TotalDifficulty: "1115792089237316195423570985008687907853269984665640564039457584007913129638912",
|
||||
},
|
||||
errString: "could not convert total difficulty to uint256",
|
||||
},
|
||||
{
|
||||
name: "current execution block has zero hash parent",
|
||||
paramsTd: "2",
|
||||
currentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'a'},
|
||||
ParentHash: params.BeaconConfig().ZeroHash[:],
|
||||
TotalDifficulty: "0x3",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "could not get parent block",
|
||||
paramsTd: "2",
|
||||
currentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'a'},
|
||||
ParentHash: []byte{'b'},
|
||||
TotalDifficulty: "0x3",
|
||||
},
|
||||
errString: "could not get parent execution block",
|
||||
},
|
||||
{
|
||||
name: "parent execution block invalid TD",
|
||||
paramsTd: "2",
|
||||
currentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'a'},
|
||||
ParentHash: []byte{'b'},
|
||||
TotalDifficulty: "0x3",
|
||||
},
|
||||
parentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'b'},
|
||||
ParentHash: []byte{'c'},
|
||||
TotalDifficulty: "1",
|
||||
},
|
||||
errString: "could not convert total difficulty to uint256",
|
||||
},
|
||||
{
|
||||
name: "happy case",
|
||||
paramsTd: "2",
|
||||
currentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'a'},
|
||||
ParentHash: []byte{'b'},
|
||||
TotalDifficulty: "0x3",
|
||||
},
|
||||
parentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'b'},
|
||||
ParentHash: []byte{'c'},
|
||||
TotalDifficulty: "0x1",
|
||||
},
|
||||
wantExists: true,
|
||||
wantTerminalBlockHash: []byte{'a'},
|
||||
},
|
||||
{
|
||||
name: "ttd not reached",
|
||||
paramsTd: "3",
|
||||
currentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'a'},
|
||||
ParentHash: []byte{'b'},
|
||||
TotalDifficulty: "0x2",
|
||||
},
|
||||
parentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'b'},
|
||||
ParentHash: []byte{'c'},
|
||||
TotalDifficulty: "0x1",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := params.BeaconConfig()
|
||||
cfg.TerminalTotalDifficulty = tt.paramsTd
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
var m map[[32]byte]*pb.ExecutionBlock
|
||||
if tt.parentPowBlock != nil {
|
||||
m = map[[32]byte]*pb.ExecutionBlock{
|
||||
bytesutil.ToBytes32(tt.parentPowBlock.Hash): tt.parentPowBlock,
|
||||
}
|
||||
}
|
||||
client := mocks.EngineClient{
|
||||
ErrLatestExecBlock: tt.errLatestExecutionBlk,
|
||||
ExecutionBlock: tt.currentPowBlock,
|
||||
BlockByHashMap: m,
|
||||
}
|
||||
b, e, err := client.GetTerminalBlockHash(context.Background())
|
||||
if tt.errString != "" {
|
||||
require.ErrorContains(t, tt.errString, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, tt.wantExists, e)
|
||||
require.DeepEqual(t, tt.wantTerminalBlockHash, b)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_tDStringToUint256(t *testing.T) {
|
||||
i, err := tDStringToUint256("0x0")
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, uint256.NewInt(0), i)
|
||||
|
||||
i, err = tDStringToUint256("0x10000")
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, uint256.NewInt(65536), i)
|
||||
|
||||
_, err = tDStringToUint256("100")
|
||||
require.ErrorContains(t, "hex string without 0x prefix", err)
|
||||
|
||||
_, err = tDStringToUint256("0xzzzzzz")
|
||||
require.ErrorContains(t, "invalid hex string", err)
|
||||
|
||||
_, err = tDStringToUint256("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
|
||||
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
|
||||
require.ErrorContains(t, "hex number > 256 bits", err)
|
||||
}
|
||||
|
||||
func TestExchangeTransitionConfiguration(t *testing.T) {
|
||||
fix := fixtures()
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package powchain
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
|
||||
@@ -11,11 +8,9 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
"github.com/prysmaticlabs/prysm/network"
|
||||
"github.com/prysmaticlabs/prysm/network/authorization"
|
||||
)
|
||||
|
||||
// DefaultRPCHTTPTimeout for HTTP requests via an RPC connection to an execution node.
|
||||
const DefaultRPCHTTPTimeout = time.Second * 6
|
||||
|
||||
type Option func(s *Service) error
|
||||
|
||||
// WithHttpEndpoints deduplicates and parses http endpoints for the powchain service to use,
|
||||
@@ -38,20 +33,29 @@ func WithHttpEndpoints(endpointStrings []string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithJWTSecret for authenticating the execution node JSON-RPC endpoint.
|
||||
func WithJWTSecret(secret []byte) Option {
|
||||
return func(c *Service) error {
|
||||
// WithHttpEndpointsAndJWTSecret for authenticating the execution node JSON-RPC endpoint.
|
||||
func WithHttpEndpointsAndJWTSecret(endpointStrings []string, secret []byte) Option {
|
||||
return func(s *Service) error {
|
||||
if len(secret) == 0 {
|
||||
return nil
|
||||
}
|
||||
authTransport := &jwtTransport{
|
||||
underlyingTransport: http.DefaultTransport,
|
||||
jwtSecret: secret,
|
||||
stringEndpoints := dedupEndpoints(endpointStrings)
|
||||
endpoints := make([]network.Endpoint, len(stringEndpoints))
|
||||
// Overwrite authorization type for all endpoints to be of a bearer
|
||||
// type.
|
||||
for i, e := range stringEndpoints {
|
||||
hEndpoint := HttpEndpoint(e)
|
||||
hEndpoint.Auth.Method = authorization.Bearer
|
||||
hEndpoint.Auth.Value = string(secret)
|
||||
endpoints[i] = hEndpoint
|
||||
}
|
||||
c.cfg.httpRPCClient = &http.Client{
|
||||
Timeout: DefaultRPCHTTPTimeout,
|
||||
Transport: authTransport,
|
||||
// Select first http endpoint in the provided list.
|
||||
var currEndpoint network.Endpoint
|
||||
if len(endpointStrings) > 0 {
|
||||
currEndpoint = endpoints[0]
|
||||
}
|
||||
s.cfg.httpEndpoints = endpoints
|
||||
s.cfg.currHttpEndpoint = currEndpoint
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
176
beacon-chain/powchain/rpc_connection.go
Normal file
176
beacon-chain/powchain/rpc_connection.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package powchain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
gethRPC "github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/io/logs"
|
||||
"github.com/prysmaticlabs/prysm/network"
|
||||
"github.com/prysmaticlabs/prysm/network/authorization"
|
||||
)
|
||||
|
||||
func (s *Service) setupExecutionClientConnections(ctx context.Context, currEndpoint network.Endpoint) error {
|
||||
client, err := s.newRPCClientWithAuth(ctx, currEndpoint)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not dial execution node")
|
||||
}
|
||||
// Attach the clients to the service struct.
|
||||
fetcher := ethclient.NewClient(client)
|
||||
s.rpcClient = client
|
||||
s.httpLogger = fetcher
|
||||
s.eth1DataFetcher = fetcher
|
||||
|
||||
depositContractCaller, err := contracts.NewDepositContractCaller(s.cfg.depositContractAddr, fetcher)
|
||||
if err != nil {
|
||||
client.Close()
|
||||
return errors.Wrap(err, "could not initialize deposit contract caller")
|
||||
}
|
||||
s.depositContractCaller = depositContractCaller
|
||||
|
||||
// Ensure we have the correct chain and deposit IDs.
|
||||
if err := ensureCorrectExecutionChain(ctx, fetcher); err != nil {
|
||||
client.Close()
|
||||
return errors.Wrap(err, "could not make initial request to verify execution chain ID")
|
||||
}
|
||||
s.updateConnectedETH1(true)
|
||||
s.runError = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Every N seconds, defined as a backoffPeriod, attempts to re-establish an execution client
|
||||
// connection and if this does not work, we fallback to the next endpoint if defined.
|
||||
func (s *Service) pollConnectionStatus(ctx context.Context) {
|
||||
// Use a custom logger to only log errors
|
||||
logCounter := 0
|
||||
errorLogger := func(err error, msg string) {
|
||||
if logCounter > logThreshold {
|
||||
log.Errorf("%s: %v", msg, err)
|
||||
logCounter = 0
|
||||
}
|
||||
logCounter++
|
||||
}
|
||||
ticker := time.NewTicker(backOffPeriod)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
log.Debugf("Trying to dial endpoint: %s", logs.MaskCredentialsLogging(s.cfg.currHttpEndpoint.Url))
|
||||
if err := s.setupExecutionClientConnections(ctx, s.cfg.currHttpEndpoint); err != nil {
|
||||
errorLogger(err, "Could not connect to execution client endpoint")
|
||||
s.runError = err
|
||||
s.fallbackToNextEndpoint()
|
||||
}
|
||||
case <-s.ctx.Done():
|
||||
log.Debug("Received cancelled context,closing existing powchain service")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forces to retry an execution client connection.
|
||||
func (s *Service) retryExecutionClientConnection(ctx context.Context, err error) {
|
||||
s.runError = err
|
||||
s.updateConnectedETH1(false)
|
||||
// Back off for a while before redialing.
|
||||
time.Sleep(backOffPeriod)
|
||||
if err := s.setupExecutionClientConnections(ctx, s.cfg.currHttpEndpoint); err != nil {
|
||||
s.runError = err
|
||||
return
|
||||
}
|
||||
// Reset run error in the event of a successful connection.
|
||||
s.runError = nil
|
||||
}
|
||||
|
||||
// This performs a health check on our primary endpoint, and if it
|
||||
// is ready to serve we connect to it again. This method is only
|
||||
// relevant if we are on our backup endpoint.
|
||||
func (s *Service) checkDefaultEndpoint(ctx context.Context) {
|
||||
primaryEndpoint := s.cfg.httpEndpoints[0]
|
||||
// Return early if we are running on our primary
|
||||
// endpoint.
|
||||
if s.cfg.currHttpEndpoint.Equals(primaryEndpoint) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.setupExecutionClientConnections(ctx, primaryEndpoint); err != nil {
|
||||
log.Debugf("Primary endpoint not ready: %v", err)
|
||||
return
|
||||
}
|
||||
s.updateCurrHttpEndpoint(primaryEndpoint)
|
||||
}
|
||||
|
||||
// This is an inefficient way to search for the next endpoint, but given N is
|
||||
// expected to be small, it is fine to search this way.
|
||||
func (s *Service) fallbackToNextEndpoint() {
|
||||
currEndpoint := s.cfg.currHttpEndpoint
|
||||
currIndex := 0
|
||||
totalEndpoints := len(s.cfg.httpEndpoints)
|
||||
|
||||
for i, endpoint := range s.cfg.httpEndpoints {
|
||||
if endpoint.Equals(currEndpoint) {
|
||||
currIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
nextIndex := currIndex + 1
|
||||
if nextIndex >= totalEndpoints {
|
||||
nextIndex = 0
|
||||
}
|
||||
s.updateCurrHttpEndpoint(s.cfg.httpEndpoints[nextIndex])
|
||||
if nextIndex != currIndex {
|
||||
log.Infof("Falling back to alternative endpoint: %s", logs.MaskCredentialsLogging(s.cfg.currHttpEndpoint.Url))
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes an RPC connection with authentication headers.
|
||||
func (s *Service) newRPCClientWithAuth(ctx context.Context, endpoint network.Endpoint) (*gethRPC.Client, error) {
|
||||
// Need to handle ipc and http
|
||||
var client *gethRPC.Client
|
||||
u, err := url.Parse(endpoint.Url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch u.Scheme {
|
||||
case "http", "https":
|
||||
client, err = gethRPC.DialHTTPWithClient(endpoint.Url, endpoint.HttpClient())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "":
|
||||
client, err = gethRPC.DialIPC(ctx, endpoint.Url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("no known transport for URL scheme %q", u.Scheme)
|
||||
}
|
||||
if endpoint.Auth.Method != authorization.None {
|
||||
header, err := endpoint.Auth.ToHeaderValue()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.SetHeader("Authorization", header)
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// Checks the chain ID of the execution client to ensure
|
||||
// it matches local parameters of what Prysm expects.
|
||||
func ensureCorrectExecutionChain(ctx context.Context, client *ethclient.Client) error {
|
||||
cID, err := client.ChainID(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wantChainID := params.BeaconConfig().DepositChainID
|
||||
if cID.Uint64() != wantChainID {
|
||||
return fmt.Errorf("wanted chain ID %d, got %d", wantChainID, cID.Uint64())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
@@ -39,10 +38,8 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/container/trie"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/io/logs"
|
||||
"github.com/prysmaticlabs/prysm/monitoring/clientstats"
|
||||
"github.com/prysmaticlabs/prysm/network"
|
||||
"github.com/prysmaticlabs/prysm/network/authorization"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
prysmTime "github.com/prysmaticlabs/prysm/time"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
@@ -114,6 +111,7 @@ type Chain interface {
|
||||
// RPCDataFetcher defines a subset of methods conformed to by ETH1.0 RPC clients for
|
||||
// fetching eth1 data from the clients.
|
||||
type RPCDataFetcher interface {
|
||||
Close()
|
||||
HeaderByNumber(ctx context.Context, number *big.Int) (*gethTypes.Header, error)
|
||||
HeaderByHash(ctx context.Context, hash common.Hash) (*gethTypes.Header, error)
|
||||
SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error)
|
||||
@@ -121,6 +119,7 @@ type RPCDataFetcher interface {
|
||||
|
||||
// RPCClient defines the rpc methods required to interact with the eth1 node.
|
||||
type RPCClient interface {
|
||||
Close()
|
||||
BatchCall(b []gethRPC.BatchElem) error
|
||||
CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error
|
||||
}
|
||||
@@ -135,7 +134,6 @@ type config struct {
|
||||
eth1HeaderReqLimit uint64
|
||||
beaconNodeStatsUpdater BeaconNodeStatsUpdater
|
||||
httpEndpoints []network.Endpoint
|
||||
httpRPCClient *http.Client
|
||||
currHttpEndpoint network.Endpoint
|
||||
finalizedStateAtStartup state.BeaconState
|
||||
}
|
||||
@@ -228,14 +226,9 @@ func NewService(ctx context.Context, opts ...Option) (*Service, error) {
|
||||
|
||||
// Start a web3 service's main event loop.
|
||||
func (s *Service) Start() {
|
||||
|
||||
if err := s.connectToPowChain(); err != nil {
|
||||
log.WithError(err).Fatal("Could not connect to execution endpoint")
|
||||
if err := s.setupExecutionClientConnections(s.ctx, s.cfg.currHttpEndpoint); err != nil {
|
||||
log.WithError(err).Error("Could not connect to execution endpoint")
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"endpoint": logs.MaskCredentialsLogging(s.cfg.currHttpEndpoint.Url),
|
||||
}).Info("Connected to Ethereum execution client RPC")
|
||||
// If the chain has not started already and we don't have access to eth1 nodes, we will not be
|
||||
// able to generate the genesis state.
|
||||
if !s.chainStartData.Chainstarted && s.cfg.currHttpEndpoint.Url == "" {
|
||||
@@ -253,7 +246,7 @@ func (s *Service) Start() {
|
||||
s.isRunning = true
|
||||
|
||||
// Poll the execution client connection and fallback if errors occur.
|
||||
go s.pollConnectionStatus()
|
||||
go s.pollConnectionStatus(s.ctx)
|
||||
|
||||
// Check transition configuration for the engine API client in the background.
|
||||
go s.checkTransitionConfiguration(s.ctx, make(chan *feed.Event, 1))
|
||||
@@ -266,7 +259,12 @@ func (s *Service) Stop() error {
|
||||
if s.cancel != nil {
|
||||
defer s.cancel()
|
||||
}
|
||||
s.closeClients()
|
||||
if s.rpcClient != nil {
|
||||
s.rpcClient.Close()
|
||||
}
|
||||
if s.eth1DataFetcher != nil {
|
||||
s.eth1DataFetcher.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -338,10 +336,7 @@ func (s *Service) CurrentETH1Endpoint() string {
|
||||
|
||||
// CurrentETH1ConnectionError returns the error (if any) of the current connection.
|
||||
func (s *Service) CurrentETH1ConnectionError() error {
|
||||
httpClient, rpcClient, err := s.dialETH1Nodes(s.cfg.currHttpEndpoint)
|
||||
httpClient.Close()
|
||||
rpcClient.Close()
|
||||
return err
|
||||
return s.runError
|
||||
}
|
||||
|
||||
// ETH1Endpoints returns the slice of HTTP endpoint URLs (default is 0th element).
|
||||
@@ -358,10 +353,17 @@ func (s *Service) ETH1Endpoints() []string {
|
||||
func (s *Service) ETH1ConnectionErrors() []error {
|
||||
var errs []error
|
||||
for _, ep := range s.cfg.httpEndpoints {
|
||||
httpClient, rpcClient, err := s.dialETH1Nodes(ep)
|
||||
httpClient.Close()
|
||||
rpcClient.Close()
|
||||
errs = append(errs, err)
|
||||
client, err := s.newRPCClientWithAuth(s.ctx, ep)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
if err := ensureCorrectExecutionChain(s.ctx, ethclient.NewClient(client)); err != nil {
|
||||
client.Close()
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
client.Close()
|
||||
}
|
||||
return errs
|
||||
}
|
||||
@@ -376,146 +378,6 @@ func (s *Service) followBlockHeight(_ context.Context) (uint64, error) {
|
||||
return latestValidBlock, nil
|
||||
}
|
||||
|
||||
func (s *Service) connectToPowChain() error {
|
||||
httpClient, rpcClient, err := s.dialETH1Nodes(s.cfg.currHttpEndpoint)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not dial execution node")
|
||||
}
|
||||
|
||||
depositContractCaller, err := contracts.NewDepositContractCaller(s.cfg.depositContractAddr, httpClient)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not initialize deposit contract caller")
|
||||
}
|
||||
|
||||
if httpClient == nil || rpcClient == nil || depositContractCaller == nil {
|
||||
return errors.New("execution client RPC is nil")
|
||||
}
|
||||
s.httpLogger = httpClient
|
||||
s.eth1DataFetcher = httpClient
|
||||
s.depositContractCaller = depositContractCaller
|
||||
s.rpcClient = rpcClient
|
||||
|
||||
s.updateConnectedETH1(true)
|
||||
s.runError = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) dialETH1Nodes(endpoint network.Endpoint) (*ethclient.Client, *gethRPC.Client, error) {
|
||||
httpRPCClient, err := gethRPC.Dial(endpoint.Url)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if endpoint.Auth.Method != authorization.None {
|
||||
header, err := endpoint.Auth.ToHeaderValue()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
httpRPCClient.SetHeader("Authorization", header)
|
||||
}
|
||||
httpClient := ethclient.NewClient(httpRPCClient)
|
||||
// Add a method to clean-up and close clients in the event
|
||||
// of any connection failure.
|
||||
closeClients := func() {
|
||||
httpRPCClient.Close()
|
||||
httpClient.Close()
|
||||
}
|
||||
// Make a simple call to ensure we are actually connected to a working node.
|
||||
cID, err := httpClient.ChainID(s.ctx)
|
||||
if err != nil {
|
||||
closeClients()
|
||||
return nil, nil, err
|
||||
}
|
||||
nID, err := httpClient.NetworkID(s.ctx)
|
||||
if err != nil {
|
||||
closeClients()
|
||||
return nil, nil, err
|
||||
}
|
||||
if cID.Uint64() != params.BeaconConfig().DepositChainID {
|
||||
closeClients()
|
||||
return nil, nil, fmt.Errorf("eth1 node using incorrect chain id, %d != %d", cID.Uint64(), params.BeaconConfig().DepositChainID)
|
||||
}
|
||||
if nID.Uint64() != params.BeaconConfig().DepositNetworkID {
|
||||
closeClients()
|
||||
return nil, nil, fmt.Errorf("eth1 node using incorrect network id, %d != %d", nID.Uint64(), params.BeaconConfig().DepositNetworkID)
|
||||
}
|
||||
|
||||
return httpClient, httpRPCClient, nil
|
||||
}
|
||||
|
||||
// closes down our active eth1 clients.
|
||||
func (s *Service) closeClients() {
|
||||
gethClient, ok := s.rpcClient.(*gethRPC.Client)
|
||||
if ok {
|
||||
gethClient.Close()
|
||||
}
|
||||
httpClient, ok := s.eth1DataFetcher.(*ethclient.Client)
|
||||
if ok {
|
||||
httpClient.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) pollConnectionStatus() {
|
||||
// Use a custom logger to only log errors
|
||||
logCounter := 0
|
||||
errorLogger := func(err error, msg string) {
|
||||
if logCounter > logThreshold {
|
||||
log.Errorf("%s: %v", msg, err)
|
||||
logCounter = 0
|
||||
}
|
||||
logCounter++
|
||||
}
|
||||
ticker := time.NewTicker(backOffPeriod)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
log.Debugf("Trying to dial endpoint: %s", logs.MaskCredentialsLogging(s.cfg.currHttpEndpoint.Url))
|
||||
errConnect := s.connectToPowChain()
|
||||
if errConnect != nil {
|
||||
errorLogger(errConnect, "Could not connect to powchain endpoint")
|
||||
s.runError = errConnect
|
||||
s.fallbackToNextEndpoint()
|
||||
continue
|
||||
}
|
||||
case <-s.ctx.Done():
|
||||
log.Debug("Received cancelled context,closing existing powchain service")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checks if the eth1 node is healthy and ready to serve before
|
||||
// fetching data from it.
|
||||
func (s *Service) isEth1NodeSynced() (bool, error) {
|
||||
syncProg, err := s.eth1DataFetcher.SyncProgress(s.ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if syncProg != nil {
|
||||
return false, nil
|
||||
}
|
||||
head, err := s.eth1DataFetcher.HeaderByNumber(s.ctx, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return !eth1HeadIsBehind(head.Time), nil
|
||||
}
|
||||
|
||||
// Reconnect to eth1 node in case of any failure.
|
||||
func (s *Service) retryETH1Node(err error) {
|
||||
s.runError = err
|
||||
s.updateConnectedETH1(false)
|
||||
// Back off for a while before
|
||||
// resuming dialing the eth1 node.
|
||||
time.Sleep(backOffPeriod)
|
||||
if err := s.connectToPowChain(); err != nil {
|
||||
s.runError = err
|
||||
return
|
||||
}
|
||||
// Reset run error in the event of a successful connection.
|
||||
s.runError = nil
|
||||
}
|
||||
|
||||
func (s *Service) initDepositCaches(ctx context.Context, ctrs []*ethpb.DepositContainer) error {
|
||||
if len(ctrs) == 0 {
|
||||
return nil
|
||||
@@ -551,11 +413,14 @@ func (s *Service) initDepositCaches(ctx context.Context, ctrs []*ethpb.DepositCo
|
||||
// accumulates. we finalize them here before we are ready to receive a block.
|
||||
// Otherwise, the first few blocks will be slower to compute as we will
|
||||
// hold the lock and be busy finalizing the deposits.
|
||||
s.cfg.depositCache.InsertFinalizedDeposits(ctx, int64(currIndex)) // lint:ignore uintcast -- deposit index will not exceed int64 in your lifetime.
|
||||
// The deposit index in the state is always the index of the next deposit
|
||||
// to be included(rather than the last one to be processed). This was most likely
|
||||
// done as the state cannot represent signed integers.
|
||||
actualIndex := int64(currIndex) - 1 // lint:ignore uintcast -- deposit index will not exceed int64 in your lifetime.
|
||||
s.cfg.depositCache.InsertFinalizedDeposits(ctx, actualIndex)
|
||||
// Deposit proofs are only used during state transition and can be safely removed to save space.
|
||||
|
||||
// lint:ignore uintcast -- deposit index will not exceed int64 in your lifetime.
|
||||
if err = s.cfg.depositCache.PruneProofs(ctx, int64(currIndex)); err != nil {
|
||||
if err = s.cfg.depositCache.PruneProofs(ctx, actualIndex); err != nil {
|
||||
return errors.Wrap(err, "could not prune deposit proofs")
|
||||
}
|
||||
}
|
||||
@@ -650,7 +515,7 @@ func (s *Service) handleETH1FollowDistance() {
|
||||
fiveMinutesTimeout := prysmTime.Now().Add(-5 * time.Minute)
|
||||
// check that web3 client is syncing
|
||||
if time.Unix(int64(s.latestEth1Data.BlockTime), 0).Before(fiveMinutesTimeout) {
|
||||
log.Warn("eth1 client is not syncing")
|
||||
log.Warn("Execution client is not syncing")
|
||||
}
|
||||
if !s.chainStartData.Chainstarted {
|
||||
if err := s.checkBlockNumberForChainStart(ctx, big.NewInt(int64(s.latestEth1Data.LastRequestedBlock))); err != nil {
|
||||
@@ -680,6 +545,15 @@ func (s *Service) handleETH1FollowDistance() {
|
||||
}
|
||||
|
||||
func (s *Service) initPOWService() {
|
||||
// Use a custom logger to only log errors
|
||||
logCounter := 0
|
||||
errorLogger := func(err error, msg string) {
|
||||
if logCounter > logThreshold {
|
||||
log.Errorf("%s: %v", msg, err)
|
||||
logCounter = 0
|
||||
}
|
||||
logCounter++
|
||||
}
|
||||
|
||||
// Run in a select loop to retry in the event of any failures.
|
||||
for {
|
||||
@@ -690,8 +564,8 @@ func (s *Service) initPOWService() {
|
||||
ctx := s.ctx
|
||||
header, err := s.eth1DataFetcher.HeaderByNumber(ctx, nil)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to retrieve latest ETH1.0 chain header: %v", err)
|
||||
s.retryETH1Node(err)
|
||||
s.retryExecutionClientConnection(ctx, err)
|
||||
errorLogger(err, "Unable to retrieve latest execution client header")
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -700,14 +574,14 @@ func (s *Service) initPOWService() {
|
||||
s.latestEth1Data.BlockTime = header.Time
|
||||
|
||||
if err := s.processPastLogs(ctx); err != nil {
|
||||
log.Errorf("Unable to process past logs %v", err)
|
||||
s.retryETH1Node(err)
|
||||
s.retryExecutionClientConnection(ctx, err)
|
||||
errorLogger(err, "Unable to process past deposit contract logs")
|
||||
continue
|
||||
}
|
||||
// Cache eth1 headers from our voting period.
|
||||
if err := s.cacheHeadersForEth1DataVote(ctx); err != nil {
|
||||
log.Errorf("Unable to process past headers %v", err)
|
||||
s.retryETH1Node(err)
|
||||
s.retryExecutionClientConnection(ctx, err)
|
||||
errorLogger(err, "Unable to cache headers for execution client votes")
|
||||
continue
|
||||
}
|
||||
// Handle edge case with embedded genesis state by fetching genesis header to determine
|
||||
@@ -720,15 +594,15 @@ func (s *Service) initPOWService() {
|
||||
if genHash != [32]byte{} {
|
||||
genHeader, err := s.eth1DataFetcher.HeaderByHash(ctx, genHash)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to retrieve genesis ETH1.0 chain header: %v", err)
|
||||
s.retryETH1Node(err)
|
||||
s.retryExecutionClientConnection(ctx, err)
|
||||
errorLogger(err, "Unable to retrieve proof-of-stake genesis block data")
|
||||
continue
|
||||
}
|
||||
genBlock = genHeader.Number.Uint64()
|
||||
}
|
||||
s.chainStartData.GenesisBlock = genBlock
|
||||
if err := s.savePowchainData(ctx); err != nil {
|
||||
log.Errorf("Unable to save powchain data: %v", err)
|
||||
errorLogger(err, "Unable to save execution client data")
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -757,17 +631,16 @@ func (s *Service) run(done <-chan struct{}) {
|
||||
head, err := s.eth1DataFetcher.HeaderByNumber(s.ctx, nil)
|
||||
if err != nil {
|
||||
log.WithError(err).Debug("Could not fetch latest eth1 header")
|
||||
s.retryETH1Node(err)
|
||||
continue
|
||||
}
|
||||
if eth1HeadIsBehind(head.Time) {
|
||||
s.retryExecutionClientConnection(s.ctx, err)
|
||||
log.WithError(errFarBehind).Debug("Could not get an up to date eth1 header")
|
||||
s.retryETH1Node(errFarBehind)
|
||||
continue
|
||||
}
|
||||
s.processBlockHeader(head)
|
||||
s.handleETH1FollowDistance()
|
||||
s.checkDefaultEndpoint()
|
||||
s.checkDefaultEndpoint(s.ctx)
|
||||
case <-chainstartTicker.C:
|
||||
if s.chainStartData.Chainstarted {
|
||||
chainstartTicker.Stop()
|
||||
@@ -854,59 +727,6 @@ func (s *Service) determineEarliestVotingBlock(ctx context.Context, followBlock
|
||||
return hdr.Number.Uint64(), nil
|
||||
}
|
||||
|
||||
// This performs a health check on our primary endpoint, and if it
|
||||
// is ready to serve we connect to it again. This method is only
|
||||
// relevant if we are on our backup endpoint.
|
||||
func (s *Service) checkDefaultEndpoint() {
|
||||
primaryEndpoint := s.cfg.httpEndpoints[0]
|
||||
// Return early if we are running on our primary
|
||||
// endpoint.
|
||||
if s.cfg.currHttpEndpoint.Equals(primaryEndpoint) {
|
||||
return
|
||||
}
|
||||
|
||||
httpClient, rpcClient, err := s.dialETH1Nodes(primaryEndpoint)
|
||||
if err != nil {
|
||||
log.Debugf("Primary endpoint not ready: %v", err)
|
||||
return
|
||||
}
|
||||
log.Info("Primary endpoint ready again, switching back to it")
|
||||
// Close the clients and let our main connection routine
|
||||
// properly connect with it.
|
||||
httpClient.Close()
|
||||
rpcClient.Close()
|
||||
// Close current active clients.
|
||||
s.closeClients()
|
||||
|
||||
// Switch back to primary endpoint and try connecting
|
||||
// to it again.
|
||||
s.updateCurrHttpEndpoint(primaryEndpoint)
|
||||
s.retryETH1Node(nil)
|
||||
}
|
||||
|
||||
// This is an inefficient way to search for the next endpoint, but given N is expected to be
|
||||
// small ( < 25), it is fine to search this way.
|
||||
func (s *Service) fallbackToNextEndpoint() {
|
||||
currEndpoint := s.cfg.currHttpEndpoint
|
||||
currIndex := 0
|
||||
totalEndpoints := len(s.cfg.httpEndpoints)
|
||||
|
||||
for i, endpoint := range s.cfg.httpEndpoints {
|
||||
if endpoint.Equals(currEndpoint) {
|
||||
currIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
nextIndex := currIndex + 1
|
||||
if nextIndex >= totalEndpoints {
|
||||
nextIndex = 0
|
||||
}
|
||||
s.updateCurrHttpEndpoint(s.cfg.httpEndpoints[nextIndex])
|
||||
if nextIndex != currIndex {
|
||||
log.Infof("Falling back to alternative endpoint: %s", logs.MaskCredentialsLogging(s.cfg.currHttpEndpoint.Url))
|
||||
}
|
||||
}
|
||||
|
||||
// initializes our service from the provided eth1data object by initializing all the relevant
|
||||
// fields and data.
|
||||
func (s *Service) initializeEth1Data(ctx context.Context, eth1DataInDB *ethpb.ETH1ChainData) error {
|
||||
|
||||
@@ -42,6 +42,8 @@ type goodLogger struct {
|
||||
backend *backends.SimulatedBackend
|
||||
}
|
||||
|
||||
func (_ *goodLogger) Close() {}
|
||||
|
||||
func (g *goodLogger) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- gethTypes.Log) (ethereum.Subscription, error) {
|
||||
if g.backend == nil {
|
||||
return new(event.Feed).Subscribe(ch), nil
|
||||
@@ -80,6 +82,8 @@ type goodFetcher struct {
|
||||
backend *backends.SimulatedBackend
|
||||
}
|
||||
|
||||
func (_ *goodFetcher) Close() {}
|
||||
|
||||
func (g *goodFetcher) HeaderByHash(_ context.Context, hash common.Hash) (*gethTypes.Header, error) {
|
||||
if bytes.Equal(hash.Bytes(), common.BytesToHash([]byte{0}).Bytes()) {
|
||||
return nil, fmt.Errorf("expected block hash to be nonzero %v", hash)
|
||||
@@ -225,10 +229,6 @@ func TestService_Eth1Synced(t *testing.T) {
|
||||
now := time.Now()
|
||||
assert.NoError(t, testAcc.Backend.AdjustTime(now.Sub(time.Unix(int64(currTime), 0))))
|
||||
testAcc.Backend.Commit()
|
||||
|
||||
synced, err := web3Service.isEth1NodeSynced()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, synced, "Expected eth1 nodes to be synced")
|
||||
}
|
||||
|
||||
func TestFollowBlock_OK(t *testing.T) {
|
||||
@@ -472,7 +472,7 @@ func TestInitDepositCacheWithFinalization_OK(t *testing.T) {
|
||||
require.NoError(t, s.cfg.beaconDB.SaveState(context.Background(), emptyState, headRoot))
|
||||
require.NoError(t, stateGen.SaveState(context.Background(), headRoot, emptyState))
|
||||
s.cfg.stateGen = stateGen
|
||||
require.NoError(t, emptyState.SetEth1DepositIndex(2))
|
||||
require.NoError(t, emptyState.SetEth1DepositIndex(3))
|
||||
|
||||
ctx := context.Background()
|
||||
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Epoch: slots.ToEpoch(0), Root: headRoot[:]}))
|
||||
@@ -480,8 +480,8 @@ func TestInitDepositCacheWithFinalization_OK(t *testing.T) {
|
||||
|
||||
s.chainStartData.Chainstarted = true
|
||||
require.NoError(t, s.initDepositCaches(context.Background(), ctrs))
|
||||
|
||||
deps := s.cfg.depositCache.NonFinalizedDeposits(context.Background(), nil)
|
||||
fDeposits := s.cfg.depositCache.FinalizedDeposits(ctx)
|
||||
deps := s.cfg.depositCache.NonFinalizedDeposits(context.Background(), fDeposits.MerkleTrieIndex, nil)
|
||||
assert.Equal(t, 0, len(deps))
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ go_library(
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
"@com_github_holiman_uint256//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -2,25 +2,32 @@ package testing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
|
||||
)
|
||||
|
||||
// EngineClient --
|
||||
type EngineClient struct {
|
||||
NewPayloadResp []byte
|
||||
PayloadIDBytes *pb.PayloadIDBytes
|
||||
ForkChoiceUpdatedResp []byte
|
||||
ExecutionPayload *pb.ExecutionPayload
|
||||
ExecutionBlock *pb.ExecutionBlock
|
||||
Err error
|
||||
ErrLatestExecBlock error
|
||||
ErrExecBlockByHash error
|
||||
ErrForkchoiceUpdated error
|
||||
ErrNewPayload error
|
||||
BlockByHashMap map[[32]byte]*pb.ExecutionBlock
|
||||
NewPayloadResp []byte
|
||||
PayloadIDBytes *pb.PayloadIDBytes
|
||||
ForkChoiceUpdatedResp []byte
|
||||
ExecutionPayload *pb.ExecutionPayload
|
||||
ExecutionBlock *pb.ExecutionBlock
|
||||
Err error
|
||||
ErrLatestExecBlock error
|
||||
ErrExecBlockByHash error
|
||||
ErrForkchoiceUpdated error
|
||||
ErrNewPayload error
|
||||
BlockByHashMap map[[32]byte]*pb.ExecutionBlock
|
||||
TerminalBlockHash []byte
|
||||
TerminalBlockHashExists bool
|
||||
}
|
||||
|
||||
// NewPayload --
|
||||
@@ -58,3 +65,52 @@ func (e *EngineClient) ExecutionBlockByHash(_ context.Context, h common.Hash) (*
|
||||
}
|
||||
return b, e.ErrExecBlockByHash
|
||||
}
|
||||
|
||||
// GetTerminalBlockHash --
|
||||
func (e *EngineClient) GetTerminalBlockHash(ctx context.Context) ([]byte, bool, error) {
|
||||
ttd := new(big.Int)
|
||||
ttd.SetString(params.BeaconConfig().TerminalTotalDifficulty, 10)
|
||||
terminalTotalDifficulty, overflows := uint256.FromBig(ttd)
|
||||
if overflows {
|
||||
return nil, false, errors.New("could not convert terminal total difficulty to uint256")
|
||||
}
|
||||
blk, err := e.LatestExecutionBlock(ctx)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "could not get latest execution block")
|
||||
}
|
||||
if blk == nil {
|
||||
return nil, false, errors.New("latest execution block is nil")
|
||||
}
|
||||
|
||||
for {
|
||||
b, err := hexutil.DecodeBig(blk.TotalDifficulty)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "could not convert total difficulty to uint256")
|
||||
}
|
||||
currentTotalDifficulty, _ := uint256.FromBig(b)
|
||||
blockReachedTTD := currentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
|
||||
|
||||
parentHash := bytesutil.ToBytes32(blk.ParentHash)
|
||||
if len(blk.ParentHash) == 0 || parentHash == params.BeaconConfig().ZeroHash {
|
||||
return nil, false, nil
|
||||
}
|
||||
parentBlk, err := e.ExecutionBlockByHash(ctx, parentHash)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "could not get parent execution block")
|
||||
}
|
||||
if blockReachedTTD {
|
||||
b, err := hexutil.DecodeBig(parentBlk.TotalDifficulty)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "could not convert total difficulty to uint256")
|
||||
}
|
||||
parentTotalDifficulty, _ := uint256.FromBig(b)
|
||||
parentReachedTTD := parentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
|
||||
if !parentReachedTTD {
|
||||
return blk.Hash, true, nil
|
||||
}
|
||||
} else {
|
||||
return nil, false, nil
|
||||
}
|
||||
blk = parentBlk
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +144,8 @@ type RPCClient struct {
|
||||
Backend *backends.SimulatedBackend
|
||||
}
|
||||
|
||||
func (_ *RPCClient) Close() {}
|
||||
|
||||
func (*RPCClient) CallContext(_ context.Context, _ interface{}, _ string, _ ...interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -443,7 +443,7 @@ type executionPayloadHeaderJson struct {
|
||||
ParentHash string `json:"parent_hash" hex:"true"`
|
||||
FeeRecipient string `json:"fee_recipient" hex:"true"`
|
||||
StateRoot string `json:"state_root" hex:"true"`
|
||||
ReceiptsRoot string `json:"receipt_root" hex:"true"`
|
||||
ReceiptsRoot string `json:"receipts_root" hex:"true"`
|
||||
LogsBloom string `json:"logs_bloom" hex:"true"`
|
||||
PrevRandao string `json:"prev_randao" hex:"true"`
|
||||
BlockNumber string `json:"block_number"`
|
||||
|
||||
@@ -1024,19 +1024,20 @@ func TestProduceBlockV2(t *testing.T) {
|
||||
TotalDifficulty: "0x1",
|
||||
},
|
||||
},
|
||||
TimeFetcher: &mockChain.ChainService{},
|
||||
HeadFetcher: &mockChain.ChainService{State: beaconState, Root: parentRoot[:]},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
BlockReceiver: &mockChain.ChainService{},
|
||||
ChainStartFetcher: &mockPOW.POWChain{},
|
||||
Eth1InfoFetcher: &mockPOW.POWChain{},
|
||||
Eth1BlockFetcher: &mockPOW.POWChain{},
|
||||
MockEth1Votes: true,
|
||||
AttPool: attestations.NewPool(),
|
||||
SlashingsPool: slashings.NewPool(),
|
||||
ExitPool: voluntaryexits.NewPool(),
|
||||
StateGen: stategen.New(db),
|
||||
SyncCommitteePool: synccommittee.NewStore(),
|
||||
TimeFetcher: &mockChain.ChainService{},
|
||||
HeadFetcher: &mockChain.ChainService{State: beaconState, Root: parentRoot[:]},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
BlockReceiver: &mockChain.ChainService{},
|
||||
ChainStartFetcher: &mockPOW.POWChain{},
|
||||
Eth1InfoFetcher: &mockPOW.POWChain{},
|
||||
Eth1BlockFetcher: &mockPOW.POWChain{},
|
||||
MockEth1Votes: true,
|
||||
AttPool: attestations.NewPool(),
|
||||
SlashingsPool: slashings.NewPool(),
|
||||
ExitPool: voluntaryexits.NewPool(),
|
||||
StateGen: stategen.New(db),
|
||||
SyncCommitteePool: synccommittee.NewStore(),
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
}
|
||||
|
||||
proposerSlashings := make([]*ethpbalpha.ProposerSlashing, params.BeaconConfig().MaxProposerSlashings)
|
||||
|
||||
@@ -77,8 +77,9 @@ go_library(
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_ferranbt_fastssz//:go_default_library",
|
||||
"@com_github_holiman_uint256//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
@@ -160,7 +161,6 @@ go_test(
|
||||
"@com_github_d4l3k_messagediff//:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_golang_mock//gomock:go_default_library",
|
||||
"@com_github_holiman_uint256//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
|
||||
@@ -138,7 +138,7 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
|
||||
return nil, status.Errorf(codes.Internal, "Could not compute committee assignments: %v", err)
|
||||
}
|
||||
// Query the next epoch assignments for committee subnet subscriptions.
|
||||
nextCommitteeAssignments, _, err := helpers.CommitteeAssignments(ctx, s, req.Epoch+1)
|
||||
nextCommitteeAssignments, nextProposerIndexToSlots, err := helpers.CommitteeAssignments(ctx, s, req.Epoch+1)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not compute next committee assignments: %v", err)
|
||||
}
|
||||
@@ -180,6 +180,16 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
|
||||
nextAssignment.AttesterSlot = ca.AttesterSlot
|
||||
nextAssignment.CommitteeIndex = ca.CommitteeIndex
|
||||
}
|
||||
// Cache proposer assignment for the current epoch.
|
||||
for _, slot := range proposerIndexToSlots[idx] {
|
||||
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, idx, [8]byte{} /* payloadID */)
|
||||
}
|
||||
// Cache proposer assignment for the next epoch.
|
||||
for _, slot := range nextProposerIndexToSlots[idx] {
|
||||
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, idx, [8]byte{} /* payloadID */)
|
||||
}
|
||||
// Prune payload ID cache for any slots before request slot.
|
||||
vs.ProposerSlotIndexCache.PrunePayloadIDs(epochStartSlot)
|
||||
} else {
|
||||
// If the validator isn't in the beacon state, try finding their deposit to determine their status.
|
||||
vStatus, _ := vs.validatorStatus(ctx, s, pubKey)
|
||||
|
||||
@@ -60,9 +60,10 @@ func TestGetDuties_OK(t *testing.T) {
|
||||
State: bs, Root: genesisRoot[:], Genesis: time.Now(),
|
||||
}
|
||||
vs := &Server{
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
}
|
||||
|
||||
// Test the first validator in registry.
|
||||
@@ -144,10 +145,11 @@ func TestGetAltairDuties_SyncCommitteeOK(t *testing.T) {
|
||||
State: bs, Root: genesisRoot[:], Genesis: time.Now().Add(time.Duration(-1*int64(slot-1)) * time.Second),
|
||||
}
|
||||
vs := &Server{
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
Eth1InfoFetcher: &mockPOW.POWChain{},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
Eth1InfoFetcher: &mockPOW.POWChain{},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
}
|
||||
|
||||
// Test the first validator in registry.
|
||||
@@ -181,12 +183,12 @@ func TestGetAltairDuties_SyncCommitteeOK(t *testing.T) {
|
||||
res, err = vs.GetDuties(context.Background(), req)
|
||||
require.NoError(t, err, "Could not call epoch committee assignment")
|
||||
for i := 0; i < len(res.CurrentEpochDuties); i++ {
|
||||
assert.Equal(t, types.ValidatorIndex(i), res.CurrentEpochDuties[i].ValidatorIndex)
|
||||
require.Equal(t, types.ValidatorIndex(i), res.CurrentEpochDuties[i].ValidatorIndex)
|
||||
}
|
||||
for i := 0; i < len(res.CurrentEpochDuties); i++ {
|
||||
assert.Equal(t, true, res.CurrentEpochDuties[i].IsSyncCommittee)
|
||||
require.Equal(t, true, res.CurrentEpochDuties[i].IsSyncCommittee)
|
||||
// Current epoch and next epoch duties should be equal before the sync period epoch boundary.
|
||||
assert.Equal(t, res.CurrentEpochDuties[i].IsSyncCommittee, res.NextEpochDuties[i].IsSyncCommittee)
|
||||
require.Equal(t, res.CurrentEpochDuties[i].IsSyncCommittee, res.NextEpochDuties[i].IsSyncCommittee)
|
||||
}
|
||||
|
||||
// Current epoch and next epoch duties should not be equal at the sync period epoch boundary.
|
||||
@@ -197,7 +199,7 @@ func TestGetAltairDuties_SyncCommitteeOK(t *testing.T) {
|
||||
res, err = vs.GetDuties(context.Background(), req)
|
||||
require.NoError(t, err, "Could not call epoch committee assignment")
|
||||
for i := 0; i < len(res.CurrentEpochDuties); i++ {
|
||||
assert.NotEqual(t, res.CurrentEpochDuties[i].IsSyncCommittee, res.NextEpochDuties[i].IsSyncCommittee)
|
||||
require.NotEqual(t, res.CurrentEpochDuties[i].IsSyncCommittee, res.NextEpochDuties[i].IsSyncCommittee)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,10 +251,11 @@ func TestGetBellatrixDuties_SyncCommitteeOK(t *testing.T) {
|
||||
State: bs, Root: genesisRoot[:], Genesis: time.Now().Add(time.Duration(-1*int64(slot-1)) * time.Second),
|
||||
}
|
||||
vs := &Server{
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
Eth1InfoFetcher: &mockPOW.POWChain{},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
Eth1InfoFetcher: &mockPOW.POWChain{},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
}
|
||||
|
||||
// Test the first validator in registry.
|
||||
@@ -302,7 +305,7 @@ func TestGetBellatrixDuties_SyncCommitteeOK(t *testing.T) {
|
||||
res, err = vs.GetDuties(context.Background(), req)
|
||||
require.NoError(t, err, "Could not call epoch committee assignment")
|
||||
for i := 0; i < len(res.CurrentEpochDuties); i++ {
|
||||
assert.NotEqual(t, res.CurrentEpochDuties[i].IsSyncCommittee, res.NextEpochDuties[i].IsSyncCommittee)
|
||||
require.NotEqual(t, res.CurrentEpochDuties[i].IsSyncCommittee, res.NextEpochDuties[i].IsSyncCommittee)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,11 +343,12 @@ func TestGetAltairDuties_UnknownPubkey(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
vs := &Server{
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
Eth1InfoFetcher: &mockPOW.POWChain{},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
DepositFetcher: depositCache,
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
Eth1InfoFetcher: &mockPOW.POWChain{},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
DepositFetcher: depositCache,
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
}
|
||||
|
||||
unknownPubkey := bytesutil.PadTo([]byte{'u'}, 48)
|
||||
@@ -399,9 +403,10 @@ func TestGetDuties_CurrentEpoch_ShouldNotFail(t *testing.T) {
|
||||
State: bState, Root: genesisRoot[:], Genesis: time.Now(),
|
||||
}
|
||||
vs := &Server{
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
}
|
||||
|
||||
// Test the first validator in registry.
|
||||
@@ -437,9 +442,10 @@ func TestGetDuties_MultipleKeys_OK(t *testing.T) {
|
||||
State: bs, Root: genesisRoot[:], Genesis: time.Now(),
|
||||
}
|
||||
vs := &Server{
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
}
|
||||
|
||||
pubkey0 := deposits[0].Data.PublicKey
|
||||
@@ -503,11 +509,12 @@ func TestStreamDuties_OK(t *testing.T) {
|
||||
Genesis: time.Now(),
|
||||
}
|
||||
vs := &Server{
|
||||
Ctx: ctx,
|
||||
HeadFetcher: &mockChain.ChainService{State: bs, Root: genesisRoot[:]},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
TimeFetcher: c,
|
||||
StateNotifier: &mockChain.MockStateNotifier{},
|
||||
Ctx: ctx,
|
||||
HeadFetcher: &mockChain.ChainService{State: bs, Root: genesisRoot[:]},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
TimeFetcher: c,
|
||||
StateNotifier: &mockChain.MockStateNotifier{},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
}
|
||||
|
||||
// Test the first validator in registry.
|
||||
@@ -560,11 +567,12 @@ func TestStreamDuties_OK_ChainReorg(t *testing.T) {
|
||||
Genesis: time.Now(),
|
||||
}
|
||||
vs := &Server{
|
||||
Ctx: ctx,
|
||||
HeadFetcher: &mockChain.ChainService{State: bs, Root: genesisRoot[:]},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
TimeFetcher: c,
|
||||
StateNotifier: &mockChain.MockStateNotifier{},
|
||||
Ctx: ctx,
|
||||
HeadFetcher: &mockChain.ChainService{State: bs, Root: genesisRoot[:]},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
TimeFetcher: c,
|
||||
StateNotifier: &mockChain.MockStateNotifier{},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
}
|
||||
|
||||
// Test the first validator in registry.
|
||||
|
||||
@@ -37,17 +37,24 @@ func (vs *Server) StreamBlocksAltair(req *ethpb.StreamBlocksRequest, stream ethp
|
||||
case version.Phase0:
|
||||
phBlk, ok := data.SignedBlock.Proto().(*ethpb.SignedBeaconBlock)
|
||||
if !ok {
|
||||
log.Warn("Mismatch between version and block type, was expecting *ethpb.SignedBeaconBlock")
|
||||
log.Warn("Mismatch between version and block type, was expecting SignedBeaconBlock")
|
||||
continue
|
||||
}
|
||||
b.Block = ðpb.StreamBlocksResponse_Phase0Block{Phase0Block: phBlk}
|
||||
case version.Altair:
|
||||
phBlk, ok := data.SignedBlock.Proto().(*ethpb.SignedBeaconBlockAltair)
|
||||
if !ok {
|
||||
log.Warn("Mismatch between version and block type, was expecting *v2.SignedBeaconBlockAltair")
|
||||
log.Warn("Mismatch between version and block type, was expecting SignedBeaconBlockAltair")
|
||||
continue
|
||||
}
|
||||
b.Block = ðpb.StreamBlocksResponse_AltairBlock{AltairBlock: phBlk}
|
||||
case version.Bellatrix:
|
||||
phBlk, ok := data.SignedBlock.Proto().(*ethpb.SignedBeaconBlockBellatrix)
|
||||
if !ok {
|
||||
log.Warn("Mismatch between version and block type, was expecting SignedBeaconBlockBellatrix")
|
||||
continue
|
||||
}
|
||||
b.Block = ðpb.StreamBlocksResponse_BellatrixBlock{BellatrixBlock: phBlk}
|
||||
}
|
||||
|
||||
if err := stream.Send(b); err != nil {
|
||||
|
||||
@@ -161,7 +161,7 @@ func (vs *Server) depositTrie(ctx context.Context, canonicalEth1Data *ethpb.Eth1
|
||||
|
||||
finalizedDeposits := vs.DepositFetcher.FinalizedDeposits(ctx)
|
||||
depositTrie = finalizedDeposits.Deposits
|
||||
upToEth1DataDeposits := vs.DepositFetcher.NonFinalizedDeposits(ctx, canonicalEth1DataHeight)
|
||||
upToEth1DataDeposits := vs.DepositFetcher.NonFinalizedDeposits(ctx, finalizedDeposits.MerkleTrieIndex, canonicalEth1DataHeight)
|
||||
insertIndex := finalizedDeposits.MerkleTrieIndex + 1
|
||||
|
||||
for _, dep := range upToEth1DataDeposits {
|
||||
|
||||
@@ -3,12 +3,10 @@ package validator
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
@@ -24,9 +22,31 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
// payloadIDCacheMiss tracks the number of payload ID requests that aren't present in the cache.
|
||||
payloadIDCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "payload_id_cache_miss",
|
||||
Help: "The number of payload id get requests that aren't present in the cache.",
|
||||
})
|
||||
// payloadIDCacheHit tracks the number of payload ID requests that are present in the cache.
|
||||
payloadIDCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "payload_id_cache_hit",
|
||||
Help: "The number of payload id get requests that are present in the cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// This returns the execution payload of a given slot. The function has full awareness of pre and post merge.
|
||||
// The payload is computed given the respected time of merge.
|
||||
func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx types.ValidatorIndex) (*enginev1.ExecutionPayload, error) {
|
||||
proposerID, payloadId, ok := vs.ProposerSlotIndexCache.GetProposerPayloadIDs(slot)
|
||||
if ok && proposerID == vIdx && payloadId != [8]byte{} { // Payload ID is cache hit. Return the cached payload ID.
|
||||
var pid [8]byte
|
||||
copy(pid[:], payloadId[:])
|
||||
payloadIDCacheHit.Inc()
|
||||
return vs.ExecutionEngineCaller.GetPayload(ctx, pid)
|
||||
}
|
||||
payloadIDCacheMiss.Inc()
|
||||
|
||||
st, err := vs.HeadFetcher.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -157,79 +177,7 @@ func (vs *Server) getTerminalBlockHashIfExists(ctx context.Context) ([]byte, boo
|
||||
return terminalBlockHash.Bytes(), true, nil
|
||||
}
|
||||
|
||||
return vs.getPowBlockHashAtTerminalTotalDifficulty(ctx)
|
||||
}
|
||||
|
||||
// This returns the valid terminal block hash based on total difficulty.
|
||||
//
|
||||
// Spec code:
|
||||
// def get_pow_block_at_terminal_total_difficulty(pow_chain: Dict[Hash32, PowBlock]) -> Optional[PowBlock]:
|
||||
// # `pow_chain` abstractly represents all blocks in the PoW chain
|
||||
// for block in pow_chain:
|
||||
// parent = pow_chain[block.parent_hash]
|
||||
// block_reached_ttd = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||
// parent_reached_ttd = parent.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||
// if block_reached_ttd and not parent_reached_ttd:
|
||||
// return block
|
||||
//
|
||||
// return None
|
||||
func (vs *Server) getPowBlockHashAtTerminalTotalDifficulty(ctx context.Context) ([]byte, bool, error) {
|
||||
ttd := new(big.Int)
|
||||
ttd.SetString(params.BeaconConfig().TerminalTotalDifficulty, 10)
|
||||
terminalTotalDifficulty, overflows := uint256.FromBig(ttd)
|
||||
if overflows {
|
||||
return nil, false, errors.New("could not convert terminal total difficulty to uint256")
|
||||
}
|
||||
blk, err := vs.ExecutionEngineCaller.LatestExecutionBlock(ctx)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "could not get latest execution block")
|
||||
}
|
||||
if blk == nil {
|
||||
return nil, false, errors.New("latest execution block is nil")
|
||||
}
|
||||
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return nil, false, ctx.Err()
|
||||
}
|
||||
currentTotalDifficulty, err := tDStringToUint256(blk.TotalDifficulty)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "could not convert total difficulty to uint256")
|
||||
}
|
||||
blockReachedTTD := currentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
|
||||
|
||||
parentHash := bytesutil.ToBytes32(blk.ParentHash)
|
||||
if len(blk.ParentHash) == 0 || parentHash == params.BeaconConfig().ZeroHash {
|
||||
return nil, false, nil
|
||||
}
|
||||
parentBlk, err := vs.ExecutionEngineCaller.ExecutionBlockByHash(ctx, parentHash)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "could not get parent execution block")
|
||||
}
|
||||
if parentBlk == nil {
|
||||
return nil, false, errors.New("parent execution block is nil")
|
||||
}
|
||||
if blockReachedTTD {
|
||||
parentTotalDifficulty, err := tDStringToUint256(parentBlk.TotalDifficulty)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "could not convert total difficulty to uint256")
|
||||
}
|
||||
parentReachedTTD := parentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
|
||||
if !parentReachedTTD {
|
||||
log.WithFields(logrus.Fields{
|
||||
"number": blk.Number,
|
||||
"hash": fmt.Sprintf("%#x", bytesutil.Trunc(blk.Hash)),
|
||||
"td": blk.TotalDifficulty,
|
||||
"parentTd": parentBlk.TotalDifficulty,
|
||||
"ttd": terminalTotalDifficulty,
|
||||
}).Info("Retrieved terminal block hash")
|
||||
return blk.Hash, true, nil
|
||||
}
|
||||
} else {
|
||||
return nil, false, nil
|
||||
}
|
||||
blk = parentBlk
|
||||
}
|
||||
return vs.ExecutionEngineCaller.GetTerminalBlockHash(ctx)
|
||||
}
|
||||
|
||||
// activationEpochNotReached returns true if activation epoch has not been reach.
|
||||
@@ -246,18 +194,6 @@ func activationEpochNotReached(slot types.Slot) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func tDStringToUint256(td string) (*uint256.Int, error) {
|
||||
b, err := hexutil.DecodeBig(td)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i, overflows := uint256.FromBig(b)
|
||||
if overflows {
|
||||
return nil, errors.New("total difficulty overflowed")
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func emptyPayload() *enginev1.ExecutionPayload {
|
||||
return &enginev1.ExecutionPayload{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/holiman/uint256"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
chainMock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
powtesting "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
@@ -21,26 +21,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/testing/util"
|
||||
)
|
||||
|
||||
func Test_tDStringToUint256(t *testing.T) {
|
||||
i, err := tDStringToUint256("0x0")
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, uint256.NewInt(0), i)
|
||||
|
||||
i, err = tDStringToUint256("0x10000")
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, uint256.NewInt(65536), i)
|
||||
|
||||
_, err = tDStringToUint256("100")
|
||||
require.ErrorContains(t, "hex string without 0x prefix", err)
|
||||
|
||||
_, err = tDStringToUint256("0xzzzzzz")
|
||||
require.ErrorContains(t, "invalid hex string", err)
|
||||
|
||||
_, err = tDStringToUint256("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
|
||||
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
|
||||
require.ErrorContains(t, "hex number > 256 bits", err)
|
||||
}
|
||||
|
||||
func TestServer_activationEpochNotReached(t *testing.T) {
|
||||
require.Equal(t, false, activationEpochNotReached(0))
|
||||
|
||||
@@ -106,6 +86,11 @@ func TestServer_getExecutionPayload(t *testing.T) {
|
||||
payloadID: &pb.PayloadIDBytes{0x1},
|
||||
validatorIndx: 1,
|
||||
},
|
||||
{
|
||||
name: "transition completed, happy case, payload ID cached)",
|
||||
st: transitionSt,
|
||||
validatorIndx: 100,
|
||||
},
|
||||
{
|
||||
name: "transition completed, could not prepare payload",
|
||||
st: transitionSt,
|
||||
@@ -132,10 +117,12 @@ func TestServer_getExecutionPayload(t *testing.T) {
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
vs := &Server{
|
||||
ExecutionEngineCaller: &powtesting.EngineClient{PayloadIDBytes: tt.payloadID, ErrForkchoiceUpdated: tt.forkchoiceErr},
|
||||
HeadFetcher: &chainMock.ChainService{State: tt.st},
|
||||
BeaconDB: beaconDB,
|
||||
ExecutionEngineCaller: &powtesting.EngineClient{PayloadIDBytes: tt.payloadID, ErrForkchoiceUpdated: tt.forkchoiceErr},
|
||||
HeadFetcher: &chainMock.ChainService{State: tt.st},
|
||||
BeaconDB: beaconDB,
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
}
|
||||
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(tt.st.Slot(), 100, [8]byte{100})
|
||||
_, err := vs.getExecutionPayload(context.Background(), tt.st.Slot(), tt.validatorIndx)
|
||||
if tt.errString != "" {
|
||||
require.ErrorContains(t, tt.errString, err)
|
||||
@@ -146,137 +133,6 @@ func TestServer_getExecutionPayload(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_getPowBlockHashAtTerminalTotalDifficulty(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
paramsTd string
|
||||
currentPowBlock *pb.ExecutionBlock
|
||||
parentPowBlock *pb.ExecutionBlock
|
||||
errLatestExecutionBlk error
|
||||
wantTerminalBlockHash []byte
|
||||
wantExists bool
|
||||
errString string
|
||||
}{
|
||||
{
|
||||
name: "config td overflows",
|
||||
paramsTd: "1115792089237316195423570985008687907853269984665640564039457584007913129638912",
|
||||
errString: "could not convert terminal total difficulty to uint256",
|
||||
},
|
||||
{
|
||||
name: "could not get latest execution block",
|
||||
paramsTd: "1",
|
||||
errLatestExecutionBlk: errors.New("blah"),
|
||||
errString: "could not get latest execution block",
|
||||
},
|
||||
{
|
||||
name: "nil latest execution block",
|
||||
paramsTd: "1",
|
||||
errString: "latest execution block is nil",
|
||||
},
|
||||
{
|
||||
name: "current execution block invalid TD",
|
||||
paramsTd: "1",
|
||||
currentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'a'},
|
||||
TotalDifficulty: "1115792089237316195423570985008687907853269984665640564039457584007913129638912",
|
||||
},
|
||||
errString: "could not convert total difficulty to uint256",
|
||||
},
|
||||
{
|
||||
name: "current execution block has zero hash parent",
|
||||
paramsTd: "2",
|
||||
currentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'a'},
|
||||
ParentHash: params.BeaconConfig().ZeroHash[:],
|
||||
TotalDifficulty: "0x3",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "could not get parent block",
|
||||
paramsTd: "2",
|
||||
currentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'a'},
|
||||
ParentHash: []byte{'b'},
|
||||
TotalDifficulty: "0x3",
|
||||
},
|
||||
errString: "could not get parent execution block",
|
||||
},
|
||||
{
|
||||
name: "parent execution block invalid TD",
|
||||
paramsTd: "2",
|
||||
currentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'a'},
|
||||
ParentHash: []byte{'b'},
|
||||
TotalDifficulty: "0x3",
|
||||
},
|
||||
parentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'b'},
|
||||
ParentHash: []byte{'c'},
|
||||
TotalDifficulty: "1",
|
||||
},
|
||||
errString: "could not convert total difficulty to uint256",
|
||||
},
|
||||
{
|
||||
name: "happy case",
|
||||
paramsTd: "2",
|
||||
currentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'a'},
|
||||
ParentHash: []byte{'b'},
|
||||
TotalDifficulty: "0x3",
|
||||
},
|
||||
parentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'b'},
|
||||
ParentHash: []byte{'c'},
|
||||
TotalDifficulty: "0x1",
|
||||
},
|
||||
wantExists: true,
|
||||
wantTerminalBlockHash: []byte{'a'},
|
||||
},
|
||||
{
|
||||
name: "ttd not reached",
|
||||
paramsTd: "3",
|
||||
currentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'a'},
|
||||
ParentHash: []byte{'b'},
|
||||
TotalDifficulty: "0x2",
|
||||
},
|
||||
parentPowBlock: &pb.ExecutionBlock{
|
||||
Hash: []byte{'b'},
|
||||
ParentHash: []byte{'c'},
|
||||
TotalDifficulty: "0x1",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := params.BeaconConfig()
|
||||
cfg.TerminalTotalDifficulty = tt.paramsTd
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
var m map[[32]byte]*pb.ExecutionBlock
|
||||
if tt.parentPowBlock != nil {
|
||||
m = map[[32]byte]*pb.ExecutionBlock{
|
||||
bytesutil.ToBytes32(tt.parentPowBlock.Hash): tt.parentPowBlock,
|
||||
}
|
||||
}
|
||||
vs := &Server{
|
||||
ExecutionEngineCaller: &powtesting.EngineClient{
|
||||
ErrLatestExecBlock: tt.errLatestExecutionBlk,
|
||||
ExecutionBlock: tt.currentPowBlock,
|
||||
BlockByHashMap: m,
|
||||
},
|
||||
}
|
||||
b, e, err := vs.getPowBlockHashAtTerminalTotalDifficulty(context.Background())
|
||||
if tt.errString != "" {
|
||||
require.ErrorContains(t, tt.errString, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, tt.wantExists, e)
|
||||
require.DeepEqual(t, tt.wantTerminalBlockHash, b)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_getTerminalBlockHashIfExists(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
@@ -2344,7 +2345,8 @@ func TestProposer_GetBeaconBlock_BellatrixEpoch(t *testing.T) {
|
||||
PayloadIDBytes: &enginev1.PayloadIDBytes{1},
|
||||
ExecutionPayload: payload,
|
||||
},
|
||||
BeaconDB: db,
|
||||
BeaconDB: db,
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
}
|
||||
|
||||
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
|
||||
|
||||
@@ -41,6 +41,7 @@ import (
|
||||
type Server struct {
|
||||
Ctx context.Context
|
||||
AttestationCache *cache.AttestationCache
|
||||
ProposerSlotIndexCache *cache.ProposerPayloadIDsCache
|
||||
HeadFetcher blockchain.HeadFetcher
|
||||
ForkFetcher blockchain.ForkFetcher
|
||||
FinalizationFetcher blockchain.FinalizationFetcher
|
||||
|
||||
@@ -109,6 +109,7 @@ type Config struct {
|
||||
StateGen *stategen.State
|
||||
MaxMsgSize int
|
||||
ExecutionEngineCaller powchain.EngineCaller
|
||||
ProposerIdsCache *cache.ProposerPayloadIDsCache
|
||||
}
|
||||
|
||||
// NewService instantiates a new RPC service instance that will
|
||||
@@ -207,6 +208,7 @@ func (s *Service) Start() {
|
||||
ReplayerBuilder: ch,
|
||||
ExecutionEngineCaller: s.cfg.ExecutionEngineCaller,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
ProposerSlotIndexCache: s.cfg.ProposerIdsCache,
|
||||
}
|
||||
validatorServerV1 := &validator.Server{
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
|
||||
@@ -16,12 +16,19 @@ import (
|
||||
type BeaconState interface {
|
||||
ReadOnlyBeaconState
|
||||
WriteOnlyBeaconState
|
||||
SpecConstantsProvider
|
||||
Copy() BeaconState
|
||||
HashTreeRoot(ctx context.Context) ([32]byte, error)
|
||||
FutureForkStub
|
||||
StateProver
|
||||
}
|
||||
|
||||
// SpecConstantsProvider defines a struct which can provide varying configuration
|
||||
// values depending on fork versions, such as the beacon state.
|
||||
type SpecConstantsProvider interface {
|
||||
InactivityPenaltyQuotient() (uint64, error)
|
||||
}
|
||||
|
||||
// StateProver defines the ability to create Merkle proofs for beacon state fields.
|
||||
type StateProver interface {
|
||||
FinalizedRootProof(ctx context.Context) ([][]byte, error)
|
||||
|
||||
7
beacon-chain/state/state-native/v1/spec_constants.go
Normal file
7
beacon-chain/state/state-native/v1/spec_constants.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package v1
|
||||
|
||||
import "github.com/prysmaticlabs/prysm/config/params"
|
||||
|
||||
func (b *BeaconState) InactivityPenaltyQuotient() uint64 {
|
||||
return params.BeaconConfig().InactivityPenaltyQuotient
|
||||
}
|
||||
7
beacon-chain/state/state-native/v2/spec_constants.go
Normal file
7
beacon-chain/state/state-native/v2/spec_constants.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package v2
|
||||
|
||||
import "github.com/prysmaticlabs/prysm/config/params"
|
||||
|
||||
func (b *BeaconState) InactivityPenaltyQuotient() uint64 {
|
||||
return params.BeaconConfig().InactivityPenaltyQuotientAltair
|
||||
}
|
||||
7
beacon-chain/state/state-native/v3/spec_constants.go
Normal file
7
beacon-chain/state/state-native/v3/spec_constants.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package v3
|
||||
|
||||
import "github.com/prysmaticlabs/prysm/config/params"
|
||||
|
||||
func (b *BeaconState) InactivityPenaltyQuotient() uint64 {
|
||||
return params.BeaconConfig().InactivityPenaltyQuotientBellatrix
|
||||
}
|
||||
@@ -151,6 +151,15 @@ func (e *epochBoundaryState) put(r [32]byte, s state.BeaconState) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// delete the state from the epoch boundary state cache.
|
||||
func (e *epochBoundaryState) delete(r [32]byte) error {
|
||||
e.lock.Lock()
|
||||
defer e.lock.Unlock()
|
||||
return e.rootStateCache.Delete(&rootStateInfo{
|
||||
root: r,
|
||||
})
|
||||
}
|
||||
|
||||
// trim the FIFO queue to the maxSize.
|
||||
func trim(queue *cache.FIFO, maxSize uint64) {
|
||||
for s := uint64(len(queue.ListKeys())); s > maxSize; s-- {
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestEpochBoundaryStateCache_BadRootKey(t *testing.T) {
|
||||
assert.ErrorContains(t, errNotRootStateInfo.Error(), err, "Did not get wanted error")
|
||||
}
|
||||
|
||||
func TestEpochBoundaryStateCache_CanSave(t *testing.T) {
|
||||
func TestEpochBoundaryStateCache_CanSaveAndDelete(t *testing.T) {
|
||||
e := newBoundaryStateCache()
|
||||
s, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
@@ -46,6 +46,17 @@ func TestEpochBoundaryStateCache_CanSave(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, exists, "Should exist")
|
||||
assert.DeepSSZEqual(t, s.InnerStateUnsafe(), got.state.InnerStateUnsafe(), "Should have the same state")
|
||||
|
||||
require.NoError(t, e.delete(r))
|
||||
got, exists, err = e.getByRoot([32]byte{'b'})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, exists, "Should not exist")
|
||||
assert.Equal(t, (*rootStateInfo)(nil), got, "Should not exist")
|
||||
|
||||
got, exists, err = e.getBySlot(1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, exists, "Should not exist")
|
||||
assert.Equal(t, (*rootStateInfo)(nil), got, "Should not exist")
|
||||
}
|
||||
|
||||
func TestEpochBoundaryStateCache_CanTrim(t *testing.T) {
|
||||
|
||||
@@ -39,7 +39,7 @@ func (s *State) HasStateInCache(ctx context.Context, blockRoot [32]byte) (bool,
|
||||
return has, nil
|
||||
}
|
||||
|
||||
// StateByRootIfCached retrieves a state using the input block root only if the state is already in the cache
|
||||
// StateByRootIfCachedNoCopy retrieves a state using the input block root only if the state is already in the cache
|
||||
func (s *State) StateByRootIfCachedNoCopy(blockRoot [32]byte) state.BeaconState {
|
||||
if !s.hotStateCache.has(blockRoot) {
|
||||
return nil
|
||||
@@ -146,6 +146,12 @@ func (s *State) RecoverStateSummary(ctx context.Context, blockRoot [32]byte) (*e
|
||||
return nil, errors.New("could not find block in DB")
|
||||
}
|
||||
|
||||
// DeleteStateFromCaches deletes the state from the caches.
|
||||
func (s *State) DeleteStateFromCaches(_ context.Context, blockRoot [32]byte) error {
|
||||
s.hotStateCache.delete(blockRoot)
|
||||
return s.epochBoundaryStateCache.delete(blockRoot)
|
||||
}
|
||||
|
||||
// This loads a beacon state from either the cache or DB then replay blocks up the requested block root.
|
||||
func (s *State) loadStateByRoot(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "stateGen.loadStateByRoot")
|
||||
|
||||
@@ -165,6 +165,35 @@ func TestStateByRoot_HotStateCached(t *testing.T) {
|
||||
require.DeepSSZEqual(t, loadedState.InnerStateUnsafe(), beaconState.InnerStateUnsafe())
|
||||
}
|
||||
|
||||
func TestDeleteStateFromCaches(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
service := New(beaconDB)
|
||||
beaconState, _ := util.DeterministicGenesisState(t, 32)
|
||||
r := [32]byte{'A'}
|
||||
|
||||
require.Equal(t, false, service.hotStateCache.has(r))
|
||||
_, has, err := service.epochBoundaryStateCache.getByRoot(r)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, has)
|
||||
|
||||
service.hotStateCache.put(r, beaconState)
|
||||
require.NoError(t, service.epochBoundaryStateCache.put(r, beaconState))
|
||||
|
||||
require.Equal(t, true, service.hotStateCache.has(r))
|
||||
_, has, err = service.epochBoundaryStateCache.getByRoot(r)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, has)
|
||||
|
||||
require.NoError(t, service.DeleteStateFromCaches(ctx, r))
|
||||
|
||||
require.Equal(t, false, service.hotStateCache.has(r))
|
||||
_, has, err = service.epochBoundaryStateCache.getByRoot(r)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, has)
|
||||
}
|
||||
|
||||
func TestStateByRoot_StateByRootInitialSync(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
@@ -23,7 +23,7 @@ func NewMockService() *MockStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
// StateByRootIfCached
|
||||
// StateByRootIfCachedNoCopy
|
||||
func (_ *MockStateManager) StateByRootIfCachedNoCopy(_ [32]byte) state.BeaconState {
|
||||
panic("implement me")
|
||||
}
|
||||
@@ -124,3 +124,8 @@ func (m *MockStateManager) AddStateForRoot(state state.BeaconState, blockRoot [3
|
||||
func (m *MockStateManager) AddStateForSlot(state state.BeaconState, slot types.Slot) {
|
||||
m.StatesBySlot[slot] = state
|
||||
}
|
||||
|
||||
// DeleteStateFromCaches --
|
||||
func (m *MockStateManager) DeleteStateFromCaches(context.Context, [32]byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ type StateManager interface {
|
||||
ForceCheckpoint(ctx context.Context, root []byte) error
|
||||
EnableSaveHotStateToDB(_ context.Context)
|
||||
DisableSaveHotStateToDB(ctx context.Context) error
|
||||
DeleteStateFromCaches(ctx context.Context, blockRoot [32]byte) error
|
||||
}
|
||||
|
||||
// State is a concrete implementation of StateManager.
|
||||
|
||||
7
beacon-chain/state/v1/spec_constants.go
Normal file
7
beacon-chain/state/v1/spec_constants.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package v1
|
||||
|
||||
import "github.com/prysmaticlabs/prysm/config/params"
|
||||
|
||||
func (b *BeaconState) InactivityPenaltyQuotient() uint64 {
|
||||
return params.BeaconConfig().InactivityPenaltyQuotient
|
||||
}
|
||||
7
beacon-chain/state/v2/spec_constants.go
Normal file
7
beacon-chain/state/v2/spec_constants.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package v1
|
||||
|
||||
import "github.com/prysmaticlabs/prysm/config/params"
|
||||
|
||||
func (b *BeaconState) InactivityPenaltyQuotient() uint64 {
|
||||
return params.BeaconConfig().InactivityPenaltyQuotientAltair
|
||||
}
|
||||
7
beacon-chain/state/v3/spec_constants.go
Normal file
7
beacon-chain/state/v3/spec_constants.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package v1
|
||||
|
||||
import "github.com/prysmaticlabs/prysm/config/params"
|
||||
|
||||
func (b *BeaconState) InactivityPenaltyQuotient() uint64 {
|
||||
return params.BeaconConfig().InactivityPenaltyQuotientBellatrix
|
||||
}
|
||||
@@ -49,30 +49,29 @@ type Service struct {
|
||||
synced *abool.AtomicBool
|
||||
chainStarted *abool.AtomicBool
|
||||
counter *ratecounter.RateCounter
|
||||
genesisChan chan time.Time
|
||||
}
|
||||
|
||||
// NewService configures the initial sync service responsible for bringing the node up to the
|
||||
// latest head of the blockchain.
|
||||
func NewService(ctx context.Context, cfg *Config) *Service {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
s := &Service{
|
||||
return &Service{
|
||||
cfg: cfg,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
synced: abool.New(),
|
||||
chainStarted: abool.New(),
|
||||
counter: ratecounter.NewRateCounter(counterSeconds * time.Second),
|
||||
genesisChan: make(chan time.Time),
|
||||
}
|
||||
go s.waitForStateInitialization()
|
||||
return s
|
||||
}
|
||||
|
||||
// Start the initial sync service.
|
||||
func (s *Service) Start() {
|
||||
// Wait for state initialized event.
|
||||
genesis := <-s.genesisChan
|
||||
genesis, err := s.waitForStateInitialization()
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to wait for state initialization.")
|
||||
return
|
||||
}
|
||||
if genesis.IsZero() {
|
||||
log.Debug("Exiting Initial Sync Service")
|
||||
return
|
||||
@@ -180,9 +179,10 @@ func (s *Service) waitForMinimumPeers() {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Return error
|
||||
// waitForStateInitialization makes sure that beacon node is ready to be accessed: it is either
|
||||
// already properly configured or system waits up until state initialized event is triggered.
|
||||
func (s *Service) waitForStateInitialization() {
|
||||
func (s *Service) waitForStateInitialization() (time.Time, error) {
|
||||
// Wait for state to be initialized.
|
||||
stateChannel := make(chan *feed.Event, 1)
|
||||
stateSub := s.cfg.StateNotifier.StateFeed().Subscribe(stateChannel)
|
||||
@@ -198,19 +198,14 @@ func (s *Service) waitForStateInitialization() {
|
||||
continue
|
||||
}
|
||||
log.WithField("starttime", data.StartTime).Debug("Received state initialized event")
|
||||
s.genesisChan <- data.StartTime
|
||||
return
|
||||
return data.StartTime, nil
|
||||
}
|
||||
case <-s.ctx.Done():
|
||||
log.Debug("Context closed, exiting goroutine")
|
||||
// Send a zero time in the event we are exiting.
|
||||
s.genesisChan <- time.Time{}
|
||||
return
|
||||
return time.Time{}, errors.New("context closed, exiting goroutine")
|
||||
case err := <-stateSub.Err():
|
||||
log.WithError(err).Error("Subscription to state notifier failed")
|
||||
// Send a zero time in the event we are exiting.
|
||||
s.genesisChan <- time.Time{}
|
||||
return
|
||||
return time.Time{}, errors.Wrap(err, "subscription to state notifier failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,11 +168,7 @@ func TestService_InitStartStop(t *testing.T) {
|
||||
Chain: mc,
|
||||
StateNotifier: notifier,
|
||||
})
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
assert.NotNil(t, s)
|
||||
if tt.methodRuns != nil {
|
||||
tt.methodRuns(notifier.StateFeed())
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
@@ -181,6 +177,11 @@ func TestService_InitStartStop(t *testing.T) {
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if tt.methodRuns != nil {
|
||||
tt.methodRuns(notifier.StateFeed())
|
||||
}
|
||||
|
||||
go func() {
|
||||
// Allow to exit from test (on no head loop waiting for head is started).
|
||||
// In most tests, this is redundant, as Start() already exited.
|
||||
@@ -207,7 +208,6 @@ func TestService_waitForStateInitialization(t *testing.T) {
|
||||
synced: abool.New(),
|
||||
chainStarted: abool.New(),
|
||||
counter: ratecounter.NewRateCounter(counterSeconds * time.Second),
|
||||
genesisChan: make(chan time.Time),
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -221,9 +221,8 @@ func TestService_waitForStateInitialization(t *testing.T) {
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
go s.waitForStateInitialization()
|
||||
currTime := <-s.genesisChan
|
||||
assert.Equal(t, true, currTime.IsZero())
|
||||
_, err := s.waitForStateInitialization()
|
||||
assert.ErrorContains(t, "context closed", err)
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
@@ -236,8 +235,6 @@ func TestService_waitForStateInitialization(t *testing.T) {
|
||||
t.Fatalf("Test should have exited by now, timed out")
|
||||
}
|
||||
assert.LogsContain(t, hook, "Waiting for state to be initialized")
|
||||
assert.LogsContain(t, hook, "Context closed, exiting goroutine")
|
||||
assert.LogsDoNotContain(t, hook, "Subscription to state notifier failed")
|
||||
})
|
||||
|
||||
t.Run("no state and state init event received", func(t *testing.T) {
|
||||
@@ -251,8 +248,9 @@ func TestService_waitForStateInitialization(t *testing.T) {
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
go s.waitForStateInitialization()
|
||||
receivedGenesisTime = <-s.genesisChan
|
||||
var err error
|
||||
receivedGenesisTime, err = s.waitForStateInitialization()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, receivedGenesisTime.IsZero())
|
||||
wg.Done()
|
||||
}()
|
||||
@@ -281,7 +279,6 @@ func TestService_waitForStateInitialization(t *testing.T) {
|
||||
assert.LogsContain(t, hook, "Event feed data is not type *statefeed.InitializedData")
|
||||
assert.LogsContain(t, hook, "Waiting for state to be initialized")
|
||||
assert.LogsContain(t, hook, "Received state initialized event")
|
||||
assert.LogsDoNotContain(t, hook, "Context closed, exiting goroutine")
|
||||
})
|
||||
|
||||
t.Run("no state and state init event received and service start", func(t *testing.T) {
|
||||
@@ -296,7 +293,8 @@ func TestService_waitForStateInitialization(t *testing.T) {
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
s.waitForStateInitialization()
|
||||
_, err := s.waitForStateInitialization()
|
||||
require.NoError(t, err)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
@@ -321,7 +319,6 @@ func TestService_waitForStateInitialization(t *testing.T) {
|
||||
}
|
||||
assert.LogsContain(t, hook, "Waiting for state to be initialized")
|
||||
assert.LogsContain(t, hook, "Received state initialized event")
|
||||
assert.LogsDoNotContain(t, hook, "Context closed, exiting goroutine")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ var (
|
||||
HTTPWeb3ProviderFlag = &cli.StringFlag{
|
||||
Name: "http-web3provider",
|
||||
Usage: "A mainchain web3 provider string http endpoint. Can contain auth header as well in the format --http-web3provider=\"https://goerli.infura.io/v3/xxxx,Basic xxx\" for project secret (base64 encoded) and --http-web3provider=\"https://goerli.infura.io/v3/xxxx,Bearer xxx\" for jwt use",
|
||||
Value: "",
|
||||
Value: "http://localhost:8545",
|
||||
}
|
||||
// ExecutionJWTSecretFlag provides a path to a file containing a hex-encoded string representing a 32 byte secret
|
||||
// used to authenticate with an execution node via HTTP. This is required if using an HTTP connection, otherwise all requests
|
||||
|
||||
@@ -27,7 +27,7 @@ func FlagOptions(c *cli.Context) ([]powchain.Option, error) {
|
||||
powchain.WithEth1HeaderRequestLimit(c.Uint64(flags.Eth1HeaderReqLimit.Name)),
|
||||
}
|
||||
if len(jwtSecret) > 0 {
|
||||
opts = append(opts, powchain.WithJWTSecret(jwtSecret))
|
||||
opts = append(opts, powchain.WithHttpEndpointsAndJWTSecret(endpoints, jwtSecret))
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ var Commands = &cli.Command{
|
||||
flags.WalletPasswordFileFlag,
|
||||
flags.DeletePublicKeysFlag,
|
||||
features.Mainnet,
|
||||
features.PyrmontTestnet,
|
||||
features.PraterTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
@@ -62,7 +61,6 @@ var Commands = &cli.Command{
|
||||
flags.GrpcRetriesFlag,
|
||||
flags.GrpcRetryDelayFlag,
|
||||
features.Mainnet,
|
||||
features.PyrmontTestnet,
|
||||
features.PraterTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
@@ -93,7 +91,6 @@ var Commands = &cli.Command{
|
||||
flags.BackupPublicKeysFlag,
|
||||
flags.BackupPasswordFile,
|
||||
features.Mainnet,
|
||||
features.PyrmontTestnet,
|
||||
features.PraterTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
@@ -121,7 +118,6 @@ var Commands = &cli.Command{
|
||||
flags.AccountPasswordFileFlag,
|
||||
flags.ImportPrivateKeyFileFlag,
|
||||
features.Mainnet,
|
||||
features.PyrmontTestnet,
|
||||
features.PraterTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
@@ -155,7 +151,6 @@ var Commands = &cli.Command{
|
||||
flags.GrpcRetryDelayFlag,
|
||||
flags.ExitAllFlag,
|
||||
features.Mainnet,
|
||||
features.PyrmontTestnet,
|
||||
features.PraterTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
|
||||
@@ -22,7 +22,6 @@ var Commands = &cli.Command{
|
||||
cmd.DataDirFlag,
|
||||
flags.SlashingProtectionExportDirFlag,
|
||||
features.Mainnet,
|
||||
features.PyrmontTestnet,
|
||||
features.PraterTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
@@ -47,7 +46,6 @@ var Commands = &cli.Command{
|
||||
cmd.DataDirFlag,
|
||||
flags.SlashingProtectionJSONFileFlag,
|
||||
features.Mainnet,
|
||||
features.PyrmontTestnet,
|
||||
features.PraterTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
|
||||
@@ -34,7 +34,6 @@ var Commands = &cli.Command{
|
||||
flags.Mnemonic25thWordFileFlag,
|
||||
flags.SkipMnemonic25thWordCheckFlag,
|
||||
features.Mainnet,
|
||||
features.PyrmontTestnet,
|
||||
features.PraterTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
@@ -64,7 +63,6 @@ var Commands = &cli.Command{
|
||||
flags.RemoteSignerKeyPathFlag,
|
||||
flags.RemoteSignerCACertPathFlag,
|
||||
features.Mainnet,
|
||||
features.PyrmontTestnet,
|
||||
features.PraterTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
@@ -93,7 +91,6 @@ var Commands = &cli.Command{
|
||||
flags.Mnemonic25thWordFileFlag,
|
||||
flags.SkipMnemonic25thWordCheckFlag,
|
||||
features.Mainnet,
|
||||
features.PyrmontTestnet,
|
||||
features.PraterTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
|
||||
@@ -35,9 +35,6 @@ const disabledFeatureFlag = "Disabled feature flag"
|
||||
|
||||
// Flags is a struct to represent which features the client will perform on runtime.
|
||||
type Flags struct {
|
||||
// Testnet Flags.
|
||||
PyrmontTestnet bool // PyrmontTestnet defines the flag through which we can enable the node to run on the Pyrmont testnet.
|
||||
|
||||
// Feature related flags.
|
||||
RemoteSlasherProtection bool // RemoteSlasherProtection utilizes a beacon node with --slasher mode for validator slashing protection.
|
||||
WriteSSZStateTransitions bool // WriteSSZStateTransitions to tmp directory.
|
||||
@@ -121,13 +118,8 @@ func InitWithReset(c *Flags) func() {
|
||||
}
|
||||
|
||||
// configureTestnet sets the config according to specified testnet flag
|
||||
func configureTestnet(ctx *cli.Context, cfg *Flags) {
|
||||
if ctx.Bool(PyrmontTestnet.Name) {
|
||||
log.Warn("Running on Pyrmont Testnet")
|
||||
params.UsePyrmontConfig()
|
||||
params.UsePyrmontNetworkConfig()
|
||||
cfg.PyrmontTestnet = true
|
||||
} else if ctx.Bool(PraterTestnet.Name) {
|
||||
func configureTestnet(ctx *cli.Context) {
|
||||
if ctx.Bool(PraterTestnet.Name) {
|
||||
log.Warn("Running on the Prater Testnet")
|
||||
params.UsePraterConfig()
|
||||
params.UsePraterNetworkConfig()
|
||||
@@ -145,7 +137,7 @@ func ConfigureBeaconChain(ctx *cli.Context) {
|
||||
if ctx.Bool(devModeFlag.Name) {
|
||||
enableDevModeFlags(ctx)
|
||||
}
|
||||
configureTestnet(ctx, cfg)
|
||||
configureTestnet(ctx)
|
||||
|
||||
if ctx.Bool(writeSSZStateTransitionsFlag.Name) {
|
||||
logEnabled(writeSSZStateTransitionsFlag)
|
||||
@@ -240,7 +232,7 @@ func ConfigureBeaconChain(ctx *cli.Context) {
|
||||
func ConfigureValidator(ctx *cli.Context) {
|
||||
complainOnDeprecatedFlags(ctx)
|
||||
cfg := &Flags{}
|
||||
configureTestnet(ctx, cfg)
|
||||
configureTestnet(ctx)
|
||||
if ctx.Bool(enableExternalSlasherProtectionFlag.Name) {
|
||||
log.Fatal(
|
||||
"Remote slashing protection has currently been disabled in Prysm due to safety concerns. " +
|
||||
|
||||
@@ -11,41 +11,41 @@ import (
|
||||
func TestInitFeatureConfig(t *testing.T) {
|
||||
defer Init(&Flags{})
|
||||
cfg := &Flags{
|
||||
PyrmontTestnet: true,
|
||||
EnablePeerScorer: true,
|
||||
}
|
||||
Init(cfg)
|
||||
c := Get()
|
||||
assert.Equal(t, true, c.PyrmontTestnet)
|
||||
assert.Equal(t, true, c.EnablePeerScorer)
|
||||
|
||||
// Reset back to false for the follow up tests.
|
||||
cfg = &Flags{PyrmontTestnet: false}
|
||||
cfg = &Flags{RemoteSlasherProtection: false}
|
||||
Init(cfg)
|
||||
}
|
||||
|
||||
func TestInitWithReset(t *testing.T) {
|
||||
defer Init(&Flags{})
|
||||
Init(&Flags{
|
||||
PyrmontTestnet: true,
|
||||
EnablePeerScorer: true,
|
||||
})
|
||||
assert.Equal(t, true, Get().PyrmontTestnet)
|
||||
assert.Equal(t, true, Get().EnablePeerScorer)
|
||||
|
||||
// Overwrite previously set value (value that didn't come by default).
|
||||
resetCfg := InitWithReset(&Flags{
|
||||
PyrmontTestnet: false,
|
||||
EnablePeerScorer: false,
|
||||
})
|
||||
assert.Equal(t, false, Get().PyrmontTestnet)
|
||||
assert.Equal(t, false, Get().EnablePeerScorer)
|
||||
|
||||
// Reset must get to previously set configuration (not to default config values).
|
||||
resetCfg()
|
||||
assert.Equal(t, true, Get().PyrmontTestnet)
|
||||
assert.Equal(t, true, Get().EnablePeerScorer)
|
||||
}
|
||||
|
||||
func TestConfigureBeaconConfig(t *testing.T) {
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool(PyrmontTestnet.Name, true, "test")
|
||||
set.Bool(enablePeerScorer.Name, true, "test")
|
||||
context := cli.NewContext(&app, set, nil)
|
||||
ConfigureBeaconChain(context)
|
||||
c := Get()
|
||||
assert.Equal(t, true, c.PyrmontTestnet)
|
||||
assert.Equal(t, true, c.EnablePeerScorer)
|
||||
}
|
||||
|
||||
@@ -70,6 +70,11 @@ var (
|
||||
Usage: deprecatedUsage,
|
||||
Hidden: true,
|
||||
}
|
||||
deprecatedPyrmontTestnet = &cli.BoolFlag{
|
||||
Name: "pyrmont",
|
||||
Usage: deprecatedUsage,
|
||||
Hidden: true,
|
||||
}
|
||||
)
|
||||
|
||||
var deprecatedFlags = []cli.Flag{
|
||||
@@ -84,4 +89,5 @@ var deprecatedFlags = []cli.Flag{
|
||||
deprecatedDisableNextSlotStateCache,
|
||||
deprecatedAttestationAggregationStrategy,
|
||||
deprecatedForceOptMaxCoverAggregationStategy,
|
||||
deprecatedPyrmontTestnet,
|
||||
}
|
||||
|
||||
@@ -7,11 +7,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// PyrmontTestnet flag for the multiclient Ethereum consensus testnet.
|
||||
PyrmontTestnet = &cli.BoolFlag{
|
||||
Name: "pyrmont",
|
||||
Usage: "This defines the flag through which we can run on the Pyrmont Multiclient Testnet",
|
||||
}
|
||||
// PraterTestnet flag for the multiclient Ethereum consensus testnet.
|
||||
PraterTestnet = &cli.BoolFlag{
|
||||
Name: "prater",
|
||||
@@ -156,7 +151,6 @@ var ValidatorFlags = append(deprecatedFlags, []cli.Flag{
|
||||
writeWalletPasswordOnWebOnboarding,
|
||||
enableExternalSlasherProtectionFlag,
|
||||
disableAttestingHistoryDBCache,
|
||||
PyrmontTestnet,
|
||||
PraterTestnet,
|
||||
Mainnet,
|
||||
dynamicKeyReloadDebounceInterval,
|
||||
@@ -175,7 +169,6 @@ var BeaconChainFlags = append(deprecatedFlags, []cli.Flag{
|
||||
devModeFlag,
|
||||
writeSSZStateTransitionsFlag,
|
||||
disableGRPCConnectionLogging,
|
||||
PyrmontTestnet,
|
||||
PraterTestnet,
|
||||
Mainnet,
|
||||
enablePeerScorer,
|
||||
|
||||
@@ -13,7 +13,6 @@ go_library(
|
||||
"network_config.go",
|
||||
"testnet_e2e_config.go",
|
||||
"testnet_prater_config.go",
|
||||
"testnet_pyrmont_config.go",
|
||||
"testutils.go",
|
||||
"values.go",
|
||||
],
|
||||
@@ -48,6 +47,7 @@ go_test(
|
||||
"@consensus_spec_tests_mainnet//:test_data",
|
||||
"@consensus_spec_tests_minimal//:test_data",
|
||||
"@eth2_networks//:configs",
|
||||
"testdata/e2e_config.yaml",
|
||||
],
|
||||
gotags = ["develop"],
|
||||
race = "on",
|
||||
@@ -61,3 +61,9 @@ go_test(
|
||||
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "custom_configs",
|
||||
srcs = glob(["testdata/*.yaml"]),
|
||||
visibility = ["//testing:__subpackages__"],
|
||||
)
|
||||
|
||||
@@ -18,94 +18,94 @@ import (
|
||||
|
||||
var placeholderFields = []string{"UPDATE_TIMEOUT", "INTERVALS_PER_SLOT"}
|
||||
|
||||
func TestLoadConfigFileMainnet(t *testing.T) {
|
||||
func TestLoadConfigFile(t *testing.T) {
|
||||
// See https://media.githubusercontent.com/media/ethereum/consensus-spec-tests/master/tests/minimal/config/phase0.yaml
|
||||
assertVals := func(name string, fields []string, c1, c2 *params.BeaconChainConfig) {
|
||||
assertVals := func(name string, fields []string, expected, actual *params.BeaconChainConfig) {
|
||||
// Misc params.
|
||||
assert.Equal(t, c1.MaxCommitteesPerSlot, c2.MaxCommitteesPerSlot, "%s: MaxCommitteesPerSlot", name)
|
||||
assert.Equal(t, c1.TargetCommitteeSize, c2.TargetCommitteeSize, "%s: TargetCommitteeSize", name)
|
||||
assert.Equal(t, c1.MaxValidatorsPerCommittee, c2.MaxValidatorsPerCommittee, "%s: MaxValidatorsPerCommittee", name)
|
||||
assert.Equal(t, c1.MinPerEpochChurnLimit, c2.MinPerEpochChurnLimit, "%s: MinPerEpochChurnLimit", name)
|
||||
assert.Equal(t, c1.ChurnLimitQuotient, c2.ChurnLimitQuotient, "%s: ChurnLimitQuotient", name)
|
||||
assert.Equal(t, c1.ShuffleRoundCount, c2.ShuffleRoundCount, "%s: ShuffleRoundCount", name)
|
||||
assert.Equal(t, c1.MinGenesisActiveValidatorCount, c2.MinGenesisActiveValidatorCount, "%s: MinGenesisActiveValidatorCount", name)
|
||||
assert.Equal(t, c1.MinGenesisTime, c2.MinGenesisTime, "%s: MinGenesisTime", name)
|
||||
assert.Equal(t, c1.HysteresisQuotient, c2.HysteresisQuotient, "%s: HysteresisQuotient", name)
|
||||
assert.Equal(t, c1.HysteresisDownwardMultiplier, c2.HysteresisDownwardMultiplier, "%s: HysteresisDownwardMultiplier", name)
|
||||
assert.Equal(t, c1.HysteresisUpwardMultiplier, c2.HysteresisUpwardMultiplier, "%s: HysteresisUpwardMultiplier", name)
|
||||
assert.Equal(t, expected.MaxCommitteesPerSlot, actual.MaxCommitteesPerSlot, "%s: MaxCommitteesPerSlot", name)
|
||||
assert.Equal(t, expected.TargetCommitteeSize, actual.TargetCommitteeSize, "%s: TargetCommitteeSize", name)
|
||||
assert.Equal(t, expected.MaxValidatorsPerCommittee, actual.MaxValidatorsPerCommittee, "%s: MaxValidatorsPerCommittee", name)
|
||||
assert.Equal(t, expected.MinPerEpochChurnLimit, actual.MinPerEpochChurnLimit, "%s: MinPerEpochChurnLimit", name)
|
||||
assert.Equal(t, expected.ChurnLimitQuotient, actual.ChurnLimitQuotient, "%s: ChurnLimitQuotient", name)
|
||||
assert.Equal(t, expected.ShuffleRoundCount, actual.ShuffleRoundCount, "%s: ShuffleRoundCount", name)
|
||||
assert.Equal(t, expected.MinGenesisActiveValidatorCount, actual.MinGenesisActiveValidatorCount, "%s: MinGenesisActiveValidatorCount", name)
|
||||
assert.Equal(t, expected.MinGenesisTime, actual.MinGenesisTime, "%s: MinGenesisTime", name)
|
||||
assert.Equal(t, expected.HysteresisQuotient, actual.HysteresisQuotient, "%s: HysteresisQuotient", name)
|
||||
assert.Equal(t, expected.HysteresisDownwardMultiplier, actual.HysteresisDownwardMultiplier, "%s: HysteresisDownwardMultiplier", name)
|
||||
assert.Equal(t, expected.HysteresisUpwardMultiplier, actual.HysteresisUpwardMultiplier, "%s: HysteresisUpwardMultiplier", name)
|
||||
|
||||
// Fork Choice params.
|
||||
assert.Equal(t, c1.SafeSlotsToUpdateJustified, c2.SafeSlotsToUpdateJustified, "%s: SafeSlotsToUpdateJustified", name)
|
||||
assert.Equal(t, expected.SafeSlotsToUpdateJustified, actual.SafeSlotsToUpdateJustified, "%s: SafeSlotsToUpdateJustified", name)
|
||||
|
||||
// Validator params.
|
||||
assert.Equal(t, c1.Eth1FollowDistance, c2.Eth1FollowDistance, "%s: Eth1FollowDistance", name)
|
||||
assert.Equal(t, c1.TargetAggregatorsPerCommittee, c2.TargetAggregatorsPerCommittee, "%s: TargetAggregatorsPerCommittee", name)
|
||||
assert.Equal(t, c1.RandomSubnetsPerValidator, c2.RandomSubnetsPerValidator, "%s: RandomSubnetsPerValidator", name)
|
||||
assert.Equal(t, c1.EpochsPerRandomSubnetSubscription, c2.EpochsPerRandomSubnetSubscription, "%s: EpochsPerRandomSubnetSubscription", name)
|
||||
assert.Equal(t, c1.SecondsPerETH1Block, c2.SecondsPerETH1Block, "%s: SecondsPerETH1Block", name)
|
||||
assert.Equal(t, expected.Eth1FollowDistance, actual.Eth1FollowDistance, "%s: Eth1FollowDistance", name)
|
||||
assert.Equal(t, expected.TargetAggregatorsPerCommittee, actual.TargetAggregatorsPerCommittee, "%s: TargetAggregatorsPerCommittee", name)
|
||||
assert.Equal(t, expected.RandomSubnetsPerValidator, actual.RandomSubnetsPerValidator, "%s: RandomSubnetsPerValidator", name)
|
||||
assert.Equal(t, expected.EpochsPerRandomSubnetSubscription, actual.EpochsPerRandomSubnetSubscription, "%s: EpochsPerRandomSubnetSubscription", name)
|
||||
assert.Equal(t, expected.SecondsPerETH1Block, actual.SecondsPerETH1Block, "%s: SecondsPerETH1Block", name)
|
||||
|
||||
// Deposit contract.
|
||||
assert.Equal(t, c1.DepositChainID, c2.DepositChainID, "%s: DepositChainID", name)
|
||||
assert.Equal(t, c1.DepositNetworkID, c2.DepositNetworkID, "%s: DepositNetworkID", name)
|
||||
assert.Equal(t, c1.DepositContractAddress, c2.DepositContractAddress, "%s: DepositContractAddress", name)
|
||||
assert.Equal(t, expected.DepositChainID, actual.DepositChainID, "%s: DepositChainID", name)
|
||||
assert.Equal(t, expected.DepositNetworkID, actual.DepositNetworkID, "%s: DepositNetworkID", name)
|
||||
assert.Equal(t, expected.DepositContractAddress, actual.DepositContractAddress, "%s: DepositContractAddress", name)
|
||||
|
||||
// Gwei values.
|
||||
assert.Equal(t, c1.MinDepositAmount, c2.MinDepositAmount, "%s: MinDepositAmount", name)
|
||||
assert.Equal(t, c1.MaxEffectiveBalance, c2.MaxEffectiveBalance, "%s: MaxEffectiveBalance", name)
|
||||
assert.Equal(t, c1.EjectionBalance, c2.EjectionBalance, "%s: EjectionBalance", name)
|
||||
assert.Equal(t, c1.EffectiveBalanceIncrement, c2.EffectiveBalanceIncrement, "%s: EffectiveBalanceIncrement", name)
|
||||
assert.Equal(t, expected.MinDepositAmount, actual.MinDepositAmount, "%s: MinDepositAmount", name)
|
||||
assert.Equal(t, expected.MaxEffectiveBalance, actual.MaxEffectiveBalance, "%s: MaxEffectiveBalance", name)
|
||||
assert.Equal(t, expected.EjectionBalance, actual.EjectionBalance, "%s: EjectionBalance", name)
|
||||
assert.Equal(t, expected.EffectiveBalanceIncrement, actual.EffectiveBalanceIncrement, "%s: EffectiveBalanceIncrement", name)
|
||||
|
||||
// Initial values.
|
||||
assert.DeepEqual(t, c1.GenesisForkVersion, c2.GenesisForkVersion, "%s: GenesisForkVersion", name)
|
||||
assert.DeepEqual(t, c1.BLSWithdrawalPrefixByte, c2.BLSWithdrawalPrefixByte, "%s: BLSWithdrawalPrefixByte", name)
|
||||
assert.DeepEqual(t, expected.GenesisForkVersion, actual.GenesisForkVersion, "%s: GenesisForkVersion", name)
|
||||
assert.DeepEqual(t, expected.BLSWithdrawalPrefixByte, actual.BLSWithdrawalPrefixByte, "%s: BLSWithdrawalPrefixByte", name)
|
||||
|
||||
// Time parameters.
|
||||
assert.Equal(t, c1.GenesisDelay, c2.GenesisDelay, "%s: GenesisDelay", name)
|
||||
assert.Equal(t, c1.SecondsPerSlot, c2.SecondsPerSlot, "%s: SecondsPerSlot", name)
|
||||
assert.Equal(t, c1.MinAttestationInclusionDelay, c2.MinAttestationInclusionDelay, "%s: MinAttestationInclusionDelay", name)
|
||||
assert.Equal(t, c1.SlotsPerEpoch, c2.SlotsPerEpoch, "%s: SlotsPerEpoch", name)
|
||||
assert.Equal(t, c1.MinSeedLookahead, c2.MinSeedLookahead, "%s: MinSeedLookahead", name)
|
||||
assert.Equal(t, c1.MaxSeedLookahead, c2.MaxSeedLookahead, "%s: MaxSeedLookahead", name)
|
||||
assert.Equal(t, c1.EpochsPerEth1VotingPeriod, c2.EpochsPerEth1VotingPeriod, "%s: EpochsPerEth1VotingPeriod", name)
|
||||
assert.Equal(t, c1.SlotsPerHistoricalRoot, c2.SlotsPerHistoricalRoot, "%s: SlotsPerHistoricalRoot", name)
|
||||
assert.Equal(t, c1.MinValidatorWithdrawabilityDelay, c2.MinValidatorWithdrawabilityDelay, "%s: MinValidatorWithdrawabilityDelay", name)
|
||||
assert.Equal(t, c1.ShardCommitteePeriod, c2.ShardCommitteePeriod, "%s: ShardCommitteePeriod", name)
|
||||
assert.Equal(t, c1.MinEpochsToInactivityPenalty, c2.MinEpochsToInactivityPenalty, "%s: MinEpochsToInactivityPenalty", name)
|
||||
assert.Equal(t, expected.GenesisDelay, actual.GenesisDelay, "%s: GenesisDelay", name)
|
||||
assert.Equal(t, expected.SecondsPerSlot, actual.SecondsPerSlot, "%s: SecondsPerSlot", name)
|
||||
assert.Equal(t, expected.MinAttestationInclusionDelay, actual.MinAttestationInclusionDelay, "%s: MinAttestationInclusionDelay", name)
|
||||
assert.Equal(t, expected.SlotsPerEpoch, actual.SlotsPerEpoch, "%s: SlotsPerEpoch", name)
|
||||
assert.Equal(t, expected.MinSeedLookahead, actual.MinSeedLookahead, "%s: MinSeedLookahead", name)
|
||||
assert.Equal(t, expected.MaxSeedLookahead, actual.MaxSeedLookahead, "%s: MaxSeedLookahead", name)
|
||||
assert.Equal(t, expected.EpochsPerEth1VotingPeriod, actual.EpochsPerEth1VotingPeriod, "%s: EpochsPerEth1VotingPeriod", name)
|
||||
assert.Equal(t, expected.SlotsPerHistoricalRoot, actual.SlotsPerHistoricalRoot, "%s: SlotsPerHistoricalRoot", name)
|
||||
assert.Equal(t, expected.MinValidatorWithdrawabilityDelay, actual.MinValidatorWithdrawabilityDelay, "%s: MinValidatorWithdrawabilityDelay", name)
|
||||
assert.Equal(t, expected.ShardCommitteePeriod, actual.ShardCommitteePeriod, "%s: ShardCommitteePeriod", name)
|
||||
assert.Equal(t, expected.MinEpochsToInactivityPenalty, actual.MinEpochsToInactivityPenalty, "%s: MinEpochsToInactivityPenalty", name)
|
||||
|
||||
// State vector lengths.
|
||||
assert.Equal(t, c1.EpochsPerHistoricalVector, c2.EpochsPerHistoricalVector, "%s: EpochsPerHistoricalVector", name)
|
||||
assert.Equal(t, c1.EpochsPerSlashingsVector, c2.EpochsPerSlashingsVector, "%s: EpochsPerSlashingsVector", name)
|
||||
assert.Equal(t, c1.HistoricalRootsLimit, c2.HistoricalRootsLimit, "%s: HistoricalRootsLimit", name)
|
||||
assert.Equal(t, c1.ValidatorRegistryLimit, c2.ValidatorRegistryLimit, "%s: ValidatorRegistryLimit", name)
|
||||
assert.Equal(t, expected.EpochsPerHistoricalVector, actual.EpochsPerHistoricalVector, "%s: EpochsPerHistoricalVector", name)
|
||||
assert.Equal(t, expected.EpochsPerSlashingsVector, actual.EpochsPerSlashingsVector, "%s: EpochsPerSlashingsVector", name)
|
||||
assert.Equal(t, expected.HistoricalRootsLimit, actual.HistoricalRootsLimit, "%s: HistoricalRootsLimit", name)
|
||||
assert.Equal(t, expected.ValidatorRegistryLimit, actual.ValidatorRegistryLimit, "%s: ValidatorRegistryLimit", name)
|
||||
|
||||
// Reward and penalty quotients.
|
||||
assert.Equal(t, c1.BaseRewardFactor, c2.BaseRewardFactor, "%s: BaseRewardFactor", name)
|
||||
assert.Equal(t, c1.WhistleBlowerRewardQuotient, c2.WhistleBlowerRewardQuotient, "%s: WhistleBlowerRewardQuotient", name)
|
||||
assert.Equal(t, c1.ProposerRewardQuotient, c2.ProposerRewardQuotient, "%s: ProposerRewardQuotient", name)
|
||||
assert.Equal(t, c1.InactivityPenaltyQuotient, c2.InactivityPenaltyQuotient, "%s: InactivityPenaltyQuotient", name)
|
||||
assert.Equal(t, c1.InactivityPenaltyQuotientAltair, c2.InactivityPenaltyQuotientAltair, "%s: InactivityPenaltyQuotientAltair", name)
|
||||
assert.Equal(t, c1.MinSlashingPenaltyQuotient, c2.MinSlashingPenaltyQuotient, "%s: MinSlashingPenaltyQuotient", name)
|
||||
assert.Equal(t, c1.MinSlashingPenaltyQuotientAltair, c2.MinSlashingPenaltyQuotientAltair, "%s: MinSlashingPenaltyQuotientAltair", name)
|
||||
assert.Equal(t, c1.ProportionalSlashingMultiplier, c2.ProportionalSlashingMultiplier, "%s: ProportionalSlashingMultiplier", name)
|
||||
assert.Equal(t, c1.ProportionalSlashingMultiplierAltair, c2.ProportionalSlashingMultiplierAltair, "%s: ProportionalSlashingMultiplierAltair", name)
|
||||
assert.Equal(t, expected.BaseRewardFactor, actual.BaseRewardFactor, "%s: BaseRewardFactor", name)
|
||||
assert.Equal(t, expected.WhistleBlowerRewardQuotient, actual.WhistleBlowerRewardQuotient, "%s: WhistleBlowerRewardQuotient", name)
|
||||
assert.Equal(t, expected.ProposerRewardQuotient, actual.ProposerRewardQuotient, "%s: ProposerRewardQuotient", name)
|
||||
assert.Equal(t, expected.InactivityPenaltyQuotient, actual.InactivityPenaltyQuotient, "%s: InactivityPenaltyQuotient", name)
|
||||
assert.Equal(t, expected.InactivityPenaltyQuotientAltair, actual.InactivityPenaltyQuotientAltair, "%s: InactivityPenaltyQuotientAltair", name)
|
||||
assert.Equal(t, expected.MinSlashingPenaltyQuotient, actual.MinSlashingPenaltyQuotient, "%s: MinSlashingPenaltyQuotient", name)
|
||||
assert.Equal(t, expected.MinSlashingPenaltyQuotientAltair, actual.MinSlashingPenaltyQuotientAltair, "%s: MinSlashingPenaltyQuotientAltair", name)
|
||||
assert.Equal(t, expected.ProportionalSlashingMultiplier, actual.ProportionalSlashingMultiplier, "%s: ProportionalSlashingMultiplier", name)
|
||||
assert.Equal(t, expected.ProportionalSlashingMultiplierAltair, actual.ProportionalSlashingMultiplierAltair, "%s: ProportionalSlashingMultiplierAltair", name)
|
||||
|
||||
// Max operations per block.
|
||||
assert.Equal(t, c1.MaxProposerSlashings, c2.MaxProposerSlashings, "%s: MaxProposerSlashings", name)
|
||||
assert.Equal(t, c1.MaxAttesterSlashings, c2.MaxAttesterSlashings, "%s: MaxAttesterSlashings", name)
|
||||
assert.Equal(t, c1.MaxAttestations, c2.MaxAttestations, "%s: MaxAttestations", name)
|
||||
assert.Equal(t, c1.MaxDeposits, c2.MaxDeposits, "%s: MaxDeposits", name)
|
||||
assert.Equal(t, c1.MaxVoluntaryExits, c2.MaxVoluntaryExits, "%s: MaxVoluntaryExits", name)
|
||||
assert.Equal(t, expected.MaxProposerSlashings, actual.MaxProposerSlashings, "%s: MaxProposerSlashings", name)
|
||||
assert.Equal(t, expected.MaxAttesterSlashings, actual.MaxAttesterSlashings, "%s: MaxAttesterSlashings", name)
|
||||
assert.Equal(t, expected.MaxAttestations, actual.MaxAttestations, "%s: MaxAttestations", name)
|
||||
assert.Equal(t, expected.MaxDeposits, actual.MaxDeposits, "%s: MaxDeposits", name)
|
||||
assert.Equal(t, expected.MaxVoluntaryExits, actual.MaxVoluntaryExits, "%s: MaxVoluntaryExits", name)
|
||||
|
||||
// Signature domains.
|
||||
assert.Equal(t, c1.DomainBeaconProposer, c2.DomainBeaconProposer, "%s: DomainBeaconProposer", name)
|
||||
assert.Equal(t, c1.DomainBeaconAttester, c2.DomainBeaconAttester, "%s: DomainBeaconAttester", name)
|
||||
assert.Equal(t, c1.DomainRandao, c2.DomainRandao, "%s: DomainRandao", name)
|
||||
assert.Equal(t, c1.DomainDeposit, c2.DomainDeposit, "%s: DomainDeposit", name)
|
||||
assert.Equal(t, c1.DomainVoluntaryExit, c2.DomainVoluntaryExit, "%s: DomainVoluntaryExit", name)
|
||||
assert.Equal(t, c1.DomainSelectionProof, c2.DomainSelectionProof, "%s: DomainSelectionProof", name)
|
||||
assert.Equal(t, c1.DomainAggregateAndProof, c2.DomainAggregateAndProof, "%s: DomainAggregateAndProof", name)
|
||||
assert.Equal(t, expected.DomainBeaconProposer, actual.DomainBeaconProposer, "%s: DomainBeaconProposer", name)
|
||||
assert.Equal(t, expected.DomainBeaconAttester, actual.DomainBeaconAttester, "%s: DomainBeaconAttester", name)
|
||||
assert.Equal(t, expected.DomainRandao, actual.DomainRandao, "%s: DomainRandao", name)
|
||||
assert.Equal(t, expected.DomainDeposit, actual.DomainDeposit, "%s: DomainDeposit", name)
|
||||
assert.Equal(t, expected.DomainVoluntaryExit, actual.DomainVoluntaryExit, "%s: DomainVoluntaryExit", name)
|
||||
assert.Equal(t, expected.DomainSelectionProof, actual.DomainSelectionProof, "%s: DomainSelectionProof", name)
|
||||
assert.Equal(t, expected.DomainAggregateAndProof, actual.DomainAggregateAndProof, "%s: DomainAggregateAndProof", name)
|
||||
|
||||
assertYamlFieldsMatch(t, name, fields, c1, c2)
|
||||
assertYamlFieldsMatch(t, name, fields, expected, actual)
|
||||
}
|
||||
|
||||
t.Run("mainnet", func(t *testing.T) {
|
||||
@@ -129,6 +129,17 @@ func TestLoadConfigFileMainnet(t *testing.T) {
|
||||
fields := fieldsFromYamls(t, append(minimalPresetsFiles, minimalConfigFile))
|
||||
assertVals("minimal", fields, params.MinimalSpecConfig(), params.BeaconConfig())
|
||||
})
|
||||
|
||||
t.Run("e2e", func(t *testing.T) {
|
||||
minimalPresetsFiles := presetsFilePath(t, "minimal")
|
||||
for _, fp := range minimalPresetsFiles {
|
||||
params.LoadChainConfigFile(fp, nil)
|
||||
}
|
||||
configFile := "testdata/e2e_config.yaml"
|
||||
params.LoadChainConfigFile(configFile, nil)
|
||||
fields := fieldsFromYamls(t, append(minimalPresetsFiles, configFile))
|
||||
assertVals("e2e", fields, params.E2ETestConfig(), params.BeaconConfig())
|
||||
})
|
||||
}
|
||||
|
||||
func TestLoadConfigFile_OverwriteCorrectly(t *testing.T) {
|
||||
|
||||
118
config/params/testdata/e2e_config.yaml
vendored
Normal file
118
config/params/testdata/e2e_config.yaml
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
# e2e config
|
||||
|
||||
# Extends the minimal preset
|
||||
PRESET_BASE: 'minimal'
|
||||
|
||||
|
||||
|
||||
# Transition
|
||||
# ---------------------------------------------------------------
|
||||
# TBD, 2**256-2**10 is a placeholder, e2e is 600
|
||||
TERMINAL_TOTAL_DIFFICULTY: 600
|
||||
# By default, don't use these params
|
||||
#TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
#TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615
|
||||
|
||||
|
||||
|
||||
# Genesis
|
||||
# ---------------------------------------------------------------
|
||||
# [customized]
|
||||
MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 256 # Override for e2e tests
|
||||
# Jan 3, 2020
|
||||
MIN_GENESIS_TIME: 1578009600
|
||||
# Highest byte set to 0x01 to avoid collisions with mainnet versioning
|
||||
GENESIS_FORK_VERSION: 0x000000fd
|
||||
# [customized] Faster to spin up testnets, but does not give validator reasonable warning time for genesis
|
||||
GENESIS_DELAY: 10 # Override for e2e tests
|
||||
|
||||
|
||||
# Forking
|
||||
# ---------------------------------------------------------------
|
||||
# Values provided for illustrative purposes.
|
||||
# Individual tests/testnets may set different values.
|
||||
|
||||
# Altair
|
||||
ALTAIR_FORK_VERSION: 0x010000fd
|
||||
ALTAIR_FORK_EPOCH: 6 # Override for e2e
|
||||
# Bellatrix
|
||||
BELLATRIX_FORK_VERSION: 0x020000fd
|
||||
BELLATRIX_FORK_EPOCH: 8
|
||||
# Sharding
|
||||
SHARDING_FORK_VERSION: 0x030000fd
|
||||
SHARDING_FORK_EPOCH: 18446744073709551615
|
||||
|
||||
|
||||
# Time parameters
|
||||
# ---------------------------------------------------------------
|
||||
# [customized] Faster for testing purposes
|
||||
SECONDS_PER_SLOT: 10 # Override for e2e tests
|
||||
# 14 (estimate from Eth1 mainnet)
|
||||
SECONDS_PER_ETH1_BLOCK: 2 # Override for e2e tests
|
||||
# 2**8 (= 256) epochs
|
||||
MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256
|
||||
# [customized] higher frequency of committee turnover and faster time to acceptable voluntary exit
|
||||
SHARD_COMMITTEE_PERIOD: 4 # Override for e2e tests
|
||||
# [customized] process deposits more quickly, but insecure
|
||||
ETH1_FOLLOW_DISTANCE: 4 # Override for e2e tests
|
||||
|
||||
|
||||
# Validator cycle
|
||||
# ---------------------------------------------------------------
|
||||
# 2**2 (= 4)
|
||||
INACTIVITY_SCORE_BIAS: 4
|
||||
# 2**4 (= 16)
|
||||
INACTIVITY_SCORE_RECOVERY_RATE: 16
|
||||
# 2**4 * 10**9 (= 16,000,000,000) Gwei
|
||||
EJECTION_BALANCE: 16000000000
|
||||
# 2**2 (= 4)
|
||||
MIN_PER_EPOCH_CHURN_LIMIT: 4
|
||||
# [customized] scale queue churn at much lower validator counts for testing
|
||||
CHURN_LIMIT_QUOTIENT: 65536
|
||||
|
||||
|
||||
# Fork choice
|
||||
# ---------------------------------------------------------------
|
||||
# 70%
|
||||
PROPOSER_SCORE_BOOST: 70
|
||||
|
||||
|
||||
# Deposit contract
|
||||
# ---------------------------------------------------------------
|
||||
# Ethereum Goerli testnet
|
||||
DEPOSIT_CHAIN_ID: 1337 # Override for e2e tests
|
||||
DEPOSIT_NETWORK_ID: 1337 # Override for e2e tests
|
||||
# Configured on a per testnet basis
|
||||
DEPOSIT_CONTRACT_ADDRESS: 0x1234567890123456789012345678901234567890
|
||||
|
||||
# Updated penalty values
|
||||
# ---------------------------------------------------------------
|
||||
# 3 * 2**24 (= 50,331,648)
|
||||
INACTIVITY_PENALTY_QUOTIENT_ALTAIR: 50331648
|
||||
# 2**6 (= 64)
|
||||
MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR: 64
|
||||
# 2
|
||||
PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR: 2
|
||||
|
||||
|
||||
# Sync committee
|
||||
# ---------------------------------------------------------------
|
||||
# [customized]
|
||||
SYNC_COMMITTEE_SIZE: 32
|
||||
# [customized]
|
||||
EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 8
|
||||
|
||||
|
||||
# Sync protocol
|
||||
# ---------------------------------------------------------------
|
||||
# 1
|
||||
MIN_SYNC_COMMITTEE_PARTICIPANTS: 1
|
||||
|
||||
# Other e2e overrides
|
||||
# ---------------------------------------------------------------
|
||||
CONFIG_NAME: "end-to-end"
|
||||
SLOTS_PER_EPOCH: 6
|
||||
EPOCHS_PER_ETH1_VOTING_PERIOD: 2
|
||||
MAX_SEED_LOOKAHEAD: 1
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user