mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Compare commits
9 Commits
unify-slas
...
hive-fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1714333ded | ||
|
|
0acd3d75e8 | ||
|
|
f4a5490ba8 | ||
|
|
6141960769 | ||
|
|
9290ad3a34 | ||
|
|
69350a6a80 | ||
|
|
93e8c749f8 | ||
|
|
96fecf8c57 | ||
|
|
5d29ca4984 |
@@ -70,6 +70,13 @@ func Test_validTerminalPowBlock(t *testing.T) {
|
||||
ttd: 2,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "hive test scenario",
|
||||
currentDifficulty: uint256.NewInt(500),
|
||||
parentDifficulty: uint256.NewInt(498),
|
||||
ttd: 498,
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -104,7 +111,7 @@ func Test_validTerminalPowBlockSpecConfig(t *testing.T) {
|
||||
|
||||
func Test_validateMergeBlock(t *testing.T) {
|
||||
cfg := params.BeaconConfig()
|
||||
cfg.TerminalTotalDifficulty = "2"
|
||||
cfg.TerminalTotalDifficulty = "500"
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -120,17 +127,25 @@ func Test_validateMergeBlock(t *testing.T) {
|
||||
|
||||
engine := &mocks.EngineClient{BlockByHashMap: map[[32]byte]*enginev1.ExecutionBlock{}}
|
||||
service.cfg.ExecutionEngineCaller = engine
|
||||
// Recent execution blocks:
|
||||
// ... <- 3 <- b <- a
|
||||
// TD: 488 498 500
|
||||
// Block 'a' is the expected TTD block with a TD of 500 and a parent TD of 498
|
||||
engine.BlockByHashMap[[32]byte{'a'}] = &enginev1.ExecutionBlock{
|
||||
ParentHash: bytesutil.PadTo([]byte{'b'}, fieldparams.RootLength),
|
||||
TotalDifficulty: "0x2",
|
||||
TotalDifficulty: "0x1F4", // 500
|
||||
}
|
||||
engine.BlockByHashMap[[32]byte{'b'}] = &enginev1.ExecutionBlock{
|
||||
ParentHash: bytesutil.PadTo([]byte{'3'}, fieldparams.RootLength),
|
||||
TotalDifficulty: "0x1",
|
||||
TotalDifficulty: "0x1F2", // 498
|
||||
}
|
||||
engine.BlockByHashMap[[32]byte{'3'}] = &enginev1.ExecutionBlock{
|
||||
ParentHash: bytesutil.PadTo([]byte{'f'}, fieldparams.RootLength),
|
||||
TotalDifficulty: "0x1E8", // 488
|
||||
}
|
||||
blk := ðpb.SignedBeaconBlockBellatrix{
|
||||
Block: ðpb.BeaconBlockBellatrix{
|
||||
Slot: 1,
|
||||
Slot: 79,
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
ExecutionPayload: &enginev1.ExecutionPayload{
|
||||
ParentHash: bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength),
|
||||
@@ -142,10 +157,10 @@ func Test_validateMergeBlock(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.validateMergeBlock(ctx, b))
|
||||
|
||||
cfg.TerminalTotalDifficulty = "1"
|
||||
cfg.TerminalTotalDifficulty = "498"
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
err = service.validateMergeBlock(ctx, b)
|
||||
require.ErrorContains(t, "invalid TTD, configTTD: 1, currentTTD: 2, parentTTD: 1", err)
|
||||
require.ErrorContains(t, "invalid TTD, configTTD: 498, currentTTD: 500, parentTTD: 498", err)
|
||||
require.Equal(t, true, IsInvalidBlock(err))
|
||||
}
|
||||
|
||||
|
||||
@@ -212,7 +212,9 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
|
||||
|
||||
// Save justified check point to db.
|
||||
if justified.Epoch > currJustifiedEpoch {
|
||||
if err := s.cfg.BeaconDB.SaveJustifiedCheckpoint(ctx, postState.CurrentJustifiedCheckpoint()); err != nil {
|
||||
if err := s.cfg.BeaconDB.SaveJustifiedCheckpoint(ctx, ðpb.Checkpoint{
|
||||
Epoch: justified.Epoch, Root: justified.Root[:],
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -220,7 +222,7 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
|
||||
// Update finalized check point.
|
||||
finalized := s.ForkChoicer().FinalizedCheckpoint()
|
||||
if finalized.Epoch > currFinalizedEpoch {
|
||||
if err := s.updateFinalized(ctx, postState.FinalizedCheckpoint()); err != nil {
|
||||
if err := s.updateFinalized(ctx, ðpb.Checkpoint{Epoch: finalized.Epoch, Root: finalized.Root[:]}); err != nil {
|
||||
return err
|
||||
}
|
||||
isOptimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(finalized.Root)
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package precompute
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/time"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
)
|
||||
@@ -14,11 +17,17 @@ var errNilState = errors.New("nil state")
|
||||
|
||||
// UnrealizedCheckpoints returns the justification and finalization checkpoints of the
|
||||
// given state as if it was progressed with empty slots until the next epoch.
|
||||
func UnrealizedCheckpoints(st state.BeaconState) (*ethpb.Checkpoint, *ethpb.Checkpoint, error) {
|
||||
func UnrealizedCheckpoints(ctx context.Context, st state.BeaconState) (*ethpb.Checkpoint, *ethpb.Checkpoint, error) {
|
||||
if st == nil || st.IsNil() {
|
||||
return nil, nil, errNilState
|
||||
}
|
||||
|
||||
if slots.ToEpoch(st.Slot()) <= params.BeaconConfig().GenesisEpoch+1 {
|
||||
jc := st.CurrentJustifiedCheckpoint()
|
||||
fc := st.FinalizedCheckpoint()
|
||||
return jc, fc, nil
|
||||
}
|
||||
|
||||
activeBalance, prevTarget, currentTarget, err := st.UnrealizedCheckpointBalances()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
@@ -243,7 +243,7 @@ func TestUnrealizedCheckpoints(t *testing.T) {
|
||||
_, _, err = altair.InitializePrecomputeValidators(context.Background(), state)
|
||||
require.NoError(t, err)
|
||||
|
||||
jc, fc, err := precompute.UnrealizedCheckpoints(state)
|
||||
jc, fc, err := precompute.UnrealizedCheckpoints(context.Background(), state)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, test.expectedJustified, jc.Epoch)
|
||||
require.DeepEqual(t, test.expectedFinalized, fc.Epoch)
|
||||
|
||||
@@ -22,9 +22,12 @@ go_library(
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/epoch/precompute:go_default_library",
|
||||
"//beacon-chain/core/time:go_default_library",
|
||||
"//beacon-chain/forkchoice:go_default_library",
|
||||
"//beacon-chain/forkchoice/types:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice"
|
||||
forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
|
||||
@@ -23,15 +24,17 @@ import (
|
||||
// New initializes a new fork choice store.
|
||||
func New() *ForkChoice {
|
||||
s := &Store{
|
||||
justifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
bestJustifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
prevJustifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
finalizedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
proposerBoostRoot: [32]byte{},
|
||||
nodeByRoot: make(map[[fieldparams.RootLength]byte]*Node),
|
||||
nodeByPayload: make(map[[fieldparams.RootLength]byte]*Node),
|
||||
slashedIndices: make(map[types.ValidatorIndex]bool),
|
||||
pruneThreshold: defaultPruneThreshold,
|
||||
justifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
bestJustifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
unrealizedJustifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
unrealizedFinalizedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
prevJustifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
finalizedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
proposerBoostRoot: [32]byte{},
|
||||
nodeByRoot: make(map[[fieldparams.RootLength]byte]*Node),
|
||||
nodeByPayload: make(map[[fieldparams.RootLength]byte]*Node),
|
||||
slashedIndices: make(map[types.ValidatorIndex]bool),
|
||||
pruneThreshold: defaultPruneThreshold,
|
||||
}
|
||||
|
||||
b := make([]uint64, 0)
|
||||
@@ -112,7 +115,7 @@ func (f *ForkChoice) ProcessAttestation(ctx context.Context, validatorIndices []
|
||||
}
|
||||
|
||||
// InsertNode processes a new block by inserting it to the fork choice store.
|
||||
func (f *ForkChoice) InsertNode(ctx context.Context, state state.ReadOnlyBeaconState, root [32]byte) error {
|
||||
func (f *ForkChoice) InsertNode(ctx context.Context, state state.BeaconState, root [32]byte) error {
|
||||
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.InsertNode")
|
||||
defer span.End()
|
||||
|
||||
@@ -142,10 +145,14 @@ func (f *ForkChoice) InsertNode(ctx context.Context, state state.ReadOnlyBeaconS
|
||||
return errInvalidNilCheckpoint
|
||||
}
|
||||
finalizedEpoch := fc.Epoch
|
||||
err := f.store.insert(ctx, slot, root, parentRoot, payloadHash, justifiedEpoch, finalizedEpoch)
|
||||
node, err := f.store.insert(ctx, slot, root, parentRoot, payloadHash, justifiedEpoch, finalizedEpoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if features.Get().PullTips {
|
||||
jc, fc = f.store.pullTips(ctx, state, node, jc, fc)
|
||||
}
|
||||
return f.updateCheckpoints(ctx, jc, fc)
|
||||
}
|
||||
|
||||
@@ -546,7 +553,7 @@ func (f *ForkChoice) InsertOptimisticChain(ctx context.Context, chain []*forkcho
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.store.insert(ctx,
|
||||
if _, err := f.store.insert(ctx,
|
||||
b.Slot(), r, parentRoot, payloadHash,
|
||||
chain[i].JustifiedCheckpoint.Epoch, chain[i].FinalizedCheckpoint.Epoch); err != nil {
|
||||
return err
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
)
|
||||
@@ -64,5 +65,8 @@ func (f *ForkChoice) NewSlot(ctx context.Context, slot types.Slot) error {
|
||||
f.store.justifiedCheckpoint = bjcp
|
||||
}
|
||||
}
|
||||
if features.Get().PullTips {
|
||||
f.UpdateUnrealizedCheckpoints()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ func (s *Store) head(ctx context.Context) ([32]byte, error) {
|
||||
func (s *Store) insert(ctx context.Context,
|
||||
slot types.Slot,
|
||||
root, parentRoot, payloadHash [fieldparams.RootLength]byte,
|
||||
justifiedEpoch, finalizedEpoch types.Epoch) error {
|
||||
justifiedEpoch, finalizedEpoch types.Epoch) (*Node, error) {
|
||||
_, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.insert")
|
||||
defer span.End()
|
||||
|
||||
@@ -116,8 +116,8 @@ func (s *Store) insert(ctx context.Context,
|
||||
defer s.nodesLock.Unlock()
|
||||
|
||||
// Return if the block has been inserted into Store before.
|
||||
if _, ok := s.nodeByRoot[root]; ok {
|
||||
return nil
|
||||
if n, ok := s.nodeByRoot[root]; ok {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
parent := s.nodeByRoot[parentRoot]
|
||||
@@ -141,14 +141,14 @@ func (s *Store) insert(ctx context.Context,
|
||||
s.treeRootNode = n
|
||||
s.headNode = n
|
||||
} else {
|
||||
return errInvalidParentRoot
|
||||
return n, errInvalidParentRoot
|
||||
}
|
||||
} else {
|
||||
parent.children = append(parent.children, n)
|
||||
// Apply proposer boost
|
||||
timeNow := uint64(time.Now().Unix())
|
||||
if timeNow < s.genesisTime {
|
||||
return nil
|
||||
return n, nil
|
||||
}
|
||||
secondsIntoSlot := (timeNow - s.genesisTime) % params.BeaconConfig().SecondsPerSlot
|
||||
currentSlot := slots.CurrentSlot(s.genesisTime)
|
||||
@@ -162,14 +162,14 @@ func (s *Store) insert(ctx context.Context,
|
||||
// Update best descendants
|
||||
if err := s.treeRootNode.updateBestDescendant(ctx,
|
||||
s.justifiedCheckpoint.Epoch, s.finalizedCheckpoint.Epoch); err != nil {
|
||||
return err
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
// Update metrics.
|
||||
processedBlockCount.Inc()
|
||||
nodeCount.Set(float64(len(s.nodeByRoot)))
|
||||
|
||||
return nil
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// pruneFinalizedNodeByRootMap prunes the `nodeByRoot` map
|
||||
|
||||
@@ -141,7 +141,8 @@ func TestStore_Insert(t *testing.T) {
|
||||
fc := &forkchoicetypes.Checkpoint{Epoch: 0}
|
||||
s := &Store{nodeByRoot: nodeByRoot, treeRootNode: treeRootNode, nodeByPayload: nodeByPayload, justifiedCheckpoint: jc, finalizedCheckpoint: fc}
|
||||
payloadHash := [32]byte{'a'}
|
||||
require.NoError(t, s.insert(context.Background(), 100, indexToHash(100), indexToHash(0), payloadHash, 1, 1))
|
||||
_, err := s.insert(context.Background(), 100, indexToHash(100), indexToHash(0), payloadHash, 1, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, len(s.nodeByRoot), "Did not insert block")
|
||||
assert.Equal(t, (*Node)(nil), treeRootNode.parent, "Incorrect parent")
|
||||
assert.Equal(t, 1, len(treeRootNode.children), "Incorrect children number")
|
||||
|
||||
@@ -18,24 +18,26 @@ type ForkChoice struct {
|
||||
|
||||
// Store defines the fork choice store which includes block nodes and the last view of checkpoint information.
|
||||
type Store struct {
|
||||
justifiedCheckpoint *forkchoicetypes.Checkpoint // latest justified epoch in store.
|
||||
bestJustifiedCheckpoint *forkchoicetypes.Checkpoint // best justified checkpoint in store.
|
||||
prevJustifiedCheckpoint *forkchoicetypes.Checkpoint // previous justified checkpoint in store.
|
||||
finalizedCheckpoint *forkchoicetypes.Checkpoint // latest finalized epoch in store.
|
||||
pruneThreshold uint64 // do not prune tree unless threshold is reached.
|
||||
proposerBoostRoot [fieldparams.RootLength]byte // latest block root that was boosted after being received in a timely manner.
|
||||
previousProposerBoostRoot [fieldparams.RootLength]byte // previous block root that was boosted after being received in a timely manner.
|
||||
previousProposerBoostScore uint64 // previous proposer boosted root score.
|
||||
treeRootNode *Node // the root node of the store tree.
|
||||
headNode *Node // last head Node
|
||||
nodeByRoot map[[fieldparams.RootLength]byte]*Node // nodes indexed by roots.
|
||||
nodeByPayload map[[fieldparams.RootLength]byte]*Node // nodes indexed by payload Hash
|
||||
slashedIndices map[types.ValidatorIndex]bool // the list of equivocating validator indices
|
||||
originRoot [fieldparams.RootLength]byte // The genesis block root
|
||||
nodesLock sync.RWMutex
|
||||
proposerBoostLock sync.RWMutex
|
||||
checkpointsLock sync.RWMutex
|
||||
genesisTime uint64
|
||||
justifiedCheckpoint *forkchoicetypes.Checkpoint // latest justified epoch in store.
|
||||
bestJustifiedCheckpoint *forkchoicetypes.Checkpoint // best justified checkpoint in store.
|
||||
unrealizedJustifiedCheckpoint *forkchoicetypes.Checkpoint // best unrealized justified checkpoint in store.
|
||||
unrealizedFinalizedCheckpoint *forkchoicetypes.Checkpoint // best unrealized finalized checkpoint in store.
|
||||
prevJustifiedCheckpoint *forkchoicetypes.Checkpoint // previous justified checkpoint in store.
|
||||
finalizedCheckpoint *forkchoicetypes.Checkpoint // latest finalized epoch in store.
|
||||
pruneThreshold uint64 // do not prune tree unless threshold is reached.
|
||||
proposerBoostRoot [fieldparams.RootLength]byte // latest block root that was boosted after being received in a timely manner.
|
||||
previousProposerBoostRoot [fieldparams.RootLength]byte // previous block root that was boosted after being received in a timely manner.
|
||||
previousProposerBoostScore uint64 // previous proposer boosted root score.
|
||||
treeRootNode *Node // the root node of the store tree.
|
||||
headNode *Node // last head Node
|
||||
nodeByRoot map[[fieldparams.RootLength]byte]*Node // nodes indexed by roots.
|
||||
nodeByPayload map[[fieldparams.RootLength]byte]*Node // nodes indexed by payload Hash
|
||||
slashedIndices map[types.ValidatorIndex]bool // the list of equivocating validator indices
|
||||
originRoot [fieldparams.RootLength]byte // The genesis block root
|
||||
nodesLock sync.RWMutex
|
||||
proposerBoostLock sync.RWMutex
|
||||
checkpointsLock sync.RWMutex
|
||||
genesisTime uint64
|
||||
}
|
||||
|
||||
// Node defines the individual block which includes its block parent, ancestor and how much weight accounted for it.
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
package doublylinkedtree
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/time"
|
||||
forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
)
|
||||
|
||||
func (s *Store) setUnrealizedJustifiedEpoch(root [32]byte, epoch types.Epoch) error {
|
||||
@@ -45,10 +54,47 @@ func (f *ForkChoice) UpdateUnrealizedCheckpoints() {
|
||||
node.justifiedEpoch = node.unrealizedJustifiedEpoch
|
||||
node.finalizedEpoch = node.unrealizedFinalizedEpoch
|
||||
if node.justifiedEpoch > f.store.justifiedCheckpoint.Epoch {
|
||||
f.store.justifiedCheckpoint.Epoch = node.justifiedEpoch
|
||||
if node.justifiedEpoch > f.store.bestJustifiedCheckpoint.Epoch {
|
||||
f.store.bestJustifiedCheckpoint = f.store.unrealizedJustifiedCheckpoint
|
||||
}
|
||||
f.store.justifiedCheckpoint = f.store.unrealizedJustifiedCheckpoint
|
||||
}
|
||||
if node.finalizedEpoch > f.store.finalizedCheckpoint.Epoch {
|
||||
f.store.finalizedCheckpoint.Epoch = node.finalizedEpoch
|
||||
f.store.justifiedCheckpoint = f.store.unrealizedJustifiedCheckpoint
|
||||
f.store.finalizedCheckpoint = f.store.unrealizedFinalizedCheckpoint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) pullTips(ctx context.Context, state state.BeaconState, node *Node, jc, fc *ethpb.Checkpoint) (*ethpb.Checkpoint, *ethpb.Checkpoint) {
|
||||
var uj, uf *ethpb.Checkpoint
|
||||
uj, uf, err := precompute.UnrealizedCheckpoints(ctx, state)
|
||||
if err != nil {
|
||||
log.WithError(err).Debug("could not compute unrealized checkpoints")
|
||||
uj, uf = jc, fc
|
||||
}
|
||||
node.unrealizedJustifiedEpoch, node.unrealizedFinalizedEpoch = uj.Epoch, uf.Epoch
|
||||
s.checkpointsLock.Lock()
|
||||
if uj.Epoch > s.unrealizedJustifiedCheckpoint.Epoch {
|
||||
s.unrealizedJustifiedCheckpoint = &forkchoicetypes.Checkpoint{
|
||||
Epoch: uj.Epoch, Root: bytesutil.ToBytes32(uj.Root),
|
||||
}
|
||||
}
|
||||
if uf.Epoch > s.unrealizedFinalizedCheckpoint.Epoch {
|
||||
s.unrealizedJustifiedCheckpoint = &forkchoicetypes.Checkpoint{
|
||||
Epoch: uj.Epoch, Root: bytesutil.ToBytes32(uj.Root),
|
||||
}
|
||||
s.unrealizedFinalizedCheckpoint = &forkchoicetypes.Checkpoint{
|
||||
Epoch: uf.Epoch, Root: bytesutil.ToBytes32(uf.Root),
|
||||
}
|
||||
}
|
||||
|
||||
currentSlot := slots.CurrentSlot(s.genesisTime)
|
||||
if time.CurrentEpoch(state) < slots.ToEpoch(currentSlot) {
|
||||
jc, fc = uj, uf
|
||||
node.justifiedEpoch = uj.Epoch
|
||||
node.finalizedEpoch = uf.Epoch
|
||||
}
|
||||
s.checkpointsLock.Unlock()
|
||||
return jc, fc
|
||||
}
|
||||
|
||||
@@ -147,6 +147,8 @@ func TestStore_NoDeadLock(t *testing.T) {
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'g'}, 2))
|
||||
require.NoError(t, f.store.setUnrealizedFinalizedEpoch([32]byte{'g'}, 1))
|
||||
f.store.unrealizedJustifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 2}
|
||||
f.store.unrealizedFinalizedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 1}
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 107, [32]byte{'h'}, [32]byte{'g'}, [32]byte{'H'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
@@ -236,6 +238,7 @@ func TestStore_ForkNextEpoch(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'d'}, 1))
|
||||
f.store.unrealizedJustifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 1}
|
||||
f.UpdateUnrealizedCheckpoints()
|
||||
headRoot, err = f.Head(ctx, []uint64{100})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -30,7 +30,7 @@ type HeadRetriever interface {
|
||||
|
||||
// BlockProcessor processes the block that's used for accounting fork choice.
|
||||
type BlockProcessor interface {
|
||||
InsertNode(context.Context, state.ReadOnlyBeaconState, [32]byte) error
|
||||
InsertNode(context.Context, state.BeaconState, [32]byte) error
|
||||
InsertOptimisticChain(context.Context, []*forkchoicetypes.BlockAndCheckpoints) error
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,12 @@ go_library(
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/epoch/precompute:go_default_library",
|
||||
"//beacon-chain/core/time:go_default_library",
|
||||
"//beacon-chain/forkchoice:go_default_library",
|
||||
"//beacon-chain/forkchoice/types:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
)
|
||||
@@ -64,5 +65,8 @@ func (f *ForkChoice) NewSlot(ctx context.Context, slot types.Slot) error {
|
||||
f.store.justifiedCheckpoint = bjcp
|
||||
}
|
||||
}
|
||||
if features.Get().PullTips {
|
||||
f.UpdateUnrealizedCheckpoints()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice"
|
||||
forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
|
||||
@@ -30,17 +31,19 @@ const defaultPruneThreshold = 256
|
||||
// New initializes a new fork choice store.
|
||||
func New() *ForkChoice {
|
||||
s := &Store{
|
||||
justifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
bestJustifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
prevJustifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
finalizedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
proposerBoostRoot: [32]byte{},
|
||||
nodes: make([]*Node, 0),
|
||||
nodesIndices: make(map[[32]byte]uint64),
|
||||
payloadIndices: make(map[[32]byte]uint64),
|
||||
canonicalNodes: make(map[[32]byte]bool),
|
||||
slashedIndices: make(map[types.ValidatorIndex]bool),
|
||||
pruneThreshold: defaultPruneThreshold,
|
||||
justifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
bestJustifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
unrealizedJustifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
prevJustifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
finalizedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
unrealizedFinalizedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||
proposerBoostRoot: [32]byte{},
|
||||
nodes: make([]*Node, 0),
|
||||
nodesIndices: make(map[[32]byte]uint64),
|
||||
payloadIndices: make(map[[32]byte]uint64),
|
||||
canonicalNodes: make(map[[32]byte]bool),
|
||||
slashedIndices: make(map[types.ValidatorIndex]bool),
|
||||
pruneThreshold: defaultPruneThreshold,
|
||||
}
|
||||
|
||||
b := make([]uint64, 0)
|
||||
@@ -117,7 +120,7 @@ func (f *ForkChoice) ProposerBoost() [fieldparams.RootLength]byte {
|
||||
}
|
||||
|
||||
// InsertNode processes a new block by inserting it to the fork choice store.
|
||||
func (f *ForkChoice) InsertNode(ctx context.Context, state state.ReadOnlyBeaconState, root [32]byte) error {
|
||||
func (f *ForkChoice) InsertNode(ctx context.Context, state state.BeaconState, root [32]byte) error {
|
||||
ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.InsertNode")
|
||||
defer span.End()
|
||||
|
||||
@@ -147,10 +150,14 @@ func (f *ForkChoice) InsertNode(ctx context.Context, state state.ReadOnlyBeaconS
|
||||
return errInvalidNilCheckpoint
|
||||
}
|
||||
finalizedEpoch := fc.Epoch
|
||||
err := f.store.insert(ctx, slot, root, parentRoot, payloadHash, justifiedEpoch, finalizedEpoch)
|
||||
node, err := f.store.insert(ctx, slot, root, parentRoot, payloadHash, justifiedEpoch, finalizedEpoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if features.Get().PullTips {
|
||||
jc, fc = f.store.pullTips(ctx, state, node, jc, fc)
|
||||
}
|
||||
return f.updateCheckpoints(ctx, jc, fc)
|
||||
}
|
||||
|
||||
@@ -462,7 +469,7 @@ func (s *Store) updateCanonicalNodes(ctx context.Context, root [32]byte) error {
|
||||
func (s *Store) insert(ctx context.Context,
|
||||
slot types.Slot,
|
||||
root, parent, payloadHash [32]byte,
|
||||
justifiedEpoch, finalizedEpoch types.Epoch) error {
|
||||
justifiedEpoch, finalizedEpoch types.Epoch) (*Node, error) {
|
||||
_, span := trace.StartSpan(ctx, "protoArrayForkChoice.insert")
|
||||
defer span.End()
|
||||
|
||||
@@ -470,8 +477,8 @@ func (s *Store) insert(ctx context.Context,
|
||||
defer s.nodesLock.Unlock()
|
||||
|
||||
// Return if the block has been inserted into Store before.
|
||||
if _, ok := s.nodesIndices[root]; ok {
|
||||
return nil
|
||||
if idx, ok := s.nodesIndices[root]; ok {
|
||||
return s.nodes[idx], nil
|
||||
}
|
||||
|
||||
index := uint64(len(s.nodes))
|
||||
@@ -502,7 +509,7 @@ func (s *Store) insert(ctx context.Context,
|
||||
// Apply proposer boost
|
||||
timeNow := uint64(time.Now().Unix())
|
||||
if timeNow < s.genesisTime {
|
||||
return nil
|
||||
return n, nil
|
||||
}
|
||||
secondsIntoSlot := (timeNow - s.genesisTime) % params.BeaconConfig().SecondsPerSlot
|
||||
currentSlot := slots.CurrentSlot(s.genesisTime)
|
||||
@@ -516,7 +523,7 @@ func (s *Store) insert(ctx context.Context,
|
||||
// Update parent with the best child and descendant only if it's available.
|
||||
if n.parent != NonExistentNode {
|
||||
if err := s.updateBestChildAndDescendant(parentIndex, index); err != nil {
|
||||
return err
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,7 +531,7 @@ func (s *Store) insert(ctx context.Context,
|
||||
processedBlockCount.Inc()
|
||||
nodeCount.Set(float64(len(s.nodes)))
|
||||
|
||||
return nil
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// applyWeightChanges iterates backwards through the nodes in store. It checks all nodes parent
|
||||
@@ -991,7 +998,7 @@ func (f *ForkChoice) InsertOptimisticChain(ctx context.Context, chain []*forkcho
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.store.insert(ctx,
|
||||
if _, err := f.store.insert(ctx,
|
||||
b.Slot(), r, parentRoot, payloadHash,
|
||||
chain[i].JustifiedCheckpoint.Epoch, chain[i].FinalizedCheckpoint.Epoch); err != nil {
|
||||
return err
|
||||
|
||||
@@ -114,7 +114,8 @@ func TestStore_Head_ContextCancelled(t *testing.T) {
|
||||
func TestStore_Insert_UnknownParent(t *testing.T) {
|
||||
// The new node does not have a parent.
|
||||
s := &Store{nodesIndices: make(map[[32]byte]uint64), payloadIndices: make(map[[32]byte]uint64)}
|
||||
require.NoError(t, s.insert(context.Background(), 100, [32]byte{'A'}, [32]byte{'B'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
_, err := s.insert(context.Background(), 100, [32]byte{'A'}, [32]byte{'B'}, params.BeaconConfig().ZeroHash, 1, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(s.nodes), "Did not insert block")
|
||||
assert.Equal(t, 1, len(s.nodesIndices), "Did not insert block")
|
||||
assert.Equal(t, NonExistentNode, s.nodes[0].parent, "Incorrect parent")
|
||||
@@ -133,7 +134,8 @@ func TestStore_Insert_KnownParent(t *testing.T) {
|
||||
payloadHash := [32]byte{'c'}
|
||||
s.justifiedCheckpoint = &forkchoicetypes.Checkpoint{}
|
||||
s.finalizedCheckpoint = &forkchoicetypes.Checkpoint{}
|
||||
require.NoError(t, s.insert(context.Background(), 100, [32]byte{'A'}, p, payloadHash, 1, 1))
|
||||
_, err := s.insert(context.Background(), 100, [32]byte{'A'}, p, payloadHash, 1, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, len(s.nodes), "Did not insert block")
|
||||
assert.Equal(t, 2, len(s.nodesIndices), "Did not insert block")
|
||||
assert.Equal(t, uint64(0), s.nodes[1].parent, "Incorrect parent")
|
||||
|
||||
@@ -18,25 +18,27 @@ type ForkChoice struct {
|
||||
|
||||
// Store defines the fork choice store which includes block nodes and the last view of checkpoint information.
|
||||
type Store struct {
|
||||
pruneThreshold uint64 // do not prune tree unless threshold is reached.
|
||||
justifiedCheckpoint *forkchoicetypes.Checkpoint // latest justified checkpoint in store.
|
||||
bestJustifiedCheckpoint *forkchoicetypes.Checkpoint // best justified checkpoint in store.
|
||||
prevJustifiedCheckpoint *forkchoicetypes.Checkpoint // previous justified checkpoint in store.
|
||||
finalizedCheckpoint *forkchoicetypes.Checkpoint // latest finalized checkpoint in store.
|
||||
proposerBoostRoot [fieldparams.RootLength]byte // latest block root that was boosted after being received in a timely manner.
|
||||
previousProposerBoostRoot [fieldparams.RootLength]byte // previous block root that was boosted after being received in a timely manner.
|
||||
previousProposerBoostScore uint64 // previous proposer boosted root score.
|
||||
nodes []*Node // list of block nodes, each node is a representation of one block.
|
||||
nodesIndices map[[fieldparams.RootLength]byte]uint64 // the root of block node and the nodes index in the list.
|
||||
canonicalNodes map[[fieldparams.RootLength]byte]bool // the canonical block nodes.
|
||||
payloadIndices map[[fieldparams.RootLength]byte]uint64 // the payload hash of block node and the index in the list
|
||||
slashedIndices map[types.ValidatorIndex]bool // The list of equivocating validators
|
||||
originRoot [fieldparams.RootLength]byte // The genesis block root
|
||||
lastHeadRoot [fieldparams.RootLength]byte // The last cached head block root
|
||||
nodesLock sync.RWMutex
|
||||
proposerBoostLock sync.RWMutex
|
||||
checkpointsLock sync.RWMutex
|
||||
genesisTime uint64
|
||||
pruneThreshold uint64 // do not prune tree unless threshold is reached.
|
||||
justifiedCheckpoint *forkchoicetypes.Checkpoint // latest justified checkpoint in store.
|
||||
bestJustifiedCheckpoint *forkchoicetypes.Checkpoint // best justified checkpoint in store.
|
||||
unrealizedJustifiedCheckpoint *forkchoicetypes.Checkpoint // best justified checkpoint in store.
|
||||
unrealizedFinalizedCheckpoint *forkchoicetypes.Checkpoint // best justified checkpoint in store.
|
||||
prevJustifiedCheckpoint *forkchoicetypes.Checkpoint // previous justified checkpoint in store.
|
||||
finalizedCheckpoint *forkchoicetypes.Checkpoint // latest finalized checkpoint in store.
|
||||
proposerBoostRoot [fieldparams.RootLength]byte // latest block root that was boosted after being received in a timely manner.
|
||||
previousProposerBoostRoot [fieldparams.RootLength]byte // previous block root that was boosted after being received in a timely manner.
|
||||
previousProposerBoostScore uint64 // previous proposer boosted root score.
|
||||
nodes []*Node // list of block nodes, each node is a representation of one block.
|
||||
nodesIndices map[[fieldparams.RootLength]byte]uint64 // the root of block node and the nodes index in the list.
|
||||
canonicalNodes map[[fieldparams.RootLength]byte]bool // the canonical block nodes.
|
||||
payloadIndices map[[fieldparams.RootLength]byte]uint64 // the payload hash of block node and the index in the list
|
||||
slashedIndices map[types.ValidatorIndex]bool // The list of equivocating validators
|
||||
originRoot [fieldparams.RootLength]byte // The genesis block root
|
||||
lastHeadRoot [fieldparams.RootLength]byte // The last cached head block root
|
||||
nodesLock sync.RWMutex
|
||||
proposerBoostLock sync.RWMutex
|
||||
checkpointsLock sync.RWMutex
|
||||
genesisTime uint64
|
||||
}
|
||||
|
||||
// Node defines the individual block which includes its block parent, ancestor and how much weight accounted for it.
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
package protoarray
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/time"
|
||||
forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
)
|
||||
|
||||
func (s *Store) setUnrealizedJustifiedEpoch(root [32]byte, epoch types.Epoch) error {
|
||||
@@ -56,10 +65,47 @@ func (f *ForkChoice) UpdateUnrealizedCheckpoints() {
|
||||
node.justifiedEpoch = node.unrealizedJustifiedEpoch
|
||||
node.finalizedEpoch = node.unrealizedFinalizedEpoch
|
||||
if node.justifiedEpoch > f.store.justifiedCheckpoint.Epoch {
|
||||
f.store.justifiedCheckpoint.Epoch = node.justifiedEpoch
|
||||
if node.justifiedEpoch > f.store.bestJustifiedCheckpoint.Epoch {
|
||||
f.store.bestJustifiedCheckpoint = f.store.unrealizedJustifiedCheckpoint
|
||||
}
|
||||
f.store.justifiedCheckpoint = f.store.unrealizedJustifiedCheckpoint
|
||||
}
|
||||
if node.finalizedEpoch > f.store.finalizedCheckpoint.Epoch {
|
||||
f.store.finalizedCheckpoint.Epoch = node.finalizedEpoch
|
||||
f.store.justifiedCheckpoint = f.store.unrealizedJustifiedCheckpoint
|
||||
f.store.finalizedCheckpoint = f.store.unrealizedFinalizedCheckpoint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) pullTips(ctx context.Context, state state.BeaconState, node *Node, jc, fc *ethpb.Checkpoint) (*ethpb.Checkpoint, *ethpb.Checkpoint) {
|
||||
var uj, uf *ethpb.Checkpoint
|
||||
uj, uf, err := precompute.UnrealizedCheckpoints(ctx, state)
|
||||
if err != nil {
|
||||
log.WithError(err).Debug("could not compute unrealized checkpoints")
|
||||
uj, uf = jc, fc
|
||||
}
|
||||
node.unrealizedJustifiedEpoch, node.unrealizedFinalizedEpoch = uj.Epoch, uf.Epoch
|
||||
s.checkpointsLock.Lock()
|
||||
if uj.Epoch > s.unrealizedJustifiedCheckpoint.Epoch {
|
||||
s.unrealizedJustifiedCheckpoint = &forkchoicetypes.Checkpoint{
|
||||
Epoch: uj.Epoch, Root: bytesutil.ToBytes32(uj.Root),
|
||||
}
|
||||
}
|
||||
if uf.Epoch > s.unrealizedFinalizedCheckpoint.Epoch {
|
||||
s.unrealizedJustifiedCheckpoint = &forkchoicetypes.Checkpoint{
|
||||
Epoch: uj.Epoch, Root: bytesutil.ToBytes32(uj.Root),
|
||||
}
|
||||
s.unrealizedFinalizedCheckpoint = &forkchoicetypes.Checkpoint{
|
||||
Epoch: uf.Epoch, Root: bytesutil.ToBytes32(uf.Root),
|
||||
}
|
||||
}
|
||||
|
||||
currentSlot := slots.CurrentSlot(s.genesisTime)
|
||||
if time.CurrentEpoch(state) < slots.ToEpoch(currentSlot) {
|
||||
jc, fc = uj, uf
|
||||
node.justifiedEpoch = uj.Epoch
|
||||
node.finalizedEpoch = uf.Epoch
|
||||
}
|
||||
s.checkpointsLock.Unlock()
|
||||
return jc, fc
|
||||
}
|
||||
|
||||
@@ -147,6 +147,8 @@ func TestStore_NoDeadLock(t *testing.T) {
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'g'}, 2))
|
||||
require.NoError(t, f.store.setUnrealizedFinalizedEpoch([32]byte{'g'}, 1))
|
||||
f.store.unrealizedJustifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 2}
|
||||
f.store.unrealizedFinalizedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 1}
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 107, [32]byte{'h'}, [32]byte{'g'}, [32]byte{'H'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
@@ -236,6 +238,7 @@ func TestStore_ForkNextEpoch(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'d'}, 1))
|
||||
f.store.unrealizedJustifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 1}
|
||||
f.UpdateUnrealizedCheckpoints()
|
||||
headRoot, err = f.Head(ctx, []uint64{100})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -57,12 +57,7 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blk, err := vs.getBellatrixBeaconBlock(ctx, req)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not fetch Bellatrix beacon block: %v", err)
|
||||
}
|
||||
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Bellatrix{Bellatrix: blk}}, nil
|
||||
return vs.getBellatrixBeaconBlock(ctx, req)
|
||||
}
|
||||
|
||||
// GetBlock is called by a proposer during its assigned slot to request a block to sign
|
||||
@@ -141,6 +136,11 @@ func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk interfaces.
|
||||
return nil, fmt.Errorf("could not tree hash block: %v", err)
|
||||
}
|
||||
|
||||
blk, err = vs.unblindBuilderBlock(ctx, blk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Do not block proposal critical path with debug logging or block feed updates.
|
||||
defer func() {
|
||||
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf(
|
||||
|
||||
@@ -16,14 +16,23 @@ import (
|
||||
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/runtime/version"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (vs *Server) getBellatrixBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (*ethpb.BeaconBlockBellatrix, error) {
|
||||
func (vs *Server) getBellatrixBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (*ethpb.GenericBeaconBlock, error) {
|
||||
altairBlk, err := vs.buildAltairBeaconBlock(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
builderReady, b, err := vs.getAndBuildHeaderBlock(ctx, altairBlk)
|
||||
if err != nil {
|
||||
// In the event of an error, the node should fall back to default execution engine for building block.
|
||||
log.WithError(err).Error("Default back to local execution client")
|
||||
} else if builderReady {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
payload, err := vs.getExecutionPayload(ctx, req.Slot, altairBlk.ProposerIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -60,7 +69,7 @@ func (vs *Server) getBellatrixBeaconBlock(ctx context.Context, req *ethpb.BlockR
|
||||
return nil, fmt.Errorf("could not compute state root: %v", err)
|
||||
}
|
||||
blk.StateRoot = stateRoot
|
||||
return blk, nil
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Bellatrix{Bellatrix: blk}}, nil
|
||||
}
|
||||
|
||||
// This function retrieves the payload header given the slot number and the validator index.
|
||||
@@ -135,7 +144,7 @@ func (vs *Server) buildHeaderBlock(ctx context.Context, b *ethpb.BeaconBlockAlta
|
||||
// This function retrieves the full payload block using the input blind block. This input must be versioned as
|
||||
// bellatrix blind block. The output block will contain the full payload. The original header block
|
||||
// will be returned the block builder is not configured.
|
||||
func (vs *Server) getBuilderBlock(ctx context.Context, b interfaces.SignedBeaconBlock) (interfaces.SignedBeaconBlock, error) {
|
||||
func (vs *Server) unblindBuilderBlock(ctx context.Context, b interfaces.SignedBeaconBlock) (interfaces.SignedBeaconBlock, error) {
|
||||
if err := coreBlock.BeaconBlockIsNil(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -210,5 +219,64 @@ func (vs *Server) getBuilderBlock(ctx context.Context, b interfaces.SignedBeacon
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"blockHash": fmt.Sprintf("%#x", h.BlockHash),
|
||||
"feeRecipient": fmt.Sprintf("%#x", h.FeeRecipient),
|
||||
"gasUsed": h.GasUsed,
|
||||
"slot": b.Block().Slot(),
|
||||
"txs": len(payload.Transactions),
|
||||
}).Info("Retrieved full payload from builder")
|
||||
|
||||
return wb, nil
|
||||
}
|
||||
|
||||
// readyForBuilder returns true if builder is allowed to be used. Builder is only allowed to be use after the
|
||||
// first finalized checkpt has been execution-enabled.
|
||||
func (vs *Server) readyForBuilder(ctx context.Context) (bool, error) {
|
||||
cp := vs.FinalizationFetcher.FinalizedCheckpt()
|
||||
// Checkpoint root is zero means we are still at genesis epoch.
|
||||
if bytesutil.ToBytes32(cp.Root) == params.BeaconConfig().ZeroHash {
|
||||
return false, nil
|
||||
}
|
||||
b, err := vs.BeaconDB.Block(ctx, bytesutil.ToBytes32(cp.Root))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err = coreBlock.BeaconBlockIsNil(b); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return blocks.IsExecutionBlock(b.Block().Body())
|
||||
}
|
||||
|
||||
// Get and builder header block. Returns a boolean status, built block and error.
|
||||
// If the status is false that means builder the header block is disallowed.
|
||||
func (vs *Server) getAndBuildHeaderBlock(ctx context.Context, b *ethpb.BeaconBlockAltair) (bool, *ethpb.GenericBeaconBlock, error) {
|
||||
// No op. Builder is not defined. User did not specify a user URL. We should use local EE.
|
||||
if vs.BlockBuilder == nil || !vs.BlockBuilder.Configured() {
|
||||
return false, nil, nil
|
||||
}
|
||||
// Does the protocol allow for builder at this current moment. Builder is only allowed post merge after finalization.
|
||||
ready, err := vs.readyForBuilder(ctx)
|
||||
if err != nil {
|
||||
return false, nil, errors.Wrap(err, "could not determine if builder is ready")
|
||||
}
|
||||
if !ready {
|
||||
return false, nil, nil
|
||||
}
|
||||
h, err := vs.getPayloadHeader(ctx, b.Slot, b.ProposerIndex)
|
||||
if err != nil {
|
||||
return false, nil, errors.Wrap(err, "could not get payload header")
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"blockHash": fmt.Sprintf("%#x", h.BlockHash),
|
||||
"feeRecipient": fmt.Sprintf("%#x", h.FeeRecipient),
|
||||
"gasUsed": h.GasUsed,
|
||||
"slot": b.Slot,
|
||||
}).Info("Retrieved header from builder")
|
||||
gb, err := vs.buildHeaderBlock(ctx, b, h)
|
||||
if err != nil {
|
||||
return false, nil, errors.Wrap(err, "could not combine altair block with payload header")
|
||||
}
|
||||
return true, gb, nil
|
||||
}
|
||||
|
||||
@@ -3,13 +3,24 @@ package validator
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
blockchainTest "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
builderTest "github.com/prysmaticlabs/prysm/beacon-chain/builder/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/synccommittee"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
|
||||
mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
|
||||
@@ -19,6 +30,8 @@ import (
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"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 TestServer_buildHeaderBlock(t *testing.T) {
|
||||
@@ -250,7 +263,7 @@ func TestServer_getBuilderBlock(t *testing.T) {
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
vs := &Server{BlockBuilder: tc.mock}
|
||||
gotBlk, err := vs.getBuilderBlock(context.Background(), tc.blk)
|
||||
gotBlk, err := vs.unblindBuilderBlock(context.Background(), tc.blk)
|
||||
if err != nil {
|
||||
require.ErrorContains(t, tc.err, err)
|
||||
} else {
|
||||
@@ -259,3 +272,344 @@ func TestServer_getBuilderBlock(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_readyForBuilder(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
vs := &Server{BeaconDB: dbTest.SetupDB(t)}
|
||||
cs := &blockchainTest.ChainService{FinalizedCheckPoint: ðpb.Checkpoint{}} // Checkpoint root is zeros.
|
||||
vs.FinalizationFetcher = cs
|
||||
ready, err := vs.readyForBuilder(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, ready)
|
||||
|
||||
b := util.NewBeaconBlockBellatrix()
|
||||
wb, err := wrapper.WrappedSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
wbr, err := wb.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
b1 := util.NewBeaconBlockBellatrix()
|
||||
b1.Block.Body.ExecutionPayload.BlockNumber = 1 // Execution enabled.
|
||||
wb1, err := wrapper.WrappedSignedBeaconBlock(b1)
|
||||
require.NoError(t, err)
|
||||
wbr1, err := wb1.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, vs.BeaconDB.SaveBlock(ctx, wb))
|
||||
require.NoError(t, vs.BeaconDB.SaveBlock(ctx, wb1))
|
||||
|
||||
// Ready is false given finalized block does not have execution.
|
||||
cs = &blockchainTest.ChainService{FinalizedCheckPoint: ðpb.Checkpoint{Root: wbr[:]}}
|
||||
vs.FinalizationFetcher = cs
|
||||
ready, err = vs.readyForBuilder(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, ready)
|
||||
|
||||
// Ready is true given finalized block has execution.
|
||||
cs = &blockchainTest.ChainService{FinalizedCheckPoint: ðpb.Checkpoint{Root: wbr1[:]}}
|
||||
vs.FinalizationFetcher = cs
|
||||
ready, err = vs.readyForBuilder(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, ready)
|
||||
}
|
||||
|
||||
func TestServer_getAndBuildHeaderBlock(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
vs := &Server{}
|
||||
|
||||
// Nil builder
|
||||
ready, _, err := vs.getAndBuildHeaderBlock(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, ready)
|
||||
|
||||
// Not configured
|
||||
vs.BlockBuilder = &builderTest.MockBuilderService{}
|
||||
ready, _, err = vs.getAndBuildHeaderBlock(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, ready)
|
||||
|
||||
// Block is not ready
|
||||
vs.BlockBuilder = &builderTest.MockBuilderService{HasConfigured: true}
|
||||
vs.FinalizationFetcher = &blockchainTest.ChainService{FinalizedCheckPoint: ðpb.Checkpoint{}}
|
||||
ready, _, err = vs.getAndBuildHeaderBlock(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, ready)
|
||||
|
||||
// Failed to get header
|
||||
b1 := util.NewBeaconBlockBellatrix()
|
||||
b1.Block.Body.ExecutionPayload.BlockNumber = 1 // Execution enabled.
|
||||
wb1, err := wrapper.WrappedSignedBeaconBlock(b1)
|
||||
require.NoError(t, err)
|
||||
wbr1, err := wb1.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
vs.BeaconDB = dbTest.SetupDB(t)
|
||||
require.NoError(t, vs.BeaconDB.SaveBlock(ctx, wb1))
|
||||
vs.FinalizationFetcher = &blockchainTest.ChainService{FinalizedCheckPoint: ðpb.Checkpoint{Root: wbr1[:]}}
|
||||
vs.HeadFetcher = &blockchainTest.ChainService{Block: wb1}
|
||||
vs.BlockBuilder = &builderTest.MockBuilderService{HasConfigured: true, ErrGetHeader: errors.New("could not get payload")}
|
||||
ready, _, err = vs.getAndBuildHeaderBlock(ctx, ðpb.BeaconBlockAltair{})
|
||||
require.ErrorContains(t, "could not get payload", err)
|
||||
require.Equal(t, false, ready)
|
||||
|
||||
// Block built and validated!
|
||||
params.SetupTestConfigCleanup(t)
|
||||
params.OverrideBeaconConfig(params.MainnetConfig())
|
||||
beaconState, keys := util.DeterministicGenesisStateAltair(t, 16384)
|
||||
sCom, err := altair.NextSyncCommittee(context.Background(), beaconState)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconState.SetCurrentSyncCommittee(sCom))
|
||||
copiedState := beaconState.Copy()
|
||||
|
||||
b, err := util.GenerateFullBlockAltair(copiedState, keys, util.DefaultBlockGenConfig(), 1)
|
||||
require.NoError(t, err)
|
||||
r := bytesutil.ToBytes32(b.Block.ParentRoot)
|
||||
util.SaveBlock(t, ctx, vs.BeaconDB, b)
|
||||
require.NoError(t, vs.BeaconDB.SaveState(ctx, beaconState, r))
|
||||
|
||||
altairBlk, err := util.GenerateFullBlockAltair(copiedState, keys, util.DefaultBlockGenConfig(), 2)
|
||||
require.NoError(t, err)
|
||||
|
||||
h := &v1.ExecutionPayloadHeader{
|
||||
BlockNumber: 123,
|
||||
GasLimit: 456,
|
||||
GasUsed: 789,
|
||||
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),
|
||||
TransactionsRoot: make([]byte, fieldparams.RootLength),
|
||||
ExtraData: make([]byte, 0),
|
||||
}
|
||||
|
||||
vs.StateGen = stategen.New(vs.BeaconDB)
|
||||
vs.BlockBuilder = &builderTest.MockBuilderService{HasConfigured: true, Bid: ðpb.SignedBuilderBid{Message: ðpb.BuilderBid{Header: h}}}
|
||||
ready, builtBlk, err := vs.getAndBuildHeaderBlock(ctx, altairBlk.Block)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, ready)
|
||||
require.DeepEqual(t, h, builtBlk.GetBlindedBellatrix().Body.ExecutionPayloadHeader)
|
||||
}
|
||||
|
||||
func TestServer_GetBellatrixBeaconBlock_HappyCase(t *testing.T) {
|
||||
db := dbTest.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
terminalBlockHash := bytesutil.PadTo([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 32)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.MainnetConfig().Copy()
|
||||
cfg.BellatrixForkEpoch = 2
|
||||
cfg.AltairForkEpoch = 1
|
||||
cfg.TerminalBlockHash = common.BytesToHash(terminalBlockHash)
|
||||
cfg.TerminalBlockHashActivationEpoch = 2
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
beaconState, privKeys := util.DeterministicGenesisState(t, 64)
|
||||
stateRoot, err := beaconState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err, "Could not hash genesis state")
|
||||
|
||||
genesis := b.NewGenesisBlock(stateRoot[:])
|
||||
wsb, err := wrapper.WrappedSignedBeaconBlock(genesis)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, wsb), "Could not save genesis block")
|
||||
|
||||
parentRoot, err := genesis.Block.HashTreeRoot()
|
||||
require.NoError(t, err, "Could not get signing root")
|
||||
require.NoError(t, db.SaveState(ctx, beaconState, parentRoot), "Could not save genesis state")
|
||||
require.NoError(t, db.SaveHeadBlockRoot(ctx, parentRoot), "Could not save genesis state")
|
||||
|
||||
bellatrixSlot, err := slots.EpochStart(params.BeaconConfig().BellatrixForkEpoch)
|
||||
require.NoError(t, err)
|
||||
|
||||
emptyPayload := &v1.ExecutionPayload{
|
||||
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),
|
||||
}
|
||||
blk := ðpb.SignedBeaconBlockBellatrix{
|
||||
Block: ðpb.BeaconBlockBellatrix{
|
||||
Slot: bellatrixSlot + 1,
|
||||
ParentRoot: parentRoot[:],
|
||||
StateRoot: genesis.Block.StateRoot,
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
RandaoReveal: genesis.Block.Body.RandaoReveal,
|
||||
Graffiti: genesis.Block.Body.Graffiti,
|
||||
Eth1Data: genesis.Block.Body.Eth1Data,
|
||||
SyncAggregate: ðpb.SyncAggregate{SyncCommitteeBits: bitfield.NewBitvector512(), SyncCommitteeSignature: make([]byte, 96)},
|
||||
ExecutionPayload: emptyPayload,
|
||||
},
|
||||
},
|
||||
Signature: genesis.Signature,
|
||||
}
|
||||
|
||||
blkRoot, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, err, "Could not get signing root")
|
||||
require.NoError(t, db.SaveState(ctx, beaconState, blkRoot), "Could not save genesis state")
|
||||
require.NoError(t, db.SaveHeadBlockRoot(ctx, blkRoot), "Could not save genesis state")
|
||||
|
||||
proposerServer := &Server{
|
||||
HeadFetcher: &blockchainTest.ChainService{State: beaconState, Root: parentRoot[:], Optimistic: false},
|
||||
TimeFetcher: &blockchainTest.ChainService{Genesis: time.Now()},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
BlockReceiver: &blockchainTest.ChainService{},
|
||||
HeadUpdater: &blockchainTest.ChainService{},
|
||||
ChainStartFetcher: &mockPOW.POWChain{},
|
||||
Eth1InfoFetcher: &mockPOW.POWChain{},
|
||||
MockEth1Votes: true,
|
||||
AttPool: attestations.NewPool(),
|
||||
SlashingsPool: slashings.NewPool(),
|
||||
ExitPool: voluntaryexits.NewPool(),
|
||||
StateGen: stategen.New(db),
|
||||
SyncCommitteePool: synccommittee.NewStore(),
|
||||
ExecutionEngineCaller: &mockPOW.EngineClient{
|
||||
PayloadIDBytes: &v1.PayloadIDBytes{1},
|
||||
ExecutionPayload: emptyPayload,
|
||||
},
|
||||
BeaconDB: db,
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
BlockBuilder: &builderTest.MockBuilderService{},
|
||||
}
|
||||
proposerServer.ProposerSlotIndexCache.SetProposerAndPayloadIDs(65, 40, [8]byte{'a'})
|
||||
|
||||
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
|
||||
require.NoError(t, err)
|
||||
|
||||
block, err := proposerServer.getBellatrixBeaconBlock(ctx, ðpb.BlockRequest{
|
||||
Slot: bellatrixSlot + 1,
|
||||
RandaoReveal: randaoReveal,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
bellatrixBlk, ok := block.GetBlock().(*ethpb.GenericBeaconBlock_Bellatrix)
|
||||
require.Equal(t, true, ok)
|
||||
require.LogsContain(t, hook, "Computed state root")
|
||||
require.DeepEqual(t, emptyPayload, bellatrixBlk.Bellatrix.Body.ExecutionPayload) // Payload should equal.
|
||||
}
|
||||
|
||||
func TestServer_GetBellatrixBeaconBlock_BuilderCase(t *testing.T) {
|
||||
db := dbTest.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.MainnetConfig().Copy()
|
||||
cfg.BellatrixForkEpoch = 2
|
||||
cfg.AltairForkEpoch = 1
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
beaconState, privKeys := util.DeterministicGenesisState(t, 64)
|
||||
stateRoot, err := beaconState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err, "Could not hash genesis state")
|
||||
|
||||
genesis := b.NewGenesisBlock(stateRoot[:])
|
||||
wsb, err := wrapper.WrappedSignedBeaconBlock(genesis)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, wsb), "Could not save genesis block")
|
||||
|
||||
parentRoot, err := genesis.Block.HashTreeRoot()
|
||||
require.NoError(t, err, "Could not get signing root")
|
||||
require.NoError(t, db.SaveState(ctx, beaconState, parentRoot), "Could not save genesis state")
|
||||
require.NoError(t, db.SaveHeadBlockRoot(ctx, parentRoot), "Could not save genesis state")
|
||||
|
||||
bellatrixSlot, err := slots.EpochStart(params.BeaconConfig().BellatrixForkEpoch)
|
||||
require.NoError(t, err)
|
||||
|
||||
emptyPayload := &v1.ExecutionPayload{
|
||||
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),
|
||||
}
|
||||
blk := ðpb.SignedBeaconBlockBellatrix{
|
||||
Block: ðpb.BeaconBlockBellatrix{
|
||||
Slot: bellatrixSlot + 1,
|
||||
ParentRoot: parentRoot[:],
|
||||
StateRoot: genesis.Block.StateRoot,
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
RandaoReveal: genesis.Block.Body.RandaoReveal,
|
||||
Graffiti: genesis.Block.Body.Graffiti,
|
||||
Eth1Data: genesis.Block.Body.Eth1Data,
|
||||
SyncAggregate: ðpb.SyncAggregate{SyncCommitteeBits: bitfield.NewBitvector512(), SyncCommitteeSignature: make([]byte, 96)},
|
||||
ExecutionPayload: emptyPayload,
|
||||
},
|
||||
},
|
||||
Signature: genesis.Signature,
|
||||
}
|
||||
|
||||
blkRoot, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, err, "Could not get signing root")
|
||||
require.NoError(t, db.SaveState(ctx, beaconState, blkRoot), "Could not save genesis state")
|
||||
require.NoError(t, db.SaveHeadBlockRoot(ctx, blkRoot), "Could not save genesis state")
|
||||
|
||||
b1 := util.NewBeaconBlockBellatrix()
|
||||
b1.Block.Body.ExecutionPayload.BlockNumber = 1 // Execution enabled.
|
||||
wb1, err := wrapper.WrappedSignedBeaconBlock(b1)
|
||||
require.NoError(t, err)
|
||||
wbr1, err := wb1.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, wb1))
|
||||
h := &v1.ExecutionPayloadHeader{
|
||||
BlockNumber: 123,
|
||||
GasLimit: 456,
|
||||
GasUsed: 789,
|
||||
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),
|
||||
TransactionsRoot: make([]byte, fieldparams.RootLength),
|
||||
ExtraData: make([]byte, 0),
|
||||
}
|
||||
|
||||
proposerServer := &Server{
|
||||
FinalizationFetcher: &blockchainTest.ChainService{FinalizedCheckPoint: ðpb.Checkpoint{Root: wbr1[:]}},
|
||||
HeadFetcher: &blockchainTest.ChainService{State: beaconState, Root: parentRoot[:], Optimistic: false, Block: wb1},
|
||||
TimeFetcher: &blockchainTest.ChainService{Genesis: time.Now()},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
BlockReceiver: &blockchainTest.ChainService{},
|
||||
HeadUpdater: &blockchainTest.ChainService{},
|
||||
ChainStartFetcher: &mockPOW.POWChain{},
|
||||
Eth1InfoFetcher: &mockPOW.POWChain{},
|
||||
MockEth1Votes: true,
|
||||
AttPool: attestations.NewPool(),
|
||||
SlashingsPool: slashings.NewPool(),
|
||||
ExitPool: voluntaryexits.NewPool(),
|
||||
StateGen: stategen.New(db),
|
||||
SyncCommitteePool: synccommittee.NewStore(),
|
||||
ExecutionEngineCaller: &mockPOW.EngineClient{
|
||||
PayloadIDBytes: &v1.PayloadIDBytes{1},
|
||||
ExecutionPayload: emptyPayload,
|
||||
},
|
||||
BeaconDB: db,
|
||||
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true, Bid: ðpb.SignedBuilderBid{Message: ðpb.BuilderBid{Header: h}}},
|
||||
}
|
||||
|
||||
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
|
||||
require.NoError(t, err)
|
||||
|
||||
block, err := proposerServer.getBellatrixBeaconBlock(ctx, ðpb.BlockRequest{
|
||||
Slot: bellatrixSlot + 1,
|
||||
RandaoReveal: randaoReveal,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
bellatrixBlk, ok := block.GetBlock().(*ethpb.GenericBeaconBlock_BlindedBellatrix)
|
||||
require.Equal(t, true, ok)
|
||||
require.LogsContain(t, hook, "Computed state root")
|
||||
require.DeepEqual(t, h, bellatrixBlk.BlindedBellatrix.Body.ExecutionPayloadHeader) // Payload header should equal.
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ func (b *BeaconState) UnrealizedCheckpointBalances() (uint64, uint64, uint64, er
|
||||
return 0, 0, 0, errNotSupported("UnrealizedCheckpointBalances", b.version)
|
||||
}
|
||||
|
||||
currentEpoch := time.CurrentEpoch(b)
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
@@ -55,7 +56,6 @@ func (b *BeaconState) UnrealizedCheckpointBalances() (uint64, uint64, uint64, er
|
||||
return 0, 0, 0, ErrNilParticipation
|
||||
}
|
||||
|
||||
currentEpoch := time.CurrentEpoch(b)
|
||||
return stateutil.UnrealizedCheckpointBalances(cp, pp, b.validators, currentEpoch)
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,11 @@ import (
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// UnrealizedCheckpointBalances returns the total current active balance, the
|
||||
// total previous epoch correctly attested for target balance, and the total
|
||||
// current epoch correctly attested for target balance. It takes the current and
|
||||
// previous epoch participation bits as parameters so implicitly only works for
|
||||
// beacon states post-Altair.
|
||||
func UnrealizedCheckpointBalances(cp, pp []byte, validators []*ethpb.Validator, currentEpoch types.Epoch) (uint64, uint64, uint64, error) {
|
||||
targetIdx := params.BeaconConfig().TimelyTargetFlagIndex
|
||||
activeBalance := uint64(0)
|
||||
|
||||
@@ -42,6 +42,7 @@ func (b *BeaconState) UnrealizedCheckpointBalances() (uint64, uint64, uint64, er
|
||||
if !b.hasInnerState() {
|
||||
return 0, 0, 0, ErrNilInnerState
|
||||
}
|
||||
currentEpoch := time.CurrentEpoch(b)
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
@@ -50,7 +51,6 @@ func (b *BeaconState) UnrealizedCheckpointBalances() (uint64, uint64, uint64, er
|
||||
if cp == nil || pp == nil {
|
||||
return 0, 0, 0, ErrNilParticipation
|
||||
}
|
||||
currentEpoch := time.CurrentEpoch(b)
|
||||
return stateutil.UnrealizedCheckpointBalances(cp, pp, b.state.Validators, currentEpoch)
|
||||
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ func (b *BeaconState) UnrealizedCheckpointBalances() (uint64, uint64, uint64, er
|
||||
if !b.hasInnerState() {
|
||||
return 0, 0, 0, ErrNilInnerState
|
||||
}
|
||||
currentEpoch := time.CurrentEpoch(b)
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
@@ -50,7 +51,6 @@ func (b *BeaconState) UnrealizedCheckpointBalances() (uint64, uint64, uint64, er
|
||||
if cp == nil || pp == nil {
|
||||
return 0, 0, 0, ErrNilParticipation
|
||||
}
|
||||
currentEpoch := time.CurrentEpoch(b)
|
||||
return stateutil.UnrealizedCheckpointBalances(cp, pp, b.state.Validators, currentEpoch)
|
||||
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ type Flags struct {
|
||||
EnableSlashingProtectionPruning bool
|
||||
|
||||
EnableNativeState bool // EnableNativeState defines whether the beacon state will be represented as a pure Go struct or a Go struct that wraps a proto struct.
|
||||
PullTips bool // Experimental disable of boundary checks
|
||||
EnableVectorizedHTR bool // EnableVectorizedHTR specifies whether the beacon state will use the optimized sha256 routines.
|
||||
EnableForkChoiceDoublyLinkedTree bool // EnableForkChoiceDoublyLinkedTree specifies whether fork choice store will use a doubly linked tree.
|
||||
EnableBatchGossipAggregation bool // EnableBatchGossipAggregation specifies whether to further aggregate our gossip batches before verifying them.
|
||||
@@ -211,6 +212,10 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
|
||||
logDisabled(disableNativeState)
|
||||
cfg.EnableNativeState = false
|
||||
}
|
||||
if ctx.Bool(pullTips.Name) {
|
||||
logEnabled(pullTips)
|
||||
cfg.PullTips = true
|
||||
}
|
||||
if ctx.Bool(enableVecHTR.Name) {
|
||||
logEnabled(enableVecHTR)
|
||||
cfg.EnableVectorizedHTR = true
|
||||
|
||||
@@ -105,6 +105,12 @@ var (
|
||||
Name: "disable-native-state",
|
||||
Usage: "Disables representing the beacon state as a pure Go struct.",
|
||||
}
|
||||
|
||||
pullTips = &cli.BoolFlag{
|
||||
Name: "experimental-disable-boundary-checks",
|
||||
Usage: "Experimental disable of boundary checks, useful for debugging, may cause bad votes.",
|
||||
}
|
||||
|
||||
enableVecHTR = &cli.BoolFlag{
|
||||
Name: "enable-vectorized-htr",
|
||||
Usage: "Enables new go sha256 library which utilizes optimized routines for merkle trees",
|
||||
@@ -163,6 +169,7 @@ var BeaconChainFlags = append(deprecatedFlags, []cli.Flag{
|
||||
enableSlasherFlag,
|
||||
enableHistoricalSpaceRepresentation,
|
||||
disableNativeState,
|
||||
pullTips,
|
||||
enableVecHTR,
|
||||
enableForkChoiceDoublyLinkedTree,
|
||||
enableGossipBatchAggregation,
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
)
|
||||
|
||||
// ProposerSettingsPayload is the struct representation of the JSON or YAML payload set in the validator through the CLI.
|
||||
// ProposeConfig is the map of validator address to fee recipient options all in hex format.
|
||||
// ProposerConfig is the map of validator address to fee recipient options all in hex format.
|
||||
// DefaultConfig is the default fee recipient address for all validators unless otherwise specified in the propose config.required.
|
||||
type ProposerSettingsPayload struct {
|
||||
ProposeConfig map[string]*ProposerOptionPayload `json:"proposer_config" yaml:"proposer_config"`
|
||||
DefaultConfig *ProposerOptionPayload `json:"default_config" yaml:"default_config"`
|
||||
ProposerConfig map[string]*ProposerOptionPayload `json:"proposer_config" yaml:"proposer_config"`
|
||||
DefaultConfig *ProposerOptionPayload `json:"default_config" yaml:"default_config"`
|
||||
}
|
||||
|
||||
// ProposerOptionPayload is the struct representation of the JSON config file set in the validator through the CLI.
|
||||
|
||||
@@ -24,7 +24,9 @@ go_library(
|
||||
"//cmd/beacon-chain/flags:go_default_library",
|
||||
"//cmd/validator/flags:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//config/validator/service:go_default_library",
|
||||
"//contracts/deposit:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
@@ -38,6 +40,7 @@ go_library(
|
||||
"//validator/keymanager:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//accounts/abi/bind:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//accounts/keystore:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
|
||||
@@ -27,6 +27,10 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
EthAddress = "0x878705ba3f8bc32fcf7f4caa1a35e72af65cf766"
|
||||
)
|
||||
|
||||
// Miner represents an ETH1 node which mines blocks.
|
||||
type Miner struct {
|
||||
e2etypes.ComponentRunner
|
||||
@@ -127,9 +131,9 @@ func (m *Miner) Start(ctx context.Context) error {
|
||||
"--ipcdisable",
|
||||
"--verbosity=4",
|
||||
"--mine",
|
||||
"--unlock=0x878705ba3f8bc32fcf7f4caa1a35e72af65cf766",
|
||||
fmt.Sprintf("--unlock=%s", EthAddress),
|
||||
"--allow-insecure-unlock",
|
||||
"--txpool.locals=0x878705ba3f8bc32fcf7f4caa1a35e72af65cf766",
|
||||
fmt.Sprintf("--txpool.locals=%s", EthAddress),
|
||||
fmt.Sprintf("--password=%s", eth1Path+"/keystore/"+minerPasswordFile),
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ func (node *Node) Start(ctx context.Context) error {
|
||||
"--ws.origins=\"*\"",
|
||||
"--ipcdisable",
|
||||
"--verbosity=4",
|
||||
"--txpool.locals=0x878705ba3f8bc32fcf7f4caa1a35e72af65cf766",
|
||||
fmt.Sprintf("--txpool.locals=%s", EthAddress),
|
||||
}
|
||||
// If we are testing sync, geth needs to be run via full sync as snap sync does not
|
||||
// work in our setup.
|
||||
|
||||
@@ -3,17 +3,20 @@ package components
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
@@ -21,9 +24,12 @@ import (
|
||||
cmdshared "github.com/prysmaticlabs/prysm/cmd"
|
||||
"github.com/prysmaticlabs/prysm/cmd/validator/flags"
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
validator_service_config "github.com/prysmaticlabs/prysm/config/validator/service"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/io/file"
|
||||
"github.com/prysmaticlabs/prysm/runtime/interop"
|
||||
"github.com/prysmaticlabs/prysm/testing/endtoend/components/eth1"
|
||||
"github.com/prysmaticlabs/prysm/testing/endtoend/helpers"
|
||||
@@ -33,6 +39,7 @@ import (
|
||||
)
|
||||
|
||||
const depositGasLimit = 4000000
|
||||
const DefaultFeeRecipientAddress = "0x099FB65722e7b2455043bfebF6177f1D2E9738d9"
|
||||
|
||||
var _ e2etypes.ComponentRunner = (*ValidatorNode)(nil)
|
||||
var _ e2etypes.ComponentRunner = (*ValidatorNodeSet)(nil)
|
||||
@@ -170,6 +177,7 @@ func NewValidatorNode(config *e2etypes.E2EConfig, validatorNum, index, offset in
|
||||
|
||||
// Start starts a validator client.
|
||||
func (v *ValidatorNode) Start(ctx context.Context) error {
|
||||
validatorHexPubKeys := make([]string, 0)
|
||||
var pkg, target string
|
||||
if v.config.UsePrysmShValidator {
|
||||
pkg = ""
|
||||
@@ -198,6 +206,15 @@ func (v *ValidatorNode) Start(ctx context.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, pubs, err := interop.DeterministicallyGenerateKeys(uint64(offset), uint64(validatorNum))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, pub := range pubs {
|
||||
validatorHexPubKeys = append(validatorHexPubKeys, hexutil.Encode(pub.Marshal()))
|
||||
}
|
||||
|
||||
args := []string{
|
||||
fmt.Sprintf("--%s=%s/eth2-val-%d", cmdshared.DataDirFlag.Name, e2e.TestParams.TestPath, index),
|
||||
fmt.Sprintf("--%s=%s", cmdshared.LogFileName.Name, file.Name()),
|
||||
@@ -219,20 +236,22 @@ func (v *ValidatorNode) Start(ctx context.Context) error {
|
||||
args = append(args, fmt.Sprintf("--%s=http://localhost:%d", flags.Web3SignerURLFlag.Name, Web3RemoteSignerPort))
|
||||
// Write the pubkeys as comma seperated hex strings with 0x prefix.
|
||||
// See: https://docs.teku.consensys.net/en/latest/HowTo/External-Signer/Use-External-Signer/
|
||||
_, pubs, err := interop.DeterministicallyGenerateKeys(uint64(offset), uint64(validatorNum))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var hexPubs []string
|
||||
for _, pub := range pubs {
|
||||
hexPubs = append(hexPubs, hexutil.Encode(pub.Marshal()))
|
||||
}
|
||||
args = append(args, fmt.Sprintf("--%s=%s", flags.Web3SignerPublicValidatorKeysFlag.Name, strings.Join(hexPubs, ",")))
|
||||
args = append(args, fmt.Sprintf("--%s=%s", flags.Web3SignerPublicValidatorKeysFlag.Name, strings.Join(validatorHexPubKeys, ",")))
|
||||
} else {
|
||||
// When not using remote key signer, use interop keys.
|
||||
args = append(args,
|
||||
fmt.Sprintf("--%s=%d", flags.InteropNumValidators.Name, validatorNum),
|
||||
fmt.Sprintf("--%s=%d", flags.InteropStartIndex.Name, offset))
|
||||
fmt.Sprintf("--%s=%d", flags.InteropStartIndex.Name, offset),
|
||||
)
|
||||
}
|
||||
//TODO: web3signer does not support validator registration signing currently, move this when support is there.
|
||||
//TODO: current version of prysmsh still uses wrong flag name.
|
||||
if !v.config.UsePrysmShValidator && !v.config.UseWeb3RemoteSigner {
|
||||
proposerSettingsPathPath, err := createProposerSettingsPath(validatorHexPubKeys, index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args = append(args, fmt.Sprintf("--%s=%s", flags.ProposerSettingsFlag.Name, proposerSettingsPathPath))
|
||||
}
|
||||
args = append(args, config.ValidatorFlags...)
|
||||
|
||||
@@ -378,3 +397,50 @@ func sendDeposits(web3 *ethclient.Client, keystoreBytes []byte, num, offset int,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createProposerSettingsPath(pubkeys []string, validatorIndex int) (string, error) {
|
||||
testNetDir := e2e.TestParams.TestPath + fmt.Sprintf("/proposer-settings/validator_%d", validatorIndex)
|
||||
configPath := filepath.Join(testNetDir, "config.json")
|
||||
if len(pubkeys) == 0 {
|
||||
return "", errors.New("number of validators must be greater than 0")
|
||||
}
|
||||
var proposerSettingsPayload validator_service_config.ProposerSettingsPayload
|
||||
if len(pubkeys) == 1 {
|
||||
proposerSettingsPayload = validator_service_config.ProposerSettingsPayload{
|
||||
DefaultConfig: &validator_service_config.ProposerOptionPayload{
|
||||
FeeRecipient: DefaultFeeRecipientAddress,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
config := make(map[string]*validator_service_config.ProposerOptionPayload)
|
||||
|
||||
for i, pubkey := range pubkeys {
|
||||
// Create an account
|
||||
byteval, err := hexutil.Decode(pubkey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
deterministicFeeRecipient := common.HexToAddress(hexutil.Encode(byteval[:fieldparams.FeeRecipientLength])).Hex()
|
||||
config[pubkeys[i]] = &validator_service_config.ProposerOptionPayload{
|
||||
FeeRecipient: deterministicFeeRecipient,
|
||||
}
|
||||
}
|
||||
proposerSettingsPayload = validator_service_config.ProposerSettingsPayload{
|
||||
ProposerConfig: config,
|
||||
DefaultConfig: &validator_service_config.ProposerOptionPayload{
|
||||
FeeRecipient: DefaultFeeRecipientAddress,
|
||||
},
|
||||
}
|
||||
}
|
||||
jsonBytes, err := json.Marshal(proposerSettingsPayload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := file.MkdirAll(testNetDir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := file.WriteFile(configPath, jsonBytes); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return configPath, nil
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@ func e2eMinimal(t *testing.T, cfgo ...types.E2EConfigOpt) *testRunner {
|
||||
for _, o := range cfgo {
|
||||
o(testConfig)
|
||||
}
|
||||
//TODO: web3signer does not currently support validator registration signing so evaluator will break code
|
||||
if !testConfig.UseWeb3RemoteSigner {
|
||||
testConfig.Evaluators = append(testConfig.Evaluators, ev.FeeRecipientIsPresent)
|
||||
}
|
||||
|
||||
return newTestRunner(t, testConfig)
|
||||
}
|
||||
@@ -158,7 +162,10 @@ func e2eMainnet(t *testing.T, usePrysmSh, useMultiClient bool, cfgo ...types.E2E
|
||||
for _, o := range cfgo {
|
||||
o(testConfig)
|
||||
}
|
||||
|
||||
//TODO: web3signer does not currently support validator registration signing so evaluator will break code
|
||||
if !testConfig.UseWeb3RemoteSigner {
|
||||
testConfig.Evaluators = append(testConfig.Evaluators, ev.FeeRecipientIsPresent)
|
||||
}
|
||||
return newTestRunner(t, testConfig)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ go_library(
|
||||
"api_middleware.go",
|
||||
"data.go",
|
||||
"execution_engine.go",
|
||||
"fee_recipient.go",
|
||||
"finality.go",
|
||||
"fork.go",
|
||||
"metrics.go",
|
||||
@@ -37,15 +38,21 @@ go_library(
|
||||
"//proto/eth/v1:go_default_library",
|
||||
"//proto/eth/v2:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//runtime/interop:go_default_library",
|
||||
"//testing/endtoend/components:go_default_library",
|
||||
"//testing/endtoend/helpers:go_default_library",
|
||||
"//testing/endtoend/params:go_default_library",
|
||||
"//testing/endtoend/policies:go_default_library",
|
||||
"//testing/endtoend/types:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_bazel_rules_go//proto/wkt:empty_go_proto",
|
||||
"@org_golang_google_grpc//:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
|
||||
|
||||
123
testing/endtoend/evaluators/fee_recipient.go
Normal file
123
testing/endtoend/evaluators/fee_recipient.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package evaluators
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/pkg/errors"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/runtime/interop"
|
||||
"github.com/prysmaticlabs/prysm/testing/endtoend/components"
|
||||
"github.com/prysmaticlabs/prysm/testing/endtoend/helpers"
|
||||
e2e "github.com/prysmaticlabs/prysm/testing/endtoend/params"
|
||||
"github.com/prysmaticlabs/prysm/testing/endtoend/policies"
|
||||
"github.com/prysmaticlabs/prysm/testing/endtoend/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
var FeeRecipientIsPresent = types.Evaluator{
|
||||
Name: "Fee_Recipient_Is_Present_%d",
|
||||
Policy: policies.AfterNthEpoch(helpers.BellatrixE2EForkEpoch),
|
||||
Evaluation: feeRecipientIsPresent,
|
||||
}
|
||||
|
||||
func feeRecipientIsPresent(conns ...*grpc.ClientConn) error {
|
||||
conn := conns[0]
|
||||
client := ethpb.NewBeaconChainClient(conn)
|
||||
chainHead, err := client.GetChainHead(context.Background(), &emptypb.Empty{})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get chain head")
|
||||
}
|
||||
req := ðpb.ListBlocksRequest{QueryFilter: ðpb.ListBlocksRequest_Epoch{Epoch: chainHead.HeadEpoch.Sub(1)}}
|
||||
blks, err := client.ListBeaconBlocks(context.Background(), req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to list blocks")
|
||||
}
|
||||
|
||||
rpcclient, err := rpc.DialHTTP(fmt.Sprintf("http://127.0.0.1:%d", e2e.TestParams.Ports.Eth1RPCPort))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rpcclient.Close()
|
||||
web3 := ethclient.NewClient(rpcclient)
|
||||
ctx := context.Background()
|
||||
|
||||
for _, ctr := range blks.BlockContainers {
|
||||
switch ctr.Block.(type) {
|
||||
case *ethpb.BeaconBlockContainer_BellatrixBlock:
|
||||
var account common.Address
|
||||
|
||||
fr := ctr.GetBellatrixBlock().Block.Body.ExecutionPayload.FeeRecipient
|
||||
if len(fr) != 0 && hexutil.Encode(fr) != params.BeaconConfig().EthBurnAddressHex {
|
||||
account = common.BytesToAddress(fr)
|
||||
} else {
|
||||
return errors.New("fee recipient is not set")
|
||||
}
|
||||
validatorRequest := ðpb.GetValidatorRequest{
|
||||
QueryFilter: ðpb.GetValidatorRequest_Index{
|
||||
Index: ctr.GetBellatrixBlock().Block.ProposerIndex,
|
||||
},
|
||||
}
|
||||
validator, err := client.GetValidator(context.Background(), validatorRequest)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get validators")
|
||||
}
|
||||
publickey := validator.GetPublicKey()
|
||||
isDeterministicKey := false
|
||||
validatorNum := int(params.BeaconConfig().MinGenesisActiveValidatorCount)
|
||||
_, pubs, err := interop.DeterministicallyGenerateKeys(uint64(0), uint64(validatorNum+int(e2e.DepositCount))) // matches validator start in validator component + validators used for deposits
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, pub := range pubs {
|
||||
if hexutil.Encode(publickey) == hexutil.Encode(pub.Marshal()) {
|
||||
isDeterministicKey = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// calculate deterministic fee recipient using first 20 bytes of public key
|
||||
deterministicFeeRecipient := common.HexToAddress(hexutil.Encode(publickey[:fieldparams.FeeRecipientLength])).Hex()
|
||||
if isDeterministicKey && deterministicFeeRecipient != account.Hex() {
|
||||
return fmt.Errorf("publickey %s, fee recipient %s does not match the proposer settings fee recipient %s",
|
||||
hexutil.Encode(publickey), account.Hex(), deterministicFeeRecipient)
|
||||
}
|
||||
if !isDeterministicKey && components.DefaultFeeRecipientAddress != account.Hex() {
|
||||
return fmt.Errorf("publickey %s, fee recipient %s does not match the default fee recipient %s",
|
||||
hexutil.Encode(publickey), account.Hex(), components.DefaultFeeRecipientAddress)
|
||||
}
|
||||
currentBlock, err := web3.BlockByHash(ctx, common.BytesToHash(ctr.GetBellatrixBlock().GetBlock().GetBody().GetExecutionPayload().BlockHash))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
accountBalance, err := web3.BalanceAt(ctx, account, currentBlock.Number())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
previousBlock, err := web3.BlockByHash(ctx, common.BytesToHash(ctr.GetBellatrixBlock().GetBlock().GetBody().GetExecutionPayload().ParentHash))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prevAccountBalance, err := web3.BalanceAt(ctx, account, previousBlock.Number())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if currentBlock.GasUsed() > 0 && accountBalance.Uint64() <= prevAccountBalance.Uint64() {
|
||||
log.Infof("current block num: %d , previous block num: %d , account balance: %d, pre account balance %d", currentBlock.Number(), previousBlock.Number(), accountBalance, prevAccountBalance)
|
||||
return errors.Errorf("account balance didn't change after applying fee recipient for account: %s", account.Hex())
|
||||
} else {
|
||||
log.Infof("current gas used: %v current account balance %v ,increased from previous account balance %v ", currentBlock.GasUsed(), accountBalance, prevAccountBalance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -31,12 +31,12 @@ func startChainService(t testing.TB, st state.BeaconState, block interfaces.Sign
|
||||
r, err := block.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, r))
|
||||
require.NoError(t, db.SaveState(ctx, st, r))
|
||||
|
||||
cp := ðpb.Checkpoint{
|
||||
Epoch: coreTime.CurrentEpoch(st),
|
||||
Root: r[:],
|
||||
}
|
||||
|
||||
require.NoError(t, db.SaveState(ctx, st, r))
|
||||
require.NoError(t, db.SaveJustifiedCheckpoint(ctx, cp))
|
||||
require.NoError(t, db.SaveFinalizedCheckpoint(ctx, cp))
|
||||
attPool, err := attestations.NewService(ctx, &attestations.Config{
|
||||
|
||||
@@ -489,7 +489,7 @@ func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSett
|
||||
if cliCtx.IsSet(flags.SuggestedFeeRecipientFlag.Name) {
|
||||
suggestedFee := cliCtx.String(flags.SuggestedFeeRecipientFlag.Name)
|
||||
fileConfig = &validatorServiceConfig.ProposerSettingsPayload{
|
||||
ProposeConfig: nil,
|
||||
ProposerConfig: nil,
|
||||
DefaultConfig: &validatorServiceConfig.ProposerOptionPayload{
|
||||
FeeRecipient: suggestedFee,
|
||||
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
|
||||
@@ -538,9 +538,9 @@ func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSett
|
||||
GasLimit: reviewGasLimit(fileConfig.DefaultConfig.GasLimit),
|
||||
}
|
||||
|
||||
if fileConfig.ProposeConfig != nil {
|
||||
if fileConfig.ProposerConfig != nil {
|
||||
vpSettings.ProposeConfig = make(map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption)
|
||||
for key, option := range fileConfig.ProposeConfig {
|
||||
for key, option := range fileConfig.ProposerConfig {
|
||||
decodedKey, err := hexutil.Decode(key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not decode public key %s", key)
|
||||
|
||||
Reference in New Issue
Block a user