mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 21:08:10 -05:00
629 lines
20 KiB
Go
629 lines
20 KiB
Go
package stategen
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/db"
|
|
testDB "github.com/OffchainLabs/prysm/v7/beacon-chain/db/testing"
|
|
doublylinkedtree "github.com/OffchainLabs/prysm/v7/beacon-chain/forkchoice/doubly-linked-tree"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
|
"github.com/OffchainLabs/prysm/v7/config/params"
|
|
blt "github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
|
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
|
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
|
"github.com/OffchainLabs/prysm/v7/testing/assert"
|
|
"github.com/OffchainLabs/prysm/v7/testing/require"
|
|
"github.com/OffchainLabs/prysm/v7/testing/util"
|
|
)
|
|
|
|
func TestStateByRoot_GenesisState(t *testing.T) {
|
|
ctx := t.Context()
|
|
beaconDB := testDB.SetupDB(t)
|
|
|
|
service := New(beaconDB, doublylinkedtree.New())
|
|
b := util.NewBeaconBlock()
|
|
bRoot, err := b.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
beaconState, _ := util.DeterministicGenesisState(t, 32)
|
|
require.NoError(t, service.beaconDB.SaveState(ctx, beaconState, bRoot))
|
|
util.SaveBlock(t, ctx, service.beaconDB, b)
|
|
require.NoError(t, service.beaconDB.SaveGenesisBlockRoot(ctx, bRoot))
|
|
loadedState, err := service.StateByRoot(ctx, params.BeaconConfig().ZeroHash) // Zero hash is genesis state root.
|
|
require.NoError(t, err)
|
|
require.DeepSSZEqual(t, loadedState.ToProtoUnsafe(), beaconState.ToProtoUnsafe())
|
|
}
|
|
|
|
func TestStateByRoot_ColdState(t *testing.T) {
|
|
ctx := t.Context()
|
|
beaconDB := testDB.SetupDB(t)
|
|
|
|
service := New(beaconDB, doublylinkedtree.New())
|
|
service.finalizedInfo.slot = 2
|
|
service.slotsPerArchivedPoint = 1
|
|
|
|
b := util.NewBeaconBlock()
|
|
b.Block.Slot = 1
|
|
util.SaveBlock(t, ctx, beaconDB, b)
|
|
bRoot, err := b.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
beaconState, _ := util.DeterministicGenesisState(t, 32)
|
|
require.NoError(t, beaconState.SetSlot(1))
|
|
val, err := beaconState.ValidatorAtIndex(0)
|
|
require.NoError(t, err)
|
|
val.Slashed = true
|
|
require.NoError(t, beaconState.UpdateValidatorAtIndex(0, val))
|
|
roval, err := beaconState.ValidatorAtIndexReadOnly(0)
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, roval.Slashed())
|
|
|
|
require.NoError(t, service.beaconDB.SaveState(ctx, beaconState, bRoot))
|
|
util.SaveBlock(t, ctx, service.beaconDB, b)
|
|
require.NoError(t, service.beaconDB.SaveGenesisBlockRoot(ctx, bRoot))
|
|
loadedState, err := service.StateByRoot(ctx, bRoot)
|
|
require.NoError(t, err)
|
|
require.DeepSSZEqual(t, loadedState.ToProtoUnsafe(), beaconState.ToProtoUnsafe())
|
|
|
|
bal, err := service.ActiveNonSlashedBalancesByRoot(ctx, bRoot)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 32, len(bal))
|
|
for _, balance := range bal[1:] {
|
|
require.Equal(t, params.BeaconConfig().MaxEffectiveBalance, balance)
|
|
}
|
|
require.Equal(t, uint64(0), bal[0])
|
|
}
|
|
|
|
func TestStateByRootIfCachedNoCopy_HotState(t *testing.T) {
|
|
ctx := t.Context()
|
|
beaconDB := testDB.SetupDB(t)
|
|
|
|
service := New(beaconDB, doublylinkedtree.New())
|
|
|
|
beaconState, _ := util.DeterministicGenesisState(t, 32)
|
|
r := [32]byte{'A'}
|
|
require.NoError(t, service.beaconDB.SaveStateSummary(ctx, ðpb.StateSummary{Root: r[:]}))
|
|
service.hotStateCache.put(r, beaconState)
|
|
|
|
loadedState := service.StateByRootIfCachedNoCopy(r)
|
|
require.DeepSSZEqual(t, loadedState.ToProtoUnsafe(), beaconState.ToProtoUnsafe())
|
|
}
|
|
|
|
func TestStateByRootIfCachedNoCopy_ColdState(t *testing.T) {
|
|
ctx := t.Context()
|
|
beaconDB := testDB.SetupDB(t)
|
|
|
|
service := New(beaconDB, doublylinkedtree.New())
|
|
service.finalizedInfo.slot = 2
|
|
service.slotsPerArchivedPoint = 1
|
|
|
|
b := util.NewBeaconBlock()
|
|
b.Block.Slot = 1
|
|
util.SaveBlock(t, ctx, beaconDB, b)
|
|
bRoot, err := b.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
beaconState, _ := util.DeterministicGenesisState(t, 32)
|
|
require.NoError(t, beaconState.SetSlot(1))
|
|
require.NoError(t, service.beaconDB.SaveState(ctx, beaconState, bRoot))
|
|
util.SaveBlock(t, ctx, service.beaconDB, b)
|
|
require.NoError(t, service.beaconDB.SaveGenesisBlockRoot(ctx, bRoot))
|
|
loadedState := service.StateByRootIfCachedNoCopy(bRoot)
|
|
require.NoError(t, err)
|
|
require.Equal(t, loadedState, nil)
|
|
}
|
|
|
|
func TestDeleteStateFromCaches(t *testing.T) {
|
|
ctx := t.Context()
|
|
beaconDB := testDB.SetupDB(t)
|
|
|
|
service := New(beaconDB, doublylinkedtree.New())
|
|
beaconState, _ := util.DeterministicGenesisState(t, 32)
|
|
r := [32]byte{'A'}
|
|
|
|
require.Equal(t, false, service.hotStateCache.has(r))
|
|
_, has, err := service.epochBoundaryStateCache.getByBlockRoot(r)
|
|
require.NoError(t, err)
|
|
require.Equal(t, false, has)
|
|
|
|
service.hotStateCache.put(r, beaconState)
|
|
require.NoError(t, service.epochBoundaryStateCache.put(r, beaconState))
|
|
|
|
require.Equal(t, true, service.hotStateCache.has(r))
|
|
_, has, err = service.epochBoundaryStateCache.getByBlockRoot(r)
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, has)
|
|
|
|
require.NoError(t, service.DeleteStateFromCaches(ctx, r))
|
|
|
|
require.Equal(t, false, service.hotStateCache.has(r))
|
|
_, has, err = service.epochBoundaryStateCache.getByBlockRoot(r)
|
|
require.NoError(t, err)
|
|
require.Equal(t, false, has)
|
|
}
|
|
|
|
// testChainSlot represents one slot of the test chain
|
|
type testChainSlot struct {
|
|
st state.BeaconState
|
|
root [32]byte
|
|
blk blt.ROBlock
|
|
}
|
|
|
|
// testChain represents the test block chain that is written to the DB / cache.
|
|
// Used to test the StateByRoot, StateByRootInitSync and loadStateByRoot methods.
|
|
type testChain struct {
|
|
t *testing.T
|
|
ctx context.Context
|
|
d db.Database
|
|
srv *State
|
|
cslots map[primitives.Slot]testChainSlot
|
|
}
|
|
|
|
// the following are helpers used in the test cases and helpers to concisely get the different
|
|
// components of the chain by slot.
|
|
func (c testChain) cslot(t *testing.T, s primitives.Slot) testChainSlot {
|
|
cs, ok := c.cslots[s]
|
|
require.Equal(t, true, ok, fmt.Sprintf("state not found for slot %d", s))
|
|
return cs
|
|
}
|
|
|
|
func (c testChain) state(t *testing.T, s primitives.Slot) state.BeaconState {
|
|
return c.cslot(t, s).st
|
|
}
|
|
|
|
func (c testChain) blockRoot(t *testing.T, s primitives.Slot) [32]byte {
|
|
return c.cslot(t, s).root
|
|
}
|
|
|
|
func (c testChain) block(t *testing.T, s primitives.Slot) blt.ROBlock {
|
|
return c.cslot(t, s).blk
|
|
}
|
|
|
|
type testSetupSlots struct {
|
|
stateAt primitives.Slot
|
|
lastblock primitives.Slot
|
|
}
|
|
|
|
func TestLoadStateByRoot(t *testing.T) {
|
|
ctx := t.Context()
|
|
persistEpochBoundary := func(r testChain, slot primitives.Slot) {
|
|
require.NoError(t, r.srv.epochBoundaryStateCache.put(r.blockRoot(r.t, slot), r.state(t, slot)))
|
|
}
|
|
persistHotStateCache := func(r testChain, slot primitives.Slot) {
|
|
r.srv.hotStateCache.put(r.blockRoot(t, slot), r.state(t, slot))
|
|
}
|
|
persistDB := func(r testChain, slot primitives.Slot) {
|
|
require.NoError(r.t, r.d.SaveState(r.ctx, r.state(t, slot), r.blockRoot(t, slot)))
|
|
}
|
|
persistFinalizedStruct := func(r testChain, slot primitives.Slot) {
|
|
st := r.state(t, slot)
|
|
r.srv.finalizedInfo.state = st
|
|
r.srv.finalizedInfo.slot = st.Slot()
|
|
r.srv.finalizedInfo.root = r.blockRoot(t, slot)
|
|
}
|
|
|
|
type testLoader func(r testChain) (state.BeaconState, error)
|
|
lsbr := func(slot primitives.Slot) testLoader {
|
|
return func(tc testChain) (state.BeaconState, error) {
|
|
return tc.srv.loadStateByRoot(tc.ctx, tc.blockRoot(t, slot))
|
|
}
|
|
}
|
|
sbrInit := func(slot primitives.Slot) testLoader {
|
|
return func(tc testChain) (state.BeaconState, error) {
|
|
return tc.srv.StateByRootInitialSync(tc.ctx, tc.blockRoot(t, slot))
|
|
}
|
|
}
|
|
sbr := func(slot primitives.Slot) testLoader {
|
|
return func(tc testChain) (state.BeaconState, error) {
|
|
return tc.srv.StateByRoot(tc.ctx, tc.blockRoot(t, slot))
|
|
}
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
slots testSetupSlots
|
|
persistState func(r testChain, s primitives.Slot)
|
|
loader testLoader // ie loadStateByRoot; StateByRootInitialSync; StateByRoot
|
|
}{
|
|
// loadStateByRoot tests
|
|
{
|
|
name: "loadStateByRoot - using epoch boundary cache",
|
|
persistState: persistEpochBoundary,
|
|
loader: lsbr(10),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 10},
|
|
},
|
|
{
|
|
name: "loadStateByRoot - with replay - using epoch boundary cache",
|
|
persistState: persistEpochBoundary,
|
|
loader: lsbr(11),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 11},
|
|
},
|
|
{
|
|
name: "loadStateByRoot - using hot state cache",
|
|
persistState: persistHotStateCache,
|
|
loader: lsbr(10),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 10},
|
|
},
|
|
{
|
|
name: "loadStateByRoot - with replay - using hot state cache",
|
|
persistState: persistHotStateCache,
|
|
loader: lsbr(11),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 11},
|
|
},
|
|
{
|
|
name: "loadStateByRoot - using db",
|
|
persistState: persistDB,
|
|
loader: lsbr(10),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 10},
|
|
},
|
|
{
|
|
name: "loadStateByRoot - with replay - using db",
|
|
persistState: persistDB,
|
|
loader: lsbr(11),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 11},
|
|
},
|
|
{
|
|
name: "loadStateByRoot - using finalizedInfo struct field",
|
|
persistState: persistFinalizedStruct,
|
|
loader: lsbr(10),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 10},
|
|
},
|
|
{
|
|
name: "loadStateByRoot - with replay - using finalizedInfo struct field",
|
|
persistState: persistFinalizedStruct,
|
|
loader: lsbr(11),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 11},
|
|
},
|
|
// StateByRootInitSync tests
|
|
{
|
|
name: "StateByRootInitSync - using epoch boundary cache",
|
|
persistState: persistEpochBoundary,
|
|
loader: sbrInit(10),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 10},
|
|
},
|
|
{
|
|
name: "StateByRootInitSync - with replay - using epoch boundary cache",
|
|
persistState: persistEpochBoundary,
|
|
loader: sbrInit(11),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 11},
|
|
},
|
|
{
|
|
name: "StateByRootInitSync - using hot state cache",
|
|
persistState: persistHotStateCache,
|
|
loader: sbrInit(10),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 10},
|
|
},
|
|
{
|
|
name: "StateByRootInitSync - with replay - using hot state cache",
|
|
persistState: persistHotStateCache,
|
|
loader: sbrInit(11),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 11},
|
|
},
|
|
{
|
|
name: "StateByRootInitSync - using db",
|
|
persistState: persistDB,
|
|
loader: sbrInit(10),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 10},
|
|
},
|
|
{
|
|
name: "StateByRootInitSync - with replay - using db",
|
|
persistState: persistDB,
|
|
loader: sbrInit(11),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 11},
|
|
},
|
|
{
|
|
name: "StateByRootInitSync - using finalizedInfo struct field",
|
|
persistState: persistFinalizedStruct,
|
|
loader: sbrInit(10),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 10},
|
|
},
|
|
{
|
|
name: "StateByRootInitSync - with replay - using finalizedInfo struct field",
|
|
persistState: persistFinalizedStruct,
|
|
loader: sbrInit(11),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 11},
|
|
},
|
|
// StateByRoot tests
|
|
{
|
|
name: "StateByRoot - using epoch boundary cache",
|
|
persistState: persistEpochBoundary,
|
|
loader: sbr(10),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 10},
|
|
},
|
|
{
|
|
name: "StateByRoot - with replay - using epoch boundary cache",
|
|
persistState: persistEpochBoundary,
|
|
loader: sbr(11),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 11},
|
|
},
|
|
{
|
|
name: "StateByRoot - using hot state cache",
|
|
persistState: persistHotStateCache,
|
|
loader: sbr(10),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 10},
|
|
},
|
|
{
|
|
name: "StateByRoot - with replay - using hot state cache",
|
|
persistState: persistHotStateCache,
|
|
loader: sbr(11),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 11},
|
|
},
|
|
{
|
|
name: "StateByRoot - using db",
|
|
persistState: persistDB,
|
|
loader: sbr(10),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 10},
|
|
},
|
|
{
|
|
name: "StateByRoot - with replay - using db",
|
|
persistState: persistDB,
|
|
loader: sbr(11),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 11},
|
|
},
|
|
{
|
|
name: "StateByRoot - using finalizedInfo struct field",
|
|
persistState: persistFinalizedStruct,
|
|
loader: sbr(10),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 10},
|
|
},
|
|
{
|
|
name: "StateByRoot - with replay - using finalizedInfo struct field",
|
|
persistState: persistFinalizedStruct,
|
|
loader: sbr(11),
|
|
slots: testSetupSlots{stateAt: 9, lastblock: 11},
|
|
},
|
|
}
|
|
|
|
// Do all the state setup just once
|
|
|
|
// generate state and wind up to slot 9
|
|
st9, _ := util.DeterministicGenesisState(t, 32)
|
|
st9, err := ReplayProcessSlots(ctx, st9, 9)
|
|
require.NoError(t, err)
|
|
// take latest block header at slot 9 as parent root for block at slot 10
|
|
hdr := st9.LatestBlockHeader()
|
|
hdrRoot, err := hdr.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
|
|
st10 := st9.Copy()
|
|
// set up block at slot 10, pointed to the latest block header as parent root
|
|
// at slot 10
|
|
// using correctly computed proposer index
|
|
blk10 := util.NewBeaconBlock()
|
|
blk10.Block.Slot = 10
|
|
blk10.Block.ParentRoot = hdrRoot[:]
|
|
idx10, err := helpers.BeaconProposerIndexAtSlot(ctx, st10, blk10.Block.Slot)
|
|
require.NoError(t, err)
|
|
blk10.Block.ProposerIndex = idx10
|
|
// modernized block types for slot 10
|
|
ib10, err := blt.NewSignedBeaconBlock(blk10)
|
|
require.NoError(t, err)
|
|
|
|
// make state at slot 10 by transitioning a copy of st9 with ib10 (aka blk10)
|
|
st10, err = executeStateTransitionStateGen(t.Context(), st10, ib10)
|
|
require.NoError(t, err)
|
|
st10Root, err := st10.HashTreeRoot(t.Context())
|
|
require.NoError(t, err)
|
|
// update state root for block 10 now that its been through stf
|
|
blk10.Block.StateRoot = st10Root[:]
|
|
ib10, err = blt.NewSignedBeaconBlock(blk10)
|
|
require.NoError(t, err)
|
|
rob10, err := blt.NewROBlock(ib10)
|
|
require.NoError(t, err)
|
|
|
|
// same series of steps for block at slot 11 - pointing to block 10 as parent
|
|
blk11 := util.NewBeaconBlock()
|
|
blk11.Block.Slot = 11
|
|
blk11.Block.ParentRoot = rob10.RootSlice()
|
|
idx11, err := helpers.BeaconProposerIndexAtSlot(t.Context(), st10, blk11.Block.Slot)
|
|
require.NoError(t, err)
|
|
blk11.Block.ProposerIndex = idx11
|
|
ib11, err := blt.NewSignedBeaconBlock(blk11)
|
|
require.NoError(t, err)
|
|
|
|
// same steps as 9->10; stf 10->11, then block update
|
|
st11 := st10.Copy()
|
|
st11, err = executeStateTransitionStateGen(t.Context(), st11, ib11)
|
|
require.NoError(t, err)
|
|
st11Root, err := st11.HashTreeRoot(t.Context())
|
|
require.NoError(t, err)
|
|
// update state root for block 11 now that its been through stf
|
|
blk11.Block.StateRoot = st11Root[:]
|
|
ib11, err = blt.NewSignedBeaconBlock(blk11)
|
|
require.NoError(t, err)
|
|
rob11, err := blt.NewROBlock(ib11)
|
|
require.NoError(t, err)
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
helpers.ClearCache()
|
|
beaconDB := testDB.SetupDB(t)
|
|
service := New(beaconDB, doublylinkedtree.New())
|
|
r := testChain{
|
|
t: t,
|
|
ctx: ctx,
|
|
d: beaconDB,
|
|
srv: service,
|
|
cslots: map[primitives.Slot]testChainSlot{
|
|
9: testChainSlot{
|
|
// note blk is nil for slot 9
|
|
st: st9.Copy(),
|
|
root: hdrRoot,
|
|
},
|
|
10: testChainSlot{
|
|
st: st10.Copy(),
|
|
blk: rob10,
|
|
root: rob10.Root(),
|
|
},
|
|
11: testChainSlot{
|
|
st: st11.Copy(),
|
|
blk: rob11,
|
|
root: rob11.Root(),
|
|
},
|
|
},
|
|
}
|
|
slots := c.slots
|
|
sumRoot := r.blockRoot(t, slots.stateAt)
|
|
require.NoError(t, r.srv.beaconDB.SaveStateSummary(r.ctx, ðpb.StateSummary{Slot: slots.stateAt, Root: sumRoot[:]}))
|
|
// Second param controls the highest slot that we save blocks for, save all blocks <= that slot
|
|
for _, ut := range []primitives.Slot{10, 11} {
|
|
if ut <= slots.lastblock {
|
|
require.NoError(t, r.d.SaveBlock(r.ctx, r.block(t, ut)))
|
|
}
|
|
}
|
|
c.persistState(r, slots.stateAt)
|
|
// DeepSSZEqual spams full state diffs on failures, so try to fail faster with more specific assertions.
|
|
expect := r.state(t, slots.lastblock)
|
|
got, err := c.loader(r)
|
|
require.NoError(t, err)
|
|
require.Equal(t, slots.lastblock, got.Slot())
|
|
lbrE, err := expect.LatestBlockHeader().HashTreeRoot()
|
|
require.NoError(t, err)
|
|
lbrG, err := got.LatestBlockHeader().HashTreeRoot()
|
|
require.NoError(t, err)
|
|
require.Equal(t, lbrE, lbrG)
|
|
require.DeepSSZEqual(t, expect.ToProtoUnsafe(), got.ToProtoUnsafe())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLastAncestorState_CanGetUsingDB(t *testing.T) {
|
|
ctx := t.Context()
|
|
beaconDB := testDB.SetupDB(t)
|
|
service := New(beaconDB, doublylinkedtree.New())
|
|
|
|
b0 := util.NewBeaconBlock()
|
|
b0.Block.ParentRoot = bytesutil.PadTo([]byte{'a'}, 32)
|
|
r0, err := b0.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
b1 := util.NewBeaconBlock()
|
|
b1.Block.Slot = 1
|
|
b1.Block.ParentRoot = bytesutil.PadTo(r0[:], 32)
|
|
r1, err := b1.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
b2 := util.NewBeaconBlock()
|
|
b2.Block.Slot = 2
|
|
b2.Block.ParentRoot = bytesutil.PadTo(r1[:], 32)
|
|
r2, err := b2.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
b3 := util.NewBeaconBlock()
|
|
b3.Block.Slot = 3
|
|
b3.Block.ParentRoot = bytesutil.PadTo(r2[:], 32)
|
|
r3, err := b3.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
|
|
b1State, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
require.NoError(t, b1State.SetSlot(1))
|
|
|
|
util.SaveBlock(t, ctx, service.beaconDB, b0)
|
|
util.SaveBlock(t, ctx, service.beaconDB, b1)
|
|
util.SaveBlock(t, ctx, service.beaconDB, b2)
|
|
util.SaveBlock(t, ctx, service.beaconDB, b3)
|
|
require.NoError(t, service.beaconDB.SaveState(ctx, b1State, r1))
|
|
|
|
lastState, err := service.latestAncestor(ctx, r3)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, b1State.Slot(), lastState.Slot(), "Did not get wanted state")
|
|
}
|
|
|
|
func TestLastAncestorState_CanGetUsingCache(t *testing.T) {
|
|
ctx := t.Context()
|
|
beaconDB := testDB.SetupDB(t)
|
|
service := New(beaconDB, doublylinkedtree.New())
|
|
|
|
b0 := util.NewBeaconBlock()
|
|
b0.Block.ParentRoot = bytesutil.PadTo([]byte{'a'}, 32)
|
|
r0, err := b0.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
b1 := util.NewBeaconBlock()
|
|
b1.Block.Slot = 1
|
|
b1.Block.ParentRoot = bytesutil.PadTo(r0[:], 32)
|
|
r1, err := b1.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
b2 := util.NewBeaconBlock()
|
|
b2.Block.Slot = 2
|
|
b2.Block.ParentRoot = bytesutil.PadTo(r1[:], 32)
|
|
r2, err := b2.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
b3 := util.NewBeaconBlock()
|
|
b3.Block.Slot = 3
|
|
b3.Block.ParentRoot = bytesutil.PadTo(r2[:], 32)
|
|
r3, err := b3.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
|
|
b1State, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
require.NoError(t, b1State.SetSlot(1))
|
|
|
|
util.SaveBlock(t, ctx, service.beaconDB, b0)
|
|
util.SaveBlock(t, ctx, service.beaconDB, b1)
|
|
util.SaveBlock(t, ctx, service.beaconDB, b2)
|
|
util.SaveBlock(t, ctx, service.beaconDB, b3)
|
|
service.hotStateCache.put(r1, b1State)
|
|
|
|
lastState, err := service.latestAncestor(ctx, r3)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, b1State.Slot(), lastState.Slot(), "Did not get wanted state")
|
|
}
|
|
|
|
func TestState_HasState(t *testing.T) {
|
|
ctx := t.Context()
|
|
beaconDB := testDB.SetupDB(t)
|
|
service := New(beaconDB, doublylinkedtree.New())
|
|
s, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
rHit1 := [32]byte{1}
|
|
rHit2 := [32]byte{2}
|
|
rMiss := [32]byte{3}
|
|
service.hotStateCache.put(rHit1, s)
|
|
require.NoError(t, service.epochBoundaryStateCache.put(rHit2, s))
|
|
|
|
b := util.NewBeaconBlock()
|
|
rHit3, err := b.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
require.NoError(t, service.beaconDB.SaveState(ctx, s, rHit3))
|
|
tt := []struct {
|
|
root [32]byte
|
|
want bool
|
|
}{
|
|
{rHit1, true},
|
|
{rHit2, true},
|
|
{rMiss, false},
|
|
{rHit3, true},
|
|
}
|
|
for _, tc := range tt {
|
|
got, err := service.HasState(ctx, tc.root)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.want, got)
|
|
}
|
|
}
|
|
|
|
func TestState_HasStateInCache(t *testing.T) {
|
|
ctx := t.Context()
|
|
beaconDB := testDB.SetupDB(t)
|
|
service := New(beaconDB, doublylinkedtree.New())
|
|
s, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
rHit1 := [32]byte{1}
|
|
rHit2 := [32]byte{2}
|
|
rMiss := [32]byte{3}
|
|
service.hotStateCache.put(rHit1, s)
|
|
require.NoError(t, service.epochBoundaryStateCache.put(rHit2, s))
|
|
|
|
tt := []struct {
|
|
root [32]byte
|
|
want bool
|
|
}{
|
|
{rHit1, true},
|
|
{rHit2, true},
|
|
{rMiss, false},
|
|
}
|
|
for _, tc := range tt {
|
|
got, err := service.hasStateInCache(ctx, tc.root)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.want, got)
|
|
}
|
|
}
|