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