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