forked from cerc-io/plugeth
core, eth, rpc: split out block validator and state processor
This removes the burden on a single object to take care of all validation and state processing. Now instead the validation is done by the `core.BlockValidator` (`types.Validator`) that takes care of both header and uncle validation through the `ValidateBlock` method and state validation through the `ValidateState` method. The state processing is done by a new object `core.StateProcessor` (`types.Processor`) and accepts a new state as input and uses that to process the given block's transactions (and uncles for rewords) to calculate the state root for the next block (P_n + 1).
This commit is contained in:
parent
9422eec554
commit
a1d9ef48c5
@ -557,8 +557,6 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database
|
|||||||
Fatalf("Could not start chainmanager: %v", err)
|
Fatalf("Could not start chainmanager: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
proc := core.NewBlockProcessor(chainDb, pow, chain, eventMux)
|
|
||||||
chain.SetProcessor(proc)
|
|
||||||
return chain, chainDb
|
return chain, chainDb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +169,6 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
|||||||
// State and blocks are stored in the same DB.
|
// State and blocks are stored in the same DB.
|
||||||
evmux := new(event.TypeMux)
|
evmux := new(event.TypeMux)
|
||||||
chainman, _ := NewBlockChain(db, FakePow{}, evmux)
|
chainman, _ := NewBlockChain(db, FakePow{}, evmux)
|
||||||
chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux))
|
|
||||||
defer chainman.Stop()
|
defer chainman.Stop()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
@ -1,460 +0,0 @@
|
|||||||
// Copyright 2014 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"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/vm"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/event"
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
"github.com/ethereum/go-ethereum/pow"
|
|
||||||
"gopkg.in/fatih/set.v0"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// must be bumped when consensus algorithm is changed, this forces the upgradedb
|
|
||||||
// command to be run (forces the blocks to be imported again using the new algorithm)
|
|
||||||
BlockChainVersion = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
type BlockProcessor struct {
|
|
||||||
chainDb ethdb.Database
|
|
||||||
// Mutex for locking the block processor. Blocks can only be handled one at a time
|
|
||||||
mutex sync.Mutex
|
|
||||||
// Canonical block chain
|
|
||||||
bc *BlockChain
|
|
||||||
// non-persistent key/value memory storage
|
|
||||||
mem map[string]*big.Int
|
|
||||||
// Proof of work used for validating
|
|
||||||
Pow pow.PoW
|
|
||||||
|
|
||||||
events event.Subscription
|
|
||||||
|
|
||||||
eventMux *event.TypeMux
|
|
||||||
}
|
|
||||||
|
|
||||||
// GasPool tracks the amount of gas available during
|
|
||||||
// execution of the transactions in a block.
|
|
||||||
// The zero value is a pool with zero gas available.
|
|
||||||
type GasPool big.Int
|
|
||||||
|
|
||||||
// AddGas makes gas available for execution.
|
|
||||||
func (gp *GasPool) AddGas(amount *big.Int) *GasPool {
|
|
||||||
i := (*big.Int)(gp)
|
|
||||||
i.Add(i, amount)
|
|
||||||
return gp
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubGas deducts the given amount from the pool if enough gas is
|
|
||||||
// available and returns an error otherwise.
|
|
||||||
func (gp *GasPool) SubGas(amount *big.Int) error {
|
|
||||||
i := (*big.Int)(gp)
|
|
||||||
if i.Cmp(amount) < 0 {
|
|
||||||
return &GasLimitErr{Have: new(big.Int).Set(i), Want: amount}
|
|
||||||
}
|
|
||||||
i.Sub(i, amount)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gp *GasPool) String() string {
|
|
||||||
return (*big.Int)(gp).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBlockProcessor(db ethdb.Database, pow pow.PoW, blockchain *BlockChain, eventMux *event.TypeMux) *BlockProcessor {
|
|
||||||
sm := &BlockProcessor{
|
|
||||||
chainDb: db,
|
|
||||||
mem: make(map[string]*big.Int),
|
|
||||||
Pow: pow,
|
|
||||||
bc: blockchain,
|
|
||||||
eventMux: eventMux,
|
|
||||||
}
|
|
||||||
return sm
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) {
|
|
||||||
gp := new(GasPool).AddGas(block.GasLimit())
|
|
||||||
if glog.V(logger.Core) {
|
|
||||||
glog.Infof("%x: gas (+ %v)", block.Coinbase(), gp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the transactions on to parent state
|
|
||||||
receipts, err = sm.ApplyTransactions(gp, statedb, block, block.Transactions(), transientProcess)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return receipts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *BlockProcessor) ApplyTransaction(gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
|
|
||||||
_, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, header), tx, gp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the state with pending changes
|
|
||||||
usedGas.Add(usedGas, gas)
|
|
||||||
receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas)
|
|
||||||
receipt.TxHash = tx.Hash()
|
|
||||||
receipt.GasUsed = new(big.Int).Set(gas)
|
|
||||||
if MessageCreatesContract(tx) {
|
|
||||||
from, _ := tx.From()
|
|
||||||
receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
|
|
||||||
}
|
|
||||||
|
|
||||||
logs := statedb.GetLogs(tx.Hash())
|
|
||||||
receipt.Logs = logs
|
|
||||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
|
||||||
|
|
||||||
glog.V(logger.Debug).Infoln(receipt)
|
|
||||||
|
|
||||||
// Notify all subscribers
|
|
||||||
if !transientProcess {
|
|
||||||
go self.eventMux.Post(TxPostEvent{tx})
|
|
||||||
go self.eventMux.Post(logs)
|
|
||||||
}
|
|
||||||
|
|
||||||
return receipt, gas, err
|
|
||||||
}
|
|
||||||
func (self *BlockProcessor) BlockChain() *BlockChain {
|
|
||||||
return self.bc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *BlockProcessor) ApplyTransactions(gp *GasPool, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, error) {
|
|
||||||
var (
|
|
||||||
receipts types.Receipts
|
|
||||||
totalUsedGas = big.NewInt(0)
|
|
||||||
err error
|
|
||||||
cumulativeSum = new(big.Int)
|
|
||||||
header = block.Header()
|
|
||||||
)
|
|
||||||
|
|
||||||
for i, tx := range txs {
|
|
||||||
statedb.StartRecord(tx.Hash(), block.Hash(), i)
|
|
||||||
|
|
||||||
receipt, txGas, err := self.ApplyTransaction(gp, statedb, header, tx, totalUsedGas, transientProcess)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
glog.V(logger.Core).Infoln("TX err:", err)
|
|
||||||
}
|
|
||||||
receipts = append(receipts, receipt)
|
|
||||||
|
|
||||||
cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if block.GasUsed().Cmp(totalUsedGas) != 0 {
|
|
||||||
return nil, ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), totalUsedGas))
|
|
||||||
}
|
|
||||||
|
|
||||||
if transientProcess {
|
|
||||||
go self.eventMux.Post(PendingBlockEvent{block, statedb.Logs()})
|
|
||||||
}
|
|
||||||
|
|
||||||
return receipts, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs vm.Logs, err error) {
|
|
||||||
// Processing a blocks may never happen simultaneously
|
|
||||||
sm.mutex.Lock()
|
|
||||||
defer sm.mutex.Unlock()
|
|
||||||
|
|
||||||
if !sm.bc.HasBlock(block.ParentHash()) {
|
|
||||||
return nil, ParentError(block.ParentHash())
|
|
||||||
}
|
|
||||||
parent := sm.bc.GetBlock(block.ParentHash())
|
|
||||||
|
|
||||||
// FIXME Change to full header validation. See #1225
|
|
||||||
errch := make(chan bool)
|
|
||||||
go func() { errch <- sm.Pow.Verify(block) }()
|
|
||||||
|
|
||||||
logs, _, err = sm.processWithParent(block, parent)
|
|
||||||
if !<-errch {
|
|
||||||
return nil, ValidationError("Block's nonce is invalid (= %x)", block.Nonce)
|
|
||||||
}
|
|
||||||
|
|
||||||
return logs, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process block will attempt to process the given block's transactions and applies them
|
|
||||||
// on top of the block's parent state (given it exists) and will return wether it was
|
|
||||||
// successful or not.
|
|
||||||
func (sm *BlockProcessor) Process(block *types.Block) (logs vm.Logs, receipts types.Receipts, err error) {
|
|
||||||
// Processing a blocks may never happen simultaneously
|
|
||||||
sm.mutex.Lock()
|
|
||||||
defer sm.mutex.Unlock()
|
|
||||||
|
|
||||||
if sm.bc.HasBlock(block.Hash()) {
|
|
||||||
if _, err := state.New(block.Root(), sm.chainDb); err == nil {
|
|
||||||
return nil, nil, &KnownBlockError{block.Number(), block.Hash()}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if parent := sm.bc.GetBlock(block.ParentHash()); parent != nil {
|
|
||||||
if _, err := state.New(parent.Root(), sm.chainDb); err == nil {
|
|
||||||
return sm.processWithParent(block, parent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, nil, ParentError(block.ParentHash())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs vm.Logs, receipts types.Receipts, err error) {
|
|
||||||
// Create a new state based on the parent's root (e.g., create copy)
|
|
||||||
state, err := state.New(parent.Root(), sm.chainDb)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
header := block.Header()
|
|
||||||
uncles := block.Uncles()
|
|
||||||
txs := block.Transactions()
|
|
||||||
|
|
||||||
// Block validation
|
|
||||||
if err = ValidateHeader(sm.Pow, header, parent.Header(), false, false); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// There can be at most two uncles
|
|
||||||
if len(uncles) > 2 {
|
|
||||||
return nil, nil, ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(uncles))
|
|
||||||
}
|
|
||||||
|
|
||||||
receipts, err = sm.TransitionState(state, parent, block, false)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the received block's bloom with the one derived from the generated receipts.
|
|
||||||
// For valid blocks this should always validate to true.
|
|
||||||
rbloom := types.CreateBloom(receipts)
|
|
||||||
if rbloom != header.Bloom {
|
|
||||||
err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]]))
|
|
||||||
// can be used by light clients to make sure they've received the correct Txs
|
|
||||||
txSha := types.DeriveSha(txs)
|
|
||||||
if txSha != header.TxHash {
|
|
||||||
err = fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
|
|
||||||
receiptSha := types.DeriveSha(receipts)
|
|
||||||
if receiptSha != header.ReceiptHash {
|
|
||||||
err = fmt.Errorf("invalid receipt root hash. received=%x calculated=%x", header.ReceiptHash, receiptSha)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify UncleHash before running other uncle validations
|
|
||||||
unclesSha := types.CalcUncleHash(uncles)
|
|
||||||
if unclesSha != header.UncleHash {
|
|
||||||
err = fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify uncles
|
|
||||||
if err = sm.VerifyUncles(state, block, parent); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Accumulate static rewards; block reward, uncle's and uncle inclusion.
|
|
||||||
AccumulateRewards(state, header, uncles)
|
|
||||||
|
|
||||||
// Commit state objects/accounts to a database batch and calculate
|
|
||||||
// the state root. The database is not modified if the root
|
|
||||||
// doesn't match.
|
|
||||||
root, batch := state.CommitBatch()
|
|
||||||
if header.Root != root {
|
|
||||||
return nil, nil, fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the database writes.
|
|
||||||
batch.Write()
|
|
||||||
|
|
||||||
return state.Logs(), receipts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
big8 = big.NewInt(8)
|
|
||||||
big32 = big.NewInt(32)
|
|
||||||
)
|
|
||||||
|
|
||||||
// AccumulateRewards credits the coinbase of the given block with the
|
|
||||||
// mining reward. The total reward consists of the static block reward
|
|
||||||
// and rewards for included uncles. The coinbase of each uncle block is
|
|
||||||
// also rewarded.
|
|
||||||
func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) {
|
|
||||||
reward := new(big.Int).Set(BlockReward)
|
|
||||||
r := new(big.Int)
|
|
||||||
for _, uncle := range uncles {
|
|
||||||
r.Add(uncle.Number, big8)
|
|
||||||
r.Sub(r, header.Number)
|
|
||||||
r.Mul(r, BlockReward)
|
|
||||||
r.Div(r, big8)
|
|
||||||
statedb.AddBalance(uncle.Coinbase, r)
|
|
||||||
|
|
||||||
r.Div(BlockReward, big32)
|
|
||||||
reward.Add(reward, r)
|
|
||||||
}
|
|
||||||
statedb.AddBalance(header.Coinbase, reward)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *types.Block) error {
|
|
||||||
uncles := set.New()
|
|
||||||
ancestors := make(map[common.Hash]*types.Block)
|
|
||||||
for _, ancestor := range sm.bc.GetBlocksFromHash(block.ParentHash(), 7) {
|
|
||||||
ancestors[ancestor.Hash()] = ancestor
|
|
||||||
// Include ancestors uncles in the uncle set. Uncles must be unique.
|
|
||||||
for _, uncle := range ancestor.Uncles() {
|
|
||||||
uncles.Add(uncle.Hash())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ancestors[block.Hash()] = block
|
|
||||||
uncles.Add(block.Hash())
|
|
||||||
|
|
||||||
for i, uncle := range block.Uncles() {
|
|
||||||
hash := uncle.Hash()
|
|
||||||
if uncles.Has(hash) {
|
|
||||||
// Error not unique
|
|
||||||
return UncleError("uncle[%d](%x) not unique", i, hash[:4])
|
|
||||||
}
|
|
||||||
uncles.Add(hash)
|
|
||||||
|
|
||||||
if ancestors[hash] != nil {
|
|
||||||
branch := fmt.Sprintf(" O - %x\n |\n", block.Hash())
|
|
||||||
for h := range ancestors {
|
|
||||||
branch += fmt.Sprintf(" O - %x\n |\n", h)
|
|
||||||
}
|
|
||||||
glog.Infoln(branch)
|
|
||||||
return UncleError("uncle[%d](%x) is ancestor", i, hash[:4])
|
|
||||||
}
|
|
||||||
|
|
||||||
if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == parent.Hash() {
|
|
||||||
return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4])
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ValidateHeader(sm.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil {
|
|
||||||
return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBlockReceipts returns the receipts beloniging to the block hash
|
|
||||||
func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts {
|
|
||||||
if block := sm.BlockChain().GetBlock(bhash); block != nil {
|
|
||||||
return GetBlockReceipts(sm.chainDb, block.Hash())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLogs returns the logs of the given block. This method is using a two step approach
|
|
||||||
// where it tries to get it from the (updated) method which gets them from the receipts or
|
|
||||||
// the depricated way by re-processing the block.
|
|
||||||
func (sm *BlockProcessor) GetLogs(block *types.Block) (logs vm.Logs, err error) {
|
|
||||||
receipts := GetBlockReceipts(sm.chainDb, block.Hash())
|
|
||||||
// coalesce logs
|
|
||||||
for _, receipt := range receipts {
|
|
||||||
logs = append(logs, receipt.Logs...)
|
|
||||||
}
|
|
||||||
return logs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateHeader verifies the validity of a header, relying on the database and
|
|
||||||
// POW behind the block processor.
|
|
||||||
func (sm *BlockProcessor) ValidateHeader(header *types.Header, checkPow, uncle bool) error {
|
|
||||||
// Short circuit if the header's already known or its parent missing
|
|
||||||
if sm.bc.HasHeader(header.Hash()) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if parent := sm.bc.GetHeader(header.ParentHash); parent == nil {
|
|
||||||
return ParentError(header.ParentHash)
|
|
||||||
} else {
|
|
||||||
return ValidateHeader(sm.Pow, header, parent, checkPow, uncle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateHeaderWithParent verifies the validity of a header, relying on the database and
|
|
||||||
// POW behind the block processor.
|
|
||||||
func (sm *BlockProcessor) ValidateHeaderWithParent(header, parent *types.Header, checkPow, uncle bool) error {
|
|
||||||
if sm.bc.HasHeader(header.Hash()) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ValidateHeader(sm.Pow, header, parent, checkPow, uncle)
|
|
||||||
}
|
|
||||||
|
|
||||||
// See YP section 4.3.4. "Block Header Validity"
|
|
||||||
// Validates a header. Returns an error if the header is invalid.
|
|
||||||
func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
|
|
||||||
if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
|
|
||||||
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
|
|
||||||
}
|
|
||||||
if uncle {
|
|
||||||
if header.Time.Cmp(common.MaxBig) == 1 {
|
|
||||||
return BlockTSTooBigErr
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if header.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 {
|
|
||||||
return BlockFutureErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if header.Time.Cmp(parent.Time) != 1 {
|
|
||||||
return BlockEqualTSErr
|
|
||||||
}
|
|
||||||
|
|
||||||
expd := CalcDifficulty(header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty)
|
|
||||||
if expd.Cmp(header.Difficulty) != 0 {
|
|
||||||
return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd)
|
|
||||||
}
|
|
||||||
|
|
||||||
a := new(big.Int).Set(parent.GasLimit)
|
|
||||||
a = a.Sub(a, header.GasLimit)
|
|
||||||
a.Abs(a)
|
|
||||||
b := new(big.Int).Set(parent.GasLimit)
|
|
||||||
b = b.Div(b, params.GasLimitBoundDivisor)
|
|
||||||
if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) {
|
|
||||||
return fmt.Errorf("GasLimit check failed for header %v (%v > %v)", header.GasLimit, a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
num := new(big.Int).Set(parent.Number)
|
|
||||||
num.Sub(header.Number, num)
|
|
||||||
if num.Cmp(big.NewInt(1)) != 0 {
|
|
||||||
return BlockNumberErr
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkPow {
|
|
||||||
// Verify the nonce of the header. Return an error if it's not valid
|
|
||||||
if !pow.Verify(types.NewBlockWithHeader(header)) {
|
|
||||||
return &BlockNonceErr{Hash: header.Hash(), Number: header.Number, Nonce: header.Nonce.Uint64()}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
243
core/block_validator.go
Normal file
243
core/block_validator.go
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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/logger/glog"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
|
"gopkg.in/fatih/set.v0"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BlockValidator is responsible for validating block headers, uncles and
|
||||||
|
// processed state.
|
||||||
|
//
|
||||||
|
// BlockValidator implements Validator.
|
||||||
|
type BlockValidator struct {
|
||||||
|
bc *BlockChain // Canonical block chain
|
||||||
|
Pow pow.PoW // Proof of work used for validating
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockValidator returns a new block validator which is safe for re-use
|
||||||
|
func NewBlockValidator(blockchain *BlockChain, pow pow.PoW) *BlockValidator {
|
||||||
|
validator := &BlockValidator{
|
||||||
|
Pow: pow,
|
||||||
|
bc: blockchain,
|
||||||
|
}
|
||||||
|
return validator
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateBlock validates the given block's header and uncles and verifies the
|
||||||
|
// the block header's transaction and uncle roots.
|
||||||
|
//
|
||||||
|
// ValidateBlock does not validate the header's pow. The pow work validated
|
||||||
|
// seperately so we can process them in paralel.
|
||||||
|
//
|
||||||
|
// ValidateBlock also validates and makes sure that any previous state (or present)
|
||||||
|
// 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 {
|
||||||
|
return &KnownBlockError{block.Number(), block.Hash()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parent := v.bc.GetBlock(block.ParentHash())
|
||||||
|
if parent == nil {
|
||||||
|
return ParentError(block.ParentHash())
|
||||||
|
}
|
||||||
|
if _, err := state.New(parent.Root(), v.bc.chainDb); err != nil {
|
||||||
|
return ParentError(block.ParentHash())
|
||||||
|
}
|
||||||
|
|
||||||
|
header := block.Header()
|
||||||
|
// validate the block header
|
||||||
|
if err := ValidateHeader(v.Pow, header, parent.Header(), false, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// verify the uncles are correctly rewarded
|
||||||
|
if err := v.VerifyUncles(block, parent); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify UncleHash before running other uncle validations
|
||||||
|
unclesSha := types.CalcUncleHash(block.Uncles())
|
||||||
|
if unclesSha != header.UncleHash {
|
||||||
|
return fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]]))
|
||||||
|
// can be used by light clients to make sure they've received the correct Txs
|
||||||
|
txSha := types.DeriveSha(block.Transactions())
|
||||||
|
if txSha != header.TxHash {
|
||||||
|
return fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateState validates the various changes that happen after a state
|
||||||
|
// transition, such as amount of used gas, the receipt roots and the state root
|
||||||
|
// itself. ValidateState returns a database batch if the validation was a succes
|
||||||
|
// otherwise nil and an error is returned.
|
||||||
|
func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas *big.Int) (err error) {
|
||||||
|
header := block.Header()
|
||||||
|
if block.GasUsed().Cmp(usedGas) != 0 {
|
||||||
|
return ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), usedGas))
|
||||||
|
}
|
||||||
|
// Validate the received block's bloom with the one derived from the generated receipts.
|
||||||
|
// For valid blocks this should always validate to true.
|
||||||
|
rbloom := types.CreateBloom(receipts)
|
||||||
|
if rbloom != header.Bloom {
|
||||||
|
return fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
|
||||||
|
}
|
||||||
|
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
|
||||||
|
receiptSha := types.DeriveSha(receipts)
|
||||||
|
if receiptSha != header.ReceiptHash {
|
||||||
|
return fmt.Errorf("invalid receipt root hash. received=%x calculated=%x", header.ReceiptHash, receiptSha)
|
||||||
|
}
|
||||||
|
// Validate the state root against the received state root and throw
|
||||||
|
// an error if they don't match.
|
||||||
|
if root := statedb.IntermediateRoot(); header.Root != root {
|
||||||
|
return fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyUncles verifies the given block's uncles and applies the Ethereum
|
||||||
|
// 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 {
|
||||||
|
// validate that there at most 2 uncles included in this block
|
||||||
|
if len(block.Uncles()) > 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) {
|
||||||
|
ancestors[ancestor.Hash()] = ancestor
|
||||||
|
// Include ancestors uncles in the uncle set. Uncles must be unique.
|
||||||
|
for _, uncle := range ancestor.Uncles() {
|
||||||
|
uncles.Add(uncle.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ancestors[block.Hash()] = block
|
||||||
|
uncles.Add(block.Hash())
|
||||||
|
|
||||||
|
for i, uncle := range block.Uncles() {
|
||||||
|
hash := uncle.Hash()
|
||||||
|
if uncles.Has(hash) {
|
||||||
|
// Error not unique
|
||||||
|
return UncleError("uncle[%d](%x) not unique", i, hash[:4])
|
||||||
|
}
|
||||||
|
uncles.Add(hash)
|
||||||
|
|
||||||
|
if ancestors[hash] != nil {
|
||||||
|
branch := fmt.Sprintf(" O - %x\n |\n", block.Hash())
|
||||||
|
for h := range ancestors {
|
||||||
|
branch += fmt.Sprintf(" O - %x\n |\n", h)
|
||||||
|
}
|
||||||
|
glog.Infoln(branch)
|
||||||
|
return UncleError("uncle[%d](%x) is ancestor", i, hash[:4])
|
||||||
|
}
|
||||||
|
|
||||||
|
if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == parent.Hash() {
|
||||||
|
return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4])
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ValidateHeader(v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil {
|
||||||
|
return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateHeader validates the given header and, depending on the pow arg,
|
||||||
|
// checks the proof of work of the given header. Returns an error if the
|
||||||
|
// validation failed.
|
||||||
|
func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow bool) error {
|
||||||
|
// Short circuit if the parent is missing.
|
||||||
|
if parent == nil {
|
||||||
|
return ParentError(header.ParentHash)
|
||||||
|
}
|
||||||
|
// Short circuit if the header's already known or its parent missing
|
||||||
|
if v.bc.HasHeader(header.Hash()) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ValidateHeader(v.Pow, header, parent, checkPow, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates a header. Returns an error if the header is invalid.
|
||||||
|
//
|
||||||
|
// See YP section 4.3.4. "Block Header Validity"
|
||||||
|
func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
|
||||||
|
if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
|
||||||
|
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
|
||||||
|
}
|
||||||
|
|
||||||
|
if uncle {
|
||||||
|
if header.Time.Cmp(common.MaxBig) == 1 {
|
||||||
|
return BlockTSTooBigErr
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if header.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 {
|
||||||
|
return BlockFutureErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if header.Time.Cmp(parent.Time) != 1 {
|
||||||
|
return BlockEqualTSErr
|
||||||
|
}
|
||||||
|
|
||||||
|
expd := CalcDifficulty(header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty)
|
||||||
|
if expd.Cmp(header.Difficulty) != 0 {
|
||||||
|
return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := new(big.Int).Set(parent.GasLimit)
|
||||||
|
a = a.Sub(a, header.GasLimit)
|
||||||
|
a.Abs(a)
|
||||||
|
b := new(big.Int).Set(parent.GasLimit)
|
||||||
|
b = b.Div(b, params.GasLimitBoundDivisor)
|
||||||
|
if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) {
|
||||||
|
return fmt.Errorf("GasLimit check failed for header %v (%v > %v)", header.GasLimit, a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
num := new(big.Int).Set(parent.Number)
|
||||||
|
num.Sub(header.Number, num)
|
||||||
|
if num.Cmp(big.NewInt(1)) != 0 {
|
||||||
|
return BlockNumberErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if checkPow {
|
||||||
|
// Verify the nonce of the header. Return an error if it's not valid
|
||||||
|
if !pow.Verify(types.NewBlockWithHeader(header)) {
|
||||||
|
return &BlockNonceErr{header.Number, header.Hash(), header.Nonce.Uint64()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -30,7 +30,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/pow/ezp"
|
"github.com/ethereum/go-ethereum/pow/ezp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func proc() (*BlockProcessor, *BlockChain) {
|
func proc() (Validator, *BlockChain) {
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
var mux event.TypeMux
|
var mux event.TypeMux
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ func proc() (*BlockProcessor, *BlockChain) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
return NewBlockProcessor(db, ezp.New(), blockchain, &mux), blockchain
|
return blockchain.validator, blockchain
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNumber(t *testing.T) {
|
func TestNumber(t *testing.T) {
|
@ -33,6 +33,7 @@ import (
|
|||||||
"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/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
@ -61,17 +62,34 @@ const (
|
|||||||
blockCacheLimit = 256
|
blockCacheLimit = 256
|
||||||
maxFutureBlocks = 256
|
maxFutureBlocks = 256
|
||||||
maxTimeFutureBlocks = 30
|
maxTimeFutureBlocks = 30
|
||||||
|
// must be bumped when consensus algorithm is changed, this forces the upgradedb
|
||||||
|
// command to be run (forces the blocks to be imported again using the new algorithm)
|
||||||
|
BlockChainVersion = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// BlockChain represents the canonical chain given a database with a genesis
|
||||||
|
// block. The Blockchain manages chain imports, reverts, chain reorganisations.
|
||||||
|
//
|
||||||
|
// Importing blocks in to the block chain happens according to the set of rules
|
||||||
|
// defined by the two stage Validator. Processing of blocks is done using the
|
||||||
|
// Processor which processes the included transaction. The validation of the state
|
||||||
|
// is done in the second part of the Validator. Failing results in aborting of
|
||||||
|
// the import.
|
||||||
|
//
|
||||||
|
// The BlockChain also helps in returning blocks from **any** chain included
|
||||||
|
// in the database as well as blocks that represents the canonical chain. It's
|
||||||
|
// important to note that GetBlock can return any block and does not need to be
|
||||||
|
// included in the canonical one where as GetBlockByNumber always represents the
|
||||||
|
// canonical chain.
|
||||||
type BlockChain struct {
|
type BlockChain struct {
|
||||||
chainDb ethdb.Database
|
chainDb ethdb.Database
|
||||||
processor types.BlockProcessor
|
|
||||||
eventMux *event.TypeMux
|
eventMux *event.TypeMux
|
||||||
genesisBlock *types.Block
|
genesisBlock *types.Block
|
||||||
// Last known total difficulty
|
// Last known total difficulty
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
chainmu sync.RWMutex
|
chainmu sync.RWMutex
|
||||||
tsmu sync.RWMutex
|
tsmu sync.RWMutex
|
||||||
|
procmu sync.RWMutex
|
||||||
|
|
||||||
checkpoint int // checkpoint counts towards the new checkpoint
|
checkpoint int // checkpoint counts towards the new checkpoint
|
||||||
currentHeader *types.Header // Current head of the header chain (may be above the block chain!)
|
currentHeader *types.Header // Current head of the header chain (may be above the block chain!)
|
||||||
@ -91,10 +109,15 @@ type BlockChain struct {
|
|||||||
procInterrupt int32 // interrupt signaler for block processing
|
procInterrupt int32 // interrupt signaler for block processing
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
||||||
pow pow.PoW
|
pow pow.PoW
|
||||||
rand *mrand.Rand
|
rand *mrand.Rand
|
||||||
|
processor Processor
|
||||||
|
validator Validator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewBlockChain returns a fully initialised block chain using information
|
||||||
|
// available in the database. It initialiser the default Ethereum Validator and
|
||||||
|
// Processor.
|
||||||
func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) {
|
func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) {
|
||||||
headerCache, _ := lru.New(headerCacheLimit)
|
headerCache, _ := lru.New(headerCacheLimit)
|
||||||
bodyCache, _ := lru.New(bodyCacheLimit)
|
bodyCache, _ := lru.New(bodyCacheLimit)
|
||||||
@ -121,6 +144,8 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
bc.rand = mrand.New(mrand.NewSource(seed.Int64()))
|
bc.rand = mrand.New(mrand.NewSource(seed.Int64()))
|
||||||
|
bc.SetValidator(NewBlockValidator(bc, pow))
|
||||||
|
bc.SetProcessor(NewStateProcessor(bc))
|
||||||
|
|
||||||
bc.genesisBlock = bc.GetBlockByNumber(0)
|
bc.genesisBlock = bc.GetBlockByNumber(0)
|
||||||
if bc.genesisBlock == nil {
|
if bc.genesisBlock == nil {
|
||||||
@ -292,6 +317,7 @@ func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GasLimit returns the gas limit of the current HEAD block.
|
||||||
func (self *BlockChain) GasLimit() *big.Int {
|
func (self *BlockChain) GasLimit() *big.Int {
|
||||||
self.mu.RLock()
|
self.mu.RLock()
|
||||||
defer self.mu.RUnlock()
|
defer self.mu.RUnlock()
|
||||||
@ -299,6 +325,7 @@ func (self *BlockChain) GasLimit() *big.Int {
|
|||||||
return self.currentBlock.GasLimit()
|
return self.currentBlock.GasLimit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LastBlockHash return the hash of the HEAD block.
|
||||||
func (self *BlockChain) LastBlockHash() common.Hash {
|
func (self *BlockChain) LastBlockHash() common.Hash {
|
||||||
self.mu.RLock()
|
self.mu.RLock()
|
||||||
defer self.mu.RUnlock()
|
defer self.mu.RUnlock()
|
||||||
@ -333,6 +360,8 @@ func (self *BlockChain) CurrentFastBlock() *types.Block {
|
|||||||
return self.currentFastBlock
|
return self.currentFastBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Status returns status information about the current chain such as the HEAD Td,
|
||||||
|
// the HEAD hash and the hash of the genesis block.
|
||||||
func (self *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) {
|
func (self *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) {
|
||||||
self.mu.RLock()
|
self.mu.RLock()
|
||||||
defer self.mu.RUnlock()
|
defer self.mu.RUnlock()
|
||||||
@ -340,10 +369,38 @@ func (self *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesis
|
|||||||
return self.GetTd(self.currentBlock.Hash()), self.currentBlock.Hash(), self.genesisBlock.Hash()
|
return self.GetTd(self.currentBlock.Hash()), self.currentBlock.Hash(), self.genesisBlock.Hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BlockChain) SetProcessor(proc types.BlockProcessor) {
|
// SetProcessor sets the processor required for making state modifications.
|
||||||
self.processor = proc
|
func (self *BlockChain) SetProcessor(processor Processor) {
|
||||||
|
self.procmu.Lock()
|
||||||
|
defer self.procmu.Unlock()
|
||||||
|
self.processor = processor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetValidator sets the validator which is used to validate incoming blocks.
|
||||||
|
func (self *BlockChain) SetValidator(validator Validator) {
|
||||||
|
self.procmu.Lock()
|
||||||
|
defer self.procmu.Unlock()
|
||||||
|
self.validator = validator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validator returns the current validator.
|
||||||
|
func (self *BlockChain) Validator() Validator {
|
||||||
|
self.procmu.RLock()
|
||||||
|
defer self.procmu.RUnlock()
|
||||||
|
return self.validator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processor returns the current processor.
|
||||||
|
func (self *BlockChain) Processor() Processor {
|
||||||
|
self.procmu.RLock()
|
||||||
|
defer self.procmu.RUnlock()
|
||||||
|
return self.processor
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuxValidator returns the auxiliary validator (Proof of work atm)
|
||||||
|
func (self *BlockChain) AuxValidator() pow.PoW { return self.pow }
|
||||||
|
|
||||||
|
// State returns a new mutable state based on the current HEAD block.
|
||||||
func (self *BlockChain) State() (*state.StateDB, error) {
|
func (self *BlockChain) State() (*state.StateDB, error) {
|
||||||
return state.New(self.CurrentBlock().Root(), self.chainDb)
|
return state.New(self.CurrentBlock().Root(), self.chainDb)
|
||||||
}
|
}
|
||||||
@ -606,6 +663,8 @@ func (self *BlockChain) GetUnclesInChain(block *types.Block, length int) []*type
|
|||||||
return uncles
|
return uncles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop stops the blockchain service. If any imports are currently in progress
|
||||||
|
// it will abort them using the procInterrupt.
|
||||||
func (bc *BlockChain) Stop() {
|
func (bc *BlockChain) Stop() {
|
||||||
if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) {
|
if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) {
|
||||||
return
|
return
|
||||||
@ -758,9 +817,9 @@ func (self *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int)
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
if index == 0 {
|
if index == 0 {
|
||||||
err = self.processor.ValidateHeader(header, checkPow, false)
|
err = self.Validator().ValidateHeader(header, self.GetHeader(header.ParentHash), checkPow)
|
||||||
} else {
|
} else {
|
||||||
err = self.processor.ValidateHeaderWithParent(header, chain[index-1], checkPow, false)
|
err = self.Validator().ValidateHeader(header, chain[index-1], checkPow)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs[index] = err
|
errs[index] = err
|
||||||
@ -1025,9 +1084,10 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||||||
// faster than direct delivery and requires much less mutex
|
// faster than direct delivery and requires much less mutex
|
||||||
// acquiring.
|
// acquiring.
|
||||||
var (
|
var (
|
||||||
stats struct{ queued, processed, ignored int }
|
stats struct{ queued, processed, ignored int }
|
||||||
events = make([]interface{}, 0, len(chain))
|
events = make([]interface{}, 0, len(chain))
|
||||||
tstart = time.Now()
|
coalescedLogs vm.Logs
|
||||||
|
tstart = time.Now()
|
||||||
|
|
||||||
nonceChecked = make([]bool, len(chain))
|
nonceChecked = make([]bool, len(chain))
|
||||||
)
|
)
|
||||||
@ -1057,12 +1117,12 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||||||
|
|
||||||
if BadHashes[block.Hash()] {
|
if BadHashes[block.Hash()] {
|
||||||
err := BadHashError(block.Hash())
|
err := BadHashError(block.Hash())
|
||||||
blockErr(block, err)
|
reportBlock(block, err)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
// Call in to the block processor and check for errors. It's likely that if one block fails
|
// Stage 1 validation of the block using the chain's validator
|
||||||
// all others will fail too (unless a known block is returned).
|
// interface.
|
||||||
logs, receipts, err := self.processor.Process(block)
|
err := self.Validator().ValidateBlock(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if IsKnownBlockErr(err) {
|
if IsKnownBlockErr(err) {
|
||||||
stats.ignored++
|
stats.ignored++
|
||||||
@ -1089,14 +1149,41 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
blockErr(block, err)
|
reportBlock(block, err)
|
||||||
|
|
||||||
go ReportBlock(block, err)
|
|
||||||
|
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a new statedb using the parent block and report an
|
||||||
|
// error if it fails.
|
||||||
|
statedb, err := state.New(self.GetBlock(block.ParentHash()).Root(), self.chainDb)
|
||||||
|
if err != nil {
|
||||||
|
reportBlock(block, err)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
// Process block using the parent state as reference point.
|
||||||
|
receipts, logs, usedGas, err := self.processor.Process(block, statedb)
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
reportBlock(block, err)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
// Write state changes to database
|
||||||
|
_, err = statedb.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// coalesce logs for later processing
|
||||||
|
coalescedLogs = append(coalescedLogs, logs...)
|
||||||
|
|
||||||
if err := PutBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil {
|
if err := PutBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil {
|
||||||
glog.V(logger.Warn).Infoln("error writing block receipts:", err)
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
txcount += len(block.Transactions())
|
txcount += len(block.Transactions())
|
||||||
@ -1105,6 +1192,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch status {
|
switch status {
|
||||||
case CanonStatTy:
|
case CanonStatTy:
|
||||||
if glog.V(logger.Debug) {
|
if glog.V(logger.Debug) {
|
||||||
@ -1141,7 +1229,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||||||
start, end := chain[0], chain[len(chain)-1]
|
start, end := chain[0], chain[len(chain)-1]
|
||||||
glog.Infof("imported %d block(s) (%d queued %d ignored) including %d txs in %v. #%v [%x / %x]\n", stats.processed, stats.queued, stats.ignored, txcount, tend, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4])
|
glog.Infof("imported %d block(s) (%d queued %d ignored) including %d txs in %v. #%v [%x / %x]\n", stats.processed, stats.queued, stats.ignored, txcount, tend, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4])
|
||||||
}
|
}
|
||||||
go self.postChainEvents(events)
|
go self.postChainEvents(events, coalescedLogs)
|
||||||
|
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
@ -1239,7 +1327,9 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
|||||||
|
|
||||||
// postChainEvents iterates over the events generated by a chain insertion and
|
// postChainEvents iterates over the events generated by a chain insertion and
|
||||||
// posts them into the event mux.
|
// posts them into the event mux.
|
||||||
func (self *BlockChain) postChainEvents(events []interface{}) {
|
func (self *BlockChain) postChainEvents(events []interface{}, logs vm.Logs) {
|
||||||
|
// post event logs for further processing
|
||||||
|
self.eventMux.Post(logs)
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
if event, ok := event.(ChainEvent); ok {
|
if event, ok := event.(ChainEvent); ok {
|
||||||
// We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long
|
// We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long
|
||||||
@ -1265,9 +1355,13 @@ func (self *BlockChain) update() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func blockErr(block *types.Block, err error) {
|
// reportBlock reports the given block and error using the canonical block
|
||||||
|
// reporting tool. Reporting the block to the service is handled in a separate
|
||||||
|
// goroutine.
|
||||||
|
func reportBlock(block *types.Block, err error) {
|
||||||
if glog.V(logger.Error) {
|
if glog.V(logger.Error) {
|
||||||
glog.Errorf("Bad block #%v (%s)\n", block.Number(), block.Hash().Hex())
|
glog.Errorf("Bad block #%v (%s)\n", block.Number(), block.Hash().Hex())
|
||||||
glog.Errorf(" %v", err)
|
glog.Errorf(" %v", err)
|
||||||
}
|
}
|
||||||
|
go ReportBlock(block, err)
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/ethash"
|
"github.com/ethereum/ethash"
|
||||||
"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/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
@ -53,31 +54,29 @@ func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain {
|
|||||||
WriteTestNetGenesisBlock(db, 0)
|
WriteTestNetGenesisBlock(db, 0)
|
||||||
blockchain, err := NewBlockChain(db, thePow(), &eventMux)
|
blockchain, err := NewBlockChain(db, thePow(), &eventMux)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed creating chainmanager:", err)
|
t.Error("failed creating blockchain:", err)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
blockMan := NewBlockProcessor(db, nil, blockchain, &eventMux)
|
|
||||||
blockchain.SetProcessor(blockMan)
|
|
||||||
|
|
||||||
return blockchain
|
return blockchain
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test fork of length N starting from block i
|
// Test fork of length N starting from block i
|
||||||
func testFork(t *testing.T, processor *BlockProcessor, i, n int, full bool, comparator func(td1, td2 *big.Int)) {
|
func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, comparator func(td1, td2 *big.Int)) {
|
||||||
// Copy old chain up to #i into a new db
|
// Copy old chain up to #i into a new db
|
||||||
db, processor2, err := newCanonical(i, full)
|
db, blockchain2, err := newCanonical(i, full)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("could not make new canonical in testFork", err)
|
t.Fatal("could not make new canonical in testFork", err)
|
||||||
}
|
}
|
||||||
// Assert the chains have the same header/block at #i
|
// Assert the chains have the same header/block at #i
|
||||||
var hash1, hash2 common.Hash
|
var hash1, hash2 common.Hash
|
||||||
if full {
|
if full {
|
||||||
hash1 = processor.bc.GetBlockByNumber(uint64(i)).Hash()
|
hash1 = blockchain.GetBlockByNumber(uint64(i)).Hash()
|
||||||
hash2 = processor2.bc.GetBlockByNumber(uint64(i)).Hash()
|
hash2 = blockchain2.GetBlockByNumber(uint64(i)).Hash()
|
||||||
} else {
|
} else {
|
||||||
hash1 = processor.bc.GetHeaderByNumber(uint64(i)).Hash()
|
hash1 = blockchain.GetHeaderByNumber(uint64(i)).Hash()
|
||||||
hash2 = processor2.bc.GetHeaderByNumber(uint64(i)).Hash()
|
hash2 = blockchain2.GetHeaderByNumber(uint64(i)).Hash()
|
||||||
}
|
}
|
||||||
if hash1 != hash2 {
|
if hash1 != hash2 {
|
||||||
t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1)
|
t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1)
|
||||||
@ -88,13 +87,13 @@ func testFork(t *testing.T, processor *BlockProcessor, i, n int, full bool, comp
|
|||||||
headerChainB []*types.Header
|
headerChainB []*types.Header
|
||||||
)
|
)
|
||||||
if full {
|
if full {
|
||||||
blockChainB = makeBlockChain(processor2.bc.CurrentBlock(), n, db, forkSeed)
|
blockChainB = makeBlockChain(blockchain2.CurrentBlock(), n, db, forkSeed)
|
||||||
if _, err := processor2.bc.InsertChain(blockChainB); err != nil {
|
if _, err := blockchain2.InsertChain(blockChainB); err != nil {
|
||||||
t.Fatalf("failed to insert forking chain: %v", err)
|
t.Fatalf("failed to insert forking chain: %v", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
headerChainB = makeHeaderChain(processor2.bc.CurrentHeader(), n, db, forkSeed)
|
headerChainB = makeHeaderChain(blockchain2.CurrentHeader(), n, db, forkSeed)
|
||||||
if _, err := processor2.bc.InsertHeaderChain(headerChainB, 1); err != nil {
|
if _, err := blockchain2.InsertHeaderChain(headerChainB, 1); err != nil {
|
||||||
t.Fatalf("failed to insert forking chain: %v", err)
|
t.Fatalf("failed to insert forking chain: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,17 +101,17 @@ func testFork(t *testing.T, processor *BlockProcessor, i, n int, full bool, comp
|
|||||||
var tdPre, tdPost *big.Int
|
var tdPre, tdPost *big.Int
|
||||||
|
|
||||||
if full {
|
if full {
|
||||||
tdPre = processor.bc.GetTd(processor.bc.CurrentBlock().Hash())
|
tdPre = blockchain.GetTd(blockchain.CurrentBlock().Hash())
|
||||||
if err := testBlockChainImport(blockChainB, processor); err != nil {
|
if err := testBlockChainImport(blockChainB, blockchain); err != nil {
|
||||||
t.Fatalf("failed to import forked block chain: %v", err)
|
t.Fatalf("failed to import forked block chain: %v", err)
|
||||||
}
|
}
|
||||||
tdPost = processor.bc.GetTd(blockChainB[len(blockChainB)-1].Hash())
|
tdPost = blockchain.GetTd(blockChainB[len(blockChainB)-1].Hash())
|
||||||
} else {
|
} else {
|
||||||
tdPre = processor.bc.GetTd(processor.bc.CurrentHeader().Hash())
|
tdPre = blockchain.GetTd(blockchain.CurrentHeader().Hash())
|
||||||
if err := testHeaderChainImport(headerChainB, processor); err != nil {
|
if err := testHeaderChainImport(headerChainB, blockchain); err != nil {
|
||||||
t.Fatalf("failed to import forked header chain: %v", err)
|
t.Fatalf("failed to import forked header chain: %v", err)
|
||||||
}
|
}
|
||||||
tdPost = processor.bc.GetTd(headerChainB[len(headerChainB)-1].Hash())
|
tdPost = blockchain.GetTd(headerChainB[len(headerChainB)-1].Hash())
|
||||||
}
|
}
|
||||||
// Compare the total difficulties of the chains
|
// Compare the total difficulties of the chains
|
||||||
comparator(tdPre, tdPost)
|
comparator(tdPre, tdPost)
|
||||||
@ -127,37 +126,52 @@ func printChain(bc *BlockChain) {
|
|||||||
|
|
||||||
// testBlockChainImport tries to process a chain of blocks, writing them into
|
// testBlockChainImport tries to process a chain of blocks, writing them into
|
||||||
// the database if successful.
|
// the database if successful.
|
||||||
func testBlockChainImport(chain []*types.Block, processor *BlockProcessor) error {
|
func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||||
for _, block := range chain {
|
for _, block := range chain {
|
||||||
// Try and process the block
|
// Try and process the block
|
||||||
if _, _, err := processor.Process(block); err != nil {
|
err := blockchain.Validator().ValidateBlock(block)
|
||||||
|
if err != nil {
|
||||||
if IsKnownBlockErr(err) {
|
if IsKnownBlockErr(err) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Manually insert the block into the database, but don't reorganize (allows subsequent testing)
|
statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), blockchain.chainDb)
|
||||||
processor.bc.mu.Lock()
|
if err != nil {
|
||||||
WriteTd(processor.chainDb, block.Hash(), new(big.Int).Add(block.Difficulty(), processor.bc.GetTd(block.ParentHash())))
|
return err
|
||||||
WriteBlock(processor.chainDb, block)
|
}
|
||||||
processor.bc.mu.Unlock()
|
receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb)
|
||||||
|
if err != nil {
|
||||||
|
reportBlock(block, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = blockchain.Validator().ValidateState(block, blockchain.GetBlock(block.ParentHash()), statedb, receipts, usedGas)
|
||||||
|
if err != nil {
|
||||||
|
reportBlock(block, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
blockchain.mu.Lock()
|
||||||
|
WriteTd(blockchain.chainDb, block.Hash(), new(big.Int).Add(block.Difficulty(), blockchain.GetTd(block.ParentHash())))
|
||||||
|
WriteBlock(blockchain.chainDb, block)
|
||||||
|
statedb.Commit()
|
||||||
|
blockchain.mu.Unlock()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// testHeaderChainImport tries to process a chain of header, writing them into
|
// testHeaderChainImport tries to process a chain of header, writing them into
|
||||||
// the database if successful.
|
// the database if successful.
|
||||||
func testHeaderChainImport(chain []*types.Header, processor *BlockProcessor) error {
|
func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error {
|
||||||
for _, header := range chain {
|
for _, header := range chain {
|
||||||
// Try and validate the header
|
// Try and validate the header
|
||||||
if err := processor.ValidateHeader(header, false, false); err != nil {
|
if err := blockchain.Validator().ValidateHeader(header, blockchain.GetHeader(header.ParentHash), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Manually insert the header into the database, but don't reorganize (allows subsequent testing)
|
// Manually insert the header into the database, but don't reorganize (allows subsequent testing)
|
||||||
processor.bc.mu.Lock()
|
blockchain.mu.Lock()
|
||||||
WriteTd(processor.chainDb, header.Hash(), new(big.Int).Add(header.Difficulty, processor.bc.GetTd(header.ParentHash)))
|
WriteTd(blockchain.chainDb, header.Hash(), new(big.Int).Add(header.Difficulty, blockchain.GetTd(header.ParentHash)))
|
||||||
WriteHeader(processor.chainDb, header)
|
WriteHeader(blockchain.chainDb, header)
|
||||||
processor.bc.mu.Unlock()
|
blockchain.mu.Unlock()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -313,19 +327,19 @@ func TestBrokenBlockChain(t *testing.T) { testBrokenChain(t, true) }
|
|||||||
|
|
||||||
func testBrokenChain(t *testing.T, full bool) {
|
func testBrokenChain(t *testing.T, full bool) {
|
||||||
// Make chain starting from genesis
|
// Make chain starting from genesis
|
||||||
db, processor, err := newCanonical(10, full)
|
db, blockchain, err := newCanonical(10, full)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to make new canonical chain: %v", err)
|
t.Fatalf("failed to make new canonical chain: %v", err)
|
||||||
}
|
}
|
||||||
// Create a forked chain, and try to insert with a missing link
|
// Create a forked chain, and try to insert with a missing link
|
||||||
if full {
|
if full {
|
||||||
chain := makeBlockChain(processor.bc.CurrentBlock(), 5, db, forkSeed)[1:]
|
chain := makeBlockChain(blockchain.CurrentBlock(), 5, db, forkSeed)[1:]
|
||||||
if err := testBlockChainImport(chain, processor); err == nil {
|
if err := testBlockChainImport(chain, blockchain); err == nil {
|
||||||
t.Errorf("broken block chain not reported")
|
t.Errorf("broken block chain not reported")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
chain := makeHeaderChain(processor.bc.CurrentHeader(), 5, db, forkSeed)[1:]
|
chain := makeHeaderChain(blockchain.CurrentHeader(), 5, db, forkSeed)[1:]
|
||||||
if err := testHeaderChainImport(chain, processor); err == nil {
|
if err := testHeaderChainImport(chain, blockchain); err == nil {
|
||||||
t.Errorf("broken header chain not reported")
|
t.Errorf("broken header chain not reported")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -415,9 +429,14 @@ func TestChainMultipleInsertions(t *testing.T) {
|
|||||||
|
|
||||||
type bproc struct{}
|
type bproc struct{}
|
||||||
|
|
||||||
func (bproc) Process(*types.Block) (vm.Logs, types.Receipts, error) { return nil, nil, nil }
|
func (bproc) ValidateBlock(*types.Block) error { return nil }
|
||||||
func (bproc) ValidateHeader(*types.Header, bool, bool) error { return nil }
|
func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return nil }
|
||||||
func (bproc) ValidateHeaderWithParent(*types.Header, *types.Header, bool, bool) error { return nil }
|
func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (bproc) Process(block *types.Block, statedb *state.StateDB) (types.Receipts, vm.Logs, *big.Int, error) {
|
||||||
|
return nil, nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header {
|
func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header {
|
||||||
blocks := makeBlockChainWithDiff(genesis, d, seed)
|
blocks := makeBlockChainWithDiff(genesis, d, seed)
|
||||||
@ -459,7 +478,8 @@ func chm(genesis *types.Block, db ethdb.Database) *BlockChain {
|
|||||||
bc.tdCache, _ = lru.New(100)
|
bc.tdCache, _ = lru.New(100)
|
||||||
bc.blockCache, _ = lru.New(100)
|
bc.blockCache, _ = lru.New(100)
|
||||||
bc.futureBlocks, _ = lru.New(100)
|
bc.futureBlocks, _ = lru.New(100)
|
||||||
bc.processor = bproc{}
|
bc.SetValidator(bproc{})
|
||||||
|
bc.SetProcessor(bproc{})
|
||||||
bc.ResetWithGenesisBlock(genesis)
|
bc.ResetWithGenesisBlock(genesis)
|
||||||
|
|
||||||
return bc
|
return bc
|
||||||
@ -612,12 +632,10 @@ func TestBlocksInsertNonceError(t *testing.T) { testInsertNonceError(t, true) }
|
|||||||
func testInsertNonceError(t *testing.T, full bool) {
|
func testInsertNonceError(t *testing.T, full bool) {
|
||||||
for i := 1; i < 25 && !t.Failed(); i++ {
|
for i := 1; i < 25 && !t.Failed(); i++ {
|
||||||
// Create a pristine chain and database
|
// Create a pristine chain and database
|
||||||
db, processor, err := newCanonical(0, full)
|
db, blockchain, err := newCanonical(0, full)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create pristine chain: %v", err)
|
t.Fatalf("failed to create pristine chain: %v", err)
|
||||||
}
|
}
|
||||||
bc := processor.bc
|
|
||||||
|
|
||||||
// Create and insert a chain with a failing nonce
|
// Create and insert a chain with a failing nonce
|
||||||
var (
|
var (
|
||||||
failAt int
|
failAt int
|
||||||
@ -626,34 +644,33 @@ func testInsertNonceError(t *testing.T, full bool) {
|
|||||||
failHash common.Hash
|
failHash common.Hash
|
||||||
)
|
)
|
||||||
if full {
|
if full {
|
||||||
blocks := makeBlockChain(processor.bc.CurrentBlock(), i, db, 0)
|
blocks := makeBlockChain(blockchain.CurrentBlock(), i, db, 0)
|
||||||
|
|
||||||
failAt = rand.Int() % len(blocks)
|
failAt = rand.Int() % len(blocks)
|
||||||
failNum = blocks[failAt].NumberU64()
|
failNum = blocks[failAt].NumberU64()
|
||||||
failHash = blocks[failAt].Hash()
|
failHash = blocks[failAt].Hash()
|
||||||
|
|
||||||
processor.bc.pow = failPow{failNum}
|
blockchain.pow = failPow{failNum}
|
||||||
processor.Pow = failPow{failNum}
|
|
||||||
|
|
||||||
failRes, err = processor.bc.InsertChain(blocks)
|
failRes, err = blockchain.InsertChain(blocks)
|
||||||
} else {
|
} else {
|
||||||
headers := makeHeaderChain(processor.bc.CurrentHeader(), i, db, 0)
|
headers := makeHeaderChain(blockchain.CurrentHeader(), i, db, 0)
|
||||||
|
|
||||||
failAt = rand.Int() % len(headers)
|
failAt = rand.Int() % len(headers)
|
||||||
failNum = headers[failAt].Number.Uint64()
|
failNum = headers[failAt].Number.Uint64()
|
||||||
failHash = headers[failAt].Hash()
|
failHash = headers[failAt].Hash()
|
||||||
|
|
||||||
processor.bc.pow = failPow{failNum}
|
blockchain.pow = failPow{failNum}
|
||||||
processor.Pow = failPow{failNum}
|
blockchain.validator = NewBlockValidator(blockchain, failPow{failNum})
|
||||||
|
|
||||||
failRes, err = processor.bc.InsertHeaderChain(headers, 1)
|
failRes, err = blockchain.InsertHeaderChain(headers, 1)
|
||||||
}
|
}
|
||||||
// Check that the returned error indicates the nonce failure.
|
// Check that the returned error indicates the nonce failure.
|
||||||
if failRes != failAt {
|
if failRes != failAt {
|
||||||
t.Errorf("test %d: failure index mismatch: have %d, want %d", i, failRes, failAt)
|
t.Errorf("test %d: failure index mismatch: have %d, want %d", i, failRes, failAt)
|
||||||
}
|
}
|
||||||
if !IsBlockNonceErr(err) {
|
if !IsBlockNonceErr(err) {
|
||||||
t.Fatalf("test %d: error mismatch: have %v, want nonce error", i, err)
|
t.Fatalf("test %d: error mismatch: have %v, want nonce error %T", i, err, err)
|
||||||
}
|
}
|
||||||
nerr := err.(*BlockNonceErr)
|
nerr := err.(*BlockNonceErr)
|
||||||
if nerr.Number.Uint64() != failNum {
|
if nerr.Number.Uint64() != failNum {
|
||||||
@ -665,11 +682,11 @@ func testInsertNonceError(t *testing.T, full bool) {
|
|||||||
// Check that all no blocks after the failing block have been inserted.
|
// Check that all no blocks after the failing block have been inserted.
|
||||||
for j := 0; j < i-failAt; j++ {
|
for j := 0; j < i-failAt; j++ {
|
||||||
if full {
|
if full {
|
||||||
if block := bc.GetBlockByNumber(failNum + uint64(j)); block != nil {
|
if block := blockchain.GetBlockByNumber(failNum + uint64(j)); block != nil {
|
||||||
t.Errorf("test %d: invalid block in chain: %v", i, block)
|
t.Errorf("test %d: invalid block in chain: %v", i, block)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if header := bc.GetHeaderByNumber(failNum + uint64(j)); header != nil {
|
if header := blockchain.GetHeaderByNumber(failNum + uint64(j)); header != nil {
|
||||||
t.Errorf("test %d: invalid header in chain: %v", i, header)
|
t.Errorf("test %d: invalid header in chain: %v", i, header)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -711,7 +728,6 @@ func TestFastVsFullChains(t *testing.T) {
|
|||||||
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
|
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
|
||||||
|
|
||||||
archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux))
|
archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux))
|
||||||
archive.SetProcessor(NewBlockProcessor(archiveDb, FakePow{}, archive, new(event.TypeMux)))
|
|
||||||
|
|
||||||
if n, err := archive.InsertChain(blocks); err != nil {
|
if n, err := archive.InsertChain(blocks); err != nil {
|
||||||
t.Fatalf("failed to process block %d: %v", n, err)
|
t.Fatalf("failed to process block %d: %v", n, err)
|
||||||
@ -720,7 +736,6 @@ func TestFastVsFullChains(t *testing.T) {
|
|||||||
fastDb, _ := ethdb.NewMemDatabase()
|
fastDb, _ := ethdb.NewMemDatabase()
|
||||||
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
|
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
|
||||||
fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux))
|
fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux))
|
||||||
fast.SetProcessor(NewBlockProcessor(fastDb, FakePow{}, fast, new(event.TypeMux)))
|
|
||||||
|
|
||||||
headers := make([]*types.Header, len(blocks))
|
headers := make([]*types.Header, len(blocks))
|
||||||
for i, block := range blocks {
|
for i, block := range blocks {
|
||||||
@ -797,7 +812,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||||||
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
|
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
|
||||||
|
|
||||||
archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux))
|
archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux))
|
||||||
archive.SetProcessor(NewBlockProcessor(archiveDb, FakePow{}, archive, new(event.TypeMux)))
|
|
||||||
|
|
||||||
if n, err := archive.InsertChain(blocks); err != nil {
|
if n, err := archive.InsertChain(blocks); err != nil {
|
||||||
t.Fatalf("failed to process block %d: %v", n, err)
|
t.Fatalf("failed to process block %d: %v", n, err)
|
||||||
@ -810,7 +824,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||||||
fastDb, _ := ethdb.NewMemDatabase()
|
fastDb, _ := ethdb.NewMemDatabase()
|
||||||
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
|
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
|
||||||
fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux))
|
fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux))
|
||||||
fast.SetProcessor(NewBlockProcessor(fastDb, FakePow{}, fast, new(event.TypeMux)))
|
|
||||||
|
|
||||||
headers := make([]*types.Header, len(blocks))
|
headers := make([]*types.Header, len(blocks))
|
||||||
for i, block := range blocks {
|
for i, block := range blocks {
|
||||||
@ -830,7 +843,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||||||
lightDb, _ := ethdb.NewMemDatabase()
|
lightDb, _ := ethdb.NewMemDatabase()
|
||||||
WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds})
|
WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds})
|
||||||
light, _ := NewBlockChain(lightDb, FakePow{}, new(event.TypeMux))
|
light, _ := NewBlockChain(lightDb, FakePow{}, new(event.TypeMux))
|
||||||
light.SetProcessor(NewBlockProcessor(lightDb, FakePow{}, light, new(event.TypeMux)))
|
|
||||||
|
|
||||||
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
|
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
|
||||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
t.Fatalf("failed to insert header %d: %v", n, err)
|
||||||
@ -895,9 +907,8 @@ func TestChainTxReorgs(t *testing.T) {
|
|||||||
})
|
})
|
||||||
// Import the chain. This runs all block validation rules.
|
// Import the chain. This runs all block validation rules.
|
||||||
evmux := &event.TypeMux{}
|
evmux := &event.TypeMux{}
|
||||||
chainman, _ := NewBlockChain(db, FakePow{}, evmux)
|
blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
|
||||||
chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux))
|
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||||
if i, err := chainman.InsertChain(chain); err != nil {
|
|
||||||
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
|
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -920,7 +931,7 @@ func TestChainTxReorgs(t *testing.T) {
|
|||||||
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
|
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if _, err := chainman.InsertChain(chain); err != nil {
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||||
t.Fatalf("failed to insert forked chain: %v", err)
|
t.Fatalf("failed to insert forked chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
|
|||||||
// newCanonical creates a chain database, and injects a deterministic canonical
|
// newCanonical creates a chain database, and injects a deterministic canonical
|
||||||
// chain. Depending on the full flag, if creates either a full block chain or a
|
// chain. Depending on the full flag, if creates either a full block chain or a
|
||||||
// header only chain.
|
// header only chain.
|
||||||
func newCanonical(n int, full bool) (ethdb.Database, *BlockProcessor, error) {
|
func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) {
|
||||||
// Create te new chain database
|
// Create te new chain database
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
evmux := &event.TypeMux{}
|
evmux := &event.TypeMux{}
|
||||||
@ -223,23 +223,20 @@ func newCanonical(n int, full bool) (ethdb.Database, *BlockProcessor, error) {
|
|||||||
genesis, _ := WriteTestNetGenesisBlock(db, 0)
|
genesis, _ := WriteTestNetGenesisBlock(db, 0)
|
||||||
|
|
||||||
blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
|
blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
|
||||||
processor := NewBlockProcessor(db, FakePow{}, blockchain, evmux)
|
|
||||||
processor.bc.SetProcessor(processor)
|
|
||||||
|
|
||||||
// Create and inject the requested chain
|
// Create and inject the requested chain
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return db, processor, nil
|
return db, blockchain, nil
|
||||||
}
|
}
|
||||||
if full {
|
if full {
|
||||||
// Full block-chain requested
|
// Full block-chain requested
|
||||||
blocks := makeBlockChain(genesis, n, db, canonicalSeed)
|
blocks := makeBlockChain(genesis, n, db, canonicalSeed)
|
||||||
_, err := blockchain.InsertChain(blocks)
|
_, err := blockchain.InsertChain(blocks)
|
||||||
return db, processor, err
|
return db, blockchain, err
|
||||||
}
|
}
|
||||||
// Header-only chain requested
|
// Header-only chain requested
|
||||||
headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed)
|
headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed)
|
||||||
_, err := blockchain.InsertHeaderChain(headers, 1)
|
_, err := blockchain.InsertHeaderChain(headers, 1)
|
||||||
return db, processor, err
|
return db, blockchain, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeHeaderChain creates a deterministic chain of headers rooted at parent.
|
// makeHeaderChain creates a deterministic chain of headers rooted at parent.
|
||||||
|
@ -77,15 +77,14 @@ func ExampleGenerateChain() {
|
|||||||
|
|
||||||
// Import the chain. This runs all block validation rules.
|
// Import the chain. This runs all block validation rules.
|
||||||
evmux := &event.TypeMux{}
|
evmux := &event.TypeMux{}
|
||||||
chainman, _ := NewBlockChain(db, FakePow{}, evmux)
|
blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
|
||||||
chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux))
|
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||||
if i, err := chainman.InsertChain(chain); err != nil {
|
|
||||||
fmt.Printf("insert error (block %d): %v\n", i, err)
|
fmt.Printf("insert error (block %d): %v\n", i, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
state, _ := chainman.State()
|
state, _ := blockchain.State()
|
||||||
fmt.Printf("last block: #%d\n", chainman.CurrentBlock().Number())
|
fmt.Printf("last block: #%d\n", blockchain.CurrentBlock().Number())
|
||||||
fmt.Println("balance of addr1:", state.GetBalance(addr1))
|
fmt.Println("balance of addr1:", state.GetBalance(addr1))
|
||||||
fmt.Println("balance of addr2:", state.GetBalance(addr2))
|
fmt.Println("balance of addr2:", state.GetBalance(addr2))
|
||||||
fmt.Println("balance of addr3:", state.GetBalance(addr3))
|
fmt.Println("balance of addr3:", state.GetBalance(addr3))
|
||||||
|
@ -14,12 +14,33 @@
|
|||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package types
|
package core
|
||||||
|
|
||||||
import "github.com/ethereum/go-ethereum/core/vm"
|
import "math/big"
|
||||||
|
|
||||||
type BlockProcessor interface {
|
// GasPool tracks the amount of gas available during
|
||||||
Process(*Block) (vm.Logs, Receipts, error)
|
// execution of the transactions in a block.
|
||||||
ValidateHeader(*Header, bool, bool) error
|
// The zero value is a pool with zero gas available.
|
||||||
ValidateHeaderWithParent(*Header, *Header, bool, bool) error
|
type GasPool big.Int
|
||||||
|
|
||||||
|
// AddGas makes gas available for execution.
|
||||||
|
func (gp *GasPool) AddGas(amount *big.Int) *GasPool {
|
||||||
|
i := (*big.Int)(gp)
|
||||||
|
i.Add(i, amount)
|
||||||
|
return gp
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubGas deducts the given amount from the pool if enough gas is
|
||||||
|
// available and returns an error otherwise.
|
||||||
|
func (gp *GasPool) SubGas(amount *big.Int) error {
|
||||||
|
i := (*big.Int)(gp)
|
||||||
|
if i.Cmp(amount) < 0 {
|
||||||
|
return &GasLimitErr{Have: new(big.Int).Set(i), Want: amount}
|
||||||
|
}
|
||||||
|
i.Sub(i, amount)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gp *GasPool) String() string {
|
||||||
|
return (*big.Int)(gp).String()
|
||||||
}
|
}
|
@ -1,34 +0,0 @@
|
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/event"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO move this to types?
|
|
||||||
type Backend interface {
|
|
||||||
AccountManager() *accounts.Manager
|
|
||||||
BlockProcessor() *BlockProcessor
|
|
||||||
BlockChain() *BlockChain
|
|
||||||
TxPool() *TxPool
|
|
||||||
ChainDb() ethdb.Database
|
|
||||||
DappDb() ethdb.Database
|
|
||||||
EventMux() *event.TypeMux
|
|
||||||
}
|
|
107
core/state_processor.go
Normal file
107
core/state_processor.go
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
big8 = big.NewInt(8)
|
||||||
|
big32 = big.NewInt(32)
|
||||||
|
)
|
||||||
|
|
||||||
|
type StateProcessor struct {
|
||||||
|
bc *BlockChain
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStateProcessor(bc *BlockChain) *StateProcessor {
|
||||||
|
return &StateProcessor{bc}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process processes the state changes according to the Ethereum rules by running
|
||||||
|
// the transaction messages using the statedb and applying any rewards to both
|
||||||
|
// the processor (coinbase) and any included uncles.
|
||||||
|
//
|
||||||
|
// Process returns the receipts and logs accumulated during the process and
|
||||||
|
// returns the amount of gas that was used in the process. If any of the
|
||||||
|
// transactions failed to execute due to insufficient gas it will return an error.
|
||||||
|
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB) (types.Receipts, vm.Logs, *big.Int, error) {
|
||||||
|
var (
|
||||||
|
receipts types.Receipts
|
||||||
|
totalUsedGas = big.NewInt(0)
|
||||||
|
err error
|
||||||
|
header = block.Header()
|
||||||
|
allLogs vm.Logs
|
||||||
|
gp = new(GasPool).AddGas(block.GasLimit())
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, tx := range block.Transactions() {
|
||||||
|
statedb.StartRecord(tx.Hash(), block.Hash(), i)
|
||||||
|
|
||||||
|
receipt, logs, _, err := ApplyTransaction(p.bc, gp, statedb, header, tx, totalUsedGas)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, totalUsedGas, err
|
||||||
|
}
|
||||||
|
receipts = append(receipts, receipt)
|
||||||
|
allLogs = append(allLogs, logs...)
|
||||||
|
}
|
||||||
|
AccumulateRewards(statedb, header, block.Uncles())
|
||||||
|
|
||||||
|
return receipts, allLogs, totalUsedGas, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyTransaction attemps to apply a transaction to the given state database
|
||||||
|
// and uses the input parameters for its environment.
|
||||||
|
//
|
||||||
|
// ApplyTransactions returns the generated receipts and vm logs during the
|
||||||
|
// execution of the state transition phase.
|
||||||
|
func ApplyTransaction(bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int) (*types.Receipt, vm.Logs, *big.Int, error) {
|
||||||
|
_, gas, err := ApplyMessage(NewEnv(statedb, bc, tx, header), tx, gp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the state with pending changes
|
||||||
|
usedGas.Add(usedGas, gas)
|
||||||
|
receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas)
|
||||||
|
receipt.TxHash = tx.Hash()
|
||||||
|
receipt.GasUsed = new(big.Int).Set(gas)
|
||||||
|
if MessageCreatesContract(tx) {
|
||||||
|
from, _ := tx.From()
|
||||||
|
receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
|
||||||
|
}
|
||||||
|
|
||||||
|
logs := statedb.GetLogs(tx.Hash())
|
||||||
|
receipt.Logs = logs
|
||||||
|
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||||
|
|
||||||
|
glog.V(logger.Debug).Infoln(receipt)
|
||||||
|
|
||||||
|
return receipt, logs, gas, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccumulateRewards credits the coinbase of the given block with the
|
||||||
|
// mining reward. The total reward consists of the static block reward
|
||||||
|
// and rewards for included uncles. The coinbase of each uncle block is
|
||||||
|
// also rewarded.
|
||||||
|
func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) {
|
||||||
|
reward := new(big.Int).Set(BlockReward)
|
||||||
|
r := new(big.Int)
|
||||||
|
for _, uncle := range uncles {
|
||||||
|
r.Add(uncle.Number, big8)
|
||||||
|
r.Sub(r, header.Number)
|
||||||
|
r.Mul(r, BlockReward)
|
||||||
|
r.Div(r, big8)
|
||||||
|
statedb.AddBalance(uncle.Coinbase, r)
|
||||||
|
|
||||||
|
r.Div(BlockReward, big32)
|
||||||
|
reward.Add(reward, r)
|
||||||
|
}
|
||||||
|
statedb.AddBalance(header.Coinbase, reward)
|
||||||
|
}
|
70
core/types.go
Normal file
70
core/types.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validator is an interface which defines the standard for block validation.
|
||||||
|
//
|
||||||
|
// The validator is responsible for validating incoming block or, if desired,
|
||||||
|
// validates headers for fast validation.
|
||||||
|
//
|
||||||
|
// ValidateBlock validates the given block and should return an error if it
|
||||||
|
// failed to do so and should be used for "full" validation.
|
||||||
|
//
|
||||||
|
// ValidateHeader validates the given header and parent and returns an error
|
||||||
|
// if it failed to do so.
|
||||||
|
//
|
||||||
|
// ValidateStack validates the given statedb and optionally the receipts and
|
||||||
|
// gas used. The implementor should decide what to do with the given input.
|
||||||
|
type Validator interface {
|
||||||
|
ValidateBlock(block *types.Block) error
|
||||||
|
ValidateHeader(header, parent *types.Header, checkPow bool) error
|
||||||
|
ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processor is an interface for processing blocks using a given initial state.
|
||||||
|
//
|
||||||
|
// Process takes the block to be processed and the statedb upon which the
|
||||||
|
// initial state is based. It should return the receipts generated, amount
|
||||||
|
// of gas used in the process and return an error if any of the internal rules
|
||||||
|
// failed.
|
||||||
|
type Processor interface {
|
||||||
|
Process(block *types.Block, statedb *state.StateDB) (types.Receipts, vm.Logs, *big.Int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backend is an interface defining the basic functionality for an operable node
|
||||||
|
// with all the functionality to be a functional, valid Ethereum operator.
|
||||||
|
//
|
||||||
|
// TODO Remove this
|
||||||
|
type Backend interface {
|
||||||
|
AccountManager() *accounts.Manager
|
||||||
|
BlockChain() *BlockChain
|
||||||
|
TxPool() *TxPool
|
||||||
|
ChainDb() ethdb.Database
|
||||||
|
DappDb() ethdb.Database
|
||||||
|
EventMux() *event.TypeMux
|
||||||
|
}
|
@ -231,9 +231,7 @@ type Ethereum struct {
|
|||||||
chainDb ethdb.Database // Block chain database
|
chainDb ethdb.Database // Block chain database
|
||||||
dappDb ethdb.Database // Dapp database
|
dappDb ethdb.Database // Dapp database
|
||||||
|
|
||||||
//*** SERVICES ***
|
// Handlers
|
||||||
// State manager for processing new blocks and managing the over all states
|
|
||||||
blockProcessor *core.BlockProcessor
|
|
||||||
txPool *core.TxPool
|
txPool *core.TxPool
|
||||||
blockchain *core.BlockChain
|
blockchain *core.BlockChain
|
||||||
accountManager *accounts.Manager
|
accountManager *accounts.Manager
|
||||||
@ -407,8 +405,6 @@ func New(config *Config) (*Ethereum, error) {
|
|||||||
newPool := core.NewTxPool(eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
|
newPool := core.NewTxPool(eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
|
||||||
eth.txPool = newPool
|
eth.txPool = newPool
|
||||||
|
|
||||||
eth.blockProcessor = core.NewBlockProcessor(chainDb, eth.pow, eth.blockchain, eth.EventMux())
|
|
||||||
eth.blockchain.SetProcessor(eth.blockProcessor)
|
|
||||||
if eth.protocolManager, err = NewProtocolManager(config.FastSync, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil {
|
if eth.protocolManager, err = NewProtocolManager(config.FastSync, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -485,24 +481,23 @@ func (s *Ethereum) IsMining() bool { return s.miner.Mining() }
|
|||||||
func (s *Ethereum) Miner() *miner.Miner { return s.miner }
|
func (s *Ethereum) Miner() *miner.Miner { return s.miner }
|
||||||
|
|
||||||
// func (s *Ethereum) Logger() logger.LogSystem { return s.logger }
|
// func (s *Ethereum) Logger() logger.LogSystem { return s.logger }
|
||||||
func (s *Ethereum) Name() string { return s.net.Name }
|
func (s *Ethereum) Name() string { return s.net.Name }
|
||||||
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
|
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
|
||||||
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
|
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
|
||||||
func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor }
|
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
|
||||||
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
|
func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
|
||||||
func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
|
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
|
||||||
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
|
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
||||||
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
|
||||||
func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
|
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
||||||
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
|
||||||
func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
|
func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
|
||||||
func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
|
func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers }
|
||||||
func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers }
|
func (s *Ethereum) ClientVersion() string { return s.clientVersion }
|
||||||
func (s *Ethereum) ClientVersion() string { return s.clientVersion }
|
func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
|
||||||
func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
|
func (s *Ethereum) NetVersion() int { return s.netVersionId }
|
||||||
func (s *Ethereum) NetVersion() int { return s.netVersionId }
|
func (s *Ethereum) ShhVersion() int { return s.shhVersionId }
|
||||||
func (s *Ethereum) ShhVersion() int { return s.shhVersionId }
|
func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
|
||||||
func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
|
|
||||||
|
|
||||||
// Start the ethereum
|
// Start the ethereum
|
||||||
func (s *Ethereum) Start() error {
|
func (s *Ethereum) Start() error {
|
||||||
|
@ -166,7 +166,7 @@ func (self *GasPriceOracle) processBlock(block *types.Block) {
|
|||||||
func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int {
|
func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int {
|
||||||
gasUsed := big.NewInt(0)
|
gasUsed := big.NewInt(0)
|
||||||
|
|
||||||
receipts := self.eth.BlockProcessor().GetBlockReceipts(block.Hash())
|
receipts := core.GetBlockReceipts(self.eth.ChainDb(), block.Hash())
|
||||||
if len(receipts) > 0 {
|
if len(receipts) > 0 {
|
||||||
if cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil {
|
if cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil {
|
||||||
gasUsed = receipts[len(receipts)-1].CumulativeGasUsed
|
gasUsed = receipts[len(receipts)-1].CumulativeGasUsed
|
||||||
|
@ -35,9 +35,7 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core
|
|||||||
db, _ = ethdb.NewMemDatabase()
|
db, _ = ethdb.NewMemDatabase()
|
||||||
genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds})
|
genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds})
|
||||||
blockchain, _ = core.NewBlockChain(db, pow, evmux)
|
blockchain, _ = core.NewBlockChain(db, pow, evmux)
|
||||||
blockproc = core.NewBlockProcessor(db, pow, blockchain, evmux)
|
|
||||||
)
|
)
|
||||||
blockchain.SetProcessor(blockproc)
|
|
||||||
chain, _ := core.GenerateChain(genesis, db, blocks, generator)
|
chain, _ := core.GenerateChain(genesis, db, blocks, generator)
|
||||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -100,7 +100,7 @@ type worker struct {
|
|||||||
|
|
||||||
eth core.Backend
|
eth core.Backend
|
||||||
chain *core.BlockChain
|
chain *core.BlockChain
|
||||||
proc *core.BlockProcessor
|
proc core.Validator
|
||||||
chainDb ethdb.Database
|
chainDb ethdb.Database
|
||||||
|
|
||||||
coinbase common.Address
|
coinbase common.Address
|
||||||
@ -131,7 +131,7 @@ func newWorker(coinbase common.Address, eth core.Backend) *worker {
|
|||||||
recv: make(chan *Result, resultQueueSize),
|
recv: make(chan *Result, resultQueueSize),
|
||||||
gasPrice: new(big.Int),
|
gasPrice: new(big.Int),
|
||||||
chain: eth.BlockChain(),
|
chain: eth.BlockChain(),
|
||||||
proc: eth.BlockProcessor(),
|
proc: eth.BlockChain().Validator(),
|
||||||
possibleUncles: make(map[common.Hash]*types.Block),
|
possibleUncles: make(map[common.Hash]*types.Block),
|
||||||
coinbase: coinbase,
|
coinbase: coinbase,
|
||||||
txQueue: make(map[common.Hash]*types.Transaction),
|
txQueue: make(map[common.Hash]*types.Transaction),
|
||||||
@ -244,7 +244,7 @@ func (self *worker) update() {
|
|||||||
// Apply transaction to the pending state if we're not mining
|
// Apply transaction to the pending state if we're not mining
|
||||||
if atomic.LoadInt32(&self.mining) == 0 {
|
if atomic.LoadInt32(&self.mining) == 0 {
|
||||||
self.currentMu.Lock()
|
self.currentMu.Lock()
|
||||||
self.current.commitTransactions(types.Transactions{ev.Tx}, self.gasPrice, self.proc)
|
self.current.commitTransactions(types.Transactions{ev.Tx}, self.gasPrice, self.chain)
|
||||||
self.currentMu.Unlock()
|
self.currentMu.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -290,7 +290,9 @@ func (self *worker) wait() {
|
|||||||
glog.V(logger.Error).Infoln("Invalid block found during mining")
|
glog.V(logger.Error).Infoln("Invalid block found during mining")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := core.ValidateHeader(self.eth.BlockProcessor().Pow, block.Header(), parent.Header(), true, false); err != nil && err != core.BlockFutureErr {
|
|
||||||
|
auxValidator := self.eth.BlockChain().AuxValidator()
|
||||||
|
if err := core.ValidateHeader(auxValidator, block.Header(), parent.Header(), true, false); err != nil && err != core.BlockFutureErr {
|
||||||
glog.V(logger.Error).Infoln("Invalid header on mined block:", err)
|
glog.V(logger.Error).Infoln("Invalid header on mined block:", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -516,7 +518,7 @@ func (self *worker) commitNewWork() {
|
|||||||
transactions := append(singleTxOwner, multiTxOwner...)
|
transactions := append(singleTxOwner, multiTxOwner...)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
work.commitTransactions(transactions, self.gasPrice, self.proc)
|
work.commitTransactions(transactions, self.gasPrice, self.chain)
|
||||||
self.eth.TxPool().RemoveTransactions(work.lowGasTxs)
|
self.eth.TxPool().RemoveTransactions(work.lowGasTxs)
|
||||||
|
|
||||||
// compute uncles for the new block.
|
// compute uncles for the new block.
|
||||||
@ -575,9 +577,8 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *big.Int, proc *core.BlockProcessor) {
|
func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *big.Int, bc *core.BlockChain) {
|
||||||
gp := new(core.GasPool).AddGas(env.header.GasLimit)
|
gp := new(core.GasPool).AddGas(env.header.GasLimit)
|
||||||
|
|
||||||
for _, tx := range transactions {
|
for _, tx := range transactions {
|
||||||
// We can skip err. It has already been validated in the tx pool
|
// We can skip err. It has already been validated in the tx pool
|
||||||
from, _ := tx.From()
|
from, _ := tx.From()
|
||||||
@ -615,7 +616,7 @@ func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *b
|
|||||||
|
|
||||||
env.state.StartRecord(tx.Hash(), common.Hash{}, 0)
|
env.state.StartRecord(tx.Hash(), common.Hash{}, 0)
|
||||||
|
|
||||||
err := env.commitTransaction(tx, proc, gp)
|
err := env.commitTransaction(tx, bc, gp)
|
||||||
switch {
|
switch {
|
||||||
case core.IsGasLimitErr(err):
|
case core.IsGasLimitErr(err):
|
||||||
// ignore the transactor so no nonce errors will be thrown for this account
|
// ignore the transactor so no nonce errors will be thrown for this account
|
||||||
@ -635,9 +636,9 @@ func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (env *Work) commitTransaction(tx *types.Transaction, proc *core.BlockProcessor, gp *core.GasPool) error {
|
func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) error {
|
||||||
snap := env.state.Copy()
|
snap := env.state.Copy()
|
||||||
receipt, _, err := proc.ApplyTransaction(gp, env.state, env.header, tx, env.header.GasUsed, true)
|
receipt, _, _, err := core.ApplyTransaction(bc, gp, env.state, env.header, tx, env.header.GasUsed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
env.state.Set(snap)
|
env.state.Set(snap)
|
||||||
return err
|
return err
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/ethash"
|
"github.com/ethereum/ethash"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
@ -166,11 +167,30 @@ func (self *debugApi) ProcessBlock(req *shared.Request) (interface{}, error) {
|
|||||||
defer func() { vm.Debug = old }()
|
defer func() { vm.Debug = old }()
|
||||||
vm.Debug = true
|
vm.Debug = true
|
||||||
|
|
||||||
_, err := self.ethereum.BlockProcessor().RetryProcess(block)
|
var (
|
||||||
if err == nil {
|
blockchain = self.ethereum.BlockChain()
|
||||||
return true, nil
|
validator = blockchain.Validator()
|
||||||
|
processor = blockchain.Processor()
|
||||||
|
)
|
||||||
|
|
||||||
|
err := core.ValidateHeader(blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash()), true, false)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
}
|
}
|
||||||
return false, err
|
statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), self.ethereum.ChainDb())
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
receipts, _, usedGas, err := processor.Process(block, statedb)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
err = validator.ValidateState(block, blockchain.GetBlock(block.ParentHash()), statedb, receipts, usedGas)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *debugApi) SeedHash(req *shared.Request) (interface{}, error) {
|
func (self *debugApi) SeedHash(req *shared.Request) (interface{}, error) {
|
||||||
|
@ -379,7 +379,7 @@ func (self *XEth) CurrentBlock() *types.Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *XEth) GetBlockReceipts(bhash common.Hash) types.Receipts {
|
func (self *XEth) GetBlockReceipts(bhash common.Hash) types.Receipts {
|
||||||
return self.backend.BlockProcessor().GetBlockReceipts(bhash)
|
return core.GetBlockReceipts(self.backend.ChainDb(), bhash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *XEth) GetTxReceipt(txhash common.Hash) *types.Receipt {
|
func (self *XEth) GetTxReceipt(txhash common.Hash) *types.Receipt {
|
||||||
|
Loading…
Reference in New Issue
Block a user