Skip to content

Commit

Permalink
Merge pull request #28 from cloudstruct/feature/local-tx-submission
Browse files Browse the repository at this point in the history
Implement local-tx-submission mini-protocol
  • Loading branch information
agaffney authored Feb 27, 2022
2 parents d6ee6ff + b56dffe commit 2c422e8
Show file tree
Hide file tree
Showing 12 changed files with 451 additions and 51 deletions.
24 changes: 21 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ but the node-to-node protocols will also be implemented in time.
| Chain-Sync | Implemented |
| Block-Fetch | Implemented |
| TxSubmission | Not Implemented |
| Local TxSubmission | Not Implemented |
| Local TxSubmission | Implemented |
| Local State Query | Not Implemented |
| Keep-Alive | Implemented |

Expand Down Expand Up @@ -65,10 +65,28 @@ Compile the test program.
$ make
```

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

```
$ ./go-ouroboros-network -address localhost:8082 -testnet
$ ./go-ouroboros-network -address localhost:8082 -testnet ...
```

Run it against the public port in node-to-node mode.

```
$ ./go-ouroboros-network -address localhost:8081 -ntn -testnet ...
```

Test chain-sync (works in node-to-node and node-to-client modes).

```
$ ./go-ouroboros-network ... chain-sync -start-era byron
```

Test local-tx-submission (only works in node-to-client mode).

```
$ ./go-ouroboros-network ... local-tx-submission ...
```

### Stopping the local `cardano-node` instance
Expand Down
2 changes: 2 additions & 0 deletions block/allegra.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const (
BLOCK_TYPE_ALLEGRA = 3

BLOCK_HEADER_TYPE_ALLEGRA = 2

TX_TYPE_ALLEGRA = 2
)

type AllegraBlock struct {
Expand Down
2 changes: 2 additions & 0 deletions block/alonzo.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const (
BLOCK_TYPE_ALONZO = 5

BLOCK_HEADER_TYPE_ALONZO = 4

TX_TYPE_ALONZO = 4
)

type AlonzoBlock struct {
Expand Down
2 changes: 2 additions & 0 deletions block/byron.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const (
BLOCK_TYPE_BYRON_MAIN = 1

BLOCK_HEADER_TYPE_BYRON = 0

TX_TYPE_BYRON = 0
)

type ByronMainBlockHeader struct {
Expand Down
2 changes: 2 additions & 0 deletions block/mary.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const (
BLOCK_TYPE_MARY = 4

BLOCK_HEADER_TYPE_MARY = 3

TX_TYPE_MARY = 3
)

type MaryBlock struct {
Expand Down
2 changes: 2 additions & 0 deletions block/shelley.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const (
BLOCK_TYPE_SHELLEY = 2

BLOCK_HEADER_TYPE_SHELLEY = 1

TX_TYPE_SHELLEY = 1
)

type ShelleyBlock struct {
Expand Down
33 changes: 27 additions & 6 deletions cmd/go-ouroboros-network/chainsync.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"encoding/hex"
"flag"
"fmt"
ouroboros "github.com/cloudstruct/go-ouroboros-network"
"github.com/cloudstruct/go-ouroboros-network/block"
Expand All @@ -21,6 +22,19 @@ type chainSyncState struct {

var syncState chainSyncState

type chainSyncFlags struct {
flagset *flag.FlagSet
startEra string
}

func newChainSyncFlags() *chainSyncFlags {
f := &chainSyncFlags{
flagset: flag.NewFlagSet("chain-sync", flag.ExitOnError),
}
f.flagset.StringVar(&f.startEra, "start-era", "byron", "era which to start chain-sync at")
return f
}

// Intersect points (last block of previous era) for each era on testnet/mainnet
var eraIntersect = map[int]map[string][]interface{}{
TESTNET_MAGIC: map[string][]interface{}{
Expand Down Expand Up @@ -67,20 +81,27 @@ func buildBlockFetchCallbackConfig() *blockfetch.BlockFetchCallbackConfig {
}
}

func testChainSync(o *ouroboros.Ouroboros, f cmdFlags) {
if _, ok := eraIntersect[f.networkMagic][f.syncEra]; !ok {
fmt.Printf("ERROR: unknown era '%s' specified as chain-sync start point\n", f.syncEra)
func testChainSync(o *ouroboros.Ouroboros, f *globalFlags) {
chainSyncFlags := newChainSyncFlags()
err := chainSyncFlags.flagset.Parse(f.flagset.Args()[1:])
if err != nil {
fmt.Printf("failed to parse subcommand args: %s\n", err)
os.Exit(1)
}

if _, ok := eraIntersect[f.networkMagic][chainSyncFlags.startEra]; !ok {
fmt.Printf("ERROR: unknown era '%s' specified as chain-sync start point\n", chainSyncFlags.startEra)
os.Exit(1)
}
syncState.oConn = o
syncState.readyForNextBlockChan = make(chan bool)
syncState.nodeToNode = f.ntnProto
intersect := []interface{}{}
if len(eraIntersect[f.networkMagic][f.syncEra]) > 0 {
if len(eraIntersect[f.networkMagic][chainSyncFlags.startEra]) > 0 {
// Slot
intersect = append(intersect, eraIntersect[f.networkMagic][f.syncEra][0])
intersect = append(intersect, eraIntersect[f.networkMagic][chainSyncFlags.startEra][0])
// Block hash
hash, _ := hex.DecodeString(eraIntersect[f.networkMagic][f.syncEra][1].(string))
hash, _ := hex.DecodeString(eraIntersect[f.networkMagic][chainSyncFlags.startEra][1].(string))
intersect = append(intersect, hash)
}
if err := o.ChainSync.FindIntersect([]interface{}{intersect}); err != nil {
Expand Down
89 changes: 89 additions & 0 deletions cmd/go-ouroboros-network/localtxsubmission.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package main

import (
"encoding/hex"
"encoding/json"
"flag"
"fmt"
ouroboros "github.com/cloudstruct/go-ouroboros-network"
"github.com/cloudstruct/go-ouroboros-network/block"
"github.com/cloudstruct/go-ouroboros-network/protocol/localtxsubmission"
"io/ioutil"
"os"
)

type localTxSubmissionState struct {
submitResponse chan bool
}

var localTxSubmitState localTxSubmissionState

type localTxSubmissionFlags struct {
flagset *flag.FlagSet
txFile string
}

func newLocalTxSubmissionFlags() *localTxSubmissionFlags {
f := &localTxSubmissionFlags{
flagset: flag.NewFlagSet("local-tx-submission", flag.ExitOnError),
}
f.flagset.StringVar(&f.txFile, "tx-file", "", "path to the transaction file to submit")
return f
}

func buildLocalTxSubmissionCallbackConfig() *localtxsubmission.CallbackConfig {
return &localtxsubmission.CallbackConfig{
AcceptTxFunc: localTxSubmissionAcceptTxHandler,
RejectTxFunc: localTxSubmissionRejectTxHandler,
}
}

func testLocalTxSubmission(o *ouroboros.Ouroboros, f *globalFlags) {
localTxSubmissionFlags := newLocalTxSubmissionFlags()
err := localTxSubmissionFlags.flagset.Parse(f.flagset.Args()[1:])
if err != nil {
fmt.Printf("failed to parse subcommand args: %s\n", err)
os.Exit(1)
}

localTxSubmitState.submitResponse = make(chan bool)

txData, err := ioutil.ReadFile(localTxSubmissionFlags.txFile)
if err != nil {
fmt.Printf("Failed to load transaction file: %s\n", err)
os.Exit(1)
}

var jsonData map[string]string
err = json.Unmarshal(txData, &jsonData)
if err != nil {
fmt.Printf("failed to parse transaction file: %s\n", err)
os.Exit(1)
}

txBytes, err := hex.DecodeString(jsonData["cborHex"])
if err != nil {
fmt.Printf("failed to decode transaction: %s\n", err)
os.Exit(1)
}

if err = o.LocalTxSubmission.SubmitTx(block.TX_TYPE_ALONZO, txBytes); err != nil {
fmt.Printf("Error submitting transaction: %s\n", err)
os.Exit(1)
}

// Wait for response
<-localTxSubmitState.submitResponse
}

func localTxSubmissionAcceptTxHandler() error {
fmt.Print("The transaction was accepted\n")
localTxSubmitState.submitResponse <- true
return nil
}

func localTxSubmissionRejectTxHandler(reason interface{}) error {
fmt.Printf("The transaction was rejected: %#v\n", reason)
os.Exit(1)
return nil
}
65 changes: 45 additions & 20 deletions cmd/go-ouroboros-network/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ const (
MAINNET_MAGIC = 764824073
)

type cmdFlags struct {
type globalFlags struct {
flagset *flag.FlagSet
socket string
address string
useTls bool
Expand All @@ -26,20 +27,30 @@ type cmdFlags struct {
syncEra string
}

func newGlobalFlags() *globalFlags {
f := &globalFlags{
flagset: flag.NewFlagSet(os.Args[0], flag.ExitOnError),
}
f.flagset.StringVar(&f.socket, "socket", "", "UNIX socket path to connect to")
f.flagset.StringVar(&f.address, "address", "", "TCP address to connect to in address:port format")
f.flagset.BoolVar(&f.useTls, "tls", false, "enable TLS")
f.flagset.BoolVar(&f.ntnProto, "ntn", false, "use node-to-node protocol (defaults to node-to-client)")
f.flagset.IntVar(&f.networkMagic, "network-magic", 0, "network magic value")
f.flagset.BoolVar(&f.testnet, "testnet", false, fmt.Sprintf("alias for -network-magic=%d", TESTNET_MAGIC))
f.flagset.BoolVar(&f.mainnet, "mainnet", false, fmt.Sprintf("alias for -network-magic=%d", MAINNET_MAGIC))
f.flagset.StringVar(&f.syncEra, "sync-era", "byron", "era which to start chain-sync at")
return f
}

func main() {
f := cmdFlags{}
flag.StringVar(&f.socket, "socket", "", "UNIX socket path to connect to")
flag.StringVar(&f.address, "address", "", "TCP address to connect to in address:port format")
flag.BoolVar(&f.useTls, "tls", false, "enable TLS")
flag.BoolVar(&f.ntnProto, "ntn", false, "use node-to-node protocol (defaults to node-to-client)")
flag.IntVar(&f.networkMagic, "network-magic", 0, "network magic value")
flag.BoolVar(&f.testnet, "testnet", false, fmt.Sprintf("alias for -network-magic=%d", TESTNET_MAGIC))
flag.BoolVar(&f.mainnet, "mainnet", false, fmt.Sprintf("alias for -network-magic=%d", MAINNET_MAGIC))
flag.StringVar(&f.syncEra, "sync-era", "byron", "era which to start chain-sync at")
flag.Parse()
f := newGlobalFlags()
err := f.flagset.Parse(os.Args[1:])
if err != nil {
fmt.Printf("failed to parse command args: %s\n", err)
os.Exit(1)
}

var conn io.ReadWriteCloser
var err error
var dialProto string
var dialAddress string
if f.socket != "" {
Expand Down Expand Up @@ -75,13 +86,14 @@ func main() {
}
errorChan := make(chan error, 10)
oOpts := &ouroboros.OuroborosOptions{
Conn: conn,
NetworkMagic: uint32(f.networkMagic),
ErrorChan: errorChan,
UseNodeToNodeProtocol: f.ntnProto,
SendKeepAlives: true,
ChainSyncCallbackConfig: buildChainSyncCallbackConfig(),
BlockFetchCallbackConfig: buildBlockFetchCallbackConfig(),
Conn: conn,
NetworkMagic: uint32(f.networkMagic),
ErrorChan: errorChan,
UseNodeToNodeProtocol: f.ntnProto,
SendKeepAlives: true,
ChainSyncCallbackConfig: buildChainSyncCallbackConfig(),
BlockFetchCallbackConfig: buildBlockFetchCallbackConfig(),
LocalTxSubmissionCallbackConfig: buildLocalTxSubmissionCallbackConfig(),
}
go func() {
for {
Expand All @@ -95,5 +107,18 @@ func main() {
fmt.Printf("ERROR: %s\n", err)
os.Exit(1)
}
testChainSync(o, f)
if len(f.flagset.Args()) > 0 {
switch f.flagset.Arg(0) {
case "chain-sync":
testChainSync(o, f)
case "local-tx-submission":
testLocalTxSubmission(o, f)
default:
fmt.Printf("Unknown subcommand: %s\n", f.flagset.Arg(0))
os.Exit(1)
}
} else {
fmt.Printf("You must specify a subcommand (chain-sync or local-tx-submission)\n")
os.Exit(1)
}
}
Loading

0 comments on commit 2c422e8

Please sign in to comment.