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 }