Skip to content

Commit

Permalink
[blockdao] introduce blockindexer with start height (#3869)
Browse files Browse the repository at this point in the history
  • Loading branch information
envestcc authored May 29, 2023
1 parent 4bbba8d commit 790c4f5
Show file tree
Hide file tree
Showing 6 changed files with 704 additions and 1 deletion.
16 changes: 15 additions & 1 deletion blockchain/blockdao/blockindexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ type (
DeleteTipBlock(context.Context, *block.Block) error
}

// BlockIndexerWithStart defines an interface to accept block to build index from a start height
BlockIndexerWithStart interface {
BlockIndexer
// StartHeight returns the start height of the indexer
StartHeight() uint64
}

// BlockIndexerChecker defines a checker of block indexer
BlockIndexerChecker struct {
dao BlockDAO
Expand Down Expand Up @@ -67,7 +74,14 @@ func (bic *BlockIndexerChecker) CheckIndexer(ctx context.Context, indexer BlockI
if targetHeight == 0 || targetHeight > daoTip {
targetHeight = daoTip
}
for i := tipHeight + 1; i <= targetHeight; i++ {
startHeight := tipHeight + 1
if indexerWS, ok := indexer.(BlockIndexerWithStart); ok {
indexStartHeight := indexerWS.StartHeight()
if indexStartHeight > startHeight {
startHeight = indexStartHeight
}
}
for i := startHeight; i <= targetHeight; i++ {
blk, err := bic.dao.GetBlockByHeight(i)
if err != nil {
return err
Expand Down
144 changes: 144 additions & 0 deletions blockchain/blockdao/blockindexer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright (c) 2019 IoTeX Foundation
// This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
// or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
// This source code is governed by Apache License 2.0 that can be found in the LICENSE file.

package blockdao

import (
"context"
"strconv"
"testing"

"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/iotexproject/iotex-proto/golang/iotextypes"

"github.com/iotexproject/iotex-core/action/protocol"
"github.com/iotexproject/iotex-core/blockchain/block"
"github.com/iotexproject/iotex-core/blockchain/blockdao/mock"
"github.com/iotexproject/iotex-core/blockchain/genesis"
"github.com/iotexproject/iotex-core/test/identityset"
)

func TestCheckIndexer(t *testing.T) {

cases := []struct {
daoHeight uint64
indexerTipHeight uint64
expectedPutBlocks []uint64
noErr bool
}{
{5, 0, []uint64{1, 2, 3, 4, 5}, true},
{5, 1, []uint64{2, 3, 4, 5}, true},
{5, 2, []uint64{3, 4, 5}, true},
{5, 3, []uint64{4, 5}, true},
{5, 4, []uint64{5}, true},
{5, 5, []uint64{}, true},
{5, 6, []uint64{}, false},
}

for i, c := range cases {
t.Run(strconv.FormatUint(uint64(i), 10), func(t *testing.T) {
require := require.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockDao := mock.NewMockBlockDAO(ctrl)
checker := NewBlockIndexerChecker(mockDao)
indexer := mock.NewMockBlockIndexer(ctrl)

putBlocks := make([]*block.Block, 0)
mockDao.EXPECT().Height().Return(c.daoHeight, nil).Times(1)
mockDao.EXPECT().GetBlockByHeight(gomock.Any()).DoAndReturn(func(arg0 uint64) (*block.Block, error) {
pb := &iotextypes.BlockHeader{
Core: &iotextypes.BlockHeaderCore{
Height: arg0,
Timestamp: timestamppb.Now(),
},
ProducerPubkey: identityset.PrivateKey(1).PublicKey().Bytes(),
}
blk := &block.Block{}
err := blk.LoadFromBlockHeaderProto(pb)
return blk, err
}).AnyTimes()
mockDao.EXPECT().GetReceipts(gomock.Any()).Return(nil, nil).AnyTimes()
indexer.EXPECT().Height().Return(c.indexerTipHeight, nil).Times(1)
indexer.EXPECT().PutBlock(gomock.Any(), gomock.Any()).DoAndReturn(func(arg0 context.Context, arg1 *block.Block) error {
putBlocks = append(putBlocks, arg1)
return nil
}).AnyTimes()

ctx := protocol.WithBlockchainCtx(context.Background(), protocol.BlockchainCtx{})
ctx = genesis.WithGenesisContext(ctx, genesis.Default)
err := checker.CheckIndexer(ctx, indexer, 0, func(u uint64) {})
require.Equalf(c.noErr, err == nil, "error: %v", err)
require.Len(putBlocks, len(c.expectedPutBlocks))
for k, h := range c.expectedPutBlocks {
require.Equal(h, putBlocks[k].Height())
}
})
}
}

func TestCheckIndexerWithStart(t *testing.T) {

cases := []struct {
daoHeight uint64
indexerTipHeight uint64
indexerStartHeight uint64
expectedPutBlocks []uint64
noErr bool
}{
{5, 0, 3, []uint64{3, 4, 5}, true},
{5, 1, 3, []uint64{3, 4, 5}, true},
{5, 2, 3, []uint64{3, 4, 5}, true},
{5, 3, 3, []uint64{4, 5}, true},
{5, 4, 3, []uint64{5}, true},
{5, 5, 3, []uint64{}, true},
{5, 6, 3, []uint64{}, false},
}

for i, c := range cases {
t.Run(strconv.FormatUint(uint64(i), 10), func(t *testing.T) {
require := require.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockDao := mock.NewMockBlockDAO(ctrl)
checker := NewBlockIndexerChecker(mockDao)
indexer := mock.NewMockBlockIndexerWithStart(ctrl)

putBlocks := make([]*block.Block, 0)
mockDao.EXPECT().Height().Return(c.daoHeight, nil).Times(1)
mockDao.EXPECT().GetBlockByHeight(gomock.Any()).DoAndReturn(func(arg0 uint64) (*block.Block, error) {
pb := &iotextypes.BlockHeader{
Core: &iotextypes.BlockHeaderCore{
Height: arg0,
Timestamp: timestamppb.Now(),
},
ProducerPubkey: identityset.PrivateKey(1).PublicKey().Bytes(),
}
blk := &block.Block{}
err := blk.LoadFromBlockHeaderProto(pb)
return blk, err
}).AnyTimes()
mockDao.EXPECT().GetReceipts(gomock.Any()).Return(nil, nil).AnyTimes()
indexer.EXPECT().Height().Return(c.indexerTipHeight, nil).Times(1)
indexer.EXPECT().StartHeight().Return(c.indexerStartHeight).AnyTimes()
indexer.EXPECT().PutBlock(gomock.Any(), gomock.Any()).DoAndReturn(func(arg0 context.Context, arg1 *block.Block) error {
putBlocks = append(putBlocks, arg1)
return nil
}).AnyTimes()

ctx := protocol.WithBlockchainCtx(context.Background(), protocol.BlockchainCtx{})
ctx = genesis.WithGenesisContext(ctx, genesis.Default)
err := checker.CheckIndexer(ctx, indexer, 0, func(u uint64) {})
require.Equalf(c.noErr, err == nil, "error: %v", err)
require.Len(putBlocks, len(c.expectedPutBlocks))
for k, h := range c.expectedPutBlocks {
require.Equal(h, putBlocks[k].Height())
}
})
}
}
Loading

0 comments on commit 790c4f5

Please sign in to comment.