database_util_test.go 15.3 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 22 23 24
	"math/big"
	"testing"

	"github.com/ethereum/go-ethereum/common"
25 26 27 28
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto/sha3"
	"github.com/ethereum/go-ethereum/ethdb"
	"github.com/ethereum/go-ethereum/rlp"
29 30
)

31 32 33 34 35
// 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
36 37
	header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header")}
	if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
38 39 40 41 42 43
		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)
	}
44
	if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry == nil {
45 46 47 48
		t.Fatalf("Stored header not found")
	} else if entry.Hash() != header.Hash() {
		t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, header)
	}
49
	if entry := GetHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil {
50 51 52 53 54 55 56 57 58 59
		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
60 61
	DeleteHeader(db, header.Hash(), header.Number.Uint64())
	if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
		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))

77
	if entry := GetBody(db, hash, 0); entry != nil {
78 79 80
		t.Fatalf("Non existent body returned: %v", entry)
	}
	// Write and verify the body in the database
81
	if err := WriteBody(db, hash, 0, body); err != nil {
82 83
		t.Fatalf("Failed to write body into database: %v", err)
	}
84
	if entry := GetBody(db, hash, 0); entry == nil {
85 86 87 88
		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)
	}
89
	if entry := GetBodyRLP(db, hash, 0); entry == nil {
90 91 92 93 94 95 96 97 98 99
		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
100 101
	DeleteBody(db, hash, 0)
	if entry := GetBody(db, hash, 0); entry != nil {
102 103 104 105 106 107 108 109 110
		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
111 112 113 114 115 116
	block := types.NewBlockWithHeader(&types.Header{
		Extra:       []byte("test block"),
		UncleHash:   types.EmptyUncleHash,
		TxHash:      types.EmptyRootHash,
		ReceiptHash: types.EmptyRootHash,
	})
117
	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
118 119
		t.Fatalf("Non existent block returned: %v", entry)
	}
120
	if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry != nil {
121 122
		t.Fatalf("Non existent header returned: %v", entry)
	}
123
	if entry := GetBody(db, block.Hash(), block.NumberU64()); entry != nil {
124 125 126 127 128 129
		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)
	}
130
	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry == nil {
131 132 133 134
		t.Fatalf("Stored block not found")
	} else if entry.Hash() != block.Hash() {
		t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
	}
135
	if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry == nil {
136 137 138 139
		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())
	}
140
	if entry := GetBody(db, block.Hash(), block.NumberU64()); entry == nil {
141 142
		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()) {
143
		t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body())
144 145
	}
	// Delete the block and verify the execution
146 147
	DeleteBlock(db, block.Hash(), block.NumberU64())
	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
148 149
		t.Fatalf("Deleted block returned: %v", entry)
	}
150
	if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry != nil {
151 152
		t.Fatalf("Deleted header returned: %v", entry)
	}
153
	if entry := GetBody(db, block.Hash(), block.NumberU64()); entry != nil {
154 155 156 157 158 159 160
		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()
161 162 163 164 165 166
	block := types.NewBlockWithHeader(&types.Header{
		Extra:       []byte("test block"),
		UncleHash:   types.EmptyUncleHash,
		TxHash:      types.EmptyRootHash,
		ReceiptHash: types.EmptyRootHash,
	})
167 168 169 170
	// 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)
	}
171
	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
172 173
		t.Fatalf("Non existent block returned: %v", entry)
	}
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 179
		t.Fatalf("Failed to write body into database: %v", err)
	}
180
	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
181 182
		t.Fatalf("Non existent block returned: %v", entry)
	}
183
	DeleteBody(db, block.Hash(), block.NumberU64())
184 185 186 187 188

	// 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)
	}
189
	if err := WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil {
190 191
		t.Fatalf("Failed to write body into database: %v", err)
	}
192
	if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry == nil {
193 194 195 196 197 198 199 200 201 202 203 204
		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)
205
	if entry := GetTd(db, hash, 0); entry != nil {
206 207 208
		t.Fatalf("Non existent TD returned: %v", entry)
	}
	// Write and verify the TD in the database
209
	if err := WriteTd(db, hash, 0, td); err != nil {
210 211
		t.Fatalf("Failed to write TD into database: %v", err)
	}
212
	if entry := GetTd(db, hash, 0); entry == nil {
213 214 215 216 217
		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
218 219
	DeleteTd(db, hash, 0)
	if entry := GetTd(db, hash, 0); entry != nil {
220 221 222 223 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
		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")})
255
	blockFast := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block fast")})
256 257 258 259 260 261 262 263

	// 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)
	}
264 265 266
	if entry := GetHeadFastBlockHash(db); entry != (common.Hash{}) {
		t.Fatalf("Non fast head block entry returned: %v", entry)
	}
267 268 269 270 271 272 273
	// 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)
	}
274 275 276
	if err := WriteHeadFastBlockHash(db, blockFast.Hash()); err != nil {
		t.Fatalf("Failed to write fast head block hash: %v", err)
	}
277 278 279 280 281 282 283
	// 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())
	}
284 285 286
	if entry := GetHeadFastBlockHash(db); entry != blockFast.Hash() {
		t.Fatalf("Fast head block hash mismatch: have %v, want %v", entry, blockFast.Hash())
	}
287
}
288

289 290
// Tests that positional lookup metadata can be stored and retrieved.
func TestLookupStorage(t *testing.T) {
291 292
	db, _ := ethdb.NewMemDatabase()

293 294 295
	tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
	tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
	tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
296 297 298 299 300 301 302 303 304 305 306
	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
307 308 309 310
	if err := WriteBlock(db, block); err != nil {
		t.Fatalf("failed to write block contents: %v", err)
	}
	if err := WriteTxLookupEntries(db, block); err != nil {
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
		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 {
327
		DeleteTxLookupEntry(db, tx.Hash())
328 329 330 331 332 333 334 335 336 337 338
		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{
339
		Status:            types.ReceiptStatusFailed,
340
		CumulativeGasUsed: 1,
341 342 343
		Logs: []*types.Log{
			{Address: common.BytesToAddress([]byte{0x11})},
			{Address: common.BytesToAddress([]byte{0x01, 0x11})},
344 345 346
		},
		TxHash:          common.BytesToHash([]byte{0x11, 0x11}),
		ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
347
		GasUsed:         111111,
348 349
	}
	receipt2 := &types.Receipt{
350
		PostState:         common.Hash{2}.Bytes(),
351
		CumulativeGasUsed: 2,
352 353 354
		Logs: []*types.Log{
			{Address: common.BytesToAddress([]byte{0x22})},
			{Address: common.BytesToAddress([]byte{0x02, 0x22})},
355 356 357
		},
		TxHash:          common.BytesToHash([]byte{0x22, 0x22}),
		ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
358
		GasUsed:         222222,
359 360 361 362 363
	}
	receipts := []*types.Receipt{receipt1, receipt2}

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

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