forked from mirror/go-ethereum
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).release/1.3.2^2
parent
9422eec554
commit
a1d9ef48c5
@ -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 |
||||
} |
@ -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 |
||||
} |
@ -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 |
||||
} |
@ -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) |
||||
} |
@ -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 |
||||
} |
Loading…
Reference in new issue