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