mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-22 19:40:37 +00:00
5a66807989
* First take at updating everything to v5 * Patch gRPC gateway to use prysm v5 Fix patch * Update go ssz --------- Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
583 lines
18 KiB
Go
583 lines
18 KiB
Go
package stategen
|
|
|
|
import (
|
|
"context"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/mock"
|
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
|
)
|
|
|
|
func TestBlockForSlotFuture(t *testing.T) {
|
|
ch := &CanonicalHistory{
|
|
cs: &mockCurrentSlotter{Slot: 0},
|
|
}
|
|
_, err := ch.BlockRootForSlot(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) {
|
|
derp := errors.New("fake hash tree root method no hash good")
|
|
var goodHTR [32]byte
|
|
copy(goodHTR[:], []byte{23})
|
|
var betterHTR [32]byte
|
|
copy(betterHTR[:], []byte{42})
|
|
|
|
cases := []struct {
|
|
name string
|
|
err error
|
|
blocks []interfaces.ReadOnlySignedBeaconBlock
|
|
roots [][32]byte
|
|
root [32]byte
|
|
cc CanonicalChecker
|
|
}{
|
|
{
|
|
name: "empty list",
|
|
err: ErrNoCanonicalBlockForSlot,
|
|
roots: [][32]byte{},
|
|
},
|
|
{
|
|
name: "IsCanonical fail",
|
|
roots: [][32]byte{goodHTR, betterHTR},
|
|
cc: &mockCanonicalChecker{is: true, err: derp},
|
|
err: derp,
|
|
},
|
|
{
|
|
name: "all non-canonical",
|
|
err: ErrNoCanonicalBlockForSlot,
|
|
roots: [][32]byte{goodHTR, betterHTR},
|
|
cc: &mockCanonicalChecker{is: false},
|
|
},
|
|
{
|
|
name: "one canonical",
|
|
cc: &mockCanonicalChecker{is: true},
|
|
root: goodHTR,
|
|
roots: [][32]byte{goodHTR},
|
|
},
|
|
{
|
|
name: "all canonical",
|
|
cc: &mockCanonicalChecker{is: true},
|
|
root: betterHTR,
|
|
roots: [][32]byte{betterHTR, goodHTR},
|
|
},
|
|
{
|
|
name: "first wins",
|
|
cc: &mockCanonicalChecker{is: true},
|
|
root: goodHTR,
|
|
roots: [][32]byte{goodHTR, betterHTR},
|
|
},
|
|
}
|
|
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, err := ch.bestForSlot(context.Background(), c.roots)
|
|
if c.err == nil {
|
|
require.NoError(t, err)
|
|
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 primitives.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 primitives.Slot
|
|
highest primitives.Slot
|
|
canon primitives.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) {
|
|
_, rs, err := hist.HighestRootsBelowSlot(ctx, c.slot+1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(rs), 1)
|
|
require.Equal(t, hist.slotMap[c.highest], rs[0])
|
|
cr, err := ch.BlockRootForSlot(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 primitives.Slot = 100, 150, 155
|
|
specs := []mockHistorySpec{
|
|
{slot: begin},
|
|
{slot: middle, savedState: true},
|
|
{slot: end, canonicalBlock: true},
|
|
}
|
|
hist := newMockHistory(t, specs, end+1)
|
|
|
|
genesis, err := hist.GenesisBlockRoot(ctx)
|
|
require.NoError(t, err)
|
|
slotOrderObserved := make([]primitives.Slot, 0)
|
|
derp := errors.New("HighestRootsBelowSlot 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 primitives.Slot
|
|
canon CanonicalChecker
|
|
overrideHighest func(context.Context, primitives.Slot) (primitives.Slot, [][32]byte, error)
|
|
slotOrderExpected []primitives.Slot
|
|
err error
|
|
root [32]byte
|
|
}{
|
|
{
|
|
name: "HighestRootsBelowSlot not called for genesis",
|
|
overrideHighest: func(_ context.Context, _ primitives.Slot) (primitives.Slot, [][32]byte, error) {
|
|
return 0, [][32]byte{}, derp
|
|
},
|
|
root: hist.slotMap[0],
|
|
},
|
|
{
|
|
name: "wrapped error from HighestRootsBelowSlot returned",
|
|
err: derp,
|
|
overrideHighest: func(_ context.Context, _ primitives.Slot) (primitives.Slot, [][32]byte, error) {
|
|
return 0, [][32]byte{}, derp
|
|
},
|
|
slot: end,
|
|
},
|
|
{
|
|
name: "HighestRootsBelowSlot empty list",
|
|
err: ErrNoBlocksBelowSlot,
|
|
overrideHighest: func(_ context.Context, _ primitives.Slot) (primitives.Slot, [][32]byte, error) {
|
|
return 0, [][32]byte{}, nil
|
|
},
|
|
slot: end,
|
|
},
|
|
{
|
|
name: "HighestRootsBelowSlot no canonical",
|
|
canon: &mockCanonicalChecker{is: false},
|
|
slot: end,
|
|
root: genesis,
|
|
},
|
|
{
|
|
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 primitives.Slot) (primitives.Slot, [][32]byte, error) {
|
|
slotOrderObserved = append(slotOrderObserved, s)
|
|
// this allows the mock HighestRootsBelowSlot to continue to execute now that we've recorded
|
|
// the slot in our channel
|
|
return 0, nil, errFallThroughOverride
|
|
},
|
|
slotOrderExpected: []primitives.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 primitives.Slot) (primitives.Slot, [][32]byte, error) {
|
|
slotOrderObserved = append(slotOrderObserved, s)
|
|
// this allows the mock HighestRootsBelowSlot to continue to execute now that we've recorded
|
|
// the slot in our channel
|
|
return 0, nil, errFallThroughOverride
|
|
},
|
|
slotOrderExpected: []primitives.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.BlockRootForSlot(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), "HighestRootsBelowSlot 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([]primitives.Slot, 0)
|
|
})
|
|
}
|
|
}
|
|
|
|
type mockCurrentSlotter struct {
|
|
Slot primitives.Slot
|
|
}
|
|
|
|
func (c *mockCurrentSlotter) CurrentSlot() primitives.Slot {
|
|
return c.Slot
|
|
}
|
|
|
|
var _ CurrentSlotter = &mockCurrentSlotter{}
|
|
|
|
func TestAncestorChainCache(t *testing.T) {
|
|
ctx := context.Background()
|
|
var begin, middle, end primitives.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 primitives.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 primitives.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 primitives.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 primitives.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 []primitives.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 []primitives.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)) []interfaces.ReadOnlySignedBeaconBlock {
|
|
bchan := make(chan uint32)
|
|
go iter(n, bchan)
|
|
mb := make([]interfaces.ReadOnlySignedBeaconBlock, 0)
|
|
for i := range bchan {
|
|
var 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
|
|
}
|