github.com/iotexproject/iotex-core@v1.14.1-rc1/blockchain/filedao/testing.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  	"testing"
    13  
    14  	"github.com/pkg/errors"
    15  	"github.com/stretchr/testify/require"
    16  
    17  	"github.com/iotexproject/go-pkgs/cache"
    18  	"github.com/iotexproject/go-pkgs/hash"
    19  	"github.com/iotexproject/iotex-proto/golang/iotextypes"
    20  
    21  	"github.com/iotexproject/iotex-core/action"
    22  	"github.com/iotexproject/iotex-core/blockchain/block"
    23  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    24  	"github.com/iotexproject/iotex-core/db"
    25  	"github.com/iotexproject/iotex-core/db/batch"
    26  	"github.com/iotexproject/iotex-core/test/identityset"
    27  	"github.com/iotexproject/iotex-core/testutil"
    28  )
    29  
    30  var _defaultEVMNetworkID uint32 = 4689
    31  
    32  type (
    33  	// testInMemFd is an in-memory FileDAO
    34  	testInMemFd struct {
    35  		*fileDAOv2
    36  	}
    37  
    38  	// testFailPutBlock will fail PutBlock() at certain height
    39  	testFailPutBlock struct {
    40  		*fileDAO
    41  		failHeight uint64
    42  	}
    43  )
    44  
    45  func newTestInMemFd() (*testInMemFd, error) {
    46  	v2, err := newFileDAOv2InMem(1)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	return &testInMemFd{fileDAOv2: v2}, nil
    51  }
    52  
    53  // newFileDAOv2InMem creates a in-memory new v2 fileDAO
    54  func newFileDAOv2InMem(bottom uint64) (*fileDAOv2, error) {
    55  	if bottom == 0 {
    56  		return nil, ErrNotSupported
    57  	}
    58  
    59  	fd := fileDAOv2{
    60  		header: &FileHeader{
    61  			Version:        FileV2,
    62  			Compressor:     "",
    63  			BlockStoreSize: 16,
    64  			Start:          bottom,
    65  		},
    66  		tip: &FileTip{
    67  			Height: bottom - 1,
    68  		},
    69  		blkStorePbCache: cache.NewThreadSafeLruCache(16),
    70  		blkCache:        cache.NewThreadSafeLruCache(256),
    71  		receiptCache:    cache.NewThreadSafeLruCache(256),
    72  		kvStore:         db.NewMemKVStore(),
    73  		batch:           batch.NewBatch(),
    74  		deser:           block.NewDeserializer(_defaultEVMNetworkID),
    75  	}
    76  	return &fd, nil
    77  }
    78  
    79  func (fd *testInMemFd) GetBlockHash(height uint64) (hash.Hash256, error) {
    80  	if height == 0 {
    81  		return block.GenesisHash(), nil
    82  	}
    83  	return fd.fileDAOv2.GetBlockHash(height)
    84  }
    85  
    86  func (fd *testInMemFd) GetBlockHeight(h hash.Hash256) (uint64, error) {
    87  	if h == block.GenesisHash() {
    88  		return 0, nil
    89  	}
    90  	return fd.fileDAOv2.GetBlockHeight(h)
    91  }
    92  
    93  func (fd *testInMemFd) GetBlock(h hash.Hash256) (*block.Block, error) {
    94  	if h == block.GenesisHash() {
    95  		return block.GenesisBlock(), nil
    96  	}
    97  	return fd.fileDAOv2.GetBlock(h)
    98  }
    99  
   100  func (fd *testInMemFd) GetBlockByHeight(height uint64) (*block.Block, error) {
   101  	if height == 0 {
   102  		return block.GenesisBlock(), nil
   103  	}
   104  	return fd.fileDAOv2.GetBlockByHeight(height)
   105  }
   106  
   107  func (fd *testInMemFd) Header(h hash.Hash256) (*block.Header, error) {
   108  	blk, err := fd.GetBlock(h)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	return &blk.Header, nil
   113  }
   114  
   115  func (fd *testInMemFd) HeaderByHeight(height uint64) (*block.Header, error) {
   116  	blk, err := fd.GetBlockByHeight(height)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	return &blk.Header, nil
   121  }
   122  
   123  func (fd *testInMemFd) FooterByHeight(height uint64) (*block.Footer, error) {
   124  	blk, err := fd.GetBlockByHeight(height)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	return &blk.Footer, nil
   129  }
   130  
   131  func (fd *testInMemFd) PutBlock(ctx context.Context, blk *block.Block) error {
   132  	// bail out if block already exists
   133  	h := blk.HashBlock()
   134  	if _, err := fd.GetBlockHeight(h); err == nil {
   135  		return ErrAlreadyExist
   136  	}
   137  	return fd.fileDAOv2.PutBlock(ctx, blk)
   138  }
   139  
   140  func newTestFailPutBlock(fd *fileDAO, height uint64) FileDAO {
   141  	return &testFailPutBlock{fileDAO: fd, failHeight: height}
   142  }
   143  
   144  func (tf *testFailPutBlock) PutBlock(ctx context.Context, blk *block.Block) error {
   145  	// bail out if block already exists
   146  	h := blk.HashBlock()
   147  	if _, err := tf.GetBlockHeight(h); err == nil {
   148  		return ErrAlreadyExist
   149  	}
   150  
   151  	// check if we need to split DB
   152  	if tf.cfg.V2BlocksToSplitDB > 0 {
   153  		if err := tf.prepNextDbFile(blk.Height()); err != nil {
   154  			return err
   155  		}
   156  	}
   157  
   158  	if tf.failHeight == blk.Height() {
   159  		return ErrInvalidTipHeight
   160  	}
   161  	return tf.currFd.PutBlock(ctx, blk)
   162  }
   163  
   164  func testCommitBlocks(t *testing.T, fd BaseFileDAO, start, end uint64, h hash.Hash256) error {
   165  	ctx := context.Background()
   166  	builder := block.NewTestingBuilder()
   167  	for i := start; i <= end; i++ {
   168  		blk := createTestingBlock(builder, i, h)
   169  		if err := fd.PutBlock(ctx, blk); err != nil {
   170  			return err
   171  		}
   172  		h = blk.HashBlock()
   173  	}
   174  	return nil
   175  }
   176  
   177  func testVerifyChainDB(t *testing.T, fd FileDAO, start, end uint64) {
   178  	r := require.New(t)
   179  
   180  	height, err := fd.Height()
   181  	r.NoError(err)
   182  	r.Equal(end, height)
   183  	for i := end; i >= start; i-- {
   184  		h, err := fd.GetBlockHash(i)
   185  		r.NoError(err)
   186  		height, err = fd.GetBlockHeight(h)
   187  		r.NoError(err)
   188  		r.Equal(height, i)
   189  		blk, err := fd.GetBlockByHeight(i)
   190  		r.NoError(err)
   191  		r.Equal(h, blk.HashBlock())
   192  		receipt, err := fd.GetReceipts(i)
   193  		r.NoError(err)
   194  		r.EqualValues(1, receipt[0].Status)
   195  		r.Equal(height, receipt[0].BlockHeight)
   196  		r.Equal(blk.Header.PrevHash(), receipt[0].ActionHash)
   197  		log, err := fd.TransactionLogs(i)
   198  		r.NoError(err)
   199  		r.NotNil(log)
   200  		l := log.Logs[0]
   201  		r.Equal(receipt[0].ActionHash[:], l.ActionHash)
   202  		r.EqualValues(1, l.NumTransactions)
   203  		tx := l.Transactions[0]
   204  		r.Equal(big.NewInt(100).String(), tx.Amount)
   205  		r.Equal(hex.EncodeToString(l.ActionHash[:]), tx.Sender)
   206  		r.Equal(hex.EncodeToString(l.ActionHash[:]), tx.Recipient)
   207  		r.Equal(iotextypes.TransactionLogType_NATIVE_TRANSFER, tx.Type)
   208  
   209  		if false {
   210  			// test DeleteTipBlock()
   211  			r.NoError(fd.DeleteTipBlock())
   212  			_, err = fd.GetBlockHash(i)
   213  			r.Equal(db.ErrNotExist, err)
   214  			_, err = fd.GetBlockHeight(h)
   215  			r.Equal(db.ErrNotExist, errors.Cause(err))
   216  			_, err = fd.GetBlock(h)
   217  			r.Equal(db.ErrNotExist, errors.Cause(err))
   218  			_, err = fd.GetBlockByHeight(i)
   219  			r.Equal(db.ErrNotExist, errors.Cause(err))
   220  			_, err = fd.GetReceipts(i)
   221  			r.Equal(db.ErrNotExist, errors.Cause(err))
   222  			_, err = fd.TransactionLogs(i)
   223  			r.Equal(ErrNotSupported, err)
   224  		}
   225  	}
   226  }
   227  
   228  func createTestingBlock(builder *block.TestingBuilder, height uint64, h hash.Hash256) *block.Block {
   229  	block.LoadGenesisHash(&genesis.Default)
   230  	r := &action.Receipt{
   231  		Status:      1,
   232  		BlockHeight: height,
   233  		ActionHash:  h,
   234  	}
   235  	blk, _ := builder.
   236  		SetHeight(height).
   237  		SetPrevBlockHash(h).
   238  		SetReceipts([]*action.Receipt{
   239  			r.AddTransactionLogs(&action.TransactionLog{
   240  				Type:      iotextypes.TransactionLogType_NATIVE_TRANSFER,
   241  				Amount:    big.NewInt(100),
   242  				Sender:    hex.EncodeToString(h[:]),
   243  				Recipient: hex.EncodeToString(h[:]),
   244  			}),
   245  		}).
   246  		SetTimeStamp(testutil.TimestampNow().UTC()).
   247  		SignAndBuild(identityset.PrivateKey(27))
   248  	return &blk
   249  }