github.com/InjectiveLabs/sdk-go@v1.53.0/client/common/network.go (about)

     1  package common
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"fmt"
     7  	"net"
     8  	"net/http"
     9  	"strings"
    10  	"time"
    11  
    12  	"google.golang.org/grpc/metadata"
    13  
    14  	"google.golang.org/grpc/credentials"
    15  )
    16  
    17  const (
    18  	MainnetTokensListURL = "https://github.com/InjectiveLabs/injective-lists/raw/master/tokens/mainnet.json" // nolint:gosec // not credentials, just the link to the public tokens list
    19  	TestnetTokensListURL = "https://github.com/InjectiveLabs/injective-lists/raw/master/tokens/testnet.json" // nolint:gosec // not credentials, just the link to the public tokens list
    20  	DevnetTokensListURL  = "https://github.com/InjectiveLabs/injective-lists/raw/master/tokens/devnet.json"  // nolint:gosec // not credentials, just the link to the public tokens list
    21  )
    22  
    23  func cookieByName(cookies []*http.Cookie, key string) *http.Cookie {
    24  	for _, c := range cookies {
    25  		if c.Name == key {
    26  			return c
    27  		}
    28  	}
    29  	return nil
    30  }
    31  
    32  type MetadataProvider struct {
    33  	f func() metadata.MD
    34  }
    35  
    36  func NewMetadataProvider(f func() metadata.MD) MetadataProvider {
    37  	return MetadataProvider{f: f}
    38  }
    39  
    40  func (provider *MetadataProvider) metadata() metadata.MD {
    41  	return provider.f()
    42  }
    43  
    44  type CookieAssistant interface {
    45  	Metadata(provider MetadataProvider) (string, error)
    46  	RealMetadata() metadata.MD
    47  	ProcessResponseMetadata(header metadata.MD)
    48  }
    49  
    50  type ExpiringCookieAssistant struct {
    51  	expirationKey string
    52  	timeLayout    string
    53  	cookie        string
    54  }
    55  
    56  func (assistant *ExpiringCookieAssistant) initializeCookie(provider MetadataProvider) error {
    57  	md := provider.metadata()
    58  	cookieInfo := md.Get("set-cookie")
    59  
    60  	if len(cookieInfo) == 0 {
    61  		return fmt.Errorf("error getting a new cookie from the server")
    62  	}
    63  
    64  	assistant.cookie = cookieInfo[0]
    65  	return nil
    66  }
    67  
    68  func (assistant *ExpiringCookieAssistant) checkCookieExpiration() {
    69  	// borrow http request to parse cookie
    70  	header := http.Header{}
    71  	header.Add("Cookie", assistant.cookie)
    72  	request := http.Request{Header: header}
    73  	cookies := request.Cookies()
    74  	cookie := cookieByName(cookies, assistant.expirationKey)
    75  
    76  	if cookie != nil {
    77  		expirationTime, err := time.Parse(assistant.timeLayout, cookie.Value)
    78  
    79  		if err == nil {
    80  			timestampDiff := time.Until(expirationTime)
    81  			if timestampDiff < 0 {
    82  				assistant.cookie = ""
    83  			}
    84  		}
    85  	}
    86  }
    87  
    88  func (assistant *ExpiringCookieAssistant) Metadata(provider MetadataProvider) (string, error) {
    89  	if assistant.cookie == "" {
    90  		err := assistant.initializeCookie(provider)
    91  		if err != nil {
    92  			return "", err
    93  		}
    94  	}
    95  
    96  	cookie := assistant.cookie
    97  	assistant.checkCookieExpiration()
    98  
    99  	return cookie, nil
   100  }
   101  
   102  func (assistant *ExpiringCookieAssistant) RealMetadata() metadata.MD {
   103  	newMetadata := metadata.Pairs()
   104  	assistant.checkCookieExpiration()
   105  	if assistant.cookie != "" {
   106  		newMetadata.Append("cookie", assistant.cookie)
   107  	}
   108  	return newMetadata
   109  }
   110  
   111  func (assistant *ExpiringCookieAssistant) ProcessResponseMetadata(header metadata.MD) {
   112  	cookieInfo := header.Get("set-cookie")
   113  	if len(cookieInfo) > 0 {
   114  		assistant.cookie = cookieInfo[0]
   115  	}
   116  }
   117  
   118  func TestnetKubernetesCookieAssistant() ExpiringCookieAssistant {
   119  	assistant := ExpiringCookieAssistant{}
   120  	assistant.expirationKey = "Expires"
   121  	assistant.timeLayout = "Mon, 02-Jan-06 15:04:05 MST"
   122  
   123  	return assistant
   124  }
   125  
   126  func MainnetKubernetesCookieAssistant() ExpiringCookieAssistant {
   127  	assistant := ExpiringCookieAssistant{}
   128  	assistant.expirationKey = "expires"
   129  	assistant.timeLayout = "Mon, 02-Jan-2006 15:04:05 MST"
   130  
   131  	return assistant
   132  }
   133  
   134  type BareMetalLoadBalancedCookieAssistant struct {
   135  	cookie string
   136  }
   137  
   138  func (assistant *BareMetalLoadBalancedCookieAssistant) initializeCookie(provider MetadataProvider) error {
   139  	md := provider.metadata()
   140  	cookieInfo := md.Get("set-cookie")
   141  
   142  	if len(cookieInfo) == 0 {
   143  		return fmt.Errorf("error getting a new cookie from the server")
   144  	}
   145  
   146  	assistant.cookie = cookieInfo[0]
   147  	return nil
   148  }
   149  
   150  func (assistant *BareMetalLoadBalancedCookieAssistant) Metadata(provider MetadataProvider) (string, error) {
   151  	if assistant.cookie == "" {
   152  		err := assistant.initializeCookie(provider)
   153  		if err != nil {
   154  			return "", err
   155  		}
   156  	}
   157  
   158  	return assistant.cookie, nil
   159  }
   160  
   161  func (assistant *BareMetalLoadBalancedCookieAssistant) RealMetadata() metadata.MD {
   162  	newMetadata := metadata.Pairs()
   163  	if assistant.cookie != "" {
   164  		newMetadata.Append("cookie", assistant.cookie)
   165  	}
   166  	return newMetadata
   167  }
   168  
   169  func (assistant *BareMetalLoadBalancedCookieAssistant) ProcessResponseMetadata(header metadata.MD) {
   170  	cookieInfo := header.Get("set-cookie")
   171  	if len(cookieInfo) > 0 {
   172  		assistant.cookie = cookieInfo[0]
   173  	}
   174  }
   175  
   176  type DisabledCookieAssistant struct{}
   177  
   178  func (assistant *DisabledCookieAssistant) Metadata(provider MetadataProvider) (string, error) {
   179  	return "", nil
   180  }
   181  
   182  func (assistant *DisabledCookieAssistant) RealMetadata() metadata.MD {
   183  	return metadata.Pairs()
   184  }
   185  
   186  func (assistant *DisabledCookieAssistant) ProcessResponseMetadata(header metadata.MD) {}
   187  
   188  type Network struct {
   189  	LcdEndpoint             string
   190  	TmEndpoint              string
   191  	ChainGrpcEndpoint       string
   192  	ChainStreamGrpcEndpoint string
   193  	ChainTLSCert            credentials.TransportCredentials
   194  	ExchangeGrpcEndpoint    string
   195  	ExplorerGrpcEndpoint    string
   196  	ExchangeTLSCert         credentials.TransportCredentials
   197  	ExplorerTLSCert         credentials.TransportCredentials
   198  	ChainId                 string
   199  	FeeDenom                string
   200  	Name                    string
   201  	ChainCookieAssistant    CookieAssistant
   202  	ExchangeCookieAssistant CookieAssistant
   203  	ExplorerCookieAssistant CookieAssistant
   204  	OfficialTokensListURL   string
   205  }
   206  
   207  func LoadNetwork(name, node string) Network {
   208  	switch name {
   209  
   210  	case "local":
   211  		return Network{
   212  			LcdEndpoint:             "http://localhost:10337",
   213  			TmEndpoint:              "http://localhost:26657",
   214  			ChainGrpcEndpoint:       "tcp://localhost:9900",
   215  			ChainStreamGrpcEndpoint: "tcp://localhost:9999",
   216  			ExchangeGrpcEndpoint:    "tcp://localhost:9910",
   217  			ExplorerGrpcEndpoint:    "tcp://localhost:9911",
   218  			ChainId:                 "injective-1",
   219  			FeeDenom:                "inj",
   220  			Name:                    "local",
   221  			ChainCookieAssistant:    &DisabledCookieAssistant{},
   222  			ExchangeCookieAssistant: &DisabledCookieAssistant{},
   223  			ExplorerCookieAssistant: &DisabledCookieAssistant{},
   224  			OfficialTokensListURL:   MainnetTokensListURL,
   225  		}
   226  
   227  	case "devnet-1":
   228  		return Network{
   229  			LcdEndpoint:             "https://devnet-1.lcd.injective.dev",
   230  			TmEndpoint:              "https://devnet-1.tm.injective.dev:443",
   231  			ChainGrpcEndpoint:       "tcp://devnet-1.grpc.injective.dev:9900",
   232  			ChainStreamGrpcEndpoint: "tcp://devnet-1.grpc.injective.dev:9999",
   233  			ExchangeGrpcEndpoint:    "tcp://devnet-1.api.injective.dev:9910",
   234  			ExplorerGrpcEndpoint:    "tcp://devnet-1.api.injective.dev:9911",
   235  			ChainId:                 "injective-777",
   236  			FeeDenom:                "inj",
   237  			Name:                    "devnet-1",
   238  			ChainCookieAssistant:    &DisabledCookieAssistant{},
   239  			ExchangeCookieAssistant: &DisabledCookieAssistant{},
   240  			ExplorerCookieAssistant: &DisabledCookieAssistant{},
   241  			OfficialTokensListURL:   DevnetTokensListURL,
   242  		}
   243  	case "devnet":
   244  		return Network{
   245  			LcdEndpoint:             "https://devnet.lcd.injective.dev",
   246  			TmEndpoint:              "https://devnet.tm.injective.dev:443",
   247  			ChainGrpcEndpoint:       "tcp://devnet.injective.dev:9900",
   248  			ChainStreamGrpcEndpoint: "tcp://devnet.injective.dev:9999",
   249  			ExchangeGrpcEndpoint:    "tcp://devnet.injective.dev:9910",
   250  			ExplorerGrpcEndpoint:    "tcp://devnet.api.injective.dev:9911",
   251  			ChainId:                 "injective-777",
   252  			FeeDenom:                "inj",
   253  			Name:                    "devnet",
   254  			ChainCookieAssistant:    &DisabledCookieAssistant{},
   255  			ExchangeCookieAssistant: &DisabledCookieAssistant{},
   256  			ExplorerCookieAssistant: &DisabledCookieAssistant{},
   257  			OfficialTokensListURL:   DevnetTokensListURL,
   258  		}
   259  	case "testnet":
   260  		validNodes := []string{"lb", "sentry"}
   261  		if !contains(validNodes, node) {
   262  			panic(fmt.Sprintf("invalid node %s for %s", node, name))
   263  		}
   264  
   265  		var lcdEndpoint, tmEndpoint, chainGrpcEndpoint, chainStreamGrpcEndpoint, exchangeGrpcEndpoint, explorerGrpcEndpoint string
   266  		var chainTLSCert, exchangeTLSCert, explorerTLSCert credentials.TransportCredentials
   267  		var chainCookieAssistant, exchangeCookieAssistant, explorerCookieAssistant CookieAssistant
   268  		if node == "lb" {
   269  			lcdEndpoint = "https://testnet.sentry.lcd.injective.network:443"
   270  			tmEndpoint = "https://testnet.sentry.tm.injective.network:443"
   271  			chainGrpcEndpoint = "testnet.sentry.chain.grpc.injective.network:443"
   272  			chainStreamGrpcEndpoint = "testnet.sentry.chain.stream.injective.network:443"
   273  			exchangeGrpcEndpoint = "testnet.sentry.exchange.grpc.injective.network:443"
   274  			explorerGrpcEndpoint = "testnet.sentry.explorer.grpc.injective.network:443"
   275  			chainTLSCert = credentials.NewServerTLSFromCert(&tls.Certificate{})
   276  			exchangeTLSCert = credentials.NewServerTLSFromCert(&tls.Certificate{})
   277  			explorerTLSCert = credentials.NewServerTLSFromCert(&tls.Certificate{})
   278  			chainCookieAssistant = &BareMetalLoadBalancedCookieAssistant{}
   279  			exchangeCookieAssistant = &BareMetalLoadBalancedCookieAssistant{}
   280  			explorerCookieAssistant = &BareMetalLoadBalancedCookieAssistant{}
   281  		} else if node == "sentry" {
   282  			lcdEndpoint = "https://testnet.lcd.injective.network:443"
   283  			tmEndpoint = "https://testnet.tm.injective.network:443"
   284  			chainGrpcEndpoint = "testnet.chain.grpc.injective.network:443"
   285  			chainStreamGrpcEndpoint = "testnet.chain.stream.injective.network:443"
   286  			exchangeGrpcEndpoint = "testnet.exchange.grpc.injective.network:443"
   287  			explorerGrpcEndpoint = "testnet.explorer.grpc.injective.network:443"
   288  			chainTLSCert = credentials.NewServerTLSFromCert(&tls.Certificate{})
   289  			exchangeTLSCert = credentials.NewServerTLSFromCert(&tls.Certificate{})
   290  			explorerTLSCert = credentials.NewServerTLSFromCert(&tls.Certificate{})
   291  			chainCookieAssistant = &DisabledCookieAssistant{}
   292  			exchangeCookieAssistant = &DisabledCookieAssistant{}
   293  			explorerCookieAssistant = &DisabledCookieAssistant{}
   294  		}
   295  
   296  		return Network{
   297  			LcdEndpoint:             lcdEndpoint,
   298  			TmEndpoint:              tmEndpoint,
   299  			ChainGrpcEndpoint:       chainGrpcEndpoint,
   300  			ChainStreamGrpcEndpoint: chainStreamGrpcEndpoint,
   301  			ChainTLSCert:            chainTLSCert,
   302  			ExchangeGrpcEndpoint:    exchangeGrpcEndpoint,
   303  			ExchangeTLSCert:         exchangeTLSCert,
   304  			ExplorerGrpcEndpoint:    explorerGrpcEndpoint,
   305  			ExplorerTLSCert:         explorerTLSCert,
   306  			ChainId:                 "injective-888",
   307  			FeeDenom:                "inj",
   308  			Name:                    "testnet",
   309  			ChainCookieAssistant:    chainCookieAssistant,
   310  			ExchangeCookieAssistant: exchangeCookieAssistant,
   311  			ExplorerCookieAssistant: explorerCookieAssistant,
   312  			OfficialTokensListURL:   TestnetTokensListURL,
   313  		}
   314  	case "mainnet":
   315  		validNodes := []string{"lb"}
   316  		if !contains(validNodes, node) {
   317  			panic(fmt.Sprintf("invalid node %s for %s", node, name))
   318  		}
   319  		var lcdEndpoint, tmEndpoint, chainGrpcEndpoint, chainStreamGrpcEndpoint, exchangeGrpcEndpoint, explorerGrpcEndpoint string
   320  		var chainTLSCert, exchangeTLSCert, explorerTLSCert credentials.TransportCredentials
   321  		var chainCookieAssistant, exchangeCookieAssistant, explorerCookieAssistant CookieAssistant
   322  
   323  		lcdEndpoint = "https://sentry.lcd.injective.network"
   324  		tmEndpoint = "https://sentry.tm.injective.network:443"
   325  		chainGrpcEndpoint = "sentry.chain.grpc.injective.network:443"
   326  		chainStreamGrpcEndpoint = "sentry.chain.stream.injective.network:443"
   327  		exchangeGrpcEndpoint = "sentry.exchange.grpc.injective.network:443"
   328  		explorerGrpcEndpoint = "sentry.explorer.grpc.injective.network:443"
   329  		chainTLSCert = credentials.NewServerTLSFromCert(&tls.Certificate{})
   330  		exchangeTLSCert = credentials.NewServerTLSFromCert(&tls.Certificate{})
   331  		explorerTLSCert = credentials.NewServerTLSFromCert(&tls.Certificate{})
   332  		chainCookieAssistant = &BareMetalLoadBalancedCookieAssistant{}
   333  		exchangeCookieAssistant = &BareMetalLoadBalancedCookieAssistant{}
   334  		explorerCookieAssistant = &BareMetalLoadBalancedCookieAssistant{}
   335  
   336  		return Network{
   337  			LcdEndpoint:             lcdEndpoint,
   338  			TmEndpoint:              tmEndpoint,
   339  			ChainGrpcEndpoint:       chainGrpcEndpoint,
   340  			ChainStreamGrpcEndpoint: chainStreamGrpcEndpoint,
   341  			ChainTLSCert:            chainTLSCert,
   342  			ExchangeGrpcEndpoint:    exchangeGrpcEndpoint,
   343  			ExchangeTLSCert:         exchangeTLSCert,
   344  			ExplorerGrpcEndpoint:    explorerGrpcEndpoint,
   345  			ExplorerTLSCert:         explorerTLSCert,
   346  			ChainId:                 "injective-1",
   347  			FeeDenom:                "inj",
   348  			Name:                    "mainnet",
   349  			ChainCookieAssistant:    chainCookieAssistant,
   350  			ExchangeCookieAssistant: exchangeCookieAssistant,
   351  			ExplorerCookieAssistant: explorerCookieAssistant,
   352  			OfficialTokensListURL:   MainnetTokensListURL,
   353  		}
   354  
   355  	default:
   356  		panic(fmt.Sprintf("invalid network %s", name))
   357  	}
   358  }
   359  
   360  // NewNetwork returns a new Network instance with all cookie assistants disabled.
   361  // It can be used to setup a custom environment from scratch.
   362  func NewNetwork() Network {
   363  	return Network{
   364  		ChainCookieAssistant:    &DisabledCookieAssistant{},
   365  		ExchangeCookieAssistant: &DisabledCookieAssistant{},
   366  		ExplorerCookieAssistant: &DisabledCookieAssistant{},
   367  		OfficialTokensListURL:   MainnetTokensListURL,
   368  	}
   369  }
   370  
   371  func contains(s []string, e string) bool {
   372  	for _, a := range s {
   373  		if a == e {
   374  			return true
   375  		}
   376  	}
   377  	return false
   378  }
   379  
   380  func DialerFunc(ctx context.Context, addr string) (net.Conn, error) {
   381  	return Connect(addr)
   382  }
   383  
   384  // Connect dials the given address and returns a net.Conn. The protoAddr argument should be prefixed with the protocol,
   385  // eg. "tcp://127.0.0.1:8080" or "unix:///tmp/test.sock"
   386  func Connect(protoAddr string) (net.Conn, error) {
   387  	proto, address := ProtocolAndAddress(protoAddr)
   388  	conn, err := net.Dial(proto, address)
   389  	return conn, err
   390  }
   391  
   392  // ProtocolAndAddress splits an address into the protocol and address components.
   393  // For instance, "tcp://127.0.0.1:8080" will be split into "tcp" and "127.0.0.1:8080".
   394  // If the address has no protocol prefix, the default is "tcp".
   395  func ProtocolAndAddress(listenAddr string) (protocol, address string) {
   396  	protocol, address = "tcp", listenAddr
   397  	parts := strings.SplitN(address, "://", 2)
   398  	if len(parts) == 2 {
   399  		protocol, address = parts[0], parts[1]
   400  	}
   401  	return protocol, address
   402  }