aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorobscuren <geffobscura@gmail.com>2015-06-10 03:12:25 +0800
committerobscuren <geffobscura@gmail.com>2015-06-10 03:12:25 +0800
commitbac9a94ddf20dc530966cbf6cd384aaf94aedc77 (patch)
tree0ced967e60315698cc5056a984d7678c417bc1ce /core
parent0e703d92ac9df61e2ededa8c895c70ded101a607 (diff)
parent14994fa21bf6f05554ff370d41005d06b68d20a5 (diff)
downloaddexon-bac9a94ddf20dc530966cbf6cd384aaf94aedc77.tar
dexon-bac9a94ddf20dc530966cbf6cd384aaf94aedc77.tar.gz
dexon-bac9a94ddf20dc530966cbf6cd384aaf94aedc77.tar.bz2
dexon-bac9a94ddf20dc530966cbf6cd384aaf94aedc77.tar.lz
dexon-bac9a94ddf20dc530966cbf6cd384aaf94aedc77.tar.xz
dexon-bac9a94ddf20dc530966cbf6cd384aaf94aedc77.tar.zst
dexon-bac9a94ddf20dc530966cbf6cd384aaf94aedc77.zip
Merge branch 'release/0.9.28'
Diffstat (limited to 'core')
-rw-r--r--core/block_processor.go15
-rw-r--r--core/block_processor_test.go9
-rw-r--r--core/blocks.go1
-rw-r--r--core/chain_makers.go5
-rw-r--r--core/chain_manager.go214
-rw-r--r--core/chain_manager_test.go104
-rw-r--r--core/error.go17
-rw-r--r--core/filter.go15
-rw-r--r--core/genesis.go8
-rw-r--r--core/state/managed_state.go2
-rw-r--r--core/state_transition.go35
-rw-r--r--core/transaction_pool.go371
-rw-r--r--core/transaction_pool_test.go130
-rw-r--r--core/types/block.go24
-rw-r--r--core/types/transaction.go38
-rw-r--r--core/types/transaction_test.go6
-rw-r--r--core/vm/analysis.go41
-rw-r--r--core/vm/context.go11
-rw-r--r--core/vm/contracts.go (renamed from core/vm/address.go)20
-rw-r--r--core/vm/environment.go40
-rw-r--r--core/vm/main_test.go9
-rw-r--r--core/vm/opcodes.go (renamed from core/vm/types.go)0
-rw-r--r--core/vm/vm.go27
-rw-r--r--core/vm/vm_test.go3
24 files changed, 676 insertions, 469 deletions
diff --git a/core/block_processor.go b/core/block_processor.go
index ca205ee86..190e72694 100644
--- a/core/block_processor.go
+++ b/core/block_processor.go
@@ -38,14 +38,12 @@ type BlockProcessor struct {
// Proof of work used for validating
Pow pow.PoW
- txpool *TxPool
-
events event.Subscription
eventMux *event.TypeMux
}
-func NewBlockProcessor(db, extra common.Database, pow pow.PoW, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
+func NewBlockProcessor(db, extra common.Database, pow pow.PoW, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
sm := &BlockProcessor{
db: db,
extraDb: extra,
@@ -53,7 +51,6 @@ func NewBlockProcessor(db, extra common.Database, pow pow.PoW, txpool *TxPool, c
Pow: pow,
bc: chainManager,
eventMux: eventMux,
- txpool: txpool,
}
return sm
@@ -178,7 +175,6 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs state.Logs, err erro
return nil, ParentError(header.ParentHash)
}
parent := sm.bc.GetBlock(header.ParentHash)
-
return sm.processWithParent(block, parent)
}
@@ -254,14 +250,9 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
return nil, err
}
- // Calculate the td for this block
- //td = CalculateTD(block, parent)
// Sync the current block's state to the database
state.Sync()
- // Remove transactions from the pool
- sm.txpool.RemoveTransactions(block.Transactions())
-
// This puts transactions in a extra db for rpc
for i, tx := range block.Transactions() {
putTx(sm.extraDb, tx, block, uint64(i))
@@ -364,8 +355,8 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty
return UncleError("uncle[%d](%x) is ancestor", i, hash[:4])
}
- if !ancestors.Has(uncle.ParentHash) {
- return UncleError("uncle[%d](%x)'s parent unknown (%x)", i, hash[:4], uncle.ParentHash[0:4])
+ if !ancestors.Has(uncle.ParentHash) || uncle.ParentHash == parent.Hash() {
+ return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4])
}
if err := sm.ValidateHeader(uncle, ancestorHeaders[uncle.ParentHash], true); err != nil {
diff --git a/core/block_processor_test.go b/core/block_processor_test.go
index 72b173a71..97b80038d 100644
--- a/core/block_processor_test.go
+++ b/core/block_processor_test.go
@@ -1,6 +1,7 @@
package core
import (
+ "fmt"
"math/big"
"testing"
@@ -16,8 +17,12 @@ func proc() (*BlockProcessor, *ChainManager) {
db, _ := ethdb.NewMemDatabase()
var mux event.TypeMux
- chainMan := NewChainManager(db, db, thePow(), &mux)
- return NewBlockProcessor(db, db, ezp.New(), nil, chainMan, &mux), chainMan
+ genesis := GenesisBlock(0, db)
+ chainMan, err := NewChainManager(genesis, db, db, thePow(), &mux)
+ if err != nil {
+ fmt.Println(err)
+ }
+ return NewBlockProcessor(db, db, ezp.New(), chainMan, &mux), chainMan
}
func TestNumber(t *testing.T) {
diff --git a/core/blocks.go b/core/blocks.go
index 83727ff62..f0d39e1e1 100644
--- a/core/blocks.go
+++ b/core/blocks.go
@@ -7,4 +7,5 @@ var BadHashes = map[common.Hash]bool{
common.HexToHash("f269c503aed286caaa0d114d6a5320e70abbc2febe37953207e76a2873f2ba79"): true,
common.HexToHash("38f5bbbffd74804820ffa4bab0cd540e9de229725afb98c1a7e57936f4a714bc"): true,
common.HexToHash("7064455b364775a16afbdecd75370e912c6e2879f202eda85b9beae547fff3ac"): true,
+ common.HexToHash("5b7c80070a6eff35f3eb3181edb023465c776d40af2885571e1bc4689f3a44d8"): true,
}
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 44f17cc33..4e685f599 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -108,7 +108,7 @@ func makeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Dat
// Create a new chain manager starting from given block
// Effectively a fork factory
func newChainManager(block *types.Block, eventMux *event.TypeMux, db common.Database) *ChainManager {
- genesis := GenesisBlock(db)
+ genesis := GenesisBlock(0, db)
bc := &ChainManager{blockDb: db, stateDb: db, genesisBlock: genesis, eventMux: eventMux, pow: FakePow{}}
bc.txState = state.ManageState(state.New(genesis.Root(), db))
bc.futureBlocks = NewBlockCache(1000)
@@ -124,8 +124,7 @@ func newChainManager(block *types.Block, eventMux *event.TypeMux, db common.Data
// block processor with fake pow
func newBlockProcessor(db common.Database, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
chainMan := newChainManager(nil, eventMux, db)
- txpool := NewTxPool(eventMux, chainMan.State, chainMan.GasLimit)
- bman := NewBlockProcessor(db, db, FakePow{}, txpool, chainMan, eventMux)
+ bman := NewBlockProcessor(db, db, FakePow{}, chainMan, eventMux)
return bman
}
diff --git a/core/chain_manager.go b/core/chain_manager.go
index 3e8ef6fd8..6897c453c 100644
--- a/core/chain_manager.go
+++ b/core/chain_manager.go
@@ -30,8 +30,9 @@ var (
)
const (
- blockCacheLimit = 10000
- maxFutureBlocks = 256
+ blockCacheLimit = 10000
+ maxFutureBlocks = 256
+ maxTimeFutureBlocks = 30
)
func CalcDifficulty(block, parent *types.Header) *big.Int {
@@ -55,10 +56,7 @@ func CalcTD(block, parent *types.Block) *big.Int {
if parent == nil {
return block.Difficulty()
}
-
- td := new(big.Int).Add(parent.Td, block.Header().Difficulty)
-
- return td
+ return new(big.Int).Add(parent.Td, block.Header().Difficulty)
}
func CalcGasLimit(parent *types.Block) *big.Int {
@@ -108,16 +106,23 @@ type ChainManager struct {
pow pow.PoW
}
-func NewChainManager(blockDb, stateDb common.Database, pow pow.PoW, mux *event.TypeMux) *ChainManager {
+func NewChainManager(genesis *types.Block, blockDb, stateDb common.Database, pow pow.PoW, mux *event.TypeMux) (*ChainManager, error) {
bc := &ChainManager{
blockDb: blockDb,
stateDb: stateDb,
- genesisBlock: GenesisBlock(stateDb),
+ genesisBlock: GenesisBlock(42, stateDb),
eventMux: mux,
quit: make(chan struct{}),
cache: NewBlockCache(blockCacheLimit),
pow: pow,
}
+
+ // Check the genesis block given to the chain manager. If the genesis block mismatches block number 0
+ // throw an error. If no block or the same block's found continue.
+ if g := bc.GetBlockByNumber(0); g != nil && g.Hash() != genesis.Hash() {
+ return nil, fmt.Errorf("Genesis mismatch. Maybe different nonce (%d vs %d)? %x / %x", g.Nonce(), genesis.Nonce(), g.Hash().Bytes()[:4], genesis.Hash().Bytes()[:4])
+ }
+ bc.genesisBlock = genesis
bc.setLastState()
// Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain
@@ -143,7 +148,7 @@ func NewChainManager(blockDb, stateDb common.Database, pow pow.PoW, mux *event.T
go bc.update()
- return bc
+ return bc, nil
}
func (bc *ChainManager) SetHead(head *types.Block) {
@@ -170,11 +175,13 @@ func (self *ChainManager) Td() *big.Int {
self.mu.RLock()
defer self.mu.RUnlock()
- return self.td
+ return new(big.Int).Set(self.td)
}
func (self *ChainManager) GasLimit() *big.Int {
- // return self.currentGasLimit
+ self.mu.RLock()
+ defer self.mu.RUnlock()
+
return self.currentBlock.GasLimit()
}
@@ -196,7 +203,7 @@ func (self *ChainManager) Status() (td *big.Int, currentBlock common.Hash, genes
self.mu.RLock()
defer self.mu.RUnlock()
- return self.td, self.currentBlock.Hash(), self.genesisBlock.Hash()
+ return new(big.Int).Set(self.td), self.currentBlock.Hash(), self.genesisBlock.Hash()
}
func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
@@ -214,19 +221,6 @@ func (self *ChainManager) TransState() *state.StateDB {
return self.transState
}
-func (self *ChainManager) TxState() *state.ManagedState {
- self.tsmu.RLock()
- defer self.tsmu.RUnlock()
-
- return self.txState
-}
-
-func (self *ChainManager) setTxState(statedb *state.StateDB) {
- self.tsmu.Lock()
- defer self.tsmu.Unlock()
- self.txState = state.ManageState(statedb)
-}
-
func (self *ChainManager) setTransState(statedb *state.StateDB) {
self.transState = statedb
}
@@ -353,13 +347,24 @@ func (bc *ChainManager) ResetWithGenesisBlock(gb *types.Block) {
// Export writes the active chain to the given writer.
func (self *ChainManager) Export(w io.Writer) error {
+ if err := self.ExportN(w, uint64(0), self.currentBlock.NumberU64()); err != nil {
+ return err
+ }
+ return nil
+}
+
+// ExportN writes a subset of the active chain to the given writer.
+func (self *ChainManager) ExportN(w io.Writer, first uint64, last uint64) error {
self.mu.RLock()
defer self.mu.RUnlock()
- glog.V(logger.Info).Infof("exporting %v blocks...\n", self.currentBlock.Header().Number)
- last := self.currentBlock.NumberU64()
+ if first > last {
+ return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last)
+ }
+
+ glog.V(logger.Info).Infof("exporting %d blocks...\n", last-first+1)
- for nr := uint64(1); nr <= last; nr++ {
+ for nr := first; nr <= last; nr++ {
block := self.GetBlockByNumber(nr)
if block == nil {
return fmt.Errorf("export failed on #%d: not found", nr)
@@ -373,11 +378,13 @@ func (self *ChainManager) Export(w io.Writer) error {
return nil
}
+// insert injects a block into the current chain block chain. Note, this function
+// assumes that the `mu` mutex is held!
func (bc *ChainManager) insert(block *types.Block) {
key := append(blockNumPre, block.Number().Bytes()...)
bc.blockDb.Put(key, block.Hash().Bytes())
-
bc.blockDb.Put([]byte("LastBlock"), block.Hash().Bytes())
+
bc.currentBlock = block
bc.lastBlockHash = block.Hash()
}
@@ -481,9 +488,10 @@ func (self *ChainManager) GetAncestors(block *types.Block, length int) (blocks [
return
}
+// setTotalDifficulty updates the TD of the chain manager. Note, this function
+// assumes that the `mu` mutex is held!
func (bc *ChainManager) setTotalDifficulty(td *big.Int) {
- //bc.blockDb.Put([]byte("LTD"), td.Bytes())
- bc.td = td
+ bc.td = new(big.Int).Set(td)
}
func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
@@ -522,13 +530,14 @@ type queueEvent struct {
}
func (self *ChainManager) procFutureBlocks() {
- blocks := make([]*types.Block, len(self.futureBlocks.blocks))
+ var blocks []*types.Block
self.futureBlocks.Each(func(i int, block *types.Block) {
- blocks[i] = block
+ blocks = append(blocks, block)
})
-
- types.BlockBy(types.Number).Sort(blocks)
- self.InsertChain(blocks)
+ if len(blocks) > 0 {
+ types.BlockBy(types.Number).Sort(blocks)
+ self.InsertChain(blocks)
+ }
}
// InsertChain will attempt to insert the given chain in to the canonical chain or, otherwise, create a fork. It an error is returned
@@ -540,17 +549,35 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
self.chainmu.Lock()
defer self.chainmu.Unlock()
- // A queued approach to delivering events. This is generally faster than direct delivery and requires much less mutex acquiring.
+ // A queued approach to delivering events. This is generally
+ // faster than direct delivery and requires much less mutex
+ // acquiring.
var (
queue = make([]interface{}, len(chain))
queueEvent = queueEvent{queue: queue}
stats struct{ queued, processed, ignored int }
tstart = time.Now()
+
+ nonceDone = make(chan nonceResult, len(chain))
+ nonceQuit = make(chan struct{})
+ nonceChecked = make([]bool, len(chain))
)
+ // Start the parallel nonce verifier.
+ go verifyNonces(self.pow, chain, nonceQuit, nonceDone)
+ defer close(nonceQuit)
+
for i, block := range chain {
- if block == nil {
- continue
+ bstart := time.Now()
+ // Wait for block i's nonce to be verified before processing
+ // its state transition.
+ for !nonceChecked[i] {
+ r := <-nonceDone
+ nonceChecked[r.i] = true
+ if !r.valid {
+ block := chain[r.i]
+ return r.i, &BlockNonceErr{Hash: block.Hash(), Number: block.Number(), Nonce: block.Nonce()}
+ }
}
if BadHashes[block.Hash()] {
@@ -559,10 +586,6 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
return i, err
}
- // create a nonce channel for parallisation of the nonce check
- nonceErrCh := make(chan error)
- go verifyBlockNonce(self.pow, block, nonceErrCh)
-
// Setting block.Td regardless of error (known for example) prevents errors down the line
// in the protocol handler
block.Td = new(big.Int).Set(CalcTD(block, self.GetBlock(block.ParentHash())))
@@ -571,15 +594,19 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
// all others will fail too (unless a known block is returned).
logs, err := self.processor.Process(block)
if err != nil {
- // empty the nonce channel
- <-nonceErrCh
-
if IsKnownBlockErr(err) {
stats.ignored++
continue
}
if err == BlockFutureErr {
+ // Allow up to MaxFuture second in the future blocks. If this limit
+ // is exceeded the chain is discarded and processed at a later time
+ // if given.
+ if max := time.Now().Unix() + maxTimeFutureBlocks; block.Time() > max {
+ return i, fmt.Errorf("%v: BlockFutureErr, %v > %v", BlockFutureErr, block.Time(), max)
+ }
+
block.SetQueued(true)
self.futureBlocks.Push(block)
stats.queued++
@@ -597,16 +624,11 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
return i, err
}
- // Wait and check nonce channel and make sure it checks out fine
- // otherwise return the error
- if err := <-nonceErrCh; err != nil {
- return i, err
- }
cblock := self.currentBlock
// Compare the TD of the last known block in the canonical chain to make sure it's greater.
// At this point it's possible that a different chain (fork) becomes the new canonical chain.
- if block.Td.Cmp(self.td) > 0 {
+ if block.Td.Cmp(self.Td()) > 0 {
// chain fork
if block.ParentHash() != cblock.Hash() {
// during split we merge two different chains and create the new canonical chain
@@ -619,8 +641,10 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
queueEvent.splitCount++
}
+ self.mu.Lock()
self.setTotalDifficulty(block.Td)
self.insert(block)
+ self.mu.Unlock()
jsonlogger.LogJson(&logger.EthChainNewHead{
BlockHash: block.Hash().Hex(),
@@ -636,11 +660,11 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
queueEvent.canonicalCount++
if glog.V(logger.Debug) {
- glog.Infof("[%v] inserted block #%d (%d TXs %d UNCs) (%x...)\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4])
+ glog.Infof("[%v] inserted block #%d (%d TXs %d UNCs) (%x...). Took %v\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
}
} else {
if glog.V(logger.Detail) {
- glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...)\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4])
+ glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...). Took %v\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
}
queue[i] = ChainSideEvent{block, logs}
@@ -728,9 +752,11 @@ func (self *ChainManager) merge(oldBlock, newBlock *types.Block) error {
}
// insert blocks. Order does not matter. Last block will be written in ImportChain itself which creates the new head properly
+ self.mu.Lock()
for _, block := range newChain {
self.insert(block)
}
+ self.mu.Unlock()
return nil
}
@@ -744,7 +770,7 @@ out:
case ev := <-events.Chan():
switch ev := ev.(type) {
case queueEvent:
- for i, event := range ev.queue {
+ for _, event := range ev.queue {
switch event := event.(type) {
case ChainEvent:
// We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long
@@ -753,12 +779,6 @@ out:
self.currentGasLimit = CalcGasLimit(event.Block)
self.eventMux.Post(ChainHeadEvent{event.Block})
}
- case ChainSplitEvent:
- // On chain splits we need to reset the transaction state. We can't be sure whether the actual
- // state of the accounts are still valid.
- if i == ev.splitCount {
- self.setTxState(state.New(event.Block.Root(), self.stateDb))
- }
}
self.eventMux.Post(event)
@@ -776,66 +796,42 @@ func blockErr(block *types.Block, err error) {
h := block.Header()
glog.V(logger.Error).Infof("Bad block #%v (%x)\n", h.Number, h.Hash().Bytes())
glog.V(logger.Error).Infoln(err)
- glog.V(logger.Debug).Infoln(block)
+ glog.V(logger.Debug).Infoln(verifyNonces)
+}
+
+type nonceResult struct {
+ i int
+ valid bool
}
-// verifyNonces verifies nonces of the given blocks in parallel and returns
+// block verifies nonces of the given blocks in parallel and returns
// an error if one of the blocks nonce verifications failed.
-func verifyNonces(pow pow.PoW, blocks []*types.Block) error {
+func verifyNonces(pow pow.PoW, blocks []*types.Block, quit <-chan struct{}, done chan<- nonceResult) {
// Spawn a few workers. They listen for blocks on the in channel
// and send results on done. The workers will exit in the
// background when in is closed.
var (
- in = make(chan *types.Block)
- done = make(chan error, runtime.GOMAXPROCS(0))
+ in = make(chan int)
+ nworkers = runtime.GOMAXPROCS(0)
)
defer close(in)
- for i := 0; i < cap(done); i++ {
- go verifyNonce(pow, in, done)
+ if len(blocks) < nworkers {
+ nworkers = len(blocks)
}
- // Feed blocks to the workers, aborting at the first invalid nonce.
- var (
- running, i int
- block *types.Block
- sendin = in
- )
- for i < len(blocks) || running > 0 {
- if i == len(blocks) {
- // Disable sending to in.
- sendin = nil
- } else {
- block = blocks[i]
- i++
- }
- select {
- case sendin <- block:
- running++
- case err := <-done:
- running--
- if err != nil {
- return err
+ for i := 0; i < nworkers; i++ {
+ go func() {
+ for i := range in {
+ done <- nonceResult{i: i, valid: pow.Verify(blocks[i])}
}
- }
+ }()
}
- return nil
-}
-
-// verifyNonce is a worker for the verifyNonces method. It will run until
-// in is closed.
-func verifyNonce(pow pow.PoW, in <-chan *types.Block, done chan<- error) {
- for block := range in {
- if !pow.Verify(block) {
- done <- ValidationError("Block (#%v / %x) nonce is invalid (= %x)", block.Number(), block.Hash(), block.Nonce)
- } else {
- done <- nil
+ // Feed block indices to the workers.
+ for i := range blocks {
+ select {
+ case in <- i:
+ continue
+ case <-quit:
+ return
}
}
}
-
-func verifyBlockNonce(pow pow.PoW, block *types.Block, done chan<- error) {
- if !pow.Verify(block) {
- done <- ValidationError("Block (#%v / %x) nonce is invalid (= %x)", block.Number(), block.Hash(), block.Nonce)
- } else {
- done <- nil
- }
-}
diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go
index 7dc7358c0..c56a3b3e1 100644
--- a/core/chain_manager_test.go
+++ b/core/chain_manager_test.go
@@ -3,6 +3,7 @@ package core
import (
"fmt"
"math/big"
+ "math/rand"
"os"
"path/filepath"
"runtime"
@@ -28,6 +29,21 @@ func thePow() pow.PoW {
return pow
}
+func theChainManager(db common.Database, t *testing.T) *ChainManager {
+ var eventMux event.TypeMux
+ genesis := GenesisBlock(0, db)
+ chainMan, err := NewChainManager(genesis, db, db, thePow(), &eventMux)
+ if err != nil {
+ t.Error("failed creating chainmanager:", err)
+ t.FailNow()
+ return nil
+ }
+ blockMan := NewBlockProcessor(db, db, nil, chainMan, &eventMux)
+ chainMan.SetProcessor(blockMan)
+
+ return chainMan
+}
+
// Test fork of length N starting from block i
func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big.Int)) {
// switch databases to process the new chain
@@ -265,11 +281,7 @@ func TestChainInsertions(t *testing.T) {
t.FailNow()
}
- var eventMux event.TypeMux
- chainMan := NewChainManager(db, db, thePow(), &eventMux)
- txPool := NewTxPool(&eventMux, chainMan.State, func() *big.Int { return big.NewInt(100000000) })
- blockMan := NewBlockProcessor(db, db, nil, txPool, chainMan, &eventMux)
- chainMan.SetProcessor(blockMan)
+ chainMan := theChainManager(db, t)
const max = 2
done := make(chan bool, max)
@@ -311,11 +323,9 @@ func TestChainMultipleInsertions(t *testing.T) {
t.FailNow()
}
}
- var eventMux event.TypeMux
- chainMan := NewChainManager(db, db, thePow(), &eventMux)
- txPool := NewTxPool(&eventMux, chainMan.State, func() *big.Int { return big.NewInt(100000000) })
- blockMan := NewBlockProcessor(db, db, nil, txPool, chainMan, &eventMux)
- chainMan.SetProcessor(blockMan)
+
+ chainMan := theChainManager(db, t)
+
done := make(chan bool, max)
for i, chain := range chains {
// XXX the go routine would otherwise reference the same (chain[3]) variable and fail
@@ -340,8 +350,7 @@ func TestGetAncestors(t *testing.T) {
t.Skip() // travil fails.
db, _ := ethdb.NewMemDatabase()
- var eventMux event.TypeMux
- chainMan := NewChainManager(db, db, thePow(), &eventMux)
+ chainMan := theChainManager(db, t)
chain, err := loadChain("valid1", t)
if err != nil {
fmt.Println(err)
@@ -392,7 +401,7 @@ func chm(genesis *types.Block, db common.Database) *ChainManager {
func TestReorgLongest(t *testing.T) {
t.Skip("skipped while cache is removed")
db, _ := ethdb.NewMemDatabase()
- genesis := GenesisBlock(db)
+ genesis := GenesisBlock(0, db)
bc := chm(genesis, db)
chain1 := makeChainWithDiff(genesis, []int{1, 2, 4}, 10)
@@ -412,7 +421,7 @@ func TestReorgLongest(t *testing.T) {
func TestReorgShortest(t *testing.T) {
t.Skip("skipped while cache is removed")
db, _ := ethdb.NewMemDatabase()
- genesis := GenesisBlock(db)
+ genesis := GenesisBlock(0, db)
bc := chm(genesis, db)
chain1 := makeChainWithDiff(genesis, []int{1, 2, 3, 4}, 10)
@@ -428,3 +437,70 @@ func TestReorgShortest(t *testing.T) {
}
}
}
+
+func TestInsertNonceError(t *testing.T) {
+ for i := 1; i < 25 && !t.Failed(); i++ {
+ db, _ := ethdb.NewMemDatabase()
+ genesis := GenesisBlock(0, db)
+ bc := chm(genesis, db)
+ bc.processor = NewBlockProcessor(db, db, bc.pow, bc, bc.eventMux)
+ blocks := makeChain(bc.processor.(*BlockProcessor), bc.currentBlock, i, db, 0)
+
+ fail := rand.Int() % len(blocks)
+ failblock := blocks[fail]
+ bc.pow = failpow{failblock.NumberU64()}
+ n, err := bc.InsertChain(blocks)
+
+ // Check that the returned error indicates the nonce failure.
+ if n != fail {
+ t.Errorf("(i=%d) wrong failed block index: got %d, want %d", i, n, fail)
+ }
+ if !IsBlockNonceErr(err) {
+ t.Fatalf("(i=%d) got %q, want a nonce error", i, err)
+ }
+ nerr := err.(*BlockNonceErr)
+ if nerr.Number.Cmp(failblock.Number()) != 0 {
+ t.Errorf("(i=%d) wrong block number in error, got %v, want %v", i, nerr.Number, failblock.Number())
+ }
+ if nerr.Hash != failblock.Hash() {
+ t.Errorf("(i=%d) wrong block hash in error, got %v, want %v", i, nerr.Hash, failblock.Hash())
+ }
+
+ // Check that all no blocks after the failing block have been inserted.
+ for _, block := range blocks[fail:] {
+ if bc.HasBlock(block.Hash()) {
+ t.Errorf("(i=%d) invalid block %d present in chain", i, block.NumberU64())
+ }
+ }
+ }
+}
+
+func TestGenesisMismatch(t *testing.T) {
+ db, _ := ethdb.NewMemDatabase()
+ var mux event.TypeMux
+ genesis := GenesisBlock(0, db)
+ _, err := NewChainManager(genesis, db, db, thePow(), &mux)
+ if err != nil {
+ t.Error(err)
+ }
+ genesis = GenesisBlock(1, db)
+ _, err = NewChainManager(genesis, db, db, thePow(), &mux)
+ if err == nil {
+ t.Error("expected genesis mismatch error")
+ }
+}
+
+// failpow returns false from Verify for a certain block number.
+type failpow struct{ num uint64 }
+
+func (pow failpow) Search(pow.Block, <-chan struct{}) (nonce uint64, mixHash []byte) {
+ return 0, nil
+}
+func (pow failpow) Verify(b pow.Block) bool {
+ return b.NumberU64() != pow.num
+}
+func (pow failpow) GetHashrate() int64 {
+ return 0
+}
+func (pow failpow) Turbo(bool) {
+}
diff --git a/core/error.go b/core/error.go
index 2bdad364f..3f3c350df 100644
--- a/core/error.go
+++ b/core/error.go
@@ -90,6 +90,23 @@ func IsNonceErr(err error) bool {
return ok
}
+// BlockNonceErr indicates that a block's nonce is invalid.
+type BlockNonceErr struct {
+ Number *big.Int
+ Hash common.Hash
+ Nonce uint64
+}
+
+func (err *BlockNonceErr) Error() string {
+ return fmt.Sprintf("block %d (%v) nonce is invalid (got %d)", err.Number, err.Hash, err.Nonce)
+}
+
+// IsBlockNonceErr returns true for invalid block nonce errors.
+func IsBlockNonceErr(err error) bool {
+ _, ok := err.(*BlockNonceErr)
+ return ok
+}
+
type InvalidTxErr struct {
Message string
}
diff --git a/core/filter.go b/core/filter.go
index 2ca57da65..fcdf68dd0 100644
--- a/core/filter.go
+++ b/core/filter.go
@@ -1,6 +1,7 @@
package core
import (
+ "fmt"
"math"
"github.com/ethereum/go-ethereum/common"
@@ -75,15 +76,19 @@ func (self *Filter) Find() state.Logs {
var (
logs state.Logs
block = self.eth.ChainManager().GetBlockByNumber(latestBlockNo)
- quit bool
)
- for i := 0; !quit && block != nil; i++ {
+
+done:
+ for i := 0; block != nil; i++ {
+ fmt.Println(block.NumberU64() == 0)
// Quit on latest
switch {
- case block.NumberU64() == earliestBlockNo, block.NumberU64() == 0:
- quit = true
+ case block.NumberU64() == 0:
+ break done
+ case block.NumberU64() == earliestBlockNo:
+ break done
case self.max <= len(logs):
- break
+ break done
}
// Use bloom filtering to see if this block is interesting given the
diff --git a/core/genesis.go b/core/genesis.go
index e72834822..dd894e0b0 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -19,8 +19,8 @@ var ZeroHash256 = make([]byte, 32)
var ZeroHash160 = make([]byte, 20)
var ZeroHash512 = make([]byte, 64)
-func GenesisBlock(db common.Database) *types.Block {
- genesis := types.NewBlock(common.Hash{}, common.Address{}, common.Hash{}, params.GenesisDifficulty, 42, nil)
+func GenesisBlock(nonce uint64, db common.Database) *types.Block {
+ genesis := types.NewBlock(common.Hash{}, common.Address{}, common.Hash{}, params.GenesisDifficulty, nonce, nil)
genesis.Header().Number = common.Big0
genesis.Header().GasLimit = params.GenesisGasLimit
genesis.Header().GasUsed = common.Big0
@@ -36,7 +36,7 @@ func GenesisBlock(db common.Database) *types.Block {
Balance string
Code string
}
- err := json.Unmarshal(GenesisData, &accounts)
+ err := json.Unmarshal(GenesisAccounts, &accounts)
if err != nil {
fmt.Println("enable to decode genesis json data:", err)
os.Exit(1)
@@ -57,7 +57,7 @@ func GenesisBlock(db common.Database) *types.Block {
return genesis
}
-var GenesisData = []byte(`{
+var GenesisAccounts = []byte(`{
"0000000000000000000000000000000000000001": {"balance": "1"},
"0000000000000000000000000000000000000002": {"balance": "1"},
"0000000000000000000000000000000000000003": {"balance": "1"},
diff --git a/core/state/managed_state.go b/core/state/managed_state.go
index 5114f7a7a..aa6650d9b 100644
--- a/core/state/managed_state.go
+++ b/core/state/managed_state.go
@@ -23,7 +23,7 @@ type ManagedState struct {
// ManagedState returns a new managed state with the statedb as it's backing layer
func ManageState(statedb *StateDB) *ManagedState {
return &ManagedState{
- StateDB: statedb,
+ StateDB: statedb.Copy(),
accounts: make(map[string]*account),
}
}
diff --git a/core/state_transition.go b/core/state_transition.go
index 3d11a0464..7672fa3ff 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -62,7 +62,6 @@ type Message interface {
func AddressFromMessage(msg Message) common.Address {
from, _ := msg.From()
-
return crypto.CreateAddress(from, msg.Nonce())
}
@@ -109,9 +108,12 @@ func NewStateTransition(env vm.Environment, msg Message, coinbase *state.StateOb
func (self *StateTransition) Coinbase() *state.StateObject {
return self.state.GetOrNewStateObject(self.coinbase)
}
-func (self *StateTransition) From() *state.StateObject {
- f, _ := self.msg.From()
- return self.state.GetOrNewStateObject(f)
+func (self *StateTransition) From() (*state.StateObject, error) {
+ f, err := self.msg.From()
+ if err != nil {
+ return nil, err
+ }
+ return self.state.GetOrNewStateObject(f), nil
}
func (self *StateTransition) To() *state.StateObject {
if self.msg == nil {
@@ -140,7 +142,10 @@ func (self *StateTransition) AddGas(amount *big.Int) {
func (self *StateTransition) BuyGas() error {
var err error
- sender := self.From()
+ sender, err := self.From()
+ if err != nil {
+ return err
+ }
if sender.Balance().Cmp(MessageGasValue(self.msg)) < 0 {
return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address().Bytes()[:4], MessageGasValue(self.msg), sender.Balance())
}
@@ -159,10 +164,11 @@ func (self *StateTransition) BuyGas() error {
}
func (self *StateTransition) preCheck() (err error) {
- var (
- msg = self.msg
- sender = self.From()
- )
+ msg := self.msg
+ sender, err := self.From()
+ if err != nil {
+ return err
+ }
// Make sure this transaction's nonce is correct
if sender.Nonce() != msg.Nonce() {
@@ -185,10 +191,8 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
return
}
- var (
- msg = self.msg
- sender = self.From()
- )
+ msg := self.msg
+ sender, _ := self.From() // err checked in preCheck
// Pay intrinsic gas
if err = self.UseGas(IntrinsicGas(self.msg)); err != nil {
@@ -212,7 +216,7 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
} else {
// Increment the nonce for the next transaction
self.state.SetNonce(sender.Address(), sender.Nonce()+1)
- ret, err = vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value)
+ ret, err = vmenv.Call(sender, self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value)
}
if err != nil && IsValueTransferErr(err) {
@@ -226,7 +230,8 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
}
func (self *StateTransition) refundGas() {
- coinbase, sender := self.Coinbase(), self.From()
+ coinbase := self.Coinbase()
+ sender, _ := self.From() // err already checked
// Return remaining gas
remaining := new(big.Int).Mul(self.gas, self.msg.GasPrice())
sender.AddBalance(remaining)
diff --git a/core/transaction_pool.go b/core/transaction_pool.go
index c896488d1..a2f970195 100644
--- a/core/transaction_pool.go
+++ b/core/transaction_pool.go
@@ -6,7 +6,6 @@ import (
"math/big"
"sort"
"sync"
- "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
@@ -14,10 +13,10 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
- "gopkg.in/fatih/set.v0"
)
var (
+ // Transaction Pool Errors
ErrInvalidSender = errors.New("Invalid sender")
ErrNonce = errors.New("Nonce too low")
ErrBalance = errors.New("Insufficient balance")
@@ -28,118 +27,141 @@ var (
ErrNegativeValue = errors.New("Negative value")
)
-const txPoolQueueSize = 50
-
-type TxPoolHook chan *types.Transaction
-type TxMsg struct{ Tx *types.Transaction }
-
type stateFn func() *state.StateDB
-const (
- minGasPrice = 1000000
-)
-
-type TxProcessor interface {
- ProcessTransaction(tx *types.Transaction)
-}
-
-// The tx pool a thread safe transaction pool handler. In order to
-// guarantee a non blocking pool we use a queue channel which can be
-// independently read without needing access to the actual pool.
+// TxPool contains all currently known transactions. Transactions
+// enter the pool when they are received from the network or submitted
+// locally. They exit the pool when they are included in the blockchain.
+//
+// The pool separates processable transactions (which can be applied to the
+// current state) and future transactions. Transactions move between those
+// two states over time as they are received and processed.
type TxPool struct {
- mu sync.RWMutex
- // Queueing channel for reading and writing incoming
- // transactions to
- queueChan chan *types.Transaction
- // Quiting channel
- quit chan bool
- // The state function which will allow us to do some pre checkes
- currentState stateFn
- // The current gas limit function callback
- gasLimit func() *big.Int
- // The actual pool
- txs map[common.Hash]*types.Transaction
- invalidHashes *set.Set
-
- queue map[common.Address]types.Transactions
-
- subscribers []chan TxMsg
-
- eventMux *event.TypeMux
+ quit chan bool // Quiting channel
+ currentState stateFn // The state function which will allow us to do some pre checkes
+ pendingState *state.ManagedState
+ gasLimit func() *big.Int // The current gas limit function callback
+ eventMux *event.TypeMux
+ events event.Subscription
+
+ mu sync.RWMutex
+ pending map[common.Hash]*types.Transaction // processable transactions
+ queue map[common.Address]map[common.Hash]*types.Transaction
}
func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool {
- txPool := &TxPool{
- txs: make(map[common.Hash]*types.Transaction),
- queue: make(map[common.Address]types.Transactions),
- queueChan: make(chan *types.Transaction, txPoolQueueSize),
- quit: make(chan bool),
- eventMux: eventMux,
- invalidHashes: set.New(),
- currentState: currentStateFn,
- gasLimit: gasLimitFn,
+ return &TxPool{
+ pending: make(map[common.Hash]*types.Transaction),
+ queue: make(map[common.Address]map[common.Hash]*types.Transaction),
+ quit: make(chan bool),
+ eventMux: eventMux,
+ currentState: currentStateFn,
+ gasLimit: gasLimitFn,
+ pendingState: state.ManageState(currentStateFn()),
}
- return txPool
}
func (pool *TxPool) Start() {
- // Queue timer will tick so we can attempt to move items from the queue to the
- // main transaction pool.
- queueTimer := time.NewTicker(300 * time.Millisecond)
- // Removal timer will tick and attempt to remove bad transactions (account.nonce>tx.nonce)
- removalTimer := time.NewTicker(1 * time.Second)
-done:
- for {
- select {
- case <-queueTimer.C:
- pool.checkQueue()
- case <-removalTimer.C:
- pool.validatePool()
- case <-pool.quit:
- break done
+ // Track chain events. When a chain events occurs (new chain canon block)
+ // we need to know the new state. The new state will help us determine
+ // the nonces in the managed state
+ pool.events = pool.eventMux.Subscribe(ChainEvent{})
+ for _ = range pool.events.Chan() {
+ pool.mu.Lock()
+
+ pool.resetState()
+
+ pool.mu.Unlock()
+ }
+}
+
+func (pool *TxPool) resetState() {
+ pool.pendingState = state.ManageState(pool.currentState())
+
+ // validate the pool of pending transactions, this will remove
+ // any transactions that have been included in the block or
+ // have been invalidated because of another transaction (e.g.
+ // higher gas price)
+ pool.validatePool()
+
+ // Loop over the pending transactions and base the nonce of the new
+ // pending transaction set.
+ for _, tx := range pool.pending {
+ if addr, err := tx.From(); err == nil {
+ // Set the nonce. Transaction nonce can never be lower
+ // than the state nonce; validatePool took care of that.
+ pool.pendingState.SetNonce(addr, tx.Nonce())
}
}
+
+ // Check the queue and move transactions over to the pending if possible
+ // or remove those that have become invalid
+ pool.checkQueue()
+}
+
+func (pool *TxPool) Stop() {
+ pool.pending = make(map[common.Hash]*types.Transaction)
+ close(pool.quit)
+ pool.events.Unsubscribe()
+ glog.V(logger.Info).Infoln("TX Pool stopped")
}
-func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
+func (pool *TxPool) State() *state.ManagedState {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ return pool.pendingState
+}
+
+// validateTx checks whether a transaction is valid according
+// to the consensus rules.
+func (pool *TxPool) validateTx(tx *types.Transaction) error {
// Validate sender
var (
from common.Address
err error
)
+ // Validate the transaction sender and it's sig. Throw
+ // if the from fields is invalid.
if from, err = tx.From(); err != nil {
return ErrInvalidSender
}
- // Validate curve param
- v, _, _ := tx.Curve()
- if v > 28 || v < 27 {
- return fmt.Errorf("tx.v != (28 || 27) => %v", v)
- }
-
+ // Make sure the account exist. Non existant accounts
+ // haven't got funds and well therefor never pass.
if !pool.currentState().HasAccount(from) {
return ErrNonExistentAccount
}
+ // Check the transaction doesn't exceed the current
+ // block limit gas.
if pool.gasLimit().Cmp(tx.GasLimit) < 0 {
return ErrGasLimit
}
+ // Transactions can't be negative. This may never happen
+ // using RLP decoded transactions but may occur if you create
+ // a transaction using the RPC for example.
if tx.Amount.Cmp(common.Big0) < 0 {
return ErrNegativeValue
}
+ // Transactor should have enough funds to cover the costs
+ // cost == V + GP * GL
total := new(big.Int).Mul(tx.Price, tx.GasLimit)
total.Add(total, tx.Value())
if pool.currentState().GetBalance(from).Cmp(total) < 0 {
return ErrInsufficientFunds
}
+ // Should supply enough intrinsic gas
if tx.GasLimit.Cmp(IntrinsicGas(tx)) < 0 {
return ErrIntrinsicGas
}
+ // Last but not least check for nonce errors (intensive
+ // operation, saved for last)
if pool.currentState().GetNonce(from) > tx.Nonce() {
return ErrNonce
}
@@ -156,38 +178,36 @@ func (self *TxPool) add(tx *types.Transaction) error {
return fmt.Errorf("Invalid transaction (%x)", hash[:4])
}
*/
- if self.txs[hash] != nil {
+ if self.pending[hash] != nil {
return fmt.Errorf("Known transaction (%x)", hash[:4])
}
- err := self.ValidateTransaction(tx)
+ err := self.validateTx(tx)
if err != nil {
return err
}
-
- self.queueTx(tx)
-
- var toname string
- if to := tx.To(); to != nil {
- toname = common.Bytes2Hex(to[:4])
- } else {
- toname = "[NEW_CONTRACT]"
- }
- // we can ignore the error here because From is
- // verified in ValidateTransaction.
- f, _ := tx.From()
- from := common.Bytes2Hex(f[:4])
+ self.queueTx(hash, tx)
if glog.V(logger.Debug) {
- glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, tx.Hash())
+ var toname string
+ if to := tx.To(); to != nil {
+ toname = common.Bytes2Hex(to[:4])
+ } else {
+ toname = "[NEW_CONTRACT]"
+ }
+ // we can ignore the error here because From is
+ // verified in ValidateTransaction.
+ f, _ := tx.From()
+ from := common.Bytes2Hex(f[:4])
+ glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, hash)
}
- return nil
-}
+ // check and validate the queueue
+ self.checkQueue()
-func (self *TxPool) Size() int {
- return len(self.txs)
+ return nil
}
+// Add queues a single transaction in the pool if it is valid.
func (self *TxPool) Add(tx *types.Transaction) error {
self.mu.Lock()
defer self.mu.Unlock()
@@ -195,6 +215,7 @@ func (self *TxPool) Add(tx *types.Transaction) error {
return self.add(tx)
}
+// AddTransactions attempts to queue all valid transactions in txs.
func (self *TxPool) AddTransactions(txs []*types.Transaction) {
self.mu.Lock()
defer self.mu.Unlock()
@@ -209,81 +230,81 @@ func (self *TxPool) AddTransactions(txs []*types.Transaction) {
}
}
-// GetTransaction allows you to check the pending and queued transaction in the
-// transaction pool.
-// It has two stategies, first check the pool (map) then check the queue
+// GetTransaction returns a transaction if it is contained in the pool
+// and nil otherwise.
func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction {
// check the txs first
- if tx, ok := tp.txs[hash]; ok {
+ if tx, ok := tp.pending[hash]; ok {
return tx
}
-
// check queue
for _, txs := range tp.queue {
- for _, tx := range txs {
- if tx.Hash() == hash {
- return tx
- }
+ if tx, ok := txs[hash]; ok {
+ return tx
}
}
-
return nil
}
+// GetTransactions returns all currently processable transactions.
+// The returned slice may be modified by the caller.
func (self *TxPool) GetTransactions() (txs types.Transactions) {
- self.mu.RLock()
- defer self.mu.RUnlock()
+ self.mu.Lock()
+ defer self.mu.Unlock()
+
+ // check queue first
+ self.checkQueue()
+ // invalidate any txs
+ self.validatePool()
- txs = make(types.Transactions, self.Size())
+ txs = make(types.Transactions, len(self.pending))
i := 0
- for _, tx := range self.txs {
+ for _, tx := range self.pending {
txs[i] = tx
i++
}
-
- return
+ return txs
}
+// GetQueuedTransactions returns all non-processable transactions.
func (self *TxPool) GetQueuedTransactions() types.Transactions {
self.mu.RLock()
defer self.mu.RUnlock()
- var txs types.Transactions
- for _, ts := range self.queue {
- txs = append(txs, ts...)
+ var ret types.Transactions
+ for _, txs := range self.queue {
+ for _, tx := range txs {
+ ret = append(ret, tx)
+ }
}
-
- return txs
+ sort.Sort(types.TxByNonce{ret})
+ return ret
}
+// RemoveTransactions removes all given transactions from the pool.
func (self *TxPool) RemoveTransactions(txs types.Transactions) {
self.mu.Lock()
defer self.mu.Unlock()
-
for _, tx := range txs {
self.removeTx(tx.Hash())
}
}
-func (pool *TxPool) Flush() {
- pool.txs = make(map[common.Hash]*types.Transaction)
-}
-
-func (pool *TxPool) Stop() {
- pool.Flush()
- close(pool.quit)
-
- glog.V(logger.Info).Infoln("TX Pool stopped")
+func (self *TxPool) queueTx(hash common.Hash, tx *types.Transaction) {
+ from, _ := tx.From() // already validated
+ if self.queue[from] == nil {
+ self.queue[from] = make(map[common.Hash]*types.Transaction)
+ }
+ self.queue[from][hash] = tx
}
-func (self *TxPool) queueTx(tx *types.Transaction) {
- from, _ := tx.From()
- self.queue[from] = append(self.queue[from], tx)
-}
+func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Transaction) {
+ if _, ok := pool.pending[hash]; !ok {
+ pool.pending[hash] = tx
-func (pool *TxPool) addTx(tx *types.Transaction) {
- if _, ok := pool.txs[tx.Hash()]; !ok {
- pool.txs[tx.Hash()] = tx
+ // Increment the nonce on the pending state. This can only happen if
+ // the nonce is +1 to the previous one.
+ pool.pendingState.SetNonce(addr, tx.AccountNonce+1)
// Notify the subscribers. This event is posted in a goroutine
// because it's possible that somewhere during the post "Remove transaction"
// gets called which will then wait for the global tx pool lock and deadlock.
@@ -291,42 +312,39 @@ func (pool *TxPool) addTx(tx *types.Transaction) {
}
}
-// check queue will attempt to insert
+// checkQueue moves transactions that have become processable to main pool.
func (pool *TxPool) checkQueue() {
- pool.mu.Lock()
- defer pool.mu.Unlock()
+ state := pool.pendingState
- statedb := pool.currentState()
+ var addq txQueue
for address, txs := range pool.queue {
- sort.Sort(types.TxByNonce{txs})
-
- var (
- nonce = statedb.GetNonce(address)
- start int
- )
- // Clean up the transactions first and determine the start of the nonces
- for _, tx := range txs {
- if tx.Nonce() >= nonce {
- break
+ // guessed nonce is the nonce currently kept by the tx pool (pending state)
+ guessedNonce := state.GetNonce(address)
+ // true nonce is the nonce known by the last state
+ trueNonce := pool.currentState().GetNonce(address)
+ addq := addq[:0]
+ for hash, tx := range txs {
+ if tx.AccountNonce < trueNonce {
+ // Drop queued transactions whose nonce is lower than
+ // the account nonce because they have been processed.
+ delete(txs, hash)
+ } else {
+ // Collect the remaining transactions for the next pass.
+ addq = append(addq, txQueueEntry{hash, address, tx})
}
- start++
}
- pool.queue[address] = txs[start:]
-
- // expected nonce
- enonce := nonce
- for _, tx := range pool.queue[address] {
- // If the expected nonce does not match up with the next one
- // (i.e. a nonce gap), we stop the loop
- if enonce != tx.Nonce() {
+ // Find the next consecutive nonce range starting at the
+ // current account nonce.
+ sort.Sort(addq)
+ for _, e := range addq {
+ if e.AccountNonce > guessedNonce {
break
}
- enonce++
-
- pool.addTx(tx)
+ delete(txs, e.hash)
+ pool.addTx(e.hash, address, e.Transaction)
}
- // delete the entire queue entry if it's empty. There's no need to keep it
- if len(pool.queue[address]) == 0 {
+ // Delete the entire queue entry if it became empty.
+ if len(txs) == 0 {
delete(pool.queue, address)
}
}
@@ -334,36 +352,41 @@ func (pool *TxPool) checkQueue() {
func (pool *TxPool) removeTx(hash common.Hash) {
// delete from pending pool
- delete(pool.txs, hash)
-
+ delete(pool.pending, hash)
// delete from queue
-out:
for address, txs := range pool.queue {
- for i, tx := range txs {
- if tx.Hash() == hash {
- if len(txs) == 1 {
- // if only one tx, remove entire address entry
- delete(pool.queue, address)
- } else {
- pool.queue[address][len(txs)-1], pool.queue[address] = nil, append(txs[:i], txs[i+1:]...)
- }
- break out
+ if _, ok := txs[hash]; ok {
+ if len(txs) == 1 {
+ // if only one tx, remove entire address entry.
+ delete(pool.queue, address)
+ } else {
+ delete(txs, hash)
}
+ break
}
}
}
+// validatePool removes invalid and processed transactions from the main pool.
func (pool *TxPool) validatePool() {
- pool.mu.Lock()
- defer pool.mu.Unlock()
-
- for hash, tx := range pool.txs {
- if err := pool.ValidateTransaction(tx); err != nil {
- if glog.V(logger.Info) {
+ for hash, tx := range pool.pending {
+ if err := pool.validateTx(tx); err != nil {
+ if glog.V(logger.Core) {
glog.Infof("removed tx (%x) from pool: %v\n", hash[:4], err)
}
-
- pool.removeTx(hash)
+ delete(pool.pending, hash)
}
}
}
+
+type txQueue []txQueueEntry
+
+type txQueueEntry struct {
+ hash common.Hash
+ addr common.Address
+ *types.Transaction
+}
+
+func (q txQueue) Len() int { return len(q) }
+func (q txQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] }
+func (q txQueue) Less(i, j int) bool { return q[i].AccountNonce < q[j].AccountNonce }
diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go
index d6ea4a2a9..b763c196d 100644
--- a/core/transaction_pool_test.go
+++ b/core/transaction_pool_test.go
@@ -68,25 +68,25 @@ func TestTransactionQueue(t *testing.T) {
tx.SignECDSA(key)
from, _ := tx.From()
pool.currentState().AddBalance(from, big.NewInt(1))
- pool.queueTx(tx)
+ pool.queueTx(tx.Hash(), tx)
pool.checkQueue()
- if len(pool.txs) != 1 {
- t.Error("expected valid txs to be 1 is", len(pool.txs))
+ if len(pool.pending) != 1 {
+ t.Error("expected valid txs to be 1 is", len(pool.pending))
}
tx = transaction()
+ tx.SetNonce(1)
tx.SignECDSA(key)
from, _ = tx.From()
- pool.currentState().SetNonce(from, 10)
- tx.SetNonce(1)
- pool.queueTx(tx)
+ pool.currentState().SetNonce(from, 2)
+ pool.queueTx(tx.Hash(), tx)
pool.checkQueue()
- if _, ok := pool.txs[tx.Hash()]; ok {
+ if _, ok := pool.pending[tx.Hash()]; ok {
t.Error("expected transaction to be in tx pool")
}
- if len(pool.queue[from]) != 0 {
+ if len(pool.queue[from]) > 0 {
t.Error("expected transaction queue to be empty. is", len(pool.queue[from]))
}
@@ -97,18 +97,18 @@ func TestTransactionQueue(t *testing.T) {
tx1.SignECDSA(key)
tx2.SignECDSA(key)
tx3.SignECDSA(key)
- pool.queueTx(tx1)
- pool.queueTx(tx2)
- pool.queueTx(tx3)
+ pool.queueTx(tx1.Hash(), tx1)
+ pool.queueTx(tx2.Hash(), tx2)
+ pool.queueTx(tx3.Hash(), tx3)
from, _ = tx1.From()
+
pool.checkQueue()
- if len(pool.txs) != 1 {
+ if len(pool.pending) != 1 {
t.Error("expected tx pool to be 1 =")
}
-
- if len(pool.queue[from]) != 3 {
- t.Error("expected transaction queue to be empty. is", len(pool.queue[from]))
+ if len(pool.queue[from]) != 2 {
+ t.Error("expected len(queue) == 2, got", len(pool.queue[from]))
}
}
@@ -118,14 +118,14 @@ func TestRemoveTx(t *testing.T) {
tx.SignECDSA(key)
from, _ := tx.From()
pool.currentState().AddBalance(from, big.NewInt(1))
- pool.queueTx(tx)
- pool.addTx(tx)
+ pool.queueTx(tx.Hash(), tx)
+ pool.addTx(tx.Hash(), from, tx)
if len(pool.queue) != 1 {
t.Error("expected queue to be 1, got", len(pool.queue))
}
- if len(pool.txs) != 1 {
- t.Error("expected txs to be 1, got", len(pool.txs))
+ if len(pool.pending) != 1 {
+ t.Error("expected txs to be 1, got", len(pool.pending))
}
pool.removeTx(tx.Hash())
@@ -134,8 +134,8 @@ func TestRemoveTx(t *testing.T) {
t.Error("expected queue to be 0, got", len(pool.queue))
}
- if len(pool.txs) > 0 {
- t.Error("expected txs to be 0, got", len(pool.txs))
+ if len(pool.pending) > 0 {
+ t.Error("expected txs to be 0, got", len(pool.pending))
}
}
@@ -152,3 +152,91 @@ func TestNegativeValue(t *testing.T) {
t.Error("expected", ErrNegativeValue, "got", err)
}
}
+
+func TestTransactionChainFork(t *testing.T) {
+ pool, key := setupTxPool()
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ resetState := func() {
+ db, _ := ethdb.NewMemDatabase()
+ statedb := state.New(common.Hash{}, db)
+ pool.currentState = func() *state.StateDB { return statedb }
+ pool.currentState().AddBalance(addr, big.NewInt(100000000000000))
+ pool.resetState()
+ }
+ resetState()
+
+ tx := transaction()
+ tx.GasLimit = big.NewInt(100000)
+ tx.SignECDSA(key)
+
+ err := pool.add(tx)
+ if err != nil {
+ t.Error("didn't expect error", err)
+ }
+ pool.RemoveTransactions([]*types.Transaction{tx})
+
+ // reset the pool's internal state
+ resetState()
+ err = pool.add(tx)
+ if err != nil {
+ t.Error("didn't expect error", err)
+ }
+}
+
+func TestTransactionDoubleNonce(t *testing.T) {
+ pool, key := setupTxPool()
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ resetState := func() {
+ db, _ := ethdb.NewMemDatabase()
+ statedb := state.New(common.Hash{}, db)
+ pool.currentState = func() *state.StateDB { return statedb }
+ pool.currentState().AddBalance(addr, big.NewInt(100000000000000))
+ pool.resetState()
+ }
+ resetState()
+
+ tx := transaction()
+ tx.GasLimit = big.NewInt(100000)
+ tx.SignECDSA(key)
+
+ err := pool.add(tx)
+ if err != nil {
+ t.Error("didn't expect error", err)
+ }
+
+ tx2 := transaction()
+ tx2.GasLimit = big.NewInt(1000000)
+ tx2.SignECDSA(key)
+
+ err = pool.add(tx2)
+ if err != nil {
+ t.Error("didn't expect error", err)
+ }
+
+ if len(pool.pending) != 2 {
+ t.Error("expected 2 pending txs. Got", len(pool.pending))
+ }
+}
+
+func TestMissingNonce(t *testing.T) {
+ pool, key := setupTxPool()
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ pool.currentState().AddBalance(addr, big.NewInt(100000000000000))
+ tx := transaction()
+ tx.AccountNonce = 1
+ tx.GasLimit = big.NewInt(100000)
+ tx.SignECDSA(key)
+
+ err := pool.add(tx)
+ if err != nil {
+ t.Error("didn't expect error", err)
+ }
+
+ if len(pool.pending) != 0 {
+ t.Error("expected 0 pending transactions, got", len(pool.pending))
+ }
+
+ if len(pool.queue[addr]) != 1 {
+ t.Error("expected 1 queued transaction, got", len(pool.queue[addr]))
+ }
+}
diff --git a/core/types/block.go b/core/types/block.go
index c93452fa7..d7963981e 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -1,7 +1,9 @@
package types
import (
+ "bytes"
"encoding/binary"
+ "encoding/json"
"fmt"
"io"
"math/big"
@@ -80,6 +82,28 @@ func (self *Header) RlpData() interface{} {
return self.rlpData(true)
}
+func (h *Header) UnmarshalJSON(data []byte) error {
+ var ext struct {
+ ParentHash string
+ Coinbase string
+ Difficulty string
+ GasLimit string
+ Time uint64
+ Extra string
+ }
+ dec := json.NewDecoder(bytes.NewReader(data))
+ if err := dec.Decode(&ext); err != nil {
+ return err
+ }
+
+ h.ParentHash = common.HexToHash(ext.ParentHash)
+ h.Coinbase = common.HexToAddress(ext.Coinbase)
+ h.Difficulty = common.String2Big(ext.Difficulty)
+ h.Time = ext.Time
+ h.Extra = []byte(ext.Extra)
+ return nil
+}
+
func rlpHash(x interface{}) (h common.Hash) {
hw := sha3.NewKeccak256()
rlp.Encode(hw, x)
diff --git a/core/types/transaction.go b/core/types/transaction.go
index d8dcd7424..a03a6b847 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -8,7 +8,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
@@ -68,6 +67,13 @@ func (tx *Transaction) Hash() common.Hash {
})
}
+// Size returns the encoded RLP size of tx.
+func (self *Transaction) Size() common.StorageSize {
+ c := writeCounter(0)
+ rlp.Encode(&c, self)
+ return common.StorageSize(c)
+}
+
func (self *Transaction) Data() []byte {
return self.Payload
}
@@ -93,9 +99,9 @@ func (self *Transaction) SetNonce(AccountNonce uint64) {
}
func (self *Transaction) From() (common.Address, error) {
- pubkey := self.PublicKey()
- if len(pubkey) == 0 || pubkey[0] != 4 {
- return common.Address{}, errors.New("invalid public key")
+ pubkey, err := self.PublicKey()
+ if err != nil {
+ return common.Address{}, err
}
var addr common.Address
@@ -110,34 +116,34 @@ func (tx *Transaction) To() *common.Address {
return tx.Recipient
}
-func (tx *Transaction) Curve() (v byte, r []byte, s []byte) {
+func (tx *Transaction) GetSignatureValues() (v byte, r []byte, s []byte) {
v = byte(tx.V)
r = common.LeftPadBytes(tx.R.Bytes(), 32)
s = common.LeftPadBytes(tx.S.Bytes(), 32)
return
}
-func (tx *Transaction) Signature(key []byte) []byte {
- hash := tx.Hash()
- sig, _ := secp256k1.Sign(hash[:], key)
- return sig
-}
+func (tx *Transaction) PublicKey() ([]byte, error) {
+ if !crypto.ValidateSignatureValues(tx.V, tx.R, tx.S) {
+ return nil, errors.New("invalid v, r, s values")
+ }
-func (tx *Transaction) PublicKey() []byte {
hash := tx.Hash()
- v, r, s := tx.Curve()
+ v, r, s := tx.GetSignatureValues()
sig := append(r, s...)
sig = append(sig, v-27)
- //pubkey := crypto.Ecrecover(append(hash[:], sig...))
- //pubkey, _ := secp256k1.RecoverPubkey(hash[:], sig)
p, err := crypto.SigToPub(hash[:], sig)
if err != nil {
glog.V(logger.Error).Infof("Could not get pubkey from signature: ", err)
- return nil
+ return nil, err
}
+
pubkey := crypto.FromECDSAPub(p)
- return pubkey
+ if len(pubkey) == 0 || pubkey[0] != 4 {
+ return nil, errors.New("invalid public key")
+ }
+ return pubkey, nil
}
func (tx *Transaction) SetSignatureValues(sig []byte) error {
diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go
index dada424e9..492059c28 100644
--- a/core/types/transaction_test.go
+++ b/core/types/transaction_test.go
@@ -64,7 +64,7 @@ func decodeTx(data []byte) (*Transaction, error) {
return &tx, rlp.Decode(bytes.NewReader(data), &tx)
}
-func defaultTestKey() (*ecdsa.PrivateKey, []byte) {
+func defaultTestKey() (*ecdsa.PrivateKey, common.Address) {
key := crypto.ToECDSA(common.Hex2Bytes("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"))
addr := crypto.PubkeyToAddress(key.PublicKey)
return key, addr
@@ -85,7 +85,7 @@ func TestRecipientEmpty(t *testing.T) {
t.FailNow()
}
- if !bytes.Equal(addr, from.Bytes()) {
+ if addr != from {
t.Error("derived address doesn't match")
}
}
@@ -105,7 +105,7 @@ func TestRecipientNormal(t *testing.T) {
t.FailNow()
}
- if !bytes.Equal(addr, from.Bytes()) {
+ if addr != from {
t.Error("derived address doesn't match")
}
}
diff --git a/core/vm/analysis.go b/core/vm/analysis.go
index 264d55cb9..a7aa8da39 100644
--- a/core/vm/analysis.go
+++ b/core/vm/analysis.go
@@ -3,34 +3,45 @@ package vm
import (
"math/big"
- "gopkg.in/fatih/set.v0"
+ "github.com/ethereum/go-ethereum/common"
)
-type destinations struct {
- set *set.Set
-}
+var bigMaxUint64 = new(big.Int).SetUint64(^uint64(0))
-func (d *destinations) Has(dest *big.Int) bool {
- return d.set.Has(string(dest.Bytes()))
-}
+// destinations stores one map per contract (keyed by hash of code).
+// The maps contain an entry for each location of a JUMPDEST
+// instruction.
+type destinations map[common.Hash]map[uint64]struct{}
-func (d *destinations) Add(dest *big.Int) {
- d.set.Add(string(dest.Bytes()))
+// has checks whether code has a JUMPDEST at dest.
+func (d destinations) has(codehash common.Hash, code []byte, dest *big.Int) bool {
+ // PC cannot go beyond len(code) and certainly can't be bigger than 64bits.
+ // Don't bother checking for JUMPDEST in that case.
+ if dest.Cmp(bigMaxUint64) > 0 {
+ return false
+ }
+ m, analysed := d[codehash]
+ if !analysed {
+ m = jumpdests(code)
+ d[codehash] = m
+ }
+ _, ok := m[dest.Uint64()]
+ return ok
}
-func analyseJumpDests(code []byte) (dests *destinations) {
- dests = &destinations{set.New()}
-
+// jumpdests creates a map that contains an entry for each
+// PC location that is a JUMPDEST instruction.
+func jumpdests(code []byte) map[uint64]struct{} {
+ m := make(map[uint64]struct{})
for pc := uint64(0); pc < uint64(len(code)); pc++ {
var op OpCode = OpCode(code[pc])
switch op {
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
a := uint64(op) - uint64(PUSH1) + 1
-
pc += a
case JUMPDEST:
- dests.Add(big.NewInt(int64(pc)))
+ m[pc] = struct{}{}
}
}
- return
+ return m
}
diff --git a/core/vm/context.go b/core/vm/context.go
index 29bb9f74e..de03f84f0 100644
--- a/core/vm/context.go
+++ b/core/vm/context.go
@@ -16,6 +16,8 @@ type Context struct {
caller ContextRef
self ContextRef
+ jumpdests destinations // result of JUMPDEST analysis.
+
Code []byte
CodeAddr *common.Address
@@ -24,10 +26,17 @@ type Context struct {
Args []byte
}
-// Create a new context for the given data items
+// Create a new context for the given data items.
func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int) *Context {
c := &Context{caller: caller, self: object, Args: nil}
+ if parent, ok := caller.(*Context); ok {
+ // Reuse JUMPDEST analysis from parent context if available.
+ c.jumpdests = parent.jumpdests
+ } else {
+ c.jumpdests = make(destinations)
+ }
+
// Gas should be a pointer so it can safely be reduced through the run
// This pointer will be off the state transition
c.Gas = gas //new(big.Int).Set(gas)
diff --git a/core/vm/address.go b/core/vm/contracts.go
index 742017dd2..90e356b1d 100644
--- a/core/vm/address.go
+++ b/core/vm/contracts.go
@@ -67,21 +67,25 @@ func ripemd160Func(in []byte) []byte {
const ecRecoverInputLength = 128
func ecrecoverFunc(in []byte) []byte {
+ in = common.RightPadBytes(in, 128)
// "in" is (hash, v, r, s), each 32 bytes
// but for ecrecover we want (r, s, v)
- if len(in) < ecRecoverInputLength {
- return nil
- }
+ r := common.BytesToBig(in[64:96])
+ s := common.BytesToBig(in[96:128])
// Treat V as a 256bit integer
- v := new(big.Int).Sub(common.Bytes2Big(in[32:64]), big.NewInt(27))
- // Ethereum requires V to be either 0 or 1 => (27 || 28)
- if !(v.Cmp(Zero) == 0 || v.Cmp(One) == 0) {
+ vbig := common.Bytes2Big(in[32:64])
+ v := byte(vbig.Uint64())
+
+ if !crypto.ValidateSignatureValues(v, r, s) {
+ glog.V(logger.Error).Infof("EC RECOVER FAIL: v, r or s value invalid")
return nil
}
- // v needs to be moved to the end
- rsv := append(in[64:128], byte(v.Uint64()))
+ // v needs to be at the end and normalized for libsecp256k1
+ vbignormal := new(big.Int).Sub(vbig, big.NewInt(27))
+ vnormal := byte(vbignormal.Uint64())
+ rsv := append(in[64:128], vnormal)
pubKey, err := crypto.Ecrecover(in[:32], rsv)
// make sure the public key is a valid one
if err != nil {
diff --git a/core/vm/environment.go b/core/vm/environment.go
index cc9570fc8..282d19578 100644
--- a/core/vm/environment.go
+++ b/core/vm/environment.go
@@ -2,13 +2,10 @@ package vm
import (
"errors"
- "fmt"
- "io"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/rlp"
)
type Environment interface {
@@ -52,40 +49,3 @@ func Transfer(from, to Account, amount *big.Int) error {
return nil
}
-
-type Log struct {
- address common.Address
- topics []common.Hash
- data []byte
- log uint64
-}
-
-func (self *Log) Address() common.Address {
- return self.address
-}
-
-func (self *Log) Topics() []common.Hash {
- return self.topics
-}
-
-func (self *Log) Data() []byte {
- return self.data
-}
-
-func (self *Log) Number() uint64 {
- return self.log
-}
-
-func (self *Log) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, []interface{}{self.address, self.topics, self.data})
-}
-
-/*
-func (self *Log) RlpData() interface{} {
- return []interface{}{self.address, common.ByteSliceToInterface(self.topics), self.data}
-}
-*/
-
-func (self *Log) String() string {
- return fmt.Sprintf("{%x %x %x}", self.address, self.data, self.topics)
-}
diff --git a/core/vm/main_test.go b/core/vm/main_test.go
deleted file mode 100644
index 0ae03bf6a..000000000
--- a/core/vm/main_test.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package vm
-
-import (
- "testing"
-
- checker "gopkg.in/check.v1"
-)
-
-func Test(t *testing.T) { checker.TestingT(t) }
diff --git a/core/vm/types.go b/core/vm/opcodes.go
index 1ea80a212..1ea80a212 100644
--- a/core/vm/types.go
+++ b/core/vm/opcodes.go
diff --git a/core/vm/vm.go b/core/vm/vm.go
index 6db99bdcc..2bd950385 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -71,18 +71,22 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
}
}
- var (
- op OpCode
+ // Don't bother with the execution if there's no code.
+ if len(code) == 0 {
+ return context.Return(nil), nil
+ }
- destinations = analyseJumpDests(context.Code)
- mem = NewMemory()
- stack = newStack()
- pc = new(big.Int)
- statedb = self.env.State()
+ var (
+ op OpCode
+ codehash = crypto.Sha3Hash(code)
+ mem = NewMemory()
+ stack = newStack()
+ pc = new(big.Int)
+ statedb = self.env.State()
jump = func(from *big.Int, to *big.Int) error {
- nop := context.GetOp(to)
- if !destinations.Has(to) {
+ if !context.jumpdests.has(codehash, code, to) {
+ nop := context.GetOp(to)
return fmt.Errorf("invalid jump destination (%v) %v", nop, to)
}
@@ -95,11 +99,6 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
}
)
- // Don't bother with the execution if there's no code.
- if len(code) == 0 {
- return context.Return(nil), nil
- }
-
for {
// The base for all big integer arithmetic
base := new(big.Int)
diff --git a/core/vm/vm_test.go b/core/vm/vm_test.go
deleted file mode 100644
index 9bd147a72..000000000
--- a/core/vm/vm_test.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package vm
-
-// Tests have been removed in favour of general tests. If anything implementation specific needs testing, put it here