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 }