github.com/klaytn/klaytn@v1.10.2/tests/db_write_and_read_test.go (about)

     1  // Copyright 2019 The klaytn Authors
     2  // This file is part of the klaytn library.
     3  //
     4  // The klaytn library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The klaytn library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package tests
    18  
    19  import (
    20  	"io/ioutil"
    21  	"math/big"
    22  	"os"
    23  	"strconv"
    24  	"testing"
    25  
    26  	"github.com/klaytn/klaytn/blockchain"
    27  	"github.com/klaytn/klaytn/blockchain/types"
    28  	"github.com/klaytn/klaytn/common"
    29  	"github.com/klaytn/klaytn/crypto"
    30  	"github.com/klaytn/klaytn/params"
    31  	"github.com/klaytn/klaytn/storage/database"
    32  	"github.com/stretchr/testify/assert"
    33  )
    34  
    35  type testEntry struct {
    36  	name     string
    37  	dbConfig *database.DBConfig
    38  }
    39  
    40  var testEntries = []testEntry{
    41  	{"BadgerDB-Single", &database.DBConfig{DBType: database.BadgerDB, SingleDB: true}},
    42  	{"BadgerDB", &database.DBConfig{DBType: database.BadgerDB, SingleDB: false, NumStateTrieShards: 4}},
    43  	{"MemoryDB-Single", &database.DBConfig{DBType: database.MemoryDB, SingleDB: true}},
    44  	{"MemoryDB", &database.DBConfig{DBType: database.MemoryDB, SingleDB: false, NumStateTrieShards: 4}},
    45  	{"LevelDB-Single", &database.DBConfig{DBType: database.LevelDB, SingleDB: true, LevelDBCacheSize: 128, OpenFilesLimit: 32}},
    46  	{"LevelDB", &database.DBConfig{DBType: database.LevelDB, SingleDB: false, LevelDBCacheSize: 128, OpenFilesLimit: 32, NumStateTrieShards: 4}},
    47  }
    48  
    49  // TestDBManager_WriteAndRead_Functional checks basic functionality of database.DBManager interface
    50  // with underlying Database with combination of various configurations. Test cases usually include
    51  // 1) read before write, 2) read after write, 3) read after overwrite and 4) read after delete.
    52  // Sometimes 3) and 4) are omitted if such operation is not possible due to some reasons.
    53  func TestDBManager_WriteAndRead_Functional(t *testing.T) {
    54  	for _, entry := range testEntries {
    55  		tempDir, err := ioutil.TempDir("", "klaytn-db-manager-test")
    56  		if err != nil {
    57  			t.Fatalf("cannot create temporary directory: %v", err)
    58  		}
    59  		entry.dbConfig.Dir = tempDir
    60  		dbManager := database.NewDBManager(entry.dbConfig)
    61  
    62  		t.Run(entry.name, func(t *testing.T) {
    63  			testWriteAndReadCanonicalHash(t, dbManager)
    64  			testWriteAndReadHeaderHash(t, dbManager)
    65  			testWriteAndReadBlockHash(t, dbManager)
    66  			testWriteAndReadFastBlockHash(t, dbManager)
    67  			testWriteAndReadFastTrieProgress(t, dbManager)
    68  			testWriteAndReadHeader(t, dbManager)
    69  			testWriteAndReadBody(t, dbManager)
    70  			testWriteAndReadTd(t, dbManager)
    71  			testWriteAndReadReceipts(t, dbManager)
    72  			testWriteAndReadBlock(t, dbManager)
    73  			// TODO-Klaytn-Storage To implement this test case, error shouldn't be returned.
    74  			// testWriteAndReadIstanbulSnapshot(t, dbManager)
    75  		})
    76  
    77  		dbManager.Close()
    78  		os.RemoveAll(tempDir)
    79  	}
    80  }
    81  
    82  func testWriteAndReadCanonicalHash(t *testing.T, dbManager database.DBManager) {
    83  	hash1 := common.HexToHash("111")
    84  	hash2 := common.HexToHash("222")
    85  
    86  	// 1. Before write, empty hash should be returned.
    87  	assert.Equal(t, common.Hash{}, dbManager.ReadCanonicalHash(111))
    88  
    89  	// 2. After write, written hash should be returned.
    90  	dbManager.WriteCanonicalHash(hash1, 111)
    91  	assert.Equal(t, hash1, dbManager.ReadCanonicalHash(111))
    92  
    93  	// 3. After overwrite, overwritten hash should be returned.
    94  	dbManager.WriteCanonicalHash(hash2, 111)
    95  	assert.Equal(t, hash2, dbManager.ReadCanonicalHash(111))
    96  
    97  	// 4. After delete, empty hash should be returned.
    98  	dbManager.DeleteCanonicalHash(111)
    99  	assert.Equal(t, common.Hash{}, dbManager.ReadCanonicalHash(111))
   100  }
   101  
   102  func testWriteAndReadHeaderHash(t *testing.T, dbManager database.DBManager) {
   103  	hash1 := common.HexToHash("111")
   104  	hash2 := common.HexToHash("222")
   105  
   106  	// 1. Before write, empty hash should be returned.
   107  	assert.Equal(t, common.Hash{}, dbManager.ReadHeadHeaderHash())
   108  
   109  	// 2. After write, written hash should be returned.
   110  	dbManager.WriteHeadHeaderHash(hash1)
   111  	assert.Equal(t, hash1, dbManager.ReadHeadHeaderHash())
   112  
   113  	// 3. After overwrite, overwritten hash should be returned.
   114  	dbManager.WriteHeadHeaderHash(hash2)
   115  	assert.Equal(t, hash2, dbManager.ReadHeadHeaderHash())
   116  
   117  	// 4. Delete operation is not supported.
   118  }
   119  
   120  func testWriteAndReadBlockHash(t *testing.T, dbManager database.DBManager) {
   121  	hash1 := common.HexToHash("111")
   122  	hash2 := common.HexToHash("222")
   123  
   124  	// 1. Before write, empty hash should be returned.
   125  	assert.Equal(t, common.Hash{}, dbManager.ReadHeadBlockHash())
   126  
   127  	// 2. After write, written hash should be returned.
   128  	dbManager.WriteHeadBlockHash(hash1)
   129  	assert.Equal(t, hash1, dbManager.ReadHeadBlockHash())
   130  
   131  	// 3. After overwrite, overwritten hash should be returned.
   132  	dbManager.WriteHeadBlockHash(hash2)
   133  	assert.Equal(t, hash2, dbManager.ReadHeadBlockHash())
   134  
   135  	// 4. Delete operation is not supported.
   136  }
   137  
   138  func testWriteAndReadFastBlockHash(t *testing.T, dbManager database.DBManager) {
   139  	hash1 := common.HexToHash("111")
   140  	hash2 := common.HexToHash("222")
   141  
   142  	// 1. Before write, empty hash should be returned.
   143  	assert.Equal(t, common.Hash{}, dbManager.ReadHeadFastBlockHash())
   144  
   145  	// 2. After write, written hash should be returned.
   146  	dbManager.WriteHeadFastBlockHash(hash1)
   147  	assert.Equal(t, hash1, dbManager.ReadHeadFastBlockHash())
   148  
   149  	// 3. After overwrite, overwritten hash should be returned.
   150  	dbManager.WriteHeadFastBlockHash(hash2)
   151  	assert.Equal(t, hash2, dbManager.ReadHeadFastBlockHash())
   152  
   153  	// 4. Delete operation is not supported.
   154  }
   155  
   156  func testWriteAndReadFastTrieProgress(t *testing.T, dbManager database.DBManager) {
   157  	// 1. Before write, empty hash should be returned.
   158  	assert.Equal(t, uint64(0), dbManager.ReadFastTrieProgress())
   159  
   160  	// 2. After write, written hash should be returned.
   161  	dbManager.WriteFastTrieProgress(111)
   162  	assert.Equal(t, uint64(111), dbManager.ReadFastTrieProgress())
   163  
   164  	// 3. After overwrite, overwritten hash should be returned.
   165  	dbManager.WriteFastTrieProgress(222)
   166  	assert.Equal(t, uint64(222), dbManager.ReadFastTrieProgress())
   167  
   168  	// 4. Delete operation is not supported.
   169  }
   170  
   171  func testWriteAndReadHeader(t *testing.T, dbManager database.DBManager) {
   172  	hash, blockNum, header := generateHeaderWithBlockNum(111)
   173  
   174  	// 1. Before write, nil header should be returned.
   175  	assert.Equal(t, (*types.Header)(nil), dbManager.ReadHeader(hash, blockNum))
   176  
   177  	// 2. After write, written header should be returned.
   178  	dbManager.WriteHeader(header)
   179  	assert.Equal(t, header, dbManager.ReadHeader(hash, blockNum))
   180  
   181  	// 3. Overwriting header with identical hash and blockNumber is not possible.
   182  
   183  	// 4. After delete, deleted header should not be returned.
   184  	dbManager.DeleteHeader(hash, blockNum)
   185  	assert.Equal(t, (*types.Header)(nil), dbManager.ReadHeader(hash, blockNum))
   186  }
   187  
   188  func testWriteAndReadBody(t *testing.T, dbManager database.DBManager) {
   189  	body := &types.Body{}
   190  
   191  	tx := generateTx(t)
   192  	txs := types.Transactions{tx}
   193  	body.Transactions = txs
   194  
   195  	hash, blockNum, _ := generateHeaderWithBlockNum(111)
   196  
   197  	// 1. Before write, nil should be returned.
   198  	assert.Equal(t, (*types.Body)(nil), dbManager.ReadBody(hash, blockNum))
   199  
   200  	// 2. After write, written body should be returned.
   201  	dbManager.WriteBody(hash, blockNum, body)
   202  	assert.Equal(t, body.Transactions[0].Hash(), dbManager.ReadBody(hash, blockNum).Transactions[0].Hash())
   203  
   204  	// 3. Overwriting body with identical hash and blockNumber is not possible.
   205  
   206  	// 4. After delete, nil should be returned.
   207  	dbManager.DeleteBody(hash, blockNum)
   208  	assert.Equal(t, (*types.Body)(nil), dbManager.ReadBody(hash, blockNum))
   209  }
   210  
   211  func testWriteAndReadTd(t *testing.T, dbManager database.DBManager) {
   212  	hash1 := common.HexToHash("111")
   213  	blockNumber1 := uint64(111)
   214  
   215  	td1 := new(big.Int).SetUint64(111)
   216  	td2 := new(big.Int).SetUint64(222)
   217  
   218  	// 1. Before write, empty td should be returned.
   219  	assert.Equal(t, (*big.Int)(nil), dbManager.ReadTd(hash1, blockNumber1))
   220  
   221  	// 2. After write, written td should be returned.
   222  	dbManager.WriteTd(hash1, blockNumber1, td1)
   223  	assert.Equal(t, td1, dbManager.ReadTd(hash1, blockNumber1))
   224  
   225  	// 3. After overwrite, overwritten td should be returned.
   226  	dbManager.WriteTd(hash1, blockNumber1, td2)
   227  	assert.Equal(t, td2, dbManager.ReadTd(hash1, blockNumber1))
   228  
   229  	// 4. After delete, deleted td should not be returned.
   230  	dbManager.DeleteTd(hash1, blockNumber1)
   231  	assert.Equal(t, (*big.Int)(nil), dbManager.ReadTd(hash1, blockNumber1))
   232  }
   233  
   234  func testWriteAndReadReceipts(t *testing.T, dbManager database.DBManager) {
   235  	receipts := types.Receipts{
   236  		generateReceipt(111),
   237  		generateReceipt(222),
   238  		generateReceipt(333),
   239  	}
   240  
   241  	hash := common.HexToHash("111")
   242  	blockNumber := uint64(111)
   243  
   244  	// 1. Before write, nil should be returned.
   245  	assert.Equal(t, (types.Receipts)(nil), dbManager.ReadReceipts(hash, blockNumber))
   246  
   247  	// 2. After write, written receipts should be returned.
   248  	dbManager.WriteReceipts(hash, blockNumber, receipts)
   249  	receiptsFromDB := dbManager.ReadReceipts(hash, blockNumber)
   250  	assert.Equal(t, len(receipts), len(receiptsFromDB))
   251  	assert.Equal(t, receipts, receiptsFromDB)
   252  
   253  	// 3. After overwrite, overwritten receipts should be returned.
   254  	receipts2 := types.Receipts{
   255  		generateReceipt(444),
   256  		generateReceipt(555),
   257  		generateReceipt(666),
   258  	}
   259  	dbManager.WriteReceipts(hash, blockNumber, receipts2)
   260  	receiptsFromDB = dbManager.ReadReceipts(hash, blockNumber)
   261  	assert.Equal(t, receipts2, receiptsFromDB)
   262  	assert.NotEqual(t, receipts, receiptsFromDB)
   263  
   264  	// 4. After delete, nil should be returned.
   265  	dbManager.DeleteReceipts(hash, blockNumber)
   266  	assert.Equal(t, (types.Receipts)(nil), dbManager.ReadReceipts(hash, blockNumber))
   267  }
   268  
   269  func testWriteAndReadBlock(t *testing.T, dbManager database.DBManager) {
   270  	hash, blockNum, header := generateHeaderWithBlockNum(111)
   271  	body := generateBody(t)
   272  	receipts := types.Receipts{generateReceipt(111)}
   273  
   274  	blockchain.InitDeriveSha(&params.ChainConfig{
   275  		DeriveShaImpl: types.ImplDeriveShaOriginal,
   276  	})
   277  	block := types.NewBlock(header, body.Transactions, receipts)
   278  
   279  	// 1. Before write, nil should be returned.
   280  	assert.Equal(t, (*types.Block)(nil), dbManager.ReadBlock(hash, blockNum))
   281  
   282  	// 2. After write, written block should be returned.
   283  	dbManager.WriteBlock(block)
   284  	blockFromDB := dbManager.ReadBlock(block.Hash(), block.NumberU64())
   285  	assert.Equal(t, block.Header(), blockFromDB.Header())
   286  	assert.Equal(t, block.Transactions()[0].Hash(), blockFromDB.Transactions()[0].Hash())
   287  
   288  	// 3. Overwriting block with exact hash and blockNumber is not possible.
   289  
   290  	// 4. After delete, nil should be returned.
   291  	dbManager.DeleteBlock(block.Hash(), block.NumberU64())
   292  	assert.Equal(t, (*types.Block)(nil), dbManager.ReadBlock(hash, blockNum))
   293  }
   294  
   295  func testWriteAndReadIstanbulSnapshot(t *testing.T, dbManager database.DBManager) {
   296  	// TODO-Klaytn-Storage To implement this test case, error shouldn't be returned.
   297  }
   298  
   299  func generateHeaderWithBlockNum(blockNum int) (common.Hash, uint64, *types.Header) {
   300  	parentHash := common.HexToHash(strconv.Itoa(blockNum))
   301  	blockNumber := new(big.Int).SetUint64(uint64(blockNum))
   302  
   303  	header := &types.Header{
   304  		ParentHash: parentHash,
   305  		Number:     blockNumber,
   306  		BlockScore: blockNumber,
   307  		Time:       blockNumber,
   308  		Extra:      []byte{'a', 'b', 'c'},
   309  		Governance: common.Hex2Bytes("b8dc7b22676f7665726e696e676e6f6465223a22307865373333636234643237396461363936663330643437306638633034646563623534666362306432222c22676f7665726e616e63656d6f6465223a2273696e676c65222c22726577617264223a7b226d696e74696e67616d6f756e74223a393630303030303030303030303030303030302c22726174696f223a2233342f33332f3333227d2c22626674223a7b2265706f6368223a33303030302c22706f6c696379223a302c22737562223a32317d2c22756e69745072696365223a32353030303030303030307d"),
   310  		Vote:       common.Hex2Bytes("e194e733cb4d279da696f30d470f8c04decb54fcb0d28565706f6368853330303030"),
   311  	}
   312  	return header.Hash(), uint64(blockNum), header
   313  }
   314  
   315  func generateBody(t *testing.T) *types.Body {
   316  	body := &types.Body{}
   317  
   318  	tx := generateTx(t)
   319  	txs := types.Transactions{tx}
   320  	body.Transactions = txs
   321  
   322  	return body
   323  }
   324  
   325  func generateTx(t *testing.T) *types.Transaction {
   326  	key, _ := crypto.GenerateKey()
   327  	addr := crypto.PubkeyToAddress(key.PublicKey)
   328  
   329  	signer := types.LatestSignerForChainID(big.NewInt(18))
   330  	tx1, err := types.SignTx(types.NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil), signer, key)
   331  	if err != nil {
   332  		t.Fatal(err)
   333  	}
   334  	return tx1
   335  }
   336  
   337  func generateReceipt(gasUsed int) *types.Receipt {
   338  	log := &types.Log{Topics: []common.Hash{}, Data: []uint8{}, BlockNumber: uint64(gasUsed)}
   339  	log.Topics = append(log.Topics, common.HexToHash(strconv.Itoa(gasUsed)))
   340  	return &types.Receipt{
   341  		TxHash:  common.HexToHash(strconv.Itoa(gasUsed)),
   342  		GasUsed: uint64(gasUsed),
   343  		Status:  types.ReceiptStatusSuccessful,
   344  		Logs:    []*types.Log{log},
   345  	}
   346  }