Skip to content

Commit

Permalink
add a second revision
Browse files Browse the repository at this point in the history
  • Loading branch information
keefertaylor committed Nov 18, 2023
1 parent 13ff006 commit 4613a4e
Show file tree
Hide file tree
Showing 11 changed files with 198 additions and 98 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,14 @@ go get github.com/tessellated-io/router

Several errors are also exported for convenience from `errors.go`.

The most useful functionality for `Router` is to add a `replace` for a private go Module in your `go.mod`, in order to route to your own infrastructure. By design, `Router` is statically configured, but in dynamic systems, a replacement module could dynamically refresh routes and supported chains.
We provide two default routing strategies out of the box:
- `static`: Allows configuration of a router with preconfigured chains
- `file`: Allows configuration of a router with routes in a file.
A file router assumes a config such as:

```yaml
- chain-id: my-chain
grpc: tcp://1.2.3.4:9090
```
The most useful functionality for `Router` is to add a `replace` for a private go Module in your `go.mod`, in order to route to your own infrastructure.
37 changes: 37 additions & 0 deletions base/router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package base

import "github.com/tessellated-io/router/types"

// baseRouter encapsulates basic functionality for all routers. You probably shouldn't use this directly.
type BaseRouter struct {
// Map of chain-id to chain.
chains map[string]types.Chain
}

// NewRouter returns a new BaseRouter

func NewRouter() (BaseRouter, error) {
chainMap := make(map[string]types.Chain)
return BaseRouter{
chains: chainMap,
}, nil
}

// Router Interace

Check failure on line 20 in base/router.go

View workflow job for this annotation

GitHub Actions / lint router

`Interace` is a misspelling of `Interacted` (misspell)

func (br *BaseRouter) GrpcEndpoint(chainID string) (string, error) {
chain := br.chains[chainID]
if chain == nil {
return "", types.ErrNoChainWithID
}

return chain.GrpcEndpoint()
}

// Helper methods

func (br *BaseRouter) AddChains(chains []types.Chain) {
for _, chain := range chains {
br.chains[chain.ChainID()] = chain
}
}
32 changes: 32 additions & 0 deletions file/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package file

import (
"os"

"gopkg.in/yaml.v2"
)

type Config struct {
Networks []NetworkConfig `yaml:"networks"`
}

type NetworkConfig struct {
ChainID string `yaml:"chain-id"`
GrpcEndpoint string `yaml:"grpc"`
}

// Parse a file into a UserConfig
func parseConfig(filename string) (*Config, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, err
}

config := &Config{}
err = yaml.Unmarshal(data, config)
if err != nil {
return nil, err
}

return config, nil
}
53 changes: 53 additions & 0 deletions file/router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package file

import (
"github.com/tessellated-io/pickaxe/arrays"
"github.com/tessellated-io/router/base"
"github.com/tessellated-io/router/types"
)

// fileRouter is a router that maps to networks in a config file.
type fileRouter struct {
base.BaseRouter

configFile string
}

// Type assertion
var _ types.Router = (*fileRouter)(nil)

// NewRouter creates a new file router.
func NewRouter(configFile string) (types.Router, error) {
fileRouter := &fileRouter{
configFile: configFile,
}

err := fileRouter.loadConfigFile()
if err != nil {
return nil, err
}

return fileRouter, nil
}

// Router interface

func (fr *fileRouter) Refresh() error {
return fr.loadConfigFile()
}

// Private methods

func (fr *fileRouter) loadConfigFile() error {
parsed, err := parseConfig(fr.configFile)
if err != nil {
return err
}

chains := arrays.Map(parsed.Networks, func(networkConfig NetworkConfig) types.Chain {
return types.NewChain(networkConfig.ChainID, &networkConfig.GrpcEndpoint)
})

fr.BaseRouter.AddChains(chains)
return nil
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
module github.com/tessellated-io/router

go 1.20

require (
github.com/tessellated-io/pickaxe v1.0.11
gopkg.in/yaml.v2 v2.4.0
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/tessellated-io/pickaxe v1.0.11 h1:gaQVdYh+DMSE1tXA44JCunwmQnXeplblp2HQFowiNaE=
github.com/tessellated-io/pickaxe v1.0.11/go.mod h1:wO7zNwLt2PbwH5C9j+p/KM+4I6K4zXY7rrq1567CPJE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
79 changes: 0 additions & 79 deletions router/router.go

This file was deleted.

35 changes: 35 additions & 0 deletions static/router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package static

import (
"github.com/tessellated-io/router/base"
"github.com/tessellated-io/router/types"
)

// staticRouter uses a preconfigured set of routes.
type staticRouter struct {
base.BaseRouter
}

// Type assertion
var _ types.Router = (*staticRouter)(nil)

// NewRouter makes a new router with the given chains.
func NewRouter(chains []types.Chain) (types.Router, error) {
baseRouter, err := base.NewRouter()
if err != nil {
return nil, err
}

baseRouter.AddChains(chains)

return &staticRouter{
BaseRouter: baseRouter,
}, nil
}

// Router Interface

func (sr *staticRouter) Refresh() error {
// Intentional no-op
return nil
}
23 changes: 6 additions & 17 deletions router/chain.go → types/chain.go
Original file line number Diff line number Diff line change
@@ -1,42 +1,31 @@
package router
package types

// Chain defines an abstraction around a Chain that Validator tooling or other blockchain applications may use.
type Chain interface {
HumanReadableName() string
ChainID() string
Bech32Prefix() string

GrpcEndpoint() (string, error)
}

// private implementation
type chain struct {
chainID string
humanReadableName string
bech32Prefix string

chainID string
grpcEndpoint *string
}

// Ensure chain is a Chain
var _ Chain = (*chain)(nil)

// Create a new Chain
func NewChain(chainID, humanReadableName, bech32Prefix string, grpcEndpoint *string) (Chain, error) {
func NewChain(chainID string, grpcEndpoint *string) Chain {
return &chain{
chainID: chainID,
humanReadableName: humanReadableName,
bech32Prefix: bech32Prefix,

chainID: chainID,
grpcEndpoint: grpcEndpoint,
}, nil
}
}

// Chain Interface

func (c *chain) HumanReadableName() string { return c.humanReadableName }
func (c *chain) ChainID() string { return c.chainID }
func (c *chain) Bech32Prefix() string { return c.bech32Prefix }
func (c *chain) ChainID() string { return c.chainID }

func (c *chain) GrpcEndpoint() (string, error) {
if c.grpcEndpoint == nil {
Expand Down
2 changes: 1 addition & 1 deletion router/error.go → types/error.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package router
package types

import "errors"

Expand Down
8 changes: 8 additions & 0 deletions types/router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package types

// Router defines a set of rules that allow you to route to a set of blockchain nodes.
type Router interface {
Refresh() error

GrpcEndpoint(chainName string) (string, error)
}

0 comments on commit 4613a4e

Please sign in to comment.