aboutsummaryrefslogtreecommitdiffstats
path: root/core/rawdb
diff options
context:
space:
mode:
authorPéter Szilágyi <peterke@gmail.com>2018-05-07 19:35:06 +0800
committerGitHub <noreply@github.com>2018-05-07 19:35:06 +0800
commit6cf0ab38bd0af77d81aad4c104979cebee9e3e63 (patch)
tree142d6f965c44dcf9e2182da67b7bbc978c573448 /core/rawdb
parent5463ed99968bf71685a2a8ee9dbf8705912f00cb (diff)
downloaddexon-6cf0ab38bd0af77d81aad4c104979cebee9e3e63.tar.gz
dexon-6cf0ab38bd0af77d81aad4c104979cebee9e3e63.tar.zst
dexon-6cf0ab38bd0af77d81aad4c104979cebee9e3e63.zip
core/rawdb: separate raw database access to own package (#16666)
Diffstat (limited to 'core/rawdb')
-rw-r--r--core/rawdb/accessors_chain.go381
-rw-r--r--core/rawdb/accessors_chain_test.go319
-rw-r--r--core/rawdb/accessors_indexes.go119
-rw-r--r--core/rawdb/accessors_indexes_test.go68
-rw-r--r--core/rawdb/accessors_metadata.go90
-rw-r--r--core/rawdb/interfaces.go33
-rw-r--r--core/rawdb/schema.go79
7 files changed, 1089 insertions, 0 deletions
diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go
new file mode 100644
index 000000000..a26a42ba7
--- /dev/null
+++ b/core/rawdb/accessors_chain.go
@@ -0,0 +1,381 @@
+// Copyright 2018 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 rawdb
+
+import (
+ "bytes"
+ "encoding/binary"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+// ReadCanonicalHash retrieves the hash assigned to a canonical block number.
+func ReadCanonicalHash(db DatabaseReader, number uint64) common.Hash {
+ data, _ := db.Get(append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...))
+ if len(data) == 0 {
+ return common.Hash{}
+ }
+ return common.BytesToHash(data)
+}
+
+// WriteCanonicalHash stores the hash assigned to a canonical block number.
+func WriteCanonicalHash(db DatabaseWriter, hash common.Hash, number uint64) {
+ key := append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...)
+ if err := db.Put(key, hash.Bytes()); err != nil {
+ log.Crit("Failed to store number to hash mapping", "err", err)
+ }
+}
+
+// DeleteCanonicalHash removes the number to hash canonical mapping.
+func DeleteCanonicalHash(db DatabaseDeleter, number uint64) {
+ if err := db.Delete(append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...)); err != nil {
+ log.Crit("Failed to delete number to hash mapping", "err", err)
+ }
+}
+
+// ReadHeaderNumber returns the header number assigned to a hash.
+func ReadHeaderNumber(db DatabaseReader, hash common.Hash) *uint64 {
+ data, _ := db.Get(append(headerNumberPrefix, hash.Bytes()...))
+ if len(data) != 8 {
+ return nil
+ }
+ number := binary.BigEndian.Uint64(data)
+ return &number
+}
+
+// ReadHeadHeaderHash retrieves the hash of the current canonical head header.
+func ReadHeadHeaderHash(db DatabaseReader) common.Hash {
+ data, _ := db.Get(headHeaderKey)
+ if len(data) == 0 {
+ return common.Hash{}
+ }
+ return common.BytesToHash(data)
+}
+
+// WriteHeadHeaderHash stores the hash of the current canonical head header.
+func WriteHeadHeaderHash(db DatabaseWriter, hash common.Hash) {
+ if err := db.Put(headHeaderKey, hash.Bytes()); err != nil {
+ log.Crit("Failed to store last header's hash", "err", err)
+ }
+}
+
+// ReadHeadBlockHash retrieves the hash of the current canonical head block.
+func ReadHeadBlockHash(db DatabaseReader) common.Hash {
+ data, _ := db.Get(headBlockKey)
+ if len(data) == 0 {
+ return common.Hash{}
+ }
+ return common.BytesToHash(data)
+}
+
+// WriteHeadBlockHash stores the head block's hash.
+func WriteHeadBlockHash(db DatabaseWriter, hash common.Hash) {
+ if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
+ log.Crit("Failed to store last block's hash", "err", err)
+ }
+}
+
+// ReadHeadFastBlockHash retrieves the hash of the current fast-sync head block.
+func ReadHeadFastBlockHash(db DatabaseReader) common.Hash {
+ data, _ := db.Get(headFastBlockKey)
+ if len(data) == 0 {
+ return common.Hash{}
+ }
+ return common.BytesToHash(data)
+}
+
+// WriteHeadFastBlockHash stores the hash of the current fast-sync head block.
+func WriteHeadFastBlockHash(db DatabaseWriter, hash common.Hash) {
+ if err := db.Put(headFastBlockKey, hash.Bytes()); err != nil {
+ log.Crit("Failed to store last fast block's hash", "err", err)
+ }
+}
+
+// ReadFastTrieProgress retrieves the number of tries nodes fast synced to allow
+// reporting correct numbers across restarts.
+func ReadFastTrieProgress(db DatabaseReader) uint64 {
+ data, _ := db.Get(fastTrieProgressKey)
+ if len(data) == 0 {
+ return 0
+ }
+ return new(big.Int).SetBytes(data).Uint64()
+}
+
+// WriteFastTrieProgress stores the fast sync trie process counter to support
+// retrieving it across restarts.
+func WriteFastTrieProgress(db DatabaseWriter, count uint64) {
+ if err := db.Put(fastTrieProgressKey, new(big.Int).SetUint64(count).Bytes()); err != nil {
+ log.Crit("Failed to store fast sync trie progress", "err", err)
+ }
+}
+
+// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
+func ReadHeaderRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
+ data, _ := db.Get(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...))
+ return data
+}
+
+// HasHeader verifies the existence of a block header corresponding to the hash.
+func HasHeader(db DatabaseReader, hash common.Hash, number uint64) bool {
+ key := append(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...))
+ if has, err := db.Has(key); !has || err != nil {
+ return false
+ }
+ return true
+}
+
+// ReadHeader retrieves the block header corresponding to the hash.
+func ReadHeader(db DatabaseReader, hash common.Hash, number uint64) *types.Header {
+ data := ReadHeaderRLP(db, hash, number)
+ if len(data) == 0 {
+ return nil
+ }
+ header := new(types.Header)
+ if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
+ log.Error("Invalid block header RLP", "hash", hash, "err", err)
+ return nil
+ }
+ return header
+}
+
+// WriteHeader stores a block header into the database and also stores the hash-
+// to-number mapping.
+func WriteHeader(db DatabaseWriter, header *types.Header) {
+ // Write the hash -> number mapping
+ var (
+ hash = header.Hash().Bytes()
+ number = header.Number.Uint64()
+ encoded = encodeBlockNumber(number)
+ )
+ key := append(headerNumberPrefix, hash...)
+ if err := db.Put(key, encoded); err != nil {
+ log.Crit("Failed to store hash to number mapping", "err", err)
+ }
+ // Write the encoded header
+ data, err := rlp.EncodeToBytes(header)
+ if err != nil {
+ log.Crit("Failed to RLP encode header", "err", err)
+ }
+ key = append(append(headerPrefix, encoded...), hash...)
+ if err := db.Put(key, data); err != nil {
+ log.Crit("Failed to store header", "err", err)
+ }
+}
+
+// DeleteHeader removes all block header data associated with a hash.
+func DeleteHeader(db DatabaseDeleter, hash common.Hash, number uint64) {
+ if err := db.Delete(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)); err != nil {
+ log.Crit("Failed to delete header", "err", err)
+ }
+ if err := db.Delete(append(headerNumberPrefix, hash.Bytes()...)); err != nil {
+ log.Crit("Failed to delete hash to number mapping", "err", err)
+ }
+}
+
+// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
+func ReadBodyRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
+ data, _ := db.Get(append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...))
+ return data
+}
+
+// WriteBodyRLP stores an RLP encoded block body into the database.
+func WriteBodyRLP(db DatabaseWriter, hash common.Hash, number uint64, rlp rlp.RawValue) {
+ key := append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
+ if err := db.Put(key, rlp); err != nil {
+ log.Crit("Failed to store block body", "err", err)
+ }
+}
+
+// HasBody verifies the existence of a block body corresponding to the hash.
+func HasBody(db DatabaseReader, hash common.Hash, number uint64) bool {
+ key := append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
+ if has, err := db.Has(key); !has || err != nil {
+ return false
+ }
+ return true
+}
+
+// ReadBody retrieves the block body corresponding to the hash.
+func ReadBody(db DatabaseReader, hash common.Hash, number uint64) *types.Body {
+ data := ReadBodyRLP(db, hash, number)
+ if len(data) == 0 {
+ return nil
+ }
+ body := new(types.Body)
+ if err := rlp.Decode(bytes.NewReader(data), body); err != nil {
+ log.Error("Invalid block body RLP", "hash", hash, "err", err)
+ return nil
+ }
+ return body
+}
+
+// WriteBody storea a block body into the database.
+func WriteBody(db DatabaseWriter, hash common.Hash, number uint64, body *types.Body) {
+ data, err := rlp.EncodeToBytes(body)
+ if err != nil {
+ log.Crit("Failed to RLP encode body", "err", err)
+ }
+ WriteBodyRLP(db, hash, number, data)
+}
+
+// DeleteBody removes all block body data associated with a hash.
+func DeleteBody(db DatabaseDeleter, hash common.Hash, number uint64) {
+ if err := db.Delete(append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)); err != nil {
+ log.Crit("Failed to delete block body", "err", err)
+ }
+}
+
+// ReadTd retrieves a block's total difficulty corresponding to the hash.
+func ReadTd(db DatabaseReader, hash common.Hash, number uint64) *big.Int {
+ data, _ := db.Get(append(append(append(headerPrefix, encodeBlockNumber(number)...), hash[:]...), headerTDSuffix...))
+ if len(data) == 0 {
+ return nil
+ }
+ td := new(big.Int)
+ if err := rlp.Decode(bytes.NewReader(data), td); err != nil {
+ log.Error("Invalid block total difficulty RLP", "hash", hash, "err", err)
+ return nil
+ }
+ return td
+}
+
+// WriteTd stores the total difficulty of a block into the database.
+func WriteTd(db DatabaseWriter, hash common.Hash, number uint64, td *big.Int) {
+ data, err := rlp.EncodeToBytes(td)
+ if err != nil {
+ log.Crit("Failed to RLP encode block total difficulty", "err", err)
+ }
+ key := append(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...), headerTDSuffix...)
+ if err := db.Put(key, data); err != nil {
+ log.Crit("Failed to store block total difficulty", "err", err)
+ }
+}
+
+// DeleteTd removes all block total difficulty data associated with a hash.
+func DeleteTd(db DatabaseDeleter, hash common.Hash, number uint64) {
+ if err := db.Delete(append(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...), headerTDSuffix...)); err != nil {
+ log.Crit("Failed to delete block total difficulty", "err", err)
+ }
+}
+
+// ReadReceipts retrieves all the transaction receipts belonging to a block.
+func ReadReceipts(db DatabaseReader, hash common.Hash, number uint64) types.Receipts {
+ // Retrieve the flattened receipt slice
+ data, _ := db.Get(append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash[:]...))
+ if len(data) == 0 {
+ return nil
+ }
+ // Convert the revceipts from their storage form to their internal representation
+ storageReceipts := []*types.ReceiptForStorage{}
+ if err := rlp.DecodeBytes(data, &storageReceipts); err != nil {
+ log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
+ return nil
+ }
+ receipts := make(types.Receipts, len(storageReceipts))
+ for i, receipt := range storageReceipts {
+ receipts[i] = (*types.Receipt)(receipt)
+ }
+ return receipts
+}
+
+// WriteReceipts stores all the transaction receipts belonging to a block.
+func WriteReceipts(db DatabaseWriter, hash common.Hash, number uint64, receipts types.Receipts) {
+ // Convert the receipts into their storage form and serialize them
+ storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
+ for i, receipt := range receipts {
+ storageReceipts[i] = (*types.ReceiptForStorage)(receipt)
+ }
+ bytes, err := rlp.EncodeToBytes(storageReceipts)
+ if err != nil {
+ log.Crit("Failed to encode block receipts", "err", err)
+ }
+ // Store the flattened receipt slice
+ key := append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
+ if err := db.Put(key, bytes); err != nil {
+ log.Crit("Failed to store block receipts", "err", err)
+ }
+}
+
+// DeleteReceipts removes all receipt data associated with a block hash.
+func DeleteReceipts(db DatabaseDeleter, hash common.Hash, number uint64) {
+ if err := db.Delete(append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)); err != nil {
+ log.Crit("Failed to delete block receipts", "err", err)
+ }
+}
+
+// ReadBlock retrieves an entire block corresponding to the hash, assembling it
+// back from the stored header and body. If either the header or body could not
+// be retrieved nil is returned.
+//
+// Note, due to concurrent download of header and block body the header and thus
+// canonical hash can be stored in the database but the body data not (yet).
+func ReadBlock(db DatabaseReader, hash common.Hash, number uint64) *types.Block {
+ header := ReadHeader(db, hash, number)
+ if header == nil {
+ return nil
+ }
+ body := ReadBody(db, hash, number)
+ if body == nil {
+ return nil
+ }
+ return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles)
+}
+
+// WriteBlock serializes a block into the database, header and body separately.
+func WriteBlock(db DatabaseWriter, block *types.Block) {
+ WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
+ WriteHeader(db, block.Header())
+}
+
+// DeleteBlock removes all block data associated with a hash.
+func DeleteBlock(db DatabaseDeleter, hash common.Hash, number uint64) {
+ DeleteReceipts(db, hash, number)
+ DeleteHeader(db, hash, number)
+ DeleteBody(db, hash, number)
+ DeleteTd(db, hash, number)
+}
+
+// FindCommonAncestor returns the last common ancestor of two block headers
+func FindCommonAncestor(db DatabaseReader, a, b *types.Header) *types.Header {
+ for bn := b.Number.Uint64(); a.Number.Uint64() > bn; {
+ a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1)
+ if a == nil {
+ return nil
+ }
+ }
+ for an := a.Number.Uint64(); an < b.Number.Uint64(); {
+ b = ReadHeader(db, b.ParentHash, b.Number.Uint64()-1)
+ if b == nil {
+ return nil
+ }
+ }
+ for a.Hash() != b.Hash() {
+ a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1)
+ if a == nil {
+ return nil
+ }
+ b = ReadHeader(db, b.ParentHash, b.Number.Uint64()-1)
+ if b == nil {
+ return nil
+ }
+ }
+ return a
+}
diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go
new file mode 100644
index 000000000..84c9c9aeb
--- /dev/null
+++ b/core/rawdb/accessors_chain_test.go
@@ -0,0 +1,319 @@
+// Copyright 2018 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 rawdb
+
+import (
+ "bytes"
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "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"
+)
+
+// 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
+ header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header")}
+ if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
+ t.Fatalf("Non existent header returned: %v", entry)
+ }
+ // Write and verify the header in the database
+ WriteHeader(db, header)
+ if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry == nil {
+ t.Fatalf("Stored header not found")
+ } else if entry.Hash() != header.Hash() {
+ t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, header)
+ }
+ if entry := ReadHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil {
+ 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
+ DeleteHeader(db, header.Hash(), header.Number.Uint64())
+ if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
+ 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))
+
+ if entry := ReadBody(db, hash, 0); entry != nil {
+ t.Fatalf("Non existent body returned: %v", entry)
+ }
+ // Write and verify the body in the database
+ WriteBody(db, hash, 0, body)
+ if entry := ReadBody(db, hash, 0); entry == nil {
+ 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)
+ }
+ if entry := ReadBodyRLP(db, hash, 0); entry == nil {
+ 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
+ DeleteBody(db, hash, 0)
+ if entry := ReadBody(db, hash, 0); entry != nil {
+ 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
+ block := types.NewBlockWithHeader(&types.Header{
+ Extra: []byte("test block"),
+ UncleHash: types.EmptyUncleHash,
+ TxHash: types.EmptyRootHash,
+ ReceiptHash: types.EmptyRootHash,
+ })
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent block returned: %v", entry)
+ }
+ if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent header returned: %v", entry)
+ }
+ if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent body returned: %v", entry)
+ }
+ // Write and verify the block in the database
+ WriteBlock(db, block)
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry == nil {
+ t.Fatalf("Stored block not found")
+ } else if entry.Hash() != block.Hash() {
+ t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
+ }
+ if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry == nil {
+ 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())
+ }
+ if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry == nil {
+ 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()) {
+ t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body())
+ }
+ // Delete the block and verify the execution
+ DeleteBlock(db, block.Hash(), block.NumberU64())
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Deleted block returned: %v", entry)
+ }
+ if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Deleted header returned: %v", entry)
+ }
+ if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry != nil {
+ 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()
+ block := types.NewBlockWithHeader(&types.Header{
+ Extra: []byte("test block"),
+ UncleHash: types.EmptyUncleHash,
+ TxHash: types.EmptyRootHash,
+ ReceiptHash: types.EmptyRootHash,
+ })
+ // Store a header and check that it's not recognized as a block
+ WriteHeader(db, block.Header())
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent block returned: %v", entry)
+ }
+ DeleteHeader(db, block.Hash(), block.NumberU64())
+
+ // Store a body and check that it's not recognized as a block
+ WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent block returned: %v", entry)
+ }
+ DeleteBody(db, block.Hash(), block.NumberU64())
+
+ // Store a header and a body separately and check reassembly
+ WriteHeader(db, block.Header())
+ WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
+
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry == nil {
+ 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)
+ if entry := ReadTd(db, hash, 0); entry != nil {
+ t.Fatalf("Non existent TD returned: %v", entry)
+ }
+ // Write and verify the TD in the database
+ WriteTd(db, hash, 0, td)
+ if entry := ReadTd(db, hash, 0); entry == nil {
+ 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
+ DeleteTd(db, hash, 0)
+ if entry := ReadTd(db, hash, 0); entry != nil {
+ 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 := ReadCanonicalHash(db, number); entry != (common.Hash{}) {
+ t.Fatalf("Non existent canonical mapping returned: %v", entry)
+ }
+ // Write and verify the TD in the database
+ WriteCanonicalHash(db, hash, number)
+ if entry := ReadCanonicalHash(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 := ReadCanonicalHash(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")})
+ blockFast := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block fast")})
+
+ // Check that no head entries are in a pristine database
+ if entry := ReadHeadHeaderHash(db); entry != (common.Hash{}) {
+ t.Fatalf("Non head header entry returned: %v", entry)
+ }
+ if entry := ReadHeadBlockHash(db); entry != (common.Hash{}) {
+ t.Fatalf("Non head block entry returned: %v", entry)
+ }
+ if entry := ReadHeadFastBlockHash(db); entry != (common.Hash{}) {
+ t.Fatalf("Non fast head block entry returned: %v", entry)
+ }
+ // Assign separate entries for the head header and block
+ WriteHeadHeaderHash(db, blockHead.Hash())
+ WriteHeadBlockHash(db, blockFull.Hash())
+ WriteHeadFastBlockHash(db, blockFast.Hash())
+
+ // Check that both heads are present, and different (i.e. two heads maintained)
+ if entry := ReadHeadHeaderHash(db); entry != blockHead.Hash() {
+ t.Fatalf("Head header hash mismatch: have %v, want %v", entry, blockHead.Hash())
+ }
+ if entry := ReadHeadBlockHash(db); entry != blockFull.Hash() {
+ t.Fatalf("Head block hash mismatch: have %v, want %v", entry, blockFull.Hash())
+ }
+ if entry := ReadHeadFastBlockHash(db); entry != blockFast.Hash() {
+ t.Fatalf("Fast head block hash mismatch: have %v, want %v", entry, blockFast.Hash())
+ }
+}
+
+// Tests that receipts associated with a single block can be stored and retrieved.
+func TestBlockReceiptStorage(t *testing.T) {
+ db, _ := ethdb.NewMemDatabase()
+
+ receipt1 := &types.Receipt{
+ Status: types.ReceiptStatusFailed,
+ CumulativeGasUsed: 1,
+ Logs: []*types.Log{
+ {Address: common.BytesToAddress([]byte{0x11})},
+ {Address: common.BytesToAddress([]byte{0x01, 0x11})},
+ },
+ TxHash: common.BytesToHash([]byte{0x11, 0x11}),
+ ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
+ GasUsed: 111111,
+ }
+ receipt2 := &types.Receipt{
+ PostState: common.Hash{2}.Bytes(),
+ CumulativeGasUsed: 2,
+ Logs: []*types.Log{
+ {Address: common.BytesToAddress([]byte{0x22})},
+ {Address: common.BytesToAddress([]byte{0x02, 0x22})},
+ },
+ TxHash: common.BytesToHash([]byte{0x22, 0x22}),
+ ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
+ GasUsed: 222222,
+ }
+ receipts := []*types.Receipt{receipt1, receipt2}
+
+ // Check that no receipt entries are in a pristine database
+ hash := common.BytesToHash([]byte{0x03, 0x14})
+ if rs := ReadReceipts(db, hash, 0); len(rs) != 0 {
+ t.Fatalf("non existent receipts returned: %v", rs)
+ }
+ // Insert the receipt slice into the database and check presence
+ WriteReceipts(db, hash, 0, receipts)
+ if rs := ReadReceipts(db, hash, 0); len(rs) == 0 {
+ t.Fatalf("no receipts returned")
+ } else {
+ for i := 0; i < len(receipts); i++ {
+ rlpHave, _ := rlp.EncodeToBytes(rs[i])
+ rlpWant, _ := rlp.EncodeToBytes(receipts[i])
+
+ if !bytes.Equal(rlpHave, rlpWant) {
+ t.Fatalf("receipt #%d: receipt mismatch: have %v, want %v", i, rs[i], receipts[i])
+ }
+ }
+ }
+ // Delete the receipt slice and check purge
+ DeleteReceipts(db, hash, 0)
+ if rs := ReadReceipts(db, hash, 0); len(rs) != 0 {
+ t.Fatalf("deleted receipts returned: %v", rs)
+ }
+}
diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go
new file mode 100644
index 000000000..9abad14e0
--- /dev/null
+++ b/core/rawdb/accessors_indexes.go
@@ -0,0 +1,119 @@
+// Copyright 2018 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 rawdb
+
+import (
+ "encoding/binary"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+// ReadTxLookupEntry retrieves the positional metadata associated with a transaction
+// hash to allow retrieving the transaction or receipt by hash.
+func ReadTxLookupEntry(db DatabaseReader, hash common.Hash) (common.Hash, uint64, uint64) {
+ data, _ := db.Get(append(txLookupPrefix, hash.Bytes()...))
+ if len(data) == 0 {
+ return common.Hash{}, 0, 0
+ }
+ var entry TxLookupEntry
+ if err := rlp.DecodeBytes(data, &entry); err != nil {
+ log.Error("Invalid transaction lookup entry RLP", "hash", hash, "err", err)
+ return common.Hash{}, 0, 0
+ }
+ return entry.BlockHash, entry.BlockIndex, entry.Index
+}
+
+// WriteTxLookupEntries stores a positional metadata for every transaction from
+// a block, enabling hash based transaction and receipt lookups.
+func WriteTxLookupEntries(db DatabaseWriter, block *types.Block) {
+ for i, tx := range block.Transactions() {
+ entry := TxLookupEntry{
+ BlockHash: block.Hash(),
+ BlockIndex: block.NumberU64(),
+ Index: uint64(i),
+ }
+ data, err := rlp.EncodeToBytes(entry)
+ if err != nil {
+ log.Crit("Failed to encode transaction lookup entry", "err", err)
+ }
+ if err := db.Put(append(txLookupPrefix, tx.Hash().Bytes()...), data); err != nil {
+ log.Crit("Failed to store transaction lookup entry", "err", err)
+ }
+ }
+}
+
+// DeleteTxLookupEntry removes all transaction data associated with a hash.
+func DeleteTxLookupEntry(db DatabaseDeleter, hash common.Hash) {
+ db.Delete(append(txLookupPrefix, hash.Bytes()...))
+}
+
+// ReadTransaction retrieves a specific transaction from the database, along with
+// its added positional metadata.
+func ReadTransaction(db DatabaseReader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
+ blockHash, blockNumber, txIndex := ReadTxLookupEntry(db, hash)
+ if blockHash == (common.Hash{}) {
+ return nil, common.Hash{}, 0, 0
+ }
+ body := ReadBody(db, blockHash, blockNumber)
+ if body == nil || len(body.Transactions) <= int(txIndex) {
+ log.Error("Transaction referenced missing", "number", blockNumber, "hash", blockHash, "index", txIndex)
+ return nil, common.Hash{}, 0, 0
+ }
+ return body.Transactions[txIndex], blockHash, blockNumber, txIndex
+}
+
+// ReadReceipt retrieves a specific transaction receipt from the database, along with
+// its added positional metadata.
+func ReadReceipt(db DatabaseReader, hash common.Hash) (*types.Receipt, common.Hash, uint64, uint64) {
+ blockHash, blockNumber, receiptIndex := ReadTxLookupEntry(db, hash)
+ if blockHash == (common.Hash{}) {
+ return nil, common.Hash{}, 0, 0
+ }
+ receipts := ReadReceipts(db, blockHash, blockNumber)
+ if len(receipts) <= int(receiptIndex) {
+ log.Error("Receipt refereced missing", "number", blockNumber, "hash", blockHash, "index", receiptIndex)
+ return nil, common.Hash{}, 0, 0
+ }
+ return receipts[receiptIndex], blockHash, blockNumber, receiptIndex
+}
+
+// ReadBloomBits retrieves the compressed bloom bit vector belonging to the given
+// section and bit index from the.
+func ReadBloomBits(db DatabaseReader, bit uint, section uint64, head common.Hash) ([]byte, error) {
+ key := append(append(bloomBitsPrefix, make([]byte, 10)...), head.Bytes()...)
+
+ binary.BigEndian.PutUint16(key[1:], uint16(bit))
+ binary.BigEndian.PutUint64(key[3:], section)
+
+ return db.Get(key)
+}
+
+// WriteBloomBits stores the compressed bloom bits vector belonging to the given
+// section and bit index.
+func WriteBloomBits(db DatabaseWriter, bit uint, section uint64, head common.Hash, bits []byte) {
+ key := append(append(bloomBitsPrefix, make([]byte, 10)...), head.Bytes()...)
+
+ binary.BigEndian.PutUint16(key[1:], uint16(bit))
+ binary.BigEndian.PutUint64(key[3:], section)
+
+ if err := db.Put(key, bits); err != nil {
+ log.Crit("Failed to store bloom bits", "err", err)
+ }
+}
diff --git a/core/rawdb/accessors_indexes_test.go b/core/rawdb/accessors_indexes_test.go
new file mode 100644
index 000000000..d9c129163
--- /dev/null
+++ b/core/rawdb/accessors_indexes_test.go
@@ -0,0 +1,68 @@
+// Copyright 2018 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 rawdb
+
+import (
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/ethdb"
+)
+
+// Tests that positional lookup metadata can be stored and retrieved.
+func TestLookupStorage(t *testing.T) {
+ db, _ := ethdb.NewMemDatabase()
+
+ 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})
+ 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, _, _, _ := ReadTransaction(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
+ WriteBlock(db, block)
+ WriteTxLookupEntries(db, block)
+
+ for i, tx := range txs {
+ if txn, hash, number, index := ReadTransaction(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.Hash() != txn.Hash() {
+ 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 {
+ DeleteTxLookupEntry(db, tx.Hash())
+ if txn, _, _, _ := ReadTransaction(db, tx.Hash()); txn != nil {
+ t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn)
+ }
+ }
+}
diff --git a/core/rawdb/accessors_metadata.go b/core/rawdb/accessors_metadata.go
new file mode 100644
index 000000000..73ab983f2
--- /dev/null
+++ b/core/rawdb/accessors_metadata.go
@@ -0,0 +1,90 @@
+// Copyright 2018 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 rawdb
+
+import (
+ "encoding/json"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+// ReadDatabaseVersion retrieves the version number of the database.
+func ReadDatabaseVersion(db DatabaseReader) int {
+ var version int
+
+ enc, _ := db.Get(databaseVerisionKey)
+ rlp.DecodeBytes(enc, &version)
+
+ return version
+}
+
+// WriteDatabaseVersion stores the version number of the database
+func WriteDatabaseVersion(db DatabaseWriter, version int) {
+ enc, _ := rlp.EncodeToBytes(version)
+ if err := db.Put(databaseVerisionKey, enc); err != nil {
+ log.Crit("Failed to store the database version", "err", err)
+ }
+}
+
+// ReadChainConfig retrieves the consensus settings based on the given genesis hash.
+func ReadChainConfig(db DatabaseReader, hash common.Hash) *params.ChainConfig {
+ data, _ := db.Get(append(configPrefix, hash[:]...))
+ if len(data) == 0 {
+ return nil
+ }
+ var config params.ChainConfig
+ if err := json.Unmarshal(data, &config); err != nil {
+ log.Error("Invalid chain config JSON", "hash", hash, "err", err)
+ return nil
+ }
+ return &config
+}
+
+// WriteChainConfig writes the chain config settings to the database.
+func WriteChainConfig(db DatabaseWriter, hash common.Hash, cfg *params.ChainConfig) {
+ if cfg == nil {
+ return
+ }
+ data, err := json.Marshal(cfg)
+ if err != nil {
+ log.Crit("Failed to JSON encode chain config", "err", err)
+ }
+ if err := db.Put(append(configPrefix, hash[:]...), data); err != nil {
+ log.Crit("Failed to store chain config", "err", err)
+ }
+}
+
+// ReadPreimage retrieves a single preimage of the provided hash.
+func ReadPreimage(db DatabaseReader, hash common.Hash) []byte {
+ data, _ := db.Get(append(preimagePrefix, hash.Bytes()...))
+ return data
+}
+
+// WritePreimages writes the provided set of preimages to the database. `number` is the
+// current block number, and is used for debug messages only.
+func WritePreimages(db DatabaseWriter, number uint64, preimages map[common.Hash][]byte) {
+ for hash, preimage := range preimages {
+ if err := db.Put(append(preimagePrefix, hash.Bytes()...), preimage); err != nil {
+ log.Crit("Failed to store trie preimage", "err", err)
+ }
+ }
+ preimageCounter.Inc(int64(len(preimages)))
+ preimageHitCounter.Inc(int64(len(preimages)))
+}
diff --git a/core/rawdb/interfaces.go b/core/rawdb/interfaces.go
new file mode 100644
index 000000000..3bdf55124
--- /dev/null
+++ b/core/rawdb/interfaces.go
@@ -0,0 +1,33 @@
+// Copyright 2018 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 rawdb
+
+// DatabaseReader wraps the Has and Get method of a backing data store.
+type DatabaseReader interface {
+ Has(key []byte) (bool, error)
+ Get(key []byte) ([]byte, error)
+}
+
+// DatabaseWriter wraps the Put method of a backing data store.
+type DatabaseWriter interface {
+ Put(key []byte, value []byte) error
+}
+
+// DatabaseDeleter wraps the Delete method of a backing data store.
+type DatabaseDeleter interface {
+ Delete(key []byte) error
+}
diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go
new file mode 100644
index 000000000..a4b1596fd
--- /dev/null
+++ b/core/rawdb/schema.go
@@ -0,0 +1,79 @@
+// Copyright 2018 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 rawdb contains a collection of low level database accessors.
+package rawdb
+
+import (
+ "encoding/binary"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/metrics"
+)
+
+// The fields below define the low level database schema prefixing.
+var (
+ // databaseVerisionKey tracks the current database version.
+ databaseVerisionKey = []byte("DatabaseVersion")
+
+ // headHeaderKey tracks the latest know header's hash.
+ headHeaderKey = []byte("LastHeader")
+
+ // headBlockKey tracks the latest know full block's hash.
+ headBlockKey = []byte("LastBlock")
+
+ // headFastBlockKey tracks the latest known incomplete block's hash duirng fast sync.
+ headFastBlockKey = []byte("LastFast")
+
+ // fastTrieProgressKey tracks the number of trie entries imported during fast sync.
+ fastTrieProgressKey = []byte("TrieSync")
+
+ // Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
+ headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
+ headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td
+ headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
+ headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian)
+
+ blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
+ blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
+
+ txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata
+ bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits
+
+ preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage
+ configPrefix = []byte("ethereum-config-") // config prefix for the db
+
+ // Chain index prefixes (use `i` + single byte to avoid mixing data types).
+ BloomBitsIndexPrefix = []byte("iB") // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress
+
+ preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil)
+ preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
+)
+
+// TxLookupEntry is a positional metadata to help looking up the data content of
+// a transaction or receipt given only its hash.
+type TxLookupEntry struct {
+ BlockHash common.Hash
+ BlockIndex uint64
+ Index uint64
+}
+
+// encodeBlockNumber encodes a block number as big endian uint64
+func encodeBlockNumber(number uint64) []byte {
+ enc := make([]byte, 8)
+ binary.BigEndian.PutUint64(enc, number)
+ return enc
+}