op-geth/core/transaction_pool.go

223 lines
4.8 KiB
Go
Raw Normal View History

2014-12-04 09:28:02 +00:00
package core
2014-02-14 22:56:09 +00:00
import (
"errors"
2014-02-14 22:56:09 +00:00
"fmt"
"math/big"
"sync"
2015-03-16 10:27:38 +00:00
"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"
2014-10-31 11:56:05 +00:00
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"gopkg.in/fatih/set.v0"
2014-02-14 22:56:09 +00:00
)
var (
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
2014-02-14 22:56:09 +00:00
type TxPoolHook chan *types.Transaction
type TxMsg struct{ Tx *types.Transaction }
2014-02-25 10:22:27 +00:00
type stateFn func() *state.StateDB
2014-02-25 10:22:27 +00:00
const (
minGasPrice = 1000000
2014-02-25 10:22:27 +00:00
)
2014-02-23 00:57:04 +00:00
type TxProcessor interface {
ProcessTransaction(tx *types.Transaction)
2014-02-23 00:57:04 +00:00
}
2014-02-14 22:56:09 +00:00
// 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.
2014-02-14 22:56:09 +00:00
type TxPool struct {
mu sync.RWMutex
2014-02-14 22:56:09 +00:00
// Queueing channel for reading and writing incoming
// transactions to
queueChan chan *types.Transaction
2014-02-14 22:56:09 +00:00
// Quiting channel
quit chan bool
// The state function which will allow us to do some pre checkes
currentState stateFn
2014-02-14 22:56:09 +00:00
// The actual pool
txs map[common.Hash]*types.Transaction
invalidHashes *set.Set
2014-02-14 22:56:09 +00:00
2014-02-25 10:22:27 +00:00
subscribers []chan TxMsg
eventMux *event.TypeMux
2014-02-14 22:56:09 +00:00
}
func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn) *TxPool {
2014-02-14 22:56:09 +00:00
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,
2014-02-14 22:56:09 +00:00
}
}
func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
// Validate sender
var (
from common.Address
err error
)
if from, err = tx.From(); err != nil {
2015-03-18 12:38:47 +00:00
return ErrInvalidSender
2014-12-01 23:14:34 +00:00
}
// Validate curve param
2014-12-03 13:05:19 +00:00
v, _, _ := tx.Curve()
if v > 28 || v < 27 {
2015-01-02 11:24:36 +00:00
return fmt.Errorf("tx.v != (28 || 27) => %v", v)
2014-06-12 08:07:27 +00:00
}
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
2014-02-14 22:56:09 +00:00
}
2015-01-05 16:10:42 +00:00
func (self *TxPool) addTx(tx *types.Transaction) {
2015-04-08 11:07:21 +00:00
self.txs[tx.Hash()] = tx
2015-01-05 16:10:42 +00:00
}
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])
}
err := self.ValidateTransaction(tx)
if err != nil {
return err
}
2015-04-08 11:07:21 +00:00
self.addTx(tx)
var toname string
if to := tx.To(); to != nil {
toname = common.Bytes2Hex(to[:4])
} else {
toname = "[NEW_CONTRACT]"
2015-01-26 18:57:23 +00:00
}
// we can ignore the error here because From is
// verified in ValidateTransaction.
f, _ := tx.From()
from := common.Bytes2Hex(f[:4])
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})
return nil
}
func (self *TxPool) Size() int {
2015-01-05 16:10:42 +00:00
return len(self.txs)
}
func (self *TxPool) Add(tx *types.Transaction) error {
self.mu.Lock()
defer self.mu.Unlock()
return self.add(tx)
}
func (self *TxPool) AddTransactions(txs []*types.Transaction) {
self.mu.Lock()
defer self.mu.Unlock()
for _, tx := range txs {
if err := self.add(tx); err != nil {
glog.V(logger.Debug).Infoln(err)
} else {
h := tx.Hash()
glog.V(logger.Debug).Infof("tx %x\n", h[:4])
}
}
}
2015-01-05 16:10:42 +00:00
func (self *TxPool) GetTransactions() (txs types.Transactions) {
self.mu.RLock()
defer self.mu.RUnlock()
2015-01-05 16:10:42 +00:00
txs = make(types.Transactions, self.Size())
2014-02-14 22:56:09 +00:00
i := 0
2015-01-05 16:10:42 +00:00
for _, tx := range self.txs {
txs[i] = tx
2014-02-14 22:56:09 +00:00
i++
2015-01-05 16:10:42 +00:00
}
2015-01-05 16:10:42 +00:00
return
}
func (self *TxPool) RemoveSet(txs types.Transactions) {
self.mu.Lock()
defer self.mu.Unlock()
for _, tx := range txs {
delete(self.txs, tx.Hash())
}
}
func (self *TxPool) InvalidateSet(hashes *set.Set) {
self.mu.Lock()
defer self.mu.Unlock()
hashes.Each(func(v interface{}) bool {
delete(self.txs, v.(common.Hash))
return true
})
self.invalidHashes.Merge(hashes)
}
func (pool *TxPool) Flush() {
pool.txs = make(map[common.Hash]*types.Transaction)
2014-02-14 22:56:09 +00:00
}
func (pool *TxPool) Start() {
}
func (pool *TxPool) Stop() {
pool.Flush()
2014-03-17 09:33:03 +00:00
glog.V(logger.Info).Infoln("TX Pool stopped")
2014-02-14 22:56:09 +00:00
}