github.com/karalabe/go-ethereum@v0.8.5/core/transaction_pool.go (about)

     1  package core
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync"
     7  
     8  	"github.com/ethereum/go-ethereum/core/types"
     9  	"github.com/ethereum/go-ethereum/ethutil"
    10  	"github.com/ethereum/go-ethereum/event"
    11  	"github.com/ethereum/go-ethereum/logger"
    12  )
    13  
    14  var (
    15  	txplogger = logger.NewLogger("TXP")
    16  
    17  	ErrInvalidSender = errors.New("Invalid sender")
    18  )
    19  
    20  const txPoolQueueSize = 50
    21  
    22  type TxPoolHook chan *types.Transaction
    23  type TxMsg struct {
    24  	Tx *types.Transaction
    25  }
    26  
    27  const (
    28  	minGasPrice = 1000000
    29  )
    30  
    31  type TxProcessor interface {
    32  	ProcessTransaction(tx *types.Transaction)
    33  }
    34  
    35  // The tx pool a thread safe transaction pool handler. In order to
    36  // guarantee a non blocking pool we use a queue channel which can be
    37  // independently read without needing access to the actual pool.
    38  type TxPool struct {
    39  	mu sync.RWMutex
    40  	// Queueing channel for reading and writing incoming
    41  	// transactions to
    42  	queueChan chan *types.Transaction
    43  	// Quiting channel
    44  	quit chan bool
    45  	// The actual pool
    46  	//pool *list.List
    47  	txs map[string]*types.Transaction
    48  
    49  	SecondaryProcessor TxProcessor
    50  
    51  	subscribers []chan TxMsg
    52  
    53  	eventMux *event.TypeMux
    54  }
    55  
    56  func NewTxPool(eventMux *event.TypeMux) *TxPool {
    57  	return &TxPool{
    58  		txs:       make(map[string]*types.Transaction),
    59  		queueChan: make(chan *types.Transaction, txPoolQueueSize),
    60  		quit:      make(chan bool),
    61  		eventMux:  eventMux,
    62  	}
    63  }
    64  
    65  func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
    66  	if len(tx.To()) != 0 && len(tx.To()) != 20 {
    67  		return fmt.Errorf("Invalid recipient. len = %d", len(tx.To()))
    68  	}
    69  
    70  	// Validate curve param
    71  	v, _, _ := tx.Curve()
    72  	if v > 28 || v < 27 {
    73  		return fmt.Errorf("tx.v != (28 || 27) => %v", v)
    74  	}
    75  
    76  	// Validate sender address
    77  	senderAddr := tx.From()
    78  	if senderAddr == nil || len(senderAddr) != 20 {
    79  		return ErrInvalidSender
    80  	}
    81  
    82  	/* XXX this kind of validation needs to happen elsewhere in the gui when sending txs.
    83  	   Other clients should do their own validation. Value transfer could throw error
    84  	   but doesn't necessarily invalidate the tx. Gas can still be payed for and miner
    85  	   can still be rewarded for their inclusion and processing.
    86  	sender := pool.stateQuery.GetAccount(senderAddr)
    87  	totAmount := new(big.Int).Set(tx.Value())
    88  	// Make sure there's enough in the sender's account. Having insufficient
    89  	// funds won't invalidate this transaction but simple ignores it.
    90  	if sender.Balance().Cmp(totAmount) < 0 {
    91  		return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.From())
    92  	}
    93  	*/
    94  
    95  	return nil
    96  }
    97  
    98  func (self *TxPool) addTx(tx *types.Transaction) {
    99  	self.txs[string(tx.Hash())] = tx
   100  }
   101  
   102  func (self *TxPool) add(tx *types.Transaction) error {
   103  	if self.txs[string(tx.Hash())] != nil {
   104  		return fmt.Errorf("Known transaction (%x)", tx.Hash()[0:4])
   105  	}
   106  
   107  	err := self.ValidateTransaction(tx)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	self.addTx(tx)
   113  
   114  	var to string
   115  	if len(tx.To()) > 0 {
   116  		to = ethutil.Bytes2Hex(tx.To()[:4])
   117  	} else {
   118  		to = "[NEW_CONTRACT]"
   119  	}
   120  
   121  	txplogger.Debugf("(t) %x => %s (%v) %x\n", tx.From()[:4], to, tx.Value, tx.Hash())
   122  
   123  	// Notify the subscribers
   124  	go self.eventMux.Post(TxPreEvent{tx})
   125  
   126  	return nil
   127  }
   128  
   129  func (self *TxPool) Size() int {
   130  	return len(self.txs)
   131  }
   132  
   133  func (self *TxPool) Add(tx *types.Transaction) error {
   134  	self.mu.Lock()
   135  	defer self.mu.Unlock()
   136  	return self.add(tx)
   137  }
   138  func (self *TxPool) AddTransactions(txs []*types.Transaction) {
   139  	self.mu.Lock()
   140  	defer self.mu.Unlock()
   141  
   142  	for _, tx := range txs {
   143  		if err := self.add(tx); err != nil {
   144  			txplogger.Debugln(err)
   145  		} else {
   146  			txplogger.Debugf("tx %x\n", tx.Hash()[0:4])
   147  		}
   148  	}
   149  }
   150  
   151  func (self *TxPool) GetTransactions() (txs types.Transactions) {
   152  	self.mu.RLock()
   153  	defer self.mu.RUnlock()
   154  
   155  	txs = make(types.Transactions, self.Size())
   156  	i := 0
   157  	for _, tx := range self.txs {
   158  		txs[i] = tx
   159  		i++
   160  	}
   161  
   162  	return
   163  }
   164  
   165  func (pool *TxPool) RemoveInvalid(query StateQuery) {
   166  	pool.mu.Lock()
   167  
   168  	var removedTxs types.Transactions
   169  	for _, tx := range pool.txs {
   170  		sender := query.GetAccount(tx.From())
   171  		err := pool.ValidateTransaction(tx)
   172  		if err != nil || sender.Nonce() >= tx.Nonce() {
   173  			removedTxs = append(removedTxs, tx)
   174  		}
   175  	}
   176  	pool.mu.Unlock()
   177  
   178  	pool.RemoveSet(removedTxs)
   179  }
   180  
   181  func (self *TxPool) RemoveSet(txs types.Transactions) {
   182  	self.mu.Lock()
   183  	defer self.mu.Unlock()
   184  
   185  	for _, tx := range txs {
   186  		delete(self.txs, string(tx.Hash()))
   187  	}
   188  }
   189  
   190  func (pool *TxPool) Flush() {
   191  	pool.txs = make(map[string]*types.Transaction)
   192  }
   193  
   194  func (pool *TxPool) Start() {
   195  }
   196  
   197  func (pool *TxPool) Stop() {
   198  	pool.Flush()
   199  
   200  	txplogger.Infoln("Stopped")
   201  }