Skip to content

Commit b56dffe

Browse files
committed
Implement local-tx-submission mini-protocol
* split test program into subcommands * update manual testing instructions Fixes #9
1 parent d6ee6ff commit b56dffe

File tree

12 files changed

+451
-51
lines changed

12 files changed

+451
-51
lines changed

README.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ but the node-to-node protocols will also be implemented in time.
2222
| Chain-Sync | Implemented |
2323
| Block-Fetch | Implemented |
2424
| TxSubmission | Not Implemented |
25-
| Local TxSubmission | Not Implemented |
25+
| Local TxSubmission | Implemented |
2626
| Local State Query | Not Implemented |
2727
| Keep-Alive | Implemented |
2828

@@ -65,10 +65,28 @@ Compile the test program.
6565
$ make
6666
```
6767

68-
Run the test program pointing to the UNIX socket from the `cardano-node` instance started above.
68+
Run the test program pointing to the UNIX socket (via `socat`) from the `cardano-node` instance started above.
6969

7070
```
71-
$ ./go-ouroboros-network -address localhost:8082 -testnet
71+
$ ./go-ouroboros-network -address localhost:8082 -testnet ...
72+
```
73+
74+
Run it against the public port in node-to-node mode.
75+
76+
```
77+
$ ./go-ouroboros-network -address localhost:8081 -ntn -testnet ...
78+
```
79+
80+
Test chain-sync (works in node-to-node and node-to-client modes).
81+
82+
```
83+
$ ./go-ouroboros-network ... chain-sync -start-era byron
84+
```
85+
86+
Test local-tx-submission (only works in node-to-client mode).
87+
88+
```
89+
$ ./go-ouroboros-network ... local-tx-submission ...
7290
```
7391

7492
### Stopping the local `cardano-node` instance

block/allegra.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const (
88
BLOCK_TYPE_ALLEGRA = 3
99

1010
BLOCK_HEADER_TYPE_ALLEGRA = 2
11+
12+
TX_TYPE_ALLEGRA = 2
1113
)
1214

1315
type AllegraBlock struct {

block/alonzo.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const (
88
BLOCK_TYPE_ALONZO = 5
99

1010
BLOCK_HEADER_TYPE_ALONZO = 4
11+
12+
TX_TYPE_ALONZO = 4
1113
)
1214

1315
type AlonzoBlock struct {

block/byron.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const (
99
BLOCK_TYPE_BYRON_MAIN = 1
1010

1111
BLOCK_HEADER_TYPE_BYRON = 0
12+
13+
TX_TYPE_BYRON = 0
1214
)
1315

1416
type ByronMainBlockHeader struct {

block/mary.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const (
88
BLOCK_TYPE_MARY = 4
99

1010
BLOCK_HEADER_TYPE_MARY = 3
11+
12+
TX_TYPE_MARY = 3
1113
)
1214

1315
type MaryBlock struct {

block/shelley.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const (
88
BLOCK_TYPE_SHELLEY = 2
99

1010
BLOCK_HEADER_TYPE_SHELLEY = 1
11+
12+
TX_TYPE_SHELLEY = 1
1113
)
1214

1315
type ShelleyBlock struct {

cmd/go-ouroboros-network/chainsync.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"encoding/hex"
5+
"flag"
56
"fmt"
67
ouroboros "github.com/cloudstruct/go-ouroboros-network"
78
"github.com/cloudstruct/go-ouroboros-network/block"
@@ -21,6 +22,19 @@ type chainSyncState struct {
2122

2223
var syncState chainSyncState
2324

25+
type chainSyncFlags struct {
26+
flagset *flag.FlagSet
27+
startEra string
28+
}
29+
30+
func newChainSyncFlags() *chainSyncFlags {
31+
f := &chainSyncFlags{
32+
flagset: flag.NewFlagSet("chain-sync", flag.ExitOnError),
33+
}
34+
f.flagset.StringVar(&f.startEra, "start-era", "byron", "era which to start chain-sync at")
35+
return f
36+
}
37+
2438
// Intersect points (last block of previous era) for each era on testnet/mainnet
2539
var eraIntersect = map[int]map[string][]interface{}{
2640
TESTNET_MAGIC: map[string][]interface{}{
@@ -67,20 +81,27 @@ func buildBlockFetchCallbackConfig() *blockfetch.BlockFetchCallbackConfig {
6781
}
6882
}
6983

70-
func testChainSync(o *ouroboros.Ouroboros, f cmdFlags) {
71-
if _, ok := eraIntersect[f.networkMagic][f.syncEra]; !ok {
72-
fmt.Printf("ERROR: unknown era '%s' specified as chain-sync start point\n", f.syncEra)
84+
func testChainSync(o *ouroboros.Ouroboros, f *globalFlags) {
85+
chainSyncFlags := newChainSyncFlags()
86+
err := chainSyncFlags.flagset.Parse(f.flagset.Args()[1:])
87+
if err != nil {
88+
fmt.Printf("failed to parse subcommand args: %s\n", err)
89+
os.Exit(1)
90+
}
91+
92+
if _, ok := eraIntersect[f.networkMagic][chainSyncFlags.startEra]; !ok {
93+
fmt.Printf("ERROR: unknown era '%s' specified as chain-sync start point\n", chainSyncFlags.startEra)
7394
os.Exit(1)
7495
}
7596
syncState.oConn = o
7697
syncState.readyForNextBlockChan = make(chan bool)
7798
syncState.nodeToNode = f.ntnProto
7899
intersect := []interface{}{}
79-
if len(eraIntersect[f.networkMagic][f.syncEra]) > 0 {
100+
if len(eraIntersect[f.networkMagic][chainSyncFlags.startEra]) > 0 {
80101
// Slot
81-
intersect = append(intersect, eraIntersect[f.networkMagic][f.syncEra][0])
102+
intersect = append(intersect, eraIntersect[f.networkMagic][chainSyncFlags.startEra][0])
82103
// Block hash
83-
hash, _ := hex.DecodeString(eraIntersect[f.networkMagic][f.syncEra][1].(string))
104+
hash, _ := hex.DecodeString(eraIntersect[f.networkMagic][chainSyncFlags.startEra][1].(string))
84105
intersect = append(intersect, hash)
85106
}
86107
if err := o.ChainSync.FindIntersect([]interface{}{intersect}); err != nil {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package main
2+
3+
import (
4+
"encoding/hex"
5+
"encoding/json"
6+
"flag"
7+
"fmt"
8+
ouroboros "github.com/cloudstruct/go-ouroboros-network"
9+
"github.com/cloudstruct/go-ouroboros-network/block"
10+
"github.com/cloudstruct/go-ouroboros-network/protocol/localtxsubmission"
11+
"io/ioutil"
12+
"os"
13+
)
14+
15+
type localTxSubmissionState struct {
16+
submitResponse chan bool
17+
}
18+
19+
var localTxSubmitState localTxSubmissionState
20+
21+
type localTxSubmissionFlags struct {
22+
flagset *flag.FlagSet
23+
txFile string
24+
}
25+
26+
func newLocalTxSubmissionFlags() *localTxSubmissionFlags {
27+
f := &localTxSubmissionFlags{
28+
flagset: flag.NewFlagSet("local-tx-submission", flag.ExitOnError),
29+
}
30+
f.flagset.StringVar(&f.txFile, "tx-file", "", "path to the transaction file to submit")
31+
return f
32+
}
33+
34+
func buildLocalTxSubmissionCallbackConfig() *localtxsubmission.CallbackConfig {
35+
return &localtxsubmission.CallbackConfig{
36+
AcceptTxFunc: localTxSubmissionAcceptTxHandler,
37+
RejectTxFunc: localTxSubmissionRejectTxHandler,
38+
}
39+
}
40+
41+
func testLocalTxSubmission(o *ouroboros.Ouroboros, f *globalFlags) {
42+
localTxSubmissionFlags := newLocalTxSubmissionFlags()
43+
err := localTxSubmissionFlags.flagset.Parse(f.flagset.Args()[1:])
44+
if err != nil {
45+
fmt.Printf("failed to parse subcommand args: %s\n", err)
46+
os.Exit(1)
47+
}
48+
49+
localTxSubmitState.submitResponse = make(chan bool)
50+
51+
txData, err := ioutil.ReadFile(localTxSubmissionFlags.txFile)
52+
if err != nil {
53+
fmt.Printf("Failed to load transaction file: %s\n", err)
54+
os.Exit(1)
55+
}
56+
57+
var jsonData map[string]string
58+
err = json.Unmarshal(txData, &jsonData)
59+
if err != nil {
60+
fmt.Printf("failed to parse transaction file: %s\n", err)
61+
os.Exit(1)
62+
}
63+
64+
txBytes, err := hex.DecodeString(jsonData["cborHex"])
65+
if err != nil {
66+
fmt.Printf("failed to decode transaction: %s\n", err)
67+
os.Exit(1)
68+
}
69+
70+
if err = o.LocalTxSubmission.SubmitTx(block.TX_TYPE_ALONZO, txBytes); err != nil {
71+
fmt.Printf("Error submitting transaction: %s\n", err)
72+
os.Exit(1)
73+
}
74+
75+
// Wait for response
76+
<-localTxSubmitState.submitResponse
77+
}
78+
79+
func localTxSubmissionAcceptTxHandler() error {
80+
fmt.Print("The transaction was accepted\n")
81+
localTxSubmitState.submitResponse <- true
82+
return nil
83+
}
84+
85+
func localTxSubmissionRejectTxHandler(reason interface{}) error {
86+
fmt.Printf("The transaction was rejected: %#v\n", reason)
87+
os.Exit(1)
88+
return nil
89+
}

cmd/go-ouroboros-network/main.go

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ const (
1515
MAINNET_MAGIC = 764824073
1616
)
1717

18-
type cmdFlags struct {
18+
type globalFlags struct {
19+
flagset *flag.FlagSet
1920
socket string
2021
address string
2122
useTls bool
@@ -26,20 +27,30 @@ type cmdFlags struct {
2627
syncEra string
2728
}
2829

30+
func newGlobalFlags() *globalFlags {
31+
f := &globalFlags{
32+
flagset: flag.NewFlagSet(os.Args[0], flag.ExitOnError),
33+
}
34+
f.flagset.StringVar(&f.socket, "socket", "", "UNIX socket path to connect to")
35+
f.flagset.StringVar(&f.address, "address", "", "TCP address to connect to in address:port format")
36+
f.flagset.BoolVar(&f.useTls, "tls", false, "enable TLS")
37+
f.flagset.BoolVar(&f.ntnProto, "ntn", false, "use node-to-node protocol (defaults to node-to-client)")
38+
f.flagset.IntVar(&f.networkMagic, "network-magic", 0, "network magic value")
39+
f.flagset.BoolVar(&f.testnet, "testnet", false, fmt.Sprintf("alias for -network-magic=%d", TESTNET_MAGIC))
40+
f.flagset.BoolVar(&f.mainnet, "mainnet", false, fmt.Sprintf("alias for -network-magic=%d", MAINNET_MAGIC))
41+
f.flagset.StringVar(&f.syncEra, "sync-era", "byron", "era which to start chain-sync at")
42+
return f
43+
}
44+
2945
func main() {
30-
f := cmdFlags{}
31-
flag.StringVar(&f.socket, "socket", "", "UNIX socket path to connect to")
32-
flag.StringVar(&f.address, "address", "", "TCP address to connect to in address:port format")
33-
flag.BoolVar(&f.useTls, "tls", false, "enable TLS")
34-
flag.BoolVar(&f.ntnProto, "ntn", false, "use node-to-node protocol (defaults to node-to-client)")
35-
flag.IntVar(&f.networkMagic, "network-magic", 0, "network magic value")
36-
flag.BoolVar(&f.testnet, "testnet", false, fmt.Sprintf("alias for -network-magic=%d", TESTNET_MAGIC))
37-
flag.BoolVar(&f.mainnet, "mainnet", false, fmt.Sprintf("alias for -network-magic=%d", MAINNET_MAGIC))
38-
flag.StringVar(&f.syncEra, "sync-era", "byron", "era which to start chain-sync at")
39-
flag.Parse()
46+
f := newGlobalFlags()
47+
err := f.flagset.Parse(os.Args[1:])
48+
if err != nil {
49+
fmt.Printf("failed to parse command args: %s\n", err)
50+
os.Exit(1)
51+
}
4052

4153
var conn io.ReadWriteCloser
42-
var err error
4354
var dialProto string
4455
var dialAddress string
4556
if f.socket != "" {
@@ -75,13 +86,14 @@ func main() {
7586
}
7687
errorChan := make(chan error, 10)
7788
oOpts := &ouroboros.OuroborosOptions{
78-
Conn: conn,
79-
NetworkMagic: uint32(f.networkMagic),
80-
ErrorChan: errorChan,
81-
UseNodeToNodeProtocol: f.ntnProto,
82-
SendKeepAlives: true,
83-
ChainSyncCallbackConfig: buildChainSyncCallbackConfig(),
84-
BlockFetchCallbackConfig: buildBlockFetchCallbackConfig(),
89+
Conn: conn,
90+
NetworkMagic: uint32(f.networkMagic),
91+
ErrorChan: errorChan,
92+
UseNodeToNodeProtocol: f.ntnProto,
93+
SendKeepAlives: true,
94+
ChainSyncCallbackConfig: buildChainSyncCallbackConfig(),
95+
BlockFetchCallbackConfig: buildBlockFetchCallbackConfig(),
96+
LocalTxSubmissionCallbackConfig: buildLocalTxSubmissionCallbackConfig(),
8597
}
8698
go func() {
8799
for {
@@ -95,5 +107,18 @@ func main() {
95107
fmt.Printf("ERROR: %s\n", err)
96108
os.Exit(1)
97109
}
98-
testChainSync(o, f)
110+
if len(f.flagset.Args()) > 0 {
111+
switch f.flagset.Arg(0) {
112+
case "chain-sync":
113+
testChainSync(o, f)
114+
case "local-tx-submission":
115+
testLocalTxSubmission(o, f)
116+
default:
117+
fmt.Printf("Unknown subcommand: %s\n", f.flagset.Arg(0))
118+
os.Exit(1)
119+
}
120+
} else {
121+
fmt.Printf("You must specify a subcommand (chain-sync or local-tx-submission)\n")
122+
os.Exit(1)
123+
}
99124
}

0 commit comments

Comments
 (0)