mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 21:56:43 +00:00
core: integrated fork procedure
This commit is contained in:
parent
888b5c706e
commit
064a78507e
7 changed files with 150 additions and 60 deletions
|
|
@ -66,19 +66,23 @@ func NewBlockValidator(config *ChainConfig, blockchain *BlockChain, pow pow.PoW)
|
|||
// state that might or might not be present is checked to make sure that fast
|
||||
// sync has done it's job proper. This prevents the block validator form accepting
|
||||
// false positives where a header is present but the state is not.
|
||||
func (v *BlockValidator) ValidateBlock(block *types.Block) error {
|
||||
if v.bc.HasBlock(block.Hash()) {
|
||||
if _, err := state.New(block.Root(), v.bc.chainDb); err == nil {
|
||||
func (v *BlockValidator) ValidateBlock(reader BlockReader, block *types.Block) error {
|
||||
/* wtf?
|
||||
if reader.GetBlock(block.Hash()) != nil {
|
||||
if _, err := state.New(block.Root(), v.chainDb); err == nil {
|
||||
return &KnownBlockError{block.Number(), block.Hash()}
|
||||
}
|
||||
}
|
||||
parent := v.bc.GetBlock(block.ParentHash())
|
||||
*/
|
||||
parent := reader.GetBlock(block.ParentHash())
|
||||
if parent == nil {
|
||||
return ParentError(block.ParentHash())
|
||||
}
|
||||
/* wtf?
|
||||
if _, err := state.New(parent.Root(), v.bc.chainDb); err != nil {
|
||||
return ParentError(block.ParentHash())
|
||||
}
|
||||
*/
|
||||
|
||||
header := block.Header()
|
||||
// validate the block header
|
||||
|
|
@ -86,7 +90,7 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error {
|
|||
return err
|
||||
}
|
||||
// verify the uncles are correctly rewarded
|
||||
if err := v.VerifyUncles(block, parent); err != nil {
|
||||
if err := v.VerifyUncles(reader, block, parent); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -138,15 +142,20 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
|
|||
// consensus rules to the various block headers included; it will return an
|
||||
// error if any of the included uncle headers were invalid. It returns an error
|
||||
// if the validation failed.
|
||||
func (v *BlockValidator) VerifyUncles(block, parent *types.Block) error {
|
||||
func (v *BlockValidator) VerifyUncles(reader BlockReader, block, parent *types.Block) error {
|
||||
usize := len(block.Uncles())
|
||||
if usize == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// validate that there at most 2 uncles included in this block
|
||||
if len(block.Uncles()) > 2 {
|
||||
if usize > 2 {
|
||||
return ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(block.Uncles()))
|
||||
}
|
||||
|
||||
uncles := set.New()
|
||||
ancestors := make(map[common.Hash]*types.Block)
|
||||
for _, ancestor := range v.bc.GetBlocksFromHash(block.ParentHash(), 7) {
|
||||
for _, ancestor := range reader.GetBlocksFromHash(block.ParentHash(), 7) {
|
||||
ancestors[ancestor.Hash()] = ancestor
|
||||
// Include ancestors uncles in the uncle set. Uncles must be unique.
|
||||
for _, uncle := range ancestor.Uncles() {
|
||||
|
|
@ -193,10 +202,12 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b
|
|||
if parent == nil {
|
||||
return ParentError(header.ParentHash)
|
||||
}
|
||||
/* XXX i really don't like this here
|
||||
// Short circuit if the header's already known or its parent missing
|
||||
if v.bc.HasHeader(header.Hash()) {
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -826,6 +826,10 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
nonceChecked = make([]bool, len(chain))
|
||||
statedb *state.StateDB
|
||||
)
|
||||
fork, err := Fork(self.chainDb, self.Config(), self, chain[0].ParentHash(), len(chain))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Start the parallel nonce verifier.
|
||||
nonceAbort, nonceResults := verifyNoncesFromBlocks(self.pow, chain)
|
||||
|
|
@ -838,7 +842,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
break
|
||||
}
|
||||
|
||||
bstart := time.Now()
|
||||
//bstart := time.Now()
|
||||
// Wait for block i's nonce to be verified before processing
|
||||
// its state transition.
|
||||
for !nonceChecked[i] {
|
||||
|
|
@ -857,7 +861,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
}
|
||||
// Stage 1 validation of the block using the chain's validator
|
||||
// interface.
|
||||
err := self.Validator().ValidateBlock(block)
|
||||
err := self.Validator().ValidateBlock(fork, block)
|
||||
if err != nil {
|
||||
if IsKnownBlockErr(err) {
|
||||
stats.ignored++
|
||||
|
|
@ -892,6 +896,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
// Create a new statedb using the parent block and report an
|
||||
// error if it fails.
|
||||
if statedb == nil {
|
||||
//statedb = fork.State()
|
||||
statedb, err = state.New(self.GetBlock(block.ParentHash()).Root(), self.chainDb)
|
||||
} else {
|
||||
err = statedb.Reset(chain[i-1].Root())
|
||||
|
|
@ -901,13 +906,13 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
return i, err
|
||||
}
|
||||
// Process block using the parent state as reference point.
|
||||
receipts, logs, usedGas, err := self.processor.Process(GetHashFn(block.ParentHash(), self), block, statedb, self.config.VmConfig)
|
||||
receipts, _, usedGas, err := self.processor.Process(fork.GetNumHash, block, statedb, self.config.VmConfig)
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
return i, err
|
||||
}
|
||||
// Validate the state using the default validator
|
||||
err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash()), statedb, receipts, usedGas)
|
||||
err = self.Validator().ValidateState(block, fork.GetBlock(block.ParentHash()), statedb, receipts, usedGas)
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
return i, err
|
||||
|
|
@ -918,50 +923,61 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
return i, err
|
||||
}
|
||||
|
||||
// coalesce logs for later processing
|
||||
coalescedLogs = append(coalescedLogs, logs...)
|
||||
|
||||
if err := WriteBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil {
|
||||
return i, err
|
||||
}
|
||||
|
||||
txcount += len(block.Transactions())
|
||||
// write the block to the chain and get the status
|
||||
status, err := self.WriteBlock(block)
|
||||
err = fork.CommitBlock(new(big.Int).Add(fork.GetTd(block.ParentHash()), block.Difficulty()), block, receipts)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
|
||||
switch status {
|
||||
case CanonStatTy:
|
||||
if glog.V(logger.Debug) {
|
||||
glog.Infof("[%v] inserted block #%d (%d TXs %v G %d UNCs) (%x...). Took %v\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), block.GasUsed(), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
|
||||
}
|
||||
events = append(events, ChainEvent{block, block.Hash(), logs})
|
||||
/*
|
||||
// coalesce logs for later processing
|
||||
coalescedLogs = append(coalescedLogs, logs...)
|
||||
|
||||
// This puts transactions in a extra db for rpc
|
||||
if err := WriteTransactions(self.chainDb, block); err != nil {
|
||||
if err := WriteBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil {
|
||||
return i, err
|
||||
}
|
||||
// store the receipts
|
||||
if err := WriteReceipts(self.chainDb, receipts); err != nil {
|
||||
return i, err
|
||||
}
|
||||
// Write map map bloom filters
|
||||
if err := WriteMipmapBloom(self.chainDb, block.NumberU64(), receipts); err != nil {
|
||||
return i, err
|
||||
}
|
||||
case SideStatTy:
|
||||
if glog.V(logger.Detail) {
|
||||
glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...). Took %v\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
|
||||
}
|
||||
events = append(events, ChainSideEvent{block, logs})
|
||||
|
||||
case SplitStatTy:
|
||||
events = append(events, ChainSplitEvent{block, logs})
|
||||
}
|
||||
txcount += len(block.Transactions())
|
||||
// write the block to the chain and get the status
|
||||
status, err := self.WriteBlock(block)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
|
||||
switch status {
|
||||
case CanonStatTy:
|
||||
if glog.V(logger.Debug) {
|
||||
glog.Infof("[%v] inserted block #%d (%d TXs %v G %d UNCs) (%x...). Took %v\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), block.GasUsed(), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
|
||||
}
|
||||
events = append(events, ChainEvent{block, block.Hash(), logs})
|
||||
|
||||
// This puts transactions in a extra db for rpc
|
||||
if err := WriteTransactions(self.chainDb, block); err != nil {
|
||||
return i, err
|
||||
}
|
||||
// store the receipts
|
||||
if err := WriteReceipts(self.chainDb, receipts); err != nil {
|
||||
return i, err
|
||||
}
|
||||
// Write map map bloom filters
|
||||
if err := WriteMipmapBloom(self.chainDb, block.NumberU64(), receipts); err != nil {
|
||||
return i, err
|
||||
}
|
||||
case SideStatTy:
|
||||
if glog.V(logger.Detail) {
|
||||
glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...). Took %v\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
|
||||
}
|
||||
events = append(events, ChainSideEvent{block, logs})
|
||||
|
||||
case SplitStatTy:
|
||||
events = append(events, ChainSplitEvent{block, logs})
|
||||
}
|
||||
*/
|
||||
stats.processed++
|
||||
}
|
||||
if err := fork.CommitToDb(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
fork.ApplyTo(self)
|
||||
|
||||
if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) {
|
||||
tend := time.Since(tstart)
|
||||
|
|
@ -973,6 +989,21 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
func (bc *BlockChain) Resolve(writer ethdb.Writer, changes []Changes) error {
|
||||
for _, change := range changes {
|
||||
block := change.block
|
||||
// Add the block to the canonical chain number scheme and mark as the head
|
||||
if err := WriteCanonicalHash(writer, block.Hash(), block.NumberU64()); err != nil {
|
||||
return fmt.Errorf("failed to insert block number: %v", err)
|
||||
}
|
||||
}
|
||||
if err := WriteHeadBlockHash(writer, changes[len(changes)-1].block.Hash()); err != nil {
|
||||
return fmt.Errorf("failed to insert head block hash: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
|
||||
// to be part of the new canonical chain and accumulates potential missing transactions and post an
|
||||
// event about them
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ func printChain(bc *BlockChain) {
|
|||
func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||
for _, block := range chain {
|
||||
// Try and process the block
|
||||
err := blockchain.Validator().ValidateBlock(block)
|
||||
err := blockchain.Validator().ValidateBlock(blockchain, block)
|
||||
if err != nil {
|
||||
if IsKnownBlockErr(err) {
|
||||
continue
|
||||
|
|
@ -430,7 +430,7 @@ func TestChainMultipleInsertions(t *testing.T) {
|
|||
|
||||
type bproc struct{}
|
||||
|
||||
func (bproc) ValidateBlock(*types.Block) error { return nil }
|
||||
func (bproc) ValidateBlock(BlockReader, *types.Block) error { return nil }
|
||||
func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return nil }
|
||||
func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ func GetReceipt(db ethdb.ReadWriter, txHash common.Hash) *types.Receipt {
|
|||
}
|
||||
|
||||
// WriteCanonicalHash stores the canonical hash for the given block number.
|
||||
func WriteCanonicalHash(db ethdb.ReadWriter, hash common.Hash, number uint64) error {
|
||||
func WriteCanonicalHash(db ethdb.Writer, hash common.Hash, number uint64) error {
|
||||
key := append(blockNumPrefix, big.NewInt(int64(number)).Bytes()...)
|
||||
if err := db.Put(key, hash.Bytes()); err != nil {
|
||||
glog.Fatalf("failed to store number to hash mapping into database: %v", err)
|
||||
|
|
@ -252,7 +252,7 @@ func WriteHeadHeaderHash(db ethdb.ReadWriter, hash common.Hash) error {
|
|||
}
|
||||
|
||||
// WriteHeadBlockHash stores the head block's hash.
|
||||
func WriteHeadBlockHash(db ethdb.ReadWriter, hash common.Hash) error {
|
||||
func WriteHeadBlockHash(db ethdb.Writer, hash common.Hash) error {
|
||||
if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
|
||||
glog.Fatalf("failed to store last block's hash into database: %v", err)
|
||||
return err
|
||||
|
|
|
|||
56
core/fork.go
56
core/fork.go
|
|
@ -19,6 +19,7 @@ package core
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
|
|
@ -33,7 +34,7 @@ var errUnboundedParent = errors.New("core/fork: parent hash does not match last
|
|||
// ChainResolver should implement chain resolving and should be capable
|
||||
// handling reorganisations.
|
||||
type ChainResolver interface {
|
||||
Resolve(ethdb.ReadWriter, []Changes) error
|
||||
Resolve(ethdb.Writer, []Changes) error
|
||||
}
|
||||
|
||||
// Changes are changes that have previously been applied to the forked blockchain.
|
||||
|
|
@ -46,7 +47,8 @@ type Changes struct {
|
|||
// BlockReader is the basic chain reader interface for reading blocks of the blockchain
|
||||
type BlockReader interface {
|
||||
GetBlock(common.Hash) *types.Block // GetBlock returns the block that corresponds to the given hash
|
||||
Db() ethdb.Database // XXX this doesn't really belong here.
|
||||
GetBlocksFromHash(common.Hash, int) []*types.Block
|
||||
GetTd(common.Hash) *big.Int
|
||||
}
|
||||
|
||||
// receipt keeps a log of changes for a specific block number which can
|
||||
|
|
@ -75,12 +77,13 @@ type ChainFork struct {
|
|||
|
||||
// Fork returns a new blockchain with the given database as backing layer
|
||||
// for the localised blockchain transaction.
|
||||
func Fork(config *ChainConfig, blockReader BlockReader, origin common.Hash) (*ChainFork, error) {
|
||||
func Fork(db ethdb.Database, config *ChainConfig, blockReader BlockReader, origin common.Hash, size int) (*ChainFork, error) {
|
||||
fork := &ChainFork{
|
||||
db: blockReader.Db(),
|
||||
db: db,
|
||||
reader: blockReader,
|
||||
config: config,
|
||||
hashToIdx: make(map[common.Hash]int),
|
||||
changes: make([]Changes, 0, size),
|
||||
}
|
||||
|
||||
// get the origin block from which this fork originates
|
||||
|
|
@ -143,6 +146,43 @@ func (fork *ChainFork) GetBlock(hash common.Hash) *types.Block {
|
|||
return fork.changes[idx].block
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fork *ChainFork) GetBlocksFromHash(hash common.Hash, size int) []*types.Block {
|
||||
blocks := make([]*types.Block, 0, size)
|
||||
|
||||
max := int(math.Min(float64(size), float64(len(fork.changes))))
|
||||
for i := 0; i < max; i++ {
|
||||
blocks = append(blocks, fork.changes[len(fork.changes)-(i+1)].block)
|
||||
}
|
||||
// fetch the rest from the blockchain
|
||||
if max < size {
|
||||
blocks = append(blocks, fork.origin)
|
||||
hash := fork.origin.ParentHash()
|
||||
for i := max + 1; i < size; i++ {
|
||||
block := GetBlock(fork.db, hash)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
blocks = append(blocks, block)
|
||||
hash = block.ParentHash()
|
||||
}
|
||||
}
|
||||
return blocks
|
||||
}
|
||||
|
||||
func (fork *ChainFork) GetTd(hash common.Hash) *big.Int {
|
||||
if len(fork.changes) == 0 {
|
||||
if fork.origin.Hash() == hash {
|
||||
return fork.reader.GetTd(hash)
|
||||
}
|
||||
return new(big.Int)
|
||||
}
|
||||
|
||||
if idx, ok := fork.hashToIdx[hash]; ok {
|
||||
return fork.changes[idx].td
|
||||
}
|
||||
return new(big.Int)
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -196,7 +236,7 @@ func (fork *ChainFork) CommitToDb() error {
|
|||
}
|
||||
|
||||
hash := change.block.Hash()
|
||||
if err := WriteHeader(tx, change.block.Header()); err != nil {
|
||||
if err := WriteBlock(tx, change.block); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := WriteTd(tx, hash, change.td); err != nil {
|
||||
|
|
@ -231,7 +271,11 @@ func (fork *ChainFork) ApplyTo(resolver ChainResolver) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return resolver.Resolve(tx, fork.changes)
|
||||
err = resolver.Resolve(tx, fork.changes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// NewUnsealedBlock creates a new unsealed block using the last block in the fork
|
||||
|
|
|
|||
|
|
@ -20,8 +20,12 @@ func newFakeChain() fakeChain {
|
|||
}
|
||||
}
|
||||
|
||||
func (c fakeChain) Db() ethdb.Database {
|
||||
return c.db
|
||||
func (c fakeChain) GetTd(common.Hash) *big.Int {
|
||||
return new(big.Int)
|
||||
}
|
||||
|
||||
func (c fakeChain) GetBlocksFromHash(common.Hash, int) []*types.Block {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c fakeChain) GetBlock(hash common.Hash) *types.Block {
|
||||
|
|
@ -33,7 +37,7 @@ func TestGetNumHash(t *testing.T) {
|
|||
genesis := WriteGenesisBlockForTesting(chain.db)
|
||||
config := &ChainConfig{HomesteadBlock: new(big.Int)}
|
||||
|
||||
fork, err := Fork(config, chain, genesis.Hash())
|
||||
fork, err := Fork(chain.db, config, chain, genesis.Hash(), 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ import (
|
|||
// gas used. The implementer should decide what to do with the given input.
|
||||
type Validator interface {
|
||||
HeaderValidator
|
||||
ValidateBlock(block *types.Block) error
|
||||
ValidateBlock(reader BlockReader, block *types.Block) error
|
||||
ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue