mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 13:58:09 -05:00
Compare commits
25 Commits
opt-store-
...
fc-playgro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f446151d1a | ||
|
|
060d646fcf | ||
|
|
956e7a7563 | ||
|
|
acd7e62cfb | ||
|
|
165f0667f0 | ||
|
|
49826ebe28 | ||
|
|
3ad6abd9c0 | ||
|
|
c61f54176d | ||
|
|
0165351db3 | ||
|
|
8f49167117 | ||
|
|
b4c27f64de | ||
|
|
e4ac5e74b7 | ||
|
|
0b9b635646 | ||
|
|
c8c1d04c07 | ||
|
|
601987faf2 | ||
|
|
a060d765b3 | ||
|
|
5550334956 | ||
|
|
002253bba3 | ||
|
|
c055642f79 | ||
|
|
c7d64c03ac | ||
|
|
131fb43ba7 | ||
|
|
cf0bd633f0 | ||
|
|
df8da80db8 | ||
|
|
dc527a3c80 | ||
|
|
94f80bd208 |
2
.github/actions/gomodtidy/entrypoint.sh
vendored
2
.github/actions/gomodtidy/entrypoint.sh
vendored
@@ -7,7 +7,7 @@ cd $GITHUB_WORKSPACE
|
||||
cp go.mod go.mod.orig
|
||||
cp go.sum go.sum.orig
|
||||
|
||||
go mod tidy
|
||||
go mod tidy -compat=1.17
|
||||
|
||||
echo "Checking go.mod and go.sum:"
|
||||
checks=0
|
||||
|
||||
@@ -288,11 +288,11 @@ func TestService_ChainHeads_ProtoArray(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0,
|
||||
params.BeaconConfig().ZeroHash)}}
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
|
||||
roots, slots := c.ChainHeads()
|
||||
require.DeepEqual(t, [][32]byte{{'c'}, {'d'}, {'e'}}, roots)
|
||||
@@ -302,11 +302,11 @@ func TestService_ChainHeads_ProtoArray(t *testing.T) {
|
||||
func TestService_ChainHeads_DoublyLinkedTree(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New(0, 0)}}
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
|
||||
roots, slots := c.ChainHeads()
|
||||
require.Equal(t, 3, len(roots))
|
||||
@@ -384,8 +384,8 @@ func TestService_IsOptimistic_ProtoArray(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0, [32]byte{})}, head: &head{slot: 101, root: [32]byte{'b'}}}
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
|
||||
opt, err := c.IsOptimistic(ctx)
|
||||
require.NoError(t, err)
|
||||
@@ -400,8 +400,8 @@ func TestService_IsOptimistic_DoublyLinkedTree(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New(0, 0)}, head: &head{slot: 101, root: [32]byte{'b'}}}
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
|
||||
opt, err := c.IsOptimistic(ctx)
|
||||
require.NoError(t, err)
|
||||
@@ -419,8 +419,8 @@ func TestService_IsOptimisticBeforeBellatrix(t *testing.T) {
|
||||
func TestService_IsOptimisticForRoot_ProtoArray(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0, [32]byte{})}, head: &head{slot: 101, root: [32]byte{'b'}}}
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
|
||||
opt, err := c.IsOptimisticForRoot(ctx, [32]byte{'a'})
|
||||
require.NoError(t, err)
|
||||
@@ -430,8 +430,8 @@ func TestService_IsOptimisticForRoot_ProtoArray(t *testing.T) {
|
||||
func TestService_IsOptimisticForRoot_DoublyLinkedTree(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New(0, 0)}, head: &head{slot: 101, root: [32]byte{'b'}}}
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
|
||||
opt, err := c.IsOptimisticForRoot(ctx, [32]byte{'a'})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -13,4 +13,7 @@ var (
|
||||
errInvalidNilSummary = errors.New("nil summary returned from the DB")
|
||||
// errNilParentInDB is returned when a nil parent block is returned from the DB.
|
||||
errNilParentInDB = errors.New("nil parent block in DB")
|
||||
// errWrongBlockCound is returned when the wrong number of blocks or
|
||||
// block roots is used
|
||||
errWrongBlockCount = errors.New("wrong number of blocks or block roots")
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@ package blockchain
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
@@ -32,6 +33,7 @@ import (
|
||||
func (s *Service) NewSlot(ctx context.Context, slot types.Slot) error {
|
||||
|
||||
// Reset proposer boost root in fork choice.
|
||||
fmt.Println("Resetting proposer boost root in fork choice")
|
||||
if err := s.cfg.ForkChoiceStore.ResetBoostedProposerRoot(ctx); err != nil {
|
||||
return errors.Wrap(err, "could not reset boosted proposer root in fork choice")
|
||||
}
|
||||
|
||||
@@ -25,11 +25,11 @@ func TestService_newSlot(t *testing.T) {
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 0, [32]byte{}, [32]byte{}, 0, 0)) // genesis
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 32, [32]byte{'a'}, [32]byte{}, 0, 0)) // finalized
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 64, [32]byte{'b'}, [32]byte{'a'}, 0, 0)) // justified
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 96, [32]byte{'c'}, [32]byte{'a'}, 0, 0)) // best justified
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 97, [32]byte{'d'}, [32]byte{}, 0, 0)) // bad
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 0, [32]byte{}, [32]byte{}, [32]byte{}, 0, 0)) // genesis
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 32, [32]byte{'a'}, [32]byte{}, [32]byte{}, 0, 0)) // finalized
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 64, [32]byte{'b'}, [32]byte{'a'}, [32]byte{}, 0, 0)) // justified
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 96, [32]byte{'c'}, [32]byte{'a'}, [32]byte{}, 0, 0)) // best justified
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 97, [32]byte{'d'}, [32]byte{}, [32]byte{}, 0, 0)) // bad
|
||||
|
||||
type args struct {
|
||||
slot types.Slot
|
||||
|
||||
@@ -16,12 +16,16 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/runtime/version"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// 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) {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.notifyForkchoiceUpdate")
|
||||
defer span.End()
|
||||
|
||||
if headBlk == nil || headBlk.IsNil() || headBlk.Body().IsNil() {
|
||||
return nil, errors.New("nil head block")
|
||||
}
|
||||
@@ -83,13 +87,17 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, headBlk block.Beac
|
||||
}
|
||||
|
||||
// notifyForkchoiceUpdate signals execution engine on a new payload
|
||||
func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion int, header *ethpb.ExecutionPayloadHeader, postState state.BeaconState, blk block.SignedBeaconBlock) error {
|
||||
func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion int, header *ethpb.ExecutionPayloadHeader, postState state.BeaconState, blk block.SignedBeaconBlock, root [32]byte) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.notifyNewPayload")
|
||||
defer span.End()
|
||||
|
||||
if postState == nil {
|
||||
return errors.New("pre and post states must not be nil")
|
||||
}
|
||||
// Execution payload is only supported in Bellatrix and beyond.
|
||||
// Execution payload is only supported in Bellatrix and beyond. Pre
|
||||
// merge blocks are never optimistic
|
||||
if isPreBellatrix(postState.Version()) {
|
||||
return nil
|
||||
return s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, root)
|
||||
}
|
||||
if err := helpers.BeaconBlockIsNil(blk); err != nil {
|
||||
return err
|
||||
@@ -100,7 +108,7 @@ func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion int, hea
|
||||
return errors.Wrap(err, "could not determine if execution is enabled")
|
||||
}
|
||||
if !enabled {
|
||||
return nil
|
||||
return s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, root)
|
||||
}
|
||||
payload, err := body.ExecutionPayload()
|
||||
if err != nil {
|
||||
@@ -120,6 +128,10 @@ func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion int, hea
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, root); err != nil {
|
||||
return errors.Wrap(err, "could not set optimistic status")
|
||||
}
|
||||
|
||||
// During the transition event, the transition block should be verified for sanity.
|
||||
if isPreBellatrix(preStateVersion) {
|
||||
// Handle case where pre-state is Altair but block contains payload.
|
||||
|
||||
@@ -44,7 +44,7 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
||||
}
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 0, [32]byte{}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -346,7 +346,9 @@ func Test_NotifyNewPayload(t *testing.T) {
|
||||
payload, err = tt.preState.LatestExecutionPayloadHeader()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
err := service.notifyNewPayload(ctx, tt.preState.Version(), payload, tt.postState, tt.blk)
|
||||
root := [32]byte{'a'}
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 0, root, root, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
err = service.notifyNewPayload(ctx, tt.preState.Version(), payload, tt.postState, tt.blk, root)
|
||||
if tt.errString != "" {
|
||||
require.ErrorContains(t, tt.errString, err)
|
||||
} else {
|
||||
@@ -356,6 +358,53 @@ func Test_NotifyNewPayload(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NotifyNewPayload_SetOptimisticToValid(t *testing.T) {
|
||||
cfg := params.BeaconConfig()
|
||||
cfg.TerminalTotalDifficulty = "2"
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
fcs := protoarray.New(0, 0, [32]byte{'a'})
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
WithForkChoiceStore(fcs),
|
||||
}
|
||||
bellatrixState, _ := util.DeterministicGenesisStateBellatrix(t, 2)
|
||||
blk := ðpb.SignedBeaconBlockBellatrix{
|
||||
Block: ðpb.BeaconBlockBellatrix{
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
ExecutionPayload: &v1.ExecutionPayload{
|
||||
ParentHash: bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
bellatrixBlk, err := wrapper.WrappedSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
engine := &mockEngineService{blks: map[[32]byte]*v1.ExecutionBlock{}}
|
||||
engine.blks[[32]byte{'a'}] = &v1.ExecutionBlock{
|
||||
ParentHash: bytesutil.PadTo([]byte{'b'}, fieldparams.RootLength),
|
||||
TotalDifficulty: "0x2",
|
||||
}
|
||||
engine.blks[[32]byte{'b'}] = &v1.ExecutionBlock{
|
||||
ParentHash: bytesutil.PadTo([]byte{'3'}, fieldparams.RootLength),
|
||||
TotalDifficulty: "0x1",
|
||||
}
|
||||
service.cfg.ExecutionEngineCaller = engine
|
||||
payload, err := bellatrixState.LatestExecutionPayloadHeader()
|
||||
require.NoError(t, err)
|
||||
root := [32]byte{'c'}
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 1, root, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
err = service.notifyNewPayload(ctx, bellatrixState.Version(), payload, bellatrixState, bellatrixBlk, root)
|
||||
require.NoError(t, err)
|
||||
optimistic, err := service.IsOptimisticForRoot(ctx, root)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, optimistic)
|
||||
}
|
||||
|
||||
func Test_IsOptimisticCandidateBlock(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
params.OverrideBeaconConfig(params.MainnetConfig())
|
||||
|
||||
@@ -256,7 +256,7 @@ func TestStore_OnAttestation_Ok_ProtoArray(t *testing.T) {
|
||||
copied, err = transition.ProcessSlots(ctx, copied, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 0, tRoot, tRoot, 1, 1))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 0, tRoot, tRoot, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, service.OnAttestation(ctx, att[0]))
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ func TestStore_OnAttestation_Ok_DoublyLinkedTree(t *testing.T) {
|
||||
copied, err = transition.ProcessSlots(ctx, copied, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 0, tRoot, tRoot, 1, 1))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 0, tRoot, tRoot, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, service.OnAttestation(ctx, att[0]))
|
||||
}
|
||||
|
||||
@@ -571,8 +571,8 @@ func TestVerifyFinalizedConsistency_IsCanonical(t *testing.T) {
|
||||
r33, err := b33.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, b32.Block.Slot, r32, [32]byte{}, 0, 0))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, b33.Block.Slot, r33, r32, 0, 0))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, b32.Block.Slot, r32, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, b33.Block.Slot, r33, r32, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
|
||||
_, err = service.cfg.ForkChoiceStore.Head(ctx, 0, r32, []uint64{}, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -107,13 +107,12 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.notifyNewPayload(ctx, preStateVersion, preStateHeader, postState, signed); err != nil {
|
||||
return errors.Wrap(err, "could not verify new payload")
|
||||
}
|
||||
|
||||
if err := s.savePostStateInfo(ctx, blockRoot, signed, postState, false /* reg sync */); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.notifyNewPayload(ctx, preStateVersion, preStateHeader, postState, signed, blockRoot); err != nil {
|
||||
return errors.Wrap(err, "could not verify new payload")
|
||||
}
|
||||
|
||||
// We add a proposer score boost to fork choice for the block root if applicable, right after
|
||||
// running a successful state transition for the block.
|
||||
@@ -280,6 +279,11 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []block.SignedBeaconBlo
|
||||
if len(blks) == 0 || len(blockRoots) == 0 {
|
||||
return nil, nil, errors.New("no blocks provided")
|
||||
}
|
||||
|
||||
if len(blks) != len(blockRoots) {
|
||||
return nil, nil, errWrongBlockCount
|
||||
}
|
||||
|
||||
if err := helpers.BeaconBlockIsNil(blks[0]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -307,23 +311,13 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []block.SignedBeaconBlo
|
||||
var set *bls.SignatureBatch
|
||||
boundaries := make(map[[32]byte]state.BeaconState)
|
||||
for i, b := range blks {
|
||||
preStateVersion, preStateHeader, err := getStateVersionAndPayload(preState)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
set, preState, err = transition.ExecuteStateTransitionNoVerifyAnySig(ctx, preState, b)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := s.notifyNewPayload(ctx, preStateVersion, preStateHeader, preState, b); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Save potential boundary states.
|
||||
if slots.IsEpochStart(preState.Slot()) {
|
||||
boundaries[blockRoots[i]] = preState.Copy()
|
||||
if err := s.handleEpochBoundary(ctx, preState); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not handle epoch boundary state")
|
||||
}
|
||||
}
|
||||
jCheckpoints[i] = preState.CurrentJustifiedCheckpoint()
|
||||
fCheckpoints[i] = preState.FinalizedCheckpoint()
|
||||
@@ -336,6 +330,25 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []block.SignedBeaconBlo
|
||||
if !verify {
|
||||
return nil, nil, errors.New("batch block signature verification failed")
|
||||
}
|
||||
|
||||
// blocks have been verified, add them to forkchoice and call the engine
|
||||
for i, b := range blks {
|
||||
preStateVersion, preStateHeader, err := getStateVersionAndPayload(preState)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
s.saveInitSyncBlock(blockRoots[i], b)
|
||||
if err := s.insertBlockToForkChoiceStore(ctx, b.Block(), blockRoots[i], fCheckpoints[i], jCheckpoints[i]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := s.notifyNewPayload(ctx, preStateVersion, preStateHeader, preState, b, blockRoots[i]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if _, err := s.notifyForkchoiceUpdate(ctx, b.Block(), blockRoots[i], bytesutil.ToBytes32(fCheckpoints[i].Root)); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
for r, st := range boundaries {
|
||||
if err := s.cfg.StateGen.SaveState(ctx, r, st); err != nil {
|
||||
return nil, nil, err
|
||||
@@ -357,15 +370,6 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []block.SignedBeaconBlo
|
||||
// their state summaries and split them off to relative hot/cold storage.
|
||||
func (s *Service) handleBlockAfterBatchVerify(ctx context.Context, signed block.SignedBeaconBlock,
|
||||
blockRoot [32]byte, fCheckpoint, jCheckpoint *ethpb.Checkpoint) error {
|
||||
b := signed.Block()
|
||||
|
||||
s.saveInitSyncBlock(blockRoot, signed)
|
||||
if err := s.insertBlockToForkChoiceStore(ctx, b, blockRoot, fCheckpoint, jCheckpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := s.notifyForkchoiceUpdate(ctx, b, blockRoot, bytesutil.ToBytes32(fCheckpoint.Root)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.cfg.BeaconDB.SaveStateSummary(ctx, ðpb.StateSummary{
|
||||
Slot: signed.Block().Slot(),
|
||||
@@ -481,12 +485,29 @@ func (s *Service) insertBlockToForkChoiceStore(ctx context.Context, blk block.Be
|
||||
return err
|
||||
}
|
||||
// Feed in block to fork choice store.
|
||||
|
||||
payloadHash, err := getBlockPayloadHash(blk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx,
|
||||
blk.Slot(), root, bytesutil.ToBytes32(blk.ParentRoot()),
|
||||
blk.Slot(), root, bytesutil.ToBytes32(blk.ParentRoot()), payloadHash,
|
||||
jCheckpoint.Epoch,
|
||||
fCheckpoint.Epoch)
|
||||
}
|
||||
|
||||
func getBlockPayloadHash(blk block.BeaconBlock) ([32]byte, error) {
|
||||
payloadHash := [32]byte{}
|
||||
if isPreBellatrix(blk.Version()) {
|
||||
return payloadHash, nil
|
||||
}
|
||||
payload, err := blk.Body().ExecutionPayload()
|
||||
if err != nil {
|
||||
return payloadHash, err
|
||||
}
|
||||
return bytesutil.ToBytes32(payload.BlockHash), nil
|
||||
}
|
||||
|
||||
// This saves post state info to DB or cache. This also saves post state info to fork choice store.
|
||||
// Post state info consists of processed block and state. Do not call this method unless the block and state are verified.
|
||||
func (s *Service) savePostStateInfo(ctx context.Context, r [32]byte, b block.SignedBeaconBlock, st state.BeaconState, initSync bool) error {
|
||||
|
||||
@@ -366,8 +366,12 @@ func (s *Service) fillInForkChoiceMissingBlocks(ctx context.Context, blk block.B
|
||||
for i := len(pendingNodes) - 1; i >= 0; i-- {
|
||||
b := pendingNodes[i]
|
||||
r := pendingRoots[i]
|
||||
payloadHash, err := getBlockPayloadHash(blk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx,
|
||||
b.Slot(), r, bytesutil.ToBytes32(b.ParentRoot()),
|
||||
b.Slot(), r, bytesutil.ToBytes32(b.ParentRoot()), payloadHash,
|
||||
jCheckpoint.Epoch,
|
||||
fCheckpoint.Epoch); err != nil {
|
||||
return errors.Wrap(err, "could not process block for proto array fork choice")
|
||||
|
||||
@@ -295,6 +295,8 @@ func TestStore_OnBlockBatch_ProtoArray(t *testing.T) {
|
||||
rBlock.Block.ParentRoot = gRoot[:]
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), blks[0]))
|
||||
require.NoError(t, service.cfg.StateGen.SaveState(ctx, blkRoots[0], firstState))
|
||||
_, _, err = service.onBlockBatch(ctx, blks, blkRoots[1:])
|
||||
require.ErrorIs(t, errWrongBlockCount, err)
|
||||
_, _, err = service.onBlockBatch(ctx, blks[1:], blkRoots[1:])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -347,6 +349,8 @@ func TestStore_OnBlockBatch_DoublyLinkedTree(t *testing.T) {
|
||||
rBlock.Block.ParentRoot = gRoot[:]
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), blks[0]))
|
||||
require.NoError(t, service.cfg.StateGen.SaveState(ctx, blkRoots[0], firstState))
|
||||
_, _, err = service.onBlockBatch(ctx, blks, blkRoots[1:])
|
||||
require.ErrorIs(t, errWrongBlockCount, err)
|
||||
_, _, err = service.onBlockBatch(ctx, blks[1:], blkRoots[1:])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -1085,7 +1089,7 @@ func TestAncestor_CanUseForkchoice(t *testing.T) {
|
||||
beaconBlock.Block.ParentRoot = bytesutil.PadTo(b.Block.ParentRoot, 32)
|
||||
r, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(context.Background(), b.Block.Slot, r, bytesutil.ToBytes32(b.Block.ParentRoot), 0, 0)) // Saves blocks to fork choice store.
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(context.Background(), b.Block.Slot, r, bytesutil.ToBytes32(b.Block.ParentRoot), params.BeaconConfig().ZeroHash, 0, 0)) // Saves blocks to fork choice store.
|
||||
}
|
||||
|
||||
r, err := service.ancestor(context.Background(), r200[:], 150)
|
||||
@@ -1130,7 +1134,7 @@ func TestAncestor_CanUseDB(t *testing.T) {
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(beaconBlock))) // Saves blocks to DB.
|
||||
}
|
||||
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(context.Background(), 200, r200, r200, 0, 0))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(context.Background(), 200, r200, r200, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
|
||||
r, err := service.ancestor(context.Background(), r200[:], 150)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -161,14 +161,36 @@ func (s *Service) spawnProcessAttestationsRoutine(stateFeed *event.Feed) {
|
||||
log.WithError(err).Errorf("Unable to get justified balances for root %v", justified.Root)
|
||||
continue
|
||||
}
|
||||
prevHead := s.headRoot()
|
||||
if err := s.updateHead(s.ctx, balances); err != nil {
|
||||
log.WithError(err).Warn("Resolving fork due to new attestation")
|
||||
}
|
||||
s.notifyEngineIfChangedHead(prevHead)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// This calls notify Forkchoice Update in the event that the head has changed
|
||||
func (s *Service) notifyEngineIfChangedHead(prevHead [32]byte) {
|
||||
if s.headRoot() == prevHead {
|
||||
return
|
||||
}
|
||||
finalized := s.store.FinalizedCheckpt()
|
||||
if finalized == nil {
|
||||
log.WithError(errNilFinalizedInStore).Error("could not get finalized checkpoint")
|
||||
return
|
||||
}
|
||||
_, err := s.notifyForkchoiceUpdate(s.ctx,
|
||||
s.headBlock().Block(),
|
||||
s.headRoot(),
|
||||
bytesutil.ToBytes32(finalized.Root),
|
||||
)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not notify forkchoice update")
|
||||
}
|
||||
}
|
||||
|
||||
// This processes fork choice attestations from the pool to account for validator votes and fork choice.
|
||||
func (s *Service) processAttestations(ctx context.Context) {
|
||||
atts := s.cfg.AttPool.ForkchoiceAttestations()
|
||||
|
||||
@@ -111,9 +111,44 @@ func TestProcessAttestations_Ok(t *testing.T) {
|
||||
copied, err = transition.ProcessSlots(ctx, copied, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 0, tRoot, tRoot, 1, 1))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 0, tRoot, tRoot, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, service.cfg.AttPool.SaveForkchoiceAttestations(atts))
|
||||
service.processAttestations(ctx)
|
||||
require.Equal(t, 0, len(service.cfg.AttPool.ForkchoiceAttestations()))
|
||||
require.LogsDoNotContain(t, hook, "Could not process attestation for fork choice")
|
||||
}
|
||||
|
||||
func TestNotifyEngineIfChangedHead(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ctx := context.Background()
|
||||
opts := testServiceOptsWithDB(t)
|
||||
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
|
||||
service.notifyEngineIfChangedHead(service.headRoot())
|
||||
hookErr := "could not notify forkchoice update"
|
||||
finalizedErr := "could not get finalized checkpoint"
|
||||
require.LogsDoNotContain(t, hook, finalizedErr)
|
||||
require.LogsDoNotContain(t, hook, hookErr)
|
||||
service.notifyEngineIfChangedHead([32]byte{'a'})
|
||||
require.LogsContain(t, hook, finalizedErr)
|
||||
|
||||
hook.Reset()
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block.Slot = 1
|
||||
wr := wrapper.WrappedPhase0SignedBeaconBlock(b)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wr))
|
||||
r, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
finalized := ðpb.Checkpoint{Root: r[:], Epoch: 0}
|
||||
service.head = &head{
|
||||
slot: 1,
|
||||
root: r,
|
||||
block: wr,
|
||||
}
|
||||
service.store.SetFinalizedCheckpt(finalized)
|
||||
service.notifyEngineIfChangedHead([32]byte{'b'})
|
||||
require.LogsDoNotContain(t, hook, finalizedErr)
|
||||
require.LogsDoNotContain(t, hook, hookErr)
|
||||
}
|
||||
|
||||
@@ -132,6 +132,7 @@ func TestService_ReceiveBlock(t *testing.T) {
|
||||
WithExitPool(voluntaryexits.NewPool()),
|
||||
WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
WithFinalizedStateAtStartUp(genesis),
|
||||
}
|
||||
s, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
@@ -191,21 +192,44 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error {
|
||||
s.store = store.New(justified, finalized)
|
||||
|
||||
var store f.ForkChoicer
|
||||
fRoot := bytesutil.ToBytes32(finalized.Root)
|
||||
if features.Get().EnableForkChoiceDoublyLinkedTree {
|
||||
store = doublylinkedtree.New(justified.Epoch, finalized.Epoch)
|
||||
} else {
|
||||
store = protoarray.New(justified.Epoch, finalized.Epoch, bytesutil.ToBytes32(finalized.Root))
|
||||
store = protoarray.New(justified.Epoch, finalized.Epoch, fRoot)
|
||||
}
|
||||
s.cfg.ForkChoiceStore = store
|
||||
|
||||
ss, err := slots.EpochStart(finalized.Epoch)
|
||||
fb, err := s.cfg.BeaconDB.Block(s.ctx, fRoot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get start slot of finalized epoch")
|
||||
return errors.Wrap(err, "could not get finalized checkpoint block")
|
||||
}
|
||||
if fb == nil {
|
||||
return errNilFinalizedInStore
|
||||
}
|
||||
payloadHash, err := getBlockPayloadHash(fb.Block())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get execution payload hash")
|
||||
}
|
||||
fSlot := fb.Block().Slot()
|
||||
if err := store.InsertOptimisticBlock(s.ctx, fSlot, fRoot, params.BeaconConfig().ZeroHash,
|
||||
payloadHash, justified.Epoch, finalized.Epoch); err != nil {
|
||||
return errors.Wrap(err, "could not insert finalized block to forkchoice")
|
||||
}
|
||||
|
||||
lastValidatedCheckpoint, err := s.cfg.BeaconDB.LastValidatedCheckpoint(s.ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get last validated checkpoint")
|
||||
}
|
||||
if bytes.Equal(finalized.Root, lastValidatedCheckpoint.Root) {
|
||||
if err := store.SetOptimisticToValid(s.ctx, fRoot); err != nil {
|
||||
return errors.Wrap(err, "could not set finalized block as validated")
|
||||
}
|
||||
}
|
||||
|
||||
h := s.headBlock().Block()
|
||||
if h.Slot() > ss {
|
||||
if h.Slot() > fSlot {
|
||||
log.WithFields(logrus.Fields{
|
||||
"startSlot": ss,
|
||||
"startSlot": fSlot,
|
||||
"endSlot": h.Slot(),
|
||||
}).Info("Loading blocks to fork choice store, this may take a while.")
|
||||
if err := s.fillInForkChoiceMissingBlocks(s.ctx, h, finalized, justified); err != nil {
|
||||
@@ -449,10 +473,15 @@ func (s *Service) saveGenesisData(ctx context.Context, genesisState state.Beacon
|
||||
genesisCheckpoint := genesisState.FinalizedCheckpoint()
|
||||
s.store = store.New(genesisCheckpoint, genesisCheckpoint)
|
||||
|
||||
payloadHash, err := getBlockPayloadHash(genesisBlk.Block())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx,
|
||||
genesisBlk.Block().Slot(),
|
||||
genesisBlkRoot,
|
||||
params.BeaconConfig().ZeroHash,
|
||||
payloadHash,
|
||||
genesisCheckpoint.Epoch,
|
||||
genesisCheckpoint.Epoch); err != nil {
|
||||
log.Fatalf("Could not process genesis block for fork choice: %v", err)
|
||||
|
||||
@@ -153,6 +153,11 @@ func TestChainStartStop_Initialized(t *testing.T) {
|
||||
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, blkRoot))
|
||||
require.NoError(t, beaconDB.SaveJustifiedCheckpoint(ctx, ðpb.Checkpoint{Root: blkRoot[:]}))
|
||||
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Root: blkRoot[:]}))
|
||||
ss := ðpb.StateSummary{
|
||||
Slot: 1,
|
||||
Root: blkRoot[:],
|
||||
}
|
||||
require.NoError(t, beaconDB.SaveStateSummary(ctx, ss))
|
||||
chainService.cfg.FinalizedStateAtStartUp = s
|
||||
// Test the start function.
|
||||
chainService.Start()
|
||||
@@ -179,7 +184,9 @@ func TestChainStartStop_GenesisZeroHashes(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveState(ctx, s, blkRoot))
|
||||
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, blkRoot))
|
||||
require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(genesisBlk)))
|
||||
require.NoError(t, beaconDB.SaveJustifiedCheckpoint(ctx, ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}))
|
||||
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Root: blkRoot[:]}))
|
||||
chainService.cfg.FinalizedStateAtStartUp = s
|
||||
// Test the start function.
|
||||
chainService.Start()
|
||||
@@ -288,8 +295,10 @@ func TestChainService_InitializeChainInfo(t *testing.T) {
|
||||
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Epoch: slots.ToEpoch(finalizedSlot), Root: headRoot[:]}))
|
||||
attSrv, err := attestations.NewService(ctx, &attestations.Config{})
|
||||
require.NoError(t, err)
|
||||
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
|
||||
stateGen := stategen.New(beaconDB)
|
||||
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stateGen), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, stateGen.SaveState(ctx, headRoot, headState))
|
||||
require.NoError(t, c.StartFromSavedState(headState))
|
||||
headBlk, err := c.HeadBlock(ctx)
|
||||
require.NoError(t, err)
|
||||
@@ -331,14 +340,22 @@ func TestChainService_InitializeChainInfo_SetHeadAtGenesis(t *testing.T) {
|
||||
require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(headBlock)))
|
||||
attSrv, err := attestations.NewService(ctx, &attestations.Config{})
|
||||
require.NoError(t, err)
|
||||
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}))
|
||||
ss := ðpb.StateSummary{
|
||||
Slot: finalizedSlot,
|
||||
Root: headRoot[:],
|
||||
}
|
||||
require.NoError(t, beaconDB.SaveStateSummary(ctx, ss))
|
||||
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Root: headRoot[:], Epoch: slots.ToEpoch(finalizedSlot)}))
|
||||
stateGen := stategen.New(beaconDB)
|
||||
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stateGen), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, c.StartFromSavedState(headState))
|
||||
s, err := c.HeadState(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.DeepSSZEqual(t, headState.InnerStateUnsafe(), s.InnerStateUnsafe(), "Head state incorrect")
|
||||
assert.Equal(t, genesisRoot, c.originBlockRoot, "Genesis block root incorrect")
|
||||
assert.DeepEqual(t, genesis, c.head.block.Proto())
|
||||
assert.DeepEqual(t, headBlock, c.head.block.Proto())
|
||||
}
|
||||
|
||||
func TestChainService_InitializeChainInfo_HeadSync(t *testing.T) {
|
||||
@@ -380,9 +397,8 @@ func TestChainService_InitializeChainInfo_HeadSync(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, headState.SetSlot(headBlock.Block.Slot))
|
||||
require.NoError(t, headState.SetGenesisValidatorsRoot(params.BeaconConfig().ZeroHash[:]))
|
||||
require.NoError(t, beaconDB.SaveState(ctx, headState, genesisRoot))
|
||||
require.NoError(t, beaconDB.SaveState(ctx, headState, finalizedRoot))
|
||||
require.NoError(t, beaconDB.SaveState(ctx, headState, headRoot))
|
||||
require.NoError(t, beaconDB.SaveState(ctx, headState, finalizedRoot))
|
||||
require.NoError(t, beaconDB.SaveHeadBlockRoot(ctx, headRoot))
|
||||
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{
|
||||
Epoch: slots.ToEpoch(finalizedBlock.Block.Slot),
|
||||
@@ -391,7 +407,8 @@ func TestChainService_InitializeChainInfo_HeadSync(t *testing.T) {
|
||||
|
||||
attSrv, err := attestations.NewService(ctx, &attestations.Config{})
|
||||
require.NoError(t, err)
|
||||
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
|
||||
stateGen := stategen.New(beaconDB)
|
||||
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stateGen), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, c.StartFromSavedState(headState))
|
||||
s, err := c.HeadState(ctx)
|
||||
|
||||
@@ -104,34 +104,30 @@ func SetParticipationAndRewardProposer(
|
||||
targetEpoch types.Epoch,
|
||||
indices []uint64,
|
||||
participatedFlags map[uint8]bool, totalBalance uint64) (state.BeaconState, error) {
|
||||
var epochParticipation []byte
|
||||
var proposerRewardNumerator uint64
|
||||
currentEpoch := time.CurrentEpoch(beaconState)
|
||||
var err error
|
||||
var stateErr error
|
||||
if targetEpoch == currentEpoch {
|
||||
epochParticipation, err = beaconState.CurrentEpochParticipation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stateErr = beaconState.ModifyCurrentParticipationBits(func(val []byte) ([]byte, error) {
|
||||
propRewardNum, epochParticipation, err := EpochParticipation(beaconState, indices, val, participatedFlags, totalBalance)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proposerRewardNumerator = propRewardNum
|
||||
return epochParticipation, nil
|
||||
})
|
||||
} else {
|
||||
epochParticipation, err = beaconState.PreviousEpochParticipation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stateErr = beaconState.ModifyPreviousParticipationBits(func(val []byte) ([]byte, error) {
|
||||
propRewardNum, epochParticipation, err := EpochParticipation(beaconState, indices, val, participatedFlags, totalBalance)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proposerRewardNumerator = propRewardNum
|
||||
return epochParticipation, nil
|
||||
})
|
||||
}
|
||||
|
||||
proposerRewardNumerator, epochParticipation, err := EpochParticipation(beaconState, indices, epochParticipation, participatedFlags, totalBalance)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if targetEpoch == currentEpoch {
|
||||
if err := beaconState.SetCurrentParticipationBits(epochParticipation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := beaconState.SetPreviousParticipationBits(epochParticipation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stateErr != nil {
|
||||
return nil, stateErr
|
||||
}
|
||||
|
||||
if err := RewardProposer(ctx, beaconState, proposerRewardNumerator); err != nil {
|
||||
|
||||
@@ -15,29 +15,12 @@ go_library(
|
||||
"weak_subjectivity.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//cmd/beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__pkg__",
|
||||
"//crypto/keystore:__pkg__",
|
||||
"//network/forks:__pkg__",
|
||||
"//proto/prysm/v1alpha1:__subpackages__",
|
||||
"//proto/prysm/v1alpha1/attestation:__pkg__",
|
||||
"//runtime/interop:__pkg__",
|
||||
"//slasher:__subpackages__",
|
||||
"//testing/altair:__pkg__",
|
||||
"//testing/benchmark/benchmark_files:__subpackages__",
|
||||
"//testing/endtoend/evaluators:__pkg__",
|
||||
"//testing/slasher/simulator:__pkg__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
"//tools:__subpackages__",
|
||||
"//validator:__subpackages__",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/core/time:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//container/slice:go_default_library",
|
||||
"//container/trie:go_default_library",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
@@ -12,9 +11,11 @@ import (
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/time"
|
||||
"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"
|
||||
"github.com/prysmaticlabs/prysm/math"
|
||||
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
v1alpha1 "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
)
|
||||
|
||||
@@ -122,20 +123,20 @@ func ComputeWeakSubjectivityPeriod(ctx context.Context, st state.ReadOnlyBeaconS
|
||||
// current_epoch = compute_epoch_at_slot(get_current_slot(store))
|
||||
// return current_epoch <= ws_state_epoch + ws_period
|
||||
func IsWithinWeakSubjectivityPeriod(
|
||||
ctx context.Context, currentEpoch types.Epoch, wsState state.ReadOnlyBeaconState, wsCheckpoint *eth.WeakSubjectivityCheckpoint) (bool, error) {
|
||||
ctx context.Context, currentEpoch types.Epoch, wsState state.ReadOnlyBeaconState, wsStateRoot [fieldparams.RootLength]byte, wsEpoch types.Epoch) (bool, error) {
|
||||
// Make sure that incoming objects are not nil.
|
||||
if wsState == nil || wsState.IsNil() || wsState.LatestBlockHeader() == nil || wsCheckpoint == nil {
|
||||
if wsState == nil || wsState.IsNil() || wsState.LatestBlockHeader() == nil {
|
||||
return false, errors.New("invalid weak subjectivity state or checkpoint")
|
||||
}
|
||||
|
||||
// Assert that state and checkpoint have the same root and epoch.
|
||||
if !bytes.Equal(wsState.LatestBlockHeader().StateRoot, wsCheckpoint.StateRoot) {
|
||||
if bytesutil.ToBytes32(wsState.LatestBlockHeader().StateRoot) != wsStateRoot {
|
||||
return false, fmt.Errorf("state (%#x) and checkpoint (%#x) roots do not match",
|
||||
wsState.LatestBlockHeader().StateRoot, wsCheckpoint.StateRoot)
|
||||
wsState.LatestBlockHeader().StateRoot, wsStateRoot)
|
||||
}
|
||||
if slots.ToEpoch(wsState.Slot()) != wsCheckpoint.Epoch {
|
||||
if slots.ToEpoch(wsState.Slot()) != wsEpoch {
|
||||
return false, fmt.Errorf("state (%v) and checkpoint (%v) epochs do not match",
|
||||
slots.ToEpoch(wsState.Slot()), wsCheckpoint.Epoch)
|
||||
slots.ToEpoch(wsState.Slot()), wsEpoch)
|
||||
}
|
||||
|
||||
// Compare given epoch to state epoch + weak subjectivity period.
|
||||
@@ -164,7 +165,7 @@ func LatestWeakSubjectivityEpoch(ctx context.Context, st state.ReadOnlyBeaconSta
|
||||
}
|
||||
|
||||
// ParseWeakSubjectivityInputString parses "blocks_root:epoch_number" string into a checkpoint.
|
||||
func ParseWeakSubjectivityInputString(wsCheckpointString string) (*eth.Checkpoint, error) {
|
||||
func ParseWeakSubjectivityInputString(wsCheckpointString string) (*v1alpha1.Checkpoint, error) {
|
||||
if wsCheckpointString == "" {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -197,7 +198,7 @@ func ParseWeakSubjectivityInputString(wsCheckpointString string) (*eth.Checkpoin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ð.Checkpoint{
|
||||
return &v1alpha1.Checkpoint{
|
||||
Epoch: types.Epoch(epoch),
|
||||
Root: bRoot,
|
||||
}, nil
|
||||
|
||||
@@ -54,12 +54,15 @@ func TestWeakSubjectivity_ComputeWeakSubjectivityPeriod(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockWsCheckpoint func() (stateRoot [32]byte, blockRoot [32]byte, e types.Epoch)
|
||||
|
||||
func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
epoch types.Epoch
|
||||
genWsState func() state.ReadOnlyBeaconState
|
||||
genWsCheckpoint func() *ethpb.WeakSubjectivityCheckpoint
|
||||
genWsCheckpoint mockWsCheckpoint
|
||||
want bool
|
||||
wantedErr string
|
||||
}{
|
||||
@@ -68,22 +71,8 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
genWsState: func() state.ReadOnlyBeaconState {
|
||||
return nil
|
||||
},
|
||||
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
|
||||
return ðpb.WeakSubjectivityCheckpoint{
|
||||
BlockRoot: make([]byte, 32),
|
||||
StateRoot: make([]byte, 32),
|
||||
Epoch: 42,
|
||||
}
|
||||
},
|
||||
wantedErr: "invalid weak subjectivity state or checkpoint",
|
||||
},
|
||||
{
|
||||
name: "nil weak subjectivity checkpoint",
|
||||
genWsState: func() state.ReadOnlyBeaconState {
|
||||
return genState(t, 128, 32)
|
||||
},
|
||||
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
|
||||
return nil
|
||||
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
|
||||
return [32]byte{}, [32]byte{}, 42
|
||||
},
|
||||
wantedErr: "invalid weak subjectivity state or checkpoint",
|
||||
},
|
||||
@@ -99,11 +88,10 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return beaconState
|
||||
},
|
||||
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
|
||||
return ðpb.WeakSubjectivityCheckpoint{
|
||||
StateRoot: bytesutil.PadTo([]byte("stateroot2"), 32),
|
||||
Epoch: 42,
|
||||
}
|
||||
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
|
||||
var sr [32]byte
|
||||
copy(sr[:], bytesutil.PadTo([]byte("stateroot2"), 32))
|
||||
return sr, [32]byte{}, 42
|
||||
},
|
||||
wantedErr: fmt.Sprintf("state (%#x) and checkpoint (%#x) roots do not match",
|
||||
bytesutil.PadTo([]byte("stateroot1"), 32), bytesutil.PadTo([]byte("stateroot2"), 32)),
|
||||
@@ -120,11 +108,10 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return beaconState
|
||||
},
|
||||
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
|
||||
return ðpb.WeakSubjectivityCheckpoint{
|
||||
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
|
||||
Epoch: 43,
|
||||
}
|
||||
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
|
||||
var sr [32]byte
|
||||
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
|
||||
return sr, [32]byte{}, 43
|
||||
},
|
||||
wantedErr: "state (42) and checkpoint (43) epochs do not match",
|
||||
},
|
||||
@@ -140,11 +127,10 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return beaconState
|
||||
},
|
||||
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
|
||||
return ðpb.WeakSubjectivityCheckpoint{
|
||||
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
|
||||
Epoch: 42,
|
||||
}
|
||||
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
|
||||
var sr [32]byte
|
||||
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
|
||||
return sr, [32]byte{}, 42
|
||||
},
|
||||
wantedErr: "cannot compute weak subjectivity period: no active validators found",
|
||||
},
|
||||
@@ -161,11 +147,10 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return beaconState
|
||||
},
|
||||
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
|
||||
return ðpb.WeakSubjectivityCheckpoint{
|
||||
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
|
||||
Epoch: 42,
|
||||
}
|
||||
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
|
||||
var sr [32]byte
|
||||
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
|
||||
return sr, [32]byte{}, 42
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
@@ -182,18 +167,18 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return beaconState
|
||||
},
|
||||
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
|
||||
return ðpb.WeakSubjectivityCheckpoint{
|
||||
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
|
||||
Epoch: 42,
|
||||
}
|
||||
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
|
||||
var sr [32]byte
|
||||
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
|
||||
return sr, [32]byte{}, 42
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := helpers.IsWithinWeakSubjectivityPeriod(context.Background(), tt.epoch, tt.genWsState(), tt.genWsCheckpoint())
|
||||
sr, _, e := tt.genWsCheckpoint()
|
||||
got, err := helpers.IsWithinWeakSubjectivityPeriod(context.Background(), tt.epoch, tt.genWsState(), sr, e)
|
||||
if tt.wantedErr != "" {
|
||||
assert.Equal(t, false, got)
|
||||
assert.ErrorContains(t, tt.wantedErr, err)
|
||||
|
||||
@@ -47,6 +47,7 @@ go_library(
|
||||
"//config/params:go_default_library",
|
||||
"//container/slice:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//encoding/ssz/detect:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//monitoring/progress:go_default_library",
|
||||
"//monitoring/tracing:go_default_library",
|
||||
|
||||
@@ -7,10 +7,9 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
statev2 "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/ssz/detect"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
)
|
||||
|
||||
// SaveOrigin loads an ssz serialized Block & BeaconState from an io.Reader
|
||||
@@ -18,33 +17,36 @@ import (
|
||||
// syncing, using the provided values as their point of origin. This is an alternative
|
||||
// to syncing from genesis, and should only be run on an empty database.
|
||||
func (s *Store) SaveOrigin(ctx context.Context, stateReader, blockReader io.Reader) error {
|
||||
// unmarshal both block and state before trying to save anything
|
||||
// so that we fail early if there is any issue with the ssz data
|
||||
blk := ðpb.SignedBeaconBlockAltair{}
|
||||
sb, err := ioutil.ReadAll(stateReader)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to read origin state bytes")
|
||||
}
|
||||
bb, err := ioutil.ReadAll(blockReader)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error reading block given to SaveOrigin")
|
||||
}
|
||||
if err := blk.UnmarshalSSZ(bb); err != nil {
|
||||
return errors.Wrap(err, "could not unmarshal checkpoint block")
|
||||
}
|
||||
wblk, err := wrapper.WrappedAltairSignedBeaconBlock(blk)
|
||||
|
||||
cf, err := detect.FromState(sb)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not wrap checkpoint block")
|
||||
return errors.Wrap(err, "failed to detect config and fork for origin state")
|
||||
}
|
||||
bs, err := statev2.InitializeFromSSZReader(stateReader)
|
||||
bs, err := cf.UnmarshalBeaconState(sb)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not initialize checkpoint state from reader")
|
||||
return errors.Wrap(err, "could not unmarshal origin state")
|
||||
}
|
||||
wblk, err := cf.UnmarshalBeaconBlock(bb)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to unmarshal origin SignedBeaconBlock")
|
||||
}
|
||||
|
||||
blockRoot, err := wblk.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not compute HashTreeRoot of checkpoint block")
|
||||
}
|
||||
// save block
|
||||
if err := s.SaveBlock(ctx, wblk); err != nil {
|
||||
return errors.Wrap(err, "could not save checkpoint block")
|
||||
}
|
||||
blockRoot, err := blk.Block.HashTreeRoot()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not compute HashTreeRoot of checkpoint block")
|
||||
}
|
||||
|
||||
// save state
|
||||
if err = s.SaveState(ctx, bs, blockRoot); err != nil {
|
||||
@@ -70,7 +72,7 @@ func (s *Store) SaveOrigin(ctx context.Context, stateReader, blockReader io.Read
|
||||
|
||||
// rebuild the checkpoint from the block
|
||||
// use it to mark the block as justified and finalized
|
||||
slotEpoch, err := blk.Block.Slot.SafeDivSlot(params.BeaconConfig().SlotsPerEpoch)
|
||||
slotEpoch, err := wblk.Block().Slot().SafeDivSlot(params.BeaconConfig().SlotsPerEpoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ func TestFFGUpdates_OneBranch(t *testing.T) {
|
||||
// 2 <- justified: 1, finalized: 0
|
||||
// |
|
||||
// 3 <- justified: 2, finalized: 1
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(3), indexToHash(2), 2, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 2, 1))
|
||||
|
||||
// With starting justified epoch at 0, the head should be 3:
|
||||
// 0 <- start
|
||||
@@ -89,17 +89,17 @@ func TestFFGUpdates_TwoBranches(t *testing.T) {
|
||||
// | |
|
||||
// justified: 2, finalized: 0 -> 9 10 <- justified: 2, finalized: 0
|
||||
// Left branch.
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(3), indexToHash(1), 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(5), indexToHash(3), 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(7), indexToHash(5), 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(9), indexToHash(7), 2, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(5), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(7), indexToHash(5), params.BeaconConfig().ZeroHash, 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(9), indexToHash(7), params.BeaconConfig().ZeroHash, 2, 0))
|
||||
// Right branch.
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(4), indexToHash(2), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(6), indexToHash(4), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(8), indexToHash(6), 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(10), indexToHash(8), 2, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(6), indexToHash(4), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(8), indexToHash(6), params.BeaconConfig().ZeroHash, 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(10), indexToHash(8), params.BeaconConfig().ZeroHash, 2, 0))
|
||||
|
||||
// With start at 0, the head should be 10:
|
||||
// 0 <-- start
|
||||
@@ -185,7 +185,7 @@ func TestFFGUpdates_TwoBranches(t *testing.T) {
|
||||
func setup(justifiedEpoch, finalizedEpoch types.Epoch) *ForkChoice {
|
||||
ctx := context.Background()
|
||||
f := New(justifiedEpoch, finalizedEpoch)
|
||||
err := f.InsertOptimisticBlock(ctx, 0, params.BeaconConfig().ZeroHash, [32]byte{}, justifiedEpoch, finalizedEpoch)
|
||||
err := f.InsertOptimisticBlock(ctx, 0, params.BeaconConfig().ZeroHash, [32]byte{}, params.BeaconConfig().ZeroHash, justifiedEpoch, finalizedEpoch)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -106,13 +106,13 @@ func (f *ForkChoice) ProcessAttestation(ctx context.Context, validatorIndices []
|
||||
func (f *ForkChoice) InsertOptimisticBlock(
|
||||
ctx context.Context,
|
||||
slot types.Slot,
|
||||
blockRoot, parentRoot [fieldparams.RootLength]byte,
|
||||
blockRoot, parentRoot, payloadHash [fieldparams.RootLength]byte,
|
||||
justifiedEpoch, finalizedEpoch types.Epoch,
|
||||
) error {
|
||||
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.InsertOptimisticBlock")
|
||||
defer span.End()
|
||||
|
||||
return f.store.insert(ctx, slot, blockRoot, parentRoot, justifiedEpoch, finalizedEpoch)
|
||||
return f.store.insert(ctx, slot, blockRoot, parentRoot, payloadHash, justifiedEpoch, finalizedEpoch)
|
||||
}
|
||||
|
||||
// Prune prunes the fork choice store with the new finalized root. The store is only pruned if the input
|
||||
@@ -302,6 +302,6 @@ func (f *ForkChoice) ForkChoiceNodes() []*pbrpc.ForkChoiceNode {
|
||||
}
|
||||
|
||||
// SetOptimisticToInvalid removes a block with an invalid execution payload from fork choice store
|
||||
func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [fieldparams.RootLength]byte) error {
|
||||
func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [fieldparams.RootLength]byte) ([][32]byte, error) {
|
||||
return f.store.removeNode(ctx, root)
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ import (
|
||||
func TestForkChoice_UpdateBalancesPositiveChange(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), 0, 0))
|
||||
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))
|
||||
|
||||
f.votes = []Vote{
|
||||
{indexToHash(1), indexToHash(1), 0},
|
||||
@@ -37,9 +37,9 @@ func TestForkChoice_UpdateBalancesPositiveChange(t *testing.T) {
|
||||
func TestForkChoice_UpdateBalancesNegativeChange(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), 0, 0))
|
||||
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
|
||||
@@ -61,12 +61,12 @@ func TestForkChoice_UpdateBalancesNegativeChange(t *testing.T) {
|
||||
func TestForkChoice_IsCanonical(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(1), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, indexToHash(4), indexToHash(2), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(5), indexToHash(4), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 6, indexToHash(6), indexToHash(5), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 6, indexToHash(6), indexToHash(5), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
require.Equal(t, true, f.IsCanonical(params.BeaconConfig().ZeroHash))
|
||||
require.Equal(t, false, f.IsCanonical(indexToHash(1)))
|
||||
@@ -80,12 +80,12 @@ func TestForkChoice_IsCanonical(t *testing.T) {
|
||||
func TestForkChoice_IsCanonicalReorg(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, [32]byte{'2'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, [32]byte{'3'}, [32]byte{'1'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, [32]byte{'4'}, [32]byte{'2'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, [32]byte{'5'}, [32]byte{'4'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 6, [32]byte{'6'}, [32]byte{'5'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, [32]byte{'2'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, [32]byte{'4'}, [32]byte{'2'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, [32]byte{'5'}, [32]byte{'4'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 6, [32]byte{'6'}, [32]byte{'5'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
f.store.nodesLock.Lock()
|
||||
f.store.nodeByRoot[[32]byte{'3'}].balance = 10
|
||||
@@ -114,9 +114,9 @@ func TestForkChoice_IsCanonicalReorg(t *testing.T) {
|
||||
func TestForkChoice_AncestorRoot(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(3), indexToHash(2), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
f.store.treeRootNode = f.store.nodeByRoot[indexToHash(1)]
|
||||
f.store.treeRootNode.parent = nil
|
||||
|
||||
@@ -140,8 +140,8 @@ func TestForkChoice_AncestorRoot(t *testing.T) {
|
||||
func TestForkChoice_AncestorEqualSlot(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'3'}, [32]byte{'1'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
r, err := f.AncestorRoot(ctx, [32]byte{'3'}, 100)
|
||||
require.NoError(t, err)
|
||||
@@ -152,8 +152,8 @@ func TestForkChoice_AncestorEqualSlot(t *testing.T) {
|
||||
func TestForkChoice_AncestorLowerSlot(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 200, [32]byte{'3'}, [32]byte{'1'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 200, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
r, err := f.AncestorRoot(ctx, [32]byte{'3'}, 150)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// 0
|
||||
// /
|
||||
// 2 <- head
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -33,7 +33,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// 0
|
||||
// / \
|
||||
// head -> 2 1
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -44,7 +44,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// head -> 2 1
|
||||
// |
|
||||
// 3
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -55,7 +55,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// 2 1
|
||||
// | |
|
||||
// head -> 4 3
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(2), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -68,7 +68,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// head -> 4 3
|
||||
// |
|
||||
// 5 <- justified epoch = 2
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), 2, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 2, 1))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -107,7 +107,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// 5
|
||||
// |
|
||||
// 6 <- head
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(5), 2, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(5), params.BeaconConfig().ZeroHash, 2, 1))
|
||||
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(6), r, "Incorrect head for with justified epoch at 2")
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
func TestNode_ApplyWeightChanges_PositiveChange(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), 0, 0))
|
||||
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))
|
||||
|
||||
// The updated balances of each node is 100
|
||||
s := f.store
|
||||
@@ -36,9 +36,9 @@ func TestNode_ApplyWeightChanges_PositiveChange(t *testing.T) {
|
||||
func TestNode_ApplyWeightChanges_NegativeChange(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), 0, 0))
|
||||
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))
|
||||
|
||||
// The updated balances of each node is 100
|
||||
s := f.store
|
||||
@@ -63,7 +63,7 @@ func TestNode_UpdateBestDescendant_NonViableChild(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
// Input child is not viable.
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 2, 3))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 2, 3))
|
||||
|
||||
// Verify parent's best child and best descendant are `none`.
|
||||
s := f.store
|
||||
@@ -76,7 +76,7 @@ func TestNode_UpdateBestDescendant_ViableChild(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
// Input child is best descendant
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
s := f.store
|
||||
assert.Equal(t, 1, len(s.treeRootNode.children))
|
||||
@@ -87,8 +87,8 @@ func TestNode_UpdateBestDescendant_HigherWeightChild(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
// Input child is best descendant
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
s := f.store
|
||||
s.nodeByRoot[indexToHash(1)].weight = 100
|
||||
@@ -103,8 +103,8 @@ func TestNode_UpdateBestDescendant_LowerWeightChild(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
// Input child is best descendant
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
s := f.store
|
||||
s.nodeByRoot[indexToHash(1)].weight = 200
|
||||
@@ -119,9 +119,9 @@ func TestNode_TestDepth(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
// Input child is best descendant
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
s := f.store
|
||||
require.Equal(t, s.nodeByRoot[indexToHash(2)].depth(), uint64(2))
|
||||
@@ -151,11 +151,11 @@ func TestNode_ViableForHead(t *testing.T) {
|
||||
func TestNode_LeadsToViableHead(t *testing.T) {
|
||||
f := setup(4, 3)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(1), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, indexToHash(4), indexToHash(2), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(5), indexToHash(3), 4, 3))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(5), indexToHash(3), params.BeaconConfig().ZeroHash, 4, 3))
|
||||
|
||||
require.Equal(t, true, f.store.treeRootNode.leadsToViableHead(4, 3))
|
||||
require.Equal(t, true, f.store.nodeByRoot[indexToHash(5)].leadsToViableHead(4, 3))
|
||||
@@ -172,13 +172,13 @@ func TestNode_SetFullyValidated(t *testing.T) {
|
||||
// \
|
||||
// -- 5 (true)
|
||||
//
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.SetOptimisticToValid(ctx, params.BeaconConfig().ZeroHash))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.SetOptimisticToValid(ctx, indexToHash(1)))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, indexToHash(4), indexToHash(3), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(5), indexToHash(1), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, indexToHash(4), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(5), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
opt, err := f.IsOptimistic(ctx, indexToHash(5))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -6,16 +6,17 @@ import (
|
||||
|
||||
// removeNode removes the node with the given root and all of its children
|
||||
// from the Fork Choice Store.
|
||||
func (s *Store) removeNode(ctx context.Context, root [32]byte) error {
|
||||
func (s *Store) removeNode(ctx context.Context, root [32]byte) ([][32]byte, error) {
|
||||
s.nodesLock.Lock()
|
||||
defer s.nodesLock.Unlock()
|
||||
invalidRoots := make([][32]byte, 0)
|
||||
|
||||
node, ok := s.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return ErrNilNode
|
||||
return invalidRoots, ErrNilNode
|
||||
}
|
||||
if !node.optimistic || node.parent == nil {
|
||||
return errInvalidOptimisticStatus
|
||||
return invalidRoots, errInvalidOptimisticStatus
|
||||
}
|
||||
children := node.parent.children
|
||||
if len(children) == 1 {
|
||||
@@ -31,20 +32,21 @@ func (s *Store) removeNode(ctx context.Context, root [32]byte) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
return s.removeNodeAndChildren(ctx, node)
|
||||
return s.removeNodeAndChildren(ctx, node, invalidRoots)
|
||||
}
|
||||
|
||||
// removeNodeAndChildren removes `node` and all of its descendant from the Store
|
||||
func (s *Store) removeNodeAndChildren(ctx context.Context, node *Node) error {
|
||||
func (s *Store) removeNodeAndChildren(ctx context.Context, node *Node, invalidRoots [][32]byte) ([][32]byte, error) {
|
||||
var err error
|
||||
for _, child := range node.children {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
return invalidRoots, ctx.Err()
|
||||
}
|
||||
if err := s.removeNodeAndChildren(ctx, child); err != nil {
|
||||
return err
|
||||
if invalidRoots, err = s.removeNodeAndChildren(ctx, child, invalidRoots); err != nil {
|
||||
return invalidRoots, err
|
||||
}
|
||||
}
|
||||
|
||||
invalidRoots = append(invalidRoots, node.root)
|
||||
delete(s.nodeByRoot, node.root)
|
||||
return nil
|
||||
return invalidRoots, nil
|
||||
}
|
||||
|
||||
@@ -25,46 +25,55 @@ func TestPruneInvalid(t *testing.T) {
|
||||
tests := []struct {
|
||||
root [32]byte // the root of the new INVALID block
|
||||
wantedNodeNumber int
|
||||
wantedRoots [][32]byte
|
||||
}{
|
||||
{
|
||||
[32]byte{'j'},
|
||||
12,
|
||||
[][32]byte{[32]byte{'j'}},
|
||||
},
|
||||
{
|
||||
[32]byte{'c'},
|
||||
4,
|
||||
[][32]byte{[32]byte{'f'}, [32]byte{'e'}, [32]byte{'i'}, [32]byte{'h'}, [32]byte{'l'},
|
||||
[32]byte{'k'}, [32]byte{'g'}, [32]byte{'d'}, [32]byte{'c'}},
|
||||
},
|
||||
{
|
||||
[32]byte{'i'},
|
||||
12,
|
||||
[][32]byte{[32]byte{'i'}},
|
||||
},
|
||||
{
|
||||
[32]byte{'h'},
|
||||
11,
|
||||
[][32]byte{[32]byte{'i'}, [32]byte{'h'}},
|
||||
},
|
||||
{
|
||||
[32]byte{'g'},
|
||||
8,
|
||||
[][32]byte{[32]byte{'i'}, [32]byte{'h'}, [32]byte{'l'}, [32]byte{'k'}, [32]byte{'g'}},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
ctx := context.Background()
|
||||
f := setup(1, 1)
|
||||
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, 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))
|
||||
|
||||
require.NoError(t, f.store.removeNode(context.Background(), tc.root))
|
||||
roots, err := f.store.removeNode(context.Background(), tc.root)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, tc.wantedRoots, roots)
|
||||
require.Equal(t, tc.wantedNodeNumber, f.NodeCount())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
slot,
|
||||
newRoot,
|
||||
headRoot,
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -69,6 +70,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
slot,
|
||||
newRoot,
|
||||
headRoot,
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -94,6 +96,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
slot,
|
||||
newRoot,
|
||||
headRoot,
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -119,6 +122,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
slot,
|
||||
newRoot,
|
||||
headRoot,
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -188,6 +192,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
honestBlockSlot,
|
||||
honestBlock,
|
||||
zeroHash,
|
||||
zeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -204,6 +209,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
maliciouslyWithheldBlockSlot,
|
||||
maliciouslyWithheldBlock,
|
||||
zeroHash,
|
||||
zeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -252,6 +258,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
honestBlockSlot,
|
||||
honestBlock,
|
||||
zeroHash,
|
||||
zeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -270,6 +277,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
maliciouslyWithheldBlockSlot,
|
||||
maliciouslyWithheldBlock,
|
||||
zeroHash,
|
||||
zeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -325,6 +333,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
cSlot,
|
||||
c,
|
||||
a, // parent
|
||||
zeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -348,6 +357,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
bSlot,
|
||||
b,
|
||||
a, // parent
|
||||
zeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -371,6 +381,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
dSlot,
|
||||
d,
|
||||
b, // parent
|
||||
zeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
|
||||
@@ -96,7 +96,7 @@ func (s *Store) head(ctx context.Context, justifiedRoot [32]byte) ([32]byte, err
|
||||
// It then updates the new node's parent with best child and descendant node.
|
||||
func (s *Store) insert(ctx context.Context,
|
||||
slot types.Slot,
|
||||
root, parentRoot [fieldparams.RootLength]byte,
|
||||
root, parentRoot, payloadHash [fieldparams.RootLength]byte,
|
||||
justifiedEpoch, finalizedEpoch types.Epoch) error {
|
||||
_, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.insert")
|
||||
defer span.End()
|
||||
@@ -118,6 +118,7 @@ func (s *Store) insert(ctx context.Context,
|
||||
justifiedEpoch: justifiedEpoch,
|
||||
finalizedEpoch: finalizedEpoch,
|
||||
optimistic: true,
|
||||
payloadHash: payloadHash,
|
||||
}
|
||||
|
||||
s.nodeByRoot[root] = n
|
||||
|
||||
@@ -33,14 +33,14 @@ func TestStore_FinalizedEpoch(t *testing.T) {
|
||||
|
||||
func TestStore_NodeCount(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.Equal(t, 2, f.NodeCount())
|
||||
}
|
||||
|
||||
func TestStore_NodeByRoot(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
node0 := f.store.treeRootNode
|
||||
node1 := node0.children[0]
|
||||
node2 := node1.children[0]
|
||||
@@ -61,7 +61,7 @@ func TestStore_NodeByRoot(t *testing.T) {
|
||||
|
||||
func TestForkChoice_HasNode(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.Equal(t, true, f.HasNode(indexToHash(1)))
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func TestStore_Head_UnknownJustifiedRoot(t *testing.T) {
|
||||
|
||||
func TestStore_Head_Itself(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
|
||||
// Since the justified node does not have a best descendant so the best node
|
||||
// is itself.
|
||||
@@ -85,10 +85,10 @@ func TestStore_Head_Itself(t *testing.T) {
|
||||
|
||||
func TestStore_Head_BestDescendant(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(3), indexToHash(1), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(4), indexToHash(2), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
h, err := f.store.head(context.Background(), indexToHash(1))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, h, indexToHash(4))
|
||||
@@ -97,9 +97,9 @@ func TestStore_Head_BestDescendant(t *testing.T) {
|
||||
func TestStore_UpdateBestDescendant_ContextCancelled(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
f := setup(0, 0)
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
cancel()
|
||||
err := f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0)
|
||||
err := f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0)
|
||||
require.ErrorContains(t, "context canceled", err)
|
||||
}
|
||||
|
||||
@@ -108,10 +108,12 @@ func TestStore_Insert(t *testing.T) {
|
||||
treeRootNode := &Node{slot: 0, root: indexToHash(0)}
|
||||
nodeByRoot := map[[32]byte]*Node{indexToHash(0): treeRootNode}
|
||||
s := &Store{nodeByRoot: nodeByRoot, treeRootNode: treeRootNode}
|
||||
require.NoError(t, s.insert(context.Background(), 100, indexToHash(100), indexToHash(0), 1, 1))
|
||||
payloadHash := [32]byte{'a'}
|
||||
require.NoError(t, s.insert(context.Background(), 100, indexToHash(100), indexToHash(0), payloadHash, 1, 1))
|
||||
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")
|
||||
assert.Equal(t, payloadHash, treeRootNode.children[0].payloadHash, "Incorrect payload hash")
|
||||
child := treeRootNode.children[0]
|
||||
assert.Equal(t, types.Epoch(1), child.justifiedEpoch, "Incorrect justification")
|
||||
assert.Equal(t, types.Epoch(1), child.finalizedEpoch, "Incorrect finalization")
|
||||
@@ -132,9 +134,9 @@ func TestStore_Prune_LessThanThreshold(t *testing.T) {
|
||||
numOfNodes := uint64(100)
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
for i := uint64(2); i < numOfNodes; i++ {
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
}
|
||||
|
||||
s := f.store
|
||||
@@ -151,9 +153,9 @@ func TestStore_Prune_MoreThanThreshold(t *testing.T) {
|
||||
numOfNodes := uint64(100)
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
for i := uint64(2); i < numOfNodes; i++ {
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
}
|
||||
|
||||
s := f.store
|
||||
@@ -169,9 +171,9 @@ func TestStore_Prune_MoreThanOnce(t *testing.T) {
|
||||
numOfNodes := uint64(100)
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
for i := uint64(2); i < numOfNodes; i++ {
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
}
|
||||
|
||||
s := f.store
|
||||
@@ -196,8 +198,8 @@ func TestStore_Prune_MoreThanOnce(t *testing.T) {
|
||||
func TestStore_Prune_NoDanglingBranch(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
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), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
f.store.pruneThreshold = 0
|
||||
|
||||
s := f.store
|
||||
@@ -221,18 +223,18 @@ func TestStore_tips(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := setup(1, 1)
|
||||
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, 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))
|
||||
expectedMap := map[[32]byte]types.Slot{
|
||||
[32]byte{'f'}: 105,
|
||||
[32]byte{'i'}: 106,
|
||||
@@ -250,8 +252,8 @@ func TestStore_tips(t *testing.T) {
|
||||
func TestStore_PruneMapsNodes(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
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), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
|
||||
s := f.store
|
||||
s.pruneThreshold = 0
|
||||
@@ -263,9 +265,9 @@ func TestStore_PruneMapsNodes(t *testing.T) {
|
||||
func TestStore_HasParent(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.Equal(t, false, f.HasParent(params.BeaconConfig().ZeroHash))
|
||||
require.Equal(t, true, f.HasParent(indexToHash(1)))
|
||||
require.Equal(t, true, f.HasParent(indexToHash(2)))
|
||||
|
||||
@@ -35,6 +35,7 @@ type Store struct {
|
||||
type Node struct {
|
||||
slot types.Slot // slot of the block converted to the node.
|
||||
root [fieldparams.RootLength]byte // root of the block converted to the node.
|
||||
payloadHash [fieldparams.RootLength]byte // payloadHash of the block converted to the node.
|
||||
parent *Node // parent index of this node.
|
||||
children []*Node // the list of direct children of this Node
|
||||
justifiedEpoch types.Epoch // justifiedEpoch of this node.
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 0
|
||||
// /
|
||||
// 2 <- head
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -32,7 +32,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 0
|
||||
// / \
|
||||
// head -> 2 1
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -62,7 +62,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// head -> 2 1
|
||||
// |
|
||||
// 3
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -98,7 +98,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 3
|
||||
// |
|
||||
// 4 <- head
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(3), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -114,7 +114,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 4 <- head
|
||||
// /
|
||||
// 5 <- justified epoch = 2
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 2, 2))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -130,7 +130,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 4 <- head
|
||||
// / \
|
||||
// 5 6 <- justified epoch = 0
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
// Moved 2 votes to block 5:
|
||||
// 0
|
||||
@@ -142,7 +142,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 4
|
||||
// / \
|
||||
// 2 votes-> 5 6
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
f.ProcessAttestation(context.Background(), []uint64{0, 1}, indexToHash(5), 4)
|
||||
|
||||
@@ -163,9 +163,9 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 8
|
||||
// |
|
||||
// 9
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(7), indexToHash(5), 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(8), indexToHash(7), 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(9), indexToHash(8), 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(7), indexToHash(5), params.BeaconConfig().ZeroHash, 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(8), indexToHash(7), params.BeaconConfig().ZeroHash, 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(9), indexToHash(8), params.BeaconConfig().ZeroHash, 2, 2))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -210,7 +210,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// / \
|
||||
// 2 votes->9 10
|
||||
f.ProcessAttestation(context.Background(), []uint64{0, 1}, indexToHash(9), 5)
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(10), indexToHash(8), 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(10), indexToHash(8), params.BeaconConfig().ZeroHash, 2, 2))
|
||||
|
||||
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
|
||||
require.NoError(t, err)
|
||||
@@ -289,7 +289,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 9 10
|
||||
// |
|
||||
// head-> 11
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(11), indexToHash(9), 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(11), indexToHash(9), params.BeaconConfig().ZeroHash, 2, 2))
|
||||
|
||||
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -33,6 +33,7 @@ type BlockProcessor interface {
|
||||
slot types.Slot,
|
||||
root [32]byte,
|
||||
parentRoot [32]byte,
|
||||
payloadHash [32]byte,
|
||||
justifiedEpoch types.Epoch,
|
||||
finalizedEpoch types.Epoch,
|
||||
) error
|
||||
@@ -70,5 +71,5 @@ type Getter interface {
|
||||
// Setter allows to set forkchoice information
|
||||
type Setter interface {
|
||||
SetOptimisticToValid(context.Context, [fieldparams.RootLength]byte) error
|
||||
SetOptimisticToInvalid(context.Context, [fieldparams.RootLength]byte) error
|
||||
SetOptimisticToInvalid(context.Context, [fieldparams.RootLength]byte) ([][32]byte, error)
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ func TestFFGUpdates_OneBranch(t *testing.T) {
|
||||
// 2 <- justified: 1, finalized: 0
|
||||
// |
|
||||
// 3 <- justified: 2, finalized: 1
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(3), indexToHash(2), 2, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 2, 1))
|
||||
|
||||
// With starting justified epoch at 0, the head should be 3:
|
||||
// 0 <- start
|
||||
@@ -89,17 +89,17 @@ func TestFFGUpdates_TwoBranches(t *testing.T) {
|
||||
// | |
|
||||
// justified: 2, finalized: 0 -> 9 10 <- justified: 2, finalized: 0
|
||||
// Left branch.
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(3), indexToHash(1), 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(5), indexToHash(3), 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(7), indexToHash(5), 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(9), indexToHash(7), 2, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(5), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(7), indexToHash(5), params.BeaconConfig().ZeroHash, 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(9), indexToHash(7), params.BeaconConfig().ZeroHash, 2, 0))
|
||||
// Right branch.
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(4), indexToHash(2), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(6), indexToHash(4), 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(8), indexToHash(6), 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(10), indexToHash(8), 2, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(6), indexToHash(4), params.BeaconConfig().ZeroHash, 0, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(8), indexToHash(6), params.BeaconConfig().ZeroHash, 1, 0))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(10), indexToHash(8), params.BeaconConfig().ZeroHash, 2, 0))
|
||||
|
||||
// With start at 0, the head should be 10:
|
||||
// 0 <-- start
|
||||
|
||||
@@ -92,6 +92,7 @@ func copyNode(node *Node) *Node {
|
||||
slot: node.slot,
|
||||
root: copiedRoot,
|
||||
parent: node.parent,
|
||||
payloadHash: node.payloadHash,
|
||||
justifiedEpoch: node.justifiedEpoch,
|
||||
finalizedEpoch: node.finalizedEpoch,
|
||||
weight: node.weight,
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// 0
|
||||
// /
|
||||
// 2 <- head
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -33,7 +33,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// 0
|
||||
// / \
|
||||
// head -> 2 1
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -44,7 +44,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// head -> 2 1
|
||||
// |
|
||||
// 3
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -55,7 +55,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// 2 1
|
||||
// | |
|
||||
// head -> 4 3
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(2), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -68,7 +68,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// head -> 4 3
|
||||
// |
|
||||
// 5 <- justified epoch = 2
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), 2, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 2, 1))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -107,7 +107,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// 5
|
||||
// |
|
||||
// 6 <- head
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(5), 2, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(5), params.BeaconConfig().ZeroHash, 2, 1))
|
||||
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(6), r, "Incorrect head for with justified epoch at 2")
|
||||
|
||||
@@ -104,6 +104,8 @@ func (s *Store) findSyncedTip(ctx context.Context, node *Node, syncedTips *optim
|
||||
// SetOptimisticToValid is called with the root of a block that was returned as
|
||||
// VALID by the EL. This routine recomputes and updates the synced_tips map to
|
||||
// account for this new tip.
|
||||
// WARNING: This method returns an error if the root is not found in forkchoice or
|
||||
// if the root is not a leaf of the fork choice tree.
|
||||
func (f *ForkChoice) SetOptimisticToValid(ctx context.Context, root [32]byte) error {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
@@ -206,30 +208,31 @@ func (f *ForkChoice) SetOptimisticToValid(ctx context.Context, root [32]byte) er
|
||||
}
|
||||
|
||||
// SetOptimisticToInvalid updates the synced_tips map when the block with the given root becomes INVALID.
|
||||
func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [32]byte) error {
|
||||
func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [32]byte) ([][32]byte, error) {
|
||||
f.store.nodesLock.Lock()
|
||||
defer f.store.nodesLock.Unlock()
|
||||
invalidRoots := make([][32]byte, 0)
|
||||
idx, ok := f.store.nodesIndices[root]
|
||||
if !ok {
|
||||
return errInvalidNodeIndex
|
||||
return invalidRoots, errInvalidNodeIndex
|
||||
}
|
||||
node := f.store.nodes[idx]
|
||||
// We only support changing status for the tips in Fork Choice store.
|
||||
if node.bestChild != NonExistentNode {
|
||||
return errInvalidNodeIndex
|
||||
return invalidRoots, errInvalidNodeIndex
|
||||
}
|
||||
|
||||
parentIndex := node.parent
|
||||
// This should not happen
|
||||
if parentIndex == NonExistentNode {
|
||||
return errInvalidNodeIndex
|
||||
return invalidRoots, errInvalidNodeIndex
|
||||
}
|
||||
// Update the weights of the nodes subtracting the INVALID node's weight
|
||||
weight := node.weight
|
||||
node = f.store.nodes[parentIndex]
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
return invalidRoots, ctx.Err()
|
||||
}
|
||||
node.weight -= weight
|
||||
if node.parent == NonExistentNode {
|
||||
@@ -242,6 +245,7 @@ func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [32]byte)
|
||||
// delete the invalid node, order is important
|
||||
f.store.nodes = append(f.store.nodes[:idx], f.store.nodes[idx+1:]...)
|
||||
delete(f.store.nodesIndices, root)
|
||||
invalidRoots = append(invalidRoots, root)
|
||||
// Fix parent and best child for each node
|
||||
for _, node := range f.store.nodes {
|
||||
if node.parent == NonExistentNode {
|
||||
@@ -268,7 +272,7 @@ func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [32]byte)
|
||||
err := f.store.updateBestChildAndDescendant(
|
||||
parentIndex, uint64(childIndex))
|
||||
if err != nil {
|
||||
return err
|
||||
return invalidRoots, err
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -281,24 +285,24 @@ func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [32]byte)
|
||||
parentRoot := parent.root
|
||||
_, ok = f.syncedTips.validatedTips[parentRoot]
|
||||
if !ok {
|
||||
return nil
|
||||
return invalidRoots, nil
|
||||
}
|
||||
|
||||
leaves, err := f.store.leaves()
|
||||
if err != nil {
|
||||
return err
|
||||
return invalidRoots, err
|
||||
}
|
||||
|
||||
for _, i := range leaves {
|
||||
node = f.store.nodes[i]
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
return invalidRoots, ctx.Err()
|
||||
}
|
||||
|
||||
// Return early if the parent is still a synced tip
|
||||
if node.root == parentRoot {
|
||||
return nil
|
||||
return invalidRoots, nil
|
||||
}
|
||||
_, ok = f.syncedTips.validatedTips[node.root]
|
||||
if ok {
|
||||
@@ -312,5 +316,5 @@ func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [32]byte)
|
||||
}
|
||||
delete(f.syncedTips.validatedTips, parentRoot)
|
||||
syncedTipsCount.Set(float64(len(f.syncedTips.validatedTips)))
|
||||
return nil
|
||||
return invalidRoots, nil
|
||||
}
|
||||
|
||||
@@ -220,18 +220,18 @@ func TestSetOptimisticToValid(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := setup(1, 1)
|
||||
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, 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))
|
||||
tests := []struct {
|
||||
root [32]byte // the root of the new VALID block
|
||||
tips map[[32]byte]types.Slot // the old synced tips
|
||||
@@ -356,6 +356,7 @@ func TestSetOptimisticToInvalid(t *testing.T) {
|
||||
newBestChild uint64
|
||||
newBestDescendant uint64
|
||||
newParentWeight uint64
|
||||
returnedRoots [][32]byte
|
||||
}{
|
||||
{
|
||||
[32]byte{'j'},
|
||||
@@ -368,6 +369,7 @@ func TestSetOptimisticToInvalid(t *testing.T) {
|
||||
3,
|
||||
4,
|
||||
8,
|
||||
[][32]byte{[32]byte{'j'}},
|
||||
},
|
||||
{
|
||||
[32]byte{'j'},
|
||||
@@ -378,6 +380,7 @@ func TestSetOptimisticToInvalid(t *testing.T) {
|
||||
3,
|
||||
4,
|
||||
8,
|
||||
[][32]byte{[32]byte{'j'}},
|
||||
},
|
||||
{
|
||||
[32]byte{'i'},
|
||||
@@ -391,6 +394,7 @@ func TestSetOptimisticToInvalid(t *testing.T) {
|
||||
NonExistentNode,
|
||||
NonExistentNode,
|
||||
1,
|
||||
[][32]byte{[32]byte{'i'}},
|
||||
},
|
||||
{
|
||||
[32]byte{'i'},
|
||||
@@ -403,24 +407,25 @@ func TestSetOptimisticToInvalid(t *testing.T) {
|
||||
NonExistentNode,
|
||||
NonExistentNode,
|
||||
1,
|
||||
[][32]byte{[32]byte{'i'}},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
ctx := context.Background()
|
||||
f := setup(1, 1)
|
||||
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, 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))
|
||||
weights := []uint64{10, 10, 9, 7, 1, 6, 2, 3, 1, 1, 1, 0, 0}
|
||||
f.syncedTips.Lock()
|
||||
f.syncedTips.validatedTips = tc.tips
|
||||
@@ -439,8 +444,9 @@ func TestSetOptimisticToInvalid(t *testing.T) {
|
||||
require.NotEqual(t, NonExistentNode, parentIndex)
|
||||
parent := f.store.nodes[parentIndex]
|
||||
f.store.nodesLock.Unlock()
|
||||
err := f.SetOptimisticToInvalid(context.Background(), tc.root)
|
||||
roots, err := f.SetOptimisticToInvalid(context.Background(), tc.root)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, tc.returnedRoots, roots)
|
||||
f.syncedTips.RLock()
|
||||
_, parentSyncedTip := f.syncedTips.validatedTips[parent.root]
|
||||
f.syncedTips.RUnlock()
|
||||
@@ -467,18 +473,18 @@ func TestFindSyncedTip(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := setup(1, 1)
|
||||
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, 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))
|
||||
tests := []struct {
|
||||
root [32]byte // the root of the block
|
||||
tips map[[32]byte]types.Slot // the synced tips
|
||||
@@ -553,11 +559,11 @@ func TestFindSyncedTip(t *testing.T) {
|
||||
func TestIsOptimistic_DeadLock(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := setup(1, 1)
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 90, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'c'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'e'}, params.BeaconConfig().ZeroHash, 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, 90, [32]byte{'b'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'c'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'d'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'e'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
tips := map[[32]byte]types.Slot{
|
||||
[32]byte{'a'}: 100,
|
||||
[32]byte{'d'}: 102,
|
||||
|
||||
@@ -2,6 +2,7 @@ package protoarray
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -27,6 +28,10 @@ func (f *ForkChoice) BoostProposerRoot(_ context.Context, blockSlot types.Slot,
|
||||
|
||||
// Only update the boosted proposer root to the incoming block root
|
||||
// if the block is for the current, clock-based slot and the block was timely.
|
||||
fmt.Println(genesisTime.Unix())
|
||||
fmt.Println(time.Now().Unix())
|
||||
fmt.Println(timeIntoSlot, secondsPerSlot, params.BeaconConfig().IntervalsPerSlot, secondsPerSlot/params.BeaconConfig().IntervalsPerSlot)
|
||||
fmt.Println(currentSlot == blockSlot, isBeforeAttestingInterval)
|
||||
if currentSlot == blockSlot && isBeforeAttestingInterval {
|
||||
f.store.proposerBoostLock.Lock()
|
||||
f.store.proposerBoostRoot = blockRoot
|
||||
@@ -38,6 +43,7 @@ func (f *ForkChoice) BoostProposerRoot(_ context.Context, blockSlot types.Slot,
|
||||
// ResetBoostedProposerRoot sets the value of the proposer boosted root to zeros.
|
||||
func (f *ForkChoice) ResetBoostedProposerRoot(_ context.Context) error {
|
||||
f.store.proposerBoostLock.Lock()
|
||||
fmt.Println("Resetting boosted proposer root")
|
||||
f.store.proposerBoostRoot = [32]byte{}
|
||||
f.store.proposerBoostLock.Unlock()
|
||||
return nil
|
||||
|
||||
@@ -46,6 +46,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
slot,
|
||||
newRoot,
|
||||
headRoot,
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -69,6 +70,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
slot,
|
||||
newRoot,
|
||||
headRoot,
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -94,6 +96,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
slot,
|
||||
newRoot,
|
||||
headRoot,
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -119,6 +122,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
slot,
|
||||
newRoot,
|
||||
headRoot,
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -185,6 +189,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
honestBlockSlot,
|
||||
honestBlock,
|
||||
zeroHash,
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -201,6 +206,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
maliciouslyWithheldBlockSlot,
|
||||
maliciouslyWithheldBlock,
|
||||
zeroHash,
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -249,6 +255,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
honestBlockSlot,
|
||||
honestBlock,
|
||||
zeroHash,
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -267,6 +274,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
maliciouslyWithheldBlockSlot,
|
||||
maliciouslyWithheldBlock,
|
||||
zeroHash,
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -322,6 +330,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
cSlot,
|
||||
c,
|
||||
a, // parent
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -345,6 +354,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
bSlot,
|
||||
b,
|
||||
a, // parent
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
@@ -368,6 +378,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
dSlot,
|
||||
d,
|
||||
b, // parent
|
||||
params.BeaconConfig().ZeroHash,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
),
|
||||
|
||||
@@ -148,12 +148,12 @@ func (f *ForkChoice) ProposerBoost() [fieldparams.RootLength]byte {
|
||||
func (f *ForkChoice) InsertOptimisticBlock(
|
||||
ctx context.Context,
|
||||
slot types.Slot,
|
||||
blockRoot, parentRoot [32]byte,
|
||||
blockRoot, parentRoot, payloadHash [32]byte,
|
||||
justifiedEpoch, finalizedEpoch types.Epoch) error {
|
||||
ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.InsertOptimisticBlock")
|
||||
defer span.End()
|
||||
|
||||
return f.store.insert(ctx, slot, blockRoot, parentRoot, justifiedEpoch, finalizedEpoch)
|
||||
return f.store.insert(ctx, slot, blockRoot, parentRoot, payloadHash, justifiedEpoch, finalizedEpoch)
|
||||
}
|
||||
|
||||
// Prune prunes the fork choice store with the new finalized root. The store is only pruned if the input
|
||||
@@ -342,7 +342,7 @@ func (s *Store) updateCanonicalNodes(ctx context.Context, root [32]byte) error {
|
||||
// It then updates the new node's parent with best child and descendant node.
|
||||
func (s *Store) insert(ctx context.Context,
|
||||
slot types.Slot,
|
||||
root, parent [32]byte,
|
||||
root, parent, payloadHash [32]byte,
|
||||
justifiedEpoch, finalizedEpoch types.Epoch) error {
|
||||
_, span := trace.StartSpan(ctx, "protoArrayForkChoice.insert")
|
||||
defer span.End()
|
||||
@@ -371,6 +371,7 @@ func (s *Store) insert(ctx context.Context,
|
||||
bestChild: NonExistentNode,
|
||||
bestDescendant: NonExistentNode,
|
||||
weight: 0,
|
||||
payloadHash: payloadHash,
|
||||
}
|
||||
|
||||
s.nodesIndices[root] = index
|
||||
|
||||
@@ -101,7 +101,7 @@ 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)}
|
||||
require.NoError(t, s.insert(context.Background(), 100, [32]byte{'A'}, [32]byte{'B'}, 1, 1))
|
||||
require.NoError(t, s.insert(context.Background(), 100, [32]byte{'A'}, [32]byte{'B'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
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")
|
||||
@@ -117,13 +117,15 @@ func TestStore_Insert_KnownParent(t *testing.T) {
|
||||
s.nodes = []*Node{{}}
|
||||
p := [32]byte{'B'}
|
||||
s.nodesIndices[p] = 0
|
||||
require.NoError(t, s.insert(context.Background(), 100, [32]byte{'A'}, p, 1, 1))
|
||||
payloadHash := [32]byte{'c'}
|
||||
require.NoError(t, s.insert(context.Background(), 100, [32]byte{'A'}, p, payloadHash, 1, 1))
|
||||
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")
|
||||
assert.Equal(t, types.Epoch(1), s.nodes[1].justifiedEpoch, "Incorrect justification")
|
||||
assert.Equal(t, types.Epoch(1), s.nodes[1].finalizedEpoch, "Incorrect finalization")
|
||||
assert.Equal(t, [32]byte{'A'}, s.nodes[1].root, "Incorrect root")
|
||||
assert.Equal(t, payloadHash, s.nodes[1].payloadHash)
|
||||
}
|
||||
|
||||
func TestStore_ApplyScoreChanges_InvalidDeltaLength(t *testing.T) {
|
||||
@@ -491,18 +493,18 @@ func TestStore_PruneSyncedTips(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := setup(1, 1)
|
||||
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, 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))
|
||||
syncedTips := &optimisticStore{
|
||||
validatedTips: map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
|
||||
@@ -37,6 +37,7 @@ type Store struct {
|
||||
type Node struct {
|
||||
slot types.Slot // slot of the block converted to the node.
|
||||
root [fieldparams.RootLength]byte // root of the block converted to the node.
|
||||
payloadHash [fieldparams.RootLength]byte // payloadHash of the block converted to the node.
|
||||
parent uint64 // parent index of this node.
|
||||
justifiedEpoch types.Epoch // justifiedEpoch of this node.
|
||||
finalizedEpoch types.Epoch // finalizedEpoch of this node.
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 0
|
||||
// /
|
||||
// 2 <- head
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -33,7 +33,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 0
|
||||
// / \
|
||||
// head -> 2 1
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -63,7 +63,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// head -> 2 1
|
||||
// |
|
||||
// 3
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -99,7 +99,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 3
|
||||
// |
|
||||
// 4 <- head
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(3), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -115,7 +115,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 4 <- head
|
||||
// /
|
||||
// 5 <- justified epoch = 2
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 2, 2))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -131,7 +131,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 4 <- head
|
||||
// / \
|
||||
// 5 6 <- justified epoch = 0
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
// Moved 2 votes to block 5:
|
||||
// 0
|
||||
@@ -143,7 +143,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 4
|
||||
// / \
|
||||
// 2 votes-> 5 6
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), 1, 1))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), params.BeaconConfig().ZeroHash, 1, 1))
|
||||
|
||||
f.ProcessAttestation(context.Background(), []uint64{0, 1}, indexToHash(5), 4)
|
||||
|
||||
@@ -164,9 +164,9 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 8
|
||||
// |
|
||||
// 9
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(7), indexToHash(5), 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(8), indexToHash(7), 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(9), indexToHash(8), 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(7), indexToHash(5), params.BeaconConfig().ZeroHash, 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(8), indexToHash(7), params.BeaconConfig().ZeroHash, 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(9), indexToHash(8), params.BeaconConfig().ZeroHash, 2, 2))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -211,7 +211,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// / \
|
||||
// 2 votes->9 10
|
||||
f.ProcessAttestation(context.Background(), []uint64{0, 1}, indexToHash(9), 5)
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(10), indexToHash(8), 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(10), indexToHash(8), params.BeaconConfig().ZeroHash, 2, 2))
|
||||
|
||||
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
|
||||
require.NoError(t, err)
|
||||
@@ -290,7 +290,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 9 10
|
||||
// |
|
||||
// head-> 11
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(11), indexToHash(9), 2, 2))
|
||||
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(11), indexToHash(9), params.BeaconConfig().ZeroHash, 2, 2))
|
||||
|
||||
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -25,7 +25,7 @@ func configureTracing(cliCtx *cli.Context) error {
|
||||
func configureChainConfig(cliCtx *cli.Context) {
|
||||
if cliCtx.IsSet(cmd.ChainConfigFileFlag.Name) {
|
||||
chainConfigFileName := cliCtx.String(cmd.ChainConfigFileFlag.Name)
|
||||
params.LoadChainConfigFile(chainConfigFileName)
|
||||
params.LoadChainConfigFile(chainConfigFileName, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,12 +108,12 @@ func configureInteropConfig(cliCtx *cli.Context) {
|
||||
}
|
||||
|
||||
func configureExecutionSetting(cliCtx *cli.Context) error {
|
||||
if !cliCtx.IsSet(flags.FeeRecipient.Name) {
|
||||
if !cliCtx.IsSet(flags.SuggestedFeeRecipient.Name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := params.BeaconConfig()
|
||||
ha := cliCtx.String(flags.FeeRecipient.Name)
|
||||
ha := cliCtx.String(flags.SuggestedFeeRecipient.Name)
|
||||
if !common.IsHexAddress(ha) {
|
||||
return fmt.Errorf("%s is not a valid fee recipient address", ha)
|
||||
}
|
||||
|
||||
@@ -90,19 +90,19 @@ func TestConfigureExecutionSetting(t *testing.T) {
|
||||
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String(flags.FeeRecipient.Name, "", "")
|
||||
require.NoError(t, set.Set(flags.FeeRecipient.Name, "0xB"))
|
||||
set.String(flags.SuggestedFeeRecipient.Name, "", "")
|
||||
require.NoError(t, set.Set(flags.SuggestedFeeRecipient.Name, "0xB"))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
err := configureExecutionSetting(cliCtx)
|
||||
require.ErrorContains(t, "0xB is not a valid fee recipient address", err)
|
||||
|
||||
require.NoError(t, set.Set(flags.FeeRecipient.Name, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
|
||||
require.NoError(t, set.Set(flags.SuggestedFeeRecipient.Name, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
|
||||
cliCtx = cli.NewContext(&app, set, nil)
|
||||
err = configureExecutionSetting(cliCtx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, common.HexToAddress("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), params.BeaconConfig().DefaultFeeRecipient)
|
||||
|
||||
require.NoError(t, set.Set(flags.FeeRecipient.Name, "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
|
||||
require.NoError(t, set.Set(flags.SuggestedFeeRecipient.Name, "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
|
||||
cliCtx = cli.NewContext(&app, set, nil)
|
||||
err = configureExecutionSetting(cliCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -18,6 +18,7 @@ go_library(
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
"@com_github_golang_jwt_jwt_v4//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -96,6 +97,9 @@ func New(ctx context.Context, endpoint string, opts ...Option) (*Client, error)
|
||||
|
||||
// NewPayload calls the engine_newPayloadV1 method via JSON-RPC.
|
||||
func (c *Client) NewPayload(ctx context.Context, payload *pb.ExecutionPayload) ([]byte, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.NewPayload")
|
||||
defer span.End()
|
||||
|
||||
result := &pb.PayloadStatus{}
|
||||
err := c.rpc.CallContext(ctx, result, NewPayloadMethod, payload)
|
||||
if err != nil {
|
||||
@@ -122,6 +126,9 @@ func (c *Client) NewPayload(ctx context.Context, payload *pb.ExecutionPayload) (
|
||||
func (c *Client) ForkchoiceUpdated(
|
||||
ctx context.Context, state *pb.ForkchoiceState, attrs *pb.PayloadAttributes,
|
||||
) (*pb.PayloadIDBytes, []byte, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ForkchoiceUpdated")
|
||||
defer span.End()
|
||||
|
||||
result := &ForkchoiceUpdatedResponse{}
|
||||
err := c.rpc.CallContext(ctx, result, ForkchoiceUpdatedMethod, state, attrs)
|
||||
if err != nil {
|
||||
@@ -148,6 +155,9 @@ func (c *Client) ForkchoiceUpdated(
|
||||
|
||||
// GetPayload calls the engine_getPayloadV1 method via JSON-RPC.
|
||||
func (c *Client) GetPayload(ctx context.Context, payloadId [8]byte) (*pb.ExecutionPayload, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.GetPayload")
|
||||
defer span.End()
|
||||
|
||||
result := &pb.ExecutionPayload{}
|
||||
err := c.rpc.CallContext(ctx, result, GetPayloadMethod, pb.PayloadIDBytes(payloadId))
|
||||
return result, handleRPCError(err)
|
||||
@@ -157,6 +167,9 @@ func (c *Client) GetPayload(ctx context.Context, payloadId [8]byte) (*pb.Executi
|
||||
func (c *Client) ExchangeTransitionConfiguration(
|
||||
ctx context.Context, cfg *pb.TransitionConfiguration,
|
||||
) error {
|
||||
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ExchangeTransitionConfiguration")
|
||||
defer span.End()
|
||||
|
||||
// We set terminal block number to 0 as the parameter is not set on the consensus layer.
|
||||
zeroBigNum := big.NewInt(0)
|
||||
cfg.TerminalBlockNumber = zeroBigNum.Bytes()
|
||||
@@ -194,6 +207,9 @@ func (c *Client) ExchangeTransitionConfiguration(
|
||||
// LatestExecutionBlock fetches the latest execution engine block by calling
|
||||
// eth_blockByNumber via JSON-RPC.
|
||||
func (c *Client) LatestExecutionBlock(ctx context.Context) (*pb.ExecutionBlock, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.LatestExecutionBlock")
|
||||
defer span.End()
|
||||
|
||||
result := &pb.ExecutionBlock{}
|
||||
err := c.rpc.CallContext(
|
||||
ctx,
|
||||
@@ -208,6 +224,9 @@ func (c *Client) LatestExecutionBlock(ctx context.Context) (*pb.ExecutionBlock,
|
||||
// ExecutionBlockByHash fetches an execution engine block by hash by calling
|
||||
// eth_blockByHash via JSON-RPC.
|
||||
func (c *Client) ExecutionBlockByHash(ctx context.Context, hash common.Hash) (*pb.ExecutionBlock, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ExecutionBlockByHash")
|
||||
defer span.End()
|
||||
|
||||
result := &pb.ExecutionBlock{}
|
||||
err := c.rpc.CallContext(ctx, result, ExecutionBlockByHashMethod, hash, false /* no full transaction objects */)
|
||||
return result, handleRPCError(err)
|
||||
|
||||
@@ -10,7 +10,7 @@ go_library(
|
||||
"structs_marshalling.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/rpc/apimiddleware",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//api/gateway/apimiddleware:go_default_library",
|
||||
"//api/grpc:go_default_library",
|
||||
|
||||
@@ -37,6 +37,7 @@ func (_ *BeaconEndpointFactory) Paths() []string {
|
||||
"/eth/v1/beacon/pool/proposer_slashings",
|
||||
"/eth/v1/beacon/pool/voluntary_exits",
|
||||
"/eth/v1/beacon/pool/sync_committees",
|
||||
"/eth/v1/beacon/weak_subjectivity",
|
||||
"/eth/v1/node/identity",
|
||||
"/eth/v1/node/peers",
|
||||
"/eth/v1/node/peers/{peer_id}",
|
||||
@@ -47,6 +48,7 @@ func (_ *BeaconEndpointFactory) Paths() []string {
|
||||
"/eth/v1/debug/beacon/states/{state_id}",
|
||||
"/eth/v2/debug/beacon/states/{state_id}",
|
||||
"/eth/v1/debug/beacon/heads",
|
||||
"/eth/v2/debug/beacon/heads",
|
||||
"/eth/v1/config/fork_schedule",
|
||||
"/eth/v1/config/deposit_contract",
|
||||
"/eth/v1/config/spec",
|
||||
@@ -142,6 +144,8 @@ func (_ *BeaconEndpointFactory) Create(path string) (*apimiddleware.Endpoint, er
|
||||
endpoint.Hooks = apimiddleware.HookCollection{
|
||||
OnPreDeserializeRequestBodyIntoContainer: wrapSyncCommitteeSignaturesArray,
|
||||
}
|
||||
case "/eth/v1/beacon/weak_subjectivity":
|
||||
endpoint.GetResponse = &WeakSubjectivityResponse{}
|
||||
case "/eth/v1/node/identity":
|
||||
endpoint.GetResponse = &identityResponseJson{}
|
||||
case "/eth/v1/node/peers":
|
||||
@@ -169,6 +173,8 @@ func (_ *BeaconEndpointFactory) Create(path string) (*apimiddleware.Endpoint, er
|
||||
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleGetBeaconStateSSZV2}
|
||||
case "/eth/v1/debug/beacon/heads":
|
||||
endpoint.GetResponse = &forkChoiceHeadsResponseJson{}
|
||||
case "/eth/v2/debug/beacon/heads":
|
||||
endpoint.GetResponse = &v2ForkChoiceHeadsResponseJson{}
|
||||
case "/eth/v1/config/fork_schedule":
|
||||
endpoint.GetResponse = &forkScheduleResponseJson{}
|
||||
case "/eth/v1/config/deposit_contract":
|
||||
|
||||
@@ -19,6 +19,15 @@ type genesisResponse_GenesisJson struct {
|
||||
GenesisForkVersion string `json:"genesis_fork_version" hex:"true"`
|
||||
}
|
||||
|
||||
// WeakSubjectivityResponse is used to marshal/unmarshal the response for the
|
||||
// /eth/v1/beacon/weak_subjectivity endpoint.
|
||||
type WeakSubjectivityResponse struct {
|
||||
Data *struct {
|
||||
Checkpoint *checkpointJson `json:"ws_checkpoint"`
|
||||
StateRoot string `json:"state_root" hex:"true"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// feeRecipientsRequestJson is used in /validator/prepare_beacon_proposers API endpoint.
|
||||
type feeRecipientsRequestJSON struct {
|
||||
Recipients []*feeRecipientJson `json:"recipients"`
|
||||
@@ -186,11 +195,16 @@ type beaconStateV2ResponseJson struct {
|
||||
Data *beaconStateContainerV2Json `json:"data"`
|
||||
}
|
||||
|
||||
// forkChoiceHeadsResponseJson is used in /debug/beacon/heads API endpoint.
|
||||
// forkChoiceHeadsResponseJson is used in /v1/debug/beacon/heads API endpoint.
|
||||
type forkChoiceHeadsResponseJson struct {
|
||||
Data []*forkChoiceHeadJson `json:"data"`
|
||||
}
|
||||
|
||||
// v2ForkChoiceHeadsResponseJson is used in /v2/debug/beacon/heads API endpoint.
|
||||
type v2ForkChoiceHeadsResponseJson struct {
|
||||
Data []*v2ForkChoiceHeadJson `json:"data"`
|
||||
}
|
||||
|
||||
// forkScheduleResponseJson is used in /config/fork_schedule API endpoint.
|
||||
type forkScheduleResponseJson struct {
|
||||
Data []*forkJson `json:"data"`
|
||||
@@ -691,6 +705,12 @@ type forkChoiceHeadJson struct {
|
||||
Slot string `json:"slot"`
|
||||
}
|
||||
|
||||
type v2ForkChoiceHeadJson struct {
|
||||
Root string `json:"root" hex:"true"`
|
||||
Slot string `json:"slot"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
}
|
||||
|
||||
type depositContractJson struct {
|
||||
ChainId string `json:"chain_id"`
|
||||
Address string `json:"address"`
|
||||
|
||||
@@ -35,6 +35,7 @@ go_library(
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//beacon-chain/state/v1:go_default_library",
|
||||
"//beacon-chain/sync:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
|
||||
@@ -5,18 +5,21 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
|
||||
blockfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/block"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
|
||||
rpchelpers "github.com/prysmaticlabs/prysm/beacon-chain/rpc/eth/helpers"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpbv1 "github.com/prysmaticlabs/prysm/proto/eth/v1"
|
||||
ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2"
|
||||
"github.com/prysmaticlabs/prysm/proto/migration"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
"go.opencensus.io/trace"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
@@ -40,6 +43,43 @@ func (e *blockIdParseError) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
// GetWeakSubjectivity computes the starting epoch of the current weak subjectivity period, and then also
|
||||
// determines the best block root and state root to use for a Checkpoint Sync starting from that point.
|
||||
func (bs *Server) GetWeakSubjectivity(ctx context.Context, _ *empty.Empty) (*ethpbv1.WeakSubjectivityResponse, error) {
|
||||
if err := rpchelpers.ValidateSync(ctx, bs.SyncChecker, bs.HeadFetcher, bs.GenesisTimeFetcher); err != nil {
|
||||
// This is already a grpc error, so we can't wrap it any further
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hs, err := bs.HeadFetcher.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "could not get head state")
|
||||
}
|
||||
wsEpoch, err := helpers.LatestWeakSubjectivityEpoch(ctx, hs)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not get weak subjectivity epoch: %v", err)
|
||||
}
|
||||
wsSlot, err := slots.EpochStart(wsEpoch)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not get weak subjectivity slot: %v", err)
|
||||
}
|
||||
cbr, cb, err := bs.CanonicalHistory.BlockForSlot(ctx, wsSlot)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, fmt.Sprintf("could not find highest block below slot %d", wsSlot))
|
||||
}
|
||||
stateRoot := bytesutil.ToBytes32(cb.Block().StateRoot())
|
||||
log.Printf("weak subjectivity checkpoint reported as epoch=%d, block root=%#x, state root=%#x", wsEpoch, cbr, stateRoot)
|
||||
return ðpbv1.WeakSubjectivityResponse{
|
||||
Data: ðpbv1.WeakSubjectivityData{
|
||||
WsCheckpoint: ðpbv1.Checkpoint{
|
||||
Epoch: wsEpoch,
|
||||
Root: cbr[:],
|
||||
},
|
||||
StateRoot: stateRoot[:],
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetBlockHeader retrieves block header for given block id.
|
||||
func (bs *Server) GetBlockHeader(ctx context.Context, req *ethpbv1.BlockRequest) (*ethpbv1.BlockHeaderResponse, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon.GetBlockHeader")
|
||||
@@ -48,14 +88,14 @@ func (bs *Server) GetBlockHeader(ctx context.Context, req *ethpbv1.BlockRequest)
|
||||
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
|
||||
err = handleGetBlockError(blk, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "GetBlockHeader")
|
||||
}
|
||||
v1alpha1Header, err := blk.Header()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not get block header from block: %v", err)
|
||||
}
|
||||
header := migration.V1Alpha1SignedHeaderToV1(v1alpha1Header)
|
||||
headerRoot, err := header.HashTreeRoot()
|
||||
headerRoot, err := header.Message.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not hash block header: %v", err)
|
||||
}
|
||||
@@ -118,7 +158,7 @@ func (bs *Server) ListBlockHeaders(ctx context.Context, req *ethpbv1.BlockHeader
|
||||
return nil, status.Errorf(codes.Internal, "Could not get block header from block: %v", err)
|
||||
}
|
||||
header := migration.V1Alpha1SignedHeaderToV1(v1alpha1Header)
|
||||
headerRoot, err := header.HashTreeRoot()
|
||||
headerRoot, err := header.Message.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not hash block header: %v", err)
|
||||
}
|
||||
@@ -148,112 +188,59 @@ func (bs *Server) SubmitBlock(ctx context.Context, req *ethpbv2.SignedBeaconBloc
|
||||
ctx, span := trace.StartSpan(ctx, "beacon.SubmitBlock")
|
||||
defer span.End()
|
||||
|
||||
phase0BlkContainer, ok := req.Message.(*ethpbv2.SignedBeaconBlockContainerV2_Phase0Block)
|
||||
if ok {
|
||||
phase0Blk := phase0BlkContainer.Phase0Block
|
||||
v1alpha1Blk, err := migration.V1ToV1Alpha1SignedBlock(ðpbv1.SignedBeaconBlock{Block: phase0Blk, Signature: req.Signature})
|
||||
var wsb block.SignedBeaconBlock
|
||||
var err error
|
||||
var v1alpha1Blk interface{}
|
||||
|
||||
switch blk := req.Message.(type) {
|
||||
case *ethpbv2.SignedBeaconBlockContainerV2_Phase0Block:
|
||||
v1alpha1Blk, err = migration.V1ToV1Alpha1SignedBlock(ðpbv1.SignedBeaconBlock{Block: blk.Phase0Block, Signature: req.Signature})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block")
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block: %v", err)
|
||||
}
|
||||
wrappedPhase0Blk := wrapper.WrappedPhase0SignedBeaconBlock(v1alpha1Blk)
|
||||
|
||||
root, err := phase0Blk.HashTreeRoot()
|
||||
wsb, err = wrapper.WrappedSignedBeaconBlock(v1alpha1Blk)
|
||||
case *ethpbv2.SignedBeaconBlockContainerV2_AltairBlock:
|
||||
v1alpha1Blk, err = migration.AltairToV1Alpha1SignedBlock(ðpbv2.SignedBeaconBlockAltair{Message: blk.AltairBlock, Signature: req.Signature})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block: %v", 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(
|
||||
"Block proposal received via RPC")
|
||||
bs.BlockNotifier.BlockFeed().Send(&feed.Event{
|
||||
Type: blockfeed.ReceivedBlock,
|
||||
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedPhase0Blk},
|
||||
})
|
||||
}()
|
||||
|
||||
// Broadcast the new block to the network.
|
||||
if err := bs.Broadcaster.Broadcast(ctx, wrappedPhase0Blk.Proto()); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
|
||||
}
|
||||
|
||||
if err := bs.BlockReceiver.ReceiveBlock(ctx, wrappedPhase0Blk, root); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
|
||||
wsb, err = wrapper.WrappedSignedBeaconBlock(v1alpha1Blk)
|
||||
case *ethpbv2.SignedBeaconBlockContainerV2_BellatrixBlock:
|
||||
v1alpha1Blk, err = migration.BellatrixToV1Alpha1SignedBlock(ðpbv2.SignedBeaconBlockBellatrix{Message: blk.BellatrixBlock, Signature: req.Signature})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block: %v", err)
|
||||
}
|
||||
wsb, err = wrapper.WrappedSignedBeaconBlock(v1alpha1Blk)
|
||||
default:
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Unsupported block type %T", req.Message)
|
||||
}
|
||||
|
||||
altairBlkContainer, ok := req.Message.(*ethpbv2.SignedBeaconBlockContainerV2_AltairBlock)
|
||||
if ok {
|
||||
altairBlk := altairBlkContainer.AltairBlock
|
||||
v1alpha1Blk, err := migration.AltairToV1Alpha1SignedBlock(ðpbv2.SignedBeaconBlockAltair{Message: altairBlk, Signature: req.Signature})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block")
|
||||
}
|
||||
wrappedAltairBlk, err := wrapper.WrappedAltairSignedBeaconBlock(v1alpha1Blk)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not prepare Altair block")
|
||||
}
|
||||
|
||||
root, err := altairBlk.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", 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(
|
||||
"Block proposal received via RPC")
|
||||
bs.BlockNotifier.BlockFeed().Send(&feed.Event{
|
||||
Type: blockfeed.ReceivedBlock,
|
||||
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedAltairBlk},
|
||||
})
|
||||
}()
|
||||
|
||||
// Broadcast the new block to the network.
|
||||
if err := bs.Broadcaster.Broadcast(ctx, wrappedAltairBlk.Proto()); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
|
||||
}
|
||||
|
||||
if err := bs.BlockReceiver.ReceiveBlock(ctx, wrappedAltairBlk, root); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not wrap beacon block: %v", err)
|
||||
}
|
||||
|
||||
bellatrixBlkContainer, ok := req.Message.(*ethpbv2.SignedBeaconBlockContainerV2_BellatrixBlock)
|
||||
if ok {
|
||||
bellatrixBlk := bellatrixBlkContainer.BellatrixBlock
|
||||
v1alpha1Blk, err := migration.BellatrixToV1Alpha1SignedBlock(ðpbv2.SignedBeaconBlockBellatrix{Message: bellatrixBlk, Signature: req.Signature})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block")
|
||||
}
|
||||
wrappedBellatrixBlk, err := wrapper.WrappedBellatrixSignedBeaconBlock(v1alpha1Blk)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not prepare bellatrix block")
|
||||
}
|
||||
root, err := wsb.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
|
||||
}
|
||||
|
||||
root, err := bellatrixBlk.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", 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(
|
||||
"Block proposal received via RPC")
|
||||
bs.BlockNotifier.BlockFeed().Send(&feed.Event{
|
||||
Type: blockfeed.ReceivedBlock,
|
||||
Data: &blockfeed.ReceivedBlockData{SignedBlock: wsb},
|
||||
})
|
||||
}()
|
||||
|
||||
// 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(
|
||||
"Block proposal received via RPC")
|
||||
bs.BlockNotifier.BlockFeed().Send(&feed.Event{
|
||||
Type: blockfeed.ReceivedBlock,
|
||||
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedBellatrixBlk},
|
||||
})
|
||||
}()
|
||||
// Broadcast the new block to the network.
|
||||
if err := bs.Broadcaster.Broadcast(ctx, wsb.Proto()); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
|
||||
}
|
||||
|
||||
// Broadcast the new block to the network.
|
||||
if err := bs.Broadcaster.Broadcast(ctx, wrappedBellatrixBlk.Proto()); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
|
||||
}
|
||||
|
||||
if err := bs.BlockReceiver.ReceiveBlock(ctx, wrappedBellatrixBlk, root); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
|
||||
}
|
||||
if err := bs.BlockReceiver.ReceiveBlock(ctx, wsb, root); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
@@ -267,7 +254,7 @@ func (bs *Server) GetBlock(ctx context.Context, req *ethpbv1.BlockRequest) (*eth
|
||||
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
|
||||
err = handleGetBlockError(blk, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "GetBlock")
|
||||
}
|
||||
signedBeaconBlock, err := migration.SignedBeaconBlock(blk)
|
||||
if err != nil {
|
||||
@@ -290,7 +277,7 @@ func (bs *Server) GetBlockSSZ(ctx context.Context, req *ethpbv1.BlockRequest) (*
|
||||
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
|
||||
err = handleGetBlockError(blk, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "GetBlockSSZ")
|
||||
}
|
||||
signedBeaconBlock, err := migration.SignedBeaconBlock(blk)
|
||||
if err != nil {
|
||||
@@ -312,7 +299,7 @@ func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) (
|
||||
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
|
||||
err = handleGetBlockError(blk, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "GetBlockV2")
|
||||
}
|
||||
|
||||
_, err = blk.PbPhase0Block()
|
||||
@@ -389,7 +376,7 @@ func (bs *Server) GetBlockSSZV2(ctx context.Context, req *ethpbv2.BlockRequestV2
|
||||
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
|
||||
err = handleGetBlockError(blk, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "GetBlockSSZV2")
|
||||
}
|
||||
|
||||
_, err = blk.PbPhase0Block()
|
||||
|
||||
@@ -239,6 +239,21 @@ func TestServer_GetBlockHeader(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
expectedBodyRoot, err := tt.want.Block.Body.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
expectedHeader := ðpbv1.BeaconBlockHeader{
|
||||
Slot: tt.want.Block.Slot,
|
||||
ProposerIndex: tt.want.Block.ProposerIndex,
|
||||
ParentRoot: tt.want.Block.ParentRoot,
|
||||
StateRoot: make([]byte, 32),
|
||||
BodyRoot: expectedBodyRoot[:],
|
||||
}
|
||||
expectedHeaderRoot, err := expectedHeader.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
headerRoot, err := header.Data.Header.Message.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedHeaderRoot, headerRoot)
|
||||
|
||||
assert.Equal(t, tt.want.Block.Slot, header.Data.Header.Message.Slot)
|
||||
assert.DeepEqual(t, tt.want.Block.StateRoot, header.Data.Header.Message.StateRoot)
|
||||
assert.DeepEqual(t, tt.want.Block.ParentRoot, header.Data.Header.Message.ParentRoot)
|
||||
@@ -320,6 +335,21 @@ func TestServer_ListBlockHeaders(t *testing.T) {
|
||||
|
||||
require.Equal(t, len(tt.want), len(headers.Data))
|
||||
for i, blk := range tt.want {
|
||||
expectedBodyRoot, err := blk.Block.Body.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
expectedHeader := ðpbv1.BeaconBlockHeader{
|
||||
Slot: blk.Block.Slot,
|
||||
ProposerIndex: blk.Block.ProposerIndex,
|
||||
ParentRoot: blk.Block.ParentRoot,
|
||||
StateRoot: make([]byte, 32),
|
||||
BodyRoot: expectedBodyRoot[:],
|
||||
}
|
||||
expectedHeaderRoot, err := expectedHeader.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
headerRoot, err := headers.Data[i].Header.Message.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedHeaderRoot, headerRoot)
|
||||
|
||||
assert.Equal(t, blk.Block.Slot, headers.Data[i].Header.Message.Slot)
|
||||
assert.DeepEqual(t, blk.Block.StateRoot, headers.Data[i].Header.Message.StateRoot)
|
||||
assert.DeepEqual(t, blk.Block.ParentRoot, headers.Data[i].Header.Message.ParentRoot)
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
v1alpha1validator "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/validator"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/rpc/statefetcher"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/sync"
|
||||
)
|
||||
|
||||
// Server defines a server implementation of the gRPC Beacon Chain service,
|
||||
@@ -34,4 +35,6 @@ type Server struct {
|
||||
StateFetcher statefetcher.Fetcher
|
||||
HeadFetcher blockchain.HeadFetcher
|
||||
V1Alpha1ValidatorServer *v1alpha1validator.Server
|
||||
SyncChecker sync.Checker
|
||||
CanonicalHistory *stategen.CanonicalHistory
|
||||
}
|
||||
|
||||
@@ -117,12 +117,7 @@ func (bs *Server) GetFinalityCheckpoints(ctx context.Context, req *ethpb.StateRe
|
||||
|
||||
st, err = bs.StateFetcher.State(ctx, req.StateId)
|
||||
if err != nil {
|
||||
if stateNotFoundErr, ok := err.(*statefetcher.StateNotFoundError); ok {
|
||||
return nil, status.Errorf(codes.NotFound, "State not found: %v", stateNotFoundErr)
|
||||
} else if parseErr, ok := err.(*statefetcher.StateIdParseError); ok {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Invalid state ID: %v", parseErr)
|
||||
}
|
||||
return nil, status.Errorf(codes.Internal, "Could not get state: %v", err)
|
||||
return nil, helpers.PrepareStateFetchGRPCError(err)
|
||||
}
|
||||
|
||||
return ðpb.StateFinalityCheckpointResponse{
|
||||
|
||||
@@ -130,7 +130,7 @@ func (ds *Server) GetBeaconStateSSZV2(ctx context.Context, req *ethpbv2.StateReq
|
||||
return ðpbv2.BeaconStateSSZResponseV2{Data: sszState}, nil
|
||||
}
|
||||
|
||||
// ListForkChoiceHeads retrieves the fork choice leaves for the current head.
|
||||
// ListForkChoiceHeads retrieves the leaves of the current fork choice tree.
|
||||
func (ds *Server) ListForkChoiceHeads(ctx context.Context, _ *emptypb.Empty) (*ethpbv1.ForkChoiceHeadsResponse, error) {
|
||||
_, span := trace.StartSpan(ctx, "debug.ListForkChoiceHeads")
|
||||
defer span.End()
|
||||
@@ -148,3 +148,27 @@ func (ds *Server) ListForkChoiceHeads(ctx context.Context, _ *emptypb.Empty) (*e
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ListForkChoiceHeadsV2 retrieves the leaves of the current fork choice tree.
|
||||
func (ds *Server) ListForkChoiceHeadsV2(ctx context.Context, _ *emptypb.Empty) (*ethpbv2.ForkChoiceHeadsResponse, error) {
|
||||
_, span := trace.StartSpan(ctx, "debug.ListForkChoiceHeadsV2")
|
||||
defer span.End()
|
||||
|
||||
headRoots, headSlots := ds.HeadFetcher.ChainHeads()
|
||||
resp := ðpbv2.ForkChoiceHeadsResponse{
|
||||
Data: make([]*ethpbv2.ForkChoiceHead, len(headRoots)),
|
||||
}
|
||||
for i := range headRoots {
|
||||
isOptimistic, err := ds.HeadFetcher.IsOptimisticForRoot(ctx, headRoots[i])
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if head is optimistic: %v", err)
|
||||
}
|
||||
resp.Data[i] = ðpbv2.ForkChoiceHead{
|
||||
Root: headRoots[i][:],
|
||||
Slot: headSlots[i],
|
||||
ExecutionOptimistic: isOptimistic,
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -186,3 +186,56 @@ func TestListForkChoiceHeads(t *testing.T) {
|
||||
assert.Equal(t, true, found, "Expected head not found")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListForkChoiceHeadsV2(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
expectedSlotsAndRoots := []struct {
|
||||
Slot types.Slot
|
||||
Root [32]byte
|
||||
}{{
|
||||
Slot: 0,
|
||||
Root: bytesutil.ToBytes32(bytesutil.PadTo([]byte("foo"), 32)),
|
||||
}, {
|
||||
Slot: 1,
|
||||
Root: bytesutil.ToBytes32(bytesutil.PadTo([]byte("bar"), 32)),
|
||||
}}
|
||||
|
||||
server := &Server{
|
||||
HeadFetcher: &blockchainmock.ChainService{},
|
||||
}
|
||||
resp, err := server.ListForkChoiceHeadsV2(ctx, &emptypb.Empty{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, len(resp.Data))
|
||||
for _, sr := range expectedSlotsAndRoots {
|
||||
found := false
|
||||
for _, h := range resp.Data {
|
||||
if h.Slot == sr.Slot {
|
||||
found = true
|
||||
assert.DeepEqual(t, sr.Root[:], h.Root)
|
||||
}
|
||||
assert.Equal(t, false, h.ExecutionOptimistic)
|
||||
}
|
||||
assert.Equal(t, true, found, "Expected head not found")
|
||||
}
|
||||
|
||||
t.Run("optimistic head", func(t *testing.T) {
|
||||
server := &Server{
|
||||
HeadFetcher: &blockchainmock.ChainService{Optimistic: true},
|
||||
}
|
||||
resp, err := server.ListForkChoiceHeadsV2(ctx, &emptypb.Empty{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, len(resp.Data))
|
||||
for _, sr := range expectedSlotsAndRoots {
|
||||
found := false
|
||||
for _, h := range resp.Data {
|
||||
if h.Slot == sr.Slot {
|
||||
found = true
|
||||
assert.DeepEqual(t, sr.Root[:], h.Root)
|
||||
}
|
||||
assert.Equal(t, true, h.ExecutionOptimistic)
|
||||
}
|
||||
assert.Equal(t, true, found, "Expected head not found")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ go_library(
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/rpc/statefetcher:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//beacon-chain/sync:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//proto/eth/v1:go_default_library",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/rpc/statefetcher"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
@@ -9,9 +12,13 @@ import (
|
||||
// PrepareStateFetchGRPCError returns an appropriate gRPC error based on the supplied argument.
|
||||
// The argument error should be a result of fetching state.
|
||||
func PrepareStateFetchGRPCError(err error) error {
|
||||
if errors.Is(err, stategen.ErrSlotBeforeOrigin) {
|
||||
return status.Errorf(codes.NotFound, "lacking historical data needed to fulfill request")
|
||||
}
|
||||
if stateNotFoundErr, ok := err.(*statefetcher.StateNotFoundError); ok {
|
||||
return status.Errorf(codes.NotFound, "State not found: %v", stateNotFoundErr)
|
||||
} else if parseErr, ok := err.(*statefetcher.StateIdParseError); ok {
|
||||
}
|
||||
if parseErr, ok := err.(*statefetcher.StateIdParseError); ok {
|
||||
return status.Errorf(codes.InvalidArgument, "Invalid state ID: %v", parseErr)
|
||||
}
|
||||
return status.Errorf(codes.Internal, "Invalid state ID: %v", err)
|
||||
|
||||
@@ -28,7 +28,7 @@ func ValidateSync(ctx context.Context, syncChecker sync.Checker, headFetcher blo
|
||||
err := grpc.AppendCustomErrorHeader(ctx, syncDetailsContainer)
|
||||
if err != nil {
|
||||
return status.Errorf(
|
||||
codes.InvalidArgument,
|
||||
codes.Internal,
|
||||
"Syncing to latest head, not ready to respond. Could not prepare sync details: %v",
|
||||
err,
|
||||
)
|
||||
|
||||
@@ -59,7 +59,7 @@ func (bs *Server) ListValidatorAssignments(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
requestedState, err := bs.ReplayerBuilder.ForSlot(startSlot).ReplayBlocks(ctx)
|
||||
requestedState, err := bs.ReplayerBuilder.ReplayerForSlot(startSlot).ReplayBlocks(ctx)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("could not replay all blocks from the closest stored state (at slot %d) "+
|
||||
"to the requested epoch (%d) - %v", startSlot, requestedEpoch, err)
|
||||
|
||||
@@ -2,7 +2,6 @@ package beacon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -394,46 +393,43 @@ func (bs *Server) chainHeadRetrieval(ctx context.Context) (*ethpb.ChainHead, err
|
||||
return nil, status.Errorf(codes.Internal, "Could not get head block root: %v", err)
|
||||
}
|
||||
|
||||
isGenesis := func(cp *ethpb.Checkpoint) bool {
|
||||
return bytesutil.ToBytes32(cp.Root) == params.BeaconConfig().ZeroHash && cp.Epoch == 0
|
||||
}
|
||||
// Retrieve genesis block in the event we have genesis checkpoints.
|
||||
genBlock, err := bs.BeaconDB.GenesisBlock(ctx)
|
||||
if err != nil || genBlock == nil || genBlock.IsNil() || genBlock.Block().IsNil() {
|
||||
return nil, status.Error(codes.Internal, "Could not get genesis block")
|
||||
validGenesis := false
|
||||
validateCP := func(cp *ethpb.Checkpoint, name string) error {
|
||||
if bytesutil.ToBytes32(cp.Root) == params.BeaconConfig().ZeroHash && cp.Epoch == 0 {
|
||||
if validGenesis {
|
||||
return nil
|
||||
}
|
||||
// Retrieve genesis block in the event we have genesis checkpoints.
|
||||
genBlock, err := bs.BeaconDB.GenesisBlock(ctx)
|
||||
if err != nil || helpers.BeaconBlockIsNil(genBlock) != nil {
|
||||
return status.Error(codes.Internal, "Could not get genesis block")
|
||||
}
|
||||
validGenesis = true
|
||||
return nil
|
||||
}
|
||||
b, err := bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(cp.Root))
|
||||
if err != nil {
|
||||
return status.Errorf(codes.Internal, "Could not get %s block: %v", name, err)
|
||||
}
|
||||
if err := helpers.BeaconBlockIsNil(b); err != nil {
|
||||
return status.Errorf(codes.Internal, "Could not get %s block: %v", name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
finalizedCheckpoint := bs.FinalizationFetcher.FinalizedCheckpt()
|
||||
if !isGenesis(finalizedCheckpoint) {
|
||||
b, err := bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(finalizedCheckpoint.Root))
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get finalized block")
|
||||
}
|
||||
if err := helpers.BeaconBlockIsNil(b); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not get finalized block: %v", err)
|
||||
}
|
||||
if err := validateCP(finalizedCheckpoint, "finalized"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
justifiedCheckpoint := bs.FinalizationFetcher.CurrentJustifiedCheckpt()
|
||||
if !isGenesis(justifiedCheckpoint) {
|
||||
b, err := bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(justifiedCheckpoint.Root))
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get justified block")
|
||||
}
|
||||
if err := helpers.BeaconBlockIsNil(b); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not get justified block: %v", err)
|
||||
}
|
||||
if err := validateCP(justifiedCheckpoint, "justified"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prevJustifiedCheckpoint := bs.FinalizationFetcher.PreviousJustifiedCheckpt()
|
||||
if !isGenesis(prevJustifiedCheckpoint) {
|
||||
b, err := bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(prevJustifiedCheckpoint.Root))
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get prev justified block")
|
||||
}
|
||||
if err := helpers.BeaconBlockIsNil(b); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not get prev justified block: %v", err)
|
||||
}
|
||||
if err := validateCP(prevJustifiedCheckpoint, "prev justified"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fSlot, err := slots.EpochStart(finalizedCheckpoint.Epoch)
|
||||
@@ -463,40 +459,3 @@ func (bs *Server) chainHeadRetrieval(ctx context.Context) (*ethpb.ChainHead, err
|
||||
PreviousJustifiedBlockRoot: prevJustifiedCheckpoint.Root,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetWeakSubjectivityCheckpoint retrieves weak subjectivity state root, block root, and epoch.
|
||||
func (bs *Server) GetWeakSubjectivityCheckpoint(ctx context.Context, _ *emptypb.Empty) (*ethpb.WeakSubjectivityCheckpoint, error) {
|
||||
hs, err := bs.HeadFetcher.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get head state")
|
||||
}
|
||||
wsEpoch, err := helpers.LatestWeakSubjectivityEpoch(ctx, hs)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get weak subjectivity epoch")
|
||||
}
|
||||
wsSlot, err := slots.EpochStart(wsEpoch)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get weak subjectivity slot")
|
||||
}
|
||||
|
||||
wsState, err := bs.ReplayerBuilder.ForSlot(wsSlot).ReplayBlocks(ctx)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("error replaying blocks for state at slot %d: %v", wsSlot, err)
|
||||
return nil, status.Error(codes.Internal, msg)
|
||||
}
|
||||
|
||||
stateRoot, err := wsState.HashTreeRoot(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get weak subjectivity state root")
|
||||
}
|
||||
blkRoot, err := wsState.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get weak subjectivity block root")
|
||||
}
|
||||
|
||||
return ðpb.WeakSubjectivityCheckpoint{
|
||||
BlockRoot: blkRoot[:],
|
||||
StateRoot: stateRoot[:],
|
||||
Epoch: wsEpoch,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -366,6 +366,60 @@ func TestServer_ListBlocks_Errors(t *testing.T) {
|
||||
assert.Equal(t, int32(0), res.TotalSize, "Wanted total size 0")
|
||||
}
|
||||
|
||||
// ensures that if any of the checkpoints are zero-valued, an error will be generated without genesis being present
|
||||
func TestServer_GetChainHead_NoGenesis(t *testing.T) {
|
||||
db := dbTest.SetupDB(t)
|
||||
|
||||
s, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.SetSlot(1))
|
||||
|
||||
genBlock := util.NewBeaconBlock()
|
||||
genBlock.Block.ParentRoot = bytesutil.PadTo([]byte{'G'}, fieldparams.RootLength)
|
||||
require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(genBlock)))
|
||||
gRoot, err := genBlock.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
cases := []struct {
|
||||
name string
|
||||
zeroSetter func(val *ethpb.Checkpoint) error
|
||||
}{
|
||||
{
|
||||
name: "zero-value prev justified",
|
||||
zeroSetter: s.SetPreviousJustifiedCheckpoint,
|
||||
},
|
||||
{
|
||||
name: "zero-value current justified",
|
||||
zeroSetter: s.SetCurrentJustifiedCheckpoint,
|
||||
},
|
||||
{
|
||||
name: "zero-value finalized",
|
||||
zeroSetter: s.SetFinalizedCheckpoint,
|
||||
},
|
||||
}
|
||||
finalized := ðpb.Checkpoint{Epoch: 1, Root: gRoot[:]}
|
||||
prevJustified := ðpb.Checkpoint{Epoch: 2, Root: gRoot[:]}
|
||||
justified := ðpb.Checkpoint{Epoch: 3, Root: gRoot[:]}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
require.NoError(t, s.SetPreviousJustifiedCheckpoint(prevJustified))
|
||||
require.NoError(t, s.SetCurrentJustifiedCheckpoint(justified))
|
||||
require.NoError(t, s.SetFinalizedCheckpoint(finalized))
|
||||
require.NoError(t, c.zeroSetter(ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]}))
|
||||
})
|
||||
bs := &Server{
|
||||
BeaconDB: db,
|
||||
HeadFetcher: &chainMock.ChainService{Block: wrapper.WrappedPhase0SignedBeaconBlock(genBlock), State: s},
|
||||
FinalizationFetcher: &chainMock.ChainService{
|
||||
FinalizedCheckPoint: s.FinalizedCheckpoint(),
|
||||
CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint(),
|
||||
PreviousJustifiedCheckPoint: s.PreviousJustifiedCheckpoint()},
|
||||
}
|
||||
_, err = bs.GetChainHead(context.Background(), nil)
|
||||
require.ErrorContains(t, "Could not get genesis block", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_GetChainHead_NoFinalizedBlock(t *testing.T) {
|
||||
db := dbTest.SetupDB(t)
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ func (bs *Server) retrieveCommitteesForEpoch(
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
requestedState, err := bs.ReplayerBuilder.ForSlot(startSlot).ReplayBlocks(ctx)
|
||||
requestedState, err := bs.ReplayerBuilder.ReplayerForSlot(startSlot).ReplayBlocks(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, status.Errorf(codes.Internal, "error replaying blocks for state at slot %d: %v", startSlot, err)
|
||||
}
|
||||
|
||||
@@ -76,10 +76,9 @@ func TestServer_ListBeaconCommittees_CurrentEpoch(t *testing.T) {
|
||||
func addDefaultReplayerBuilder(s *Server, h stategen.HistoryAccessor) {
|
||||
cc := &mockstategen.MockCanonicalChecker{Is: true, Err: nil}
|
||||
cs := &mockstategen.MockCurrentSlotter{Slot: math.MaxUint64 - 1}
|
||||
s.ReplayerBuilder = stategen.NewCanonicalBuilder(h, cc, cs)
|
||||
s.ReplayerBuilder = stategen.NewCanonicalHistory(h, cc, cs)
|
||||
}
|
||||
|
||||
// TODO: test failure
|
||||
func TestServer_ListBeaconCommittees_PreviousEpoch(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
params.OverrideBeaconConfig(params.MainnetConfig())
|
||||
|
||||
@@ -65,7 +65,7 @@ func (bs *Server) ListValidatorBalances(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
requestedState, err := bs.ReplayerBuilder.ForSlot(startSlot).ReplayBlocks(ctx)
|
||||
requestedState, err := bs.ReplayerBuilder.ReplayerForSlot(startSlot).ReplayBlocks(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, fmt.Sprintf("error replaying blocks for state at slot %d: %v", startSlot, err))
|
||||
}
|
||||
@@ -220,7 +220,7 @@ func (bs *Server) ListValidators(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reqState, err = bs.ReplayerBuilder.ForSlot(s).ReplayBlocks(ctx)
|
||||
reqState, err = bs.ReplayerBuilder.ReplayerForSlot(s).ReplayBlocks(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, fmt.Sprintf("error replaying blocks for state at slot %d: %v", s, err))
|
||||
}
|
||||
@@ -415,7 +415,7 @@ func (bs *Server) GetValidatorActiveSetChanges(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
requestedState, err := bs.ReplayerBuilder.ForSlot(s).ReplayBlocks(ctx)
|
||||
requestedState, err := bs.ReplayerBuilder.ReplayerForSlot(s).ReplayBlocks(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, fmt.Sprintf("error replaying blocks for state at slot %d: %v", s, err))
|
||||
}
|
||||
@@ -512,7 +512,7 @@ func (bs *Server) GetValidatorParticipation(
|
||||
}
|
||||
|
||||
// ReplayerBuilder ensures that a canonical chain is followed to the slot
|
||||
beaconState, err := bs.ReplayerBuilder.ForSlot(startSlot).ReplayBlocks(ctx)
|
||||
beaconState, err := bs.ReplayerBuilder.ReplayerForSlot(startSlot).ReplayBlocks(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, fmt.Sprintf("error replaying blocks for state at slot %d: %v", startSlot, err))
|
||||
}
|
||||
@@ -831,7 +831,7 @@ func (bs *Server) GetIndividualVotes(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st, err := bs.ReplayerBuilder.ForSlot(s).ReplayBlocks(ctx)
|
||||
st, err := bs.ReplayerBuilder.ReplayerForSlot(s).ReplayBlocks(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to replay blocks for state at epoch %d: %v", req.Epoch, err)
|
||||
}
|
||||
|
||||
@@ -1505,6 +1505,8 @@ func TestServer_GetValidatorParticipation_CannotRequestFutureEpoch(t *testing.T)
|
||||
|
||||
func TestServer_GetValidatorParticipation_CurrentAndPrevEpoch(t *testing.T) {
|
||||
helpers.ClearCache()
|
||||
params.SetupTestConfigCleanup(t)
|
||||
params.OverrideBeaconConfig(params.MainnetConfig())
|
||||
beaconDB := dbTest.SetupDB(t)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -29,7 +29,7 @@ func (ds *Server) GetBeaconState(
|
||||
)
|
||||
}
|
||||
|
||||
st, err := ds.ReplayerBuilder.ForSlot(q.Slot).ReplayBlocks(ctx)
|
||||
st, err := ds.ReplayerBuilder.ReplayerForSlot(q.Slot).ReplayBlocks(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, fmt.Sprintf("error replaying blocks for state at slot %d: %v", q.Slot, err))
|
||||
}
|
||||
|
||||
@@ -18,14 +18,10 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/testing/util"
|
||||
)
|
||||
|
||||
func addReplayerBuilder(s *Server, h stategen.HistoryAccessor, is bool, canonErr error, currSlot types.Slot) {
|
||||
cc := &mockstategen.MockCanonicalChecker{Is: is, Err: canonErr}
|
||||
cs := &mockstategen.MockCurrentSlotter{Slot: currSlot}
|
||||
s.ReplayerBuilder = stategen.NewCanonicalBuilder(h, cc, cs)
|
||||
}
|
||||
|
||||
func addDefaultReplayerBuilder(s *Server, h stategen.HistoryAccessor) {
|
||||
addReplayerBuilder(s, h, true, nil, math.MaxUint64-1)
|
||||
cc := &mockstategen.MockCanonicalChecker{Is: true}
|
||||
cs := &mockstategen.MockCurrentSlotter{Slot: math.MaxUint64 - 1}
|
||||
s.ReplayerBuilder = stategen.NewCanonicalHistory(h, cc, cs)
|
||||
}
|
||||
|
||||
func TestServer_GetBeaconState(t *testing.T) {
|
||||
|
||||
@@ -362,7 +362,7 @@ func (vs *Server) retrieveAfterEpochTransition(ctx context.Context, epoch types.
|
||||
return nil, err
|
||||
}
|
||||
// replay to first slot of following epoch
|
||||
return vs.ReplayerBuilder.ForSlot(endSlot).ReplayToSlot(ctx, endSlot+1)
|
||||
return vs.ReplayerBuilder.ReplayerForSlot(endSlot).ReplayToSlot(ctx, endSlot+1)
|
||||
}
|
||||
|
||||
func checkValidatorsAreRecent(headEpoch types.Epoch, req *ethpb.DoppelGangerRequest) (bool, *ethpb.DoppelGangerResponse) {
|
||||
|
||||
@@ -178,6 +178,7 @@ func (s *Service) Start() {
|
||||
stateCache = s.cfg.StateGen.CombinedCache()
|
||||
}
|
||||
withCache := stategen.WithCache(stateCache)
|
||||
ch := stategen.NewCanonicalHistory(s.cfg.BeaconDB, s.cfg.ChainInfoFetcher, s.cfg.ChainInfoFetcher, withCache)
|
||||
|
||||
validatorServer := &validatorv1alpha1.Server{
|
||||
Ctx: s.ctx,
|
||||
@@ -204,7 +205,7 @@ func (s *Service) Start() {
|
||||
SlashingsPool: s.cfg.SlashingsPool,
|
||||
StateGen: s.cfg.StateGen,
|
||||
SyncCommitteePool: s.cfg.SyncCommitteeObjectPool,
|
||||
ReplayerBuilder: stategen.NewCanonicalBuilder(s.cfg.BeaconDB, s.cfg.ChainInfoFetcher, s.cfg.ChainInfoFetcher, withCache),
|
||||
ReplayerBuilder: ch,
|
||||
ExecutionEngineCaller: s.cfg.ExecutionEngineCaller,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
}
|
||||
@@ -221,7 +222,7 @@ func (s *Service) Start() {
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
StateGenService: s.cfg.StateGen,
|
||||
ReplayerBuilder: stategen.NewCanonicalBuilder(s.cfg.BeaconDB, s.cfg.ChainInfoFetcher, s.cfg.ChainInfoFetcher, withCache),
|
||||
ReplayerBuilder: ch,
|
||||
},
|
||||
SyncCommitteePool: s.cfg.SyncCommitteeObjectPool,
|
||||
}
|
||||
@@ -271,9 +272,10 @@ func (s *Service) Start() {
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
ReceivedAttestationsBuffer: make(chan *ethpbv1alpha1.Attestation, attestationBufferSize),
|
||||
CollectedAttestationsBuffer: make(chan []*ethpbv1alpha1.Attestation, attestationBufferSize),
|
||||
ReplayerBuilder: stategen.NewCanonicalBuilder(s.cfg.BeaconDB, s.cfg.ChainInfoFetcher, s.cfg.ChainInfoFetcher, withCache),
|
||||
ReplayerBuilder: ch,
|
||||
}
|
||||
beaconChainServerV1 := &beacon.Server{
|
||||
CanonicalHistory: ch,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
AttestationsPool: s.cfg.AttestationsPool,
|
||||
SlashingsPool: s.cfg.SlashingsPool,
|
||||
@@ -293,6 +295,7 @@ func (s *Service) Start() {
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
VoluntaryExitsPool: s.cfg.ExitPool,
|
||||
V1Alpha1ValidatorServer: validatorServer,
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
}
|
||||
ethpbv1alpha1.RegisterNodeServer(s.grpcServer, nodeServer)
|
||||
ethpbservice.RegisterBeaconNodeServer(s.grpcServer, nodeServerV1)
|
||||
@@ -314,7 +317,7 @@ func (s *Service) Start() {
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
PeerManager: s.cfg.PeerManager,
|
||||
PeersFetcher: s.cfg.PeersFetcher,
|
||||
ReplayerBuilder: stategen.NewCanonicalBuilder(s.cfg.BeaconDB, s.cfg.ChainInfoFetcher, s.cfg.ChainInfoFetcher, withCache),
|
||||
ReplayerBuilder: ch,
|
||||
}
|
||||
debugServerV1 := &debug.Server{
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
@@ -324,7 +327,7 @@ func (s *Service) Start() {
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
StateGenService: s.cfg.StateGen,
|
||||
ReplayerBuilder: stategen.NewCanonicalBuilder(s.cfg.BeaconDB, s.cfg.ChainInfoFetcher, s.cfg.ChainInfoFetcher, withCache),
|
||||
ReplayerBuilder: ch,
|
||||
},
|
||||
}
|
||||
ethpbv1alpha1.RegisterDebugServer(s.grpcServer, debugServer)
|
||||
|
||||
@@ -11,6 +11,7 @@ go_library(
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
@@ -110,7 +111,7 @@ func (p *StateProvider) State(ctx context.Context, stateId []byte) (state.Beacon
|
||||
return nil, errors.Wrap(err, "could not get head state")
|
||||
}
|
||||
case "genesis":
|
||||
s, err = p.BeaconDB.GenesisState(ctx)
|
||||
s, err = p.StateBySlot(ctx, params.BeaconConfig().GenesisSlot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get genesis state")
|
||||
}
|
||||
@@ -203,7 +204,7 @@ func (p *StateProvider) StateBySlot(ctx context.Context, target types.Slot) (sta
|
||||
ctx, span := trace.StartSpan(ctx, "statefetcher.StateBySlot")
|
||||
defer span.End()
|
||||
|
||||
st, err := p.ReplayerBuilder.ForSlot(target).ReplayBlocks(ctx)
|
||||
st, err := p.ReplayerBuilder.ReplayerForSlot(target).ReplayBlocks(ctx)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("error while replaying history to slot=%d", target)
|
||||
return nil, errors.Wrap(err, msg)
|
||||
|
||||
@@ -73,8 +73,12 @@ func TestGetState(t *testing.T) {
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, r))
|
||||
require.NoError(t, db.SaveState(ctx, bs, r))
|
||||
|
||||
cc := &mockstategen.MockCanonicalChecker{Is: true}
|
||||
cs := &mockstategen.MockCurrentSlotter{Slot: bs.Slot() + 1}
|
||||
ch := stategen.NewCanonicalHistory(db, cc, cs)
|
||||
p := StateProvider{
|
||||
BeaconDB: db,
|
||||
BeaconDB: db,
|
||||
ReplayerBuilder: ch,
|
||||
}
|
||||
|
||||
s, err := p.State(ctx, []byte("genesis"))
|
||||
|
||||
@@ -10,21 +10,7 @@ go_library(
|
||||
"prometheus.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__subpackages__",
|
||||
"//proto/migration:__pkg__",
|
||||
"//proto/prysm/v1alpha1:__subpackages__",
|
||||
"//proto/testing:__subpackages__",
|
||||
"//slasher/rpc:__subpackages__",
|
||||
"//testing/benchmark:__pkg__",
|
||||
"//testing/slasher/simulator:__pkg__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
"//tools/benchmark-files-gen:__pkg__",
|
||||
"//tools/exploredb:__pkg__",
|
||||
"//tools/pcli:__pkg__",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
|
||||
@@ -18,4 +18,6 @@ type BeaconStateAltair interface {
|
||||
SetInactivityScores(val []uint64) error
|
||||
SetPreviousParticipationBits(val []byte) error
|
||||
SetCurrentParticipationBits(val []byte) error
|
||||
ModifyCurrentParticipationBits(func(val []byte) ([]byte, error)) error
|
||||
ModifyPreviousParticipationBits(func(val []byte) ([]byte, error)) error
|
||||
}
|
||||
|
||||
@@ -302,11 +302,11 @@ func handlePendingAttestationSlice(val []*ethpb.PendingAttestation, indices []ui
|
||||
// handleBalanceSlice returns the root of a slice of validator balances.
|
||||
func handleBalanceSlice(val, indices []uint64, convertAll bool) ([][32]byte, error) {
|
||||
if convertAll {
|
||||
balancesMarshaling := make([][]byte, 0)
|
||||
for _, b := range val {
|
||||
balancesMarshaling := make([][]byte, len(val))
|
||||
for i, b := range val {
|
||||
balanceBuf := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(balanceBuf, b)
|
||||
balancesMarshaling = append(balancesMarshaling, balanceBuf)
|
||||
balancesMarshaling[i] = balanceBuf
|
||||
}
|
||||
balancesChunks, err := ssz.PackByChunk(balancesMarshaling)
|
||||
if err != nil {
|
||||
|
||||
@@ -221,6 +221,8 @@ type FutureForkStub interface {
|
||||
SetCurrentSyncCommittee(val *ethpb.SyncCommittee) error
|
||||
SetPreviousParticipationBits(val []byte) error
|
||||
SetCurrentParticipationBits(val []byte) error
|
||||
ModifyCurrentParticipationBits(func(val []byte) ([]byte, error)) error
|
||||
ModifyPreviousParticipationBits(func(val []byte) ([]byte, error)) error
|
||||
NextSyncCommittee() (*ethpb.SyncCommittee, error)
|
||||
SetNextSyncCommittee(val *ethpb.SyncCommittee) error
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
func TestBeaconState_JustificationBitsNil(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateJustificationBitsNil(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconState{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_JustificationBits(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateJustificationBits(
|
||||
t,
|
||||
func(bits bitfield.Bitvector4) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconState{JustificationBits: bits})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_PreviousJustifiedCheckpointNil(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStatePreviousJustifiedCheckpointNil(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconState{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_PreviousJustifiedCheckpoint(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStatePreviousJustifiedCheckpoint(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconState{PreviousJustifiedCheckpoint: cp})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_CurrentJustifiedCheckpointNil(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateCurrentJustifiedCheckpointNil(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconState{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_CurrentJustifiedCheckpoint(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateCurrentJustifiedCheckpoint(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconState{CurrentJustifiedCheckpoint: cp})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_FinalizedCheckpointNil(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateFinalizedCheckpointNil(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconState{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_FinalizedCheckpoint(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateFinalizedCheckpoint(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconState{FinalizedCheckpoint: cp})
|
||||
})
|
||||
}
|
||||
@@ -190,3 +190,19 @@ func TestBeaconState_AppendBalanceWithTrie(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, wantedRt, newRt, "state roots are unequal")
|
||||
}
|
||||
|
||||
func TestBeaconState_ModifyPreviousParticipationBits(t *testing.T) {
|
||||
st, err := InitializeFromProtoUnsafe(ðpb.BeaconState{})
|
||||
assert.NoError(t, err)
|
||||
assert.ErrorContains(t, "ModifyPreviousParticipationBits is not supported for phase 0 beacon state", st.ModifyPreviousParticipationBits(func(val []byte) ([]byte, error) {
|
||||
return nil, nil
|
||||
}))
|
||||
}
|
||||
|
||||
func TestBeaconState_ModifyCurrentParticipationBits(t *testing.T) {
|
||||
st, err := InitializeFromProtoUnsafe(ðpb.BeaconState{})
|
||||
assert.NoError(t, err)
|
||||
assert.ErrorContains(t, "ModifyCurrentParticipationBits is not supported for phase 0 beacon state", st.ModifyCurrentParticipationBits(func(val []byte) ([]byte, error) {
|
||||
return nil, nil
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -40,6 +40,16 @@ func (*BeaconState) SetCurrentParticipationBits(_ []byte) error {
|
||||
return errors.New("SetCurrentParticipationBits is not supported for phase 0 beacon state")
|
||||
}
|
||||
|
||||
// ModifyPreviousParticipationBits is not supported for phase 0 beacon state.
|
||||
func (b *BeaconState) ModifyPreviousParticipationBits(mutator func(val []byte) ([]byte, error)) error {
|
||||
return errors.New("ModifyPreviousParticipationBits is not supported for phase 0 beacon state")
|
||||
}
|
||||
|
||||
// ModifyCurrentParticipationBits is not supported for phase 0 beacon state.
|
||||
func (b *BeaconState) ModifyCurrentParticipationBits(mutator func(val []byte) ([]byte, error)) error {
|
||||
return errors.New("ModifyCurrentParticipationBits is not supported for phase 0 beacon state")
|
||||
}
|
||||
|
||||
// SetInactivityScores is not supported for phase 0 beacon state.
|
||||
func (*BeaconState) SetInactivityScores(_ []uint64) error {
|
||||
return errors.New("SetInactivityScores is not supported for phase 0 beacon state")
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
func TestBeaconState_JustificationBitsNil(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateJustificationBitsNil(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateAltair{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_JustificationBits(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateJustificationBits(
|
||||
t,
|
||||
func(bits bitfield.Bitvector4) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateAltair{JustificationBits: bits})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_PreviousJustifiedCheckpointNil(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStatePreviousJustifiedCheckpointNil(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateAltair{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_PreviousJustifiedCheckpoint(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStatePreviousJustifiedCheckpoint(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateAltair{PreviousJustifiedCheckpoint: cp})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_CurrentJustifiedCheckpointNil(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateCurrentJustifiedCheckpointNil(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateAltair{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_CurrentJustifiedCheckpoint(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateCurrentJustifiedCheckpoint(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateAltair{CurrentJustifiedCheckpoint: cp})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_FinalizedCheckpointNil(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateFinalizedCheckpointNil(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateAltair{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_FinalizedCheckpoint(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateFinalizedCheckpoint(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateAltair{FinalizedCheckpoint: cp})
|
||||
})
|
||||
}
|
||||
@@ -75,3 +75,63 @@ func (b *BeaconState) AppendPreviousParticipationBits(val byte) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ModifyPreviousParticipationBits modifies the previous participation bitfield via
|
||||
// the provided mutator function.
|
||||
func (b *BeaconState) ModifyPreviousParticipationBits(mutator func(val []byte) ([]byte, error)) error {
|
||||
b.lock.Lock()
|
||||
|
||||
participation := b.previousEpochParticipation
|
||||
if b.sharedFieldReferences[previousEpochParticipationBits].Refs() > 1 {
|
||||
// Copy elements in underlying array by reference.
|
||||
participation = make([]byte, len(b.previousEpochParticipation))
|
||||
copy(participation, b.previousEpochParticipation)
|
||||
b.sharedFieldReferences[previousEpochParticipationBits].MinusRef()
|
||||
b.sharedFieldReferences[previousEpochParticipationBits] = stateutil.NewRef(1)
|
||||
}
|
||||
// Lock is released so that mutator can
|
||||
// acquire it.
|
||||
b.lock.Unlock()
|
||||
|
||||
var err error
|
||||
participation, err = mutator(participation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
b.previousEpochParticipation = participation
|
||||
b.markFieldAsDirty(previousEpochParticipationBits)
|
||||
b.rebuildTrie[previousEpochParticipationBits] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// ModifyCurrentParticipationBits modifies the current participation bitfield via
|
||||
// the provided mutator function.
|
||||
func (b *BeaconState) ModifyCurrentParticipationBits(mutator func(val []byte) ([]byte, error)) error {
|
||||
b.lock.Lock()
|
||||
|
||||
participation := b.currentEpochParticipation
|
||||
if b.sharedFieldReferences[currentEpochParticipationBits].Refs() > 1 {
|
||||
// Copy elements in underlying array by reference.
|
||||
participation = make([]byte, len(b.currentEpochParticipation))
|
||||
copy(participation, b.currentEpochParticipation)
|
||||
b.sharedFieldReferences[currentEpochParticipationBits].MinusRef()
|
||||
b.sharedFieldReferences[currentEpochParticipationBits] = stateutil.NewRef(1)
|
||||
}
|
||||
// Lock is released so that mutator can
|
||||
// acquire it.
|
||||
b.lock.Unlock()
|
||||
|
||||
var err error
|
||||
participation, err = mutator(participation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
b.currentEpochParticipation = participation
|
||||
b.markFieldAsDirty(currentEpochParticipationBits)
|
||||
b.rebuildTrie[currentEpochParticipationBits] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
|
||||
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
@@ -114,3 +116,63 @@ func TestBeaconState_AppendBalanceWithTrie(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, wantedRt, newRt, "state roots are unequal")
|
||||
}
|
||||
|
||||
func TestBeaconState_ModifyPreviousParticipationBits(t *testing.T) {
|
||||
testState := createState(200)
|
||||
testtmpl.VerifyBeaconStateModifyPreviousParticipationField(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(testState)
|
||||
},
|
||||
)
|
||||
testtmpl.VerifyBeaconStateModifyPreviousParticipationField_NestedAction(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(testState)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestBeaconState_ModifyCurrentParticipationBits(t *testing.T) {
|
||||
testState := createState(200)
|
||||
testtmpl.VerifyBeaconStateModifyCurrentParticipationField(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(testState)
|
||||
},
|
||||
)
|
||||
testtmpl.VerifyBeaconStateModifyCurrentParticipationField_NestedAction(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(testState)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func createState(count uint64) *ethpb.BeaconStateAltair {
|
||||
vals := make([]*ethpb.Validator, 0, count)
|
||||
bals := make([]uint64, 0, count)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
someRoot := [32]byte{}
|
||||
someKey := [fieldparams.BLSPubkeyLength]byte{}
|
||||
copy(someRoot[:], strconv.Itoa(int(i)))
|
||||
copy(someKey[:], strconv.Itoa(int(i)))
|
||||
vals = append(vals, ðpb.Validator{
|
||||
PublicKey: someKey[:],
|
||||
WithdrawalCredentials: someRoot[:],
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
Slashed: false,
|
||||
ActivationEligibilityEpoch: 1,
|
||||
ActivationEpoch: 1,
|
||||
ExitEpoch: 1,
|
||||
WithdrawableEpoch: 1,
|
||||
})
|
||||
bals = append(bals, params.BeaconConfig().MaxEffectiveBalance)
|
||||
}
|
||||
return ðpb.BeaconStateAltair{
|
||||
CurrentEpochParticipation: make([]byte, count),
|
||||
PreviousEpochParticipation: make([]byte, count),
|
||||
Validators: vals,
|
||||
Balances: bals,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"sort"
|
||||
|
||||
@@ -30,27 +28,6 @@ func InitializeFromProto(st *ethpb.BeaconStateAltair) (state.BeaconStateAltair,
|
||||
return InitializeFromProtoUnsafe(proto.Clone(st).(*ethpb.BeaconStateAltair))
|
||||
}
|
||||
|
||||
// InitializeFromSSZReader can be used when the source for a serialized BeaconState object
|
||||
// is an io.Reader. This allows client code to remain agnostic about whether the data comes
|
||||
// from the network or a file without needing to read the entire state into mem as a large byte slice.
|
||||
func InitializeFromSSZReader(r io.Reader) (state.BeaconStateAltair, error) {
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return InitializeFromSSZBytes(b)
|
||||
}
|
||||
|
||||
// InitializeFromSSZBytes is a convenience method to obtain a BeaconState by unmarshaling
|
||||
// a slice of bytes containing the ssz-serialized representation of the state.
|
||||
func InitializeFromSSZBytes(marshaled []byte) (*BeaconState, error) {
|
||||
st := ðpb.BeaconStateAltair{}
|
||||
if err := st.UnmarshalSSZ(marshaled); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return InitializeFromProtoUnsafe(st)
|
||||
}
|
||||
|
||||
// InitializeFromProtoUnsafe directly uses the beacon state protobuf fields
|
||||
// and sets them as fields of the BeaconState type.
|
||||
func InitializeFromProtoUnsafe(st *ethpb.BeaconStateAltair) (*BeaconState, error) {
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
func TestBeaconState_JustificationBitsNil(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateJustificationBitsNil(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateBellatrix{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_JustificationBits(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateJustificationBits(
|
||||
t,
|
||||
func(bits bitfield.Bitvector4) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateBellatrix{JustificationBits: bits})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_PreviousJustifiedCheckpointNil(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStatePreviousJustifiedCheckpointNil(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateBellatrix{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_PreviousJustifiedCheckpoint(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStatePreviousJustifiedCheckpoint(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateBellatrix{PreviousJustifiedCheckpoint: cp})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_CurrentJustifiedCheckpointNil(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateCurrentJustifiedCheckpointNil(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateBellatrix{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_CurrentJustifiedCheckpoint(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateCurrentJustifiedCheckpoint(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateBellatrix{CurrentJustifiedCheckpoint: cp})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_FinalizedCheckpointNil(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateFinalizedCheckpointNil(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateBellatrix{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeaconState_FinalizedCheckpoint(t *testing.T) {
|
||||
testtmpl.VerifyBeaconStateFinalizedCheckpoint(
|
||||
t,
|
||||
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafe(ðpb.BeaconStateBellatrix{FinalizedCheckpoint: cp})
|
||||
})
|
||||
}
|
||||
@@ -75,3 +75,63 @@ func (b *BeaconState) AppendPreviousParticipationBits(val byte) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ModifyPreviousParticipationBits modifies the previous participation bitfield via
|
||||
// the provided mutator function.
|
||||
func (b *BeaconState) ModifyPreviousParticipationBits(mutator func(val []byte) ([]byte, error)) error {
|
||||
b.lock.Lock()
|
||||
|
||||
participation := b.previousEpochParticipation
|
||||
if b.sharedFieldReferences[previousEpochParticipationBits].Refs() > 1 {
|
||||
// Copy elements in underlying array by reference.
|
||||
participation = make([]byte, len(b.previousEpochParticipation))
|
||||
copy(participation, b.previousEpochParticipation)
|
||||
b.sharedFieldReferences[previousEpochParticipationBits].MinusRef()
|
||||
b.sharedFieldReferences[previousEpochParticipationBits] = stateutil.NewRef(1)
|
||||
}
|
||||
// Lock is released so that mutator can
|
||||
// acquire it.
|
||||
b.lock.Unlock()
|
||||
|
||||
var err error
|
||||
participation, err = mutator(participation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
b.previousEpochParticipation = participation
|
||||
b.markFieldAsDirty(previousEpochParticipationBits)
|
||||
b.rebuildTrie[previousEpochParticipationBits] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// ModifyCurrentParticipationBits modifies the current participation bitfield via
|
||||
// the provided mutator function.
|
||||
func (b *BeaconState) ModifyCurrentParticipationBits(mutator func(val []byte) ([]byte, error)) error {
|
||||
b.lock.Lock()
|
||||
|
||||
participation := b.currentEpochParticipation
|
||||
if b.sharedFieldReferences[currentEpochParticipationBits].Refs() > 1 {
|
||||
// Copy elements in underlying array by reference.
|
||||
participation = make([]byte, len(b.currentEpochParticipation))
|
||||
copy(participation, b.currentEpochParticipation)
|
||||
b.sharedFieldReferences[currentEpochParticipationBits].MinusRef()
|
||||
b.sharedFieldReferences[currentEpochParticipationBits] = stateutil.NewRef(1)
|
||||
}
|
||||
// Lock is released so that mutator can
|
||||
// acquire it.
|
||||
b.lock.Unlock()
|
||||
|
||||
var err error
|
||||
participation, err = mutator(participation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
b.currentEpochParticipation = participation
|
||||
b.markFieldAsDirty(currentEpochParticipationBits)
|
||||
b.rebuildTrie[currentEpochParticipationBits] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
|
||||
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
@@ -126,3 +128,63 @@ func TestBeaconState_AppendBalanceWithTrie(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, wantedRt, newRt, "state roots are unequal")
|
||||
}
|
||||
|
||||
func TestBeaconState_ModifyPreviousParticipationBits(t *testing.T) {
|
||||
testState := createState(200)
|
||||
testtmpl.VerifyBeaconStateModifyPreviousParticipationField(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(testState)
|
||||
},
|
||||
)
|
||||
testtmpl.VerifyBeaconStateModifyPreviousParticipationField_NestedAction(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(testState)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestBeaconState_ModifyCurrentParticipationBits(t *testing.T) {
|
||||
testState := createState(200)
|
||||
testtmpl.VerifyBeaconStateModifyCurrentParticipationField(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(testState)
|
||||
},
|
||||
)
|
||||
testtmpl.VerifyBeaconStateModifyCurrentParticipationField_NestedAction(
|
||||
t,
|
||||
func() (state.BeaconState, error) {
|
||||
return InitializeFromProto(testState)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func createState(count uint64) *ethpb.BeaconStateBellatrix {
|
||||
vals := make([]*ethpb.Validator, 0, count)
|
||||
bals := make([]uint64, 0, count)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
someRoot := [32]byte{}
|
||||
someKey := [fieldparams.BLSPubkeyLength]byte{}
|
||||
copy(someRoot[:], strconv.Itoa(int(i)))
|
||||
copy(someKey[:], strconv.Itoa(int(i)))
|
||||
vals = append(vals, ðpb.Validator{
|
||||
PublicKey: someKey[:],
|
||||
WithdrawalCredentials: someRoot[:],
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
Slashed: false,
|
||||
ActivationEligibilityEpoch: 1,
|
||||
ActivationEpoch: 1,
|
||||
ExitEpoch: 1,
|
||||
WithdrawableEpoch: 1,
|
||||
})
|
||||
bals = append(bals, params.BeaconConfig().MaxEffectiveBalance)
|
||||
}
|
||||
return ðpb.BeaconStateBellatrix{
|
||||
CurrentEpochParticipation: make([]byte, count),
|
||||
PreviousEpochParticipation: make([]byte, count),
|
||||
Validators: vals,
|
||||
Balances: bals,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ go_library(
|
||||
"epoch_boundary_state_cache.go",
|
||||
"errors.go",
|
||||
"getter.go",
|
||||
"history.go",
|
||||
"hot_state_cache.go",
|
||||
"log.go",
|
||||
"metrics.go",
|
||||
@@ -51,6 +52,7 @@ go_test(
|
||||
srcs = [
|
||||
"epoch_boundary_state_cache_test.go",
|
||||
"getter_test.go",
|
||||
"history_test.go",
|
||||
"hot_state_cache_test.go",
|
||||
"init_test.go",
|
||||
"migrate_test.go",
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
@@ -13,6 +12,8 @@ import (
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var ErrSlotBeforeOrigin = errors.New("cannot retrieve data for slots before sync origin")
|
||||
|
||||
// HasState returns true if the state exists in cache or in DB.
|
||||
func (s *State) HasState(ctx context.Context, blockRoot [32]byte) (bool, error) {
|
||||
has, err := s.HasStateInCache(ctx, blockRoot)
|
||||
@@ -111,14 +112,6 @@ func (s *State) StateByRootInitialSync(ctx context.Context, blockRoot [32]byte)
|
||||
return startState, nil
|
||||
}
|
||||
|
||||
// StateBySlot retrieves the state using input slot.
|
||||
func (s *State) StateBySlot(ctx context.Context, slot types.Slot) (state.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "stateGen.StateBySlot")
|
||||
defer span.End()
|
||||
|
||||
return s.loadStateBySlot(ctx, slot)
|
||||
}
|
||||
|
||||
// This returns the state summary object of a given block root, it first checks the cache
|
||||
// then checks the DB. An error is returned if state summary object is nil.
|
||||
func (s *State) stateSummary(ctx context.Context, blockRoot [32]byte) (*ethpb.StateSummary, error) {
|
||||
@@ -208,36 +201,6 @@ func (s *State) loadStateByRoot(ctx context.Context, blockRoot [32]byte) (state.
|
||||
return s.ReplayBlocks(ctx, startState, blks, targetSlot)
|
||||
}
|
||||
|
||||
// This loads a state by slot.
|
||||
func (s *State) loadStateBySlot(ctx context.Context, slot types.Slot) (state.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "stateGen.loadStateBySlot")
|
||||
defer span.End()
|
||||
|
||||
// Return genesis state if slot is 0.
|
||||
if slot == 0 {
|
||||
return s.beaconDB.GenesisState(ctx)
|
||||
}
|
||||
|
||||
// Gather the last saved block root and the slot number.
|
||||
lastValidRoot, lastValidSlot, err := s.lastSavedBlock(ctx, slot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get last valid block for hot state using slot")
|
||||
}
|
||||
|
||||
replayStartState, err := s.loadStateByRoot(ctx, lastValidRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if lastValidSlot < slot {
|
||||
replayStartState, err = ReplayProcessSlots(ctx, replayStartState, slot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return replayStartState, nil
|
||||
}
|
||||
|
||||
// This returns the highest available ancestor state of the input block root.
|
||||
// It recursively look up block's parent until a corresponding state of the block root
|
||||
// is found in the caches or DB.
|
||||
@@ -266,6 +229,11 @@ func (s *State) LastAncestorState(ctx context.Context, root [32]byte) (state.Bea
|
||||
if ctx.Err() != nil {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
// return an error if we have rewound to before the checkpoint sync slot
|
||||
if (b.Block().Slot() - 1) < s.minimumSlot() {
|
||||
return nil, errors.Wrapf(ErrSlotBeforeOrigin, "no blocks in db prior to slot %d", s.minimumSlot())
|
||||
}
|
||||
// Is the state a genesis state.
|
||||
parentRoot := bytesutil.ToBytes32(b.Block().ParentRoot())
|
||||
if parentRoot == params.BeaconConfig().ZeroHash {
|
||||
|
||||
@@ -231,63 +231,6 @@ func TestStateByRootInitialSync_CanProcessUpTo(t *testing.T) {
|
||||
assert.Equal(t, targetSlot, loadedState.Slot(), "Did not correctly load state")
|
||||
}
|
||||
|
||||
func TestStateBySlot_ColdState(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
service := New(beaconDB)
|
||||
service.slotsPerArchivedPoint = params.BeaconConfig().SlotsPerEpoch * 2
|
||||
service.finalizedInfo.slot = service.slotsPerArchivedPoint + 1
|
||||
|
||||
beaconState, pks := util.DeterministicGenesisState(t, 32)
|
||||
genesisStateRoot, err := beaconState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
genesis := blocks.NewGenesisBlock(genesisStateRoot[:])
|
||||
assert.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(genesis)))
|
||||
gRoot, err := genesis.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, beaconDB.SaveState(ctx, beaconState, gRoot))
|
||||
assert.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, gRoot))
|
||||
|
||||
b, err := util.GenerateFullBlock(beaconState, pks, util.DefaultBlockGenConfig(), 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b)))
|
||||
bRoot, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveState(ctx, beaconState, bRoot))
|
||||
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, bRoot))
|
||||
|
||||
r := [32]byte{}
|
||||
require.NoError(t, service.beaconDB.SaveStateSummary(ctx, ðpb.StateSummary{Slot: service.slotsPerArchivedPoint, Root: r[:]}))
|
||||
|
||||
slot := types.Slot(20)
|
||||
loadedState, err := service.StateBySlot(ctx, slot)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, slot, loadedState.Slot(), "Did not correctly save state")
|
||||
}
|
||||
|
||||
func TestStateBySlot_HotStateDB(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
service := New(beaconDB)
|
||||
|
||||
beaconState, _ := util.DeterministicGenesisState(t, 32)
|
||||
genesisStateRoot, err := beaconState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
genesis := blocks.NewGenesisBlock(genesisStateRoot[:])
|
||||
assert.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(genesis)))
|
||||
gRoot, err := genesis.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, beaconDB.SaveState(ctx, beaconState, gRoot))
|
||||
assert.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, gRoot))
|
||||
|
||||
slot := types.Slot(10)
|
||||
loadedState, err := service.StateBySlot(ctx, slot)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, slot, loadedState.Slot(), "Did not correctly load state")
|
||||
}
|
||||
|
||||
func TestLoadeStateByRoot_Cached(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
@@ -379,83 +322,6 @@ func TestLoadeStateByRoot_FromDBBoundaryCase(t *testing.T) {
|
||||
assert.Equal(t, types.Slot(10), loadedState.Slot(), "Did not correctly load state")
|
||||
}
|
||||
|
||||
func TestLoadeStateBySlot_CanAdvanceSlotUsingDB(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
service := New(beaconDB)
|
||||
beaconState, _ := util.DeterministicGenesisState(t, 32)
|
||||
b := util.NewBeaconBlock()
|
||||
require.NoError(t, service.beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b)))
|
||||
gRoot, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.beaconDB.SaveGenesisBlockRoot(ctx, gRoot))
|
||||
require.NoError(t, service.beaconDB.SaveState(ctx, beaconState, gRoot))
|
||||
|
||||
slot := types.Slot(10)
|
||||
loadedState, err := service.loadStateBySlot(ctx, slot)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, slot, loadedState.Slot(), "Did not correctly load state")
|
||||
}
|
||||
|
||||
func TestLoadeStateBySlot_CanReplayBlock(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
service := New(beaconDB)
|
||||
genesis, keys := util.DeterministicGenesisState(t, 64)
|
||||
genesisBlockRoot := bytesutil.ToBytes32(nil)
|
||||
require.NoError(t, beaconDB.SaveState(ctx, genesis, genesisBlockRoot))
|
||||
stateRoot, err := genesis.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
genesisBlk := blocks.NewGenesisBlock(stateRoot[:])
|
||||
require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(genesisBlk)))
|
||||
genesisBlkRoot, err := genesisBlk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, genesisBlkRoot))
|
||||
|
||||
b1, err := util.GenerateFullBlock(genesis, keys, util.DefaultBlockGenConfig(), 1)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b1)))
|
||||
r1, err := b1.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.beaconDB.SaveStateSummary(ctx, ðpb.StateSummary{Slot: 1, Root: r1[:]}))
|
||||
service.hotStateCache.put(bytesutil.ToBytes32(b1.Block.ParentRoot), genesis)
|
||||
|
||||
loadedState, err := service.loadStateBySlot(ctx, 2)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, types.Slot(2), loadedState.Slot(), "Did not correctly load state")
|
||||
}
|
||||
|
||||
func TestLoadeStateBySlot_DoesntReplayBlockOnRequestedSlot(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
service := New(beaconDB)
|
||||
genesis, keys := util.DeterministicGenesisState(t, 64)
|
||||
genesisBlockRoot := bytesutil.ToBytes32(nil)
|
||||
require.NoError(t, beaconDB.SaveState(ctx, genesis, genesisBlockRoot))
|
||||
stateRoot, err := genesis.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
genesisBlk := blocks.NewGenesisBlock(stateRoot[:])
|
||||
require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(genesisBlk)))
|
||||
genesisBlkRoot, err := genesisBlk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, genesisBlkRoot))
|
||||
|
||||
b1, err := util.GenerateFullBlock(genesis, keys, util.DefaultBlockGenConfig(), 1)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b1)))
|
||||
r1, err := b1.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.beaconDB.SaveStateSummary(ctx, ðpb.StateSummary{Slot: 1, Root: r1[:]}))
|
||||
service.hotStateCache.put(bytesutil.ToBytes32(b1.Block.ParentRoot), genesis)
|
||||
|
||||
loadedState, err := service.loadStateBySlot(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, types.Slot(1), loadedState.Slot(), "Did not correctly load state")
|
||||
|
||||
// Latest block header's state root should not be zero. Zero means the current slot's block has been processed.
|
||||
require.NotEqual(t, params.BeaconConfig().ZeroHash, bytesutil.ToBytes32(loadedState.LatestBlockHeader().StateRoot))
|
||||
}
|
||||
|
||||
func TestLastAncestorState_CanGetUsingDB(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
206
beacon-chain/state/stategen/history.go
Normal file
206
beacon-chain/state/stategen/history.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package stategen
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
func WithCache(c CachedGetter) CanonicalHistoryOption {
|
||||
return func(h *CanonicalHistory) {
|
||||
h.cache = c
|
||||
}
|
||||
}
|
||||
|
||||
type CanonicalHistoryOption func(*CanonicalHistory)
|
||||
|
||||
func NewCanonicalHistory(h HistoryAccessor, cc CanonicalChecker, cs CurrentSlotter, opts ...CanonicalHistoryOption) *CanonicalHistory {
|
||||
ch := &CanonicalHistory{
|
||||
h: h,
|
||||
cc: cc,
|
||||
cs: cs,
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(ch)
|
||||
}
|
||||
return ch
|
||||
}
|
||||
|
||||
type CanonicalHistory struct {
|
||||
h HistoryAccessor
|
||||
cc CanonicalChecker
|
||||
cs CurrentSlotter
|
||||
cache CachedGetter
|
||||
}
|
||||
|
||||
func (ch *CanonicalHistory) ReplayerForSlot(target types.Slot) Replayer {
|
||||
return &stateReplayer{chainer: ch, method: forSlot, target: target}
|
||||
}
|
||||
|
||||
func (c *CanonicalHistory) BlockForSlot(ctx context.Context, target types.Slot) ([32]byte, block.SignedBeaconBlock, error) {
|
||||
currentSlot := c.cs.CurrentSlot()
|
||||
if target > currentSlot {
|
||||
return [32]byte{}, nil, errors.Wrap(ErrFutureSlotRequested, fmt.Sprintf("requested=%d, current=%d", target, currentSlot))
|
||||
}
|
||||
for target > 0 {
|
||||
if ctx.Err() != nil {
|
||||
return [32]byte{}, nil, errors.Wrap(ctx.Err(), "context canceled during canonicalBlockForSlot")
|
||||
}
|
||||
hbs, err := c.h.HighestSlotBlocksBelow(ctx, target+1)
|
||||
if err != nil {
|
||||
return [32]byte{}, nil, errors.Wrap(err, fmt.Sprintf("error finding highest block w/ slot <= %d", target))
|
||||
}
|
||||
if len(hbs) == 0 {
|
||||
return [32]byte{}, nil, errors.Wrap(ErrNoBlocksBelowSlot, fmt.Sprintf("slot=%d", target))
|
||||
}
|
||||
r, b, err := c.bestForSlot(ctx, hbs)
|
||||
if err == nil {
|
||||
// we found a valid, canonical block!
|
||||
return r, b, nil
|
||||
}
|
||||
|
||||
// we found a block, but it wasn't considered canonical - keep looking
|
||||
if errors.Is(err, ErrNoCanonicalBlockForSlot) {
|
||||
// break once we've seen slot 0 (and prevent underflow)
|
||||
if hbs[0].Block().Slot() == params.BeaconConfig().GenesisSlot {
|
||||
break
|
||||
}
|
||||
target = hbs[0].Block().Slot() - 1
|
||||
continue
|
||||
}
|
||||
return [32]byte{}, nil, err
|
||||
}
|
||||
b, err := c.h.GenesisBlock(ctx)
|
||||
if err != nil {
|
||||
return [32]byte{}, nil, errors.Wrap(err, "db error while retrieving genesis block")
|
||||
}
|
||||
root, _, err := c.bestForSlot(ctx, []block.SignedBeaconBlock{b})
|
||||
if err != nil {
|
||||
return [32]byte{}, nil, errors.Wrap(err, "problem retrieving genesis block")
|
||||
}
|
||||
return root, b, nil
|
||||
}
|
||||
|
||||
// bestForSlot encapsulates several messy realities of the underlying db code, looping through multiple blocks,
|
||||
// performing null/validity checks, and using CanonicalChecker to only pick canonical blocks.
|
||||
func (c *CanonicalHistory) bestForSlot(ctx context.Context, hbs []block.SignedBeaconBlock) ([32]byte, block.SignedBeaconBlock, error) {
|
||||
for _, b := range hbs {
|
||||
if helpers.BeaconBlockIsNil(b) != nil {
|
||||
continue
|
||||
}
|
||||
root, err := b.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
// use this error message to wrap a sentinel error for error type matching
|
||||
wrapped := errors.Wrap(ErrInvalidDBBlock, err.Error())
|
||||
msg := fmt.Sprintf("could not compute hash_tree_root for block at slot=%d", b.Block().Slot())
|
||||
return [32]byte{}, nil, errors.Wrap(wrapped, msg)
|
||||
}
|
||||
canon, err := c.cc.IsCanonical(ctx, root)
|
||||
if err != nil {
|
||||
return [32]byte{}, nil, errors.Wrap(err, "replayer could not check if block is canonical")
|
||||
}
|
||||
if canon {
|
||||
return root, b, nil
|
||||
}
|
||||
}
|
||||
return [32]byte{}, nil, errors.Wrap(ErrNoCanonicalBlockForSlot, "no good block for slot")
|
||||
}
|
||||
|
||||
// ChainForSlot creates a value that satisfies the Replayer interface via db queries
|
||||
// and the stategen transition helper methods. This implementation uses the following algorithm:
|
||||
// - find the highest canonical block <= the target slot
|
||||
// - starting with this block, recursively search backwards for a stored state, and accumulate intervening blocks
|
||||
func (c *CanonicalHistory) chainForSlot(ctx context.Context, target types.Slot) (state.BeaconState, []block.SignedBeaconBlock, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "canonicalChainer.chainForSlot")
|
||||
defer span.End()
|
||||
_, b, err := c.BlockForSlot(ctx, target)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, fmt.Sprintf("unable to find replay data for slot=%d", target))
|
||||
}
|
||||
s, descendants, err := c.ancestorChain(ctx, b)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to query for ancestor and descendant blocks")
|
||||
}
|
||||
|
||||
return s, descendants, nil
|
||||
}
|
||||
|
||||
func (c *CanonicalHistory) getState(ctx context.Context, root [32]byte) (state.BeaconState, error) {
|
||||
if c.cache != nil {
|
||||
st, err := c.cache.ByRoot(root)
|
||||
if err == nil {
|
||||
return st, nil
|
||||
}
|
||||
if !errors.Is(err, ErrNotInCache) {
|
||||
return nil, errors.Wrap(err, "error reading from state cache during state replay")
|
||||
}
|
||||
}
|
||||
return c.h.StateOrError(ctx, root)
|
||||
}
|
||||
|
||||
// ancestorChain works backwards through the chain lineage, accumulating blocks and checking for a saved state.
|
||||
// If it finds a saved state that the tail block was descended from, it returns this state and
|
||||
// all blocks in the lineage, including the tail block. Blocks are returned in ascending order.
|
||||
// Note that this function assumes that the tail is a canonical block, and therefore assumes that
|
||||
// all ancestors are also canonical.
|
||||
func (c *CanonicalHistory) ancestorChain(ctx context.Context, tail block.SignedBeaconBlock) (state.BeaconState, []block.SignedBeaconBlock, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "canonicalChainer.ancestorChain")
|
||||
defer span.End()
|
||||
chain := make([]block.SignedBeaconBlock, 0)
|
||||
for {
|
||||
if err := ctx.Err(); err != nil {
|
||||
msg := fmt.Sprintf("context canceled while finding ancestors of block at slot %d", tail.Block().Slot())
|
||||
return nil, nil, errors.Wrap(err, msg)
|
||||
}
|
||||
b := tail.Block()
|
||||
// compute hash_tree_root of current block and try to look up the corresponding state
|
||||
root, err := b.HashTreeRoot()
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("could not compute htr for descendant block at slot=%d", b.Slot())
|
||||
return nil, nil, errors.Wrap(err, msg)
|
||||
}
|
||||
st, err := c.getState(ctx, root)
|
||||
// err == nil, we've got a real state - the job is done!
|
||||
// Note: in cases where there are skipped slots we could find a state that is a descendant
|
||||
// of the block we are searching for. We don't want to return a future block, so in this case
|
||||
// we keep working backwards.
|
||||
if err == nil && st.Slot() == b.Slot() {
|
||||
// we found the state by the root of the head, meaning it has already been applied.
|
||||
// we only want to return the blocks descended from it.
|
||||
reverseChain(chain)
|
||||
return st, chain, nil
|
||||
}
|
||||
// ErrNotFoundState errors are fine, but other errors mean something is wrong with the db
|
||||
if err != nil && !errors.Is(err, db.ErrNotFoundState) {
|
||||
return nil, nil, errors.Wrap(err, fmt.Sprintf("error querying database for state w/ block root = %#x", root))
|
||||
}
|
||||
parent, err := c.h.Block(ctx, bytesutil.ToBytes32(b.ParentRoot()))
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("db error when retrieving parent of block at slot=%d by root=%#x", b.Slot(), b.ParentRoot())
|
||||
return nil, nil, errors.Wrap(err, msg)
|
||||
}
|
||||
if helpers.BeaconBlockIsNil(parent) != nil {
|
||||
msg := fmt.Sprintf("unable to retrieve parent of block at slot=%d by root=%#x", b.Slot(), b.ParentRoot())
|
||||
return nil, nil, errors.Wrap(db.ErrNotFound, msg)
|
||||
}
|
||||
chain = append(chain, tail)
|
||||
tail = parent
|
||||
}
|
||||
}
|
||||
|
||||
func reverseChain(c []block.SignedBeaconBlock) {
|
||||
last := len(c) - 1
|
||||
swaps := (last + 1) / 2
|
||||
for i := 0; i < swaps; i++ {
|
||||
c[i], c[last-i] = c[last-i], c[i]
|
||||
}
|
||||
}
|
||||
615
beacon-chain/state/stategen/history_test.go
Normal file
615
beacon-chain/state/stategen/history_test.go
Normal file
@@ -0,0 +1,615 @@
|
||||
package stategen
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block/mock"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestBlockForSlotFuture(t *testing.T) {
|
||||
ch := &CanonicalHistory{
|
||||
cs: &mockCurrentSlotter{Slot: 0},
|
||||
}
|
||||
_, _, err := ch.BlockForSlot(context.Background(), 1)
|
||||
require.ErrorIs(t, err, ErrFutureSlotRequested)
|
||||
}
|
||||
|
||||
func TestChainForSlotFuture(t *testing.T) {
|
||||
ch := &CanonicalHistory{
|
||||
cs: &mockCurrentSlotter{Slot: 0},
|
||||
}
|
||||
_, _, err := ch.chainForSlot(context.Background(), 1)
|
||||
require.ErrorIs(t, err, ErrFutureSlotRequested)
|
||||
}
|
||||
|
||||
func TestBestForSlot(t *testing.T) {
|
||||
nilBlock, err := wrapper.WrappedSignedBeaconBlock(ðpb.SignedBeaconBlock{})
|
||||
require.NoError(t, err)
|
||||
nilBody, err := wrapper.WrappedSignedBeaconBlock(ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{}})
|
||||
require.NoError(t, err)
|
||||
derp := errors.New("fake hash tree root method no hash good")
|
||||
badHTR := &mock.SignedBeaconBlock{BeaconBlock: &mock.BeaconBlock{HtrErr: derp, BeaconBlockBody: &mock.BeaconBlockBody{}}}
|
||||
var goodHTR [32]byte
|
||||
copy(goodHTR[:], []byte{23})
|
||||
var betterHTR [32]byte
|
||||
copy(betterHTR[:], []byte{42})
|
||||
good := &mock.SignedBeaconBlock{BeaconBlock: &mock.BeaconBlock{BeaconBlockBody: &mock.BeaconBlockBody{}, Htr: goodHTR}}
|
||||
better := &mock.SignedBeaconBlock{BeaconBlock: &mock.BeaconBlock{BeaconBlockBody: &mock.BeaconBlockBody{}, Htr: betterHTR}}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
err error
|
||||
blocks []block.SignedBeaconBlock
|
||||
best block.SignedBeaconBlock
|
||||
root [32]byte
|
||||
cc CanonicalChecker
|
||||
}{
|
||||
{
|
||||
name: "empty list",
|
||||
err: ErrNoCanonicalBlockForSlot,
|
||||
blocks: []block.SignedBeaconBlock{},
|
||||
},
|
||||
{
|
||||
name: "empty SignedBeaconBlock",
|
||||
err: ErrNoCanonicalBlockForSlot,
|
||||
blocks: []block.SignedBeaconBlock{nil},
|
||||
},
|
||||
{
|
||||
name: "empty BeaconBlock",
|
||||
err: ErrNoCanonicalBlockForSlot,
|
||||
blocks: []block.SignedBeaconBlock{nilBlock},
|
||||
},
|
||||
{
|
||||
name: "empty BeaconBlockBody",
|
||||
err: ErrNoCanonicalBlockForSlot,
|
||||
blocks: []block.SignedBeaconBlock{nilBody},
|
||||
},
|
||||
{
|
||||
name: "bad HTR",
|
||||
err: ErrInvalidDBBlock,
|
||||
blocks: []block.SignedBeaconBlock{badHTR},
|
||||
},
|
||||
{
|
||||
name: "IsCanonical fail",
|
||||
blocks: []block.SignedBeaconBlock{good, better},
|
||||
cc: &mockCanonicalChecker{is: true, err: derp},
|
||||
err: derp,
|
||||
},
|
||||
{
|
||||
name: "all non-canonical",
|
||||
err: ErrNoCanonicalBlockForSlot,
|
||||
blocks: []block.SignedBeaconBlock{good, better},
|
||||
cc: &mockCanonicalChecker{is: false},
|
||||
},
|
||||
{
|
||||
name: "one canonical",
|
||||
blocks: []block.SignedBeaconBlock{good},
|
||||
cc: &mockCanonicalChecker{is: true},
|
||||
root: goodHTR,
|
||||
best: good,
|
||||
},
|
||||
{
|
||||
name: "all canonical",
|
||||
blocks: []block.SignedBeaconBlock{better, good},
|
||||
cc: &mockCanonicalChecker{is: true},
|
||||
root: betterHTR,
|
||||
best: better,
|
||||
},
|
||||
{
|
||||
name: "first wins",
|
||||
blocks: []block.SignedBeaconBlock{good, better},
|
||||
cc: &mockCanonicalChecker{is: true},
|
||||
root: goodHTR,
|
||||
best: good,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
chk := CanonicalChecker(&mockCanonicalChecker{is: true})
|
||||
if c.cc != nil {
|
||||
chk = c.cc
|
||||
}
|
||||
ch := &CanonicalHistory{cc: chk}
|
||||
r, b, err := ch.bestForSlot(context.Background(), c.blocks)
|
||||
if c.err == nil {
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, c.best, b)
|
||||
require.Equal(t, c.root, r)
|
||||
} else {
|
||||
require.ErrorIs(t, err, c.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// happy path tests
|
||||
func TestCanonicalBlockForSlotHappy(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
var begin, middle, end types.Slot = 100, 150, 155
|
||||
specs := []mockHistorySpec{
|
||||
{slot: begin},
|
||||
{slot: middle, savedState: true},
|
||||
{slot: end, canonicalBlock: true},
|
||||
}
|
||||
hist := newMockHistory(t, specs, end+1)
|
||||
ch := &CanonicalHistory{h: hist, cc: hist, cs: hist}
|
||||
|
||||
// since only the end block and genesis are canonical, once the slot drops below
|
||||
// end, we should always get genesis
|
||||
cases := []struct {
|
||||
slot types.Slot
|
||||
highest types.Slot
|
||||
canon types.Slot
|
||||
name string
|
||||
}{
|
||||
{slot: hist.current, highest: end, canon: end, name: "slot > end"},
|
||||
{slot: end, highest: end, canon: end, name: "slot == end"},
|
||||
{slot: end - 1, highest: middle, canon: 0, name: "middle < slot < end"},
|
||||
{slot: middle, highest: middle, canon: 0, name: "slot == middle"},
|
||||
{slot: middle - 1, highest: begin, canon: 0, name: "begin < slot < middle"},
|
||||
{slot: begin, highest: begin, canon: 0, name: "slot == begin"},
|
||||
{slot: begin - 1, highest: 0, canon: 0, name: "genesis < slot < begin"},
|
||||
{slot: 0, highest: 0, canon: 0, name: "slot == genesis"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
bs, err := hist.HighestSlotBlocksBelow(ctx, c.slot+1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(bs), 1)
|
||||
r, err := bs[0].Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, hist.slotMap[c.highest], r)
|
||||
cr, _, err := ch.BlockForSlot(ctx, c.slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, hist.slotMap[c.canon], cr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanonicalBlockForSlotNonHappy(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
var begin, middle, end types.Slot = 100, 150, 155
|
||||
specs := []mockHistorySpec{
|
||||
{slot: begin},
|
||||
{slot: middle, savedState: true},
|
||||
{slot: end, canonicalBlock: true},
|
||||
}
|
||||
hist := newMockHistory(t, specs, end+1)
|
||||
|
||||
slotOrderObserved := make([]types.Slot, 0)
|
||||
derp := errors.New("HighestSlotBlocksBelow don't work")
|
||||
// since only the end block and genesis are canonical, once the slot drops below
|
||||
// end, we should always get genesis
|
||||
cases := []struct {
|
||||
name string
|
||||
slot types.Slot
|
||||
canon CanonicalChecker
|
||||
overrideHighest func(context.Context, types.Slot) ([]block.SignedBeaconBlock, error)
|
||||
slotOrderExpected []types.Slot
|
||||
err error
|
||||
root [32]byte
|
||||
}{
|
||||
{
|
||||
name: "HighestSlotBlocksBelow not called for genesis",
|
||||
overrideHighest: func(_ context.Context, _ types.Slot) ([]block.SignedBeaconBlock, error) {
|
||||
return nil, derp
|
||||
},
|
||||
root: hist.slotMap[0],
|
||||
},
|
||||
{
|
||||
name: "wrapped error from HighestSlotBlocksBelow returned",
|
||||
err: derp,
|
||||
overrideHighest: func(_ context.Context, _ types.Slot) ([]block.SignedBeaconBlock, error) {
|
||||
return nil, derp
|
||||
},
|
||||
slot: end,
|
||||
},
|
||||
{
|
||||
name: "HighestSlotBlocksBelow empty list",
|
||||
err: ErrNoBlocksBelowSlot,
|
||||
overrideHighest: func(_ context.Context, _ types.Slot) ([]block.SignedBeaconBlock, error) {
|
||||
return []block.SignedBeaconBlock{}, nil
|
||||
},
|
||||
slot: end,
|
||||
},
|
||||
{
|
||||
name: "HighestSlotBlocksBelow no canonical",
|
||||
err: ErrNoCanonicalBlockForSlot,
|
||||
canon: &mockCanonicalChecker{is: false},
|
||||
slot: end,
|
||||
},
|
||||
{
|
||||
name: "slot ordering correct - only genesis canonical",
|
||||
canon: &mockCanonicalChecker{isCanon: func(root [32]byte) (bool, error) {
|
||||
if root == hist.slotMap[0] {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}},
|
||||
overrideHighest: func(_ context.Context, s types.Slot) ([]block.SignedBeaconBlock, error) {
|
||||
slotOrderObserved = append(slotOrderObserved, s)
|
||||
// this allows the mock HighestSlotBlocksBelow to continue to execute now that we've recorded
|
||||
// the slot in our channel
|
||||
return nil, errFallThroughOverride
|
||||
},
|
||||
slotOrderExpected: []types.Slot{156, 155, 150, 100},
|
||||
slot: end,
|
||||
root: hist.slotMap[0],
|
||||
},
|
||||
{
|
||||
name: "slot ordering correct - slot 100 canonical",
|
||||
canon: &mockCanonicalChecker{isCanon: func(root [32]byte) (bool, error) {
|
||||
if root == hist.slotMap[100] {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}},
|
||||
overrideHighest: func(_ context.Context, s types.Slot) ([]block.SignedBeaconBlock, error) {
|
||||
slotOrderObserved = append(slotOrderObserved, s)
|
||||
// this allows the mock HighestSlotBlocksBelow to continue to execute now that we've recorded
|
||||
// the slot in our channel
|
||||
return nil, errFallThroughOverride
|
||||
},
|
||||
slotOrderExpected: []types.Slot{156, 155, 150},
|
||||
slot: end,
|
||||
root: hist.slotMap[100],
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var canon CanonicalChecker = hist
|
||||
if c.canon != nil {
|
||||
canon = c.canon
|
||||
}
|
||||
ch := &CanonicalHistory{h: hist, cc: canon, cs: hist}
|
||||
hist.overrideHighestSlotBlocksBelow = c.overrideHighest
|
||||
r, _, err := ch.BlockForSlot(ctx, c.slot)
|
||||
if c.err == nil {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.ErrorIs(t, err, c.err)
|
||||
}
|
||||
if len(c.slotOrderExpected) > 0 {
|
||||
require.Equal(t, len(c.slotOrderExpected), len(slotOrderObserved), "HighestSlotBlocksBelow not called the expected number of times")
|
||||
for i := range c.slotOrderExpected {
|
||||
require.Equal(t, c.slotOrderExpected[i], slotOrderObserved[i])
|
||||
}
|
||||
}
|
||||
if c.root != [32]byte{} {
|
||||
require.Equal(t, c.root, r)
|
||||
}
|
||||
slotOrderObserved = make([]types.Slot, 0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockCurrentSlotter struct {
|
||||
Slot types.Slot
|
||||
}
|
||||
|
||||
func (c *mockCurrentSlotter) CurrentSlot() types.Slot {
|
||||
return c.Slot
|
||||
}
|
||||
|
||||
var _ CurrentSlotter = &mockCurrentSlotter{}
|
||||
|
||||
func TestAncestorChainCache(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
var begin, middle, end types.Slot = 100, 150, 155
|
||||
specs := []mockHistorySpec{
|
||||
{slot: begin, canonicalBlock: true},
|
||||
{slot: middle, canonicalBlock: true},
|
||||
{slot: end, canonicalBlock: true},
|
||||
}
|
||||
hist := newMockHistory(t, specs, end+1)
|
||||
ch := &CanonicalHistory{h: hist, cc: hist, cs: hist}
|
||||
|
||||
// should only contain the genesis block
|
||||
require.Equal(t, 1, len(hist.states))
|
||||
|
||||
endBlock := hist.blocks[hist.slotMap[end]]
|
||||
st, bs, err := ch.ancestorChain(ctx, endBlock)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 3, len(bs))
|
||||
expectedHTR, err := hist.states[hist.slotMap[0]].HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
actualHTR, err := st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedHTR, actualHTR)
|
||||
|
||||
// now populate the cache, we should get the cached state instead of genesis
|
||||
ch.cache = &mockCachedGetter{
|
||||
cache: map[[32]byte]state.BeaconState{
|
||||
hist.slotMap[end]: hist.hiddenStates[hist.slotMap[end]],
|
||||
},
|
||||
}
|
||||
st, bs, err = ch.ancestorChain(ctx, endBlock)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(bs))
|
||||
expectedHTR, err = hist.hiddenStates[hist.slotMap[end]].HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
actualHTR, err = st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedHTR, actualHTR)
|
||||
|
||||
// populate cache with a different state for good measure
|
||||
ch.cache = &mockCachedGetter{
|
||||
cache: map[[32]byte]state.BeaconState{
|
||||
hist.slotMap[begin]: hist.hiddenStates[hist.slotMap[begin]],
|
||||
},
|
||||
}
|
||||
st, bs, err = ch.ancestorChain(ctx, endBlock)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(bs))
|
||||
expectedHTR, err = hist.hiddenStates[hist.slotMap[begin]].HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
actualHTR, err = st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedHTR, actualHTR)
|
||||
|
||||
// rebuild history w/ last state saved, make sure we get that instead of cache
|
||||
specs[2].savedState = true
|
||||
hist = newMockHistory(t, specs, end+1)
|
||||
ch = &CanonicalHistory{h: hist, cc: hist, cs: hist}
|
||||
ch.cache = &mockCachedGetter{
|
||||
cache: map[[32]byte]state.BeaconState{
|
||||
hist.slotMap[begin]: hist.hiddenStates[hist.slotMap[begin]],
|
||||
},
|
||||
}
|
||||
st, bs, err = ch.ancestorChain(ctx, endBlock)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(bs))
|
||||
expectedHTR, err = hist.states[hist.slotMap[end]].HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
actualHTR, err = st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedHTR, actualHTR)
|
||||
}
|
||||
|
||||
func TestAncestorChainOK(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
var begin, middle, end types.Slot = 100, 150, 155
|
||||
specs := []mockHistorySpec{
|
||||
{slot: begin},
|
||||
{slot: middle, savedState: true},
|
||||
{slot: end, canonicalBlock: true},
|
||||
}
|
||||
hist := newMockHistory(t, specs, end+1)
|
||||
ch := &CanonicalHistory{h: hist, cc: hist, cs: hist}
|
||||
|
||||
endBlock := hist.blocks[hist.slotMap[end]]
|
||||
st, bs, err := ch.ancestorChain(ctx, endBlock)
|
||||
require.NoError(t, err)
|
||||
|
||||
// middle is the most recent slot where savedState == true
|
||||
require.Equal(t, 1, len(bs))
|
||||
require.DeepEqual(t, endBlock, bs[0])
|
||||
expectedHTR, err := hist.states[hist.slotMap[middle]].HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
actualHTR, err := st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedHTR, actualHTR)
|
||||
|
||||
middleBlock := hist.blocks[hist.slotMap[middle]]
|
||||
st, bs, err = ch.ancestorChain(ctx, middleBlock)
|
||||
require.NoError(t, err)
|
||||
actualHTR, err = st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(bs))
|
||||
require.Equal(t, expectedHTR, actualHTR)
|
||||
}
|
||||
|
||||
func TestChainForSlot(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
var zero, one, two, three types.Slot = 50, 51, 150, 151
|
||||
specs := []mockHistorySpec{
|
||||
{slot: zero, canonicalBlock: true, savedState: true},
|
||||
{slot: one, canonicalBlock: true},
|
||||
{slot: two},
|
||||
{slot: three, canonicalBlock: true},
|
||||
}
|
||||
hist := newMockHistory(t, specs, three+10)
|
||||
ch := &CanonicalHistory{h: hist, cc: hist, cs: hist}
|
||||
firstNonGenesisRoot := hist.slotMap[zero]
|
||||
nonGenesisStateRoot, err := hist.states[firstNonGenesisRoot].HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
slot types.Slot
|
||||
stateRoot [32]byte
|
||||
blockRoots [][32]byte
|
||||
}{
|
||||
{
|
||||
name: "above latest slot (but before current slot)",
|
||||
slot: three + 1,
|
||||
stateRoot: nonGenesisStateRoot,
|
||||
blockRoots: [][32]byte{hist.slotMap[one], hist.slotMap[two], hist.slotMap[three]},
|
||||
},
|
||||
{
|
||||
name: "last canonical slot - two treated as canonical because it is parent of three",
|
||||
slot: three,
|
||||
stateRoot: nonGenesisStateRoot,
|
||||
blockRoots: [][32]byte{hist.slotMap[one], hist.slotMap[two], hist.slotMap[three]},
|
||||
},
|
||||
{
|
||||
name: "non-canonical slot skipped",
|
||||
slot: two,
|
||||
stateRoot: nonGenesisStateRoot,
|
||||
blockRoots: [][32]byte{hist.slotMap[one]},
|
||||
},
|
||||
{
|
||||
name: "first canonical slot",
|
||||
slot: one,
|
||||
stateRoot: nonGenesisStateRoot,
|
||||
blockRoots: [][32]byte{hist.slotMap[one]},
|
||||
},
|
||||
{
|
||||
name: "slot at saved state",
|
||||
slot: zero,
|
||||
stateRoot: nonGenesisStateRoot,
|
||||
blockRoots: [][32]byte{},
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
st, blocks, err := ch.chainForSlot(ctx, c.slot)
|
||||
require.NoError(t, err)
|
||||
actualStRoot, err := st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, c.stateRoot, actualStRoot)
|
||||
require.Equal(t, len(c.blockRoots), len(blocks))
|
||||
for i, b := range blocks {
|
||||
root, err := b.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, c.blockRoots[i], root)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAncestorChainOrdering(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
var zero, one, two, three, four, five types.Slot = 50, 51, 150, 151, 152, 200
|
||||
specs := []mockHistorySpec{
|
||||
{slot: zero},
|
||||
{slot: one, savedState: true},
|
||||
{slot: two},
|
||||
{slot: three},
|
||||
{slot: four},
|
||||
{slot: five},
|
||||
}
|
||||
|
||||
hist := newMockHistory(t, specs, five+1)
|
||||
endRoot := hist.slotMap[specs[len(specs)-1].slot]
|
||||
endBlock := hist.blocks[endRoot]
|
||||
|
||||
ch := &CanonicalHistory{h: hist, cc: hist, cs: hist}
|
||||
st, bs, err := ch.ancestorChain(ctx, endBlock)
|
||||
require.NoError(t, err)
|
||||
expectedRoot, err := hist.states[hist.slotMap[one]].HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
actualRoot, err := st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedRoot, actualRoot)
|
||||
// we asked for the chain leading up to five
|
||||
// one has the savedState. one is applied to the savedState, so it should be omitted
|
||||
// that means we should get two, three, four, five (length of 4)
|
||||
require.Equal(t, 4, len(bs))
|
||||
for i, slot := range []types.Slot{two, three, four, five} {
|
||||
require.Equal(t, slot, bs[i].Block().Slot(), fmt.Sprintf("wrong value at index %d", i))
|
||||
}
|
||||
|
||||
// do the same query, but with the final state saved
|
||||
// we should just get the final state w/o block to apply
|
||||
specs[5].savedState = true
|
||||
hist = newMockHistory(t, specs, five+1)
|
||||
endRoot = hist.slotMap[specs[len(specs)-1].slot]
|
||||
endBlock = hist.blocks[endRoot]
|
||||
|
||||
ch = &CanonicalHistory{h: hist, cc: hist, cs: hist}
|
||||
st, bs, err = ch.ancestorChain(ctx, endBlock)
|
||||
require.NoError(t, err)
|
||||
expectedRoot, err = hist.states[endRoot].HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
actualRoot, err = st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedRoot, actualRoot)
|
||||
require.Equal(t, 0, len(bs))
|
||||
|
||||
// slice off the last element for an odd size list (to cover odd/even in the reverseChain func)
|
||||
specs = specs[:len(specs)-1]
|
||||
require.Equal(t, 5, len(specs))
|
||||
hist = newMockHistory(t, specs, five+1)
|
||||
|
||||
ch = &CanonicalHistory{h: hist, cc: hist, cs: hist}
|
||||
endRoot = hist.slotMap[specs[len(specs)-1].slot]
|
||||
endBlock = hist.blocks[endRoot]
|
||||
st, bs, err = ch.ancestorChain(ctx, endBlock)
|
||||
require.NoError(t, err)
|
||||
expectedRoot, err = hist.states[hist.slotMap[one]].HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
actualRoot, err = st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedRoot, actualRoot)
|
||||
require.Equal(t, 3, len(bs))
|
||||
for i, slot := range []types.Slot{two, three, four} {
|
||||
require.Equal(t, slot, bs[i].Block().Slot(), fmt.Sprintf("wrong value at index %d", i))
|
||||
}
|
||||
}
|
||||
|
||||
type mockCanonicalChecker struct {
|
||||
isCanon func([32]byte) (bool, error)
|
||||
is bool
|
||||
err error
|
||||
}
|
||||
|
||||
func (m *mockCanonicalChecker) IsCanonical(_ context.Context, root [32]byte) (bool, error) {
|
||||
if m.isCanon != nil {
|
||||
return m.isCanon(root)
|
||||
}
|
||||
return m.is, m.err
|
||||
}
|
||||
|
||||
func TestReverseChain(t *testing.T) {
|
||||
// test 0,1,2,3 elements to handle: zero case; single element; even number; odd number
|
||||
for i := 0; i < 4; i++ {
|
||||
t.Run(fmt.Sprintf("reverseChain with %d elements", i), func(t *testing.T) {
|
||||
actual := mockBlocks(i, incrFwd)
|
||||
expected := mockBlocks(i, incrBwd)
|
||||
reverseChain(actual)
|
||||
if len(actual) != len(expected) {
|
||||
t.Errorf("different list lengths")
|
||||
}
|
||||
for i := 0; i < len(actual); i++ {
|
||||
sblockA, ok := actual[i].(*mock.SignedBeaconBlock)
|
||||
require.Equal(t, true, ok)
|
||||
blockA, ok := sblockA.BeaconBlock.(*mock.BeaconBlock)
|
||||
require.Equal(t, true, ok)
|
||||
sblockE, ok := expected[i].(*mock.SignedBeaconBlock)
|
||||
require.Equal(t, true, ok)
|
||||
blockE, ok := sblockE.BeaconBlock.(*mock.BeaconBlock)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, blockA.Htr, blockE.Htr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func incrBwd(n int, c chan uint32) {
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
c <- uint32(i)
|
||||
}
|
||||
close(c)
|
||||
}
|
||||
|
||||
func incrFwd(n int, c chan uint32) {
|
||||
for i := 0; i < n; i++ {
|
||||
c <- uint32(i)
|
||||
}
|
||||
close(c)
|
||||
}
|
||||
|
||||
func mockBlocks(n int, iter func(int, chan uint32)) []block.SignedBeaconBlock {
|
||||
bchan := make(chan uint32)
|
||||
go iter(n, bchan)
|
||||
mb := make([]block.SignedBeaconBlock, 0)
|
||||
for i := range bchan {
|
||||
h := [32]byte{}
|
||||
binary.LittleEndian.PutUint32(h[:], i)
|
||||
b := &mock.SignedBeaconBlock{BeaconBlock: &mock.BeaconBlock{BeaconBlockBody: &mock.BeaconBlockBody{}, Htr: h}}
|
||||
mb = append(mb, b)
|
||||
}
|
||||
return mb
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func (b *MockReplayerBuilder) SetMockSlotError(s types.Slot, e error) {
|
||||
b.forSlot[s] = &MockReplayer{Err: e}
|
||||
}
|
||||
|
||||
func (b *MockReplayerBuilder) ForSlot(target types.Slot) stategen.Replayer {
|
||||
func (b *MockReplayerBuilder) ReplayerForSlot(target types.Slot) stategen.Replayer {
|
||||
return b.forSlot[target]
|
||||
}
|
||||
|
||||
|
||||
@@ -235,79 +235,6 @@ func ReplayProcessSlots(ctx context.Context, state state.BeaconState, slot types
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// This finds the last saved block in DB from searching backwards from input slot,
|
||||
// it returns the block root and the slot of the block.
|
||||
// This is used by both hot and cold state management.
|
||||
func (s *State) lastSavedBlock(ctx context.Context, slot types.Slot) ([32]byte, types.Slot, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "stateGen.lastSavedBlock")
|
||||
defer span.End()
|
||||
|
||||
// Handle the genesis case where the input slot is 0.
|
||||
if slot == 0 {
|
||||
gRoot, err := s.genesisRoot(ctx)
|
||||
if err != nil {
|
||||
return [32]byte{}, 0, err
|
||||
}
|
||||
return gRoot, 0, nil
|
||||
}
|
||||
|
||||
lastSaved, err := s.beaconDB.HighestSlotBlocksBelow(ctx, slot)
|
||||
if err != nil {
|
||||
return [32]byte{}, 0, err
|
||||
}
|
||||
|
||||
// Given this is used to query canonical block. There should only be one saved canonical block of a given slot.
|
||||
if len(lastSaved) != 1 {
|
||||
return [32]byte{}, 0, fmt.Errorf("highest saved block does not equal to 1, it equals to %d", len(lastSaved))
|
||||
}
|
||||
if lastSaved[0] == nil || lastSaved[0].IsNil() || lastSaved[0].Block().IsNil() {
|
||||
return [32]byte{}, 0, nil
|
||||
}
|
||||
r, err := lastSaved[0].Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
return [32]byte{}, 0, err
|
||||
}
|
||||
|
||||
return r, lastSaved[0].Block().Slot(), nil
|
||||
}
|
||||
|
||||
// This finds the last saved state in DB from searching backwards from input slot,
|
||||
// it returns the block root of the block which was used to produce the state.
|
||||
// This is used by both hot and cold state management.
|
||||
func (s *State) lastSavedState(ctx context.Context, slot types.Slot) (state.ReadOnlyBeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "stateGen.lastSavedState")
|
||||
defer span.End()
|
||||
|
||||
// Handle the genesis case where the input slot is 0.
|
||||
if slot == 0 {
|
||||
return s.beaconDB.GenesisState(ctx)
|
||||
}
|
||||
|
||||
lastSaved, err := s.beaconDB.HighestSlotStatesBelow(ctx, slot+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Given this is used to query canonical state. There should only be one saved canonical block of a given slot.
|
||||
if len(lastSaved) != 1 {
|
||||
return nil, fmt.Errorf("highest saved state does not equal to 1, it equals to %d", len(lastSaved))
|
||||
}
|
||||
if lastSaved[0] == nil {
|
||||
return nil, errUnknownState
|
||||
}
|
||||
|
||||
return lastSaved[0], nil
|
||||
}
|
||||
|
||||
// This returns the genesis root.
|
||||
func (s *State) genesisRoot(ctx context.Context) ([32]byte, error) {
|
||||
b, err := s.beaconDB.GenesisBlock(ctx)
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
return b.Block().HashTreeRoot()
|
||||
}
|
||||
|
||||
// Given the start slot and the end slot, this returns the finalized beacon blocks in between.
|
||||
// Since hot states don't have finalized blocks, this should ONLY be used for replaying cold state.
|
||||
func (s *State) loadFinalizedBlocks(ctx context.Context, startSlot, endSlot types.Slot) ([]block.SignedBeaconBlock, error) {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
@@ -344,135 +343,6 @@ func TestLoadBlocks_BadStart(t *testing.T) {
|
||||
assert.ErrorContains(t, "end block roots don't match", err)
|
||||
}
|
||||
|
||||
func TestLastSavedBlock_Genesis(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
s := &State{
|
||||
beaconDB: beaconDB,
|
||||
finalizedInfo: &finalizedInfo{slot: 128},
|
||||
}
|
||||
|
||||
gBlk := util.NewBeaconBlock()
|
||||
gRoot, err := gBlk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(gBlk)))
|
||||
require.NoError(t, s.beaconDB.SaveGenesisBlockRoot(ctx, gRoot))
|
||||
|
||||
savedRoot, savedSlot, err := s.lastSavedBlock(ctx, 0)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, types.Slot(0), savedSlot, "Did not save genesis slot")
|
||||
assert.Equal(t, savedRoot, savedRoot, "Did not save genesis root")
|
||||
}
|
||||
|
||||
func TestLastSavedBlock_CanGet(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
s := &State{
|
||||
beaconDB: beaconDB,
|
||||
finalizedInfo: &finalizedInfo{slot: 128},
|
||||
}
|
||||
|
||||
b1 := util.NewBeaconBlock()
|
||||
b1.Block.Slot = s.finalizedInfo.slot + 5
|
||||
require.NoError(t, s.beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b1)))
|
||||
b2 := util.NewBeaconBlock()
|
||||
b2.Block.Slot = s.finalizedInfo.slot + 10
|
||||
require.NoError(t, s.beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b2)))
|
||||
b3 := util.NewBeaconBlock()
|
||||
b3.Block.Slot = s.finalizedInfo.slot + 20
|
||||
require.NoError(t, s.beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b3)))
|
||||
|
||||
savedRoot, savedSlot, err := s.lastSavedBlock(ctx, s.finalizedInfo.slot+100)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, s.finalizedInfo.slot+20, savedSlot)
|
||||
wantedRoot, err := b3.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, wantedRoot, savedRoot, "Did not save correct root")
|
||||
}
|
||||
|
||||
func TestLastSavedBlock_NoSavedBlock(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
s := &State{
|
||||
beaconDB: beaconDB,
|
||||
finalizedInfo: &finalizedInfo{slot: 128},
|
||||
}
|
||||
|
||||
root, slot, err := s.lastSavedBlock(ctx, s.finalizedInfo.slot+1)
|
||||
require.NoError(t, err)
|
||||
if slot != 0 && root != [32]byte{} {
|
||||
t.Error("Did not get wanted block")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLastSavedState_Genesis(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
s := &State{
|
||||
beaconDB: beaconDB,
|
||||
finalizedInfo: &finalizedInfo{slot: 128},
|
||||
}
|
||||
|
||||
gBlk := util.NewBeaconBlock()
|
||||
gState, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
gRoot, err := gBlk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(gBlk)))
|
||||
require.NoError(t, s.beaconDB.SaveGenesisBlockRoot(ctx, gRoot))
|
||||
require.NoError(t, s.beaconDB.SaveState(ctx, gState, gRoot))
|
||||
|
||||
savedState, err := s.lastSavedState(ctx, 0)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, gState.InnerStateUnsafe(), savedState.InnerStateUnsafe())
|
||||
}
|
||||
|
||||
func TestLastSavedState_CanGet(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
s := &State{
|
||||
beaconDB: beaconDB,
|
||||
finalizedInfo: &finalizedInfo{slot: 128},
|
||||
}
|
||||
|
||||
b1 := util.NewBeaconBlock()
|
||||
b1.Block.Slot = s.finalizedInfo.slot + 5
|
||||
require.NoError(t, s.beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b1)))
|
||||
b2 := util.NewBeaconBlock()
|
||||
b2.Block.Slot = s.finalizedInfo.slot + 10
|
||||
require.NoError(t, s.beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b2)))
|
||||
b2Root, err := b2.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
st, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, st.SetSlot(s.finalizedInfo.slot+10))
|
||||
|
||||
require.NoError(t, s.beaconDB.SaveState(ctx, st, b2Root))
|
||||
b3 := util.NewBeaconBlock()
|
||||
b3.Block.Slot = s.finalizedInfo.slot + 20
|
||||
require.NoError(t, s.beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b3)))
|
||||
|
||||
savedState, err := s.lastSavedState(ctx, s.finalizedInfo.slot+100)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, st.InnerStateUnsafe(), savedState.InnerStateUnsafe())
|
||||
}
|
||||
|
||||
func TestLastSavedState_NoSavedBlockState(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
s := &State{
|
||||
beaconDB: beaconDB,
|
||||
finalizedInfo: &finalizedInfo{slot: 128},
|
||||
}
|
||||
|
||||
b1 := util.NewBeaconBlock()
|
||||
b1.Block.Slot = 127
|
||||
require.NoError(t, s.beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b1)))
|
||||
|
||||
_, err := s.lastSavedState(ctx, s.finalizedInfo.slot+1)
|
||||
assert.ErrorContains(t, errUnknownState.Error(), err)
|
||||
}
|
||||
|
||||
// tree1 constructs the following tree:
|
||||
// B0 - B1 - - B3 -- B5
|
||||
// \- B2 -- B4 -- B6 ----- B8
|
||||
|
||||
@@ -7,10 +7,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
@@ -22,6 +19,12 @@ var ErrNoBlocksBelowSlot = errors.New("no blocks found in db below slot")
|
||||
var ErrInvalidDBBlock = errors.New("invalid block found in database")
|
||||
var ErrReplayTargetSlotExceeded = errors.New("desired replay slot is less than state's slot")
|
||||
|
||||
type retrievalMethod int
|
||||
|
||||
const (
|
||||
forSlot retrievalMethod = iota
|
||||
)
|
||||
|
||||
// HistoryAccessor describes the minimum set of database methods needed to support the ReplayerBuilder.
|
||||
type HistoryAccessor interface {
|
||||
HighestSlotBlocksBelow(ctx context.Context, slot types.Slot) ([]block.SignedBeaconBlock, error)
|
||||
@@ -48,12 +51,18 @@ type Replayer interface {
|
||||
// ReplayToSlot invokes ReplayBlocks under the hood,
|
||||
// but then also runs process_slots to advance the state past the root or slot used in the builder.
|
||||
// For example, if you wanted the state to be at the target slot, but only integrating blocks up to
|
||||
// slot-1, you could request Builder.ForSlot(slot-1).ReplayToSlot(slot)
|
||||
// slot-1, you could request Builder.ReplayerForSlot(slot-1).ReplayToSlot(slot)
|
||||
ReplayToSlot(ctx context.Context, target types.Slot) (state.BeaconState, error)
|
||||
}
|
||||
|
||||
var _ Replayer = &stateReplayer{}
|
||||
|
||||
// chainer is responsible for supplying the chain components necessary to rebuild a state,
|
||||
// namely a starting BeaconState and all available blocks from the starting state up to and including the target slot
|
||||
type chainer interface {
|
||||
chainForSlot(ctx context.Context, target types.Slot) (state.BeaconState, []block.SignedBeaconBlock, error)
|
||||
}
|
||||
|
||||
type stateReplayer struct {
|
||||
s state.BeaconState
|
||||
target types.Slot
|
||||
@@ -118,7 +127,7 @@ func (rs *stateReplayer) ReplayBlocks(ctx context.Context) (state.BeaconState, e
|
||||
// ReplayToSlot invokes ReplayBlocks under the hood,
|
||||
// but then also runs process_slots to advance the state past the root or slot used in the builder.
|
||||
// for example, if you wanted the state to be at the target slot, but only integrating blocks up to
|
||||
// slot-1, you could request Builder.ForSlot(slot-1).ReplayToSlot(slot)
|
||||
// slot-1, you could request Builder.ReplayerForSlot(slot-1).ReplayToSlot(slot)
|
||||
func (rs *stateReplayer) ReplayToSlot(ctx context.Context, replayTo types.Slot) (state.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "stateGen.stateReplayer.ReplayToSlot")
|
||||
defer span.End()
|
||||
@@ -159,232 +168,9 @@ func (rs *stateReplayer) ReplayToSlot(ctx context.Context, replayTo types.Slot)
|
||||
// (only ForSlot implemented so far).
|
||||
// See documentation on Replayer for more on how to use this to obtain pre/post-block states
|
||||
type ReplayerBuilder interface {
|
||||
// ForSlot creates a builder that will create a state that includes blocks up to and including the requested slot
|
||||
// ReplayerForSlot creates a builder that will create a state that includes blocks up to and including the requested slot
|
||||
// The resulting Replayer will always yield a state with .Slot=target; if there are skipped blocks
|
||||
// between the highest canonical block in the db and the target, the replayer will fast-forward past the intervening
|
||||
// slots via process_slots.
|
||||
ForSlot(target types.Slot) Replayer
|
||||
}
|
||||
|
||||
func WithCache(c CachedGetter) CanonicalBuilderOption {
|
||||
return func(b *CanonicalBuilder) {
|
||||
b.chainer.useCache(c)
|
||||
}
|
||||
}
|
||||
|
||||
type CanonicalBuilderOption func(*CanonicalBuilder)
|
||||
|
||||
// NewCanonicalBuilder handles initializing the default concrete ReplayerBuilder implementation.
|
||||
func NewCanonicalBuilder(h HistoryAccessor, c CanonicalChecker, cs CurrentSlotter, opts ...CanonicalBuilderOption) *CanonicalBuilder {
|
||||
b := &CanonicalBuilder{
|
||||
chainer: &canonicalChainer{
|
||||
h: h,
|
||||
c: c,
|
||||
cs: cs,
|
||||
},
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(b)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
type retrievalMethod int
|
||||
|
||||
const (
|
||||
forSlot retrievalMethod = iota
|
||||
)
|
||||
|
||||
// CanonicalBuilder builds a Replayer that uses a combination of database queries and a
|
||||
// CanonicalChecker (which should usually be a fork choice store implementing an IsCanonical method)
|
||||
// to determine the canonical chain and apply it to generate the desired state.
|
||||
type CanonicalBuilder struct {
|
||||
chainer chainer
|
||||
}
|
||||
|
||||
var _ ReplayerBuilder = &CanonicalBuilder{}
|
||||
|
||||
func (r *CanonicalBuilder) ForSlot(target types.Slot) Replayer {
|
||||
return &stateReplayer{chainer: r.chainer, method: forSlot, target: target}
|
||||
}
|
||||
|
||||
// chainer is responsible for supplying the chain components necessary to rebuild a state,
|
||||
// namely a starting BeaconState and all available blocks from the starting state up to and including the target slot
|
||||
type chainer interface {
|
||||
chainForSlot(ctx context.Context, target types.Slot) (state.BeaconState, []block.SignedBeaconBlock, error)
|
||||
useCache(c CachedGetter)
|
||||
}
|
||||
|
||||
type canonicalChainer struct {
|
||||
h HistoryAccessor
|
||||
c CanonicalChecker
|
||||
cs CurrentSlotter
|
||||
cache CachedGetter
|
||||
}
|
||||
|
||||
var _ chainer = &canonicalChainer{}
|
||||
|
||||
func (c *canonicalChainer) useCache(cache CachedGetter) {
|
||||
c.cache = cache
|
||||
}
|
||||
|
||||
// ChainForSlot creates a value that satisfies the Replayer interface via db queries
|
||||
// and the stategen transition helper methods. This implementation uses the following algorithm:
|
||||
// - find the highest canonical block <= the target slot
|
||||
// - starting with this block, recursively search backwards for a stored state, and accumulate intervening blocks
|
||||
func (c *canonicalChainer) chainForSlot(ctx context.Context, target types.Slot) (state.BeaconState, []block.SignedBeaconBlock, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "canonicalChainer.chainForSlot")
|
||||
defer span.End()
|
||||
currentSlot := c.cs.CurrentSlot()
|
||||
if target > currentSlot {
|
||||
return nil, nil, errors.Wrap(ErrFutureSlotRequested, fmt.Sprintf("requested=%d, current=%d", target, currentSlot))
|
||||
}
|
||||
_, b, err := c.canonicalBlockForSlot(ctx, target)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, fmt.Sprintf("unable to find replay data for slot=%d", target))
|
||||
}
|
||||
s, descendants, err := c.ancestorChain(ctx, b)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to query for ancestor and descendant blocks")
|
||||
}
|
||||
|
||||
return s, descendants, nil
|
||||
}
|
||||
|
||||
// canonicalBlockForSlot uses HighestSlotBlocksBelow(target+1) and the CanonicalChecker
|
||||
// to find the highest canonical block available to replay to the given slot.
|
||||
func (c *canonicalChainer) canonicalBlockForSlot(ctx context.Context, target types.Slot) ([32]byte, block.SignedBeaconBlock, error) {
|
||||
for target > 0 {
|
||||
if ctx.Err() != nil {
|
||||
return [32]byte{}, nil, errors.Wrap(ctx.Err(), "context canceled during canonicalBlockForSlot")
|
||||
}
|
||||
hbs, err := c.h.HighestSlotBlocksBelow(ctx, target+1)
|
||||
if err != nil {
|
||||
return [32]byte{}, nil, errors.Wrap(err, fmt.Sprintf("error finding highest block w/ slot <= %d", target))
|
||||
}
|
||||
if len(hbs) == 0 {
|
||||
return [32]byte{}, nil, errors.Wrap(ErrNoBlocksBelowSlot, fmt.Sprintf("slot=%d", target))
|
||||
}
|
||||
r, b, err := c.bestForSlot(ctx, hbs)
|
||||
if err == nil {
|
||||
// we found a valid, canonical block!
|
||||
return r, b, nil
|
||||
}
|
||||
|
||||
// we found a block, but it wasn't considered canonical - keep looking
|
||||
if errors.Is(err, ErrNoCanonicalBlockForSlot) {
|
||||
// break once we've seen slot 0 (and prevent underflow)
|
||||
if hbs[0].Block().Slot() == 0 {
|
||||
break
|
||||
}
|
||||
target = hbs[0].Block().Slot() - 1
|
||||
continue
|
||||
}
|
||||
return [32]byte{}, nil, err
|
||||
}
|
||||
b, err := c.h.GenesisBlock(ctx)
|
||||
if err != nil {
|
||||
return [32]byte{}, nil, errors.Wrap(err, "db error while retrieving genesis block")
|
||||
}
|
||||
root, _, err := c.bestForSlot(ctx, []block.SignedBeaconBlock{b})
|
||||
if err != nil {
|
||||
return [32]byte{}, nil, errors.Wrap(err, "problem retrieving genesis block")
|
||||
}
|
||||
return root, b, nil
|
||||
}
|
||||
|
||||
// bestForSlot encapsulates several messy realities of the underlying db code, looping through multiple blocks,
|
||||
// performing null/validity checks, and using CanonicalChecker to only pick canonical blocks.
|
||||
func (c *canonicalChainer) bestForSlot(ctx context.Context, hbs []block.SignedBeaconBlock) ([32]byte, block.SignedBeaconBlock, error) {
|
||||
for _, b := range hbs {
|
||||
if helpers.BeaconBlockIsNil(b) != nil {
|
||||
continue
|
||||
}
|
||||
root, err := b.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
// use this error message to wrap a sentinel error for error type matching
|
||||
wrapped := errors.Wrap(ErrInvalidDBBlock, err.Error())
|
||||
msg := fmt.Sprintf("could not compute hash_tree_root for block at slot=%d", b.Block().Slot())
|
||||
return [32]byte{}, nil, errors.Wrap(wrapped, msg)
|
||||
}
|
||||
canon, err := c.c.IsCanonical(ctx, root)
|
||||
if err != nil {
|
||||
return [32]byte{}, nil, errors.Wrap(err, "replayer could not check if block is canonical")
|
||||
}
|
||||
if canon {
|
||||
return root, b, nil
|
||||
}
|
||||
}
|
||||
return [32]byte{}, nil, errors.Wrap(ErrNoCanonicalBlockForSlot, "no good block for slot")
|
||||
}
|
||||
|
||||
func (c *canonicalChainer) getState(ctx context.Context, root [32]byte) (state.BeaconState, error) {
|
||||
if c.cache != nil {
|
||||
st, err := c.cache.ByRoot(root)
|
||||
if err == nil {
|
||||
return st, nil
|
||||
}
|
||||
if !errors.Is(err, ErrNotInCache) {
|
||||
return nil, errors.Wrap(err, "error reading from state cache during state replay")
|
||||
}
|
||||
}
|
||||
return c.h.StateOrError(ctx, root)
|
||||
}
|
||||
|
||||
// ancestorChain works backwards through the chain lineage, accumulating blocks and checking for a saved state.
|
||||
// If it finds a saved state that the tail block was descended from, it returns this state and
|
||||
// all blocks in the lineage, including the tail block. Blocks are returned in ascending order.
|
||||
// Note that this function assumes that the tail is a canonical block, and therefore assumes that
|
||||
// all ancestors are also canonical.
|
||||
func (c *canonicalChainer) ancestorChain(ctx context.Context, tail block.SignedBeaconBlock) (state.BeaconState, []block.SignedBeaconBlock, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "canonicalChainer.ancestorChain")
|
||||
defer span.End()
|
||||
chain := make([]block.SignedBeaconBlock, 0)
|
||||
for {
|
||||
if err := ctx.Err(); err != nil {
|
||||
msg := fmt.Sprintf("context canceled while finding ancestors of block at slot %d", tail.Block().Slot())
|
||||
return nil, nil, errors.Wrap(err, msg)
|
||||
}
|
||||
b := tail.Block()
|
||||
// compute hash_tree_root of current block and try to look up the corresponding state
|
||||
root, err := b.HashTreeRoot()
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("could not compute htr for descendant block at slot=%d", b.Slot())
|
||||
return nil, nil, errors.Wrap(err, msg)
|
||||
}
|
||||
st, err := c.getState(ctx, root)
|
||||
// err == nil, we've got a real state - the job is done!
|
||||
// Note: in cases where there are skipped slots we could find a state that is a descendant
|
||||
// of the block we are searching for. We don't want to return a future block, so in this case
|
||||
// we keep working backwards.
|
||||
if err == nil && st.Slot() == b.Slot() {
|
||||
// we found the state by the root of the head, meaning it has already been applied.
|
||||
// we only want to return the blocks descended from it.
|
||||
reverseChain(chain)
|
||||
return st, chain, nil
|
||||
}
|
||||
// ErrNotFoundState errors are fine, but other errors mean something is wrong with the db
|
||||
if err != nil && !errors.Is(err, db.ErrNotFoundState) {
|
||||
return nil, nil, errors.Wrap(err, fmt.Sprintf("error querying database for state w/ block root = %#x", root))
|
||||
}
|
||||
parent, err := c.h.Block(ctx, bytesutil.ToBytes32(b.ParentRoot()))
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("db error when retrieving parent of block at slot=%d by root=%#x", b.Slot(), b.ParentRoot())
|
||||
return nil, nil, errors.Wrap(err, msg)
|
||||
}
|
||||
if helpers.BeaconBlockIsNil(parent) != nil {
|
||||
msg := fmt.Sprintf("unable to retrieve parent of block at slot=%d by root=%#x", b.Slot(), b.ParentRoot())
|
||||
return nil, nil, errors.Wrap(db.ErrNotFound, msg)
|
||||
}
|
||||
chain = append(chain, tail)
|
||||
tail = parent
|
||||
}
|
||||
}
|
||||
|
||||
func reverseChain(c []block.SignedBeaconBlock) {
|
||||
last := len(c) - 1
|
||||
swaps := (last + 1) / 2
|
||||
for i := 0; i < swaps; i++ {
|
||||
c[i], c[last-i] = c[last-i], c[i]
|
||||
}
|
||||
ReplayerForSlot(target types.Slot) Replayer
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user