github.com/klaytn/klaytn@v1.12.1/node/sc/bridgepool/bridge_tx_pool.go (about)

     1  // Modifications Copyright 2019 The klaytn Authors
     2  // Copyright 2014 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from core/tx_pool.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package bridgepool
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  	"math/big"
    27  	"sync"
    28  	"time"
    29  
    30  	"github.com/klaytn/klaytn/blockchain/types"
    31  	"github.com/klaytn/klaytn/common"
    32  	"github.com/klaytn/klaytn/log"
    33  	"github.com/rcrowley/go-metrics"
    34  )
    35  
    36  var logger = log.NewModuleLogger(log.ServiceChain)
    37  
    38  var (
    39  	ErrKnownTx           = errors.New("Known Transaction")
    40  	ErrUnknownTx         = errors.New("Unknown Transaction")
    41  	ErrDuplicatedNonceTx = errors.New("Duplicated Nonce Transaction")
    42  )
    43  
    44  // TODO-Klaytn-Servicechain Add Metrics
    45  var (
    46  	// Metrics for the pending pool
    47  	refusedTxCounter = metrics.NewRegisteredCounter("bridgeTxpool/refuse", nil)
    48  )
    49  
    50  // BridgeTxPoolConfig are the configuration parameters of the transaction pool.
    51  type BridgeTxPoolConfig struct {
    52  	ParentChainID *big.Int
    53  	Journal       string        // Journal of local transactions to survive node restarts
    54  	Rejournal     time.Duration // Time interval to regenerate the local transaction journal
    55  
    56  	GlobalQueue uint64 // Maximum number of non-executable transaction slots for all accounts
    57  }
    58  
    59  // DefaultBridgeTxPoolConfig contains the default configurations for the transaction
    60  // pool.
    61  var DefaultBridgeTxPoolConfig = BridgeTxPoolConfig{
    62  	ParentChainID: big.NewInt(2018),
    63  	Journal:       "bridge_transactions.rlp",
    64  	Rejournal:     10 * time.Minute,
    65  	GlobalQueue:   8192,
    66  }
    67  
    68  // sanitize checks the provided user configurations and changes anything that's
    69  // unreasonable or unworkable.
    70  func (config *BridgeTxPoolConfig) sanitize() BridgeTxPoolConfig {
    71  	conf := *config
    72  	if conf.Rejournal < time.Second {
    73  		logger.Error("Sanitizing invalid bridgetxpool journal time", "provided", conf.Rejournal, "updated", time.Second)
    74  		conf.Rejournal = time.Second
    75  	}
    76  
    77  	if conf.Journal == "" {
    78  		logger.Error("Sanitizing invalid bridgetxpool journal file name", "updated", DefaultBridgeTxPoolConfig.Journal)
    79  		conf.Journal = DefaultBridgeTxPoolConfig.Journal
    80  	}
    81  
    82  	return conf
    83  }
    84  
    85  // BridgeTxPool contains all currently known chain transactions.
    86  type BridgeTxPool struct {
    87  	config BridgeTxPoolConfig
    88  	// TODO-Klaytn-Servicechain consider to remove singer. For now, caused of value transfer tx which don't have `from` value, I leave it.
    89  	signer types.Signer
    90  	mu     sync.RWMutex
    91  	// txMu   sync.RWMutex // TODO-Klaytn-Servicechain: implement fine-grained locks
    92  
    93  	journal *bridgeTxJournal // Journal of transaction to back up to disk
    94  
    95  	queue map[common.Address]*ItemSortedMap // Queued but non-processable transactions
    96  	// TODO-Klaytn-Servicechain refine heartbeat for the tx not for account.
    97  	all map[common.Hash]*types.Transaction // All transactions to allow lookups
    98  
    99  	wg     sync.WaitGroup // for shutdown sync
   100  	closed chan struct{}
   101  }
   102  
   103  // NewBridgeTxPool creates a new transaction pool to gather, sort and filter inbound
   104  // transactions from the network.
   105  func NewBridgeTxPool(config BridgeTxPoolConfig) *BridgeTxPool {
   106  	// Sanitize the input to ensure no vulnerable gas prices are set
   107  	config = (&config).sanitize()
   108  
   109  	// Create the transaction pool with its initial settings
   110  	pool := &BridgeTxPool{
   111  		config: config,
   112  		queue:  make(map[common.Address]*ItemSortedMap),
   113  		all:    make(map[common.Hash]*types.Transaction),
   114  		closed: make(chan struct{}),
   115  	}
   116  
   117  	pool.SetLatestSigner(config.ParentChainID)
   118  
   119  	// load from disk
   120  	pool.journal = newBridgeTxJournal(config.Journal)
   121  
   122  	if err := pool.journal.load(pool.AddLocals); err != nil {
   123  		logger.Error("Failed to load chain transaction journal", "err", err)
   124  	}
   125  	if err := pool.journal.rotate(pool.Pending()); err != nil {
   126  		logger.Error("Failed to rotate chain transaction journal", "err", err)
   127  	}
   128  
   129  	// Start the event loop and return
   130  	pool.wg.Add(1)
   131  	go pool.loop()
   132  
   133  	return pool
   134  }
   135  
   136  // Deprecated: This function is deprecated. Use SetLatestSigner instead.
   137  // SetEIP155Signer set signer of txpool.
   138  func (pool *BridgeTxPool) SetEIP155Signer(chainID *big.Int) {
   139  	pool.signer = types.NewEIP155Signer(chainID)
   140  }
   141  
   142  // SetLatestSigner set latest signer to txpool
   143  func (pool *BridgeTxPool) SetLatestSigner(chainID *big.Int) {
   144  	pool.signer = types.LatestSignerForChainID(chainID)
   145  }
   146  
   147  // loop is the transaction pool's main event loop, waiting for and reacting to
   148  // outside blockchain events as well as for various reporting and transaction
   149  // eviction events.
   150  func (pool *BridgeTxPool) loop() {
   151  	defer pool.wg.Done()
   152  
   153  	journal := time.NewTicker(pool.config.Rejournal)
   154  	defer journal.Stop()
   155  
   156  	// Keep waiting for and reacting to the various events
   157  	for {
   158  		select {
   159  		// Handle local transaction journal rotation
   160  		case <-journal.C:
   161  			if pool.journal != nil {
   162  				pool.mu.Lock()
   163  				if err := pool.journal.rotate(pool.pending()); err != nil {
   164  					logger.Error("Failed to rotate local tx journal", "err", err)
   165  				}
   166  				pool.mu.Unlock()
   167  			}
   168  		case <-pool.closed:
   169  			// update journal file with txs in pool.
   170  			// if txpool close without the rotate process,
   171  			// when loading the txpool with the journal file again,
   172  			// there is a limit to the size of pool so that not all tx will be
   173  			// loaded and especially the latest tx will not be loaded
   174  			if pool.journal != nil {
   175  				pool.mu.Lock()
   176  				if err := pool.journal.rotate(pool.pending()); err != nil {
   177  					logger.Error("Failed to rotate local tx journal", "err", err)
   178  				}
   179  				pool.mu.Unlock()
   180  			}
   181  			logger.Info("BridgeTxPool loop is closing")
   182  			return
   183  		}
   184  	}
   185  }
   186  
   187  // Stop terminates the transaction pool.
   188  func (pool *BridgeTxPool) Stop() {
   189  	close(pool.closed)
   190  	pool.wg.Wait()
   191  
   192  	if pool.journal != nil {
   193  		pool.journal.close()
   194  	}
   195  	logger.Info("Transaction pool stopped")
   196  }
   197  
   198  // Stats retrieves the current pool stats, namely the number of pending transactions.
   199  func (pool *BridgeTxPool) Stats() int {
   200  	pool.mu.RLock()
   201  	defer pool.mu.RUnlock()
   202  
   203  	queued := 0
   204  	for _, list := range pool.queue {
   205  		queued += list.Len()
   206  	}
   207  	return queued
   208  }
   209  
   210  // Content retrieves the data content of the transaction pool, returning all the
   211  // queued transactions, grouped by account and sorted by nonce.
   212  func (pool *BridgeTxPool) Content() map[common.Address]types.Transactions {
   213  	pool.mu.Lock()
   214  	defer pool.mu.Unlock()
   215  
   216  	queued := make(map[common.Address]types.Transactions)
   217  	for addr, list := range pool.queue {
   218  		var queuedTxs []*types.Transaction
   219  		txs := list.Flatten()
   220  		for _, tx := range txs {
   221  			queuedTxs = append(queuedTxs, tx.(*types.Transaction))
   222  		}
   223  		queued[addr] = queuedTxs
   224  	}
   225  	return queued
   226  }
   227  
   228  // GetTx get the tx by tx hash.
   229  func (pool *BridgeTxPool) GetTx(txHash common.Hash) (*types.Transaction, error) {
   230  	pool.mu.RLock()
   231  	defer pool.mu.RUnlock()
   232  
   233  	tx, ok := pool.all[txHash]
   234  
   235  	if ok {
   236  		return tx, nil
   237  	} else {
   238  		return nil, ErrUnknownTx
   239  	}
   240  }
   241  
   242  // Pending returns all pending transactions by calling internal pending method.
   243  func (pool *BridgeTxPool) Pending() map[common.Address]types.Transactions {
   244  	pool.mu.Lock()
   245  	defer pool.mu.Unlock()
   246  
   247  	pending := pool.pending()
   248  	return pending
   249  }
   250  
   251  // Pending retrieves all pending transactions, grouped by origin
   252  // account and sorted by nonce.
   253  func (pool *BridgeTxPool) pending() map[common.Address]types.Transactions {
   254  	pending := make(map[common.Address]types.Transactions)
   255  	for addr, list := range pool.queue {
   256  		var pendingTxs []*types.Transaction
   257  		txs := list.Flatten()
   258  		for _, tx := range txs {
   259  			pendingTxs = append(pendingTxs, tx.(*types.Transaction))
   260  		}
   261  		pending[addr] = pendingTxs
   262  	}
   263  	return pending
   264  }
   265  
   266  // PendingTxsByAddress retrieves pending transactions of from. They are sorted by nonce.
   267  func (pool *BridgeTxPool) PendingTxsByAddress(from *common.Address, limit int) types.Transactions {
   268  	pool.mu.Lock()
   269  	defer pool.mu.Unlock()
   270  
   271  	var pendingTxs types.Transactions
   272  
   273  	if list, exist := pool.queue[*from]; exist {
   274  		txs := list.FlattenByCount(limit)
   275  		for _, tx := range txs {
   276  			pendingTxs = append(pendingTxs, tx.(*types.Transaction))
   277  		}
   278  		return pendingTxs
   279  	}
   280  	return nil
   281  }
   282  
   283  // PendingTxHashesByAddress retrieves pending transaction hashes of from. They are sorted by nonce.
   284  func (pool *BridgeTxPool) PendingTxHashesByAddress(from *common.Address, limit int) []common.Hash {
   285  	pool.mu.Lock()
   286  	defer pool.mu.Unlock()
   287  
   288  	if list, exist := pool.queue[*from]; exist {
   289  		pendingTxHashes := make([]common.Hash, limit)
   290  		txs := list.FlattenByCount(limit)
   291  		for _, tx := range txs {
   292  			pendingTxHashes = append(pendingTxHashes, tx.(*types.Transaction).Hash())
   293  		}
   294  		return pendingTxHashes
   295  	}
   296  	return nil
   297  }
   298  
   299  // GetMaxTxNonce finds max nonce of the address.
   300  func (pool *BridgeTxPool) GetMaxTxNonce(from *common.Address) uint64 {
   301  	pool.mu.RLock()
   302  	defer pool.mu.RUnlock()
   303  
   304  	maxNonce := uint64(0)
   305  	if list, exist := pool.queue[*from]; exist {
   306  		for _, t := range list.items {
   307  			if maxNonce < t.Nonce() {
   308  				maxNonce = t.Nonce()
   309  			}
   310  		}
   311  	}
   312  	return maxNonce
   313  }
   314  
   315  // add validates a transaction and inserts it into the non-executable queue for
   316  // later pending promotion and execution. If the transaction is a replacement for
   317  // an already pending or queued one, it overwrites the previous and returns this
   318  // so outer code doesn't uselessly call promote.
   319  func (pool *BridgeTxPool) add(tx *types.Transaction) error {
   320  	// If the transaction is already known, discard it
   321  	hash := tx.Hash()
   322  	if pool.all[hash] != nil {
   323  		logger.Trace("Discarding already known transaction", "hash", hash)
   324  		return ErrKnownTx
   325  	}
   326  
   327  	from, err := types.Sender(pool.signer, tx)
   328  	if err != nil {
   329  		return err
   330  	}
   331  
   332  	if uint64(len(pool.all)) >= pool.config.GlobalQueue {
   333  		logger.Trace("Rejecting a new Tx, because BridgeTxPool is full and there is no room for the account", "hash", tx.Hash(), "account", from)
   334  		refusedTxCounter.Inc(1)
   335  		return fmt.Errorf("txpool is full: %d", uint64(len(pool.all)))
   336  	}
   337  
   338  	if pool.queue[from] == nil {
   339  		pool.queue[from] = NewItemSortedMap(UnlimitedItemSortedMap)
   340  	} else {
   341  		if pool.queue[from].Get(tx.Nonce()) != nil {
   342  			return ErrDuplicatedNonceTx
   343  		}
   344  	}
   345  
   346  	pool.queue[from].Put(tx)
   347  
   348  	if pool.all[hash] == nil {
   349  		pool.all[hash] = tx
   350  	}
   351  
   352  	// Mark journal transactions
   353  	pool.journalTx(from, tx)
   354  
   355  	logger.Trace("Pooled new future transaction", "hash", hash, "from", from, "to", tx.To())
   356  	return nil
   357  }
   358  
   359  // journalTx adds the specified transaction to the local disk journal if it is
   360  // deemed to have been sent from a service chain account.
   361  func (pool *BridgeTxPool) journalTx(from common.Address, tx *types.Transaction) {
   362  	// Only journal if it's enabled
   363  	if pool.journal == nil {
   364  		return
   365  	}
   366  	if err := pool.journal.insert(tx); err != nil {
   367  		logger.Error("Failed to journal local transaction", "err", err)
   368  	}
   369  }
   370  
   371  // AddLocal enqueues a single transaction into the pool if it is valid, marking
   372  // the sender as a local one.
   373  func (pool *BridgeTxPool) AddLocal(tx *types.Transaction) error {
   374  	pool.mu.Lock()
   375  	defer pool.mu.Unlock()
   376  
   377  	return pool.addTx(tx)
   378  }
   379  
   380  // AddLocals enqueues a batch of transactions into the pool if they are valid,
   381  // marking the senders as a local ones.
   382  func (pool *BridgeTxPool) AddLocals(txs []*types.Transaction) []error {
   383  	pool.mu.Lock()
   384  	defer pool.mu.Unlock()
   385  
   386  	return pool.addTxs(txs)
   387  }
   388  
   389  // addTx enqueues a single transaction into the pool if it is valid.
   390  func (pool *BridgeTxPool) addTx(tx *types.Transaction) error {
   391  	// senderCacher.recover(pool.signer, []*types.Transaction{tx})
   392  	// Try to inject the transaction and update any state
   393  	return pool.add(tx)
   394  }
   395  
   396  // addTxs attempts to queue a batch of transactions if they are valid.
   397  func (pool *BridgeTxPool) addTxs(txs []*types.Transaction) []error {
   398  	// senderCacher.recover(pool.signer, txs)
   399  	// Add the batch of transaction, tracking the accepted ones
   400  	errs := make([]error, len(txs))
   401  
   402  	for i, tx := range txs {
   403  		errs[i] = pool.add(tx)
   404  	}
   405  
   406  	return errs
   407  }
   408  
   409  // Get returns a transaction if it is contained in the pool
   410  // and nil otherwise.
   411  func (pool *BridgeTxPool) Get(hash common.Hash) *types.Transaction {
   412  	pool.mu.RLock()
   413  	defer pool.mu.RUnlock()
   414  
   415  	return pool.all[hash]
   416  }
   417  
   418  // removeTx removes a single transaction from the queue.
   419  func (pool *BridgeTxPool) removeTx(hash common.Hash) error {
   420  	// Fetch the transaction we wish to delete
   421  	tx, ok := pool.all[hash]
   422  	if !ok {
   423  		return ErrUnknownTx
   424  	}
   425  
   426  	addr, err := types.Sender(pool.signer, tx)
   427  	if err != nil {
   428  		return err
   429  	}
   430  
   431  	// Remove it from the list of known transactions
   432  	delete(pool.all, hash)
   433  
   434  	// Transaction is in the future queue
   435  	if future := pool.queue[addr]; future != nil {
   436  		future.Remove(tx.Nonce())
   437  		if future.Len() == 0 {
   438  			delete(pool.queue, addr)
   439  		}
   440  	}
   441  
   442  	return nil
   443  }
   444  
   445  // Remove removes transactions from the queue.
   446  func (pool *BridgeTxPool) Remove(txs types.Transactions) []error {
   447  	pool.mu.Lock()
   448  	defer pool.mu.Unlock()
   449  
   450  	errs := make([]error, len(txs))
   451  	for i, tx := range txs {
   452  		errs[i] = pool.removeTx(tx.Hash())
   453  	}
   454  	return errs
   455  }
   456  
   457  // RemoveTx removes a single transaction from the queue.
   458  func (pool *BridgeTxPool) RemoveTx(tx *types.Transaction) error {
   459  	pool.mu.Lock()
   460  	defer pool.mu.Unlock()
   461  
   462  	err := pool.removeTx(tx.Hash())
   463  	if err != nil {
   464  		logger.Error("RemoveTx", "err", err)
   465  		return err
   466  	}
   467  	return nil
   468  }