aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/utils/flags.go2
-rw-r--r--core/bench_test.go1
-rw-r--r--core/block_processor.go460
-rw-r--r--core/block_validator.go243
-rw-r--r--core/block_validator_test.go (renamed from core/block_processor_test.go)4
-rw-r--r--core/blockchain.go134
-rw-r--r--core/blockchain_test.go139
-rw-r--r--core/chain_makers.go11
-rw-r--r--core/chain_makers_test.go9
-rw-r--r--core/gaspool.go46
-rw-r--r--core/state_processor.go107
-rw-r--r--core/types.go70
-rw-r--r--core/vm/runtime/doc.go (renamed from core/types/common.go)11
-rw-r--r--core/vm/runtime/env.go106
-rw-r--r--core/vm/runtime/runtime.go121
-rw-r--r--core/vm/runtime/runtime_example_test.go (renamed from core/manager.go)26
-rw-r--r--core/vm/runtime/runtime_test.go120
-rw-r--r--crypto/secp256k1/panic_cb.go33
-rw-r--r--crypto/secp256k1/secp256.go122
-rw-r--r--crypto/secp256k1/secp256_test.go15
-rw-r--r--eth/backend.go41
-rw-r--r--eth/gasprice.go2
-rw-r--r--eth/helper_test.go2
-rw-r--r--miner/worker.go21
-rw-r--r--rpc/api/debug.go28
-rw-r--r--rpc/api/utils.go2
-rw-r--r--xeth/xeth.go2
27 files changed, 1172 insertions, 706 deletions
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 8335517df..3792dc1e0 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -557,8 +557,6 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database
Fatalf("Could not start chainmanager: %v", err)
}
- proc := core.NewBlockProcessor(chainDb, pow, chain, eventMux)
- chain.SetProcessor(proc)
return chain, chainDb
}
diff --git a/core/bench_test.go b/core/bench_test.go
index b5eb51803..6fa7659b9 100644
--- a/core/bench_test.go
+++ b/core/bench_test.go
@@ -169,7 +169,6 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// State and blocks are stored in the same DB.
evmux := new(event.TypeMux)
chainman, _ := NewBlockChain(db, FakePow{}, evmux)
- chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux))
defer chainman.Stop()
b.ReportAllocs()
b.ResetTimer()
diff --git a/core/block_processor.go b/core/block_processor.go
deleted file mode 100644
index e7b2f63e5..000000000
--- a/core/block_processor.go
+++ /dev/null
@@ -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
-}
diff --git a/core/block_validator.go b/core/block_validator.go
new file mode 100644
index 000000000..62d096d02
--- /dev/null
+++ b/core/block_validator.go
@@ -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
+}
diff --git a/core/block_processor_test.go b/core/block_validator_test.go
index 3050456b4..a0694f067 100644
--- a/core/block_processor_test.go
+++ b/core/block_validator_test.go
@@ -30,7 +30,7 @@ import (
"github.com/ethereum/go-ethereum/pow/ezp"
)
-func proc() (*BlockProcessor, *BlockChain) {
+func proc() (Validator, *BlockChain) {
db, _ := ethdb.NewMemDatabase()
var mux event.TypeMux
@@ -39,7 +39,7 @@ func proc() (*BlockProcessor, *BlockChain) {
if err != nil {
fmt.Println(err)
}
- return NewBlockProcessor(db, ezp.New(), blockchain, &mux), blockchain
+ return blockchain.validator, blockchain
}
func TestNumber(t *testing.T) {
diff --git a/core/blockchain.go b/core/blockchain.go
index cea346e38..b6b00ca04 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -33,6 +33,7 @@ import (
"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"
@@ -61,17 +62,34 @@ const (
blockCacheLimit = 256
maxFutureBlocks = 256
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 {
chainDb ethdb.Database
- processor types.BlockProcessor
eventMux *event.TypeMux
genesisBlock *types.Block
// Last known total difficulty
mu sync.RWMutex
chainmu sync.RWMutex
tsmu sync.RWMutex
+ procmu sync.RWMutex
checkpoint int // checkpoint counts towards the new checkpoint
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
wg sync.WaitGroup
- pow pow.PoW
- rand *mrand.Rand
+ pow pow.PoW
+ 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) {
headerCache, _ := lru.New(headerCacheLimit)
bodyCache, _ := lru.New(bodyCacheLimit)
@@ -121,6 +144,8 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
return nil, err
}
bc.rand = mrand.New(mrand.NewSource(seed.Int64()))
+ bc.SetValidator(NewBlockValidator(bc, pow))
+ bc.SetProcessor(NewStateProcessor(bc))
bc.genesisBlock = bc.GetBlockByNumber(0)
if bc.genesisBlock == nil {
@@ -292,6 +317,7 @@ func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error {
return nil
}
+// GasLimit returns the gas limit of the current HEAD block.
func (self *BlockChain) GasLimit() *big.Int {
self.mu.RLock()
defer self.mu.RUnlock()
@@ -299,6 +325,7 @@ func (self *BlockChain) GasLimit() *big.Int {
return self.currentBlock.GasLimit()
}
+// LastBlockHash return the hash of the HEAD block.
func (self *BlockChain) LastBlockHash() common.Hash {
self.mu.RLock()
defer self.mu.RUnlock()
@@ -333,6 +360,8 @@ func (self *BlockChain) CurrentFastBlock() *types.Block {
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) {
self.mu.RLock()
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()
}
-func (self *BlockChain) SetProcessor(proc types.BlockProcessor) {
- self.processor = proc
+// SetProcessor sets the processor required for making state modifications.
+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) {
return state.New(self.CurrentBlock().Root(), self.chainDb)
}
@@ -606,6 +663,8 @@ func (self *BlockChain) GetUnclesInChain(block *types.Block, length int) []*type
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() {
if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) {
return
@@ -758,9 +817,9 @@ func (self *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int)
var err error
if index == 0 {
- err = self.processor.ValidateHeader(header, checkPow, false)
+ err = self.Validator().ValidateHeader(header, self.GetHeader(header.ParentHash), checkPow)
} else {
- err = self.processor.ValidateHeaderWithParent(header, chain[index-1], checkPow, false)
+ err = self.Validator().ValidateHeader(header, chain[index-1], checkPow)
}
if err != nil {
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
// acquiring.
var (
- stats struct{ queued, processed, ignored int }
- events = make([]interface{}, 0, len(chain))
- tstart = time.Now()
+ stats struct{ queued, processed, ignored int }
+ events = make([]interface{}, 0, len(chain))
+ coalescedLogs vm.Logs
+ tstart = time.Now()
nonceChecked = make([]bool, len(chain))
)
@@ -1057,12 +1117,12 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
if BadHashes[block.Hash()] {
err := BadHashError(block.Hash())
- blockErr(block, err)
+ reportBlock(block, err)
return i, err
}
- // Call in to the block processor and check for errors. It's likely that if one block fails
- // all others will fail too (unless a known block is returned).
- logs, receipts, err := self.processor.Process(block)
+ // Stage 1 validation of the block using the chain's validator
+ // interface.
+ err := self.Validator().ValidateBlock(block)
if err != nil {
if IsKnownBlockErr(err) {
stats.ignored++
@@ -1089,14 +1149,41 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
continue
}
- blockErr(block, err)
+ reportBlock(block, err)
- go ReportBlock(block, 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 {
- glog.V(logger.Warn).Infoln("error writing block receipts:", err)
+ return i, err
}
txcount += len(block.Transactions())
@@ -1105,6 +1192,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
if err != nil {
return i, err
}
+
switch status {
case CanonStatTy:
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]
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
}
@@ -1239,7 +1327,9 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
// postChainEvents iterates over the events generated by a chain insertion and
// 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 {
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
@@ -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) {
glog.Errorf("Bad block #%v (%s)\n", block.Number(), block.Hash().Hex())
glog.Errorf(" %v", err)
}
+ go ReportBlock(block, err)
}
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 8ddc5032b..e5ed66377 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/ethash"
"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"
@@ -53,31 +54,29 @@ func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain {
WriteTestNetGenesisBlock(db, 0)
blockchain, err := NewBlockChain(db, thePow(), &eventMux)
if err != nil {
- t.Error("failed creating chainmanager:", err)
+ t.Error("failed creating blockchain:", err)
t.FailNow()
return nil
}
- blockMan := NewBlockProcessor(db, nil, blockchain, &eventMux)
- blockchain.SetProcessor(blockMan)
return blockchain
}
// 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
- db, processor2, err := newCanonical(i, full)
+ db, blockchain2, err := newCanonical(i, full)
if err != nil {
t.Fatal("could not make new canonical in testFork", err)
}
// Assert the chains have the same header/block at #i
var hash1, hash2 common.Hash
if full {
- hash1 = processor.bc.GetBlockByNumber(uint64(i)).Hash()
- hash2 = processor2.bc.GetBlockByNumber(uint64(i)).Hash()
+ hash1 = blockchain.GetBlockByNumber(uint64(i)).Hash()
+ hash2 = blockchain2.GetBlockByNumber(uint64(i)).Hash()
} else {
- hash1 = processor.bc.GetHeaderByNumber(uint64(i)).Hash()
- hash2 = processor2.bc.GetHeaderByNumber(uint64(i)).Hash()
+ hash1 = blockchain.GetHeaderByNumber(uint64(i)).Hash()
+ hash2 = blockchain2.GetHeaderByNumber(uint64(i)).Hash()
}
if hash1 != hash2 {
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
)
if full {
- blockChainB = makeBlockChain(processor2.bc.CurrentBlock(), n, db, forkSeed)
- if _, err := processor2.bc.InsertChain(blockChainB); err != nil {
+ blockChainB = makeBlockChain(blockchain2.CurrentBlock(), n, db, forkSeed)
+ if _, err := blockchain2.InsertChain(blockChainB); err != nil {
t.Fatalf("failed to insert forking chain: %v", err)
}
} else {
- headerChainB = makeHeaderChain(processor2.bc.CurrentHeader(), n, db, forkSeed)
- if _, err := processor2.bc.InsertHeaderChain(headerChainB, 1); err != nil {
+ headerChainB = makeHeaderChain(blockchain2.CurrentHeader(), n, db, forkSeed)
+ if _, err := blockchain2.InsertHeaderChain(headerChainB, 1); err != nil {
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
if full {
- tdPre = processor.bc.GetTd(processor.bc.CurrentBlock().Hash())
- if err := testBlockChainImport(blockChainB, processor); err != nil {
+ tdPre = blockchain.GetTd(blockchain.CurrentBlock().Hash())
+ if err := testBlockChainImport(blockChainB, blockchain); err != nil {
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 {
- tdPre = processor.bc.GetTd(processor.bc.CurrentHeader().Hash())
- if err := testHeaderChainImport(headerChainB, processor); err != nil {
+ tdPre = blockchain.GetTd(blockchain.CurrentHeader().Hash())
+ if err := testHeaderChainImport(headerChainB, blockchain); err != nil {
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
comparator(tdPre, tdPost)
@@ -127,37 +126,52 @@ func printChain(bc *BlockChain) {
// testBlockChainImport tries to process a chain of blocks, writing them into
// the database if successful.
-func testBlockChainImport(chain []*types.Block, processor *BlockProcessor) error {
+func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
for _, block := range chain {
// Try and process the block
- if _, _, err := processor.Process(block); err != nil {
+ err := blockchain.Validator().ValidateBlock(block)
+ if err != nil {
if IsKnownBlockErr(err) {
continue
}
return err
}
- // Manually insert the block into the database, but don't reorganize (allows subsequent testing)
- processor.bc.mu.Lock()
- WriteTd(processor.chainDb, block.Hash(), new(big.Int).Add(block.Difficulty(), processor.bc.GetTd(block.ParentHash())))
- WriteBlock(processor.chainDb, block)
- processor.bc.mu.Unlock()
+ statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), blockchain.chainDb)
+ if err != nil {
+ return err
+ }
+ 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
}
// testHeaderChainImport tries to process a chain of header, writing them into
// the database if successful.
-func testHeaderChainImport(chain []*types.Header, processor *BlockProcessor) error {
+func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error {
for _, header := range chain {
// 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
}
// Manually insert the header into the database, but don't reorganize (allows subsequent testing)
- processor.bc.mu.Lock()
- WriteTd(processor.chainDb, header.Hash(), new(big.Int).Add(header.Difficulty, processor.bc.GetTd(header.ParentHash)))
- WriteHeader(processor.chainDb, header)
- processor.bc.mu.Unlock()
+ blockchain.mu.Lock()
+ WriteTd(blockchain.chainDb, header.Hash(), new(big.Int).Add(header.Difficulty, blockchain.GetTd(header.ParentHash)))
+ WriteHeader(blockchain.chainDb, header)
+ blockchain.mu.Unlock()
}
return nil
}
@@ -313,19 +327,19 @@ func TestBrokenBlockChain(t *testing.T) { testBrokenChain(t, true) }
func testBrokenChain(t *testing.T, full bool) {
// Make chain starting from genesis
- db, processor, err := newCanonical(10, full)
+ db, blockchain, err := newCanonical(10, full)
if err != nil {
t.Fatalf("failed to make new canonical chain: %v", err)
}
// Create a forked chain, and try to insert with a missing link
if full {
- chain := makeBlockChain(processor.bc.CurrentBlock(), 5, db, forkSeed)[1:]
- if err := testBlockChainImport(chain, processor); err == nil {
+ chain := makeBlockChain(blockchain.CurrentBlock(), 5, db, forkSeed)[1:]
+ if err := testBlockChainImport(chain, blockchain); err == nil {
t.Errorf("broken block chain not reported")
}
} else {
- chain := makeHeaderChain(processor.bc.CurrentHeader(), 5, db, forkSeed)[1:]
- if err := testHeaderChainImport(chain, processor); err == nil {
+ chain := makeHeaderChain(blockchain.CurrentHeader(), 5, db, forkSeed)[1:]
+ if err := testHeaderChainImport(chain, blockchain); err == nil {
t.Errorf("broken header chain not reported")
}
}
@@ -415,9 +429,14 @@ func TestChainMultipleInsertions(t *testing.T) {
type bproc struct{}
-func (bproc) Process(*types.Block) (vm.Logs, types.Receipts, error) { return nil, nil, nil }
-func (bproc) ValidateHeader(*types.Header, bool, bool) error { return nil }
-func (bproc) ValidateHeaderWithParent(*types.Header, *types.Header, bool, bool) error { return nil }
+func (bproc) ValidateBlock(*types.Block) error { return nil }
+func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return nil }
+func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error {
+ return nil
+}
+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 {
blocks := makeBlockChainWithDiff(genesis, d, seed)
@@ -459,7 +478,8 @@ func chm(genesis *types.Block, db ethdb.Database) *BlockChain {
bc.tdCache, _ = lru.New(100)
bc.blockCache, _ = lru.New(100)
bc.futureBlocks, _ = lru.New(100)
- bc.processor = bproc{}
+ bc.SetValidator(bproc{})
+ bc.SetProcessor(bproc{})
bc.ResetWithGenesisBlock(genesis)
return bc
@@ -612,12 +632,10 @@ func TestBlocksInsertNonceError(t *testing.T) { testInsertNonceError(t, true) }
func testInsertNonceError(t *testing.T, full bool) {
for i := 1; i < 25 && !t.Failed(); i++ {
// Create a pristine chain and database
- db, processor, err := newCanonical(0, full)
+ db, blockchain, err := newCanonical(0, full)
if err != nil {
t.Fatalf("failed to create pristine chain: %v", err)
}
- bc := processor.bc
-
// Create and insert a chain with a failing nonce
var (
failAt int
@@ -626,34 +644,33 @@ func testInsertNonceError(t *testing.T, full bool) {
failHash common.Hash
)
if full {
- blocks := makeBlockChain(processor.bc.CurrentBlock(), i, db, 0)
+ blocks := makeBlockChain(blockchain.CurrentBlock(), i, db, 0)
failAt = rand.Int() % len(blocks)
failNum = blocks[failAt].NumberU64()
failHash = blocks[failAt].Hash()
- processor.bc.pow = failPow{failNum}
- processor.Pow = failPow{failNum}
+ blockchain.pow = failPow{failNum}
- failRes, err = processor.bc.InsertChain(blocks)
+ failRes, err = blockchain.InsertChain(blocks)
} else {
- headers := makeHeaderChain(processor.bc.CurrentHeader(), i, db, 0)
+ headers := makeHeaderChain(blockchain.CurrentHeader(), i, db, 0)
failAt = rand.Int() % len(headers)
failNum = headers[failAt].Number.Uint64()
failHash = headers[failAt].Hash()
- processor.bc.pow = failPow{failNum}
- processor.Pow = failPow{failNum}
+ blockchain.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.
if failRes != failAt {
t.Errorf("test %d: failure index mismatch: have %d, want %d", i, failRes, failAt)
}
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)
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.
for j := 0; j < i-failAt; j++ {
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)
}
} 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)
}
}
@@ -711,7 +728,6 @@ func TestFastVsFullChains(t *testing.T) {
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux))
- archive.SetProcessor(NewBlockProcessor(archiveDb, FakePow{}, archive, new(event.TypeMux)))
if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err)
@@ -720,7 +736,6 @@ func TestFastVsFullChains(t *testing.T) {
fastDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux))
- fast.SetProcessor(NewBlockProcessor(fastDb, FakePow{}, fast, new(event.TypeMux)))
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
@@ -797,7 +812,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux))
- archive.SetProcessor(NewBlockProcessor(archiveDb, FakePow{}, archive, new(event.TypeMux)))
if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err)
@@ -810,7 +824,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
fastDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux))
- fast.SetProcessor(NewBlockProcessor(fastDb, FakePow{}, fast, new(event.TypeMux)))
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
@@ -830,7 +843,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
lightDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds})
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 {
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.
evmux := &event.TypeMux{}
- chainman, _ := NewBlockChain(db, FakePow{}, evmux)
- chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux))
- if i, err := chainman.InsertChain(chain); err != nil {
+ blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
+ if i, err := blockchain.InsertChain(chain); err != nil {
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
}
})
- if _, err := chainman.InsertChain(chain); err != nil {
+ if _, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert forked chain: %v", err)
}
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 56e37a0fc..f1ada487f 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -214,7 +214,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
// 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
// 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
db, _ := ethdb.NewMemDatabase()
evmux := &event.TypeMux{}
@@ -223,23 +223,20 @@ func newCanonical(n int, full bool) (ethdb.Database, *BlockProcessor, error) {
genesis, _ := WriteTestNetGenesisBlock(db, 0)
blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
- processor := NewBlockProcessor(db, FakePow{}, blockchain, evmux)
- processor.bc.SetProcessor(processor)
-
// Create and inject the requested chain
if n == 0 {
- return db, processor, nil
+ return db, blockchain, nil
}
if full {
// Full block-chain requested
blocks := makeBlockChain(genesis, n, db, canonicalSeed)
_, err := blockchain.InsertChain(blocks)
- return db, processor, err
+ return db, blockchain, err
}
// Header-only chain requested
headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed)
_, err := blockchain.InsertHeaderChain(headers, 1)
- return db, processor, err
+ return db, blockchain, err
}
// makeHeaderChain creates a deterministic chain of headers rooted at parent.
diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go
index 7f47cf288..b9c1d89b7 100644
--- a/core/chain_makers_test.go
+++ b/core/chain_makers_test.go
@@ -77,15 +77,14 @@ func ExampleGenerateChain() {
// Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{}
- chainman, _ := NewBlockChain(db, FakePow{}, evmux)
- chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux))
- if i, err := chainman.InsertChain(chain); err != nil {
+ blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
+ if i, err := blockchain.InsertChain(chain); err != nil {
fmt.Printf("insert error (block %d): %v\n", i, err)
return
}
- state, _ := chainman.State()
- fmt.Printf("last block: #%d\n", chainman.CurrentBlock().Number())
+ state, _ := blockchain.State()
+ fmt.Printf("last block: #%d\n", blockchain.CurrentBlock().Number())
fmt.Println("balance of addr1:", state.GetBalance(addr1))
fmt.Println("balance of addr2:", state.GetBalance(addr2))
fmt.Println("balance of addr3:", state.GetBalance(addr3))
diff --git a/core/gaspool.go b/core/gaspool.go
new file mode 100644
index 000000000..2ef07c754
--- /dev/null
+++ b/core/gaspool.go
@@ -0,0 +1,46 @@
+// 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"
+
+// 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()
+}
diff --git a/core/state_processor.go b/core/state_processor.go
new file mode 100644
index 000000000..d9c24935d
--- /dev/null
+++ b/core/state_processor.go
@@ -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)
+}
diff --git a/core/types.go b/core/types.go
new file mode 100644
index 000000000..027f628b1
--- /dev/null
+++ b/core/types.go
@@ -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
+}
diff --git a/core/types/common.go b/core/vm/runtime/doc.go
index fe682f98a..a3b464a7d 100644
--- a/core/types/common.go
+++ b/core/vm/runtime/doc.go
@@ -14,12 +14,5 @@
// 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 types
-
-import "github.com/ethereum/go-ethereum/core/vm"
-
-type BlockProcessor interface {
- Process(*Block) (vm.Logs, Receipts, error)
- ValidateHeader(*Header, bool, bool) error
- ValidateHeaderWithParent(*Header, *Header, bool, bool) error
-}
+// Package runtime provides a basic execution model for executing EVM code.
+package runtime
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
new file mode 100644
index 000000000..22f9ea14d
--- /dev/null
+++ b/core/vm/runtime/env.go
@@ -0,0 +1,106 @@
+// 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 runtime
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/vm"
+)
+
+// Env is a basic runtime environment required for running the EVM.
+type Env struct {
+ depth int
+ state *state.StateDB
+
+ origin common.Address
+ coinbase common.Address
+
+ number *big.Int
+ time *big.Int
+ difficulty *big.Int
+ gasLimit *big.Int
+
+ logs []vm.StructLog
+
+ getHashFn func(uint64) common.Hash
+}
+
+// NewEnv returns a new vm.Environment
+func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
+ return &Env{
+ state: state,
+ origin: cfg.Origin,
+ coinbase: cfg.Coinbase,
+ number: cfg.BlockNumber,
+ time: cfg.Time,
+ difficulty: cfg.Difficulty,
+ gasLimit: cfg.GasLimit,
+ }
+}
+
+func (self *Env) StructLogs() []vm.StructLog {
+ return self.logs
+}
+
+func (self *Env) AddStructLog(log vm.StructLog) {
+ self.logs = append(self.logs, log)
+}
+
+func (self *Env) Origin() common.Address { return self.origin }
+func (self *Env) BlockNumber() *big.Int { return self.number }
+func (self *Env) Coinbase() common.Address { return self.coinbase }
+func (self *Env) Time() *big.Int { return self.time }
+func (self *Env) Difficulty() *big.Int { return self.difficulty }
+func (self *Env) Db() vm.Database { return self.state }
+func (self *Env) GasLimit() *big.Int { return self.gasLimit }
+func (self *Env) VmType() vm.Type { return vm.StdVmTy }
+func (self *Env) GetHash(n uint64) common.Hash {
+ return self.getHashFn(n)
+}
+func (self *Env) AddLog(log *vm.Log) {
+ self.state.AddLog(log)
+}
+func (self *Env) Depth() int { return self.depth }
+func (self *Env) SetDepth(i int) { self.depth = i }
+func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
+ return self.state.GetBalance(from).Cmp(balance) >= 0
+}
+func (self *Env) MakeSnapshot() vm.Database {
+ return self.state.Copy()
+}
+func (self *Env) SetSnapshot(copy vm.Database) {
+ self.state.Set(copy.(*state.StateDB))
+}
+
+func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
+ core.Transfer(from, to, amount)
+}
+
+func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
+ return core.Call(self, caller, addr, data, gas, price, value)
+}
+func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
+ return core.CallCode(self, caller, addr, data, gas, price, value)
+}
+
+func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
+ return core.Create(self, caller, data, gas, price, value)
+}
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
new file mode 100644
index 000000000..dd3aa1b0b
--- /dev/null
+++ b/core/vm/runtime/runtime.go
@@ -0,0 +1,121 @@
+// 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 runtime
+
+import (
+ "math/big"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb"
+)
+
+// Config is a basic type specifing certain configuration flags for running
+// the EVM.
+type Config struct {
+ Difficulty *big.Int
+ Origin common.Address
+ Coinbase common.Address
+ BlockNumber *big.Int
+ Time *big.Int
+ GasLimit *big.Int
+ GasPrice *big.Int
+ Value *big.Int
+ DisableJit bool // "disable" so it's enabled by default
+ Debug bool
+
+ GetHashFn func(n uint64) common.Hash
+}
+
+// sets defaults on the config
+func setDefaults(cfg *Config) {
+ if cfg.Difficulty == nil {
+ cfg.Difficulty = new(big.Int)
+ }
+ if cfg.Time == nil {
+ cfg.Time = big.NewInt(time.Now().Unix())
+ }
+ if cfg.GasLimit == nil {
+ cfg.GasLimit = new(big.Int).Set(common.MaxBig)
+ }
+ if cfg.GasPrice == nil {
+ cfg.GasPrice = new(big.Int)
+ }
+ if cfg.Value == nil {
+ cfg.Value = new(big.Int)
+ }
+ if cfg.BlockNumber == nil {
+ cfg.BlockNumber = new(big.Int)
+ }
+ if cfg.GetHashFn == nil {
+ cfg.GetHashFn = func(n uint64) common.Hash {
+ return common.BytesToHash(crypto.Sha3([]byte(new(big.Int).SetUint64(n).String())))
+ }
+ }
+}
+
+// Execute executes the code using the input as call data during the execution.
+// It returns the EVM's return value, the new state and an error if it failed.
+//
+// Executes sets up a in memory, temporarily, environment for the execution of
+// the given code. It enabled the JIT by default and make sure that it's restored
+// to it's original state afterwards.
+func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
+ if cfg == nil {
+ cfg = new(Config)
+ }
+ setDefaults(cfg)
+
+ // defer the call to setting back the original values
+ defer func(debug, forceJit, enableJit bool) {
+ vm.Debug = debug
+ vm.ForceJit = forceJit
+ vm.EnableJit = enableJit
+ }(vm.Debug, vm.ForceJit, vm.EnableJit)
+
+ vm.ForceJit = !cfg.DisableJit
+ vm.EnableJit = !cfg.DisableJit
+ vm.Debug = cfg.Debug
+
+ var (
+ db, _ = ethdb.NewMemDatabase()
+ statedb, _ = state.New(common.Hash{}, db)
+ vmenv = NewEnv(cfg, statedb)
+ sender = statedb.CreateAccount(cfg.Origin)
+ receiver = statedb.CreateAccount(common.StringToAddress("contract"))
+ )
+ // set the receiver's (the executing contract) code for execution.
+ receiver.SetCode(code)
+
+ // Call the code with the given configuration.
+ ret, err := vmenv.Call(
+ sender,
+ receiver.Address(),
+ input,
+ cfg.GasLimit,
+ cfg.GasPrice,
+ cfg.Value,
+ )
+
+ if cfg.Debug {
+ vm.StdErrFormat(vmenv.StructLogs())
+ }
+ return ret, statedb, err
+}
diff --git a/core/manager.go b/core/vm/runtime/runtime_example_test.go
index 289c87c11..b7d0ddc38 100644
--- a/core/manager.go
+++ b/core/vm/runtime/runtime_example_test.go
@@ -14,21 +14,21 @@
// 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
+package runtime_test
import (
- "github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/event"
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/vm/runtime"
)
-// 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
+func ExampleExecute() {
+ ret, _, err := runtime.Execute(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00"), nil, nil)
+ if err != nil {
+ fmt.Println(err)
+ }
+ fmt.Println(ret)
+ // Output:
+ // [96 96 96 64 82 96 8 86 91 0]
}
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
new file mode 100644
index 000000000..773a0163e
--- /dev/null
+++ b/core/vm/runtime/runtime_test.go
@@ -0,0 +1,120 @@
+// 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 runtime
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/vm"
+)
+
+func TestDefaults(t *testing.T) {
+ cfg := new(Config)
+ setDefaults(cfg)
+
+ if cfg.Difficulty == nil {
+ t.Error("expected difficulty to be non nil")
+ }
+
+ if cfg.Time == nil {
+ t.Error("expected time to be non nil")
+ }
+ if cfg.GasLimit == nil {
+ t.Error("expected time to be non nil")
+ }
+ if cfg.GasPrice == nil {
+ t.Error("expected time to be non nil")
+ }
+ if cfg.Value == nil {
+ t.Error("expected time to be non nil")
+ }
+ if cfg.GetHashFn == nil {
+ t.Error("expected time to be non nil")
+ }
+ if cfg.BlockNumber == nil {
+ t.Error("expected block number to be non nil")
+ }
+}
+
+func TestEnvironment(t *testing.T) {
+ defer func() {
+ if r := recover(); r != nil {
+ t.Fatalf("crashed with: %v", r)
+ }
+ }()
+
+ Execute([]byte{
+ byte(vm.DIFFICULTY),
+ byte(vm.TIMESTAMP),
+ byte(vm.GASLIMIT),
+ byte(vm.PUSH1),
+ byte(vm.ORIGIN),
+ byte(vm.BLOCKHASH),
+ byte(vm.COINBASE),
+ }, nil, nil)
+}
+
+func TestRestoreDefaults(t *testing.T) {
+ Execute(nil, nil, &Config{Debug: true})
+ if vm.ForceJit {
+ t.Error("expected force jit to be disabled")
+ }
+
+ if vm.Debug {
+ t.Error("expected debug to be disabled")
+ }
+
+ if vm.EnableJit {
+ t.Error("expected jit to be disabled")
+ }
+}
+
+func BenchmarkCall(b *testing.B) {
+ var definition = `[{"constant":true,"inputs":[],"name":"seller","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"abort","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"refund","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"buyer","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmReceived","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmPurchase","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"},{"anonymous":false,"inputs":[],"name":"Aborted","type":"event"},{"anonymous":false,"inputs":[],"name":"PurchaseConfirmed","type":"event"},{"anonymous":false,"inputs":[],"name":"ItemReceived","type":"event"},{"anonymous":false,"inputs":[],"name":"Refunded","type":"event"}]`
+
+ var code = common.Hex2Bytes("6060604052361561006c5760e060020a600035046308551a53811461007457806335a063b4146100865780633fa4f245146100a6578063590e1ae3146100af5780637150d8ae146100cf57806373fac6f0146100e1578063c19d93fb146100fe578063d696069714610112575b610131610002565b610133600154600160a060020a031681565b610131600154600160a060020a0390811633919091161461015057610002565b61014660005481565b610131600154600160a060020a039081163391909116146102d557610002565b610133600254600160a060020a031681565b610131600254600160a060020a0333811691161461023757610002565b61014660025460ff60a060020a9091041681565b61013160025460009060ff60a060020a9091041681146101cc57610002565b005b600160a060020a03166060908152602090f35b6060908152602090f35b60025460009060a060020a900460ff16811461016b57610002565b600154600160a060020a03908116908290301631606082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f72c874aeff0b183a56e2b79c71b46e1aed4dee5e09862134b8821ba2fddbf8bf9250a150565b80546002023414806101dd57610002565b6002805460a060020a60ff021973ffffffffffffffffffffffffffffffffffffffff1990911633171660a060020a1790557fd5d55c8a68912e9a110618df8d5e2e83b8d83211c57a8ddd1203df92885dc881826060a15050565b60025460019060a060020a900460ff16811461025257610002565b60025460008054600160a060020a0390921691606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517fe89152acd703c9d8c7d28829d443260b411454d45394e7995815140c8cbcbcf79250a150565b60025460019060a060020a900460ff1681146102f057610002565b6002805460008054600160a060020a0390921692909102606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f8616bbbbad963e4e65b1366f1d75dfb63f9e9704bbbf91fb01bec70849906cf79250a15056")
+
+ abi, err := abi.JSON(strings.NewReader(definition))
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ cpurchase, err := abi.Pack("confirmPurchase")
+ if err != nil {
+ b.Fatal(err)
+ }
+ creceived, err := abi.Pack("confirmReceived")
+ if err != nil {
+ b.Fatal(err)
+ }
+ refund, err := abi.Pack("refund")
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ for j := 0; j < 400; j++ {
+ Execute(code, cpurchase, nil)
+ Execute(code, creceived, nil)
+ Execute(code, refund, nil)
+ }
+ }
+}
diff --git a/crypto/secp256k1/panic_cb.go b/crypto/secp256k1/panic_cb.go
new file mode 100644
index 000000000..e0e9034ee
--- /dev/null
+++ b/crypto/secp256k1/panic_cb.go
@@ -0,0 +1,33 @@
+// 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 secp256k1
+
+import "C"
+import "unsafe"
+
+// Callbacks for converting libsecp256k1 internal faults into
+// recoverable Go panics.
+
+//export secp256k1GoPanicIllegal
+func secp256k1GoPanicIllegal(msg *C.char, data unsafe.Pointer) {
+ panic("illegal argument: " + C.GoString(msg))
+}
+
+//export secp256k1GoPanicError
+func secp256k1GoPanicError(msg *C.char, data unsafe.Pointer) {
+ panic("internal error: " + C.GoString(msg))
+}
diff --git a/crypto/secp256k1/secp256.go b/crypto/secp256k1/secp256.go
index a2104016a..41a5608a5 100644
--- a/crypto/secp256k1/secp256.go
+++ b/crypto/secp256k1/secp256.go
@@ -20,11 +20,11 @@ package secp256k1
/*
#cgo CFLAGS: -I./libsecp256k1
-#cgo darwin CFLAGS: -I/usr/local/include -I/opt/pkg/include
+#cgo darwin CFLAGS: -I/usr/local/include
#cgo freebsd CFLAGS: -I/usr/local/include
#cgo linux,arm CFLAGS: -I/usr/local/arm/include
#cgo LDFLAGS: -lgmp
-#cgo darwin LDFLAGS: -L/usr/local/lib -L/opt/pkg/lib
+#cgo darwin LDFLAGS: -L/usr/local/lib
#cgo freebsd LDFLAGS: -L/usr/local/lib
#cgo linux,arm LDFLAGS: -L/usr/local/arm/lib
#define USE_NUM_GMP
@@ -35,11 +35,14 @@ package secp256k1
#define NDEBUG
#include "./libsecp256k1/src/secp256k1.c"
#include "./libsecp256k1/src/modules/recovery/main_impl.h"
+
+typedef void (*callbackFunc) (const char* msg, void* data);
+extern void secp256k1GoPanicIllegal(const char* msg, void* data);
+extern void secp256k1GoPanicError(const char* msg, void* data);
*/
import "C"
import (
- "bytes"
"errors"
"unsafe"
@@ -62,8 +65,16 @@ var context *C.secp256k1_context
func init() {
// around 20 ms on a modern CPU.
context = C.secp256k1_context_create(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY
+ C.secp256k1_context_set_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil)
+ C.secp256k1_context_set_error_callback(context, C.callbackFunc(C.secp256k1GoPanicError), nil)
}
+var (
+ ErrInvalidMsgLen = errors.New("invalid message length for signature recovery")
+ ErrInvalidSignatureLen = errors.New("invalid signature length")
+ ErrInvalidRecoveryID = errors.New("invalid signature recovery id")
+)
+
func GenerateKeyPair() ([]byte, []byte) {
var seckey []byte = randentropy.GetEntropyCSPRNG(32)
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
@@ -177,69 +188,20 @@ func VerifySeckeyValidity(seckey []byte) error {
return nil
}
-func VerifySignatureValidity(sig []byte) bool {
- //64+1
- if len(sig) != 65 {
- return false
- }
- //malleability check, highest bit must be 1
- if (sig[32] & 0x80) == 0x80 {
- return false
- }
- //recovery id check
- if sig[64] >= 4 {
- return false
- }
-
- return true
-}
-
-//for compressed signatures, does not need pubkey
-func VerifySignature(msg []byte, sig []byte, pubkey1 []byte) error {
- if msg == nil || sig == nil || pubkey1 == nil {
- return errors.New("inputs must be non-nil")
- }
- if len(sig) != 65 {
- return errors.New("invalid signature length")
- }
- if len(pubkey1) != 65 {
- return errors.New("Invalid public key length")
- }
-
- //to enforce malleability, highest bit of S must be 0
- //S starts at 32nd byte
- if (sig[32] & 0x80) == 0x80 { //highest bit must be 1
- return errors.New("Signature not malleable")
- }
-
- if sig[64] >= 4 {
- return errors.New("Recover byte invalid")
- }
-
- // if pubkey recovered, signature valid
- pubkey2, err := RecoverPubkey(msg, sig)
- if err != nil {
- return err
- }
- if len(pubkey2) != 65 {
- return errors.New("Invalid recovered public key length")
- }
- if !bytes.Equal(pubkey1, pubkey2) {
- return errors.New("Public key does not match recovered public key")
- }
-
- return nil
-}
-
-// recovers a public key from the signature
+// RecoverPubkey returns the the public key of the signer.
+// msg must be the 32-byte hash of the message to be signed.
+// sig must be a 65-byte compact ECDSA signature containing the
+// recovery id as the last element.
func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
- if len(sig) != 65 {
- return nil, errors.New("Invalid signature length")
+ if len(msg) != 32 {
+ return nil, ErrInvalidMsgLen
+ }
+ if err := checkSignature(sig); err != nil {
+ return nil, err
}
msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
sig_ptr := (*C.uchar)(unsafe.Pointer(&sig[0]))
-
pubkey := make([]byte, 64)
/*
this slice is used for both the recoverable signature and the
@@ -248,17 +210,15 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
pubkey recovery is one bottleneck during load in Ethereum
*/
bytes65 := make([]byte, 65)
-
pubkey_ptr := (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey[0]))
recoverable_sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&bytes65[0]))
-
recid := C.int(sig[64])
+
ret := C.secp256k1_ecdsa_recoverable_signature_parse_compact(
context,
recoverable_sig_ptr,
sig_ptr,
recid)
-
if ret == C.int(0) {
return nil, errors.New("Failed to parse signature")
}
@@ -269,20 +229,28 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
recoverable_sig_ptr,
msg_ptr,
)
-
if ret == C.int(0) {
return nil, errors.New("Failed to recover public key")
- } else {
- serialized_pubkey_ptr := (*C.uchar)(unsafe.Pointer(&bytes65[0]))
-
- var output_len C.size_t
- C.secp256k1_ec_pubkey_serialize( // always returns 1
- context,
- serialized_pubkey_ptr,
- &output_len,
- pubkey_ptr,
- 0, // SECP256K1_EC_COMPRESSED
- )
- return bytes65, nil
}
+
+ serialized_pubkey_ptr := (*C.uchar)(unsafe.Pointer(&bytes65[0]))
+ var output_len C.size_t
+ C.secp256k1_ec_pubkey_serialize( // always returns 1
+ context,
+ serialized_pubkey_ptr,
+ &output_len,
+ pubkey_ptr,
+ 0, // SECP256K1_EC_COMPRESSED
+ )
+ return bytes65, nil
+}
+
+func checkSignature(sig []byte) error {
+ if len(sig) != 65 {
+ return ErrInvalidSignatureLen
+ }
+ if sig[64] >= 4 {
+ return ErrInvalidRecoveryID
+ }
+ return nil
}
diff --git a/crypto/secp256k1/secp256_test.go b/crypto/secp256k1/secp256_test.go
index 45c448f3c..cb71ea5e7 100644
--- a/crypto/secp256k1/secp256_test.go
+++ b/crypto/secp256k1/secp256_test.go
@@ -56,6 +56,17 @@ func TestSignatureValidity(t *testing.T) {
}
}
+func TestInvalidRecoveryID(t *testing.T) {
+ _, seckey := GenerateKeyPair()
+ msg := randentropy.GetEntropyCSPRNG(32)
+ sig, _ := Sign(msg, seckey)
+ sig[64] = 99
+ _, err := RecoverPubkey(msg, sig)
+ if err != ErrInvalidRecoveryID {
+ t.Fatalf("got %q, want %q", err, ErrInvalidRecoveryID)
+ }
+}
+
func TestSignAndRecover(t *testing.T) {
pubkey1, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
@@ -70,10 +81,6 @@ func TestSignAndRecover(t *testing.T) {
if !bytes.Equal(pubkey1, pubkey2) {
t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2)
}
- err = VerifySignature(msg, sig, pubkey1)
- if err != nil {
- t.Errorf("signature verification error: %s", err)
- }
}
func TestRandomMessagesWithSameKey(t *testing.T) {
diff --git a/eth/backend.go b/eth/backend.go
index 761a17a8f..5bd6ac55d 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -231,9 +231,7 @@ type Ethereum struct {
chainDb ethdb.Database // Block chain database
dappDb ethdb.Database // Dapp database
- //*** SERVICES ***
- // State manager for processing new blocks and managing the over all states
- blockProcessor *core.BlockProcessor
+ // Handlers
txPool *core.TxPool
blockchain *core.BlockChain
accountManager *accounts.Manager
@@ -407,8 +405,6 @@ func New(config *Config) (*Ethereum, error) {
newPool := core.NewTxPool(eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
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 {
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) Logger() logger.LogSystem { return s.logger }
-func (s *Ethereum) Name() string { return s.net.Name }
-func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
-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) Whisper() *whisper.Whisper { return s.whisper }
-func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
-func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
-func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
-func (s *Ethereum) IsListening() bool { return true } // Always listening
-func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
-func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
-func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers }
-func (s *Ethereum) ClientVersion() string { return s.clientVersion }
-func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
-func (s *Ethereum) NetVersion() int { return s.netVersionId }
-func (s *Ethereum) ShhVersion() int { return s.shhVersionId }
-func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
+func (s *Ethereum) Name() string { return s.net.Name }
+func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
+func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
+func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
+func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
+func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
+func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
+func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
+func (s *Ethereum) IsListening() bool { return true } // Always listening
+func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
+func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
+func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers }
+func (s *Ethereum) ClientVersion() string { return s.clientVersion }
+func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
+func (s *Ethereum) NetVersion() int { return s.netVersionId }
+func (s *Ethereum) ShhVersion() int { return s.shhVersionId }
+func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
// Start the ethereum
func (s *Ethereum) Start() error {
diff --git a/eth/gasprice.go b/eth/gasprice.go
index b752c22dd..e0de89e62 100644
--- a/eth/gasprice.go
+++ b/eth/gasprice.go
@@ -166,7 +166,7 @@ func (self *GasPriceOracle) processBlock(block *types.Block) {
func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int {
gasUsed := big.NewInt(0)
- receipts := self.eth.BlockProcessor().GetBlockReceipts(block.Hash())
+ receipts := core.GetBlockReceipts(self.eth.ChainDb(), block.Hash())
if len(receipts) > 0 {
if cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil {
gasUsed = receipts[len(receipts)-1].CumulativeGasUsed
diff --git a/eth/helper_test.go b/eth/helper_test.go
index 65fccf7b4..bbd1fb818 100644
--- a/eth/helper_test.go
+++ b/eth/helper_test.go
@@ -35,9 +35,7 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core
db, _ = ethdb.NewMemDatabase()
genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds})
blockchain, _ = core.NewBlockChain(db, pow, evmux)
- blockproc = core.NewBlockProcessor(db, pow, blockchain, evmux)
)
- blockchain.SetProcessor(blockproc)
chain, _ := core.GenerateChain(genesis, db, blocks, generator)
if _, err := blockchain.InsertChain(chain); err != nil {
panic(err)
diff --git a/miner/worker.go b/miner/worker.go
index 2d072ef60..238f1a4bf 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -100,7 +100,7 @@ type worker struct {
eth core.Backend
chain *core.BlockChain
- proc *core.BlockProcessor
+ proc core.Validator
chainDb ethdb.Database
coinbase common.Address
@@ -131,7 +131,7 @@ func newWorker(coinbase common.Address, eth core.Backend) *worker {
recv: make(chan *Result, resultQueueSize),
gasPrice: new(big.Int),
chain: eth.BlockChain(),
- proc: eth.BlockProcessor(),
+ proc: eth.BlockChain().Validator(),
possibleUncles: make(map[common.Hash]*types.Block),
coinbase: coinbase,
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
if atomic.LoadInt32(&self.mining) == 0 {
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()
}
}
@@ -290,7 +290,9 @@ func (self *worker) wait() {
glog.V(logger.Error).Infoln("Invalid block found during mining")
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)
continue
}
@@ -516,7 +518,7 @@ func (self *worker) commitNewWork() {
transactions := append(singleTxOwner, multiTxOwner...)
*/
- work.commitTransactions(transactions, self.gasPrice, self.proc)
+ work.commitTransactions(transactions, self.gasPrice, self.chain)
self.eth.TxPool().RemoveTransactions(work.lowGasTxs)
// compute uncles for the new block.
@@ -575,9 +577,8 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
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)
-
for _, tx := range transactions {
// We can skip err. It has already been validated in the tx pool
from, _ := tx.From()
@@ -615,7 +616,7 @@ func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *b
env.state.StartRecord(tx.Hash(), common.Hash{}, 0)
- err := env.commitTransaction(tx, proc, gp)
+ err := env.commitTransaction(tx, bc, gp)
switch {
case core.IsGasLimitErr(err):
// 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()
- 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 {
env.state.Set(snap)
return err
diff --git a/rpc/api/debug.go b/rpc/api/debug.go
index d2cbc7f19..a6faa335e 100644
--- a/rpc/api/debug.go
+++ b/rpc/api/debug.go
@@ -22,6 +22,7 @@ import (
"time"
"github.com/ethereum/ethash"
+ "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth"
@@ -166,11 +167,30 @@ func (self *debugApi) ProcessBlock(req *shared.Request) (interface{}, error) {
defer func() { vm.Debug = old }()
vm.Debug = true
- _, err := self.ethereum.BlockProcessor().RetryProcess(block)
- if err == nil {
- return true, nil
+ var (
+ blockchain = self.ethereum.BlockChain()
+ 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) {
diff --git a/rpc/api/utils.go b/rpc/api/utils.go
index 5a3ade46b..8351e88d3 100644
--- a/rpc/api/utils.go
+++ b/rpc/api/utils.go
@@ -130,7 +130,7 @@ var (
},
"shh": []string{
"post",
- "newIdentify",
+ "newIdentity",
"hasIdentity",
"newGroup",
"addToGroup",
diff --git a/xeth/xeth.go b/xeth/xeth.go
index 35e6dd52d..243bef0b8 100644
--- a/xeth/xeth.go
+++ b/xeth/xeth.go
@@ -379,7 +379,7 @@ func (self *XEth) CurrentBlock() *types.Block {
}
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 {