github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/core/database_util_test.go (about)

     1  package core
     2  
     3  import (
     4  	"bytes"
     5  	"math/big"
     6  	"testing"
     7  
     8  	"github.com/quickchainproject/quickchain/common"
     9  	"github.com/quickchainproject/quickchain/core/types"
    10  	"github.com/quickchainproject/quickchain/crypto/sha3"
    11  	"github.com/quickchainproject/quickchain/qctdb"
    12  	"github.com/quickchainproject/quickchain/rlp"
    13  )
    14  
    15  // Tests block header storage and retrieval operations.
    16  func TestHeaderStorage(t *testing.T) {
    17  	db, _ := qctdb.NewMemDatabase()
    18  
    19  	// Create a test header to move around the database and make sure it's really new
    20  	dposCtx, _ := types.NewDposContext(db)
    21  	header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header"), DposContext: dposCtx.ToProto()}
    22  	if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
    23  		t.Fatalf("Non existent header returned: %v", entry)
    24  	}
    25  	// Write and verify the header in the database
    26  	if err := WriteHeader(db, header); err != nil {
    27  		t.Fatalf("Failed to write header into database: %v", err)
    28  	}
    29  	if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry == nil {
    30  		t.Fatalf("Stored header not found")
    31  	} else if entry.Hash() != header.Hash() {
    32  		t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, header)
    33  	}
    34  	if entry := GetHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil {
    35  		t.Fatalf("Stored header RLP not found")
    36  	} else {
    37  		hasher := sha3.NewKeccak256()
    38  		hasher.Write(entry)
    39  
    40  		if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() {
    41  			t.Fatalf("Retrieved RLP header mismatch: have %v, want %v", entry, header)
    42  		}
    43  	}
    44  	// Delete the header and verify the execution
    45  	DeleteHeader(db, header.Hash(), header.Number.Uint64())
    46  	if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
    47  		t.Fatalf("Deleted header returned: %v", entry)
    48  	}
    49  }
    50  
    51  // Tests block body storage and retrieval operations.
    52  func TestBodyStorage(t *testing.T) {
    53  	db, _ := qctdb.NewMemDatabase()
    54  
    55  	// Create a test body to move around the database and make sure it's really new
    56  	dposCtx, _ := types.NewDposContext(db)
    57  	body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header"), DposContext: dposCtx.ToProto()}}}
    58  
    59  	hasher := sha3.NewKeccak256()
    60  	rlp.Encode(hasher, body)
    61  	hash := common.BytesToHash(hasher.Sum(nil))
    62  
    63  	if entry := GetBody(db, hash, 0); entry != nil {
    64  		t.Fatalf("Non existent body returned: %v", entry)
    65  	}
    66  	// Write and verify the body in the database
    67  	if err := WriteBody(db, hash, 0, body); err != nil {
    68  		t.Fatalf("Failed to write body into database: %v", err)
    69  	}
    70  	if entry := GetBody(db, hash, 0); entry == nil {
    71  		t.Fatalf("Stored body not found")
    72  	} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(types.Transactions(body.Transactions)) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) {
    73  		t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body)
    74  	}
    75  	if entry := GetBodyRLP(db, hash, 0); entry == nil {
    76  		t.Fatalf("Stored body RLP not found")
    77  	} else {
    78  		hasher := sha3.NewKeccak256()
    79  		hasher.Write(entry)
    80  
    81  		if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash {
    82  			t.Fatalf("Retrieved RLP body mismatch: have %v, want %v", entry, body)
    83  		}
    84  	}
    85  	// Delete the body and verify the execution
    86  	DeleteBody(db, hash, 0)
    87  	if entry := GetBody(db, hash, 0); entry != nil {
    88  		t.Fatalf("Deleted body returned: %v", entry)
    89  	}
    90  }
    91  
    92  // Tests block storage and retrieval operations.
    93  func TestBlockStorage(t *testing.T) {
    94  	db, _ := qctdb.NewMemDatabase()
    95  
    96  	// Create a test block to move around the database and make sure it's really new
    97  	block := types.NewBlockWithHeader(&types.Header{
    98  		Extra:       []byte("test block"),
    99  		UncleHash:   types.EmptyUncleHash,
   100  		TxHash:      types.EmptyRootHash,
   101  		ReceiptHash: types.EmptyRootHash,
   102  	})
   103  	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
   104  		t.Fatalf("Non existent block returned: %v", entry)
   105  	}
   106  	if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry != nil {
   107  		t.Fatalf("Non existent header returned: %v", entry)
   108  	}
   109  	if entry := GetBody(db, block.Hash(), block.NumberU64()); entry != nil {
   110  		t.Fatalf("Non existent body returned: %v", entry)
   111  	}
   112  	// Write and verify the block in the database
   113  	if err := WriteBlock(db, block); err != nil {
   114  		t.Fatalf("Failed to write block into database: %v", err)
   115  	}
   116  	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry == nil {
   117  		t.Fatalf("Stored block not found")
   118  	} else if entry.Hash() != block.Hash() {
   119  		t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
   120  	}
   121  	if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry == nil {
   122  		t.Fatalf("Stored header not found")
   123  	} else if entry.Hash() != block.Header().Hash() {
   124  		t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header())
   125  	}
   126  	if entry := GetBody(db, block.Hash(), block.NumberU64()); entry == nil {
   127  		t.Fatalf("Stored body not found")
   128  	} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(block.Transactions()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) {
   129  		t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body())
   130  	}
   131  	// Delete the block and verify the execution
   132  	DeleteBlock(db, block.Hash(), block.NumberU64())
   133  	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
   134  		t.Fatalf("Deleted block returned: %v", entry)
   135  	}
   136  	if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry != nil {
   137  		t.Fatalf("Deleted header returned: %v", entry)
   138  	}
   139  	if entry := GetBody(db, block.Hash(), block.NumberU64()); entry != nil {
   140  		t.Fatalf("Deleted body returned: %v", entry)
   141  	}
   142  }
   143  
   144  // Tests that partial block contents don't get reassembled into full blocks.
   145  func TestPartialBlockStorage(t *testing.T) {
   146  	db, _ := qctdb.NewMemDatabase()
   147  	block := types.NewBlockWithHeader(&types.Header{
   148  		Extra:       []byte("test block"),
   149  		UncleHash:   types.EmptyUncleHash,
   150  		TxHash:      types.EmptyRootHash,
   151  		ReceiptHash: types.EmptyRootHash,
   152  	})
   153  	// Store a header and check that it's not recognized as a block
   154  	if err := WriteHeader(db, block.Header()); err != nil {
   155  		t.Fatalf("Failed to write header into database: %v", err)
   156  	}
   157  	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
   158  		t.Fatalf("Non existent block returned: %v", entry)
   159  	}
   160  	DeleteHeader(db, block.Hash(), block.NumberU64())
   161  
   162  	// Store a body and check that it's not recognized as a block
   163  	if err := WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil {
   164  		t.Fatalf("Failed to write body into database: %v", err)
   165  	}
   166  	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
   167  		t.Fatalf("Non existent block returned: %v", entry)
   168  	}
   169  	DeleteBody(db, block.Hash(), block.NumberU64())
   170  
   171  	// Store a header and a body separately and check reassembly
   172  	if err := WriteHeader(db, block.Header()); err != nil {
   173  		t.Fatalf("Failed to write header into database: %v", err)
   174  	}
   175  	if err := WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil {
   176  		t.Fatalf("Failed to write body into database: %v", err)
   177  	}
   178  	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry == nil {
   179  		t.Fatalf("Stored block not found")
   180  	} else if entry.Hash() != block.Hash() {
   181  		t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
   182  	}
   183  }
   184  
   185  // Tests block total difficulty storage and retrieval operations.
   186  func TestTdStorage(t *testing.T) {
   187  	db, _ := qctdb.NewMemDatabase()
   188  
   189  	// Create a test TD to move around the database and make sure it's really new
   190  	hash, td := common.Hash{}, big.NewInt(314)
   191  	if entry := GetTd(db, hash, 0); entry != nil {
   192  		t.Fatalf("Non existent TD returned: %v", entry)
   193  	}
   194  	// Write and verify the TD in the database
   195  	if err := WriteTd(db, hash, 0, td); err != nil {
   196  		t.Fatalf("Failed to write TD into database: %v", err)
   197  	}
   198  	if entry := GetTd(db, hash, 0); entry == nil {
   199  		t.Fatalf("Stored TD not found")
   200  	} else if entry.Cmp(td) != 0 {
   201  		t.Fatalf("Retrieved TD mismatch: have %v, want %v", entry, td)
   202  	}
   203  	// Delete the TD and verify the execution
   204  	DeleteTd(db, hash, 0)
   205  	if entry := GetTd(db, hash, 0); entry != nil {
   206  		t.Fatalf("Deleted TD returned: %v", entry)
   207  	}
   208  }
   209  
   210  // Tests that canonical numbers can be mapped to hashes and retrieved.
   211  func TestCanonicalMappingStorage(t *testing.T) {
   212  	db, _ := qctdb.NewMemDatabase()
   213  
   214  	// Create a test canonical number and assinged hash to move around
   215  	hash, number := common.Hash{0: 0xff}, uint64(314)
   216  	if entry := GetCanonicalHash(db, number); entry != (common.Hash{}) {
   217  		t.Fatalf("Non existent canonical mapping returned: %v", entry)
   218  	}
   219  	// Write and verify the TD in the database
   220  	if err := WriteCanonicalHash(db, hash, number); err != nil {
   221  		t.Fatalf("Failed to write canonical mapping into database: %v", err)
   222  	}
   223  	if entry := GetCanonicalHash(db, number); entry == (common.Hash{}) {
   224  		t.Fatalf("Stored canonical mapping not found")
   225  	} else if entry != hash {
   226  		t.Fatalf("Retrieved canonical mapping mismatch: have %v, want %v", entry, hash)
   227  	}
   228  	// Delete the TD and verify the execution
   229  	DeleteCanonicalHash(db, number)
   230  	if entry := GetCanonicalHash(db, number); entry != (common.Hash{}) {
   231  		t.Fatalf("Deleted canonical mapping returned: %v", entry)
   232  	}
   233  }
   234  
   235  // Tests that head headers and head blocks can be assigned, individually.
   236  func TestHeadStorage(t *testing.T) {
   237  	db, _ := qctdb.NewMemDatabase()
   238  
   239  	blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")})
   240  	blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")})
   241  	blockFast := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block fast")})
   242  
   243  	// Check that no head entries are in a pristine database
   244  	if entry := GetHeadHeaderHash(db); entry != (common.Hash{}) {
   245  		t.Fatalf("Non head header entry returned: %v", entry)
   246  	}
   247  	if entry := GetHeadBlockHash(db); entry != (common.Hash{}) {
   248  		t.Fatalf("Non head block entry returned: %v", entry)
   249  	}
   250  	if entry := GetHeadFastBlockHash(db); entry != (common.Hash{}) {
   251  		t.Fatalf("Non fast head block entry returned: %v", entry)
   252  	}
   253  	// Assign separate entries for the head header and block
   254  	if err := WriteHeadHeaderHash(db, blockHead.Hash()); err != nil {
   255  		t.Fatalf("Failed to write head header hash: %v", err)
   256  	}
   257  	if err := WriteHeadBlockHash(db, blockFull.Hash()); err != nil {
   258  		t.Fatalf("Failed to write head block hash: %v", err)
   259  	}
   260  	if err := WriteHeadFastBlockHash(db, blockFast.Hash()); err != nil {
   261  		t.Fatalf("Failed to write fast head block hash: %v", err)
   262  	}
   263  	// Check that both heads are present, and different (i.e. two heads maintained)
   264  	if entry := GetHeadHeaderHash(db); entry != blockHead.Hash() {
   265  		t.Fatalf("Head header hash mismatch: have %v, want %v", entry, blockHead.Hash())
   266  	}
   267  	if entry := GetHeadBlockHash(db); entry != blockFull.Hash() {
   268  		t.Fatalf("Head block hash mismatch: have %v, want %v", entry, blockFull.Hash())
   269  	}
   270  	if entry := GetHeadFastBlockHash(db); entry != blockFast.Hash() {
   271  		t.Fatalf("Fast head block hash mismatch: have %v, want %v", entry, blockFast.Hash())
   272  	}
   273  }
   274  
   275  // Tests that positional lookup metadata can be stored and retrieved.
   276  func TestLookupStorage(t *testing.T) {
   277  	db, _ := qctdb.NewMemDatabase()
   278  
   279  	tx1 := types.NewTransaction(types.Binary, 1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
   280  	tx2 := types.NewTransaction(types.Binary, 2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
   281  	tx3 := types.NewTransaction(types.Binary, 3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
   282  	txs := []*types.Transaction{tx1, tx2, tx3}
   283  
   284  	block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)
   285  
   286  	// Check that no transactions entries are in a pristine database
   287  	for i, tx := range txs {
   288  		if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil {
   289  			t.Fatalf("tx #%d [%x]: non existent transaction returned: %v", i, tx.Hash(), txn)
   290  		}
   291  	}
   292  	// Insert all the transactions into the database, and verify contents
   293  	if err := WriteBlock(db, block); err != nil {
   294  		t.Fatalf("failed to write block contents: %v", err)
   295  	}
   296  	if err := WriteTxLookupEntries(db, block); err != nil {
   297  		t.Fatalf("failed to write transactions: %v", err)
   298  	}
   299  	for i, tx := range txs {
   300  		if txn, hash, number, index := GetTransaction(db, tx.Hash()); txn == nil {
   301  			t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash())
   302  		} else {
   303  			if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) {
   304  				t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i)
   305  			}
   306  			if tx.Hash() != txn.Hash() {
   307  				t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx)
   308  			}
   309  		}
   310  	}
   311  	// Delete the transactions and check purge
   312  	for i, tx := range txs {
   313  		DeleteTxLookupEntry(db, tx.Hash())
   314  		if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil {
   315  			t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn)
   316  		}
   317  	}
   318  }
   319  
   320  // Tests that receipts associated with a single block can be stored and retrieved.
   321  func TestBlockReceiptStorage(t *testing.T) {
   322  	db, _ := qctdb.NewMemDatabase()
   323  
   324  	receipt1 := &types.Receipt{
   325  		Status:            types.ReceiptStatusFailed,
   326  		CumulativeGasUsed: 1,
   327  		Logs: []*types.Log{
   328  			{Address: common.BytesToAddress([]byte{0x11})},
   329  			{Address: common.BytesToAddress([]byte{0x01, 0x11})},
   330  		},
   331  		TxHash:          common.BytesToHash([]byte{0x11, 0x11}),
   332  		ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
   333  		GasUsed:         111111,
   334  	}
   335  	receipt2 := &types.Receipt{
   336  		PostState:         common.Hash{2}.Bytes(),
   337  		CumulativeGasUsed: 2,
   338  		Logs: []*types.Log{
   339  			{Address: common.BytesToAddress([]byte{0x22})},
   340  			{Address: common.BytesToAddress([]byte{0x02, 0x22})},
   341  		},
   342  		TxHash:          common.BytesToHash([]byte{0x22, 0x22}),
   343  		ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
   344  		GasUsed:         222222,
   345  	}
   346  	receipts := []*types.Receipt{receipt1, receipt2}
   347  
   348  	// Check that no receipt entries are in a pristine database
   349  	hash := common.BytesToHash([]byte{0x03, 0x14})
   350  	if rs := GetBlockReceipts(db, hash, 0); len(rs) != 0 {
   351  		t.Fatalf("non existent receipts returned: %v", rs)
   352  	}
   353  	// Insert the receipt slice into the database and check presence
   354  	if err := WriteBlockReceipts(db, hash, 0, receipts); err != nil {
   355  		t.Fatalf("failed to write block receipts: %v", err)
   356  	}
   357  	if rs := GetBlockReceipts(db, hash, 0); len(rs) == 0 {
   358  		t.Fatalf("no receipts returned")
   359  	} else {
   360  		for i := 0; i < len(receipts); i++ {
   361  			rlpHave, _ := rlp.EncodeToBytes(rs[i])
   362  			rlpWant, _ := rlp.EncodeToBytes(receipts[i])
   363  
   364  			if !bytes.Equal(rlpHave, rlpWant) {
   365  				t.Fatalf("receipt #%d: receipt mismatch: have %v, want %v", i, rs[i], receipts[i])
   366  			}
   367  		}
   368  	}
   369  	// Delete the receipt slice and check purge
   370  	DeleteBlockReceipts(db, hash, 0)
   371  	if rs := GetBlockReceipts(db, hash, 0); len(rs) != 0 {
   372  		t.Fatalf("deleted receipts returned: %v", rs)
   373  	}
   374  }