Some checks failed
CodeQL / Analyze (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Has been cancelled
129 lines
4.2 KiB
Go
129 lines
4.2 KiB
Go
package api
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
|
|
)
|
|
|
|
// Router contains all the module-defined callbacks required by IBC Protocol V2.
|
|
type Router struct {
|
|
// routes is a map from portID to IBCModule
|
|
routes map[string]IBCModule
|
|
// prefixRoutes is a map from portID prefix to IBCModule
|
|
prefixRoutes map[string]IBCModule
|
|
}
|
|
|
|
// NewRouter creates a new Router instance.
|
|
func NewRouter() *Router {
|
|
return &Router{
|
|
routes: make(map[string]IBCModule),
|
|
prefixRoutes: make(map[string]IBCModule),
|
|
}
|
|
}
|
|
|
|
// AddRoute registers a route for a given portID to a given IBCModule.
|
|
//
|
|
// Panics:
|
|
// - if a route with the same portID has already been registered
|
|
// - if the portID is not alphanumeric
|
|
func (rtr *Router) AddRoute(portID string, cbs IBCModule) *Router {
|
|
if !sdk.IsAlphaNumeric(portID) {
|
|
panic(errors.New("route expressions can only contain alphanumeric characters"))
|
|
}
|
|
|
|
if _, ok := rtr.routes[portID]; ok {
|
|
panic(fmt.Errorf("route %s has already been registered", portID))
|
|
}
|
|
|
|
for prefix := range rtr.prefixRoutes {
|
|
// Prevent existing prefix routes from colliding with the new direct route to avoid confusing behavior.
|
|
if strings.HasPrefix(portID, prefix) {
|
|
panic(fmt.Errorf("route %s is already matched by registered prefix route: %s", portID, prefix))
|
|
}
|
|
}
|
|
|
|
rtr.routes[portID] = cbs
|
|
|
|
return rtr
|
|
}
|
|
|
|
// AddPrefixRoute registers a route for a given portID prefix to a given IBCModule.
|
|
// A prefix route matches any portID that starts with the given prefix.
|
|
//
|
|
// Panics:
|
|
// - if `portIDPrefix` is not alphanumeric.
|
|
// - if a direct route `portIDPrefix` has already been registered.
|
|
// - if a prefix of `portIDPrefix` is already registered as a prefix.
|
|
// - if `portIDPrefix` is a prefix of am already registered prefix.
|
|
func (rtr *Router) AddPrefixRoute(portIDPrefix string, cbs IBCModule) *Router {
|
|
if !sdk.IsAlphaNumeric(portIDPrefix) {
|
|
panic(errors.New("route prefix can only contain alphanumeric characters"))
|
|
}
|
|
|
|
// If the prefix is a prefix of an already registered route, we panic to avoid confusing behavior.
|
|
for portID := range rtr.routes {
|
|
if strings.HasPrefix(portID, portIDPrefix) {
|
|
panic(fmt.Errorf("route prefix %s is a prefix for already registered route: %s", portIDPrefix, portID))
|
|
}
|
|
}
|
|
|
|
for prefix := range rtr.prefixRoutes {
|
|
// Prevent two scenarios:
|
|
// * Adding a string that prefix is already registered e.g.
|
|
// add prefix "portPrefix" and try to add "portPrefixSomeSuffix".
|
|
// * Adding a string that is a prefix of already registered prefix route e.g.
|
|
// add prefix "portPrefix" and try to add "port".
|
|
if strings.HasPrefix(portIDPrefix, prefix) {
|
|
panic(fmt.Errorf("route prefix %s has already been covered by registered prefix: %s", portIDPrefix, prefix))
|
|
}
|
|
if strings.HasPrefix(prefix, portIDPrefix) {
|
|
panic(fmt.Errorf("route prefix %s is a prefix for already registered prefix: %s", portIDPrefix, prefix))
|
|
}
|
|
}
|
|
|
|
rtr.prefixRoutes[portIDPrefix] = cbs
|
|
|
|
return rtr
|
|
}
|
|
|
|
// Route returns the IBCModule for a given portID.
|
|
func (rtr *Router) Route(portID string) IBCModule {
|
|
cbs, ok := rtr.getRoute(portID)
|
|
if !ok {
|
|
panic(fmt.Sprintf("no route for %s", portID))
|
|
}
|
|
|
|
return cbs
|
|
}
|
|
|
|
// HasRoute returns true if the Router has a module registered (whether it's a direct or a prefix route)
|
|
// for the portID or false if no module is registered for it.
|
|
func (rtr *Router) HasRoute(portID string) bool {
|
|
_, ok := rtr.getRoute(portID)
|
|
return ok
|
|
}
|
|
|
|
// getRoute is a helper function that retrieves the IBCModule for a given portID.
|
|
func (rtr *Router) getRoute(portID string) (IBCModule, bool) {
|
|
// Direct routes take precedence over prefix routes
|
|
route, ok := rtr.routes[portID]
|
|
if ok {
|
|
return route, true
|
|
}
|
|
|
|
// If the portID is not found as a direct route, check for prefix routes
|
|
for prefix, cbs := range rtr.prefixRoutes {
|
|
// Note that this iteration is deterministic because there can only ever be one prefix route
|
|
// that matches a given portID. This is because of the checks in AddPrefixRoute preventing
|
|
// any colliding prefixes to be added.
|
|
if strings.HasPrefix(portID, prefix) {
|
|
return cbs, true
|
|
}
|
|
}
|
|
|
|
// At this point neither a direct route nor a prefix route was found
|
|
return nil, false
|
|
}
|