mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 14:16:44 +00:00
core: chain tracking
This commit is contained in:
parent
dae51d9a84
commit
25fddfb76f
4 changed files with 147 additions and 59 deletions
|
|
@ -353,7 +353,7 @@ func WriteBlockReceipts(db ethdb.ReadWriter, hash common.Hash, receipts types.Re
|
||||||
// into the given database. Beside writing the transaction, the function also
|
// into the given database. Beside writing the transaction, the function also
|
||||||
// stores a metadata entry along with the transaction, detailing the position
|
// stores a metadata entry along with the transaction, detailing the position
|
||||||
// of this within the blockchain.
|
// of this within the blockchain.
|
||||||
func WriteBlockTransactions(db ethdb.ReadWriter, header types.Header, transactions types.Transactions) error {
|
func WriteBlockTransactions(db ethdb.ReadWriter, header *types.Header, transactions types.Transactions) error {
|
||||||
// Iterate over each transaction and encode it with its metadata
|
// Iterate over each transaction and encode it with its metadata
|
||||||
for i, tx := range transactions {
|
for i, tx := range transactions {
|
||||||
// Encode and queue up the transaction for storage
|
// Encode and queue up the transaction for storage
|
||||||
|
|
|
||||||
156
core/fork.go
156
core/fork.go
|
|
@ -17,11 +17,13 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
)
|
)
|
||||||
|
|
@ -34,10 +36,9 @@ type ChainResolver interface {
|
||||||
|
|
||||||
// Changes are changes that have previously been applied to the forked blockchain.
|
// Changes are changes that have previously been applied to the forked blockchain.
|
||||||
type Changes struct {
|
type Changes struct {
|
||||||
td *big.Int // total difficulty of the block
|
td *big.Int // total difficulty of the block
|
||||||
header types.Header // block's header
|
block *types.Block // the block
|
||||||
txs types.Transactions // block's transactions
|
receipts types.Receipts // block's receipts after processing transactions
|
||||||
receipts types.Receipts // block's receipts after processing transactions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockReader is the basic chain reader interface for reading blocks of the blockchain
|
// BlockReader is the basic chain reader interface for reading blocks of the blockchain
|
||||||
|
|
@ -88,10 +89,11 @@ type receipt struct {
|
||||||
// fork.CommitBlock(block)
|
// fork.CommitBlock(block)
|
||||||
//
|
//
|
||||||
type ChainFork struct {
|
type ChainFork struct {
|
||||||
db ethdb.Database // backing database
|
db ethdb.Database // backing database
|
||||||
tx ethdb.Transaction // current operating transaction
|
reader BlockReader // block reader utility interface
|
||||||
reader BlockReader // block reader utility interface
|
state *state.StateDB // the current state database
|
||||||
|
|
||||||
|
originN uint64 // the origin block number
|
||||||
origin *types.Block // origin notes the start of the fork
|
origin *types.Block // origin notes the start of the fork
|
||||||
currentBlock *types.Block // the current block within this transaction
|
currentBlock *types.Block // the current block within this transaction
|
||||||
|
|
||||||
|
|
@ -105,72 +107,82 @@ func Fork(blockReader BlockReader, origin common.Hash) (*ChainFork, error) {
|
||||||
db: blockReader.Db(),
|
db: blockReader.Db(),
|
||||||
reader: blockReader,
|
reader: blockReader,
|
||||||
}
|
}
|
||||||
// open a new leveldb transaction
|
|
||||||
tx, err := fork.db.OpenTransaction()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
fork.tx = tx
|
|
||||||
|
|
||||||
// get the origin block from which this fork originates
|
// get the origin block from which this fork originates
|
||||||
if fork.origin = blockReader.GetBlock(origin); fork.origin == nil {
|
if fork.origin = blockReader.GetBlock(origin); fork.origin == nil {
|
||||||
return nil, fmt.Errorf("core/fork: no block found with hash: %x", origin)
|
return nil, fmt.Errorf("core/fork: no block found with hash: %x", origin)
|
||||||
}
|
}
|
||||||
|
fork.originN = fork.origin.NumberU64()
|
||||||
|
|
||||||
|
statedb, err := state.New(fork.origin.Root(), fork.db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("core/fork: enable to create state: %v", err)
|
||||||
|
}
|
||||||
|
fork.state = statedb
|
||||||
|
|
||||||
return fork, nil
|
return fork, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitChanges commits the changes to the fork and takes care of the writing
|
// GetNumHash returns the hash of the block that corresponds to the block number
|
||||||
// to the tx (e.g. blocks, block receipts, transactions, etc.).
|
// in our current fork
|
||||||
func (fork *ChainFork) CommitChanges(td *big.Int, header types.Header, transactions types.Transactions, receipts types.Receipts) error {
|
func (fork *ChainFork) GetNumHash(n uint64) common.Hash {
|
||||||
hash := header.Hash()
|
// Short circuit if the number is larger than our chain.
|
||||||
|
if n > uint64(len(fork.changes))+fork.originN {
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we should have it cached
|
||||||
|
if n > fork.originN {
|
||||||
|
return fork.changes[n-fork.originN].block.Hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise search in the database
|
||||||
|
for block := fork.reader.GetBlock(fork.origin.Hash()); block != nil; block = fork.reader.GetBlock(block.ParentHash()) {
|
||||||
|
if block.NumberU64() == n {
|
||||||
|
return block.Hash()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fork *ChainFork) State() *state.StateDB {
|
||||||
|
return fork.state
|
||||||
|
}
|
||||||
|
|
||||||
|
var errUnboundedParent = errors.New("core/fork: parent hash does not match last block")
|
||||||
|
|
||||||
|
// CommitBlock commits a new block to the fork. The block that's being commited their parent hash must
|
||||||
|
// match the previously committed block or the origin if the fork is empty.
|
||||||
|
func (fork *ChainFork) CommitBlock(td *big.Int, block *types.Block, receipts types.Receipts) error {
|
||||||
|
// Check and make sure that the block being applied is valid and can be applied
|
||||||
|
// on the last block.
|
||||||
|
if len(fork.changes) == 0 && block.ParentHash() != fork.origin.Hash() {
|
||||||
|
return errUnboundedParent
|
||||||
|
} else if len(fork.changes) > 0 && block.ParentHash() != fork.changes[len(fork.changes)-1].block.Hash() {
|
||||||
|
return errUnboundedParent
|
||||||
|
}
|
||||||
|
|
||||||
fork.changes = append(fork.changes, Changes{
|
fork.changes = append(fork.changes, Changes{
|
||||||
td: td,
|
td: td,
|
||||||
header: header,
|
block: block,
|
||||||
txs: transactions,
|
|
||||||
receipts: receipts,
|
receipts: receipts,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err := WriteHeader(fork.tx, &header); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := WriteTd(fork.tx, hash, td); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(receipts) > 0 {
|
|
||||||
if err := WriteBlockReceipts(fork.tx, hash, receipts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(transactions) > 0 {
|
|
||||||
if err := WriteBlockTransactions(fork.tx, header, transactions); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteBlockBody writes the gives block to the database transaction.
|
|
||||||
func (fork *ChainFork) WriteBlockBody(hash common.Hash, body types.Body) error {
|
|
||||||
// Store the body first to retain database consistency
|
|
||||||
if err := WriteBody(fork.tx, hash, &body); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitTo commits the current changes to the database
|
// CommitTo commits the current changes to the database
|
||||||
func (fork *ChainFork) CommitToDb() error {
|
func (fork *ChainFork) CommitToDb() error {
|
||||||
|
tx, err := fork.db.OpenTransaction()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("core/fork: unable to create db transaction: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// write mips to the datatabase transaction
|
// write mips to the datatabase transaction
|
||||||
cachedMips := make(map[string]types.Bloom)
|
cachedMips := make(map[string]types.Bloom)
|
||||||
for _, level := range MIPMapLevels {
|
for _, change := range fork.changes {
|
||||||
for _, change := range fork.changes {
|
for _, level := range MIPMapLevels {
|
||||||
var (
|
var (
|
||||||
key = mipmapKey(change.header.Number.Uint64(), level)
|
key = mipmapKey(change.block.NumberU64(), level)
|
||||||
mipmap types.Bloom
|
mipmap types.Bloom
|
||||||
ok bool
|
ok bool
|
||||||
)
|
)
|
||||||
|
|
@ -186,32 +198,58 @@ func (fork *ChainFork) CommitToDb() error {
|
||||||
}
|
}
|
||||||
cachedMips[string(key)] = mipmap
|
cachedMips[string(key)] = mipmap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hash := change.block.Hash()
|
||||||
|
if err := WriteHeader(tx, change.block.Header()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := WriteTd(tx, hash, change.td); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(change.receipts) > 0 {
|
||||||
|
if err := WriteBlockReceipts(tx, hash, change.receipts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
txs := change.block.Transactions()
|
||||||
|
if len(txs) > 0 {
|
||||||
|
if err := WriteBlockTransactions(tx, change.block.Header(), txs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// write out mip maps
|
// write out mip maps
|
||||||
for key, mipmap := range cachedMips {
|
for key, mipmap := range cachedMips {
|
||||||
fork.tx.Put([]byte(key), mipmap.Bytes())
|
tx.Put([]byte(key), mipmap.Bytes())
|
||||||
}
|
}
|
||||||
return fork.tx.Commit()
|
return tx.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyTo applies the fork to chain and uses the resolver to write and resolve
|
// ApplyTo applies the fork to chain and uses the resolver to write and resolve
|
||||||
// any chain reorganisations.
|
// any chain reorganisations.
|
||||||
func (fork *ChainFork) ApplyTo(resolver ChainResolver) error {
|
func (fork *ChainFork) ApplyTo(resolver ChainResolver) error {
|
||||||
return resolver.Resolve(fork.tx, fork.changes)
|
tx, err := fork.db.OpenTransaction()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return resolver.Resolve(tx, fork.changes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUnsealedBlock creates a new unsealed block using the last block in the fork
|
// NewUnsealedBlock creates a new unsealed block using the last block in the fork
|
||||||
// as its parent.
|
// as its parent.
|
||||||
func (fork *ChainFork) NewUnsealedBlock(coinbase common.Address, extra []byte) *UnsealedBlock {
|
func (fork *ChainFork) NewUnsealedBlock(coinbase common.Address, extra []byte) *UnsealedBlock {
|
||||||
var (
|
var (
|
||||||
parent types.Header
|
parent *types.Header
|
||||||
unsealedBlock = new(UnsealedBlock)
|
unsealedBlock = new(UnsealedBlock)
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(fork.changes) == 0 {
|
if len(fork.changes) == 0 {
|
||||||
parent = *fork.origin.Header()
|
parent = fork.origin.Header()
|
||||||
} else {
|
} else {
|
||||||
parent = fork.changes[len(fork.changes)-1].header
|
parent = fork.changes[len(fork.changes)-1].block.Header()
|
||||||
}
|
}
|
||||||
|
|
||||||
tstamp := time.Now().Unix()
|
tstamp := time.Now().Unix()
|
||||||
|
|
@ -220,14 +258,16 @@ func (fork *ChainFork) NewUnsealedBlock(coinbase common.Address, extra []byte) *
|
||||||
}
|
}
|
||||||
|
|
||||||
unsealedBlock.header = types.Header{
|
unsealedBlock.header = types.Header{
|
||||||
|
Root: fork.state.IntermediateRoot(),
|
||||||
ParentHash: parent.Hash(),
|
ParentHash: parent.Hash(),
|
||||||
Number: new(big.Int).Add(parent.Number, common.Big1),
|
Number: new(big.Int).Add(parent.Number, common.Big1),
|
||||||
Difficulty: CalcDifficulty(nil, uint64(tstamp), parent.Time.Uint64(), parent.Number, parent.Difficulty),
|
Difficulty: CalcDifficulty(nil, uint64(tstamp), parent.Time.Uint64(), parent.Number, parent.Difficulty),
|
||||||
GasLimit: CalcGasLimit(types.NewBlockWithHeader(&parent)),
|
GasLimit: CalcGasLimit(types.NewBlockWithHeader(parent)),
|
||||||
GasUsed: new(big.Int),
|
GasUsed: new(big.Int),
|
||||||
Coinbase: coinbase,
|
Coinbase: coinbase,
|
||||||
Extra: extra,
|
Extra: extra,
|
||||||
Time: big.NewInt(tstamp),
|
Time: big.NewInt(tstamp),
|
||||||
}
|
}
|
||||||
|
|
||||||
return unsealedBlock
|
return unsealedBlock
|
||||||
}
|
}
|
||||||
|
|
|
||||||
47
core/fork_test.go
Normal file
47
core/fork_test.go
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fakeChain struct {
|
||||||
|
db *ethdb.LDBDatabase
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFakeChain() fakeChain {
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
return fakeChain{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c fakeChain) Db() ethdb.Database {
|
||||||
|
return c.db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c fakeChain) GetBlock(hash common.Hash) *types.Block {
|
||||||
|
return GetBlock(c.db, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetNumHash(t *testing.T) {
|
||||||
|
chain := newFakeChain()
|
||||||
|
genesis := WriteGenesisBlockForTesting(chain.db)
|
||||||
|
|
||||||
|
fork, err := Fork(chain, genesis.Hash())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
genesisHash := fork.GetNumHash(0)
|
||||||
|
if genesisHash != genesis.Hash() {
|
||||||
|
t.Error("genesis hash failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if fork.GetNumHash(1) != (common.Hash{}) {
|
||||||
|
t.Error("expected exmpty hash to be returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import "github.com/ethereum/go-ethereum/core/types"
|
||||||
type UnsealedBlock struct {
|
type UnsealedBlock struct {
|
||||||
header types.Header // block header
|
header types.Header // block header
|
||||||
transactions types.Transactions // transactions previously applied
|
transactions types.Transactions // transactions previously applied
|
||||||
|
receipts types.Receipts // receipts generated by applying transactions
|
||||||
uncles []types.Header // uncles previously applied
|
uncles []types.Header // uncles previously applied
|
||||||
|
|
||||||
gasPool *GasPool // gas pool for this block
|
gasPool *GasPool // gas pool for this block
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue