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 }