Skip to content

Commit 05e8f9f

Browse files
authored
Merge pull request #108 from blinklabs-io/feat/hastx-endpoint
feat: endpoint to check for transactions in mempool
2 parents c4b6568 + b5e757c commit 05e8f9f

File tree

7 files changed

+233
-10
lines changed

7 files changed

+233
-10
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ build: $(BINARY)
2323
clean:
2424
rm -f $(BINARY)
2525

26+
format: mod-tidy
27+
go fmt ./...
28+
2629
swagger:
2730
swag f -g api.go -d internal/api
2831
swag i -g api.go -d internal/api

docs/docs.go

Lines changed: 50 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/swagger.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,56 @@
1919
},
2020
"basePath": "/",
2121
"paths": {
22+
"/api/hastx/{tx_hash}": {
23+
"get": {
24+
"description": "Determine if a given transaction ID exists in the node mempool.",
25+
"produces": [
26+
"application/json"
27+
],
28+
"summary": "HasTx",
29+
"parameters": [
30+
{
31+
"type": "string",
32+
"description": "Transaction Hash",
33+
"name": "tx_hash",
34+
"in": "path",
35+
"required": true
36+
}
37+
],
38+
"responses": {
39+
"200": {
40+
"description": "Ok",
41+
"schema": {
42+
"type": "string"
43+
}
44+
},
45+
"400": {
46+
"description": "Bad Request",
47+
"schema": {
48+
"type": "string"
49+
}
50+
},
51+
"404": {
52+
"description": "Not Found",
53+
"schema": {
54+
"type": "string"
55+
}
56+
},
57+
"415": {
58+
"description": "Unsupported Media Type",
59+
"schema": {
60+
"type": "string"
61+
}
62+
},
63+
"500": {
64+
"description": "Server Error",
65+
"schema": {
66+
"type": "string"
67+
}
68+
}
69+
}
70+
}
71+
},
2272
"/api/submit/tx": {
2373
"post": {
2474
"description": "Submit an already serialized transaction to the network.",

docs/swagger.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,39 @@ info:
1111
title: tx-submit-api
1212
version: v0
1313
paths:
14+
/api/hastx/{tx_hash}:
15+
get:
16+
description: Determine if a given transaction ID exists in the node mempool.
17+
parameters:
18+
- description: Transaction Hash
19+
in: path
20+
name: tx_hash
21+
required: true
22+
type: string
23+
produces:
24+
- application/json
25+
responses:
26+
"200":
27+
description: Ok
28+
schema:
29+
type: string
30+
"400":
31+
description: Bad Request
32+
schema:
33+
type: string
34+
"404":
35+
description: Not Found
36+
schema:
37+
type: string
38+
"415":
39+
description: Unsupported Media Type
40+
schema:
41+
type: string
42+
"500":
43+
description: Server Error
44+
schema:
45+
type: string
46+
summary: HasTx
1447
/api/submit/tx:
1548
post:
1649
description: Submit an already serialized transaction to the network.

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ module github.com/blinklabs-io/tx-submit-api
33
go 1.19
44

55
require (
6-
github.com/blinklabs-io/gouroboros v0.40.1
6+
github.com/blinklabs-io/gouroboros v0.41.0
77
github.com/fxamacker/cbor/v2 v2.4.0
88
github.com/gin-contrib/zap v0.1.0
99
github.com/gin-gonic/gin v1.9.0
1010
github.com/kelseyhightower/envconfig v1.4.0
1111
github.com/penglongli/gin-metrics v0.1.10
1212
github.com/swaggo/files v1.0.1
1313
github.com/swaggo/gin-swagger v1.6.0
14-
github.com/swaggo/swag v1.16.1
14+
github.com/swaggo/swag v1.8.12
1515
go.uber.org/zap v1.24.0
1616
golang.org/x/crypto v0.9.0
1717
gopkg.in/yaml.v2 v2.4.0

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
4848
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
4949
github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
5050
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
51-
github.com/blinklabs-io/gouroboros v0.40.1 h1:+aioKYE7o+Ekgo9VLD4IIRXeryP9pUR3IhasDR6+jcM=
52-
github.com/blinklabs-io/gouroboros v0.40.1/go.mod h1:YNHQqwU1yv620T5C+umkDmYf8BgBHlm6QpI9LUQ3Jhw=
51+
github.com/blinklabs-io/gouroboros v0.41.0 h1:2HbpHtxnn7uJj8sxNpzltVSMqqMOY+myogjwrXLKHps=
52+
github.com/blinklabs-io/gouroboros v0.41.0/go.mod h1:YNHQqwU1yv620T5C+umkDmYf8BgBHlm6QpI9LUQ3Jhw=
5353
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
5454
github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
5555
github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
@@ -296,8 +296,8 @@ github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
296296
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
297297
github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
298298
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
299-
github.com/swaggo/swag v1.16.1 h1:fTNRhKstPKxcnoKsytm4sahr8FaYzUcT7i1/3nd/fBg=
300-
github.com/swaggo/swag v1.16.1/go.mod h1:9/LMvHycG3NFHfR6LwvikHv5iFvmPADQ359cKikGxto=
299+
github.com/swaggo/swag v1.8.12 h1:pctzkNPu0AlQP2royqX3apjKCQonAnf7KGoxeO4y64w=
300+
github.com/swaggo/swag v1.8.12/go.mod h1:lNfm6Gg+oAq3zRJQNEMBE66LIJKM44mxFqhEEgy2its=
301301
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
302302
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
303303
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=

internal/api/api.go

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)