Skip to content

Commit a533dc0

Browse files
committed
feat: refactor submit into top-level package
Signed-off-by: Chris Gianelloni <[email protected]>
1 parent eaa1a16 commit a533dc0

File tree

2 files changed

+127
-70
lines changed

2 files changed

+127
-70
lines changed

internal/api/api.go

Lines changed: 27 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,7 @@ import (
2020
"time"
2121

2222
ouroboros "github.com/blinklabs-io/gouroboros"
23-
"github.com/blinklabs-io/gouroboros/ledger"
2423
"github.com/blinklabs-io/gouroboros/protocol/localtxsubmission"
25-
"github.com/blinklabs-io/tx-submit-api/internal/config"
26-
"github.com/blinklabs-io/tx-submit-api/internal/logging"
27-
2824
"github.com/fxamacker/cbor/v2"
2925
ginzap "github.com/gin-contrib/zap"
3026
"github.com/gin-gonic/gin"
@@ -33,6 +29,10 @@ import (
3329
ginSwagger "github.com/swaggo/gin-swagger" // gin-swagger middleware
3430

3531
_ "github.com/blinklabs-io/tx-submit-api/docs" // docs is generated by Swag CLI
32+
"github.com/blinklabs-io/tx-submit-api/internal/config"
33+
"github.com/blinklabs-io/tx-submit-api/internal/logging"
34+
"github.com/blinklabs-io/tx-submit-api/submit"
35+
3636
)
3737

3838
// @title tx-submit-api
@@ -45,8 +45,8 @@ import (
4545
// @contact.url https://blinklabs.io
4646
// @contact.email [email protected]
4747

48-
// @license.name Apache 2.0
49-
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
48+
// @license.name Apache 2.0
49+
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
5050
func Start(cfg *config.Config) error {
5151
// Disable gin debug and color output
5252
gin.SetMode(gin.ReleaseMode)
@@ -259,58 +259,31 @@ func handleSubmitTx(c *gin.Context) {
259259
if err := c.Request.Body.Close(); err != nil {
260260
logger.Errorf("failed to close request body: %s", err)
261261
}
262-
// Determine transaction type (era)
263-
txType, err := ledger.DetermineTransactionType(txRawBytes)
264-
if err != nil {
265-
logger.Errorf("could not parse transaction to determine type: %s", err)
266-
c.JSON(400, "could not parse transaction to determine type")
267-
_ = ginmetrics.GetMonitor().GetMetric("tx_submit_fail_count").Inc(nil)
268-
return
269-
}
270-
tx, err := ledger.NewTransactionFromCbor(txType, txRawBytes)
271-
if err != nil {
272-
logger.Errorf("failed to parse transaction CBOR: %s", err)
273-
c.JSON(400, fmt.Sprintf("failed to parse transaction CBOR: %s", err))
274-
_ = ginmetrics.GetMonitor().GetMetric("tx_submit_fail_count").Inc(nil)
275-
return
276-
}
277-
// Connect to cardano-node and submit TX
262+
// Send TX
278263
errorChan := make(chan error)
279-
oConn, err := ouroboros.NewConnection(
280-
ouroboros.WithNetworkMagic(uint32(cfg.Node.NetworkMagic)),
281-
ouroboros.WithErrorChan(errorChan),
282-
ouroboros.WithNodeToNode(false),
283-
ouroboros.WithLocalTxSubmissionConfig(
284-
localtxsubmission.NewConfig(
285-
localtxsubmission.WithTimeout(
286-
time.Duration(cfg.Node.Timeout)*time.Second,
287-
),
288-
),
289-
),
290-
)
264+
submitConfig := &submit.Config{
265+
ErrorChan: errorChan,
266+
NetworkMagic: cfg.Node.NetworkMagic,
267+
NodeAddress: cfg.Node.Address,
268+
NodePort: cfg.Node.Port,
269+
SocketPath: cfg.Node.SocketPath,
270+
Timeout: cfg.Node.Timeout,
271+
}
272+
txHash, err := submit.SubmitTx(submitConfig, txRawBytes)
291273
if err != nil {
292-
logger.Errorf("failure creating Ouroboros connection: %s", err)
293-
c.JSON(500, "failure communicating with node")
274+
if c.GetHeader("Accept") == "application/cbor" {
275+
txRejectErr := err.(localtxsubmission.TransactionRejectedError)
276+
c.Data(400, "application/cbor", txRejectErr.ReasonCbor)
277+
} else {
278+
if err.Error() != "" {
279+
c.JSON(400, err.Error())
280+
} else {
281+
c.JSON(400, fmt.Sprintf("%s", err))
282+
}
283+
}
294284
_ = ginmetrics.GetMonitor().GetMetric("tx_submit_fail_count").Inc(nil)
295285
return
296286
}
297-
if cfg.Node.Address != "" && cfg.Node.Port > 0 {
298-
if err := oConn.Dial("tcp", fmt.Sprintf("%s:%d", cfg.Node.Address, cfg.Node.Port)); err != nil {
299-
logger.Errorf("failure connecting to node via TCP: %s", err)
300-
c.JSON(500, "failure communicating with node")
301-
_ = ginmetrics.GetMonitor().
302-
GetMetric("tx_submit_fail_count").
303-
Inc(nil)
304-
return
305-
}
306-
} else {
307-
if err := oConn.Dial("unix", cfg.Node.SocketPath); err != nil {
308-
logger.Errorf("failure connecting to node via UNIX socket: %s", err)
309-
c.JSON(500, "failure communicating with node")
310-
_ = ginmetrics.GetMonitor().GetMetric("tx_submit_fail_count").Inc(nil)
311-
return
312-
}
313-
}
314287
// Start async error handler
315288
go func() {
316289
err, ok := <-errorChan
@@ -322,24 +295,8 @@ func handleSubmitTx(c *gin.Context) {
322295
Inc(nil)
323296
}
324297
}()
325-
defer func() {
326-
// Close Ouroboros connection
327-
oConn.Close()
328-
}()
329-
// Submit the transaction
330-
if err := oConn.LocalTxSubmission().Client.SubmitTx(uint16(txType), txRawBytes); err != nil {
331-
if c.GetHeader("Accept") == "application/cbor" {
332-
txRejectErr := err.(localtxsubmission.TransactionRejectedError)
333-
c.Data(400, "application/cbor", txRejectErr.ReasonCbor)
334-
} else {
335-
c.JSON(400, err.Error())
336-
}
337-
// Increment custom metric
338-
_ = ginmetrics.GetMonitor().GetMetric("tx_submit_fail_count").Inc(nil)
339-
return
340-
}
341298
// Return transaction ID
342-
c.JSON(202, tx.Hash())
299+
c.JSON(202, txHash)
343300
// Increment custom metric
344301
_ = ginmetrics.GetMonitor().GetMetric("tx_submit_count").Inc(nil)
345302
}

submit/tx.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright 2023 Blink Labs Software
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package submit
16+
17+
import (
18+
"fmt"
19+
"time"
20+
21+
ouroboros "github.com/blinklabs-io/gouroboros"
22+
"github.com/blinklabs-io/gouroboros/ledger"
23+
"github.com/blinklabs-io/gouroboros/protocol/localtxsubmission"
24+
)
25+
26+
type Config struct {
27+
ErrorChan chan error
28+
Network string
29+
NetworkMagic uint32
30+
NodeAddress string
31+
NodePort uint
32+
SocketPath string
33+
Timeout uint
34+
}
35+
36+
func SubmitTx(cfg *Config, txRawBytes []byte) (string, error) {
37+
// Determine transaction type (era)
38+
txType, err := ledger.DetermineTransactionType(txRawBytes)
39+
if err != nil {
40+
return "", fmt.Errorf("could not parse transaction to determine type: %s", err)
41+
}
42+
tx, err := ledger.NewTransactionFromCbor(txType, txRawBytes)
43+
if err != nil {
44+
return "", fmt.Errorf("failed to parse transaction CBOR: %s", err)
45+
}
46+
47+
err = cfg.populateNetworkMagic()
48+
if err != nil {
49+
return "", fmt.Errorf("failed to populate networkMagic: %s", err)
50+
}
51+
52+
// Connect to cardano-node and submit TX using Ouroboros LocalTxSubmission
53+
oConn, err := ouroboros.NewConnection(
54+
ouroboros.WithNetworkMagic(uint32(cfg.NetworkMagic)),
55+
ouroboros.WithErrorChan(cfg.ErrorChan),
56+
ouroboros.WithNodeToNode(false),
57+
ouroboros.WithLocalTxSubmissionConfig(
58+
localtxsubmission.NewConfig(
59+
localtxsubmission.WithTimeout(
60+
time.Duration(cfg.Timeout)*time.Second,
61+
),
62+
),
63+
),
64+
)
65+
if err != nil {
66+
return "", fmt.Errorf("failure creating Ouroboros connection: %s", err)
67+
}
68+
if cfg.NodeAddress != "" && cfg.NodePort > 0 {
69+
if err := oConn.Dial("tcp", fmt.Sprintf("%s:%d", cfg.NodeAddress, cfg.NodePort)); err != nil {
70+
return "", fmt.Errorf("failure connecting to node via TCP: %s", err)
71+
}
72+
} else {
73+
if err := oConn.Dial("unix", cfg.SocketPath); err != nil {
74+
return "", fmt.Errorf("failure connecting to node via UNIX socket: %s", err)
75+
}
76+
}
77+
defer func() {
78+
// Close Ouroboros connection
79+
oConn.Close()
80+
}()
81+
// Submit the transaction
82+
if err := oConn.LocalTxSubmission().Client.SubmitTx(uint16(txType), txRawBytes); err != nil {
83+
return "", fmt.Errorf("%s", err.Error())
84+
}
85+
return tx.Hash(), nil
86+
}
87+
88+
func (c *Config) populateNetworkMagic() error {
89+
if c.NetworkMagic == 0 {
90+
if c.Network != "" {
91+
network := ouroboros.NetworkByName(c.Network)
92+
if network == ouroboros.NetworkInvalid {
93+
return fmt.Errorf("unknown network: %s", c.Network)
94+
}
95+
c.NetworkMagic = uint32(network.NetworkMagic)
96+
return nil
97+
}
98+
}
99+
return nil
100+
}

0 commit comments

Comments
 (0)