From 6184781b49242b8029522612ad94cd45b508abc1 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 8 Apr 2015 20:47:32 +0200 Subject: Improved transaction pool The transaction pool will now some easily be able to pre determine the validity of a transaction by checking the following: * Account existst * gas limit higher than the instrinsic gas * enough funds to pay upfront costs * nonce check --- core/transaction_pool.go | 87 +++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 42 deletions(-) (limited to 'core/transaction_pool.go') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 58ea518d6..bfb54e24d 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -3,19 +3,24 @@ package core import ( "errors" "fmt" + "math/big" "sync" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" "gopkg.in/fatih/set.v0" ) var ( - txplogger = logger.NewLogger("TXP") - - ErrInvalidSender = errors.New("Invalid sender") + ErrInvalidSender = errors.New("Invalid sender") + ErrImpossibleNonce = errors.New("Impossible nonce") + ErrNonExistentAccount = errors.New("Account does not exist") + ErrInsufficientFunds = errors.New("Insufficient funds") + ErrIntrinsicGas = errors.New("Intrinsic gas too low") ) const txPoolQueueSize = 50 @@ -41,52 +46,62 @@ type TxPool struct { queueChan chan *types.Transaction // Quiting channel quit chan bool + // The state function which will allow us to do some pre checkes + currentState func() *state.StateDB // The actual pool - //pool *list.List txs map[common.Hash]*types.Transaction invalidHashes *set.Set - SecondaryProcessor TxProcessor - subscribers []chan TxMsg eventMux *event.TypeMux } -func NewTxPool(eventMux *event.TypeMux) *TxPool { +func NewTxPool(eventMux *event.TypeMux, currentStateFn func() *state.StateDB) *TxPool { return &TxPool{ txs: make(map[common.Hash]*types.Transaction), queueChan: make(chan *types.Transaction, txPoolQueueSize), quit: make(chan bool), eventMux: eventMux, invalidHashes: set.New(), + currentState: currentStateFn, } } func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error { // Validate sender - if _, err := tx.From(); err != nil { + var ( + from common.Address + err error + ) + + if from, err = tx.From(); err != nil { return ErrInvalidSender } + // Validate curve param v, _, _ := tx.Curve() if v > 28 || v < 27 { return fmt.Errorf("tx.v != (28 || 27) => %v", v) } - return nil - /* XXX this kind of validation needs to happen elsewhere in the gui when sending txs. - Other clients should do their own validation. Value transfer could throw error - but doesn't necessarily invalidate the tx. Gas can still be payed for and miner - can still be rewarded for their inclusion and processing. - sender := pool.stateQuery.GetAccount(senderAddr) - totAmount := new(big.Int).Set(tx.Value()) - // Make sure there's enough in the sender's account. Having insufficient - // funds won't invalidate this transaction but simple ignores it. - if sender.Balance().Cmp(totAmount) < 0 { - return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.From()) + if !pool.currentState().HasAccount(from) { + return ErrNonExistentAccount } - */ + + if pool.currentState().GetBalance(from).Cmp(new(big.Int).Mul(tx.Price, tx.GasLimit)) < 0 { + return ErrInsufficientFunds + } + + if tx.GasLimit.Cmp(IntrinsicGas(tx)) < 0 { + return ErrIntrinsicGas + } + + if pool.currentState().GetNonce(from) > tx.Nonce() { + return ErrImpossibleNonce + } + + return nil } func (self *TxPool) addTx(tx *types.Transaction) { @@ -96,10 +111,12 @@ func (self *TxPool) addTx(tx *types.Transaction) { func (self *TxPool) add(tx *types.Transaction) error { hash := tx.Hash() + /* XXX I'm unsure about this. This is extremely dangerous and may result + in total black listing of certain transactions if self.invalidHashes.Has(hash) { return fmt.Errorf("Invalid transaction (%x)", hash[:4]) } - + */ if self.txs[hash] != nil { return fmt.Errorf("Known transaction (%x)", hash[:4]) } @@ -121,7 +138,10 @@ func (self *TxPool) add(tx *types.Transaction) error { // verified in ValidateTransaction. f, _ := tx.From() from := common.Bytes2Hex(f[:4]) - txplogger.Debugf("(t) %x => %s (%v) %x\n", from, toname, tx.Value, tx.Hash()) + + if glog.V(logger.Debug) { + glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, tx.Hash()) + } // Notify the subscribers go self.eventMux.Post(TxPreEvent{tx}) @@ -146,10 +166,10 @@ func (self *TxPool) AddTransactions(txs []*types.Transaction) { for _, tx := range txs { if err := self.add(tx); err != nil { - txplogger.Debugln(err) + glog.V(logger.Debug).Infoln(err) } else { h := tx.Hash() - txplogger.Debugf("tx %x\n", h[:4]) + glog.V(logger.Debug).Infof("tx %x\n", h[:4]) } } } @@ -168,23 +188,6 @@ func (self *TxPool) GetTransactions() (txs types.Transactions) { return } -func (pool *TxPool) RemoveInvalid(query StateQuery) { - pool.mu.Lock() - - var removedTxs types.Transactions - for _, tx := range pool.txs { - from, _ := tx.From() - sender := query.GetAccount(from[:]) - err := pool.ValidateTransaction(tx) - if err != nil || sender.Nonce() >= tx.Nonce() { - removedTxs = append(removedTxs, tx) - } - } - pool.mu.Unlock() - - pool.RemoveSet(removedTxs) -} - func (self *TxPool) RemoveSet(txs types.Transactions) { self.mu.Lock() defer self.mu.Unlock() @@ -214,5 +217,5 @@ func (pool *TxPool) Start() { func (pool *TxPool) Stop() { pool.Flush() - txplogger.Infoln("Stopped") + glog.V(logger.Info).Infoln("TX Pool stopped") } -- cgit v1.2.3