From b7fc85d68ea22a99222ba8535be8ff6410826644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Fri, 29 May 2015 23:46:10 +0300 Subject: core: fix #1154, sort out data race accessing the future blocks --- core/chain_manager.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index edf8825f3..3408b5030 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -522,9 +522,9 @@ type queueEvent struct { } func (self *ChainManager) procFutureBlocks() { - blocks := make([]*types.Block, len(self.futureBlocks.blocks)) + 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) -- cgit v1.2.3 From e7e2cbfc018f5e0353d26b94ff785a206c9a16c4 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 29 May 2015 18:07:23 +0200 Subject: core: re-add parallel nonce checks In this incancation, the processor waits until the nonce has been verified before handling the block. --- core/chain_manager.go | 107 +++++++++++++++++++++----------------------------- 1 file changed, 44 insertions(+), 63 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index 088ca8d5b..a785e1854 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -540,17 +540,34 @@ 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 + // 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[i] + return i, ValidationError("Block (#%v / %x) nonce is invalid (= %x)", block.Number(), block.Hash(), block.Nonce) + } } if BadHashes[block.Hash()] { @@ -559,10 +576,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,9 +584,6 @@ 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 @@ -597,11 +607,6 @@ 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. @@ -776,66 +781,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 - } -} -- cgit v1.2.3 From 55b60e699b3ab381b4576d1a02337704b0f74c74 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 29 May 2015 18:55:42 +0200 Subject: core: insert less length zero chains This reduces the amount of queueEvents that are sent internally. --- core/chain_manager.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index a785e1854..927055103 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -522,13 +522,14 @@ type queueEvent struct { } func (self *ChainManager) procFutureBlocks() { - blocks := []*types.Block{} + var blocks []*types.Block self.futureBlocks.Each(func(i int, block *types.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 -- cgit v1.2.3 From 32e1b104f8fbc0f80bf2b6a93492aa01fa323e35 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Mon, 1 Jun 2015 20:27:20 +0200 Subject: Add EC signature validations before call to libsecp256k1 --- core/transaction_pool.go | 6 ------ core/types/transaction.go | 31 +++++++++++++++---------------- 2 files changed, 15 insertions(+), 22 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index c896488d1..ee6360614 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -112,12 +112,6 @@ func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error { return ErrInvalidSender } - // Validate curve param - v, _, _ := tx.Curve() - if v > 28 || v < 27 { - return fmt.Errorf("tx.v != (28 || 27) => %v", v) - } - if !pool.currentState().HasAccount(from) { return ErrNonExistentAccount } diff --git a/core/types/transaction.go b/core/types/transaction.go index d8dcd7424..3d6d31ae7 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" @@ -93,9 +92,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 +109,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 { -- cgit v1.2.3 From 147a699c6543b1e4ec8c933f8aaff4e0639897b6 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Mon, 1 Jun 2015 22:00:48 +0200 Subject: Add missing err checks on From() (skip RPC for now) --- core/state_transition.go | 35 ++++++++++++++++++++--------------- core/transaction_pool.go | 2 +- 2 files changed, 21 insertions(+), 16 deletions(-) (limited to 'core') 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..83ea82ca7 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -277,7 +277,7 @@ func (pool *TxPool) Stop() { } func (self *TxPool) queueTx(tx *types.Transaction) { - from, _ := tx.From() + from, _ := tx.From() // already validated self.queue[from] = append(self.queue[from], tx) } -- cgit v1.2.3 From 55bf5051adea17b5981d4f567d9e9dc6865f7af7 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Mon, 1 Jun 2015 22:43:05 +0200 Subject: Unsupport bruncles --- core/block_processor.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/block_processor.go b/core/block_processor.go index ca205ee86..a3ad383d0 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -364,8 +364,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 { -- cgit v1.2.3 From ea2718c9462ae351baab5eaa05a7e1ef9dc916fa Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 29 May 2015 14:40:45 +0200 Subject: core/vm: improve JUMPDEST analysis * JUMPDEST analysis is faster because less type conversions are performed. * The map of JUMPDEST locations is now created lazily at the first JUMP. * The result of the analysis is kept around for recursive invocations through CALL/CALLCODE. Fixes #1147 --- core/vm/analysis.go | 41 ++++++++++++++++++++++++++--------------- core/vm/context.go | 11 ++++++++++- core/vm/vm.go | 17 ++++++++--------- 3 files changed, 44 insertions(+), 25 deletions(-) (limited to 'core') 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/vm.go b/core/vm/vm.go index 6db99bdcc..0d8facbb6 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -72,17 +72,16 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { } var ( - op OpCode - - destinations = analyseJumpDests(context.Code) - mem = NewMemory() - stack = newStack() - pc = new(big.Int) - statedb = self.env.State() + 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) } -- cgit v1.2.3 From 48fb0c3213a1634a266dd661d30c9ecd3c352f49 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 29 May 2015 14:58:57 +0200 Subject: core/vm: check for 'no code' before doing any work --- core/vm/vm.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'core') diff --git a/core/vm/vm.go b/core/vm/vm.go index 0d8facbb6..2bd950385 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -71,6 +71,11 @@ 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 + } + var ( op OpCode codehash = crypto.Sha3Hash(code) @@ -94,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) -- cgit v1.2.3 From fa4aefee44c5dd32fb7e0d02960c1550e9c8a330 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 31 May 2015 15:05:00 +0200 Subject: core/vm: cleanup and renames --- core/vm/address.go | 98 --------------- core/vm/contracts.go | 98 +++++++++++++++ core/vm/environment.go | 40 ------ core/vm/main_test.go | 9 -- core/vm/opcodes.go | 334 +++++++++++++++++++++++++++++++++++++++++++++++++ core/vm/types.go | 334 ------------------------------------------------- core/vm/vm_test.go | 3 - 7 files changed, 432 insertions(+), 484 deletions(-) delete mode 100644 core/vm/address.go create mode 100644 core/vm/contracts.go delete mode 100644 core/vm/main_test.go create mode 100644 core/vm/opcodes.go delete mode 100644 core/vm/types.go delete mode 100644 core/vm/vm_test.go (limited to 'core') diff --git a/core/vm/address.go b/core/vm/address.go deleted file mode 100644 index 742017dd2..000000000 --- a/core/vm/address.go +++ /dev/null @@ -1,98 +0,0 @@ -package vm - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/params" -) - -type Address interface { - Call(in []byte) []byte -} - -type PrecompiledAccount struct { - Gas func(l int) *big.Int - fn func(in []byte) []byte -} - -func (self PrecompiledAccount) Call(in []byte) []byte { - return self.fn(in) -} - -var Precompiled = PrecompiledContracts() - -// XXX Could set directly. Testing requires resetting and setting of pre compiled contracts. -func PrecompiledContracts() map[string]*PrecompiledAccount { - return map[string]*PrecompiledAccount{ - // ECRECOVER - string(common.LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int { - return params.EcrecoverGas - }, ecrecoverFunc}, - - // SHA256 - string(common.LeftPadBytes([]byte{2}, 20)): &PrecompiledAccount{func(l int) *big.Int { - n := big.NewInt(int64(l+31) / 32) - n.Mul(n, params.Sha256WordGas) - return n.Add(n, params.Sha256Gas) - }, sha256Func}, - - // RIPEMD160 - string(common.LeftPadBytes([]byte{3}, 20)): &PrecompiledAccount{func(l int) *big.Int { - n := big.NewInt(int64(l+31) / 32) - n.Mul(n, params.Ripemd160WordGas) - return n.Add(n, params.Ripemd160Gas) - }, ripemd160Func}, - - string(common.LeftPadBytes([]byte{4}, 20)): &PrecompiledAccount{func(l int) *big.Int { - n := big.NewInt(int64(l+31) / 32) - n.Mul(n, params.IdentityWordGas) - - return n.Add(n, params.IdentityGas) - }, memCpy}, - } -} - -func sha256Func(in []byte) []byte { - return crypto.Sha256(in) -} - -func ripemd160Func(in []byte) []byte { - return common.LeftPadBytes(crypto.Ripemd160(in), 32) -} - -const ecRecoverInputLength = 128 - -func ecrecoverFunc(in []byte) []byte { - // "in" is (hash, v, r, s), each 32 bytes - // but for ecrecover we want (r, s, v) - if len(in) < ecRecoverInputLength { - return nil - } - - // 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) { - return nil - } - - // v needs to be moved to the end - rsv := append(in[64:128], byte(v.Uint64())) - pubKey, err := crypto.Ecrecover(in[:32], rsv) - // make sure the public key is a valid one - if err != nil { - glog.V(logger.Error).Infof("EC RECOVER FAIL: ", err) - return nil - } - - // the first byte of pubkey is bitcoin heritage - return common.LeftPadBytes(crypto.Sha3(pubKey[1:])[12:], 32) -} - -func memCpy(in []byte) []byte { - return in -} diff --git a/core/vm/contracts.go b/core/vm/contracts.go new file mode 100644 index 000000000..742017dd2 --- /dev/null +++ b/core/vm/contracts.go @@ -0,0 +1,98 @@ +package vm + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" +) + +type Address interface { + Call(in []byte) []byte +} + +type PrecompiledAccount struct { + Gas func(l int) *big.Int + fn func(in []byte) []byte +} + +func (self PrecompiledAccount) Call(in []byte) []byte { + return self.fn(in) +} + +var Precompiled = PrecompiledContracts() + +// XXX Could set directly. Testing requires resetting and setting of pre compiled contracts. +func PrecompiledContracts() map[string]*PrecompiledAccount { + return map[string]*PrecompiledAccount{ + // ECRECOVER + string(common.LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int { + return params.EcrecoverGas + }, ecrecoverFunc}, + + // SHA256 + string(common.LeftPadBytes([]byte{2}, 20)): &PrecompiledAccount{func(l int) *big.Int { + n := big.NewInt(int64(l+31) / 32) + n.Mul(n, params.Sha256WordGas) + return n.Add(n, params.Sha256Gas) + }, sha256Func}, + + // RIPEMD160 + string(common.LeftPadBytes([]byte{3}, 20)): &PrecompiledAccount{func(l int) *big.Int { + n := big.NewInt(int64(l+31) / 32) + n.Mul(n, params.Ripemd160WordGas) + return n.Add(n, params.Ripemd160Gas) + }, ripemd160Func}, + + string(common.LeftPadBytes([]byte{4}, 20)): &PrecompiledAccount{func(l int) *big.Int { + n := big.NewInt(int64(l+31) / 32) + n.Mul(n, params.IdentityWordGas) + + return n.Add(n, params.IdentityGas) + }, memCpy}, + } +} + +func sha256Func(in []byte) []byte { + return crypto.Sha256(in) +} + +func ripemd160Func(in []byte) []byte { + return common.LeftPadBytes(crypto.Ripemd160(in), 32) +} + +const ecRecoverInputLength = 128 + +func ecrecoverFunc(in []byte) []byte { + // "in" is (hash, v, r, s), each 32 bytes + // but for ecrecover we want (r, s, v) + if len(in) < ecRecoverInputLength { + return nil + } + + // 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) { + return nil + } + + // v needs to be moved to the end + rsv := append(in[64:128], byte(v.Uint64())) + pubKey, err := crypto.Ecrecover(in[:32], rsv) + // make sure the public key is a valid one + if err != nil { + glog.V(logger.Error).Infof("EC RECOVER FAIL: ", err) + return nil + } + + // the first byte of pubkey is bitcoin heritage + return common.LeftPadBytes(crypto.Sha3(pubKey[1:])[12:], 32) +} + +func memCpy(in []byte) []byte { + return in +} 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/opcodes.go b/core/vm/opcodes.go new file mode 100644 index 000000000..1ea80a212 --- /dev/null +++ b/core/vm/opcodes.go @@ -0,0 +1,334 @@ +package vm + +import ( + "fmt" +) + +type OpCode byte + +// Op codes +const ( + // 0x0 range - arithmetic ops + STOP OpCode = iota + ADD + MUL + SUB + DIV + SDIV + MOD + SMOD + ADDMOD + MULMOD + EXP + SIGNEXTEND +) + +const ( + LT OpCode = iota + 0x10 + GT + SLT + SGT + EQ + ISZERO + AND + OR + XOR + NOT + BYTE + + SHA3 = 0x20 +) + +const ( + // 0x30 range - closure state + ADDRESS OpCode = 0x30 + iota + BALANCE + ORIGIN + CALLER + CALLVALUE + CALLDATALOAD + CALLDATASIZE + CALLDATACOPY + CODESIZE + CODECOPY + GASPRICE + EXTCODESIZE + EXTCODECOPY +) + +const ( + + // 0x40 range - block operations + BLOCKHASH OpCode = 0x40 + iota + COINBASE + TIMESTAMP + NUMBER + DIFFICULTY + GASLIMIT +) + +const ( + // 0x50 range - 'storage' and execution + POP OpCode = 0x50 + iota + MLOAD + MSTORE + MSTORE8 + SLOAD + SSTORE + JUMP + JUMPI + PC + MSIZE + GAS + JUMPDEST +) + +const ( + // 0x60 range + PUSH1 OpCode = 0x60 + iota + 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 + DUP1 + DUP2 + DUP3 + DUP4 + DUP5 + DUP6 + DUP7 + DUP8 + DUP9 + DUP10 + DUP11 + DUP12 + DUP13 + DUP14 + DUP15 + DUP16 + SWAP1 + SWAP2 + SWAP3 + SWAP4 + SWAP5 + SWAP6 + SWAP7 + SWAP8 + SWAP9 + SWAP10 + SWAP11 + SWAP12 + SWAP13 + SWAP14 + SWAP15 + SWAP16 +) + +const ( + LOG0 OpCode = 0xa0 + iota + LOG1 + LOG2 + LOG3 + LOG4 +) + +const ( + // 0xf0 range - closures + CREATE OpCode = 0xf0 + iota + CALL + CALLCODE + RETURN + + // 0x70 range - other + SUICIDE = 0xff +) + +// Since the opcodes aren't all in order we can't use a regular slice +var opCodeToString = map[OpCode]string{ + // 0x0 range - arithmetic ops + STOP: "STOP", + ADD: "ADD", + MUL: "MUL", + SUB: "SUB", + DIV: "DIV", + SDIV: "SDIV", + MOD: "MOD", + SMOD: "SMOD", + EXP: "EXP", + NOT: "NOT", + LT: "LT", + GT: "GT", + SLT: "SLT", + SGT: "SGT", + EQ: "EQ", + ISZERO: "ISZERO", + SIGNEXTEND: "SIGNEXTEND", + + // 0x10 range - bit ops + AND: "AND", + OR: "OR", + XOR: "XOR", + BYTE: "BYTE", + ADDMOD: "ADDMOD", + MULMOD: "MULMOD", + + // 0x20 range - crypto + SHA3: "SHA3", + + // 0x30 range - closure state + ADDRESS: "ADDRESS", + BALANCE: "BALANCE", + ORIGIN: "ORIGIN", + CALLER: "CALLER", + CALLVALUE: "CALLVALUE", + CALLDATALOAD: "CALLDATALOAD", + CALLDATASIZE: "CALLDATASIZE", + CALLDATACOPY: "CALLDATACOPY", + CODESIZE: "CODESIZE", + CODECOPY: "CODECOPY", + GASPRICE: "TXGASPRICE", + + // 0x40 range - block operations + BLOCKHASH: "BLOCKHASH", + COINBASE: "COINBASE", + TIMESTAMP: "TIMESTAMP", + NUMBER: "NUMBER", + DIFFICULTY: "DIFFICULTY", + GASLIMIT: "GASLIMIT", + EXTCODESIZE: "EXTCODESIZE", + EXTCODECOPY: "EXTCODECOPY", + + // 0x50 range - 'storage' and execution + POP: "POP", + //DUP: "DUP", + //SWAP: "SWAP", + MLOAD: "MLOAD", + MSTORE: "MSTORE", + MSTORE8: "MSTORE8", + SLOAD: "SLOAD", + SSTORE: "SSTORE", + JUMP: "JUMP", + JUMPI: "JUMPI", + PC: "PC", + MSIZE: "MSIZE", + GAS: "GAS", + JUMPDEST: "JUMPDEST", + + // 0x60 range - push + PUSH1: "PUSH1", + PUSH2: "PUSH2", + PUSH3: "PUSH3", + PUSH4: "PUSH4", + PUSH5: "PUSH5", + PUSH6: "PUSH6", + PUSH7: "PUSH7", + PUSH8: "PUSH8", + PUSH9: "PUSH9", + PUSH10: "PUSH10", + PUSH11: "PUSH11", + PUSH12: "PUSH12", + PUSH13: "PUSH13", + PUSH14: "PUSH14", + PUSH15: "PUSH15", + PUSH16: "PUSH16", + PUSH17: "PUSH17", + PUSH18: "PUSH18", + PUSH19: "PUSH19", + PUSH20: "PUSH20", + PUSH21: "PUSH21", + PUSH22: "PUSH22", + PUSH23: "PUSH23", + PUSH24: "PUSH24", + PUSH25: "PUSH25", + PUSH26: "PUSH26", + PUSH27: "PUSH27", + PUSH28: "PUSH28", + PUSH29: "PUSH29", + PUSH30: "PUSH30", + PUSH31: "PUSH31", + PUSH32: "PUSH32", + + DUP1: "DUP1", + DUP2: "DUP2", + DUP3: "DUP3", + DUP4: "DUP4", + DUP5: "DUP5", + DUP6: "DUP6", + DUP7: "DUP7", + DUP8: "DUP8", + DUP9: "DUP9", + DUP10: "DUP10", + DUP11: "DUP11", + DUP12: "DUP12", + DUP13: "DUP13", + DUP14: "DUP14", + DUP15: "DUP15", + DUP16: "DUP16", + + SWAP1: "SWAP1", + SWAP2: "SWAP2", + SWAP3: "SWAP3", + SWAP4: "SWAP4", + SWAP5: "SWAP5", + SWAP6: "SWAP6", + SWAP7: "SWAP7", + SWAP8: "SWAP8", + SWAP9: "SWAP9", + SWAP10: "SWAP10", + SWAP11: "SWAP11", + SWAP12: "SWAP12", + SWAP13: "SWAP13", + SWAP14: "SWAP14", + SWAP15: "SWAP15", + SWAP16: "SWAP16", + LOG0: "LOG0", + LOG1: "LOG1", + LOG2: "LOG2", + LOG3: "LOG3", + LOG4: "LOG4", + + // 0xf0 range + CREATE: "CREATE", + CALL: "CALL", + RETURN: "RETURN", + CALLCODE: "CALLCODE", + + // 0x70 range - other + SUICIDE: "SUICIDE", +} + +func (o OpCode) String() string { + str := opCodeToString[o] + if len(str) == 0 { + return fmt.Sprintf("Missing opcode 0x%x", int(o)) + } + + return str +} diff --git a/core/vm/types.go b/core/vm/types.go deleted file mode 100644 index 1ea80a212..000000000 --- a/core/vm/types.go +++ /dev/null @@ -1,334 +0,0 @@ -package vm - -import ( - "fmt" -) - -type OpCode byte - -// Op codes -const ( - // 0x0 range - arithmetic ops - STOP OpCode = iota - ADD - MUL - SUB - DIV - SDIV - MOD - SMOD - ADDMOD - MULMOD - EXP - SIGNEXTEND -) - -const ( - LT OpCode = iota + 0x10 - GT - SLT - SGT - EQ - ISZERO - AND - OR - XOR - NOT - BYTE - - SHA3 = 0x20 -) - -const ( - // 0x30 range - closure state - ADDRESS OpCode = 0x30 + iota - BALANCE - ORIGIN - CALLER - CALLVALUE - CALLDATALOAD - CALLDATASIZE - CALLDATACOPY - CODESIZE - CODECOPY - GASPRICE - EXTCODESIZE - EXTCODECOPY -) - -const ( - - // 0x40 range - block operations - BLOCKHASH OpCode = 0x40 + iota - COINBASE - TIMESTAMP - NUMBER - DIFFICULTY - GASLIMIT -) - -const ( - // 0x50 range - 'storage' and execution - POP OpCode = 0x50 + iota - MLOAD - MSTORE - MSTORE8 - SLOAD - SSTORE - JUMP - JUMPI - PC - MSIZE - GAS - JUMPDEST -) - -const ( - // 0x60 range - PUSH1 OpCode = 0x60 + iota - 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 - DUP1 - DUP2 - DUP3 - DUP4 - DUP5 - DUP6 - DUP7 - DUP8 - DUP9 - DUP10 - DUP11 - DUP12 - DUP13 - DUP14 - DUP15 - DUP16 - SWAP1 - SWAP2 - SWAP3 - SWAP4 - SWAP5 - SWAP6 - SWAP7 - SWAP8 - SWAP9 - SWAP10 - SWAP11 - SWAP12 - SWAP13 - SWAP14 - SWAP15 - SWAP16 -) - -const ( - LOG0 OpCode = 0xa0 + iota - LOG1 - LOG2 - LOG3 - LOG4 -) - -const ( - // 0xf0 range - closures - CREATE OpCode = 0xf0 + iota - CALL - CALLCODE - RETURN - - // 0x70 range - other - SUICIDE = 0xff -) - -// Since the opcodes aren't all in order we can't use a regular slice -var opCodeToString = map[OpCode]string{ - // 0x0 range - arithmetic ops - STOP: "STOP", - ADD: "ADD", - MUL: "MUL", - SUB: "SUB", - DIV: "DIV", - SDIV: "SDIV", - MOD: "MOD", - SMOD: "SMOD", - EXP: "EXP", - NOT: "NOT", - LT: "LT", - GT: "GT", - SLT: "SLT", - SGT: "SGT", - EQ: "EQ", - ISZERO: "ISZERO", - SIGNEXTEND: "SIGNEXTEND", - - // 0x10 range - bit ops - AND: "AND", - OR: "OR", - XOR: "XOR", - BYTE: "BYTE", - ADDMOD: "ADDMOD", - MULMOD: "MULMOD", - - // 0x20 range - crypto - SHA3: "SHA3", - - // 0x30 range - closure state - ADDRESS: "ADDRESS", - BALANCE: "BALANCE", - ORIGIN: "ORIGIN", - CALLER: "CALLER", - CALLVALUE: "CALLVALUE", - CALLDATALOAD: "CALLDATALOAD", - CALLDATASIZE: "CALLDATASIZE", - CALLDATACOPY: "CALLDATACOPY", - CODESIZE: "CODESIZE", - CODECOPY: "CODECOPY", - GASPRICE: "TXGASPRICE", - - // 0x40 range - block operations - BLOCKHASH: "BLOCKHASH", - COINBASE: "COINBASE", - TIMESTAMP: "TIMESTAMP", - NUMBER: "NUMBER", - DIFFICULTY: "DIFFICULTY", - GASLIMIT: "GASLIMIT", - EXTCODESIZE: "EXTCODESIZE", - EXTCODECOPY: "EXTCODECOPY", - - // 0x50 range - 'storage' and execution - POP: "POP", - //DUP: "DUP", - //SWAP: "SWAP", - MLOAD: "MLOAD", - MSTORE: "MSTORE", - MSTORE8: "MSTORE8", - SLOAD: "SLOAD", - SSTORE: "SSTORE", - JUMP: "JUMP", - JUMPI: "JUMPI", - PC: "PC", - MSIZE: "MSIZE", - GAS: "GAS", - JUMPDEST: "JUMPDEST", - - // 0x60 range - push - PUSH1: "PUSH1", - PUSH2: "PUSH2", - PUSH3: "PUSH3", - PUSH4: "PUSH4", - PUSH5: "PUSH5", - PUSH6: "PUSH6", - PUSH7: "PUSH7", - PUSH8: "PUSH8", - PUSH9: "PUSH9", - PUSH10: "PUSH10", - PUSH11: "PUSH11", - PUSH12: "PUSH12", - PUSH13: "PUSH13", - PUSH14: "PUSH14", - PUSH15: "PUSH15", - PUSH16: "PUSH16", - PUSH17: "PUSH17", - PUSH18: "PUSH18", - PUSH19: "PUSH19", - PUSH20: "PUSH20", - PUSH21: "PUSH21", - PUSH22: "PUSH22", - PUSH23: "PUSH23", - PUSH24: "PUSH24", - PUSH25: "PUSH25", - PUSH26: "PUSH26", - PUSH27: "PUSH27", - PUSH28: "PUSH28", - PUSH29: "PUSH29", - PUSH30: "PUSH30", - PUSH31: "PUSH31", - PUSH32: "PUSH32", - - DUP1: "DUP1", - DUP2: "DUP2", - DUP3: "DUP3", - DUP4: "DUP4", - DUP5: "DUP5", - DUP6: "DUP6", - DUP7: "DUP7", - DUP8: "DUP8", - DUP9: "DUP9", - DUP10: "DUP10", - DUP11: "DUP11", - DUP12: "DUP12", - DUP13: "DUP13", - DUP14: "DUP14", - DUP15: "DUP15", - DUP16: "DUP16", - - SWAP1: "SWAP1", - SWAP2: "SWAP2", - SWAP3: "SWAP3", - SWAP4: "SWAP4", - SWAP5: "SWAP5", - SWAP6: "SWAP6", - SWAP7: "SWAP7", - SWAP8: "SWAP8", - SWAP9: "SWAP9", - SWAP10: "SWAP10", - SWAP11: "SWAP11", - SWAP12: "SWAP12", - SWAP13: "SWAP13", - SWAP14: "SWAP14", - SWAP15: "SWAP15", - SWAP16: "SWAP16", - LOG0: "LOG0", - LOG1: "LOG1", - LOG2: "LOG2", - LOG3: "LOG3", - LOG4: "LOG4", - - // 0xf0 range - CREATE: "CREATE", - CALL: "CALL", - RETURN: "RETURN", - CALLCODE: "CALLCODE", - - // 0x70 range - other - SUICIDE: "SUICIDE", -} - -func (o OpCode) String() string { - str := opCodeToString[o] - if len(str) == 0 { - return fmt.Sprintf("Missing opcode 0x%x", int(o)) - } - - return str -} 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 -- cgit v1.2.3 From b26f5e0bb7e8922c80bc3513d1ebce2c99a081f5 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 31 May 2015 15:53:17 +0200 Subject: types: block json unmarshal method added --- core/genesis.go | 4 ++-- core/types/block.go | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/genesis.go b/core/genesis.go index e72834822..a9b7339f3 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -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/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) -- cgit v1.2.3 From 770a0e78396d66dc0b15a267891ed69be1414f52 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 3 Jun 2015 13:14:06 +0200 Subject: wip --- core/block_processor.go | 6 ------ core/chain_manager.go | 5 +++-- 2 files changed, 3 insertions(+), 8 deletions(-) (limited to 'core') diff --git a/core/block_processor.go b/core/block_processor.go index a3ad383d0..9a013b280 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -178,7 +178,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 +253,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)) diff --git a/core/chain_manager.go b/core/chain_manager.go index 927055103..d58c0d504 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -560,6 +560,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { defer close(nonceQuit) for i, block := range chain { + bstart := time.Now() // Wait for block i's nonce to be verified before processing // its state transition. for nonceChecked[i] { @@ -642,11 +643,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} -- cgit v1.2.3 From 08befff8f168f0fd59a3ff36a7205ba44fb82540 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 3 Jun 2015 14:06:20 +0200 Subject: core: compute less transaction hashes in TxPool --- core/transaction_pool.go | 144 +++++++++++++++++++++--------------------- core/transaction_pool_test.go | 20 +++--- 2 files changed, 82 insertions(+), 82 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 4296c79f6..d8debe1c0 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -61,7 +61,7 @@ type TxPool struct { txs map[common.Hash]*types.Transaction invalidHashes *set.Set - queue map[common.Address]types.Transactions + queue map[common.Address]map[common.Hash]*types.Transaction subscribers []chan TxMsg @@ -71,7 +71,7 @@ type TxPool struct { 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), + queue: make(map[common.Address]map[common.Hash]*types.Transaction), queueChan: make(chan *types.Transaction, txPoolQueueSize), quit: make(chan bool), eventMux: eventMux, @@ -157,22 +157,20 @@ func (self *TxPool) add(tx *types.Transaction) error { 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 @@ -211,16 +209,12 @@ func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { if tx, ok := tp.txs[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 } @@ -234,26 +228,26 @@ func (self *TxPool) GetTransactions() (txs types.Transactions) { txs[i] = tx i++ } - - return + return txs } 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 } func (self *TxPool) RemoveTransactions(txs types.Transactions) { self.mu.Lock() defer self.mu.Unlock() - for _, tx := range txs { self.removeTx(tx.Hash()) } @@ -270,14 +264,17 @@ func (pool *TxPool) Stop() { glog.V(logger.Info).Infoln("TX Pool stopped") } -func (self *TxPool) queueTx(tx *types.Transaction) { +func (self *TxPool) queueTx(hash common.Hash, tx *types.Transaction) { from, _ := tx.From() // already validated - self.queue[from] = append(self.queue[from], tx) + if self.queue[from] == nil { + self.queue[from] = make(map[common.Hash]*types.Transaction) + } + self.queue[from][hash] = tx } -func (pool *TxPool) addTx(tx *types.Transaction) { - if _, ok := pool.txs[tx.Hash()]; !ok { - pool.txs[tx.Hash()] = tx +func (pool *TxPool) addTx(hash common.Hash, tx *types.Transaction) { + if _, ok := pool.txs[hash]; !ok { + pool.txs[hash] = tx // 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,36 +288,33 @@ func (pool *TxPool) checkQueue() { defer pool.mu.Unlock() 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 + curnonce := statedb.GetNonce(address) + addq := addq[:0] + for hash, tx := range txs { + if tx.AccountNonce < curnonce { + // 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, 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 != curnonce { break } - enonce++ - - pool.addTx(tx) + curnonce++ + delete(txs, e.hash) + pool.addTx(e.hash, 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) } } @@ -329,20 +323,16 @@ func (pool *TxPool) checkQueue() { func (pool *TxPool) removeTx(hash common.Hash) { // delete from pending pool delete(pool.txs, 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 } } } @@ -356,8 +346,18 @@ func (pool *TxPool) validatePool() { if glog.V(logger.Info) { glog.Infof("removed tx (%x) from pool: %v\n", hash[:4], err) } - - pool.removeTx(hash) + delete(pool.txs, hash) } } } + +type txQueue []txQueueEntry + +type txQueueEntry struct { + hash common.Hash + *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..600fd9b4f 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -68,7 +68,7 @@ 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 { @@ -80,7 +80,7 @@ func TestTransactionQueue(t *testing.T) { from, _ = tx.From() pool.currentState().SetNonce(from, 10) tx.SetNonce(1) - pool.queueTx(tx) + pool.queueTx(tx.Hash(), tx) pool.checkQueue() if _, ok := pool.txs[tx.Hash()]; ok { t.Error("expected transaction to be in tx pool") @@ -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 { 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,8 +118,8 @@ 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(), tx) if len(pool.queue) != 1 { t.Error("expected queue to be 1, got", len(pool.queue)) } -- cgit v1.2.3 From ca31d71107127a1b4f950e334ee5a99955d97e3c Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 3 Jun 2015 14:20:44 +0200 Subject: core: remove unused code from TxPool --- core/transaction_pool.go | 54 ++++++++++++------------------------------------ 1 file changed, 13 insertions(+), 41 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index d8debe1c0..fa2c4fed6 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -14,7 +14,6 @@ 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 ( @@ -28,58 +27,31 @@ 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. 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 + quit chan bool // Quiting channel + currentState stateFn // The state function which will allow us to do some pre checkes + gasLimit func() *big.Int // The current gas limit function callback + eventMux *event.TypeMux + mu sync.RWMutex + txs map[common.Hash]*types.Transaction // The actual pool queue map[common.Address]map[common.Hash]*types.Transaction - - subscribers []chan TxMsg - - eventMux *event.TypeMux } 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]map[common.Hash]*types.Transaction), - queueChan: make(chan *types.Transaction, txPoolQueueSize), - quit: make(chan bool), - eventMux: eventMux, - invalidHashes: set.New(), - currentState: currentStateFn, - gasLimit: gasLimitFn, + return &TxPool{ + txs: 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, } - return txPool } func (pool *TxPool) Start() { -- cgit v1.2.3 From 5721c43585100aad82a4c18341e9518c6e91393b Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 3 Jun 2015 15:23:31 +0200 Subject: core: update documentation comments for TxPool --- core/transaction_pool.go | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index fa2c4fed6..7d58ffbd9 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -17,6 +17,7 @@ import ( ) var ( + // Transaction Pool Errors ErrInvalidSender = errors.New("Invalid sender") ErrNonce = errors.New("Nonce too low") ErrBalance = errors.New("Insufficient balance") @@ -29,9 +30,13 @@ var ( type stateFn func() *state.StateDB -// 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 { quit chan bool // Quiting channel currentState stateFn // The state function which will allow us to do some pre checkes @@ -39,7 +44,7 @@ type TxPool struct { eventMux *event.TypeMux mu sync.RWMutex - txs map[common.Hash]*types.Transaction // The actual pool + txs map[common.Hash]*types.Transaction // processable transactions queue map[common.Address]map[common.Hash]*types.Transaction } @@ -73,7 +78,9 @@ done: } } -func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error { +// 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 @@ -125,7 +132,7 @@ func (self *TxPool) add(tx *types.Transaction) error { if self.txs[hash] != nil { return fmt.Errorf("Known transaction (%x)", hash[:4]) } - err := self.ValidateTransaction(tx) + err := self.validateTx(tx) if err != nil { return err } @@ -148,10 +155,7 @@ func (self *TxPool) add(tx *types.Transaction) error { return nil } -func (self *TxPool) Size() int { - return len(self.txs) -} - +// 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() @@ -159,6 +163,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() @@ -173,9 +178,8 @@ 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 { @@ -190,11 +194,12 @@ func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { return nil } +// GetTransactions returns all currently processable transactions. func (self *TxPool) GetTransactions() (txs types.Transactions) { self.mu.RLock() defer self.mu.RUnlock() - txs = make(types.Transactions, self.Size()) + txs = make(types.Transactions, len(self.txs)) i := 0 for _, tx := range self.txs { txs[i] = tx @@ -203,6 +208,7 @@ func (self *TxPool) GetTransactions() (txs types.Transactions) { return txs } +// GetQueuedTransactions returns all non-processable transactions. func (self *TxPool) GetQueuedTransactions() types.Transactions { self.mu.RLock() defer self.mu.RUnlock() @@ -217,6 +223,7 @@ func (self *TxPool) GetQueuedTransactions() types.Transactions { return ret } +// RemoveTransactions removes all given transactions from the pool. func (self *TxPool) RemoveTransactions(txs types.Transactions) { self.mu.Lock() defer self.mu.Unlock() @@ -225,14 +232,9 @@ func (self *TxPool) RemoveTransactions(txs types.Transactions) { } } -func (pool *TxPool) Flush() { - pool.txs = make(map[common.Hash]*types.Transaction) -} - func (pool *TxPool) Stop() { - pool.Flush() + pool.txs = make(map[common.Hash]*types.Transaction) close(pool.quit) - glog.V(logger.Info).Infoln("TX Pool stopped") } @@ -254,7 +256,7 @@ func (pool *TxPool) addTx(hash common.Hash, 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() @@ -309,12 +311,13 @@ func (pool *TxPool) removeTx(hash common.Hash) { } } +// 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 err := pool.validateTx(tx); err != nil { if glog.V(logger.Info) { glog.Infof("removed tx (%x) from pool: %v\n", hash[:4], err) } -- cgit v1.2.3 From ec7a2c34423f9337c238ff07c39a15eb18eed79a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 3 Jun 2015 15:52:25 +0200 Subject: core: don't remove transactions after block processing The transaction pool drops processed transactions on its own during pool maintenance. --- core/block_processor.go | 5 +---- core/block_processor_test.go | 2 +- core/chain_makers.go | 3 +-- core/chain_manager_test.go | 6 ++---- 4 files changed, 5 insertions(+), 11 deletions(-) (limited to 'core') diff --git a/core/block_processor.go b/core/block_processor.go index 9a013b280..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 diff --git a/core/block_processor_test.go b/core/block_processor_test.go index 72b173a71..b52c3d3f8 100644 --- a/core/block_processor_test.go +++ b/core/block_processor_test.go @@ -17,7 +17,7 @@ func proc() (*BlockProcessor, *ChainManager) { var mux event.TypeMux chainMan := NewChainManager(db, db, thePow(), &mux) - return NewBlockProcessor(db, db, ezp.New(), nil, chainMan, &mux), chainMan + return NewBlockProcessor(db, db, ezp.New(), chainMan, &mux), chainMan } func TestNumber(t *testing.T) { diff --git a/core/chain_makers.go b/core/chain_makers.go index 44f17cc33..3039e52da 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -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_test.go b/core/chain_manager_test.go index 7dc7358c0..560e85f77 100644 --- a/core/chain_manager_test.go +++ b/core/chain_manager_test.go @@ -267,8 +267,7 @@ func TestChainInsertions(t *testing.T) { 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) + blockMan := NewBlockProcessor(db, db, nil, chainMan, &eventMux) chainMan.SetProcessor(blockMan) const max = 2 @@ -313,8 +312,7 @@ func TestChainMultipleInsertions(t *testing.T) { } 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) + blockMan := NewBlockProcessor(db, db, nil, chainMan, &eventMux) chainMan.SetProcessor(blockMan) done := make(chan bool, max) for i, chain := range chains { -- cgit v1.2.3 From d09a6e54215bef8b1ac16a99f0b1d75a8a92a6a8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 3 Jun 2015 22:22:20 +0200 Subject: core, eth, miner: moved nonce management to tx pool. Removed the managed tx state from the chain manager to the transaction pool where it's much easier to keep track of nonces (and manage them). The transaction pool now also uses the queue and pending txs differently where queued txs are now moved over to the pending queue (i.e. txs ready for processing and propagation). --- core/chain_manager.go | 21 +---------- core/transaction_pool.go | 88 +++++++++++++++++++++++++------------------ core/transaction_pool_test.go | 20 +++++----- 3 files changed, 62 insertions(+), 67 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index d58c0d504..d14a19fea 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -214,19 +214,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 } @@ -751,7 +738,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 @@ -760,12 +747,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) diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 7d58ffbd9..5e6f2c6a4 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" @@ -38,10 +37,12 @@ type stateFn func() *state.StateDB // current state) and future transactions. Transactions move between those // two states over time as they are received and processed. type TxPool struct { - quit chan bool // Quiting channel - currentState stateFn // The state function which will allow us to do some pre checkes + quit chan bool // Quiting channel + currentState stateFn // The state function which will allow us to do some pre checkes + state *state.ManagedState gasLimit func() *big.Int // The current gas limit function callback eventMux *event.TypeMux + events event.Subscription mu sync.RWMutex txs map[common.Hash]*types.Transaction // processable transactions @@ -56,28 +57,41 @@ func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func( eventMux: eventMux, currentState: currentStateFn, gasLimit: gasLimitFn, + state: state.ManageState(currentStateFn()), } } 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 + pool.events = pool.eventMux.Subscribe(ChainEvent{}) + for _ = range pool.events.Chan() { + pool.mu.Lock() + pool.state = state.ManageState(pool.currentState()) + + for _, tx := range pool.txs { + if addr, err := tx.From(); err == nil { + pool.state.SetNonce(addr, tx.Nonce()) + } } + + pool.checkQueue() + pool.mu.Unlock() } } +func (pool *TxPool) Stop() { + pool.txs = make(map[common.Hash]*types.Transaction) + close(pool.quit) + pool.events.Unsubscribe() + glog.V(logger.Info).Infoln("TX Pool stopped") +} + +func (pool *TxPool) State() *state.ManagedState { + pool.mu.RLock() + defer pool.mu.RUnlock() + + return pool.state +} + // validateTx checks whether a transaction is valid according // to the consensus rules. func (pool *TxPool) validateTx(tx *types.Transaction) error { @@ -152,6 +166,9 @@ func (self *TxPool) add(tx *types.Transaction) error { glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, hash) } + // check and validate the queueue + self.checkQueue() + return nil } @@ -196,8 +213,13 @@ func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { // GetTransactions returns all currently processable transactions. 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, len(self.txs)) i := 0 @@ -232,12 +254,6 @@ func (self *TxPool) RemoveTransactions(txs types.Transactions) { } } -func (pool *TxPool) Stop() { - pool.txs = make(map[common.Hash]*types.Transaction) - 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 { @@ -246,9 +262,11 @@ func (self *TxPool) queueTx(hash common.Hash, tx *types.Transaction) { self.queue[from][hash] = tx } -func (pool *TxPool) addTx(hash common.Hash, tx *types.Transaction) { +func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Transaction) { if _, ok := pool.txs[hash]; !ok { pool.txs[hash] = tx + + pool.state.SetNonce(addr, tx.AccountNonce) // 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. @@ -258,34 +276,32 @@ func (pool *TxPool) addTx(hash common.Hash, tx *types.Transaction) { // checkQueue moves transactions that have become processable to main pool. func (pool *TxPool) checkQueue() { - pool.mu.Lock() - defer pool.mu.Unlock() + state := pool.state - statedb := pool.currentState() var addq txQueue for address, txs := range pool.queue { - curnonce := statedb.GetNonce(address) + curnonce := state.GetNonce(address) addq := addq[:0] for hash, tx := range txs { if tx.AccountNonce < curnonce { + fmt.Println("delete the tx", tx.AccountNonce, curnonce) // 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, tx}) + addq = append(addq, txQueueEntry{hash, address, tx}) } } // Find the next consecutive nonce range starting at the // current account nonce. sort.Sort(addq) for _, e := range addq { - if e.AccountNonce != curnonce { + if e.AccountNonce > curnonce+1 { break } - curnonce++ delete(txs, e.hash) - pool.addTx(e.hash, e.Transaction) + pool.addTx(e.hash, address, e.Transaction) } // Delete the entire queue entry if it became empty. if len(txs) == 0 { @@ -313,9 +329,6 @@ func (pool *TxPool) removeTx(hash common.Hash) { // 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.validateTx(tx); err != nil { if glog.V(logger.Info) { @@ -330,6 +343,7 @@ type txQueue []txQueueEntry type txQueueEntry struct { hash common.Hash + addr common.Address *types.Transaction } diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go index 600fd9b4f..170bdfa72 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -37,21 +37,21 @@ func TestInvalidTransactions(t *testing.T) { } from, _ := tx.From() - pool.currentState().AddBalance(from, big.NewInt(1)) + pool.state.AddBalance(from, big.NewInt(1)) err = pool.Add(tx) if err != ErrInsufficientFunds { t.Error("expected", ErrInsufficientFunds) } balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(tx.Gas(), tx.GasPrice())) - pool.currentState().AddBalance(from, balance) + pool.state.AddBalance(from, balance) err = pool.Add(tx) if err != ErrIntrinsicGas { t.Error("expected", ErrIntrinsicGas, "got", err) } - pool.currentState().SetNonce(from, 1) - pool.currentState().AddBalance(from, big.NewInt(0xffffffffffffff)) + pool.state.SetNonce(from, 1) + pool.state.AddBalance(from, big.NewInt(0xffffffffffffff)) tx.GasLimit = big.NewInt(100000) tx.Price = big.NewInt(1) tx.SignECDSA(key) @@ -67,7 +67,7 @@ func TestTransactionQueue(t *testing.T) { tx := transaction() tx.SignECDSA(key) from, _ := tx.From() - pool.currentState().AddBalance(from, big.NewInt(1)) + pool.state.AddBalance(from, big.NewInt(1)) pool.queueTx(tx.Hash(), tx) pool.checkQueue() @@ -76,17 +76,17 @@ func TestTransactionQueue(t *testing.T) { } tx = transaction() + tx.SetNonce(1) tx.SignECDSA(key) from, _ = tx.From() - pool.currentState().SetNonce(from, 10) - tx.SetNonce(1) + pool.state.SetNonce(from, 2) pool.queueTx(tx.Hash(), tx) pool.checkQueue() if _, ok := pool.txs[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])) } @@ -117,7 +117,7 @@ func TestRemoveTx(t *testing.T) { tx := transaction() tx.SignECDSA(key) from, _ := tx.From() - pool.currentState().AddBalance(from, big.NewInt(1)) + pool.state.AddBalance(from, big.NewInt(1)) pool.queueTx(tx.Hash(), tx) pool.addTx(tx.Hash(), tx) if len(pool.queue) != 1 { @@ -146,7 +146,7 @@ func TestNegativeValue(t *testing.T) { tx.Value().Set(big.NewInt(-1)) tx.SignECDSA(key) from, _ := tx.From() - pool.currentState().AddBalance(from, big.NewInt(1)) + pool.state.AddBalance(from, big.NewInt(1)) err := pool.Add(tx) if err != ErrNegativeValue { t.Error("expected", ErrNegativeValue, "got", err) -- cgit v1.2.3 From 140d8839018527fe64a0c1a6b79af4ccae66ec3a Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 3 Jun 2015 22:53:33 +0200 Subject: core: test updates --- core/transaction_pool.go | 1 - core/transaction_pool_test.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 5e6f2c6a4..ba0fba91f 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -284,7 +284,6 @@ func (pool *TxPool) checkQueue() { addq := addq[:0] for hash, tx := range txs { if tx.AccountNonce < curnonce { - fmt.Println("delete the tx", tx.AccountNonce, curnonce) // Drop queued transactions whose nonce is lower than // the account nonce because they have been processed. delete(txs, hash) diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go index 170bdfa72..df5af9429 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -119,7 +119,7 @@ func TestRemoveTx(t *testing.T) { from, _ := tx.From() pool.state.AddBalance(from, big.NewInt(1)) pool.queueTx(tx.Hash(), tx) - pool.addTx(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)) } -- cgit v1.2.3 From 9dd12a64a7dc998e32ee1bcf6b23d9d55cf6e6c0 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 4 Jun 2015 12:47:46 +0200 Subject: core: renamed txs to pending --- core/transaction_pool.go | 32 ++++++++++++++++---------------- core/transaction_pool_test.go | 16 ++++++++-------- 2 files changed, 24 insertions(+), 24 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index ba0fba91f..c59eaa061 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -44,14 +44,14 @@ type TxPool struct { eventMux *event.TypeMux events event.Subscription - mu sync.RWMutex - txs map[common.Hash]*types.Transaction // processable transactions - queue map[common.Address]map[common.Hash]*types.Transaction + 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 { return &TxPool{ - txs: make(map[common.Hash]*types.Transaction), + pending: make(map[common.Hash]*types.Transaction), queue: make(map[common.Address]map[common.Hash]*types.Transaction), quit: make(chan bool), eventMux: eventMux, @@ -67,7 +67,7 @@ func (pool *TxPool) Start() { pool.mu.Lock() pool.state = state.ManageState(pool.currentState()) - for _, tx := range pool.txs { + for _, tx := range pool.pending { if addr, err := tx.From(); err == nil { pool.state.SetNonce(addr, tx.Nonce()) } @@ -79,7 +79,7 @@ func (pool *TxPool) Start() { } func (pool *TxPool) Stop() { - pool.txs = make(map[common.Hash]*types.Transaction) + pool.pending = make(map[common.Hash]*types.Transaction) close(pool.quit) pool.events.Unsubscribe() glog.V(logger.Info).Infoln("TX Pool stopped") @@ -143,7 +143,7 @@ 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.validateTx(tx) @@ -199,7 +199,7 @@ func (self *TxPool) AddTransactions(txs []*types.Transaction) { // 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 @@ -221,9 +221,9 @@ func (self *TxPool) GetTransactions() (txs types.Transactions) { // invalidate any txs self.validatePool() - txs = make(types.Transactions, len(self.txs)) + txs = make(types.Transactions, len(self.pending)) i := 0 - for _, tx := range self.txs { + for _, tx := range self.pending { txs[i] = tx i++ } @@ -263,8 +263,8 @@ func (self *TxPool) queueTx(hash common.Hash, tx *types.Transaction) { } func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Transaction) { - if _, ok := pool.txs[hash]; !ok { - pool.txs[hash] = tx + if _, ok := pool.pending[hash]; !ok { + pool.pending[hash] = tx pool.state.SetNonce(addr, tx.AccountNonce) // Notify the subscribers. This event is posted in a goroutine @@ -311,7 +311,7 @@ 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 for address, txs := range pool.queue { if _, ok := txs[hash]; ok { @@ -328,12 +328,12 @@ func (pool *TxPool) removeTx(hash common.Hash) { // validatePool removes invalid and processed transactions from the main pool. func (pool *TxPool) validatePool() { - for hash, tx := range pool.txs { + for hash, tx := range pool.pending { if err := pool.validateTx(tx); err != nil { - if glog.V(logger.Info) { + if glog.V(logger.Core) { glog.Infof("removed tx (%x) from pool: %v\n", hash[:4], err) } - delete(pool.txs, hash) + delete(pool.pending, hash) } } } diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go index df5af9429..bbd5ddad4 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -71,8 +71,8 @@ func TestTransactionQueue(t *testing.T) { 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() @@ -82,7 +82,7 @@ func TestTransactionQueue(t *testing.T) { pool.state.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") } @@ -104,7 +104,7 @@ func TestTransactionQueue(t *testing.T) { 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]) != 2 { @@ -124,8 +124,8 @@ func TestRemoveTx(t *testing.T) { 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)) } } -- cgit v1.2.3 From cf5ad266f6b8b7dd4800b63404c0efe680d47673 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 4 Jun 2015 15:44:42 +0200 Subject: core: only change the nonce if the account nonce is lower --- core/transaction_pool.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index c59eaa061..a0f3c326a 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -69,7 +69,9 @@ func (pool *TxPool) Start() { for _, tx := range pool.pending { if addr, err := tx.From(); err == nil { - pool.state.SetNonce(addr, tx.Nonce()) + if pool.state.GetNonce(addr) < tx.Nonce() { + pool.state.SetNonce(addr, tx.Nonce()) + } } } -- cgit v1.2.3 From dcdb4554d7f7ea3e96fec57805f0ea1042d0abc7 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 4 Jun 2015 16:19:22 +0200 Subject: core: documented changes in tx pool --- core/transaction_pool.go | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index a0f3c326a..462159fa7 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -62,19 +62,32 @@ func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func( } func (pool *TxPool) Start() { + // 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.state = 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 { - if pool.state.GetNonce(addr) < tx.Nonce() { - pool.state.SetNonce(addr, tx.Nonce()) - } + // Set the nonce. Transaction nonce can never be lower + // than the state nonce; validatePool took care of that. + pool.state.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() pool.mu.Unlock() } @@ -103,32 +116,46 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { 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 } + // 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 } -- cgit v1.2.3 From 0f51ee6c88f0697cec368d6e2c88b35cc173e37a Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 4 Jun 2015 16:52:23 +0200 Subject: crypto: return common.Address rather than raw bytes --- core/types/transaction_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'core') 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") } } -- cgit v1.2.3 From 912cf7ba049e4bcd5e497c62bb7cb96e7502f1b9 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 4 Jun 2015 17:28:09 +0200 Subject: core: added fork test & double nonce test --- core/transaction_pool.go | 46 ++++++++++++++++++++++------------------ core/transaction_pool_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 20 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 462159fa7..27dc1b0d1 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -68,31 +68,37 @@ func (pool *TxPool) Start() { pool.events = pool.eventMux.Subscribe(ChainEvent{}) for _ = range pool.events.Chan() { pool.mu.Lock() - pool.state = 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.state.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() + pool.resetState() + pool.mu.Unlock() } } +func (pool *TxPool) resetState() { + pool.state = 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.state.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) diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go index bbd5ddad4..ac297d266 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -152,3 +152,52 @@ func TestNegativeValue(t *testing.T) { t.Error("expected", ErrNegativeValue, "got", err) } } + +func TestTransactionChainFork(t *testing.T) { + pool, key := setupTxPool() + addr := crypto.PubkeyToAddress(key.PublicKey) + pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) + 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 + pool.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) + pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) + 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)) + } +} -- cgit v1.2.3 From 7ab87f9f6e3c3032d2d171c402286d7e92e3b98c Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 5 Jun 2015 14:07:49 +0200 Subject: wip --- core/chain_manager.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index d14a19fea..86d1c1454 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 { @@ -579,6 +580,13 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { } 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++ -- cgit v1.2.3 From 89c9320d8000845ee144f12adee958a5ab2303ef Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Fri, 5 Jun 2015 23:01:54 -0400 Subject: Allow exporting subset of chain --- core/chain_manager.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index 86d1c1454..1d1b6554e 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -341,13 +341,25 @@ 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(1), 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) - 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) -- cgit v1.2.3 From ed621aae333ac183acc29c396c708ae57301838a Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Sat, 6 Jun 2015 09:50:23 -0400 Subject: Cleanup --- core/chain_manager.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index 1d1b6554e..ae25ff6b1 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -351,13 +351,12 @@ func (self *ChainManager) Export(w io.Writer) error { 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) 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) + glog.V(logger.Info).Infof("exporting %d blocks...\n", last-first+1) for nr := first; nr <= last; nr++ { block := self.GetBlockByNumber(nr) -- cgit v1.2.3 From 4ab0cedf42abea8becc5177e566c070730f13a07 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Sat, 6 Jun 2015 10:59:56 -0400 Subject: Export should start with block 0 --- core/chain_manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index ae25ff6b1..a00fd288d 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -341,7 +341,7 @@ 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(1), self.currentBlock.NumberU64()); err != nil { + if err := self.ExportN(w, uint64(0), self.currentBlock.NumberU64()); err != nil { return err } return nil -- cgit v1.2.3 From 0b493910d38c4f6ed25a196b0e8071dc2afd1fd6 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 8 Jun 2015 02:19:39 +0200 Subject: core: fix the nonce check one more time The block nonce verification was effectively disabled by a typo. This time, there is an actual test for it. --- core/chain_manager.go | 6 +++--- core/chain_manager_test.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++ core/error.go | 17 +++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index 86d1c1454..291e411ae 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -551,12 +551,12 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { bstart := time.Now() // Wait for block i's nonce to be verified before processing // its state transition. - for nonceChecked[i] { + for !nonceChecked[i] { r := <-nonceDone nonceChecked[r.i] = true if !r.valid { - block := chain[i] - return i, ValidationError("Block (#%v / %x) nonce is invalid (= %x)", block.Number(), block.Hash(), block.Nonce) + block := chain[r.i] + return r.i, &BlockNonceErr{Hash: block.Hash(), Number: block.Number(), Nonce: block.Nonce()} } } diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go index 560e85f77..45bec7140 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" @@ -426,3 +427,55 @@ func TestReorgShortest(t *testing.T) { } } } + +func TestInsertNonceError(t *testing.T) { + for i := 1; i < 25 && !t.Failed(); i++ { + db, _ := ethdb.NewMemDatabase() + genesis := GenesisBlock(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()) + } + } + } +} + +// 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 } -- cgit v1.2.3 From 6c27e2aaf6de99518feea48981882a8a39fa0727 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 8 Jun 2015 02:49:43 +0200 Subject: core: add bad block for the 'missing nonce check' fork --- core/blocks.go | 1 + 1 file changed, 1 insertion(+) (limited to 'core') 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, } -- cgit v1.2.3 From 6244b10a8f74d92addf977994e5a9c0e457229bb Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 8 Jun 2015 12:12:13 +0200 Subject: core: settable genesis nonce You can set the nonce of the block with `--genesisnonce`. When the genesis nonce changes and it doesn't match with the first block in your database it will fail. A new `datadir` must be given if the nonce of the genesis block changes. --- core/block_processor_test.go | 7 +++++- core/chain_makers.go | 2 +- core/chain_manager.go | 24 +++++++++++++-------- core/chain_manager_test.go | 51 +++++++++++++++++++++++++++++++++----------- core/genesis.go | 4 ++-- 5 files changed, 62 insertions(+), 26 deletions(-) (limited to 'core') diff --git a/core/block_processor_test.go b/core/block_processor_test.go index b52c3d3f8..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,7 +17,11 @@ func proc() (*BlockProcessor, *ChainManager) { db, _ := ethdb.NewMemDatabase() var mux event.TypeMux - chainMan := NewChainManager(db, db, thePow(), &mux) + 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 } diff --git a/core/chain_makers.go b/core/chain_makers.go index 3039e52da..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) diff --git a/core/chain_manager.go b/core/chain_manager.go index 291e411ae..edd1cc742 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -109,16 +109,22 @@ 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), - eventMux: mux, - quit: make(chan struct{}), - cache: NewBlockCache(blockCacheLimit), - pow: pow, + blockDb: blockDb, + stateDb: 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 @@ -144,7 +150,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) { diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go index 45bec7140..c56a3b3e1 100644 --- a/core/chain_manager_test.go +++ b/core/chain_manager_test.go @@ -29,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 @@ -266,10 +281,7 @@ func TestChainInsertions(t *testing.T) { t.FailNow() } - var eventMux event.TypeMux - chainMan := NewChainManager(db, db, thePow(), &eventMux) - blockMan := NewBlockProcessor(db, db, nil, chainMan, &eventMux) - chainMan.SetProcessor(blockMan) + chainMan := theChainManager(db, t) const max = 2 done := make(chan bool, max) @@ -311,10 +323,9 @@ func TestChainMultipleInsertions(t *testing.T) { t.FailNow() } } - var eventMux event.TypeMux - chainMan := NewChainManager(db, db, thePow(), &eventMux) - blockMan := NewBlockProcessor(db, db, nil, 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 @@ -339,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) @@ -391,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) @@ -411,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) @@ -431,7 +441,7 @@ func TestReorgShortest(t *testing.T) { func TestInsertNonceError(t *testing.T) { for i := 1; i < 25 && !t.Failed(); i++ { db, _ := ethdb.NewMemDatabase() - genesis := GenesisBlock(db) + 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) @@ -465,6 +475,21 @@ func TestInsertNonceError(t *testing.T) { } } +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 } diff --git a/core/genesis.go b/core/genesis.go index a9b7339f3..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 -- cgit v1.2.3 From 5245bd7b20192071cac5477168dc6735abd35c6c Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 9 Jun 2015 00:41:47 +0200 Subject: core: added a test for missing nonces This test showed the logic in the queue was slightly flawed sending out transactions to its peer it couldn't even resolve itself. --- core/transaction_pool.go | 2 +- core/transaction_pool_test.go | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 27dc1b0d1..77744f8f7 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -331,7 +331,7 @@ func (pool *TxPool) checkQueue() { // current account nonce. sort.Sort(addq) for _, e := range addq { - if e.AccountNonce > curnonce+1 { + if e.AccountNonce > curnonce { break } delete(txs, e.hash) diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go index ac297d266..b8bf78f00 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -201,3 +201,26 @@ func TestTransactionDoubleNonce(t *testing.T) { 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])) + } +} -- cgit v1.2.3 From a5b977aa9042b8b5199067d435c5cbd3f6459834 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 9 Jun 2015 11:36:23 +0200 Subject: core: write accounts to statedb. Closes #1210 --- core/chain_manager.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index e87253304..a0ce20006 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -111,12 +111,13 @@ type ChainManager struct { func NewChainManager(genesis *types.Block, blockDb, stateDb common.Database, pow pow.PoW, mux *event.TypeMux) (*ChainManager, error) { bc := &ChainManager{ - blockDb: blockDb, - stateDb: stateDb, - eventMux: mux, - quit: make(chan struct{}), - cache: NewBlockCache(blockCacheLimit), - pow: pow, + blockDb: blockDb, + stateDb: 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 -- cgit v1.2.3 From 190c1b688afa1b834ba1ccd7d0b7501bfaedbd18 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 9 Jun 2015 13:22:16 +0200 Subject: core: skip genesis block for reprocess. Closes #1202 --- core/filter.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'core') 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 -- cgit v1.2.3 From 07baf66200c74a97b440a199dce7321b23aea4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 9 Jun 2015 15:23:20 +0300 Subject: core: fix data race in accessing ChainManager.td --- core/chain_manager.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index a0ce20006..2333368de 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -56,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 { @@ -178,7 +175,7 @@ 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 { @@ -204,7 +201,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) { @@ -488,8 +485,10 @@ func (self *ChainManager) GetAncestors(block *types.Block, length int) (blocks [ } func (bc *ChainManager) setTotalDifficulty(td *big.Int) { - //bc.blockDb.Put([]byte("LTD"), td.Bytes()) - bc.td = td + bc.mu.Lock() + defer bc.mu.Unlock() + + bc.td.Set(td) } func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) { @@ -626,7 +625,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { 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 -- cgit v1.2.3 From ca8cb65b73b5bdb6a30b6a45304b3c45acc66bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 9 Jun 2015 15:30:46 +0300 Subject: core: fix data race accessing ChainManager.currentBlock --- core/chain_manager.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index 2333368de..2ba81550e 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -379,8 +379,11 @@ func (self *ChainManager) ExportN(w io.Writer, first uint64, last uint64) error 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.mu.Lock() + defer bc.mu.Unlock() + bc.currentBlock = block bc.lastBlockHash = block.Hash() } -- cgit v1.2.3 From ebf2aabd254a4e765b68cdb46b18806fa7e4cb4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 9 Jun 2015 16:26:44 +0300 Subject: core: fix up a deadlock caused by double locking --- core/chain_manager.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index 2ba81550e..c69d3a10e 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -381,9 +381,6 @@ func (bc *ChainManager) insert(block *types.Block) { bc.blockDb.Put(key, block.Hash().Bytes()) bc.blockDb.Put([]byte("LastBlock"), block.Hash().Bytes()) - bc.mu.Lock() - defer bc.mu.Unlock() - bc.currentBlock = block bc.lastBlockHash = block.Hash() } @@ -488,10 +485,7 @@ func (self *ChainManager) GetAncestors(block *types.Block, length int) (blocks [ } func (bc *ChainManager) setTotalDifficulty(td *big.Int) { - bc.mu.Lock() - defer bc.mu.Unlock() - - bc.td.Set(td) + bc.td = new(big.Int).Set(td) } func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) { @@ -546,6 +540,9 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { self.wg.Add(1) defer self.wg.Done() + self.mu.Lock() + defer self.mu.Unlock() + self.chainmu.Lock() defer self.chainmu.Unlock() @@ -628,7 +625,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { 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 -- cgit v1.2.3 From ad5b5a4895f39180f329f2532e2484430c1ac480 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Tue, 9 Jun 2015 15:41:15 +0200 Subject: Pad precompiled EC recover input and add validations --- core/vm/contracts.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'core') diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 742017dd2..b5cb9ccd2 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -67,21 +67,27 @@ func ripemd160Func(in []byte) []byte { const ecRecoverInputLength = 128 func ecrecoverFunc(in []byte) []byte { - // "in" is (hash, v, r, s), each 32 bytes - // but for ecrecover we want (r, s, v) if len(in) < ecRecoverInputLength { - return nil + in = common.RightPadBytes(in, 128) } + // "in" is (hash, v, r, s), each 32 bytes + // but for ecrecover we want (r, s, v) + 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 { -- cgit v1.2.3 From 6e3b58e491c822cc6e4aa822c31c6dee034e3df9 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Tue, 9 Jun 2015 16:03:05 +0200 Subject: Remove unneeded if check on EC recover padding --- core/vm/contracts.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'core') diff --git a/core/vm/contracts.go b/core/vm/contracts.go index b5cb9ccd2..90e356b1d 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -67,9 +67,7 @@ func ripemd160Func(in []byte) []byte { const ecRecoverInputLength = 128 func ecrecoverFunc(in []byte) []byte { - if len(in) < ecRecoverInputLength { - in = common.RightPadBytes(in, 128) - } + in = common.RightPadBytes(in, 128) // "in" is (hash, v, r, s), each 32 bytes // but for ecrecover we want (r, s, v) -- cgit v1.2.3 From 7aefe123e98240ad4df440a8d1be4446744c8ca2 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 9 Jun 2015 11:58:41 +0200 Subject: core/types: add Transaction.Size --- core/types/transaction.go | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'core') diff --git a/core/types/transaction.go b/core/types/transaction.go index 3d6d31ae7..a03a6b847 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -67,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 } -- cgit v1.2.3 From 73c355591fe0279334675c555b6d614aa25b6781 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 9 Jun 2015 17:03:07 +0200 Subject: core, eth: document that result of GetTransactions is modifiable --- core/transaction_pool.go | 1 + 1 file changed, 1 insertion(+) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 77744f8f7..918e7b957 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -247,6 +247,7 @@ func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { } // GetTransactions returns all currently processable transactions. +// The returned slice may be modified by the caller. func (self *TxPool) GetTransactions() (txs types.Transactions) { self.mu.Lock() defer self.mu.Unlock() -- cgit v1.2.3 From fecf2141750fe61640870672987023fe7213901a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 9 Jun 2015 21:02:26 +0300 Subject: core: fix a lock annoyance and potential deadlock --- core/chain_manager.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index c69d3a10e..82fdbb1f1 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -376,6 +376,8 @@ func (self *ChainManager) ExportN(w io.Writer, first uint64, last uint64) error return nil } +// insert appends 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()) @@ -484,6 +486,8 @@ 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.td = new(big.Int).Set(td) } @@ -540,9 +544,6 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { self.wg.Add(1) defer self.wg.Done() - self.mu.Lock() - defer self.mu.Unlock() - self.chainmu.Lock() defer self.chainmu.Unlock() @@ -625,7 +626,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { 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 @@ -638,8 +639,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(), @@ -747,9 +750,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 } -- cgit v1.2.3 From d652a58ada207fa9a372f2fa594d5a151ed44a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 9 Jun 2015 21:13:21 +0300 Subject: core: fix a race condition accessing the gas limit --- core/chain_manager.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index 82fdbb1f1..6897c453c 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -179,7 +179,9 @@ func (self *ChainManager) Td() *big.Int { } func (self *ChainManager) GasLimit() *big.Int { - // return self.currentGasLimit + self.mu.RLock() + defer self.mu.RUnlock() + return self.currentBlock.GasLimit() } @@ -376,8 +378,8 @@ func (self *ChainManager) ExportN(w io.Writer, first uint64, last uint64) error return nil } -// insert appends injects a block into the current chain block chain. Note, this -// function assumes that the `mu` mutex is held! +// 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()) -- cgit v1.2.3 From bc6031e7bb453ec7e1f229b39e11967a8b32175a Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 9 Jun 2015 18:14:46 +0200 Subject: core, xeth: moved nonce management burden from xeth to txpool --- core/state/managed_state.go | 2 +- core/transaction_pool.go | 25 +++++++++++++++---------- core/transaction_pool_test.go | 38 +++++++++++++++++++++++++++----------- 3 files changed, 43 insertions(+), 22 deletions(-) (limited to 'core') 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/transaction_pool.go b/core/transaction_pool.go index 918e7b957..a2f970195 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -39,7 +39,7 @@ type stateFn func() *state.StateDB type TxPool struct { quit chan bool // Quiting channel currentState stateFn // The state function which will allow us to do some pre checkes - state *state.ManagedState + pendingState *state.ManagedState gasLimit func() *big.Int // The current gas limit function callback eventMux *event.TypeMux events event.Subscription @@ -57,7 +57,7 @@ func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func( eventMux: eventMux, currentState: currentStateFn, gasLimit: gasLimitFn, - state: state.ManageState(currentStateFn()), + pendingState: state.ManageState(currentStateFn()), } } @@ -76,7 +76,7 @@ func (pool *TxPool) Start() { } func (pool *TxPool) resetState() { - pool.state = state.ManageState(pool.currentState()) + 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 @@ -90,7 +90,7 @@ func (pool *TxPool) resetState() { 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.state.SetNonce(addr, tx.Nonce()) + pool.pendingState.SetNonce(addr, tx.Nonce()) } } @@ -110,7 +110,7 @@ func (pool *TxPool) State() *state.ManagedState { pool.mu.RLock() defer pool.mu.RUnlock() - return pool.state + return pool.pendingState } // validateTx checks whether a transaction is valid according @@ -302,7 +302,9 @@ func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Trans if _, ok := pool.pending[hash]; !ok { pool.pending[hash] = tx - pool.state.SetNonce(addr, tx.AccountNonce) + // 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. @@ -312,14 +314,17 @@ func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Trans // checkQueue moves transactions that have become processable to main pool. func (pool *TxPool) checkQueue() { - state := pool.state + state := pool.pendingState var addq txQueue for address, txs := range pool.queue { - curnonce := state.GetNonce(address) + // 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 < curnonce { + if tx.AccountNonce < trueNonce { // Drop queued transactions whose nonce is lower than // the account nonce because they have been processed. delete(txs, hash) @@ -332,7 +337,7 @@ func (pool *TxPool) checkQueue() { // current account nonce. sort.Sort(addq) for _, e := range addq { - if e.AccountNonce > curnonce { + if e.AccountNonce > guessedNonce { break } delete(txs, e.hash) diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go index b8bf78f00..b763c196d 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -37,21 +37,21 @@ func TestInvalidTransactions(t *testing.T) { } from, _ := tx.From() - pool.state.AddBalance(from, big.NewInt(1)) + pool.currentState().AddBalance(from, big.NewInt(1)) err = pool.Add(tx) if err != ErrInsufficientFunds { t.Error("expected", ErrInsufficientFunds) } balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(tx.Gas(), tx.GasPrice())) - pool.state.AddBalance(from, balance) + pool.currentState().AddBalance(from, balance) err = pool.Add(tx) if err != ErrIntrinsicGas { t.Error("expected", ErrIntrinsicGas, "got", err) } - pool.state.SetNonce(from, 1) - pool.state.AddBalance(from, big.NewInt(0xffffffffffffff)) + pool.currentState().SetNonce(from, 1) + pool.currentState().AddBalance(from, big.NewInt(0xffffffffffffff)) tx.GasLimit = big.NewInt(100000) tx.Price = big.NewInt(1) tx.SignECDSA(key) @@ -67,7 +67,7 @@ func TestTransactionQueue(t *testing.T) { tx := transaction() tx.SignECDSA(key) from, _ := tx.From() - pool.state.AddBalance(from, big.NewInt(1)) + pool.currentState().AddBalance(from, big.NewInt(1)) pool.queueTx(tx.Hash(), tx) pool.checkQueue() @@ -79,7 +79,7 @@ func TestTransactionQueue(t *testing.T) { tx.SetNonce(1) tx.SignECDSA(key) from, _ = tx.From() - pool.state.SetNonce(from, 2) + pool.currentState().SetNonce(from, 2) pool.queueTx(tx.Hash(), tx) pool.checkQueue() if _, ok := pool.pending[tx.Hash()]; ok { @@ -117,7 +117,7 @@ func TestRemoveTx(t *testing.T) { tx := transaction() tx.SignECDSA(key) from, _ := tx.From() - pool.state.AddBalance(from, big.NewInt(1)) + pool.currentState().AddBalance(from, big.NewInt(1)) pool.queueTx(tx.Hash(), tx) pool.addTx(tx.Hash(), from, tx) if len(pool.queue) != 1 { @@ -146,7 +146,7 @@ func TestNegativeValue(t *testing.T) { tx.Value().Set(big.NewInt(-1)) tx.SignECDSA(key) from, _ := tx.From() - pool.state.AddBalance(from, big.NewInt(1)) + pool.currentState().AddBalance(from, big.NewInt(1)) err := pool.Add(tx) if err != ErrNegativeValue { t.Error("expected", ErrNegativeValue, "got", err) @@ -156,7 +156,15 @@ func TestNegativeValue(t *testing.T) { func TestTransactionChainFork(t *testing.T) { pool, key := setupTxPool() addr := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) + 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) @@ -168,7 +176,7 @@ func TestTransactionChainFork(t *testing.T) { pool.RemoveTransactions([]*types.Transaction{tx}) // reset the pool's internal state - pool.resetState() + resetState() err = pool.add(tx) if err != nil { t.Error("didn't expect error", err) @@ -178,7 +186,15 @@ func TestTransactionChainFork(t *testing.T) { func TestTransactionDoubleNonce(t *testing.T) { pool, key := setupTxPool() addr := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) + 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) -- cgit v1.2.3