From 8d652a14ab684c5f1ac560cfe2b350bb60b84c5b Mon Sep 17 00:00:00 2001 From: Aurora Gaffney Date: Mon, 6 Nov 2023 19:22:39 -0600 Subject: [PATCH] feat: support for parsing a cardano-node topology config --- topology.go | 74 ++++++++++++++++++++++++++ topology_test.go | 135 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 topology.go create mode 100644 topology_test.go diff --git a/topology.go b/topology.go new file mode 100644 index 00000000..c0fd9d56 --- /dev/null +++ b/topology.go @@ -0,0 +1,74 @@ +// Copyright 2023 Blink Labs, LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ouroboros + +import ( + "encoding/json" + "io" + "os" +) + +// TopologyConfig represents a Cardano node topology config +type TopologyConfig struct { + Producers []TopologyConfigLegacyProducer `json:"Producers"` + LocalRoots []TopologyConfigP2PLocalRoot `json:"localRoots"` + PublicRoots []TopologyConfigP2PPublicRoot `json:"publicRoots"` + UseLedgerAfterSlot uint64 `json:"useLedgerAfterSlot"` +} + +type TopologyConfigLegacyProducer struct { + Address string `json:"addr"` + Port uint16 `json:"port"` + Valency uint `json:"valency"` + Continent string `json:"continent"` + State string `json:"state"` +} + +type TopologyConfigP2PAccessPoint struct { + Address string `json:"address"` + Port uint16 `json:"port"` +} + +type TopologyConfigP2PLocalRoot struct { + AccessPoints []TopologyConfigP2PAccessPoint `json:"accessPoints"` + Advertise bool `json:"advertise"` + Valency uint `json:"valency"` +} + +type TopologyConfigP2PPublicRoot struct { + AccessPoints []TopologyConfigP2PAccessPoint `json:"accessPoints"` + Advertise bool `json:"advertise"` + Valency uint `json:"valency"` +} + +func NewTopologyConfigFromFile(path string) (*TopologyConfig, error) { + dataFile, err := os.Open(path) + if err != nil { + return nil, err + } + return NewTopologyConfigFromReader(dataFile) +} + +func NewTopologyConfigFromReader(r io.Reader) (*TopologyConfig, error) { + t := &TopologyConfig{} + data, err := io.ReadAll(r) + if err != nil { + return nil, err + } + if err := json.Unmarshal(data, t); err != nil { + return nil, err + } + return t, nil +} diff --git a/topology_test.go b/topology_test.go new file mode 100644 index 00000000..c3b2c06a --- /dev/null +++ b/topology_test.go @@ -0,0 +1,135 @@ +// Copyright 2023 Blink Labs, LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ouroboros_test + +import ( + "reflect" + "strings" + "testing" + + ouroboros "github.com/blinklabs-io/gouroboros" +) + +type topologyTestDefinition struct { + jsonData string + expectedObject *ouroboros.TopologyConfig +} + +var topologyTests = []topologyTestDefinition{ + { + jsonData: ` +{ + "Producers": [ + { + "addr": "relays-new.cardano-mainnet.iohk.io", + "port": 3001, + "valency": 2 + } + ] +} +`, + expectedObject: &ouroboros.TopologyConfig{ + Producers: []ouroboros.TopologyConfigLegacyProducer{ + { + Address: "relays-new.cardano-mainnet.iohk.io", + Port: 3001, + Valency: 2, + }, + }, + }, + }, + { + jsonData: ` +{ + "localRoots": [ + { + "accessPoints": [], + "advertise": false, + "valency": 1 + } + ], + "publicRoots": [ + { + "accessPoints": [ + { + "address": "backbone.cardano-mainnet.iohk.io", + "port": 3001 + } + ], + "advertise": false + }, + { + "accessPoints": [ + { + "address": "backbone.mainnet.emurgornd.com", + "port": 3001 + } + ], + "advertise": false + } + ], + "useLedgerAfterSlot": 99532743 +} +`, + expectedObject: &ouroboros.TopologyConfig{ + LocalRoots: []ouroboros.TopologyConfigP2PLocalRoot{ + { + AccessPoints: []ouroboros.TopologyConfigP2PAccessPoint{}, + Advertise: false, + Valency: 1, + }, + }, + PublicRoots: []ouroboros.TopologyConfigP2PPublicRoot{ + { + AccessPoints: []ouroboros.TopologyConfigP2PAccessPoint{ + { + Address: "backbone.cardano-mainnet.iohk.io", + Port: 3001, + }, + }, + Advertise: false, + }, + { + AccessPoints: []ouroboros.TopologyConfigP2PAccessPoint{ + { + Address: "backbone.mainnet.emurgornd.com", + Port: 3001, + }, + }, + Advertise: false, + }, + }, + UseLedgerAfterSlot: 99532743, + }, + }, +} + +func TestParseTopologyConfig(t *testing.T) { + for _, test := range topologyTests { + topology, err := ouroboros.NewTopologyConfigFromReader( + strings.NewReader(test.jsonData), + ) + if err != nil { + t.Fatalf("failed to load TopologyConfig from JSON data: %s", err) + } + if !reflect.DeepEqual(topology, test.expectedObject) { + t.Fatalf( + "did not get expected object\n got:\n %#v\n wanted:\n %#v", + topology, + test.expectedObject, + ) + } + } +}