@@ -6,11 +6,11 @@ import (
66 "io"
77 "time"
88
9- "github.com/blinklabs-io/tx-submit-api/internal/config"
10- "github.com/blinklabs-io/tx-submit-api/internal/logging"
119 ouroboros "github.com/blinklabs-io/gouroboros"
1210 "github.com/blinklabs-io/gouroboros/ledger"
1311 "github.com/blinklabs-io/gouroboros/protocol/localtxsubmission"
12+ "github.com/blinklabs-io/tx-submit-api/internal/config"
13+ "github.com/blinklabs-io/tx-submit-api/internal/logging"
1414
1515 "github.com/fxamacker/cbor/v2"
1616 ginzap "github.com/gin-contrib/zap"
@@ -107,6 +107,7 @@ func Start(cfg *config.Config) error {
107107
108108 // Configure API routes
109109 router .POST ("/api/submit/tx" , handleSubmitTx )
110+ router .GET ("/api/hastx/:tx_hash" , handleHasTx )
110111
111112 // Start API listener
112113 err := router .Run (fmt .Sprintf ("%s:%d" ,
@@ -120,7 +121,95 @@ func handleHealthcheck(c *gin.Context) {
120121 c .JSON (200 , gin.H {"failed" : false })
121122}
122123
124+ // Path parameters for GET requests
125+ type TxHashPathParams struct {
126+ TxHash string `uri:"tx_hash" binding:"required"` // Transaction hash
127+ }
128+
129+ // handleHasTx godoc
130+ //
131+ // @Summary HasTx
132+ // @Description Determine if a given transaction ID exists in the node mempool.
133+ // @Produce json
134+ // @Param tx_hash path string true "Transaction Hash"
135+ // @Success 200 {object} string "Ok"
136+ // @Failure 400 {object} string "Bad Request"
137+ // @Failure 404 {object} string "Not Found"
138+ // @Failure 415 {object} string "Unsupported Media Type"
139+ // @Failure 500 {object} string "Server Error"
140+ // @Router /api/hastx/{tx_hash} [get]
141+ func handleHasTx (c * gin.Context ) {
142+ // First, initialize our configuration and loggers
143+ cfg := config .GetConfig ()
144+ logger := logging .GetLogger ()
145+
146+ var uriParams TxHashPathParams
147+ if err := c .ShouldBindUri (& uriParams ); err != nil {
148+ logger .Errorf ("failed to bind transaction hash from path: %s" , err )
149+ c .JSON (400 , fmt .Sprintf ("invalid transaction hash: %s" , err ))
150+ return
151+ }
152+
153+ txHash := uriParams .TxHash
154+ // convert to cbor bytes
155+ cborData , err := cbor .Marshal (txHash )
156+ if err != nil {
157+ logger .Errorf ("failed to encode transaction hash to CBOR: %s" , err )
158+ c .JSON (400 , fmt .Sprintf ("failed to encode transaction hash to CBOR: %s" , err ))
159+ return
160+ }
161+
162+ // Connect to cardano-node and check for transaction
163+ errorChan := make (chan error )
164+ oConn , err := ouroboros .NewConnection (
165+ ouroboros .WithNetworkMagic (uint32 (cfg .Node .NetworkMagic )),
166+ ouroboros .WithErrorChan (errorChan ),
167+ ouroboros .WithNodeToNode (false ),
168+ )
169+ if err != nil {
170+ logger .Errorf ("failure creating Ouroboros connection: %s" , err )
171+ c .JSON (500 , "failure communicating with node" )
172+ return
173+ }
174+ if cfg .Node .Address != "" && cfg .Node .Port > 0 {
175+ if err := oConn .Dial ("tcp" , fmt .Sprintf ("%s:%d" , cfg .Node .Address , cfg .Node .Port )); err != nil {
176+ logger .Errorf ("failure connecting to node via TCP: %s" , err )
177+ c .JSON (500 , "failure communicating with node" )
178+ return
179+ }
180+ } else {
181+ if err := oConn .Dial ("unix" , cfg .Node .SocketPath ); err != nil {
182+ logger .Errorf ("failure connecting to node via UNIX socket: %s" , err )
183+ c .JSON (500 , "failure communicating with node" )
184+ return
185+ }
186+ }
187+ // Start async error handler
188+ go func () {
189+ err , ok := <- errorChan
190+ if ok {
191+ logger .Errorf ("failure communicating with node: %s" , err )
192+ c .JSON (500 , "failure communicating with node" )
193+ }
194+ }()
195+ defer func () {
196+ // Close Ouroboros connection
197+ oConn .Close ()
198+ }()
199+ hasTx , err := oConn .LocalTxMonitor ().Client .HasTx (cborData )
200+ if err != nil {
201+ logger .Errorf ("failure getting transaction: %s" , err )
202+ c .JSON (500 , fmt .Sprintf ("failure getting transaction: %s" , err ))
203+ }
204+ if ! hasTx {
205+ c .JSON (404 , "transaction not found in mempool" )
206+ return
207+ }
208+ c .JSON (200 , "transaction found in mempool" )
209+ }
210+
123211// handleSubmitTx godoc
212+ //
124213// @Summary Submit Tx
125214// @Description Submit an already serialized transaction to the network.
126215// @Produce json
@@ -217,8 +306,6 @@ func handleSubmitTx(c *gin.Context) {
217306 // Close Ouroboros connection
218307 oConn .Close ()
219308 }()
220- // Start local-tx-submission protocol
221- oConn .LocalTxSubmission ().Client .Start ()
222309 // Determine transaction type (era)
223310 txType , err := determineTransactionType (txRawBytes )
224311 if err != nil {
0 commit comments