github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/internal/rpc/core/mempool.go (about)

     1  package core
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"math/rand"
     8  	"time"
     9  
    10  	abci "github.com/ari-anchor/sei-tendermint/abci/types"
    11  	"github.com/ari-anchor/sei-tendermint/internal/mempool"
    12  	"github.com/ari-anchor/sei-tendermint/internal/state/indexer"
    13  	tmmath "github.com/ari-anchor/sei-tendermint/libs/math"
    14  	"github.com/ari-anchor/sei-tendermint/rpc/coretypes"
    15  )
    16  
    17  //-----------------------------------------------------------------------------
    18  // NOTE: tx should be signed, but this is only checked at the app level (not by Tendermint!)
    19  
    20  // BroadcastTxAsync returns right away, with no response. Does not wait for
    21  // CheckTx nor DeliverTx results.
    22  // More:
    23  // https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_async
    24  // Deprecated and should be removed in 0.37
    25  func (env *Environment) BroadcastTxAsync(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTx, error) {
    26  	go func() { _ = env.Mempool.CheckTx(ctx, req.Tx, nil, mempool.TxInfo{}) }()
    27  
    28  	return &coretypes.ResultBroadcastTx{Hash: req.Tx.Hash()}, nil
    29  }
    30  
    31  // Deprecated and should be remove in 0.37
    32  func (env *Environment) BroadcastTxSync(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTx, error) {
    33  	return env.BroadcastTx(ctx, req)
    34  }
    35  
    36  // BroadcastTx returns with the response from CheckTx. Does not wait for
    37  // DeliverTx result.
    38  // More: https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_sync
    39  func (env *Environment) BroadcastTx(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTx, error) {
    40  	resCh := make(chan *abci.ResponseCheckTx, 1)
    41  	err := env.Mempool.CheckTx(
    42  		ctx,
    43  		req.Tx,
    44  		func(res *abci.ResponseCheckTx) {
    45  			select {
    46  			case <-ctx.Done():
    47  			case resCh <- res:
    48  			}
    49  		},
    50  		mempool.TxInfo{},
    51  	)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	select {
    57  	case <-ctx.Done():
    58  		return nil, fmt.Errorf("broadcast confirmation not received: %w", ctx.Err())
    59  	case r := <-resCh:
    60  		return &coretypes.ResultBroadcastTx{
    61  			Code:      r.Code,
    62  			Data:      r.Data,
    63  			Codespace: r.Codespace,
    64  			Hash:      req.Tx.Hash(),
    65  		}, nil
    66  	}
    67  }
    68  
    69  // BroadcastTxCommit returns with the responses from CheckTx and DeliverTx.
    70  // More: https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_commit
    71  func (env *Environment) BroadcastTxCommit(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTxCommit, error) {
    72  	resCh := make(chan *abci.ResponseCheckTx, 1)
    73  	err := env.Mempool.CheckTx(
    74  		ctx,
    75  		req.Tx,
    76  		func(res *abci.ResponseCheckTx) {
    77  			select {
    78  			case <-ctx.Done():
    79  			case resCh <- res:
    80  			}
    81  		},
    82  		mempool.TxInfo{},
    83  	)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	select {
    89  	case <-ctx.Done():
    90  		return nil, fmt.Errorf("broadcast confirmation not received: %w", ctx.Err())
    91  	case r := <-resCh:
    92  		if r.Code != abci.CodeTypeOK {
    93  			return &coretypes.ResultBroadcastTxCommit{
    94  				CheckTx: *r,
    95  				Hash:    req.Tx.Hash(),
    96  			}, nil
    97  		}
    98  
    99  		if !indexer.KVSinkEnabled(env.EventSinks) {
   100  			return &coretypes.ResultBroadcastTxCommit{
   101  					CheckTx: *r,
   102  					Hash:    req.Tx.Hash(),
   103  				},
   104  				errors.New("cannot confirm transaction because kvEventSink is not enabled")
   105  		}
   106  
   107  		startAt := time.Now()
   108  		timer := time.NewTimer(0)
   109  		defer timer.Stop()
   110  
   111  		count := 0
   112  		for {
   113  			count++
   114  			select {
   115  			case <-ctx.Done():
   116  				env.Logger.Error("error on broadcastTxCommit",
   117  					"duration", time.Since(startAt),
   118  					"err", err)
   119  				return &coretypes.ResultBroadcastTxCommit{
   120  						CheckTx: *r,
   121  						Hash:    req.Tx.Hash(),
   122  					}, fmt.Errorf("timeout waiting for commit of tx %s (%s)",
   123  						req.Tx.Hash(), time.Since(startAt))
   124  			case <-timer.C:
   125  				txres, err := env.Tx(ctx, &coretypes.RequestTx{
   126  					Hash:  req.Tx.Hash(),
   127  					Prove: false,
   128  				})
   129  				if err != nil {
   130  					jitter := 100*time.Millisecond + time.Duration(rand.Int63n(int64(time.Second))) // nolint: gosec
   131  					backoff := 100 * time.Duration(count) * time.Millisecond
   132  					timer.Reset(jitter + backoff)
   133  					continue
   134  				}
   135  
   136  				return &coretypes.ResultBroadcastTxCommit{
   137  					CheckTx:  *r,
   138  					TxResult: txres.TxResult,
   139  					Hash:     req.Tx.Hash(),
   140  					Height:   txres.Height,
   141  				}, nil
   142  			}
   143  		}
   144  	}
   145  }
   146  
   147  // UnconfirmedTxs gets unconfirmed transactions from the mempool in order of priority
   148  // More: https://docs.tendermint.com/master/rpc/#/Info/unconfirmed_txs
   149  func (env *Environment) UnconfirmedTxs(ctx context.Context, req *coretypes.RequestUnconfirmedTxs) (*coretypes.ResultUnconfirmedTxs, error) {
   150  	totalCount := env.Mempool.Size()
   151  	perPage := env.validatePerPage(req.PerPage.IntPtr())
   152  	page, err := validatePage(req.Page.IntPtr(), perPage, totalCount)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	skipCount := validateSkipCount(page, perPage)
   158  
   159  	txs := env.Mempool.ReapMaxTxs(skipCount + tmmath.MinInt(perPage, totalCount-skipCount))
   160  	result := txs[skipCount:]
   161  
   162  	return &coretypes.ResultUnconfirmedTxs{
   163  		Count:      len(result),
   164  		Total:      totalCount,
   165  		TotalBytes: env.Mempool.SizeBytes(),
   166  		Txs:        result,
   167  	}, nil
   168  }
   169  
   170  // NumUnconfirmedTxs gets number of unconfirmed transactions.
   171  // More: https://docs.tendermint.com/master/rpc/#/Info/num_unconfirmed_txs
   172  func (env *Environment) NumUnconfirmedTxs(ctx context.Context) (*coretypes.ResultUnconfirmedTxs, error) {
   173  	return &coretypes.ResultUnconfirmedTxs{
   174  		Count:      env.Mempool.Size(),
   175  		Total:      env.Mempool.Size(),
   176  		TotalBytes: env.Mempool.SizeBytes()}, nil
   177  }
   178  
   179  // CheckTx checks the transaction without executing it. The transaction won't
   180  // be added to the mempool either.
   181  // More: https://docs.tendermint.com/master/rpc/#/Tx/check_tx
   182  func (env *Environment) CheckTx(ctx context.Context, req *coretypes.RequestCheckTx) (*coretypes.ResultCheckTx, error) {
   183  	res, err := env.ProxyApp.CheckTx(ctx, &abci.RequestCheckTx{Tx: req.Tx})
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  	return &coretypes.ResultCheckTx{ResponseCheckTx: *res}, nil
   188  }
   189  
   190  func (env *Environment) RemoveTx(ctx context.Context, req *coretypes.RequestRemoveTx) error {
   191  	return env.Mempool.RemoveTxByKey(req.TxKey)
   192  }