@ -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" )
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 . Debug ln( err )
glog . V ( logger . Debug ) . Info ln( err )
} else {
h := tx . Hash ( )
txplogger . Debug f( "tx %x\n" , h [ : 4 ] )
glog . V ( logger . Debug ) . Info f( "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 ( "S topped" )
glog . V ( logger . Info ) . Infoln ( "TX Pool s topped" )
}