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 }