database_util_test.go 18.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package core

import (
20
	"bytes"
21
	"io/ioutil"
22 23 24 25 26
	"math/big"
	"os"
	"testing"

	"github.com/ethereum/go-ethereum/common"
27
	"github.com/ethereum/go-ethereum/core/types"
28
	"github.com/ethereum/go-ethereum/crypto"
29 30
	"github.com/ethereum/go-ethereum/crypto/sha3"
	"github.com/ethereum/go-ethereum/ethdb"
31
	"github.com/ethereum/go-ethereum/params"
32
	"github.com/ethereum/go-ethereum/rlp"
33 34
)

35 36 37 38 39
// Tests block header storage and retrieval operations.
func TestHeaderStorage(t *testing.T) {
	db, _ := ethdb.NewMemDatabase()

	// Create a test header to move around the database and make sure it's really new
40 41
	header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header")}
	if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
42 43 44 45 46 47
		t.Fatalf("Non existent header returned: %v", entry)
	}
	// Write and verify the header in the database
	if err := WriteHeader(db, header); err != nil {
		t.Fatalf("Failed to write header into database: %v", err)
	}
48
	if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry == nil {
49 50 51 52
		t.Fatalf("Stored header not found")
	} else if entry.Hash() != header.Hash() {
		t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, header)
	}
53
	if entry := GetHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil {
54 55 56 57 58 59 60 61 62 63
		t.Fatalf("Stored header RLP not found")
	} else {
		hasher := sha3.NewKeccak256()
		hasher.Write(entry)

		if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() {
			t.Fatalf("Retrieved RLP header mismatch: have %v, want %v", entry, header)
		}
	}
	// Delete the header and verify the execution
64 65
	DeleteHeader(db, header.Hash(), header.Number.Uint64())
	if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
		t.Fatalf("Deleted header returned: %v", entry)
	}
}

// Tests block body storage and retrieval operations.
func TestBodyStorage(t *testing.T) {
	db, _ := ethdb.NewMemDatabase()

	// Create a test body to move around the database and make sure it's really new
	body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}

	hasher := sha3.NewKeccak256()
	rlp.Encode(hasher, body)
	hash := common.BytesToHash(hasher.Sum(nil))

81
	if entry := GetBody(db, hash, 0); entry != nil {
82 83 84
		t.Fatalf("Non existent body returned: %v", entry)
	}
	// Write and verify the body in the database
85
	if err := WriteBody(db, hash, 0, body); err != nil {
86 87
		t.Fatalf("Failed to write body into database: %v", err)
	}
88
	if entry := GetBody(db, hash, 0); entry == nil {
89 90 91 92
		t.Fatalf("Stored body not found")
	} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(types.Transactions(body.Transactions)) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) {
		t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body)
	}
93
	if entry := GetBodyRLP(db, hash, 0); entry == nil {
94 95 96 97 98 99 100 101 102 103
		t.Fatalf("Stored body RLP not found")
	} else {
		hasher := sha3.NewKeccak256()
		hasher.Write(entry)

		if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash {
			t.Fatalf("Retrieved RLP body mismatch: have %v, want %v", entry, body)
		}
	}
	// Delete the body and verify the execution
104 105
	DeleteBody(db, hash, 0)
	if entry := GetBody(db, hash, 0); entry != nil {
106 107 108 109 110 111 112 113 114
		t.Fatalf("Deleted body returned: %v", entry)
	}
}

// Tests block storage and retrieval operations.
func TestBlockStorage(t *testing.T) {
	db, _ := ethdb.NewMemDatabase()

	// Create a test block to move around the database and make sure it's really new
115 116 117 118 119 120
	block := types.NewBlockWithHeader(&types.Header{
		Extra:       []byte("test block"),
		UncleHash:   types.EmptyUncleHash,
		TxHash:      types.EmptyRootHash,
		ReceiptHash: types.EmptyRootHash,
	})
121
	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
122 123
		t.Fatalf("Non existent block returned: %v", entry)
	}
124
	if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry != nil {
125 126
		t.Fatalf("Non existent header returned: %v", entry)
	}
127
	if entry := GetBody(db, block.Hash(), block.NumberU64()); entry != nil {
128 129 130 131 132 133
		t.Fatalf("Non existent body returned: %v", entry)
	}
	// Write and verify the block in the database
	if err := WriteBlock(db, block); err != nil {
		t.Fatalf("Failed to write block into database: %v", err)
	}
134
	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry == nil {
135 136 137 138
		t.Fatalf("Stored block not found")
	} else if entry.Hash() != block.Hash() {
		t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
	}
139
	if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry == nil {
140 141 142 143
		t.Fatalf("Stored header not found")
	} else if entry.Hash() != block.Header().Hash() {
		t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header())
	}
144
	if entry := GetBody(db, block.Hash(), block.NumberU64()); entry == nil {
145 146
		t.Fatalf("Stored body not found")
	} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(block.Transactions()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) {
147
		t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body())
148 149
	}
	// Delete the block and verify the execution
150 151
	DeleteBlock(db, block.Hash(), block.NumberU64())
	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
152 153
		t.Fatalf("Deleted block returned: %v", entry)
	}
154
	if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry != nil {
155 156
		t.Fatalf("Deleted header returned: %v", entry)
	}
157
	if entry := GetBody(db, block.Hash(), block.NumberU64()); entry != nil {
158 159 160 161 162 163 164
		t.Fatalf("Deleted body returned: %v", entry)
	}
}

// Tests that partial block contents don't get reassembled into full blocks.
func TestPartialBlockStorage(t *testing.T) {
	db, _ := ethdb.NewMemDatabase()
165 166 167 168 169 170
	block := types.NewBlockWithHeader(&types.Header{
		Extra:       []byte("test block"),
		UncleHash:   types.EmptyUncleHash,
		TxHash:      types.EmptyRootHash,
		ReceiptHash: types.EmptyRootHash,
	})
171 172 173 174
	// Store a header and check that it's not recognized as a block
	if err := WriteHeader(db, block.Header()); err != nil {
		t.Fatalf("Failed to write header into database: %v", err)
	}
175
	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
176 177
		t.Fatalf("Non existent block returned: %v", entry)
	}
178
	DeleteHeader(db, block.Hash(), block.NumberU64())
179 180

	// Store a body and check that it's not recognized as a block
181
	if err := WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil {
182 183
		t.Fatalf("Failed to write body into database: %v", err)
	}
184
	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
185 186
		t.Fatalf("Non existent block returned: %v", entry)
	}
187
	DeleteBody(db, block.Hash(), block.NumberU64())
188 189 190 191 192

	// Store a header and a body separately and check reassembly
	if err := WriteHeader(db, block.Header()); err != nil {
		t.Fatalf("Failed to write header into database: %v", err)
	}
193
	if err := WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil {
194 195
		t.Fatalf("Failed to write body into database: %v", err)
	}
196
	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry == nil {
197 198 199 200 201 202 203 204 205 206 207 208
		t.Fatalf("Stored block not found")
	} else if entry.Hash() != block.Hash() {
		t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
	}
}

// Tests block total difficulty storage and retrieval operations.
func TestTdStorage(t *testing.T) {
	db, _ := ethdb.NewMemDatabase()

	// Create a test TD to move around the database and make sure it's really new
	hash, td := common.Hash{}, big.NewInt(314)
209
	if entry := GetTd(db, hash, 0); entry != nil {
210 211 212
		t.Fatalf("Non existent TD returned: %v", entry)
	}
	// Write and verify the TD in the database
213
	if err := WriteTd(db, hash, 0, td); err != nil {
214 215
		t.Fatalf("Failed to write TD into database: %v", err)
	}
216
	if entry := GetTd(db, hash, 0); entry == nil {
217 218 219 220 221
		t.Fatalf("Stored TD not found")
	} else if entry.Cmp(td) != 0 {
		t.Fatalf("Retrieved TD mismatch: have %v, want %v", entry, td)
	}
	// Delete the TD and verify the execution
222 223
	DeleteTd(db, hash, 0)
	if entry := GetTd(db, hash, 0); entry != nil {
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
		t.Fatalf("Deleted TD returned: %v", entry)
	}
}

// Tests that canonical numbers can be mapped to hashes and retrieved.
func TestCanonicalMappingStorage(t *testing.T) {
	db, _ := ethdb.NewMemDatabase()

	// Create a test canonical number and assinged hash to move around
	hash, number := common.Hash{0: 0xff}, uint64(314)
	if entry := GetCanonicalHash(db, number); entry != (common.Hash{}) {
		t.Fatalf("Non existent canonical mapping returned: %v", entry)
	}
	// Write and verify the TD in the database
	if err := WriteCanonicalHash(db, hash, number); err != nil {
		t.Fatalf("Failed to write canonical mapping into database: %v", err)
	}
	if entry := GetCanonicalHash(db, number); entry == (common.Hash{}) {
		t.Fatalf("Stored canonical mapping not found")
	} else if entry != hash {
		t.Fatalf("Retrieved canonical mapping mismatch: have %v, want %v", entry, hash)
	}
	// Delete the TD and verify the execution
	DeleteCanonicalHash(db, number)
	if entry := GetCanonicalHash(db, number); entry != (common.Hash{}) {
		t.Fatalf("Deleted canonical mapping returned: %v", entry)
	}
}

// Tests that head headers and head blocks can be assigned, individually.
func TestHeadStorage(t *testing.T) {
	db, _ := ethdb.NewMemDatabase()

	blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")})
	blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")})
259
	blockFast := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block fast")})
260 261 262 263 264 265 266 267

	// Check that no head entries are in a pristine database
	if entry := GetHeadHeaderHash(db); entry != (common.Hash{}) {
		t.Fatalf("Non head header entry returned: %v", entry)
	}
	if entry := GetHeadBlockHash(db); entry != (common.Hash{}) {
		t.Fatalf("Non head block entry returned: %v", entry)
	}
268 269 270
	if entry := GetHeadFastBlockHash(db); entry != (common.Hash{}) {
		t.Fatalf("Non fast head block entry returned: %v", entry)
	}
271 272 273 274 275 276 277
	// Assign separate entries for the head header and block
	if err := WriteHeadHeaderHash(db, blockHead.Hash()); err != nil {
		t.Fatalf("Failed to write head header hash: %v", err)
	}
	if err := WriteHeadBlockHash(db, blockFull.Hash()); err != nil {
		t.Fatalf("Failed to write head block hash: %v", err)
	}
278 279 280
	if err := WriteHeadFastBlockHash(db, blockFast.Hash()); err != nil {
		t.Fatalf("Failed to write fast head block hash: %v", err)
	}
281 282 283 284 285 286 287
	// Check that both heads are present, and different (i.e. two heads maintained)
	if entry := GetHeadHeaderHash(db); entry != blockHead.Hash() {
		t.Fatalf("Head header hash mismatch: have %v, want %v", entry, blockHead.Hash())
	}
	if entry := GetHeadBlockHash(db); entry != blockFull.Hash() {
		t.Fatalf("Head block hash mismatch: have %v, want %v", entry, blockFull.Hash())
	}
288 289 290
	if entry := GetHeadFastBlockHash(db); entry != blockFast.Hash() {
		t.Fatalf("Fast head block hash mismatch: have %v, want %v", entry, blockFast.Hash())
	}
291
}
292

293 294
// Tests that positional lookup metadata can be stored and retrieved.
func TestLookupStorage(t *testing.T) {
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
	db, _ := ethdb.NewMemDatabase()

	tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), big.NewInt(1111), big.NewInt(11111), []byte{0x11, 0x11, 0x11})
	tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), big.NewInt(2222), big.NewInt(22222), []byte{0x22, 0x22, 0x22})
	tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), big.NewInt(3333), big.NewInt(33333), []byte{0x33, 0x33, 0x33})
	txs := []*types.Transaction{tx1, tx2, tx3}

	block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)

	// Check that no transactions entries are in a pristine database
	for i, tx := range txs {
		if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil {
			t.Fatalf("tx #%d [%x]: non existent transaction returned: %v", i, tx.Hash(), txn)
		}
	}
	// Insert all the transactions into the database, and verify contents
311 312 313 314
	if err := WriteBlock(db, block); err != nil {
		t.Fatalf("failed to write block contents: %v", err)
	}
	if err := WriteTxLookupEntries(db, block); err != nil {
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
		t.Fatalf("failed to write transactions: %v", err)
	}
	for i, tx := range txs {
		if txn, hash, number, index := GetTransaction(db, tx.Hash()); txn == nil {
			t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash())
		} else {
			if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) {
				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)
			}
			if tx.String() != txn.String() {
				t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx)
			}
		}
	}
	// Delete the transactions and check purge
	for i, tx := range txs {
331
		DeleteTxLookupEntry(db, tx.Hash())
332 333 334 335 336 337 338 339 340 341 342 343 344
		if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil {
			t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn)
		}
	}
}

// Tests that receipts associated with a single block can be stored and retrieved.
func TestBlockReceiptStorage(t *testing.T) {
	db, _ := ethdb.NewMemDatabase()

	receipt1 := &types.Receipt{
		PostState:         []byte{0x01},
		CumulativeGasUsed: big.NewInt(1),
F
Felix Lange 已提交
345 346 347
		Logs: []*types.Log{
			{Address: common.BytesToAddress([]byte{0x11})},
			{Address: common.BytesToAddress([]byte{0x01, 0x11})},
348 349 350 351 352 353 354 355
		},
		TxHash:          common.BytesToHash([]byte{0x11, 0x11}),
		ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
		GasUsed:         big.NewInt(111111),
	}
	receipt2 := &types.Receipt{
		PostState:         []byte{0x02},
		CumulativeGasUsed: big.NewInt(2),
F
Felix Lange 已提交
356 357 358
		Logs: []*types.Log{
			{Address: common.BytesToAddress([]byte{0x22})},
			{Address: common.BytesToAddress([]byte{0x02, 0x22})},
359 360 361 362 363 364 365 366 367
		},
		TxHash:          common.BytesToHash([]byte{0x22, 0x22}),
		ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
		GasUsed:         big.NewInt(222222),
	}
	receipts := []*types.Receipt{receipt1, receipt2}

	// Check that no receipt entries are in a pristine database
	hash := common.BytesToHash([]byte{0x03, 0x14})
368
	if rs := GetBlockReceipts(db, hash, 0); len(rs) != 0 {
369 370 371
		t.Fatalf("non existent receipts returned: %v", rs)
	}
	// Insert the receipt slice into the database and check presence
372
	if err := WriteBlockReceipts(db, hash, 0, receipts); err != nil {
373 374
		t.Fatalf("failed to write block receipts: %v", err)
	}
375
	if rs := GetBlockReceipts(db, hash, 0); len(rs) == 0 {
376 377 378 379 380 381
		t.Fatalf("no receipts returned")
	} else {
		for i := 0; i < len(receipts); i++ {
			rlpHave, _ := rlp.EncodeToBytes(rs[i])
			rlpWant, _ := rlp.EncodeToBytes(receipts[i])

382
			if !bytes.Equal(rlpHave, rlpWant) {
383 384 385 386 387
				t.Fatalf("receipt #%d: receipt mismatch: have %v, want %v", i, rs[i], receipts[i])
			}
		}
	}
	// Delete the receipt slice and check purge
388 389
	DeleteBlockReceipts(db, hash, 0)
	if rs := GetBlockReceipts(db, hash, 0); len(rs) != 0 {
390 391 392 393
		t.Fatalf("deleted receipts returned: %v", rs)
	}
}

394 395 396 397
func TestMipmapBloom(t *testing.T) {
	db, _ := ethdb.NewMemDatabase()

	receipt1 := new(types.Receipt)
F
Felix Lange 已提交
398 399 400
	receipt1.Logs = []*types.Log{
		{Address: common.BytesToAddress([]byte("test"))},
		{Address: common.BytesToAddress([]byte("address"))},
401
	}
402
	receipt2 := new(types.Receipt)
F
Felix Lange 已提交
403 404 405
	receipt2.Logs = []*types.Log{
		{Address: common.BytesToAddress([]byte("test"))},
		{Address: common.BytesToAddress([]byte("address1"))},
406
	}
407 408 409 410 411 412 413 414 415 416 417 418 419 420

	WriteMipmapBloom(db, 1, types.Receipts{receipt1})
	WriteMipmapBloom(db, 2, types.Receipts{receipt2})

	for _, level := range MIPMapLevels {
		bloom := GetMipmapBloom(db, 2, level)
		if !bloom.Test(new(big.Int).SetBytes([]byte("address1"))) {
			t.Error("expected test to be included on level:", level)
		}
	}

	// reset
	db, _ = ethdb.NewMemDatabase()
	receipt := new(types.Receipt)
F
Felix Lange 已提交
421 422
	receipt.Logs = []*types.Log{
		{Address: common.BytesToAddress([]byte("test"))},
423
	}
424 425 426
	WriteMipmapBloom(db, 999, types.Receipts{receipt1})

	receipt = new(types.Receipt)
F
Felix Lange 已提交
427 428
	receipt.Logs = []*types.Log{
		{Address: common.BytesToAddress([]byte("test 1"))},
429
	}
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
	WriteMipmapBloom(db, 1000, types.Receipts{receipt})

	bloom := GetMipmapBloom(db, 1000, 1000)
	if bloom.TestBytes([]byte("test")) {
		t.Error("test should not have been included")
	}
}

func TestMipmapChain(t *testing.T) {
	dir, err := ioutil.TempDir("", "mipmap")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)

	var (
446
		db, _   = ethdb.NewLDBDatabase(dir, 0, 0)
447 448 449 450 451 452 453 454
		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
		addr    = crypto.PubkeyToAddress(key1.PublicKey)
		addr2   = common.BytesToAddress([]byte("jeff"))

		hash1 = common.BytesToHash([]byte("topic1"))
	)
	defer db.Close()

455 456 457 458 459
	gspec := &Genesis{
		Config: params.TestChainConfig,
		Alloc:  GenesisAlloc{addr: {Balance: big.NewInt(1000000)}},
	}
	genesis := gspec.MustCommit(db)
460
	chain, receipts := GenerateChain(params.TestChainConfig, genesis, db, 1010, func(i int, gen *BlockGen) {
461 462 463
		var receipts types.Receipts
		switch i {
		case 1:
464
			receipt := types.NewReceipt(nil, false, new(big.Int))
F
Felix Lange 已提交
465
			receipt.Logs = []*types.Log{{Address: addr, Topics: []common.Hash{hash1}}}
466 467 468
			gen.AddUncheckedReceipt(receipt)
			receipts = types.Receipts{receipt}
		case 1000:
469
			receipt := types.NewReceipt(nil, false, new(big.Int))
F
Felix Lange 已提交
470
			receipt.Logs = []*types.Log{{Address: addr2}}
471 472 473 474 475 476 477 478
			gen.AddUncheckedReceipt(receipt)
			receipts = types.Receipts{receipt}

		}

		// store the receipts
		WriteMipmapBloom(db, uint64(i+1), receipts)
	})
479
	for i, block := range chain {
480 481 482 483 484 485 486
		WriteBlock(db, block)
		if err := WriteCanonicalHash(db, block.Hash(), block.NumberU64()); err != nil {
			t.Fatalf("failed to insert block number: %v", err)
		}
		if err := WriteHeadBlockHash(db, block.Hash()); err != nil {
			t.Fatalf("failed to insert block number: %v", err)
		}
487
		if err := WriteBlockReceipts(db, block.Hash(), block.NumberU64(), receipts[i]); err != nil {
488 489 490 491 492 493 494 495 496
			t.Fatal("error writing block receipts:", err)
		}
	}

	bloom := GetMipmapBloom(db, 0, 1000)
	if bloom.TestBytes(addr2[:]) {
		t.Error("address was included in bloom and should not have")
	}
}