github.com/consideritdone/landslidecore@v0.0.0-20230718131026-a8b21c5cf8a7/rpc/core/mempool.go (about)

     1  package core
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	abci "github.com/consideritdone/landslidecore/abci/types"
    10  	mempl "github.com/consideritdone/landslidecore/mempool"
    11  	ctypes "github.com/consideritdone/landslidecore/rpc/core/types"
    12  	rpctypes "github.com/consideritdone/landslidecore/rpc/jsonrpc/types"
    13  	"github.com/consideritdone/landslidecore/types"
    14  )
    15  
    16  //-----------------------------------------------------------------------------
    17  // NOTE: tx should be signed, but this is only checked at the app level (not by Tendermint!)
    18  
    19  // BroadcastTxAsync returns right away, with no response. Does not wait for
    20  // CheckTx nor DeliverTx results.
    21  // More: https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_async
    22  func BroadcastTxAsync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
    23  	err := env.Mempool.CheckTx(tx, nil, mempl.TxInfo{})
    24  
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  	return &ctypes.ResultBroadcastTx{Hash: tx.Hash()}, nil
    29  }
    30  
    31  // BroadcastTxSync returns with the response from CheckTx. Does not wait for
    32  // DeliverTx result.
    33  // More: https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_sync
    34  func BroadcastTxSync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
    35  	resCh := make(chan *abci.Response, 1)
    36  	err := env.Mempool.CheckTx(tx, func(res *abci.Response) {
    37  		resCh <- res
    38  	}, mempl.TxInfo{})
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  	res := <-resCh
    43  	r := res.GetCheckTx()
    44  	return &ctypes.ResultBroadcastTx{
    45  		Code:      r.Code,
    46  		Data:      r.Data,
    47  		Log:       r.Log,
    48  		Codespace: r.Codespace,
    49  		Hash:      tx.Hash(),
    50  	}, nil
    51  }
    52  
    53  // BroadcastTxCommit returns with the responses from CheckTx and DeliverTx.
    54  // More: https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_commit
    55  func BroadcastTxCommit(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
    56  	subscriber := ctx.RemoteAddr()
    57  
    58  	if env.EventBus.NumClients() >= env.Config.MaxSubscriptionClients {
    59  		return nil, fmt.Errorf("max_subscription_clients %d reached", env.Config.MaxSubscriptionClients)
    60  	} else if env.EventBus.NumClientSubscriptions(subscriber) >= env.Config.MaxSubscriptionsPerClient {
    61  		return nil, fmt.Errorf("max_subscriptions_per_client %d reached", env.Config.MaxSubscriptionsPerClient)
    62  	}
    63  
    64  	// Subscribe to tx being committed in block.
    65  	subCtx, cancel := context.WithTimeout(ctx.Context(), SubscribeTimeout)
    66  	defer cancel()
    67  	q := types.EventQueryTxFor(tx)
    68  	deliverTxSub, err := env.EventBus.Subscribe(subCtx, subscriber, q)
    69  	if err != nil {
    70  		err = fmt.Errorf("failed to subscribe to tx: %w", err)
    71  		env.Logger.Error("Error on broadcast_tx_commit", "err", err)
    72  		return nil, err
    73  	}
    74  	defer func() {
    75  		if err := env.EventBus.Unsubscribe(context.Background(), subscriber, q); err != nil {
    76  			env.Logger.Error("Error unsubscribing from eventBus", "err", err)
    77  		}
    78  	}()
    79  
    80  	// Broadcast tx and wait for CheckTx result
    81  	checkTxResCh := make(chan *abci.Response, 1)
    82  	err = env.Mempool.CheckTx(tx, func(res *abci.Response) {
    83  		checkTxResCh <- res
    84  	}, mempl.TxInfo{})
    85  	if err != nil {
    86  		env.Logger.Error("Error on broadcastTxCommit", "err", err)
    87  		return nil, fmt.Errorf("error on broadcastTxCommit: %v", err)
    88  	}
    89  	checkTxResMsg := <-checkTxResCh
    90  	checkTxRes := checkTxResMsg.GetCheckTx()
    91  	if checkTxRes.Code != abci.CodeTypeOK {
    92  		return &ctypes.ResultBroadcastTxCommit{
    93  			CheckTx:   *checkTxRes,
    94  			DeliverTx: abci.ResponseDeliverTx{},
    95  			Hash:      tx.Hash(),
    96  		}, nil
    97  	}
    98  
    99  	// Wait for the tx to be included in a block or timeout.
   100  	select {
   101  	case msg := <-deliverTxSub.Out(): // The tx was included in a block.
   102  		deliverTxRes := msg.Data().(types.EventDataTx)
   103  		return &ctypes.ResultBroadcastTxCommit{
   104  			CheckTx:   *checkTxRes,
   105  			DeliverTx: deliverTxRes.Result,
   106  			Hash:      tx.Hash(),
   107  			Height:    deliverTxRes.Height,
   108  		}, nil
   109  	case <-deliverTxSub.Cancelled():
   110  		var reason string
   111  		if deliverTxSub.Err() == nil {
   112  			reason = "Tendermint exited"
   113  		} else {
   114  			reason = deliverTxSub.Err().Error()
   115  		}
   116  		err = fmt.Errorf("deliverTxSub was cancelled (reason: %s)", reason)
   117  		env.Logger.Error("Error on broadcastTxCommit", "err", err)
   118  		return &ctypes.ResultBroadcastTxCommit{
   119  			CheckTx:   *checkTxRes,
   120  			DeliverTx: abci.ResponseDeliverTx{},
   121  			Hash:      tx.Hash(),
   122  		}, err
   123  	case <-time.After(env.Config.TimeoutBroadcastTxCommit):
   124  		err = errors.New("timed out waiting for tx to be included in a block")
   125  		env.Logger.Error("Error on broadcastTxCommit", "err", err)
   126  		return &ctypes.ResultBroadcastTxCommit{
   127  			CheckTx:   *checkTxRes,
   128  			DeliverTx: abci.ResponseDeliverTx{},
   129  			Hash:      tx.Hash(),
   130  		}, err
   131  	}
   132  }
   133  
   134  // UnconfirmedTxs gets unconfirmed transactions (maximum ?limit entries)
   135  // including their number.
   136  // More: https://docs.tendermint.com/master/rpc/#/Info/unconfirmed_txs
   137  func UnconfirmedTxs(ctx *rpctypes.Context, limitPtr *int) (*ctypes.ResultUnconfirmedTxs, error) {
   138  	// reuse per_page validator
   139  	limit := validatePerPage(limitPtr)
   140  
   141  	txs := env.Mempool.ReapMaxTxs(limit)
   142  	return &ctypes.ResultUnconfirmedTxs{
   143  		Count:      len(txs),
   144  		Total:      env.Mempool.Size(),
   145  		TotalBytes: env.Mempool.TxsBytes(),
   146  		Txs:        txs}, nil
   147  }
   148  
   149  // NumUnconfirmedTxs gets number of unconfirmed transactions.
   150  // More: https://docs.tendermint.com/master/rpc/#/Info/num_unconfirmed_txs
   151  func NumUnconfirmedTxs(ctx *rpctypes.Context) (*ctypes.ResultUnconfirmedTxs, error) {
   152  	return &ctypes.ResultUnconfirmedTxs{
   153  		Count:      env.Mempool.Size(),
   154  		Total:      env.Mempool.Size(),
   155  		TotalBytes: env.Mempool.TxsBytes()}, nil
   156  }
   157  
   158  // CheckTx checks the transaction without executing it. The transaction won't
   159  // be added to the mempool either.
   160  // More: https://docs.tendermint.com/master/rpc/#/Tx/check_tx
   161  func CheckTx(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultCheckTx, error) {
   162  	res, err := env.ProxyAppMempool.CheckTxSync(abci.RequestCheckTx{Tx: tx})
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	return &ctypes.ResultCheckTx{ResponseCheckTx: *res}, nil
   167  }