Skip to content

Commit 50ee868

Browse files
committed
chore(config)_: extract rpc_provider_persistence + tests
* Add rpc_providers table, migration * add RpcProvider type * deprecate old rpc fields in networks, add RpcProviders list * add persistence packages for rpc_providers, networks * Tests (without integration)
1 parent 9a94a82 commit 50ee868

14 files changed

+1729
-31
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CREATE TABLE IF NOT EXISTS rpc_providers (
2+
id INTEGER PRIMARY KEY AUTOINCREMENT, -- Unique provider ID (sorting)
3+
chain_id INTEGER NOT NULL CHECK (chain_id > 0), -- Chain ID for the network
4+
name TEXT NOT NULL CHECK (LENGTH(name) > 0), -- Provider name
5+
url TEXT NOT NULL CHECK (LENGTH(url) > 0), -- Provider URL
6+
enable_rps_limiter BOOLEAN NOT NULL DEFAULT FALSE, -- Enable RPS limiter
7+
type TEXT NOT NULL DEFAULT 'user', -- Provider type: embedded-proxy, embedded-direct, user
8+
enabled BOOLEAN NOT NULL DEFAULT TRUE, -- Whether the provider is active or not
9+
auth_type TEXT NOT NULL DEFAULT 'no-auth', -- Authentication type: no-auth, basic-auth, token-auth
10+
auth_login TEXT, -- BasicAuth login (nullable)
11+
auth_password TEXT, -- Password for BasicAuth (nullable)
12+
auth_token TEXT -- Token for TokenAuth (nullable)
13+
);

params/config.go

+1-31
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"go.uber.org/zap"
1515
validator "gopkg.in/go-playground/validator.v9"
1616

17-
"github.com/ethereum/go-ethereum/common"
1817
"github.com/ethereum/go-ethereum/p2p/discv5"
1918
"github.com/ethereum/go-ethereum/params"
2019

@@ -514,35 +513,6 @@ type NodeConfig struct {
514513
ProcessBackedupMessages bool
515514
}
516515

517-
type TokenOverride struct {
518-
Symbol string `json:"symbol"`
519-
Address common.Address `json:"address"`
520-
}
521-
522-
type Network struct {
523-
ChainID uint64 `json:"chainId"`
524-
ChainName string `json:"chainName"`
525-
DefaultRPCURL string `json:"defaultRpcUrl"` // proxy rpc url
526-
DefaultFallbackURL string `json:"defaultFallbackURL"` // proxy fallback url
527-
DefaultFallbackURL2 string `json:"defaultFallbackURL2"` // second proxy fallback url
528-
RPCURL string `json:"rpcUrl"`
529-
OriginalRPCURL string `json:"originalRpcUrl"`
530-
FallbackURL string `json:"fallbackURL"`
531-
OriginalFallbackURL string `json:"originalFallbackURL"`
532-
BlockExplorerURL string `json:"blockExplorerUrl,omitempty"`
533-
IconURL string `json:"iconUrl,omitempty"`
534-
NativeCurrencyName string `json:"nativeCurrencyName,omitempty"`
535-
NativeCurrencySymbol string `json:"nativeCurrencySymbol,omitempty"`
536-
NativeCurrencyDecimals uint64 `json:"nativeCurrencyDecimals"`
537-
IsTest bool `json:"isTest"`
538-
Layer uint64 `json:"layer"`
539-
Enabled bool `json:"enabled"`
540-
ChainColor string `json:"chainColor"`
541-
ShortName string `json:"shortName"`
542-
TokenOverrides []TokenOverride `json:"tokenOverrides"`
543-
RelatedChainID uint64 `json:"relatedChainId"`
544-
}
545-
546516
// WalletConfig extra configuration for wallet.Service.
547517
type WalletConfig struct {
548518
Enabled bool
@@ -598,7 +568,7 @@ type MailserversConfig struct {
598568
Enabled bool
599569
}
600570

601-
// ProviderConfig extra configuration for provider.Service
571+
// ProviderAuthConfig extra configuration for provider.Service
602572
type Web3ProviderConfig struct {
603573
Enabled bool
604574
}

params/network_config.go

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package params
2+
3+
import "github.com/ethereum/go-ethereum/common"
4+
5+
// RpcProviderAuthType defines the different types of authentication for RPC providers
6+
type RpcProviderAuthType string
7+
8+
const (
9+
NoAuth RpcProviderAuthType = "no-auth" // No authentication
10+
BasicAuth RpcProviderAuthType = "basic-auth" // HTTP Header "Authorization: Basic base64(username:password)"
11+
TokenAuth RpcProviderAuthType = "token-auth" // URL Token-based authentication "https://api.example.com/YOUR_TOKEN"
12+
)
13+
14+
// RpcProviderType defines the type of RPC provider
15+
type RpcProviderType string
16+
17+
const (
18+
EmbeddedProxyProviderType RpcProviderType = "embedded-proxy" // Proxy-based RPC provider
19+
EmbeddedDirectProviderType RpcProviderType = "embedded-direct" // Direct RPC provider
20+
UserProviderType RpcProviderType = "user" // User-defined RPC provider
21+
)
22+
23+
// RpcProvider represents an RPC provider configuration with various options
24+
type RpcProvider struct {
25+
ID int64 `json:"id" validate:"omitempty"` // Auto-increment ID (for sorting order)
26+
ChainID uint64 `json:"chainId" validate:"required,gt=0"` // Chain ID of the network
27+
Name string `json:"name" validate:"required,min=1"` // Provider name for identification
28+
URL string `json:"url" validate:"required,url"` // Current Provider URL
29+
EnableRPSLimiter bool `json:"enableRpsLimiter"` // Enable RPC rate limiting for this provider
30+
Type RpcProviderType `json:"type" validate:"required,oneof=embedded-proxy embedded-direct user"`
31+
Enabled bool `json:"enabled"` // Whether the provider is enabled
32+
// Authentication
33+
AuthType RpcProviderAuthType `json:"authType" validate:"required,oneof=no-auth basic-auth token-auth"` // Type of authentication
34+
AuthLogin string `json:"authLogin" validate:"omitempty,min=1"` // Login for BasicAuth (empty string if not used)
35+
AuthPassword string `json:"authPassword" validate:"omitempty,min=1"` // Password for BasicAuth (empty string if not used)
36+
AuthToken string `json:"authToken" validate:"omitempty,min=1"` // Token for TokenAuth (empty string if not used)
37+
}
38+
39+
type TokenOverride struct {
40+
Symbol string `json:"symbol"`
41+
Address common.Address `json:"address"`
42+
}
43+
44+
type Network struct {
45+
ChainID uint64 `json:"chainId" validate:"required,gt=0"`
46+
ChainName string `json:"chainName" validate:"required,min=1"`
47+
RpcProviders []RpcProvider `json:"rpcProviders" validate:"dive,required"` // List of RPC providers, in the order in which they are accessed
48+
49+
// Deprecated fields (kept for backward compatibility)
50+
// FIXME: remove deprecated fields (keeping until client integrate this). TODO: add ticket URL
51+
DefaultRPCURL string `json:"defaultRpcUrl" validate:"omitempty,url"` // Deprecated: proxy rpc url
52+
DefaultFallbackURL string `json:"defaultFallbackURL" validate:"omitempty,url"` // Deprecated: proxy fallback url
53+
DefaultFallbackURL2 string `json:"defaultFallbackURL2" validate:"omitempty,url"` // Deprecated: second proxy fallback url
54+
RPCURL string `json:"rpcUrl" validate:"omitempty,url"` // Deprecated: direct rpc url
55+
OriginalRPCURL string `json:"originalRpcUrl" validate:"omitempty,url"` // Deprecated: direct rpc url if user overrides RPCURL
56+
FallbackURL string `json:"fallbackURL" validate:"omitempty,url"` // Deprecated
57+
OriginalFallbackURL string `json:"originalFallbackURL" validate:"omitempty,url"` // Deprecated
58+
59+
BlockExplorerURL string `json:"blockExplorerUrl,omitempty" validate:"omitempty,url"`
60+
IconURL string `json:"iconUrl,omitempty" validate:"omitempty"`
61+
NativeCurrencyName string `json:"nativeCurrencyName,omitempty" validate:"omitempty,min=1"`
62+
NativeCurrencySymbol string `json:"nativeCurrencySymbol,omitempty" validate:"omitempty,min=1"`
63+
NativeCurrencyDecimals uint64 `json:"nativeCurrencyDecimals" validate:"omitempty"`
64+
IsTest bool `json:"isTest"`
65+
Layer uint64 `json:"layer" validate:"omitempty"`
66+
Enabled bool `json:"enabled"`
67+
ChainColor string `json:"chainColor" validate:"omitempty"`
68+
ShortName string `json:"shortName" validate:"omitempty,min=1"`
69+
TokenOverrides []TokenOverride `json:"tokenOverrides" validate:"omitempty,dive"`
70+
RelatedChainID uint64 `json:"relatedChainId" validate:"omitempty"`
71+
}
72+
73+
func newRpcProvider(chainID uint64, name, url string, enableRpsLimiter bool, providerType RpcProviderType) *RpcProvider {
74+
return &RpcProvider{
75+
ChainID: chainID,
76+
Name: name,
77+
URL: url,
78+
EnableRPSLimiter: enableRpsLimiter,
79+
Type: providerType,
80+
Enabled: true,
81+
AuthType: NoAuth,
82+
}
83+
}
84+
85+
func NewUserProvider(chainID uint64, name, url string, enableRpsLimiter bool) *RpcProvider {
86+
return newRpcProvider(chainID, name, url, enableRpsLimiter, UserProviderType)
87+
}
88+
89+
func NewProxyProvider(chainID uint64, name, url string, enableRpsLimiter bool) *RpcProvider {
90+
return newRpcProvider(chainID, name, url, enableRpsLimiter, EmbeddedProxyProviderType)
91+
}
92+
93+
func NewDirectProvider(chainID uint64, name, url string, enableRpsLimiter bool) *RpcProvider {
94+
return newRpcProvider(chainID, name, url, enableRpsLimiter, EmbeddedDirectProviderType)
95+
}
+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package networkhelper
2+
3+
import (
4+
"net/url"
5+
"strings"
6+
7+
"github.com/status-im/status-go/params"
8+
)
9+
10+
// MergeProvidersPreservingUsersAndEnabledState merges new embedded providers with the current ones,
11+
// preserving user-defined providers and maintaining the Enabled state.
12+
func MergeProvidersPreservingUsersAndEnabledState(currentProviders, newProviders []params.RpcProvider) []params.RpcProvider {
13+
// Create a map for quick lookup of the Enabled state by Name
14+
enabledState := make(map[string]bool)
15+
for _, provider := range currentProviders {
16+
enabledState[provider.Name] = provider.Enabled
17+
}
18+
19+
// Update the Enabled field in newProviders if the Name matches
20+
for i := range newProviders {
21+
if enabled, exists := enabledState[newProviders[i].Name]; exists {
22+
newProviders[i].Enabled = enabled
23+
}
24+
}
25+
26+
// Retain current providers of type UserProviderType and add them to the beginning of the list
27+
var mergedProviders []params.RpcProvider
28+
for _, provider := range currentProviders {
29+
if provider.Type == params.UserProviderType {
30+
mergedProviders = append(mergedProviders, provider)
31+
}
32+
}
33+
34+
// Add the updated newProviders
35+
mergedProviders = append(mergedProviders, newProviders...)
36+
37+
return mergedProviders
38+
}
39+
40+
// ToggleUserProviders enables or disables all user-defined providers and disables other types.
41+
func ToggleUserProviders(providers []params.RpcProvider, enabled bool) []params.RpcProvider {
42+
for i := range providers {
43+
if providers[i].Type == params.UserProviderType {
44+
providers[i].Enabled = enabled
45+
} else {
46+
providers[i].Enabled = !enabled
47+
}
48+
}
49+
return providers
50+
}
51+
52+
// GetEmbeddedProviders returns the embedded providers from the list.
53+
func GetEmbeddedProviders(providers []params.RpcProvider) []params.RpcProvider {
54+
var embeddedProviders []params.RpcProvider
55+
for _, provider := range providers {
56+
if provider.Type != params.UserProviderType {
57+
embeddedProviders = append(embeddedProviders, provider)
58+
}
59+
}
60+
return embeddedProviders
61+
}
62+
63+
// GetUserProviders returns the user-defined providers from the list.
64+
func GetUserProviders(providers []params.RpcProvider) []params.RpcProvider {
65+
var userProviders []params.RpcProvider
66+
for _, provider := range providers {
67+
if provider.Type == params.UserProviderType {
68+
userProviders = append(userProviders, provider)
69+
}
70+
}
71+
return userProviders
72+
}
73+
74+
// ReplaceUserProviders replaces user-defined providers with new ones, retaining the rest of the providers.
75+
func ReplaceUserProviders(currentProviders, newUserProviders []params.RpcProvider) []params.RpcProvider {
76+
// Extract embedded providers from the current list
77+
embeddedProviders := GetEmbeddedProviders(currentProviders)
78+
userProviders := GetUserProviders(newUserProviders)
79+
80+
// Combine new user providers with the existing embedded providers
81+
return append(userProviders, embeddedProviders...)
82+
}
83+
84+
// ReplaceEmbeddedProviders replaces embedded providers with new ones, retaining user-defined providers.
85+
func ReplaceEmbeddedProviders(currentProviders, newEmbeddedProviders []params.RpcProvider) []params.RpcProvider {
86+
// Extract user-defined providers from the current list
87+
userProviders := GetUserProviders(currentProviders)
88+
embeddedProviders := GetEmbeddedProviders(newEmbeddedProviders)
89+
90+
// Combine existing user-defined providers with the new embedded providers
91+
return append(userProviders, embeddedProviders...)
92+
}
93+
94+
// OverrideEmbeddedProxyProviders updates all embedded-proxy providers in the given networks.
95+
// It sets the `Enabled` flag and configures the `AuthLogin` and `AuthPassword` for each provider.
96+
func OverrideEmbeddedProxyProviders(networks []params.Network, enabled bool, user, password string) []params.Network {
97+
updatedNetworks := make([]params.Network, len(networks))
98+
for i, network := range networks {
99+
// Deep copy the network to avoid mutating the input slice
100+
updatedNetwork := network
101+
updatedProviders := make([]params.RpcProvider, len(network.RpcProviders))
102+
103+
// Update the embedded-proxy providers
104+
for j, provider := range network.RpcProviders {
105+
if provider.Type == params.EmbeddedProxyProviderType {
106+
provider.Enabled = enabled
107+
provider.AuthLogin = user
108+
provider.AuthPassword = password
109+
}
110+
updatedProviders[j] = provider
111+
}
112+
113+
updatedNetwork.RpcProviders = updatedProviders
114+
updatedNetworks[i] = updatedNetwork
115+
}
116+
117+
return updatedNetworks
118+
}
119+
120+
func OverrideDirectProvidersAuth(networks []params.Network, authTokens map[string]string) []params.Network {
121+
updatedNetworks := make([]params.Network, len(networks))
122+
for i, network := range networks {
123+
updatedNetwork := network
124+
updatedProviders := make([]params.RpcProvider, len(network.RpcProviders))
125+
126+
for j, provider := range network.RpcProviders {
127+
updatedProvider := provider
128+
129+
if provider.Type == params.EmbeddedDirectProviderType {
130+
host, err := extractHost(provider.URL)
131+
if err == nil {
132+
for suffix, token := range authTokens {
133+
if strings.HasSuffix(host, suffix) && token != "" {
134+
updatedProvider.AuthType = params.TokenAuth
135+
updatedProvider.AuthToken = token
136+
break
137+
}
138+
}
139+
}
140+
}
141+
142+
updatedProviders[j] = updatedProvider
143+
}
144+
145+
updatedNetwork.RpcProviders = updatedProviders
146+
updatedNetworks[i] = updatedNetwork
147+
}
148+
return updatedNetworks
149+
}
150+
151+
func OverrideGanacheToken(networks []params.Network, ganacheURL string, chainID uint64, tokenOverride params.TokenOverride) []params.Network {
152+
updatedNetworks := make([]params.Network, len(networks))
153+
for i, network := range networks {
154+
updatedNetwork := network
155+
156+
if network.ChainID == chainID {
157+
updatedProviders := make([]params.RpcProvider, len(network.RpcProviders))
158+
159+
for j, provider := range network.RpcProviders {
160+
updatedProvider := provider
161+
if ganacheURL != "" {
162+
updatedProvider.URL = ganacheURL
163+
}
164+
updatedProviders[j] = updatedProvider
165+
}
166+
167+
updatedNetwork.RpcProviders = updatedProviders
168+
updatedNetwork.TokenOverrides = []params.TokenOverride{
169+
tokenOverride,
170+
}
171+
}
172+
173+
updatedNetworks[i] = updatedNetwork
174+
}
175+
return updatedNetworks
176+
}
177+
178+
func extractHost(providerURL string) (string, error) {
179+
parsedURL, err := url.Parse(providerURL)
180+
if err != nil {
181+
return "", err
182+
}
183+
return parsedURL.Host, nil
184+
}

0 commit comments

Comments
 (0)