github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/app/rpc/namespaces/eth/tx_pool.go (about)

     1  package eth
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"strconv"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/ethereum/go-ethereum/common"
    12  	rpctypes "github.com/fibonacci-chain/fbc/app/rpc/types"
    13  	ethermint "github.com/fibonacci-chain/fbc/app/types"
    14  	clientcontext "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/context"
    15  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    16  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    17  	authclient "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/client/utils"
    18  	authtypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/types"
    19  	"github.com/fibonacci-chain/fbc/libs/tendermint/libs/log"
    20  	"github.com/fibonacci-chain/fbc/libs/tendermint/types"
    21  	tmdb "github.com/fibonacci-chain/fbc/libs/tm-db"
    22  	evmtypes "github.com/fibonacci-chain/fbc/x/evm/types"
    23  	"github.com/spf13/viper"
    24  )
    25  
    26  const (
    27  	FlagEnableTxPool      = "enable-tx-pool"
    28  	TxPoolCap             = "tx-pool-cap"
    29  	BroadcastPeriodSecond = "broadcast-period-second"
    30  	txPoolDb              = "tx_pool"
    31  )
    32  
    33  var broadcastErrors = map[uint32]*sdkerrors.Error{
    34  	sdkerrors.ErrTxInMempoolCache.ABCICode(): sdkerrors.ErrTxInMempoolCache,
    35  	sdkerrors.ErrMempoolIsFull.ABCICode():    sdkerrors.ErrMempoolIsFull,
    36  	sdkerrors.ErrTxTooLarge.ABCICode():       sdkerrors.ErrTxTooLarge,
    37  	sdkerrors.ErrInvalidSequence.ABCICode():  sdkerrors.ErrInvalidSequence,
    38  }
    39  
    40  type TxPool struct {
    41  	addressTxsPool    map[common.Address][]*evmtypes.MsgEthereumTx // All currently processable transactions
    42  	clientCtx         clientcontext.CLIContext
    43  	db                tmdb.DB
    44  	mu                sync.Mutex
    45  	cap               uint64
    46  	broadcastInterval time.Duration
    47  	logger            log.Logger
    48  }
    49  
    50  func NewTxPool(clientCtx clientcontext.CLIContext, api *PublicEthereumAPI) *TxPool {
    51  	db, err := openDB()
    52  	if err != nil {
    53  		panic(err)
    54  	}
    55  	interval := time.Second * time.Duration(viper.GetInt(BroadcastPeriodSecond))
    56  	pool := &TxPool{
    57  		addressTxsPool:    make(map[common.Address][]*evmtypes.MsgEthereumTx),
    58  		clientCtx:         clientCtx,
    59  		db:                db,
    60  		cap:               viper.GetUint64(TxPoolCap),
    61  		broadcastInterval: interval,
    62  		logger:            api.logger.With("module", "tx_pool", "namespace", "eth"),
    63  	}
    64  
    65  	if err = pool.initDB(api); err != nil {
    66  		panic(err)
    67  	}
    68  
    69  	return pool
    70  }
    71  
    72  func openDB() (tmdb.DB, error) {
    73  	rootDir := viper.GetString("home")
    74  	dataDir := filepath.Join(rootDir, "data")
    75  	return sdk.NewDB(txPoolDb, dataDir)
    76  }
    77  
    78  func (pool *TxPool) initDB(api *PublicEthereumAPI) error {
    79  	itr, err := pool.db.Iterator(nil, nil)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	defer itr.Close()
    84  	for ; itr.Valid(); itr.Next() {
    85  		key := string(itr.Key())
    86  		txBytes := itr.Value()
    87  
    88  		tmp := strings.Split(key, "|")
    89  		if len(tmp) != 2 {
    90  			continue
    91  		}
    92  		address := common.HexToAddress(tmp[0])
    93  		txNonce, err := strconv.Atoi(tmp[1])
    94  		if err != nil {
    95  			return err
    96  		}
    97  
    98  		tx := new(evmtypes.MsgEthereumTx)
    99  		if err = authtypes.EthereumTxDecode(txBytes, tx); err != nil {
   100  			return err
   101  		}
   102  		if int(tx.Data.AccountNonce) != txNonce {
   103  			return fmt.Errorf("nonce[%d] in key is not equal to nonce[%d] in value", tx.Data.AccountNonce, txNonce)
   104  		}
   105  		blockNrOrHash := rpctypes.BlockNumberOrHashWithNumber(rpctypes.PendingBlockNumber)
   106  		pCurrentNonce, err := api.GetTransactionCount(address, blockNrOrHash)
   107  		if err != nil {
   108  			return err
   109  		}
   110  		currentNonce := int(*pCurrentNonce)
   111  		if txNonce < currentNonce {
   112  			continue
   113  		}
   114  
   115  		if err = pool.insertTx(address, tx); err != nil {
   116  			return err
   117  		}
   118  	}
   119  
   120  	return nil
   121  }
   122  
   123  func broadcastTxByTxPool(api *PublicEthereumAPI, tx *evmtypes.MsgEthereumTx, txBytes []byte) (common.Hash, error) {
   124  	//TODO: to delete after venus height
   125  	lastHeight, err := api.clientCtx.Client.LatestBlockNumber()
   126  	if err != nil {
   127  		return common.Hash{}, err
   128  	}
   129  	// Get sender address
   130  	chainIDEpoch, err := ethermint.ParseChainID(api.clientCtx.ChainID)
   131  	if err != nil {
   132  		return common.Hash{}, err
   133  	}
   134  	err = tx.VerifySig(chainIDEpoch, api.clientCtx.Height)
   135  	if err != nil {
   136  		return common.Hash{}, err
   137  	}
   138  
   139  	txHash := common.BytesToHash(types.Tx(txBytes).Hash(lastHeight))
   140  	tx.Data.Hash = &txHash
   141  	from := common.HexToAddress(tx.GetFrom())
   142  	api.txPool.mu.Lock()
   143  	defer api.txPool.mu.Unlock()
   144  	if err = api.txPool.CacheAndBroadcastTx(api, from, tx); err != nil {
   145  		api.txPool.logger.Error("eth_sendRawTransaction txPool err:", err.Error())
   146  		return common.Hash{}, err
   147  	}
   148  
   149  	return txHash, nil
   150  }
   151  
   152  func (pool *TxPool) CacheAndBroadcastTx(api *PublicEthereumAPI, address common.Address, tx *evmtypes.MsgEthereumTx) error {
   153  	// get currentNonce
   154  	blockNrOrHash := rpctypes.BlockNumberOrHashWithNumber(rpctypes.PendingBlockNumber)
   155  	pCurrentNonce, err := api.GetTransactionCount(address, blockNrOrHash)
   156  	if err != nil {
   157  		return err
   158  	}
   159  	currentNonce := uint64(*pCurrentNonce)
   160  
   161  	if tx.Data.AccountNonce < currentNonce {
   162  		return fmt.Errorf("AccountNonce of tx is less than currentNonce in memPool: AccountNonce[%d], currentNonce[%d]", tx.Data.AccountNonce, currentNonce)
   163  	}
   164  
   165  	if tx.Data.AccountNonce > currentNonce+pool.cap {
   166  		return fmt.Errorf("AccountNonce of tx is bigger than txPool capacity, please try later: AccountNonce[%d]", tx.Data.AccountNonce)
   167  	}
   168  
   169  	if err = pool.insertTx(address, tx); err != nil {
   170  		return err
   171  	}
   172  
   173  	// update DB
   174  	if err = pool.writeTxInDB(address, tx); err != nil {
   175  		return err
   176  	}
   177  
   178  	_ = pool.continueBroadcast(api, currentNonce, address)
   179  
   180  	return nil
   181  }
   182  
   183  func (pool *TxPool) update(index int, address common.Address, tx *evmtypes.MsgEthereumTx) error {
   184  	txsLen := len(pool.addressTxsPool[address])
   185  	if index >= txsLen {
   186  		pool.addressTxsPool[address] = append(pool.addressTxsPool[address], tx)
   187  	} else {
   188  		tmpTx := make([]*evmtypes.MsgEthereumTx, len(pool.addressTxsPool[address][index:]))
   189  		copy(tmpTx, pool.addressTxsPool[address][index:])
   190  
   191  		pool.addressTxsPool[address] =
   192  			append(append(pool.addressTxsPool[address][:index], tx), tmpTx...)
   193  	}
   194  	return nil
   195  }
   196  
   197  // insert the tx into the txPool
   198  func (pool *TxPool) insertTx(address common.Address, tx *evmtypes.MsgEthereumTx) error {
   199  	// if this is the first time to insertTx, make the cap of txPool be TxPoolSliceMaxLen
   200  	if _, ok := pool.addressTxsPool[address]; !ok {
   201  		pool.addressTxsPool[address] = make([]*evmtypes.MsgEthereumTx, 0, pool.cap)
   202  	}
   203  	index := 0
   204  	for index < len(pool.addressTxsPool[address]) {
   205  		// the tx nonce has in txPool, drop duplicate tx
   206  		if tx.Data.AccountNonce == pool.addressTxsPool[address][index].Data.AccountNonce {
   207  			return fmt.Errorf("duplicate tx, this AccountNonce of tx has been in txPool. AccountNonce[%d]", tx.Data.AccountNonce)
   208  		}
   209  		// find the index to insert
   210  		if tx.Data.AccountNonce < pool.addressTxsPool[address][index].Data.AccountNonce {
   211  			break
   212  		}
   213  		index++
   214  	}
   215  	// update txPool
   216  	return pool.update(index, address, tx)
   217  }
   218  
   219  // iterate through the txPool map, check if need to continue broadcast tx and do it
   220  func (pool *TxPool) continueBroadcast(api *PublicEthereumAPI, currentNonce uint64, address common.Address) error {
   221  	i := 0
   222  	txsLen := len(pool.addressTxsPool[address])
   223  	var err error
   224  	for ; i < txsLen; i++ {
   225  		if pool.addressTxsPool[address][i].Data.AccountNonce == currentNonce {
   226  			// do broadcast
   227  			if err = pool.broadcast(pool.addressTxsPool[address][i]); err != nil {
   228  				pool.logger.Error(err.Error())
   229  				// delete the tx when broadcast failed
   230  				if err := pool.delTxInDB(address, pool.addressTxsPool[address][i].Data.AccountNonce); err != nil {
   231  					pool.logger.Error(err.Error())
   232  				}
   233  				break
   234  			}
   235  			// update currentNonce
   236  			currentNonce++
   237  		} else if pool.addressTxsPool[address][i].Data.AccountNonce < currentNonce {
   238  			continue
   239  		} else {
   240  			break
   241  		}
   242  	}
   243  	// i is the start index of txs that don't need to be dropped
   244  	if err != nil {
   245  		if !strings.Contains(err.Error(), sdkerrors.ErrMempoolIsFull.Error()) &&
   246  			!strings.Contains(err.Error(), sdkerrors.ErrInvalidSequence.Error()) {
   247  			// tx has err, and err is not mempoolfull, the tx should be dropped
   248  			err = fmt.Errorf("broadcast failed and tx dropped. err:%s; nonce:%d; tx_hash:%s; address:%s\n",
   249  				err.Error(), pool.addressTxsPool[address][i].Data.AccountNonce, pool.addressTxsPool[address][i].Data.Hash.String(), address.String())
   250  			pool.dropTxs(i+1, address)
   251  		} else {
   252  			err = fmt.Errorf("broadcast failed. err:%s; nonce:%d; tx_hash:%s; address:%s\n",
   253  				err.Error(), pool.addressTxsPool[address][i].Data.AccountNonce, pool.addressTxsPool[address][i].Data.Hash.String(), address.String())
   254  			pool.dropTxs(i, address)
   255  		}
   256  		pool.logger.Error(err.Error())
   257  	} else {
   258  		pool.dropTxs(i, address)
   259  	}
   260  
   261  	return err
   262  }
   263  
   264  // drop [0:index) txs in txpool
   265  func (pool *TxPool) dropTxs(index int, address common.Address) {
   266  	tmp := make([]*evmtypes.MsgEthereumTx, len(pool.addressTxsPool[address][index:]), pool.cap)
   267  	copy(tmp, pool.addressTxsPool[address][index:])
   268  	pool.addressTxsPool[address] = tmp
   269  }
   270  
   271  func (pool *TxPool) broadcast(tx *evmtypes.MsgEthereumTx) error {
   272  	// TODO: to delete after venus height
   273  	lastHeight, err := pool.clientCtx.Client.LatestBlockNumber()
   274  	if err != nil {
   275  		return err
   276  	}
   277  	var txEncoder sdk.TxEncoder
   278  	if types.HigherThanVenus(lastHeight) {
   279  		txEncoder = authclient.GetTxEncoder(nil, authclient.WithEthereumTx())
   280  	} else {
   281  		txEncoder = authclient.GetTxEncoder(pool.clientCtx.Codec)
   282  	}
   283  
   284  	txBytes, err := txEncoder(tx)
   285  	if err != nil {
   286  		return err
   287  	}
   288  	res, err := pool.clientCtx.BroadcastTx(txBytes)
   289  	if err != nil {
   290  		pool.logger.Error(err.Error())
   291  	}
   292  	if res.Code != sdk.CodeOK {
   293  		if broadcastErrors[res.Code] == nil {
   294  			return fmt.Errorf("broadcast tx failed, code: %d, rawLog: %s", res.Code, res.RawLog)
   295  		} else {
   296  			return fmt.Errorf("broadcast tx failed, err: %s", broadcastErrors[res.Code].Error())
   297  		}
   298  	}
   299  	return nil
   300  }
   301  
   302  func (pool *TxPool) writeTxInDB(address common.Address, tx *evmtypes.MsgEthereumTx) error {
   303  	key := []byte(address.Hex() + "|" + strconv.Itoa(int(tx.Data.AccountNonce)))
   304  
   305  	txEncoder := authclient.GetTxEncoder(nil, authclient.WithEthereumTx())
   306  	// Encode transaction by RLP encoder
   307  	txBytes, err := txEncoder(tx)
   308  	if err != nil {
   309  		return err
   310  	}
   311  
   312  	ok, err := pool.db.Has(key)
   313  	if err != nil {
   314  		return err
   315  	}
   316  	if ok {
   317  		return fmt.Errorf("this AccountNonce of tx has been in DB. AccountNonce[%d]", tx.Data.AccountNonce)
   318  	}
   319  
   320  	return pool.db.Set(key, txBytes)
   321  }
   322  
   323  func (pool *TxPool) delTxInDB(address common.Address, txNonce uint64) error {
   324  	key := []byte(address.Hex() + "|" + strconv.Itoa(int(txNonce)))
   325  	ok, err := pool.db.Has(key)
   326  	if err != nil {
   327  		return err
   328  	}
   329  	if !ok {
   330  		return fmt.Errorf("this AccontNonce is not found in DB. AccountNonce[%d]", txNonce)
   331  	}
   332  
   333  	return pool.db.Delete(key)
   334  }
   335  
   336  func (pool *TxPool) broadcastPeriod(api *PublicEthereumAPI) {
   337  	for {
   338  		time.Sleep(pool.broadcastInterval)
   339  		pool.broadcastPeriodCore(api)
   340  	}
   341  }
   342  func (pool *TxPool) broadcastPeriodCore(api *PublicEthereumAPI) {
   343  	pool.mu.Lock()
   344  	defer pool.mu.Unlock()
   345  	blockNrOrHash := rpctypes.BlockNumberOrHashWithNumber(rpctypes.PendingBlockNumber)
   346  	for address, _ := range pool.addressTxsPool {
   347  		pCurrentNonce, err := api.GetTransactionCount(address, blockNrOrHash)
   348  		if err != nil {
   349  			pool.logger.Error(err.Error())
   350  			continue
   351  		}
   352  		currentNonce := uint64(*pCurrentNonce)
   353  
   354  		pool.continueBroadcast(api, currentNonce, address)
   355  	}
   356  }
   357  
   358  func (pool *TxPool) broadcastOnce(api *PublicEthereumAPI) {
   359  	pool.mu.Lock()
   360  	defer pool.mu.Unlock()
   361  	blockNrOrHash := rpctypes.BlockNumberOrHashWithNumber(rpctypes.PendingBlockNumber)
   362  	for address, _ := range pool.addressTxsPool {
   363  		pCurrentNonce, err := api.GetTransactionCount(address, blockNrOrHash)
   364  		if err != nil {
   365  			continue
   366  		}
   367  		currentNonce := uint64(*pCurrentNonce)
   368  
   369  		pool.continueBroadcast(api, currentNonce, address)
   370  	}
   371  }