diff --git a/cmd/gossamer/config.go b/cmd/gossamer/config.go
index 3f0abca651..9907abdaa8 100644
--- a/cmd/gossamer/config.go
+++ b/cmd/gossamer/config.go
@@ -407,6 +407,7 @@ func setDotGlobalConfig(ctx *cli.Context, tomlCfg *ctoml.Config, cfg *dot.Global
cfg.MetricsPort = tomlCfg.Global.MetricsPort
}
+ // TODO: generate random name if one is not assigned (see issue #1496)
// check --name flag and update node configuration
if name := ctx.GlobalString(NameFlag.Name); name != "" {
cfg.Name = name
@@ -442,6 +443,8 @@ func setDotGlobalConfig(ctx *cli.Context, tomlCfg *ctoml.Config, cfg *dot.Global
cfg.MetricsPort = uint32(metricsPort)
}
+ cfg.NoTelemetry = ctx.Bool("no-telemetry")
+
logger.Debug(
"global configuration",
"name", cfg.Name,
diff --git a/cmd/gossamer/config_test.go b/cmd/gossamer/config_test.go
index f9fa04c0d4..c0485bc8a5 100644
--- a/cmd/gossamer/config_test.go
+++ b/cmd/gossamer/config_test.go
@@ -222,6 +222,20 @@ func TestGlobalConfigFromFlags(t *testing.T) {
MetricsPort: uint32(9871),
},
},
+ {
+ "Test gossamer --no-telemetry",
+ []string{"config", "no-telemetry", "name"},
+ []interface{}{testCfgFile.Name(), true, testCfg.Global.Name},
+ dot.GlobalConfig{
+ Name: testCfg.Global.Name,
+ ID: testCfg.Global.ID,
+ BasePath: testCfg.Global.BasePath,
+ LogLvl: log.LvlInfo,
+ PublishMetrics: testCfg.Global.PublishMetrics,
+ MetricsPort: testCfg.Global.MetricsPort,
+ NoTelemetry: true,
+ },
+ },
}
for _, c := range testcases {
diff --git a/cmd/gossamer/flags.go b/cmd/gossamer/flags.go
index 0a877af8a5..8708433a73 100644
--- a/cmd/gossamer/flags.go
+++ b/cmd/gossamer/flags.go
@@ -98,6 +98,12 @@ var (
Name: "metrics-port",
Usage: "Set metric listening port ",
}
+
+ // NoTelemetryFlag stops publishing telemetry to default defined in genesis.json
+ NoTelemetryFlag = cli.BoolFlag{
+ Name: "no-telemetry",
+ Usage: "Disable connecting to the Substrate telemetry server",
+ }
)
// Initialization-only flags
@@ -295,6 +301,9 @@ var (
// metrics flag
PublishMetricsFlag,
MetricsPortFlag,
+
+ // telemetry flags
+ NoTelemetryFlag,
}
)
diff --git a/dot/config.go b/dot/config.go
index 73d17e0880..57d1e14634 100644
--- a/dot/config.go
+++ b/dot/config.go
@@ -50,6 +50,7 @@ type GlobalConfig struct {
LogLvl log.Lvl
PublishMetrics bool
MetricsPort uint32
+ NoTelemetry bool
}
// LogConfig represents the log levels for individual packages
diff --git a/dot/node.go b/dot/node.go
index b4de844251..cc11ef1330 100644
--- a/dot/node.go
+++ b/dot/node.go
@@ -23,21 +23,23 @@ import (
"os/signal"
"path"
"runtime/debug"
+ "strconv"
"sync"
"syscall"
+ "time"
+ "github.com/ChainSafe/chaindb"
gssmrmetrics "github.com/ChainSafe/gossamer/dot/metrics"
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/state"
+ "github.com/ChainSafe/gossamer/dot/telemetry"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/genesis"
"github.com/ChainSafe/gossamer/lib/keystore"
"github.com/ChainSafe/gossamer/lib/services"
+ log "github.com/ChainSafe/log15"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/metrics/prometheus"
-
- "github.com/ChainSafe/chaindb"
- log "github.com/ChainSafe/log15"
)
var logger = log.New("pkg", "dot")
@@ -306,6 +308,28 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore, stopFunc func()) (*Node,
publishMetrics(cfg)
}
+ gd, err := stateSrvc.Storage.GetGenesisData()
+ if err != nil {
+ return nil, err
+ }
+
+ if cfg.Global.NoTelemetry {
+ return node, nil
+ }
+
+ telemetry.GetInstance().AddConnections(gd.TelemetryEndpoints)
+ data := &telemetry.ConnectionData{
+ Authority: cfg.Core.GrandpaAuthority,
+ Chain: sysSrvc.ChainName(),
+ GenesisHash: stateSrvc.Block.GenesisHash().String(),
+ SystemName: sysSrvc.SystemName(),
+ NodeName: cfg.Global.Name,
+ SystemVersion: sysSrvc.SystemVersion(),
+ NetworkID: networkSrvc.NetworkState().PeerID,
+ StartTime: strconv.FormatInt(time.Now().UnixNano(), 10),
+ }
+ telemetry.GetInstance().SendConnection(data)
+
return node, nil
}
diff --git a/dot/rpc/modules/api.go b/dot/rpc/modules/api.go
index 91f531df2e..28eea721d5 100644
--- a/dot/rpc/modules/api.go
+++ b/dot/rpc/modules/api.go
@@ -90,7 +90,7 @@ type RuntimeAPI interface {
type SystemAPI interface {
SystemName() string
SystemVersion() string
- NodeName() string
Properties() map[string]interface{}
ChainType() string
+ ChainName() string
}
diff --git a/dot/rpc/modules/system.go b/dot/rpc/modules/system.go
index 4123a470d3..7831bea61e 100644
--- a/dot/rpc/modules/system.go
+++ b/dot/rpc/modules/system.go
@@ -83,7 +83,7 @@ func NewSystemModule(net NetworkAPI, sys SystemAPI, core CoreAPI,
// Chain returns the runtime chain
func (sm *SystemModule) Chain(r *http.Request, req *EmptyRequest, res *string) error {
- *res = sm.systemAPI.NodeName()
+ *res = sm.systemAPI.ChainName()
return nil
}
@@ -161,7 +161,7 @@ func (sm *SystemModule) NodeRoles(r *http.Request, req *EmptyRequest, res *[]int
// AccountNextIndex Returns the next valid index (aka. nonce) for given account.
func (sm *SystemModule) AccountNextIndex(r *http.Request, req *StringRequest, res *U64Response) error {
if req == nil || len(req.String) == 0 {
- return errors.New("Account address must be valid")
+ return errors.New("account address must be valid")
}
addressPubKey := crypto.PublicAddressToByteArray(common.Address(req.String))
diff --git a/dot/rpc/modules/system_test.go b/dot/rpc/modules/system_test.go
index c1f982530c..98c52935a1 100644
--- a/dot/rpc/modules/system_test.go
+++ b/dot/rpc/modules/system_test.go
@@ -210,10 +210,9 @@ func (api *mockSystemAPI) SystemVersion() string {
return api.info.SystemVersion
}
-func (api *mockSystemAPI) NodeName() string {
+func (api *mockSystemAPI) ChainName() string {
return api.genData.Name
}
-
func (api *mockSystemAPI) Properties() map[string]interface{} {
return nil
}
diff --git a/dot/sync/syncer.go b/dot/sync/syncer.go
index b04aadb864..dcae7dc8ae 100644
--- a/dot/sync/syncer.go
+++ b/dot/sync/syncer.go
@@ -24,12 +24,12 @@ import (
"os"
"github.com/ChainSafe/gossamer/dot/network"
+ "github.com/ChainSafe/gossamer/dot/telemetry"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/blocktree"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/runtime"
rtstorage "github.com/ChainSafe/gossamer/lib/runtime/storage"
-
log "github.com/ChainSafe/log15"
)
@@ -339,6 +339,7 @@ func (s *Service) handleBlock(block *types.Block) error {
}
} else {
logger.Debug("🔗 imported block", "number", block.Header.Number, "hash", block.Header.Hash())
+ telemetry.GetInstance().SendBlockImport(block.Header.Hash().String(), block.Header.Number)
}
// handle consensus digest for authority changes
diff --git a/dot/system/service.go b/dot/system/service.go
index 9d08f7fd48..8ce3b647f7 100644
--- a/dot/system/service.go
+++ b/dot/system/service.go
@@ -50,8 +50,8 @@ func (s *Service) SystemVersion() string {
return s.systemInfo.SystemVersion
}
-// NodeName returns the nodeName (chain name)
-func (s *Service) NodeName() string {
+// ChainName returns the chain name defined in genesis.json
+func (s *Service) ChainName() string {
return s.genesisData.Name
}
diff --git a/dot/system/service_test.go b/dot/system/service_test.go
index 5e49d6cfed..cd0aa0f9cc 100644
--- a/dot/system/service_test.go
+++ b/dot/system/service_test.go
@@ -24,10 +24,10 @@ import (
"github.com/stretchr/testify/require"
)
-func TestService_NodeName(t *testing.T) {
+func TestService_ChainName(t *testing.T) {
svc := newTestService()
- name := svc.NodeName()
+ name := svc.ChainName()
require.Equal(t, "gssmr", name)
}
diff --git a/dot/telemetry/telemetry.go b/dot/telemetry/telemetry.go
new file mode 100644
index 0000000000..4577a4a1f3
--- /dev/null
+++ b/dot/telemetry/telemetry.go
@@ -0,0 +1,123 @@
+// Copyright 2021 ChainSafe Systems (ON) Corp.
+// This file is part of gossamer.
+//
+// The gossamer library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The gossamer library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the gossamer library. If not, see .
+package telemetry
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "math/big"
+ "sync"
+ "time"
+
+ "github.com/ChainSafe/gossamer/lib/genesis"
+ "github.com/gorilla/websocket"
+ log "github.com/sirupsen/logrus"
+)
+
+// Handler struct for holding telemetry related things
+type Handler struct {
+ buf bytes.Buffer
+ wsConn []*websocket.Conn
+ telemetryLogger *log.Entry
+}
+
+// MyJSONFormatter struct for defining JSON Formatter
+type MyJSONFormatter struct {
+}
+
+// Format function for handling JSON formatting, this overrides default logging formatter to remove
+// log level, line number and timestamp
+func (f *MyJSONFormatter) Format(entry *log.Entry) ([]byte, error) {
+ serialized, err := json.Marshal(entry.Data)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
+ }
+ return append(serialized, '\n'), nil
+}
+
+var (
+ once sync.Once
+ handlerInstance *Handler
+)
+
+// GetInstance singleton pattern to for accessing TelemetryHandler
+func GetInstance() *Handler {
+ if handlerInstance == nil {
+ once.Do(
+ func() {
+ handlerInstance = &Handler{
+ buf: bytes.Buffer{},
+ }
+ log.SetOutput(&handlerInstance.buf)
+ log.SetFormatter(new(MyJSONFormatter))
+ })
+ }
+ return handlerInstance
+}
+
+// AddConnections adds connections to telemetry sever
+func (h *Handler) AddConnections(conns []*genesis.TelemetryEndpoint) {
+ for _, v := range conns {
+ c, _, err := websocket.DefaultDialer.Dial(v.Endpoint, nil)
+ if err != nil {
+ fmt.Printf("Error %v\n", err)
+ return
+ }
+ h.wsConn = append(h.wsConn, c)
+ }
+}
+
+// ConnectionData struct to hold connection data
+type ConnectionData struct {
+ Authority bool
+ Chain string
+ GenesisHash string
+ SystemName string
+ NodeName string
+ SystemVersion string
+ NetworkID string
+ StartTime string
+}
+
+// SendConnection sends connection request message to telemetry connection
+func (h *Handler) SendConnection(data *ConnectionData) {
+ payload := log.Fields{"authority": data.Authority, "chain": data.Chain, "config": "", "genesis_hash": data.GenesisHash,
+ "implementation": data.SystemName, "msg": "system.connected", "name": data.NodeName, "network_id": data.NetworkID, "startup_time": data.StartTime,
+ "version": data.SystemVersion}
+ h.telemetryLogger = log.WithFields(log.Fields{"id": 1, "payload": payload, "ts": time.Now()})
+ h.telemetryLogger.Print()
+ h.sendTelemtry()
+}
+
+// SendBlockImport sends block imported message to telemetry connection
+func (h *Handler) SendBlockImport(bestHash string, height *big.Int) {
+ payload := log.Fields{"best": bestHash, "height": height.Int64(), "msg": "block.import", "origin": "NetworkInitialSync"}
+ h.telemetryLogger = log.WithFields(log.Fields{"id": 1, "payload": payload, "ts": time.Now()})
+ h.telemetryLogger.Print()
+ h.sendTelemtry()
+}
+
+func (h *Handler) sendTelemtry() {
+ for _, c := range h.wsConn {
+ err := c.WriteMessage(websocket.TextMessage, h.buf.Bytes())
+ if err != nil {
+ // TODO (ed) determine how to handle this error
+ fmt.Printf("ERROR connecting to telemetry %v\n", err)
+ }
+ }
+ h.buf.Reset()
+}
diff --git a/dot/telemetry/telemetry_test.go b/dot/telemetry/telemetry_test.go
new file mode 100644
index 0000000000..d8a505f6cc
--- /dev/null
+++ b/dot/telemetry/telemetry_test.go
@@ -0,0 +1,78 @@
+package telemetry
+
+import (
+ "log"
+ "math/big"
+ "net/http"
+ "os"
+ "testing"
+ "time"
+
+ "github.com/ChainSafe/gossamer/lib/genesis"
+ "github.com/gorilla/websocket"
+ "github.com/stretchr/testify/require"
+)
+
+var upgrader = websocket.Upgrader{}
+var lastMessage []byte
+
+func TestMain(m *testing.M) {
+ // start server to listen for websocket connections
+ upgrader.CheckOrigin = func(r *http.Request) bool { return true }
+ http.HandleFunc("/", listen)
+ go http.ListenAndServe("127.0.0.1:8001", nil)
+
+ time.Sleep(time.Millisecond)
+ // instantiate telemetry to connect to websocket (test) server
+ var testEndpoints []*genesis.TelemetryEndpoint
+ var testEndpoint1 = &genesis.TelemetryEndpoint{
+ Endpoint: "ws://127.0.0.1:8001/",
+ Verbosity: 0,
+ }
+ GetInstance().AddConnections(append(testEndpoints, testEndpoint1))
+
+ // Start all tests
+ code := m.Run()
+ os.Exit(code)
+}
+func TestHandler_SendConnection(t *testing.T) {
+ expected := []byte(`{"id":1,"payload":{"authority":false,"chain":"chain","config":"","genesis_hash":"hash","implementation":"systemName","msg":"system.connected","name":"nodeName","network_id":"netID","startup_time":"startTime","version":"version"},"ts":`)
+ data := &ConnectionData{
+ Authority: false,
+ Chain: "chain",
+ GenesisHash: "hash",
+ SystemName: "systemName",
+ NodeName: "nodeName",
+ SystemVersion: "version",
+ NetworkID: "netID",
+ StartTime: "startTime",
+ }
+ GetInstance().SendConnection(data)
+ time.Sleep(time.Millisecond)
+ // note, we only check the first 234 bytes because the remaining bytes are the timestamp, which we can't estimate
+ require.Equal(t, expected, lastMessage[:234])
+}
+
+func TestHandler_SendBlockImport(t *testing.T) {
+ expected := []byte(`{"id":1,"payload":{"best":"hash","height":2,"msg":"block.import","origin":"NetworkInitialSync"},"ts":`)
+ GetInstance().SendBlockImport("hash", big.NewInt(2))
+ time.Sleep(time.Millisecond)
+ // note, we only check the first 101 bytes because the remaining bytes are the timestamp, which we can't estimate
+ require.Equal(t, expected, lastMessage[:101])
+}
+
+func listen(w http.ResponseWriter, r *http.Request) {
+ c, err := upgrader.Upgrade(w, r, nil)
+ if err != nil {
+ log.Printf("Error %v\n", err)
+ }
+ defer c.Close()
+ for {
+ _, msg, err := c.ReadMessage()
+ if err != nil {
+ log.Printf("read err %v", err)
+ break
+ }
+ lastMessage = msg
+ }
+}
diff --git a/go.mod b/go.mod
index 9cd50dc071..a38499f774 100644
--- a/go.mod
+++ b/go.mod
@@ -46,6 +46,7 @@ require (
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416
github.com/perlin-network/life v0.0.0-20191203030451-05c0e0f7eaea
github.com/rs/cors v1.7.0 // indirect
+ github.com/sirupsen/logrus v1.2.0
github.com/stretchr/testify v1.7.0
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca // indirect
github.com/urfave/cli v1.20.0
@@ -56,7 +57,7 @@ require (
golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
- golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd // indirect
+ golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd
google.golang.org/appengine v1.6.5 // indirect
google.golang.org/protobuf v1.25.0
)
diff --git a/go.sum b/go.sum
index 4f336bffd9..d838e5b9da 100644
--- a/go.sum
+++ b/go.sum
@@ -264,6 +264,7 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ=
github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk=
@@ -601,6 +602,7 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY=
github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0=
diff --git a/lib/genesis/genesis.go b/lib/genesis/genesis.go
index 458dc9e9e0..5617243d05 100644
--- a/lib/genesis/genesis.go
+++ b/lib/genesis/genesis.go
@@ -22,29 +22,37 @@ import (
// Genesis stores the data parsed from the genesis configuration file
type Genesis struct {
- Name string `json:"name"`
- ID string `json:"id"`
- ChainType string `json:"chainType"`
- Bootnodes []string `json:"bootNodes"`
- ProtocolID string `json:"protocolId"`
- Genesis Fields `json:"genesis"`
- Properties map[string]interface{} `json:"properties"`
- ForkBlocks []string `json:"forkBlocks"`
- BadBlocks []string `json:"badBlocks"`
- ConsensusEngine string `json:"consensusEngine"`
+ Name string `json:"name"`
+ ID string `json:"id"`
+ ChainType string `json:"chainType"`
+ Bootnodes []string `json:"bootNodes"`
+ TelemetryEndpoints []interface{} `json:"telemetryEndpoints"`
+ ProtocolID string `json:"protocolId"`
+ Genesis Fields `json:"genesis"`
+ Properties map[string]interface{} `json:"properties"`
+ ForkBlocks []string `json:"forkBlocks"`
+ BadBlocks []string `json:"badBlocks"`
+ ConsensusEngine string `json:"consensusEngine"`
}
// Data defines the genesis file data formatted for trie storage
type Data struct {
- Name string
- ID string
- ChainType string
- Bootnodes [][]byte
- ProtocolID string
- Properties map[string]interface{}
- ForkBlocks []string
- BadBlocks []string
- ConsensusEngine string
+ Name string
+ ID string
+ ChainType string
+ Bootnodes [][]byte
+ TelemetryEndpoints []*TelemetryEndpoint
+ ProtocolID string
+ Properties map[string]interface{}
+ ForkBlocks []string
+ BadBlocks []string
+ ConsensusEngine string
+}
+
+// TelemetryEndpoint struct to hold telemetry endpoint information
+type TelemetryEndpoint struct {
+ Endpoint string
+ Verbosity int
}
// Fields stores genesis raw data, and human readable runtime data
@@ -56,15 +64,16 @@ type Fields struct {
// GenesisData formats genesis for trie storage
func (g *Genesis) GenesisData() *Data {
return &Data{
- Name: g.Name,
- ID: g.ID,
- ChainType: g.ChainType,
- Bootnodes: common.StringArrayToBytes(g.Bootnodes),
- ProtocolID: g.ProtocolID,
- Properties: g.Properties,
- ForkBlocks: g.ForkBlocks,
- BadBlocks: g.BadBlocks,
- ConsensusEngine: g.ConsensusEngine,
+ Name: g.Name,
+ ID: g.ID,
+ ChainType: g.ChainType,
+ Bootnodes: common.StringArrayToBytes(g.Bootnodes),
+ TelemetryEndpoints: interfaceToTelemetryEndpoint(g.TelemetryEndpoints),
+ ProtocolID: g.ProtocolID,
+ Properties: g.Properties,
+ ForkBlocks: g.ForkBlocks,
+ BadBlocks: g.BadBlocks,
+ ConsensusEngine: g.ConsensusEngine,
}
}
@@ -94,3 +103,31 @@ func (g *Genesis) ToRaw() error {
g.Genesis.Raw["top"] = res
return nil
}
+
+func interfaceToTelemetryEndpoint(endpoints []interface{}) []*TelemetryEndpoint {
+ var res []*TelemetryEndpoint
+ for _, v := range endpoints {
+ epi, ok := v.([]interface{})
+ if !ok {
+ continue
+ }
+ if len(epi) != 2 {
+ continue
+ }
+ eps, ok := epi[0].(string)
+ if !ok {
+ continue
+ }
+ epv, ok := epi[1].(float64)
+ if !ok {
+ continue
+ }
+ ep := &TelemetryEndpoint{
+ Endpoint: eps,
+ Verbosity: int(epv),
+ }
+ res = append(res, ep)
+ }
+
+ return res
+}
diff --git a/lib/genesis/genesis_test.go b/lib/genesis/genesis_test.go
new file mode 100644
index 0000000000..8da2252ca6
--- /dev/null
+++ b/lib/genesis/genesis_test.go
@@ -0,0 +1,65 @@
+// Copyright 2019 ChainSafe Systems (ON) Corp.
+// This file is part of gossamer.
+//
+// The gossamer library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The gossamer library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the gossamer library. If not, see .
+
+package genesis
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestInterfaceToTelemetryEndpoint(t *testing.T) {
+ testcases := []struct {
+ description string
+ values []interface{}
+ expected []*TelemetryEndpoint
+ }{
+ {
+ "Test with wrong interface type",
+ []interface{}{"string"},
+ nil,
+ },
+ {
+ "Test with interface field len != 2",
+ append(testEndpoints, []interface{}{"wss://telemetry.polkadot.io/submit/"}),
+ nil,
+ },
+ {
+ "Test with interface field 0 wrong type",
+ append(testEndpoints, []interface{}{float32(0), "wss://telemetry.polkadot.io/submit/"}),
+ nil,
+ },
+ {
+ "Test with interface field 1 wrong type",
+ append(testEndpoints, []interface{}{"wss://telemetry.polkadot.io/submit/", "1"}),
+ nil,
+ },
+ {
+ "Test with correctly formed values",
+ append(testEndpoints, testEndpoint1),
+ append([]*TelemetryEndpoint{}, &TelemetryEndpoint{
+ Endpoint: "wss://telemetry.polkadot.io/submit/",
+ Verbosity: 1,
+ }),
+ },
+ }
+
+ for _, test := range testcases {
+ res := interfaceToTelemetryEndpoint(test.values)
+ require.Equal(t, test.expected, res)
+ }
+}
diff --git a/lib/genesis/test_utils.go b/lib/genesis/test_utils.go
index 39de2dbc23..0a7531ac4b 100644
--- a/lib/genesis/test_utils.go
+++ b/lib/genesis/test_utils.go
@@ -28,6 +28,8 @@ var testBootnodes = []string{
"/dns4/p2p.cc3-1.kusama.network/tcp/30100/p2p/QmchDJtEGiEWf7Ag58HNoTg9jSGzxkSZ23VgmF6xiLKKsZ",
}
+var testEndpoints = []interface{}{}
+var testEndpoint1 = []interface{}{"wss://telemetry.polkadot.io/submit/", float64(1)}
var testProperties = map[string]interface{}{"ss58Format": float64(0), "tokenDecimals": float64(10), "tokenSymbol": "DOT"}
var testForkBlocks = []string{"fork1", "forkBlock2"}
@@ -36,13 +38,14 @@ var testBadBlocks = []string{"badBlock1", "badBlock2"}
// TestGenesis instance of Genesis struct for testing
var TestGenesis = &Genesis{
- Name: "gossamer",
- ID: "gossamer",
- Bootnodes: testBootnodes,
- ProtocolID: testProtocolID,
- Properties: testProperties,
- ForkBlocks: testForkBlocks,
- BadBlocks: testBadBlocks,
+ Name: "gossamer",
+ ID: "gossamer",
+ Bootnodes: testBootnodes,
+ TelemetryEndpoints: append(testEndpoints, testEndpoint1),
+ ProtocolID: testProtocolID,
+ Properties: testProperties,
+ ForkBlocks: testForkBlocks,
+ BadBlocks: testBadBlocks,
}
// TestFieldsHR instance of human-readable Fields struct for testing, use with TestGenesis