github.com/iotexproject/iotex-core@v1.14.1-rc1/blockchain/filedao/filedao_v2_test.go (about)

     1  // Copyright (c) 2020 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package filedao
     7  
     8  import (
     9  	"context"
    10  	"encoding/hex"
    11  	"math/big"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/pkg/errors"
    16  	"github.com/stretchr/testify/require"
    17  
    18  	"github.com/iotexproject/go-pkgs/hash"
    19  	"github.com/iotexproject/iotex-proto/golang/iotextypes"
    20  
    21  	"github.com/iotexproject/iotex-core/blockchain/block"
    22  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    23  	"github.com/iotexproject/iotex-core/db"
    24  	"github.com/iotexproject/iotex-core/pkg/compress"
    25  	"github.com/iotexproject/iotex-core/testutil"
    26  )
    27  
    28  const (
    29  	_blockStoreBatchSize = 16
    30  )
    31  
    32  func TestNewFileDAOv2(t *testing.T) {
    33  	testNewFd := func(fd *fileDAOv2, t *testing.T) {
    34  		r := require.New(t)
    35  
    36  		ctx := context.Background()
    37  		r.NoError(fd.Start(ctx))
    38  		defer fd.Stop(ctx)
    39  		tip := fd.loadTip().Height
    40  		r.NoError(testCommitBlocks(t, fd, tip+1, tip+3, hash.ZeroHash256))
    41  
    42  		// new file does not use legacy's namespaces
    43  		for _, v := range []string{
    44  			_blockNS,
    45  			_blockHeaderNS,
    46  			_blockBodyNS,
    47  			_blockFooterNS,
    48  			_receiptsNS,
    49  		} {
    50  			_, err := fd.kvStore.Get(v, []byte{})
    51  			r.Error(err)
    52  			r.True(strings.Contains(err.Error(), " = "+hex.EncodeToString([]byte(v))+" doesn't exist"))
    53  		}
    54  
    55  		// test counting index add empty transaction log
    56  		ser := (&block.BlkTransactionLog{}).Serialize()
    57  		r.Equal([]byte{}, ser)
    58  		for _, test := range []struct {
    59  			compress string
    60  			height   uint64
    61  		}{
    62  			{"", 3},
    63  			{compress.Gzip, 4},
    64  			{compress.Snappy, 5},
    65  		} {
    66  			data := ser
    67  			if test.compress != "" {
    68  				var err error
    69  				data, err = compress.Compress(ser, test.compress)
    70  				r.NoError(err)
    71  			}
    72  			r.NoError(addOneEntryToBatch(fd.hashStore, data, fd.batch))
    73  			r.NoError(fd.kvStore.WriteBatch(fd.batch))
    74  			v, err := fd.hashStore.Get(test.height)
    75  			r.NoError(err)
    76  			r.Equal(data, v)
    77  			if test.compress != "" {
    78  				v, err = compress.Decompress(v, test.compress)
    79  			}
    80  			r.NoError(err)
    81  			r.Equal(ser, v)
    82  		}
    83  	}
    84  
    85  	r := require.New(t)
    86  	testPath, err := testutil.PathOfTempFile("test-newfd")
    87  	r.NoError(err)
    88  	defer func() {
    89  		testutil.CleanupPath(testPath)
    90  	}()
    91  
    92  	cfg := db.DefaultConfig
    93  	r.Equal(compress.Snappy, cfg.Compressor)
    94  	r.Equal(16, cfg.BlockStoreBatchSize)
    95  	cfg.DbPath = testPath
    96  	deser := block.NewDeserializer(_defaultEVMNetworkID)
    97  	_, err = newFileDAOv2(0, cfg, deser)
    98  	r.Equal(ErrNotSupported, err)
    99  
   100  	inMemFd, err := newFileDAOv2InMem(1)
   101  	r.NoError(err)
   102  	fd, err := newFileDAOv2(2, cfg, deser)
   103  	r.NoError(err)
   104  
   105  	for _, v2Fd := range []*fileDAOv2{inMemFd, fd} {
   106  		t.Run("test newFileDAOv2", func(t *testing.T) {
   107  			testNewFd(v2Fd, t)
   108  		})
   109  	}
   110  }
   111  
   112  func TestNewFdInterface(t *testing.T) {
   113  	testFdInterface := func(cfg db.Config, start uint64, t *testing.T) {
   114  		r := require.New(t)
   115  
   116  		testutil.CleanupPath(cfg.DbPath)
   117  		deser := block.NewDeserializer(_defaultEVMNetworkID)
   118  		fd, err := newFileDAOv2(start, cfg, deser)
   119  		r.NoError(err)
   120  
   121  		ctx := context.Background()
   122  		r.NoError(fd.Start(ctx))
   123  		defer fd.Stop(ctx)
   124  
   125  		height, err := fd.Bottom()
   126  		r.NoError(err)
   127  		r.Equal(start, height)
   128  		height, err = fd.Height()
   129  		r.NoError(err)
   130  		r.Equal(start-1, height)
   131  
   132  		// cannot commit height != tip+1
   133  		builder := block.NewTestingBuilder()
   134  		h := hash.ZeroHash256
   135  		blk := createTestingBlock(builder, start-1, h)
   136  		r.Equal(ErrInvalidTipHeight, fd.PutBlock(ctx, blk))
   137  		blk = createTestingBlock(builder, start+1, h)
   138  		r.Equal(ErrInvalidTipHeight, fd.PutBlock(ctx, blk))
   139  
   140  		// verify API for genesis block
   141  		h, err = fd.GetBlockHash(0)
   142  		r.NoError(err)
   143  		r.Equal(block.GenesisHash(), h)
   144  		height, err = fd.GetBlockHeight(h)
   145  		r.NoError(err)
   146  		r.Zero(height)
   147  		blk, err = fd.GetBlock(h)
   148  		r.NoError(err)
   149  		r.Equal(block.GenesisBlock(), blk)
   150  
   151  		// commit _blockStoreBatchSize blocks
   152  		for i := uint64(0); i < fd.header.BlockStoreSize; i++ {
   153  			blk = createTestingBlock(builder, start+i, h)
   154  			r.NoError(fd.PutBlock(ctx, blk))
   155  			h = blk.HashBlock()
   156  			height, err = fd.Height()
   157  			r.NoError(err)
   158  			r.Equal(start+i, height)
   159  			if i < fd.header.BlockStoreSize-1 {
   160  				r.EqualValues(0, fd.lowestBlockOfStoreTip())
   161  				r.Equal(start-1, fd.highestBlockOfStoreTip())
   162  			} else {
   163  				r.Equal(start, fd.lowestBlockOfStoreTip())
   164  				r.Equal(start+fd.header.BlockStoreSize-1, fd.highestBlockOfStoreTip())
   165  				r.Equal(start+fd.header.BlockStoreSize-1, height)
   166  			}
   167  		}
   168  
   169  		// commit 3 more blocks
   170  		for i := uint64(1); i <= 3; i++ {
   171  			blk = createTestingBlock(builder, height+i, h)
   172  			r.NoError(fd.PutBlock(ctx, blk))
   173  			h = blk.HashBlock()
   174  			r.Equal(start, fd.lowestBlockOfStoreTip())
   175  			r.Equal(start+fd.header.BlockStoreSize-1, fd.highestBlockOfStoreTip())
   176  		}
   177  		height, err = fd.Height()
   178  		r.NoError(err)
   179  		r.Equal(start+fd.header.BlockStoreSize+2, height)
   180  		r.False(fd.ContainsHeight(start - 1))
   181  		r.False(fd.ContainsHeight(height + 1))
   182  
   183  		// verify API for all blocks
   184  		r.True(fd.ContainsTransactionLog())
   185  		for i := height; i >= start; i-- {
   186  			height, err = fd.Bottom()
   187  			r.NoError(err)
   188  			r.Equal(start, height)
   189  			r.True(fd.ContainsHeight(i))
   190  			height, err = fd.Height()
   191  			r.NoError(err)
   192  			r.Equal(i, height)
   193  			h, err = fd.GetBlockHash(i)
   194  			r.NoError(err)
   195  			height, err = fd.GetBlockHeight(h)
   196  			r.NoError(err)
   197  			r.Equal(height, i)
   198  			blk, err = fd.GetBlockByHeight(i)
   199  			r.NoError(err)
   200  			r.Equal(h, blk.HashBlock())
   201  			receipt, err := fd.GetReceipts(i)
   202  			r.NoError(err)
   203  			r.EqualValues(1, receipt[0].Status)
   204  			r.Equal(height, receipt[0].BlockHeight)
   205  			r.Equal(blk.Header.PrevHash(), receipt[0].ActionHash)
   206  			log, err := fd.TransactionLogs(i)
   207  			r.NoError(err)
   208  			l := log.Logs[0]
   209  			r.Equal(receipt[0].ActionHash[:], l.ActionHash)
   210  			r.EqualValues(1, l.NumTransactions)
   211  			tx := l.Transactions[0]
   212  			r.Equal(big.NewInt(100).String(), tx.Amount)
   213  			r.Equal(hex.EncodeToString(l.ActionHash[:]), tx.Sender)
   214  			r.Equal(hex.EncodeToString(l.ActionHash[:]), tx.Recipient)
   215  			r.Equal(iotextypes.TransactionLogType_NATIVE_TRANSFER, tx.Type)
   216  
   217  			// test DeleteTipBlock()
   218  			r.NoError(fd.DeleteTipBlock())
   219  			r.False(fd.ContainsHeight(i))
   220  			_, err = fd.GetBlockHash(i)
   221  			r.Equal(db.ErrNotExist, err)
   222  			_, err = fd.GetBlockHeight(h)
   223  			r.Equal(db.ErrNotExist, errors.Cause(err))
   224  			_, err = fd.GetBlock(h)
   225  			r.Equal(db.ErrNotExist, errors.Cause(err))
   226  			_, err = fd.GetBlockByHeight(i)
   227  			r.Equal(db.ErrNotExist, errors.Cause(err))
   228  			_, err = fd.GetReceipts(i)
   229  			r.Equal(db.ErrNotExist, errors.Cause(err))
   230  			_, err = fd.TransactionLogs(i)
   231  			r.Equal(ErrNotSupported, err)
   232  		}
   233  
   234  		// after deleting all blocks
   235  		height, err = fd.Height()
   236  		r.NoError(err)
   237  		r.Equal(start-1, height)
   238  		h, err = fd.GetBlockHash(height)
   239  		if height == 0 {
   240  			r.NoError(err)
   241  			r.Equal(block.GenesisHash(), h)
   242  		} else {
   243  			r.Equal(db.ErrNotExist, err)
   244  			r.Equal(hash.ZeroHash256, h)
   245  		}
   246  		r.EqualValues(0, fd.lowestBlockOfStoreTip())
   247  		r.Equal(start-1, fd.highestBlockOfStoreTip())
   248  	}
   249  
   250  	r := require.New(t)
   251  	testPath, err := testutil.PathOfTempFile("test-interface")
   252  	r.NoError(err)
   253  	defer func() {
   254  		testutil.CleanupPath(testPath)
   255  	}()
   256  
   257  	cfg := db.DefaultConfig
   258  	cfg.DbPath = testPath
   259  	deser := block.NewDeserializer(_defaultEVMNetworkID)
   260  	_, err = newFileDAOv2(0, cfg, deser)
   261  	r.Equal(ErrNotSupported, err)
   262  	genesis.SetGenesisTimestamp(genesis.Default.Timestamp)
   263  	block.LoadGenesisHash(&genesis.Default)
   264  
   265  	for _, compress := range []string{"", compress.Snappy} {
   266  		for _, start := range []uint64{1, 5, _blockStoreBatchSize + 1, 4 * _blockStoreBatchSize} {
   267  			cfg.Compressor = compress
   268  			t.Run("test fileDAOv2 interface", func(t *testing.T) {
   269  				testFdInterface(cfg, start, t)
   270  			})
   271  		}
   272  	}
   273  }
   274  
   275  func TestNewFdStart(t *testing.T) {
   276  	testFdStart := func(cfg db.Config, start uint64, t *testing.T) {
   277  		r := require.New(t)
   278  		deser := block.NewDeserializer(_defaultEVMNetworkID)
   279  		for _, num := range []uint64{3, _blockStoreBatchSize - 1, _blockStoreBatchSize, 2*_blockStoreBatchSize - 1} {
   280  			testutil.CleanupPath(cfg.DbPath)
   281  			fd, err := newFileDAOv2(start, cfg, deser)
   282  			r.NoError(err)
   283  			ctx := context.Background()
   284  			r.NoError(fd.Start(ctx))
   285  			defer fd.Stop(ctx)
   286  
   287  			r.NoError(testCommitBlocks(t, fd, start, start+num-1, hash.ZeroHash256))
   288  			height, err := fd.Height()
   289  			r.NoError(err)
   290  			r.Equal(start+num-1, height)
   291  			r.NoError(fd.Stop(ctx))
   292  
   293  			// start from existing file
   294  			fd = openFileDAOv2(cfg, deser)
   295  			r.NoError(fd.Start(ctx))
   296  			height, err = fd.Bottom()
   297  			r.NoError(err)
   298  			r.Equal(start, height)
   299  			height, err = fd.Height()
   300  			r.NoError(err)
   301  			r.Equal(start+num-1, height)
   302  
   303  			// verify API for all blocks
   304  			for i := start; i < start+num; i++ {
   305  				r.True(fd.ContainsHeight(i))
   306  				h, err := fd.GetBlockHash(i)
   307  				r.NoError(err)
   308  				height, err = fd.GetBlockHeight(h)
   309  				r.NoError(err)
   310  				r.Equal(height, i)
   311  				blk, err := fd.GetBlockByHeight(i)
   312  				r.NoError(err)
   313  				r.Equal(h, blk.HashBlock())
   314  				receipt, err := fd.GetReceipts(i)
   315  				r.NoError(err)
   316  				r.EqualValues(1, receipt[0].Status)
   317  				r.Equal(height, receipt[0].BlockHeight)
   318  				r.Equal(blk.Header.PrevHash(), receipt[0].ActionHash)
   319  				log, err := fd.TransactionLogs(i)
   320  				r.NoError(err)
   321  				r.NotNil(log)
   322  				l := log.Logs[0]
   323  				r.Equal(receipt[0].ActionHash[:], l.ActionHash)
   324  				r.EqualValues(1, l.NumTransactions)
   325  				tx := l.Transactions[0]
   326  				r.Equal(big.NewInt(100).String(), tx.Amount)
   327  				r.Equal(hex.EncodeToString(l.ActionHash[:]), tx.Sender)
   328  				r.Equal(hex.EncodeToString(l.ActionHash[:]), tx.Recipient)
   329  				r.Equal(iotextypes.TransactionLogType_NATIVE_TRANSFER, tx.Type)
   330  			}
   331  		}
   332  	}
   333  
   334  	r := require.New(t)
   335  	testPath, err := testutil.PathOfTempFile("test-start")
   336  	r.NoError(err)
   337  	defer func() {
   338  		testutil.CleanupPath(testPath)
   339  	}()
   340  
   341  	cfg := db.DefaultConfig
   342  	cfg.DbPath = testPath
   343  	for _, compress := range []string{"", compress.Gzip} {
   344  		for _, start := range []uint64{1, 5, _blockStoreBatchSize + 1, 4 * _blockStoreBatchSize} {
   345  			cfg.Compressor = compress
   346  			t.Run("test fileDAOv2 start", func(t *testing.T) {
   347  				testFdStart(cfg, start, t)
   348  			})
   349  		}
   350  	}
   351  }