github.com/Finschia/ostracon@v1.1.5/rpc/core/mempool.go (about)

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