github.com/iotexproject/iotex-core@v1.14.1-rc1/blockindex/indexbuilder_test.go (about)

     1  package blockindex
     2  
     3  import (
     4  	"context"
     5  	"math/big"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/iotexproject/go-pkgs/hash"
    10  	"github.com/pkg/errors"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/iotexproject/iotex-core/action"
    14  	"github.com/iotexproject/iotex-core/action/protocol"
    15  	"github.com/iotexproject/iotex-core/blockchain"
    16  	"github.com/iotexproject/iotex-core/blockchain/block"
    17  	"github.com/iotexproject/iotex-core/blockchain/blockdao"
    18  	"github.com/iotexproject/iotex-core/blockchain/filedao"
    19  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    20  	"github.com/iotexproject/iotex-core/db"
    21  	"github.com/iotexproject/iotex-core/test/identityset"
    22  	"github.com/iotexproject/iotex-core/testutil"
    23  )
    24  
    25  func TestIndexBuilder(t *testing.T) {
    26  	require := require.New(t)
    27  
    28  	blks := getTestBlocks(t)
    29  
    30  	t1Hash, _ := blks[0].Actions[0].Hash()
    31  	t4Hash, _ := blks[0].Actions[1].Hash()
    32  	e1Hash, _ := blks[0].Actions[2].Hash()
    33  	t2Hash, _ := blks[1].Actions[0].Hash()
    34  	t5Hash, _ := blks[1].Actions[1].Hash()
    35  	e2Hash, _ := blks[1].Actions[2].Hash()
    36  	t3Hash, _ := blks[2].Actions[0].Hash()
    37  	t6Hash, _ := blks[2].Actions[1].Hash()
    38  	e3Hash, _ := blks[2].Actions[2].Hash()
    39  
    40  	type index struct {
    41  		addr   hash.Hash160
    42  		hashes [][]byte
    43  	}
    44  
    45  	indexTests := struct {
    46  		total     uint64
    47  		hashTotal [][]byte
    48  		actions   [4]index
    49  	}{
    50  		9,
    51  		[][]byte{t1Hash[:], t4Hash[:], e1Hash[:], t2Hash[:], t5Hash[:], e2Hash[:], t3Hash[:], t6Hash[:], e3Hash[:]},
    52  		[4]index{
    53  			{hash.BytesToHash160(identityset.Address(28).Bytes()),
    54  				[][]byte{t1Hash[:], t4Hash[:], e1Hash[:], t6Hash[:]}},
    55  			{hash.BytesToHash160(identityset.Address(29).Bytes()),
    56  				[][]byte{t4Hash[:], t2Hash[:], t5Hash[:], e2Hash[:]}},
    57  			{hash.BytesToHash160(identityset.Address(30).Bytes()),
    58  				[][]byte{t5Hash[:], t3Hash[:], t6Hash[:], e3Hash[:]}},
    59  			{hash.BytesToHash160(identityset.Address(31).Bytes()),
    60  				[][]byte{e1Hash[:], e2Hash[:], e3Hash[:]}},
    61  		},
    62  	}
    63  
    64  	testIndexer := func(dao blockdao.BlockDAO, indexer Indexer, t *testing.T) {
    65  		ctx := protocol.WithBlockchainCtx(
    66  			genesis.WithGenesisContext(context.Background(), genesis.Default),
    67  			protocol.BlockchainCtx{
    68  				ChainID: blockchain.DefaultConfig.ID,
    69  			})
    70  		require.NoError(dao.Start(ctx))
    71  		require.NoError(indexer.Start(ctx))
    72  		ib := &IndexBuilder{
    73  			dao:     dao,
    74  			indexer: indexer,
    75  			genesis: genesis.Default,
    76  		}
    77  		defer func() {
    78  			require.NoError(ib.Stop(ctx))
    79  			require.NoError(dao.Stop(ctx))
    80  		}()
    81  
    82  		// put 2 blocks first
    83  		require.NoError(dao.PutBlock(ctx, blks[0]))
    84  		require.NoError(dao.PutBlock(ctx, blks[1]))
    85  		startHeight, err := ib.indexer.Height()
    86  		require.NoError(err)
    87  		require.EqualValues(0, startHeight)
    88  		tipHeight, err := dao.Height()
    89  		require.NoError(err)
    90  		require.EqualValues(2, tipHeight)
    91  
    92  		// init() should build index for first 2 blocks
    93  		require.NoError(ib.init(ctx))
    94  		height, err := ib.indexer.Height()
    95  		require.NoError(err)
    96  		require.EqualValues(2, height)
    97  
    98  		// test handle 1 new block
    99  		require.NoError(dao.PutBlock(ctx, blks[2]))
   100  		ib.ReceiveBlock(blks[2])
   101  		time.Sleep(500 * time.Millisecond)
   102  
   103  		height, err = ib.indexer.Height()
   104  		require.NoError(err)
   105  		require.EqualValues(3, height)
   106  
   107  		// Test GetActionIndex
   108  		actIndex, err := indexer.GetActionIndex(t1Hash[:])
   109  		require.NoError(err)
   110  		require.Equal(blks[0].Height(), actIndex.BlockHeight())
   111  		actIndex, err = indexer.GetActionIndex(t2Hash[:])
   112  		require.NoError(err)
   113  		require.Equal(blks[1].Height(), actIndex.BlockHeight())
   114  		actIndex, err = indexer.GetActionIndex(t3Hash[:])
   115  		require.NoError(err)
   116  		require.Equal(blks[2].Height(), actIndex.BlockHeight())
   117  
   118  		// Test get actions
   119  		total, err := indexer.GetTotalActions()
   120  		require.NoError(err)
   121  		require.EqualValues(indexTests.total, total)
   122  		_, err = indexer.GetActionHashFromIndex(1, total)
   123  		require.Equal(db.ErrInvalid, errors.Cause(err))
   124  		actions, err := indexer.GetActionHashFromIndex(0, total)
   125  		require.NoError(err)
   126  		require.Equal(actions, indexTests.hashTotal)
   127  		for j := range indexTests.actions {
   128  			actionCount, err := indexer.GetActionCountByAddress(indexTests.actions[j].addr)
   129  			require.NoError(err)
   130  			require.EqualValues(len(indexTests.actions[j].hashes), actionCount)
   131  			if actionCount > 0 {
   132  				actions, err := indexer.GetActionsByAddress(indexTests.actions[j].addr, 0, actionCount)
   133  				require.NoError(err)
   134  				require.Equal(actions, indexTests.actions[j].hashes)
   135  			}
   136  		}
   137  
   138  		for i := 0; i < 3; i++ {
   139  			amount := big.NewInt(0)
   140  			tsfs, _ := classifyActions(blks[i].Actions)
   141  			for _, tsf := range tsfs {
   142  				amount.Add(amount, tsf.Amount())
   143  			}
   144  
   145  			// test getNumActions/getTranferAmount
   146  			index, err := indexer.GetBlockIndex(blks[i].Height())
   147  			require.NoError(err)
   148  			require.Equal(blks[i].HashBlock(), hash.BytesToHash256(index.Hash()))
   149  			require.EqualValues(len(blks[i].Actions), index.NumAction())
   150  			require.Equal(amount, index.TsfAmount())
   151  		}
   152  	}
   153  
   154  	path := "test-indexer"
   155  	testPath, err := testutil.PathOfTempFile(path)
   156  	require.NoError(err)
   157  	indexPath, err := testutil.PathOfTempFile(path)
   158  	require.NoError(err)
   159  	defer func() {
   160  		testutil.CleanupPath(testPath)
   161  		testutil.CleanupPath(indexPath)
   162  	}()
   163  	cfg := db.DefaultConfig
   164  	cfg.DbPath = testPath
   165  	filestore, err := filedao.NewFileDAO(cfg, block.NewDeserializer(blockchain.DefaultConfig.EVMNetworkID))
   166  	require.NoError(err)
   167  	memstore, err := filedao.NewFileDAOInMemForTest()
   168  	require.NoError(err)
   169  	for _, v := range []struct {
   170  		dao   blockdao.BlockDAO
   171  		inMem bool
   172  	}{
   173  		{
   174  			memstore, true,
   175  		},
   176  		{
   177  			filestore, false,
   178  		},
   179  	} {
   180  		t.Run("test indexbuilder", func(t *testing.T) {
   181  			var (
   182  				indexer Indexer
   183  				err     error
   184  			)
   185  			if v.inMem {
   186  				indexer, err = NewIndexer(db.NewMemKVStore(), hash.ZeroHash256)
   187  			} else {
   188  				cfg.DbPath = indexPath
   189  				indexer, err = NewIndexer(db.NewBoltDB(cfg), hash.ZeroHash256)
   190  			}
   191  			require.NoError(err)
   192  			testIndexer(v.dao, indexer, t)
   193  		})
   194  	}
   195  }
   196  
   197  // classifyActions classfies actions
   198  func classifyActions(actions []*action.SealedEnvelope) ([]*action.Transfer, []*action.Execution) {
   199  	tsfs := make([]*action.Transfer, 0)
   200  	exes := make([]*action.Execution, 0)
   201  	for _, elp := range actions {
   202  		act := elp.Action()
   203  		switch act := act.(type) {
   204  		case *action.Transfer:
   205  			tsfs = append(tsfs, act)
   206  		case *action.Execution:
   207  			exes = append(exes, act)
   208  		}
   209  	}
   210  	return tsfs, exes
   211  }