mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 21:56:43 +00:00
core, eth: split the db blocks into headers and bodies
This commit is contained in:
parent
ebbe25ee71
commit
fce218f7f8
4 changed files with 185 additions and 92 deletions
|
|
@ -361,12 +361,7 @@ func (bc *ChainManager) Genesis() *types.Block {
|
||||||
|
|
||||||
// Block fetching methods
|
// Block fetching methods
|
||||||
func (bc *ChainManager) HasBlock(hash common.Hash) bool {
|
func (bc *ChainManager) HasBlock(hash common.Hash) bool {
|
||||||
if bc.cache.Contains(hash) {
|
return bc.GetBlock(hash) != nil
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
data, _ := bc.chainDb.Get(append(blockHashPre, hash[:]...))
|
|
||||||
return len(data) != 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) (chain []common.Hash) {
|
func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) (chain []common.Hash) {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ package core
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -30,9 +29,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
blockHashPre = []byte("block-hash-")
|
headerHashPre = []byte("header-hash-")
|
||||||
|
bodyHashPre = []byte("body-hash-")
|
||||||
blockNumPre = []byte("block-num-")
|
blockNumPre = []byte("block-num-")
|
||||||
ExpDiffPeriod = big.NewInt(100000)
|
ExpDiffPeriod = big.NewInt(100000)
|
||||||
|
|
||||||
|
blockHashPre = []byte("block-hash-") // [deprecated by eth/63]
|
||||||
)
|
)
|
||||||
|
|
||||||
// CalcDifficulty is the difficulty adjustment algorithm. It returns
|
// CalcDifficulty is the difficulty adjustment algorithm. It returns
|
||||||
|
|
@ -112,27 +114,91 @@ func CalcGasLimit(parent *types.Block) *big.Int {
|
||||||
return gl
|
return gl
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockByHash returns the block corresponding to the hash or nil if not found
|
// storageBody is the block body encoding used for the database.
|
||||||
func GetBlockByHash(db common.Database, hash common.Hash) *types.Block {
|
type storageBody struct {
|
||||||
data, _ := db.Get(append(blockHashPre, hash[:]...))
|
Transactions []*types.Transaction
|
||||||
|
Uncles []*types.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeaderRLPByHash retrieves a block header in its raw RLP database encoding,
|
||||||
|
// or nil if the header's not found.
|
||||||
|
func GetHeaderRLPByHash(db common.Database, hash common.Hash) []byte {
|
||||||
|
data, _ := db.Get(append(headerHashPre, hash[:]...))
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeaderByHash retrieves the block header corresponding to the hash, nil if
|
||||||
|
// none found.
|
||||||
|
func GetHeaderByHash(db common.Database, hash common.Hash) *types.Header {
|
||||||
|
data := GetHeaderRLPByHash(db, hash)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var block types.StorageBlock
|
header := new(types.Header)
|
||||||
if err := rlp.Decode(bytes.NewReader(data), &block); err != nil {
|
if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
|
||||||
glog.V(logger.Error).Infof("invalid block RLP for hash %x: %v", hash, err)
|
glog.V(logger.Error).Infof("invalid block header RLP for hash %x: %v", hash, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return (*types.Block)(&block)
|
return header
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockByHash returns the canonical block by number or nil if not found
|
// GetBodyRLPByHash retrieves the block body (transactions and uncles) in RLP
|
||||||
|
// encoding, and the associated total difficulty.
|
||||||
|
func GetBodyRLPByHash(db common.Database, hash common.Hash) ([]byte, *big.Int) {
|
||||||
|
combo, _ := db.Get(append(bodyHashPre, hash[:]...))
|
||||||
|
if len(combo) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
buffer := bytes.NewBuffer(combo)
|
||||||
|
|
||||||
|
td := new(big.Int)
|
||||||
|
if err := rlp.Decode(buffer, td); err != nil {
|
||||||
|
glog.V(logger.Error).Infof("invalid block td RLP for hash %x: %v", hash, err)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return buffer.Bytes(), td
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBodyByHash retrieves the block body (transactons, uncles, total difficulty)
|
||||||
|
// corresponding to the hash, nils if none found.
|
||||||
|
func GetBodyByHash(db common.Database, hash common.Hash) ([]*types.Transaction, []*types.Header, *big.Int) {
|
||||||
|
data, td := GetBodyRLPByHash(db, hash)
|
||||||
|
if len(data) == 0 || td == nil {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
body := new(storageBody)
|
||||||
|
if err := rlp.Decode(bytes.NewReader(data), body); err != nil {
|
||||||
|
glog.V(logger.Error).Infof("invalid block body RLP for hash %x: %v", hash, err)
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
return body.Transactions, body.Uncles, td
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockByHash retrieves an entire block corresponding to the hash, assembling
|
||||||
|
// it back from the stored header and body.
|
||||||
|
func GetBlockByHash(db common.Database, hash common.Hash) *types.Block {
|
||||||
|
// Retrieve the block header and body contents
|
||||||
|
header := GetHeaderByHash(db, hash)
|
||||||
|
if header == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
transactions, uncles, td := GetBodyByHash(db, hash)
|
||||||
|
if td == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Reassemble the block and return
|
||||||
|
block := types.NewBlockWithHeader(header).WithBody(transactions, uncles)
|
||||||
|
block.Td = td
|
||||||
|
|
||||||
|
return block
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockByNumber returns the canonical block by number or nil if not found.
|
||||||
func GetBlockByNumber(db common.Database, number uint64) *types.Block {
|
func GetBlockByNumber(db common.Database, number uint64) *types.Block {
|
||||||
key, _ := db.Get(append(blockNumPre, big.NewInt(int64(number)).Bytes()...))
|
key, _ := db.Get(append(blockNumPre, big.NewInt(int64(number)).Bytes()...))
|
||||||
if len(key) == 0 {
|
if len(key) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetBlockByHash(db, common.BytesToHash(key))
|
return GetBlockByHash(db, common.BytesToHash(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,21 +225,67 @@ func WriteHead(db common.Database, block *types.Block) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteBlock writes a block to the database
|
// WriteHeader serializes a block header into the database.
|
||||||
func WriteBlock(db common.Database, block *types.Block) error {
|
func WriteHeader(db common.Database, header *types.Header) error {
|
||||||
tstart := time.Now()
|
data, err := rlp.EncodeToBytes(header)
|
||||||
|
|
||||||
enc, _ := rlp.EncodeToBytes((*types.StorageBlock)(block))
|
|
||||||
key := append(blockHashPre, block.Hash().Bytes()...)
|
|
||||||
err := db.Put(key, enc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Fatal("db write fail:", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
key := append(headerHashPre, header.Hash().Bytes()...)
|
||||||
if glog.V(logger.Debug) {
|
if err := db.Put(key, data); err != nil {
|
||||||
glog.Infof("wrote block #%v %s. Took %v\n", block.Number(), common.PP(block.Hash().Bytes()), time.Since(tstart))
|
glog.Fatalf("failed to store header into database: %v", err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
glog.V(logger.Debug).Infof("stored header #%v [%x…]", header.Number, header.Hash().Bytes()[:4])
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteBody serializes the body of a block into the database.
|
||||||
|
func WriteBody(db common.Database, block *types.Block) error {
|
||||||
|
body, err := rlp.EncodeToBytes(&storageBody{block.Transactions(), block.Uncles()})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
td, err := rlp.EncodeToBytes(block.Td)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
key := append(bodyHashPre, block.Hash().Bytes()...)
|
||||||
|
if err := db.Put(key, append(td, body...)); err != nil {
|
||||||
|
glog.Fatalf("failed to store block body into database: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
glog.V(logger.Debug).Infof("stored block body #%v [%x…]", block.Number, block.Hash().Bytes()[:4])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteBlock serializes a block into the database, header and body separately.
|
||||||
|
func WriteBlock(db common.Database, block *types.Block) error {
|
||||||
|
// Store the body first to retain database consistency
|
||||||
|
if err := WriteBody(db, block); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Store the header too, signaling full block ownership
|
||||||
|
if err := WriteHeader(db, block.Header()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// [deprecated by eth/63]
|
||||||
|
// GetBlockByHashOld returns the old combined block corresponding to the hash
|
||||||
|
// or nil if not found. This method is only used by the upgrade mechanism to
|
||||||
|
// access the old combined block representation. It will be dropped after the
|
||||||
|
// network transitions to eth/63.
|
||||||
|
func GetBlockByHashOld(db common.Database, hash common.Hash) *types.Block {
|
||||||
|
data, _ := db.Get(append(blockHashPre, hash[:]...))
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var block types.StorageBlock
|
||||||
|
if err := rlp.Decode(bytes.NewReader(data), &block); err != nil {
|
||||||
|
glog.V(logger.Error).Infof("invalid block RLP for hash %x: %v", hash, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return (*types.Block)(&block)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,7 @@ type Block struct {
|
||||||
ReceivedAt time.Time
|
ReceivedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [deprecated by eth/63]
|
||||||
// StorageBlock defines the RLP encoding of a Block stored in the
|
// StorageBlock defines the RLP encoding of a Block stored in the
|
||||||
// state database. The StorageBlock encoding contains fields that
|
// state database. The StorageBlock encoding contains fields that
|
||||||
// would otherwise need to be recomputed.
|
// would otherwise need to be recomputed.
|
||||||
|
|
@ -147,6 +148,7 @@ type extblock struct {
|
||||||
Uncles []*Header
|
Uncles []*Header
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [deprecated by eth/63]
|
||||||
// "storage" block encoding. used for database.
|
// "storage" block encoding. used for database.
|
||||||
type storageblock struct {
|
type storageblock struct {
|
||||||
Header *Header
|
Header *Header
|
||||||
|
|
@ -268,6 +270,7 @@ func (b *Block) EncodeRLP(w io.Writer) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [deprecated by eth/63]
|
||||||
func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error {
|
func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error {
|
||||||
var sb storageblock
|
var sb storageblock
|
||||||
if err := s.Decode(&sb); err != nil {
|
if err := s.Decode(&sb); err != nil {
|
||||||
|
|
@ -277,6 +280,7 @@ func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [deprecated by eth/63]
|
||||||
func (b *StorageBlock) EncodeRLP(w io.Writer) error {
|
func (b *StorageBlock) EncodeRLP(w io.Writer) error {
|
||||||
return rlp.Encode(w, storageblock{
|
return rlp.Encode(w, storageblock{
|
||||||
Header: b.header,
|
Header: b.header,
|
||||||
|
|
|
||||||
108
eth/backend.go
108
eth/backend.go
|
|
@ -18,6 +18,7 @@
|
||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -267,11 +268,7 @@ func New(config *Config) (*Ethereum, error) {
|
||||||
newdb = func(path string) (common.Database, error) { return ethdb.NewLDBDatabase(path, config.DatabaseCache) }
|
newdb = func(path string) (common.Database, error) { return ethdb.NewLDBDatabase(path, config.DatabaseCache) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// attempt to merge database together, upgrading from an old version
|
// Open the chain database and perform any upgrades needed
|
||||||
if err := mergeDatabases(config.DataDir, newdb); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
chainDb, err := newdb(filepath.Join(config.DataDir, "chaindata"))
|
chainDb, err := newdb(filepath.Join(config.DataDir, "chaindata"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("blockchain db err: %v", err)
|
return nil, fmt.Errorf("blockchain db err: %v", err)
|
||||||
|
|
@ -279,6 +276,10 @@ func New(config *Config) (*Ethereum, error) {
|
||||||
if db, ok := chainDb.(*ethdb.LDBDatabase); ok {
|
if db, ok := chainDb.(*ethdb.LDBDatabase); ok {
|
||||||
db.Meter("eth/db/chaindata/")
|
db.Meter("eth/db/chaindata/")
|
||||||
}
|
}
|
||||||
|
if err := upgradeChainDatabase(chainDb); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
dappDb, err := newdb(filepath.Join(config.DataDir, "dapp"))
|
dappDb, err := newdb(filepath.Join(config.DataDir, "dapp"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("dapp db err: %v", err)
|
return nil, fmt.Errorf("dapp db err: %v", err)
|
||||||
|
|
@ -718,74 +719,55 @@ func saveBlockchainVersion(db common.Database, bcVersion int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeDatabases when required merge old database layout to one single database
|
// upgradeChainDatabase ensures that the chain database stores block split into
|
||||||
func mergeDatabases(datadir string, newdb func(path string) (common.Database, error)) error {
|
// separate header and body entries.
|
||||||
// Check if already upgraded
|
func upgradeChainDatabase(db common.Database) error {
|
||||||
data := filepath.Join(datadir, "chaindata")
|
// Short circuit if the head block is stored already as separate header and body
|
||||||
if _, err := os.Stat(data); !os.IsNotExist(err) {
|
data, err := db.Get([]byte("LastBlock"))
|
||||||
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// make sure it's not just a clean path
|
head := common.BytesToHash(data)
|
||||||
chainPath := filepath.Join(datadir, "blockchain")
|
|
||||||
if _, err := os.Stat(chainPath); os.IsNotExist(err) {
|
if block := core.GetBlockByHashOld(db, head); block == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
glog.Infoln("Database upgrade required. Upgrading...")
|
// At least some of the database is still the old format, upgrade (skip the head block!)
|
||||||
|
glog.V(logger.Info).Info("Old database detected, upgrading...")
|
||||||
|
|
||||||
database, err := newdb(data)
|
if db, ok := db.(*ethdb.LDBDatabase); ok {
|
||||||
if err != nil {
|
blockPrefix := []byte("block-hash-")
|
||||||
return fmt.Errorf("creating data db err: %v", err)
|
for it := db.NewIterator(); it.Next(); {
|
||||||
}
|
// Skip anything other than a combined block
|
||||||
defer database.Close()
|
if !bytes.HasPrefix(it.Key(), blockPrefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Skip the head block (merge last to signal upgrade completion)
|
||||||
|
if bytes.HasSuffix(it.Key(), head.Bytes()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Load the block, split and serialize (order!)
|
||||||
|
block := core.GetBlockByHashOld(db, common.BytesToHash(bytes.TrimPrefix(it.Key(), blockPrefix)))
|
||||||
|
|
||||||
// Migrate blocks
|
if err := core.WriteBody(db, block); err != nil {
|
||||||
chainDb, err := newdb(chainPath)
|
return err
|
||||||
if err != nil {
|
}
|
||||||
return fmt.Errorf("state db err: %v", err)
|
if err := core.WriteHeader(db, block.Header()); err != nil {
|
||||||
}
|
return err
|
||||||
defer chainDb.Close()
|
}
|
||||||
|
if err := db.Delete(it.Key()); err != nil {
|
||||||
if chain, ok := chainDb.(*ethdb.LDBDatabase); ok {
|
return err
|
||||||
glog.Infoln("Merging blockchain database...")
|
}
|
||||||
it := chain.NewIterator()
|
|
||||||
for it.Next() {
|
|
||||||
database.Put(it.Key(), it.Value())
|
|
||||||
}
|
}
|
||||||
it.Release()
|
// Lastly, upgrade the head block, disabling the upgrade mechanism
|
||||||
}
|
current := core.GetBlockByHashOld(db, head)
|
||||||
|
|
||||||
// Migrate state
|
if err := core.WriteBody(db, current); err != nil {
|
||||||
stateDb, err := newdb(filepath.Join(datadir, "state"))
|
return err
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("state db err: %v", err)
|
|
||||||
}
|
|
||||||
defer stateDb.Close()
|
|
||||||
|
|
||||||
if state, ok := stateDb.(*ethdb.LDBDatabase); ok {
|
|
||||||
glog.Infoln("Merging state database...")
|
|
||||||
it := state.NewIterator()
|
|
||||||
for it.Next() {
|
|
||||||
database.Put(it.Key(), it.Value())
|
|
||||||
}
|
}
|
||||||
it.Release()
|
if err := core.WriteHeader(db, current.Header()); err != nil {
|
||||||
}
|
return err
|
||||||
|
|
||||||
// Migrate transaction / receipts
|
|
||||||
extraDb, err := newdb(filepath.Join(datadir, "extra"))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("state db err: %v", err)
|
|
||||||
}
|
|
||||||
defer extraDb.Close()
|
|
||||||
|
|
||||||
if extra, ok := extraDb.(*ethdb.LDBDatabase); ok {
|
|
||||||
glog.Infoln("Merging transaction database...")
|
|
||||||
|
|
||||||
it := extra.NewIterator()
|
|
||||||
for it.Next() {
|
|
||||||
database.Put(it.Key(), it.Value())
|
|
||||||
}
|
}
|
||||||
it.Release()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue