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 }