|
|
@ -6,7 +6,6 @@ import ( |
|
|
|
"math/big" |
|
|
|
"math/big" |
|
|
|
"sort" |
|
|
|
"sort" |
|
|
|
"sync" |
|
|
|
"sync" |
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common" |
|
|
|
"github.com/ethereum/go-ethereum/common" |
|
|
|
"github.com/ethereum/go-ethereum/core/state" |
|
|
|
"github.com/ethereum/go-ethereum/core/state" |
|
|
@ -14,10 +13,10 @@ import ( |
|
|
|
"github.com/ethereum/go-ethereum/event" |
|
|
|
"github.com/ethereum/go-ethereum/event" |
|
|
|
"github.com/ethereum/go-ethereum/logger" |
|
|
|
"github.com/ethereum/go-ethereum/logger" |
|
|
|
"github.com/ethereum/go-ethereum/logger/glog" |
|
|
|
"github.com/ethereum/go-ethereum/logger/glog" |
|
|
|
"gopkg.in/fatih/set.v0" |
|
|
|
|
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
var ( |
|
|
|
var ( |
|
|
|
|
|
|
|
// Transaction Pool Errors
|
|
|
|
ErrInvalidSender = errors.New("Invalid sender") |
|
|
|
ErrInvalidSender = errors.New("Invalid sender") |
|
|
|
ErrNonce = errors.New("Nonce too low") |
|
|
|
ErrNonce = errors.New("Nonce too low") |
|
|
|
ErrBalance = errors.New("Insufficient balance") |
|
|
|
ErrBalance = errors.New("Insufficient balance") |
|
|
@ -28,112 +27,141 @@ var ( |
|
|
|
ErrNegativeValue = errors.New("Negative value") |
|
|
|
ErrNegativeValue = errors.New("Negative value") |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
const txPoolQueueSize = 50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type TxPoolHook chan *types.Transaction |
|
|
|
|
|
|
|
type TxMsg struct{ Tx *types.Transaction } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type stateFn func() *state.StateDB |
|
|
|
type stateFn func() *state.StateDB |
|
|
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
// TxPool contains all currently known transactions. Transactions
|
|
|
|
minGasPrice = 1000000 |
|
|
|
// enter the pool when they are received from the network or submitted
|
|
|
|
) |
|
|
|
// locally. They exit the pool when they are included in the blockchain.
|
|
|
|
|
|
|
|
//
|
|
|
|
type TxProcessor interface { |
|
|
|
// The pool separates processable transactions (which can be applied to the
|
|
|
|
ProcessTransaction(tx *types.Transaction) |
|
|
|
// current state) and future transactions. Transactions move between those
|
|
|
|
} |
|
|
|
// two states over time as they are received and processed.
|
|
|
|
|
|
|
|
|
|
|
|
// 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 { |
|
|
|
type TxPool struct { |
|
|
|
mu sync.RWMutex |
|
|
|
quit chan bool // Quiting channel
|
|
|
|
// Queueing channel for reading and writing incoming
|
|
|
|
currentState stateFn // The state function which will allow us to do some pre checkes
|
|
|
|
// transactions to
|
|
|
|
state *state.ManagedState |
|
|
|
queueChan chan *types.Transaction |
|
|
|
gasLimit func() *big.Int // The current gas limit function callback
|
|
|
|
// Quiting channel
|
|
|
|
|
|
|
|
quit chan bool |
|
|
|
|
|
|
|
// The state function which will allow us to do some pre checkes
|
|
|
|
|
|
|
|
currentState stateFn |
|
|
|
|
|
|
|
// The current gas limit function callback
|
|
|
|
|
|
|
|
gasLimit func() *big.Int |
|
|
|
|
|
|
|
// The actual pool
|
|
|
|
|
|
|
|
txs map[common.Hash]*types.Transaction |
|
|
|
|
|
|
|
invalidHashes *set.Set |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
queue map[common.Address]types.Transactions |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
subscribers []chan TxMsg |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eventMux *event.TypeMux |
|
|
|
eventMux *event.TypeMux |
|
|
|
|
|
|
|
events event.Subscription |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mu sync.RWMutex |
|
|
|
|
|
|
|
pending map[common.Hash]*types.Transaction // processable transactions
|
|
|
|
|
|
|
|
queue map[common.Address]map[common.Hash]*types.Transaction |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { |
|
|
|
func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { |
|
|
|
txPool := &TxPool{ |
|
|
|
return &TxPool{ |
|
|
|
txs: make(map[common.Hash]*types.Transaction), |
|
|
|
pending: 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), |
|
|
|
quit: make(chan bool), |
|
|
|
eventMux: eventMux, |
|
|
|
eventMux: eventMux, |
|
|
|
invalidHashes: set.New(), |
|
|
|
|
|
|
|
currentState: currentStateFn, |
|
|
|
currentState: currentStateFn, |
|
|
|
gasLimit: gasLimitFn, |
|
|
|
gasLimit: gasLimitFn, |
|
|
|
|
|
|
|
state: state.ManageState(currentStateFn()), |
|
|
|
} |
|
|
|
} |
|
|
|
return txPool |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pool *TxPool) Start() { |
|
|
|
func (pool *TxPool) Start() { |
|
|
|
// Queue timer will tick so we can attempt to move items from the queue to the
|
|
|
|
// Track chain events. When a chain events occurs (new chain canon block)
|
|
|
|
// main transaction pool.
|
|
|
|
// we need to know the new state. The new state will help us determine
|
|
|
|
queueTimer := time.NewTicker(300 * time.Millisecond) |
|
|
|
// the nonces in the managed state
|
|
|
|
// Removal timer will tick and attempt to remove bad transactions (account.nonce>tx.nonce)
|
|
|
|
pool.events = pool.eventMux.Subscribe(ChainEvent{}) |
|
|
|
removalTimer := time.NewTicker(1 * time.Second) |
|
|
|
for _ = range pool.events.Chan() { |
|
|
|
done: |
|
|
|
pool.mu.Lock() |
|
|
|
for { |
|
|
|
|
|
|
|
select { |
|
|
|
pool.resetState() |
|
|
|
case <-queueTimer.C: |
|
|
|
|
|
|
|
pool.checkQueue() |
|
|
|
pool.mu.Unlock() |
|
|
|
case <-removalTimer.C: |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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() |
|
|
|
pool.validatePool() |
|
|
|
case <-pool.quit: |
|
|
|
|
|
|
|
break done |
|
|
|
// 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) ValidateTransaction(tx *types.Transaction) error { |
|
|
|
func (pool *TxPool) Stop() { |
|
|
|
|
|
|
|
pool.pending = make(map[common.Hash]*types.Transaction) |
|
|
|
|
|
|
|
close(pool.quit) |
|
|
|
|
|
|
|
pool.events.Unsubscribe() |
|
|
|
|
|
|
|
glog.V(logger.Info).Infoln("TX Pool stopped") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pool *TxPool) 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 { |
|
|
|
// Validate sender
|
|
|
|
// Validate sender
|
|
|
|
var ( |
|
|
|
var ( |
|
|
|
from common.Address |
|
|
|
from common.Address |
|
|
|
err 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 { |
|
|
|
if from, err = tx.From(); err != nil { |
|
|
|
return ErrInvalidSender |
|
|
|
return ErrInvalidSender |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure the account exist. Non existant accounts
|
|
|
|
|
|
|
|
// haven't got funds and well therefor never pass.
|
|
|
|
if !pool.currentState().HasAccount(from) { |
|
|
|
if !pool.currentState().HasAccount(from) { |
|
|
|
return ErrNonExistentAccount |
|
|
|
return ErrNonExistentAccount |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check the transaction doesn't exceed the current
|
|
|
|
|
|
|
|
// block limit gas.
|
|
|
|
if pool.gasLimit().Cmp(tx.GasLimit) < 0 { |
|
|
|
if pool.gasLimit().Cmp(tx.GasLimit) < 0 { |
|
|
|
return ErrGasLimit |
|
|
|
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 { |
|
|
|
if tx.Amount.Cmp(common.Big0) < 0 { |
|
|
|
return ErrNegativeValue |
|
|
|
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 := new(big.Int).Mul(tx.Price, tx.GasLimit) |
|
|
|
total.Add(total, tx.Value()) |
|
|
|
total.Add(total, tx.Value()) |
|
|
|
if pool.currentState().GetBalance(from).Cmp(total) < 0 { |
|
|
|
if pool.currentState().GetBalance(from).Cmp(total) < 0 { |
|
|
|
return ErrInsufficientFunds |
|
|
|
return ErrInsufficientFunds |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Should supply enough intrinsic gas
|
|
|
|
if tx.GasLimit.Cmp(IntrinsicGas(tx)) < 0 { |
|
|
|
if tx.GasLimit.Cmp(IntrinsicGas(tx)) < 0 { |
|
|
|
return ErrIntrinsicGas |
|
|
|
return ErrIntrinsicGas |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Last but not least check for nonce errors (intensive
|
|
|
|
|
|
|
|
// operation, saved for last)
|
|
|
|
if pool.currentState().GetNonce(from) > tx.Nonce() { |
|
|
|
if pool.currentState().GetNonce(from) > tx.Nonce() { |
|
|
|
return ErrNonce |
|
|
|
return ErrNonce |
|
|
|
} |
|
|
|
} |
|
|
@ -150,16 +178,16 @@ func (self *TxPool) add(tx *types.Transaction) error { |
|
|
|
return fmt.Errorf("Invalid transaction (%x)", hash[:4]) |
|
|
|
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]) |
|
|
|
return fmt.Errorf("Known transaction (%x)", hash[:4]) |
|
|
|
} |
|
|
|
} |
|
|
|
err := self.ValidateTransaction(tx) |
|
|
|
err := self.validateTx(tx) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
self.queueTx(hash, tx) |
|
|
|
|
|
|
|
|
|
|
|
self.queueTx(tx) |
|
|
|
if glog.V(logger.Debug) { |
|
|
|
|
|
|
|
|
|
|
|
var toname string |
|
|
|
var toname string |
|
|
|
if to := tx.To(); to != nil { |
|
|
|
if to := tx.To(); to != nil { |
|
|
|
toname = common.Bytes2Hex(to[:4]) |
|
|
|
toname = common.Bytes2Hex(to[:4]) |
|
|
@ -170,18 +198,16 @@ func (self *TxPool) add(tx *types.Transaction) error { |
|
|
|
// verified in ValidateTransaction.
|
|
|
|
// verified in ValidateTransaction.
|
|
|
|
f, _ := tx.From() |
|
|
|
f, _ := tx.From() |
|
|
|
from := common.Bytes2Hex(f[:4]) |
|
|
|
from := common.Bytes2Hex(f[:4]) |
|
|
|
|
|
|
|
glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, hash) |
|
|
|
if glog.V(logger.Debug) { |
|
|
|
|
|
|
|
glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, tx.Hash()) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
// check and validate the queueue
|
|
|
|
} |
|
|
|
self.checkQueue() |
|
|
|
|
|
|
|
|
|
|
|
func (self *TxPool) Size() int { |
|
|
|
return nil |
|
|
|
return len(self.txs) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add queues a single transaction in the pool if it is valid.
|
|
|
|
func (self *TxPool) Add(tx *types.Transaction) error { |
|
|
|
func (self *TxPool) Add(tx *types.Transaction) error { |
|
|
|
self.mu.Lock() |
|
|
|
self.mu.Lock() |
|
|
|
defer self.mu.Unlock() |
|
|
|
defer self.mu.Unlock() |
|
|
@ -189,6 +215,7 @@ func (self *TxPool) Add(tx *types.Transaction) error { |
|
|
|
return self.add(tx) |
|
|
|
return self.add(tx) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// AddTransactions attempts to queue all valid transactions in txs.
|
|
|
|
func (self *TxPool) AddTransactions(txs []*types.Transaction) { |
|
|
|
func (self *TxPool) AddTransactions(txs []*types.Transaction) { |
|
|
|
self.mu.Lock() |
|
|
|
self.mu.Lock() |
|
|
|
defer self.mu.Unlock() |
|
|
|
defer self.mu.Unlock() |
|
|
@ -203,81 +230,78 @@ func (self *TxPool) AddTransactions(txs []*types.Transaction) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// GetTransaction allows you to check the pending and queued transaction in the
|
|
|
|
// GetTransaction returns a transaction if it is contained in the pool
|
|
|
|
// transaction pool.
|
|
|
|
// and nil otherwise.
|
|
|
|
// It has two stategies, first check the pool (map) then check the queue
|
|
|
|
|
|
|
|
func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { |
|
|
|
func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { |
|
|
|
// check the txs first
|
|
|
|
// check the txs first
|
|
|
|
if tx, ok := tp.txs[hash]; ok { |
|
|
|
if tx, ok := tp.pending[hash]; ok { |
|
|
|
return tx |
|
|
|
return tx |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// check queue
|
|
|
|
// check queue
|
|
|
|
for _, txs := range tp.queue { |
|
|
|
for _, txs := range tp.queue { |
|
|
|
for _, tx := range txs { |
|
|
|
if tx, ok := txs[hash]; ok { |
|
|
|
if tx.Hash() == hash { |
|
|
|
|
|
|
|
return tx |
|
|
|
return tx |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// GetTransactions returns all currently processable transactions.
|
|
|
|
func (self *TxPool) GetTransactions() (txs types.Transactions) { |
|
|
|
func (self *TxPool) GetTransactions() (txs types.Transactions) { |
|
|
|
self.mu.RLock() |
|
|
|
self.mu.Lock() |
|
|
|
defer self.mu.RUnlock() |
|
|
|
defer self.mu.Unlock() |
|
|
|
|
|
|
|
|
|
|
|
txs = make(types.Transactions, self.Size()) |
|
|
|
// check queue first
|
|
|
|
|
|
|
|
self.checkQueue() |
|
|
|
|
|
|
|
// invalidate any txs
|
|
|
|
|
|
|
|
self.validatePool() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
txs = make(types.Transactions, len(self.pending)) |
|
|
|
i := 0 |
|
|
|
i := 0 |
|
|
|
for _, tx := range self.txs { |
|
|
|
for _, tx := range self.pending { |
|
|
|
txs[i] = tx |
|
|
|
txs[i] = tx |
|
|
|
i++ |
|
|
|
i++ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return txs |
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// GetQueuedTransactions returns all non-processable transactions.
|
|
|
|
func (self *TxPool) GetQueuedTransactions() types.Transactions { |
|
|
|
func (self *TxPool) GetQueuedTransactions() types.Transactions { |
|
|
|
self.mu.RLock() |
|
|
|
self.mu.RLock() |
|
|
|
defer self.mu.RUnlock() |
|
|
|
defer self.mu.RUnlock() |
|
|
|
|
|
|
|
|
|
|
|
var txs types.Transactions |
|
|
|
var ret types.Transactions |
|
|
|
for _, ts := range self.queue { |
|
|
|
for _, txs := range self.queue { |
|
|
|
txs = append(txs, ts...) |
|
|
|
for _, tx := range txs { |
|
|
|
|
|
|
|
ret = append(ret, tx) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
return txs |
|
|
|
sort.Sort(types.TxByNonce{ret}) |
|
|
|
|
|
|
|
return ret |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// RemoveTransactions removes all given transactions from the pool.
|
|
|
|
func (self *TxPool) RemoveTransactions(txs types.Transactions) { |
|
|
|
func (self *TxPool) RemoveTransactions(txs types.Transactions) { |
|
|
|
self.mu.Lock() |
|
|
|
self.mu.Lock() |
|
|
|
defer self.mu.Unlock() |
|
|
|
defer self.mu.Unlock() |
|
|
|
|
|
|
|
|
|
|
|
for _, tx := range txs { |
|
|
|
for _, tx := range txs { |
|
|
|
self.removeTx(tx.Hash()) |
|
|
|
self.removeTx(tx.Hash()) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pool *TxPool) Flush() { |
|
|
|
func (self *TxPool) queueTx(hash common.Hash, tx *types.Transaction) { |
|
|
|
pool.txs = make(map[common.Hash]*types.Transaction) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pool *TxPool) Stop() { |
|
|
|
|
|
|
|
pool.Flush() |
|
|
|
|
|
|
|
close(pool.quit) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
glog.V(logger.Info).Infoln("TX Pool stopped") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (self *TxPool) queueTx(tx *types.Transaction) { |
|
|
|
|
|
|
|
from, _ := tx.From() // already validated
|
|
|
|
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) { |
|
|
|
func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Transaction) { |
|
|
|
if _, ok := pool.txs[tx.Hash()]; !ok { |
|
|
|
if _, ok := pool.pending[hash]; !ok { |
|
|
|
pool.txs[tx.Hash()] = tx |
|
|
|
pool.pending[hash] = tx |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pool.state.SetNonce(addr, tx.AccountNonce) |
|
|
|
// Notify the subscribers. This event is posted in a goroutine
|
|
|
|
// Notify the subscribers. This event is posted in a goroutine
|
|
|
|
// because it's possible that somewhere during the post "Remove transaction"
|
|
|
|
// 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.
|
|
|
|
// gets called which will then wait for the global tx pool lock and deadlock.
|
|
|
@ -285,42 +309,36 @@ func (pool *TxPool) addTx(tx *types.Transaction) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// check queue will attempt to insert
|
|
|
|
// checkQueue moves transactions that have become processable to main pool.
|
|
|
|
func (pool *TxPool) checkQueue() { |
|
|
|
func (pool *TxPool) checkQueue() { |
|
|
|
pool.mu.Lock() |
|
|
|
state := pool.state |
|
|
|
defer pool.mu.Unlock() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
statedb := pool.currentState() |
|
|
|
var addq txQueue |
|
|
|
for address, txs := range pool.queue { |
|
|
|
for address, txs := range pool.queue { |
|
|
|
sort.Sort(types.TxByNonce{txs}) |
|
|
|
curnonce := state.GetNonce(address) |
|
|
|
|
|
|
|
addq := addq[:0] |
|
|
|
var ( |
|
|
|
for hash, tx := range txs { |
|
|
|
nonce = statedb.GetNonce(address) |
|
|
|
if tx.AccountNonce < curnonce { |
|
|
|
start int |
|
|
|
// Drop queued transactions whose nonce is lower than
|
|
|
|
) |
|
|
|
// the account nonce because they have been processed.
|
|
|
|
// Clean up the transactions first and determine the start of the nonces
|
|
|
|
delete(txs, hash) |
|
|
|
for _, tx := range txs { |
|
|
|
} else { |
|
|
|
if tx.Nonce() >= nonce { |
|
|
|
// Collect the remaining transactions for the next pass.
|
|
|
|
break |
|
|
|
addq = append(addq, txQueueEntry{hash, address, tx}) |
|
|
|
} |
|
|
|
} |
|
|
|
start++ |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
pool.queue[address] = txs[start:] |
|
|
|
// Find the next consecutive nonce range starting at the
|
|
|
|
|
|
|
|
// current account nonce.
|
|
|
|
// expected nonce
|
|
|
|
sort.Sort(addq) |
|
|
|
enonce := nonce |
|
|
|
for _, e := range addq { |
|
|
|
for _, tx := range pool.queue[address] { |
|
|
|
if e.AccountNonce > curnonce+1 { |
|
|
|
// 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() { |
|
|
|
|
|
|
|
break |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
enonce++ |
|
|
|
delete(txs, e.hash) |
|
|
|
|
|
|
|
pool.addTx(e.hash, address, e.Transaction) |
|
|
|
pool.addTx(tx) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
// delete the entire queue entry if it's empty. There's no need to keep it
|
|
|
|
// Delete the entire queue entry if it became empty.
|
|
|
|
if len(pool.queue[address]) == 0 { |
|
|
|
if len(txs) == 0 { |
|
|
|
delete(pool.queue, address) |
|
|
|
delete(pool.queue, address) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -328,36 +346,41 @@ func (pool *TxPool) checkQueue() { |
|
|
|
|
|
|
|
|
|
|
|
func (pool *TxPool) removeTx(hash common.Hash) { |
|
|
|
func (pool *TxPool) removeTx(hash common.Hash) { |
|
|
|
// delete from pending pool
|
|
|
|
// delete from pending pool
|
|
|
|
delete(pool.txs, hash) |
|
|
|
delete(pool.pending, hash) |
|
|
|
|
|
|
|
|
|
|
|
// delete from queue
|
|
|
|
// delete from queue
|
|
|
|
out: |
|
|
|
|
|
|
|
for address, txs := range pool.queue { |
|
|
|
for address, txs := range pool.queue { |
|
|
|
for i, tx := range txs { |
|
|
|
if _, ok := txs[hash]; ok { |
|
|
|
if tx.Hash() == hash { |
|
|
|
|
|
|
|
if len(txs) == 1 { |
|
|
|
if len(txs) == 1 { |
|
|
|
// if only one tx, remove entire address entry
|
|
|
|
// if only one tx, remove entire address entry.
|
|
|
|
delete(pool.queue, address) |
|
|
|
delete(pool.queue, address) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
pool.queue[address][len(txs)-1], pool.queue[address] = nil, append(txs[:i], txs[i+1:]...) |
|
|
|
delete(txs, hash) |
|
|
|
} |
|
|
|
|
|
|
|
break out |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// validatePool removes invalid and processed transactions from the main pool.
|
|
|
|
func (pool *TxPool) validatePool() { |
|
|
|
func (pool *TxPool) validatePool() { |
|
|
|
pool.mu.Lock() |
|
|
|
for hash, tx := range pool.pending { |
|
|
|
defer pool.mu.Unlock() |
|
|
|
if err := pool.validateTx(tx); err != nil { |
|
|
|
|
|
|
|
if glog.V(logger.Core) { |
|
|
|
for hash, tx := range pool.txs { |
|
|
|
|
|
|
|
if err := pool.ValidateTransaction(tx); err != nil { |
|
|
|
|
|
|
|
if glog.V(logger.Info) { |
|
|
|
|
|
|
|
glog.Infof("removed tx (%x) from pool: %v\n", hash[:4], err) |
|
|
|
glog.Infof("removed tx (%x) from pool: %v\n", hash[:4], err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
delete(pool.pending, hash) |
|
|
|
pool.removeTx(hash) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type txQueue []txQueueEntry |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type txQueueEntry struct { |
|
|
|
|
|
|
|
hash common.Hash |
|
|
|
|
|
|
|
addr common.Address |
|
|
|
|
|
|
|
*types.Transaction |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (q txQueue) Len() int { return len(q) } |
|
|
|
|
|
|
|
func (q txQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] } |
|
|
|
|
|
|
|
func (q txQueue) Less(i, j int) bool { return q[i].AccountNonce < q[j].AccountNonce } |
|
|
|