Skip to content

Commit

Permalink
wip: some more preFork UTs for block backfilling
Browse files Browse the repository at this point in the history
  • Loading branch information
abi87 committed Nov 3, 2023
1 parent 8ac3a71 commit 8b643e7
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 6 deletions.
130 changes: 130 additions & 0 deletions vms/proposervm/state_sync_block_backfilling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,135 @@ import (
statelessblock "github.com/ava-labs/avalanchego/vms/proposervm/block"
)

// Pre Fork section
func TestBlockBackfillPreForkSuccess(t *testing.T) {
// setup VM with backfill enabled
require := require.New(t)
toEngineCh := make(chan common.Message)
innerVM, vm, fromInnerVMCh := setupBlockBackfillingVM(t, toEngineCh)
defer func() {
require.NoError(vm.Shutdown(context.Background()))
}()

var (
forkHeight = uint64(2000)
blkCount = 10
startBlkHeight = uint64(100)

// create a list of consecutive blocks and build state summary of top of them
// proBlks should all be preForkBlocks
proBlks, innerBlks = createTestBlocks(t, vm, forkHeight, blkCount, startBlkHeight)

innerTopBlk = innerBlks[len(innerBlks)-1]
preForkTopBlk = proBlks[len(proBlks)-1]
stateSummaryHeight = innerTopBlk.Height() + 1
)

stateSummary := &block.TestStateSummary{
IDV: ids.ID{'s', 'u', 'm', 'm', 'a', 'r', 'y', 'I', 'D'},
HeightV: stateSummaryHeight,
BytesV: []byte{'i', 'n', 'n', 'e', 'r'},
}
innerStateSyncedBlk := &snowman.TestBlock{
TestDecidable: choices.TestDecidable{
IDV: ids.ID{'i', 'n', 'n', 'e', 'r', 'S', 'y', 'n', 'c', 'e', 'd'},
},
ParentV: innerTopBlk.ID(),
HeightV: stateSummary.Height(),
BytesV: []byte("inner state synced block"),
}
stateSummary.AcceptF = func(ctx context.Context) (block.StateSyncMode, error) {
return block.StateSyncStatic, nil
}

ctx := context.Background()
_, err := stateSummary.Accept(ctx)
require.NoError(err)

innerVM.LastAcceptedF = func(context.Context) (ids.ID, error) {
return innerStateSyncedBlk.ID(), nil
}
innerVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
switch blkID {
case innerStateSyncedBlk.ID():
return innerStateSyncedBlk, nil
default:
return nil, database.ErrNotFound
}
}

fromInnerVMCh <- common.StateSyncDone
<-toEngineCh

innerVM.BackfillBlocksEnabledF = func(ctx context.Context) (ids.ID, uint64, error) {
return innerStateSyncedBlk.ID(), innerStateSyncedBlk.Height() - 1, nil
}
innerVM.GetBlockIDAtHeightF = func(ctx context.Context, height uint64) (ids.ID, error) {
if height == innerStateSyncedBlk.Height() {
return innerStateSyncedBlk.ID(), nil
}
return ids.Empty, database.ErrNotFound
}

blkID, _, err := vm.BackfillBlocksEnabled(ctx)
require.NoError(err)
require.Equal(preForkTopBlk.ID(), blkID)

// Backfill some blocks
innerVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
for _, blk := range innerBlks {
if bytes.Equal(b, blk.Bytes()) {
return blk, nil
}
}
return nil, database.ErrNotFound
}
innerVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
for _, blk := range innerBlks {
if blkID == blk.ID() {
return blk, nil
}
}
return nil, database.ErrNotFound
}
innerVM.GetBlockIDAtHeightF = func(ctx context.Context, height uint64) (ids.ID, error) {
for _, blk := range innerBlks {
if height == blk.Height() {
return blk.ID(), nil
}
}
return ids.Empty, database.ErrNotFound
}
innerVM.BackfillBlocksF = func(_ context.Context, b [][]byte) (ids.ID, uint64, error) {
lowestblk := innerBlks[0]
for _, blk := range innerBlks {
if blk.Height() < lowestblk.Height() {
lowestblk = blk
}
}
return lowestblk.Parent(), lowestblk.Height() - 1, nil
}

blkBytes := make([][]byte, 0, len(proBlks))
for _, blk := range proBlks {
blkBytes = append(blkBytes, blk.Bytes())
}
nextBlkID, nextBlkHeight, err := vm.BackfillBlocks(ctx, blkBytes)
require.NoError(err)
require.Equal(proBlks[0].Parent(), nextBlkID)
require.Equal(proBlks[0].Height()-1, nextBlkHeight)

// check proBlocks have been indexed
for _, blk := range proBlks {
blkID, err := vm.GetBlockIDAtHeight(ctx, blk.Height())
require.NoError(err)
require.Equal(blk.ID(), blkID)

_, err = vm.GetBlock(ctx, blkID)
require.NoError(err)
}
}

func TestBlockBackfillEnabledPreFork(t *testing.T) {
require := require.New(t)
toEngineCh := make(chan common.Message)
Expand Down Expand Up @@ -104,6 +233,7 @@ func TestBlockBackfillEnabledPreFork(t *testing.T) {
require.ErrorIs(err, block.ErrBlockBackfillingNotEnabled)
}

// Post Fork section
func TestBlockBackfillEnabledPostFork(t *testing.T) {
require := require.New(t)
toEngineCh := make(chan common.Message)
Expand Down
27 changes: 21 additions & 6 deletions vms/proposervm/state_syncable_vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,17 +189,32 @@ func (vm *VM) BackfillBlocks(ctx context.Context, blksBytes [][]byte) (ids.ID, u
blks[blk.Height()] = blk
}

// 2. Validate outer blocks
// 2. Validate blocks, checking that they are continguous
blkHeights := maps.Keys(blks)
sort.Slice(blkHeights, func(i, j int) bool {
return blkHeights[i] < blkHeights[j] // sort in ascending order by heights
})

topBlk, err := vm.getBlock(ctx, vm.latestBackfilledBlock)
if err != nil {
return ids.Empty, 0, fmt.Errorf("failed retrieving latest backfilled block, %s, %w", vm.latestBackfilledBlock, err)
var (
topBlk = blks[blkHeights[len(blkHeights)-1]]
topIdx = len(blkHeights) - 2
)

// vm.latestBackfilledBlock is non nil only if proposerVM has forked
if vm.latestBackfilledBlock != ids.Empty {
latestBackfilledBlk, err := vm.getBlock(ctx, vm.latestBackfilledBlock)
if err != nil {
return ids.Empty, 0, fmt.Errorf("failed retrieving latest backfilled block, %s, %w", vm.latestBackfilledBlock, err)
}
if latestBackfilledBlk.Parent() != topBlk.ID() {
return ids.Empty, 0, fmt.Errorf("unexpected backfilled block %s, expected child' parent is %s", topBlk.ID(), latestBackfilledBlk.Parent())
}

topBlk = latestBackfilledBlk
topIdx = len(blkHeights) - 1
}
for i := len(blkHeights) - 1; i >= 0; i-- {

for i := topIdx; i >= 0; i-- {
blk := blks[blkHeights[i]]
if topBlk.Parent() != blk.ID() {
return ids.Empty, 0, fmt.Errorf("unexpected backfilled block %s, expected child' parent is %s", blk.ID(), topBlk.Parent())
Expand Down Expand Up @@ -272,7 +287,7 @@ func (vm *VM) nextBlockBackfillData(ctx context.Context, innerBlkHeight uint64)

}

return childBlk.Parent(), innerBlkHeight, nil
return childBlk.Parent(), childBlk.Height() - 1, nil
}

func (vm *VM) revertBackfilledBlock(blk Block) error {
Expand Down
3 changes: 3 additions & 0 deletions vms/proposervm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ type VM struct {

stateSyncDone utils.Atomic[bool]

// latestBackfilledBlock track the latest post fork block
// indexed via block backfilling. Will be ids.Empty if proposerVM
// fork is not active
latestBackfilledBlock ids.ID
}

Expand Down

0 comments on commit 8b643e7

Please sign in to comment.