package api import ( "crypto/rand" "encoding/json" "math/big" "path/filepath" "github.com/status-im/status-go/account/generator" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/multiaccounts/settings" "github.com/status-im/status-go/params" "github.com/status-im/status-go/protocol" "github.com/status-im/status-go/protocol/encryption/multidevice" "github.com/status-im/status-go/protocol/identity/alias" "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/requests" ) const ( pathWalletRoot = "m/44'/60'/0'/0" pathEIP1581 = "m/43'/60'/1581'" pathDefaultChat = pathEIP1581 + "/0'/0" pathEncryption = pathEIP1581 + "/1'/0" pathDefaultWallet = pathWalletRoot + "/0" defaultMnemonicLength = 12 shardsTestClusterID = 16 walletAccountDefaultName = "Account 1" DefaultKeystoreRelativePath = "keystore" DefaultKeycardPairingDataFile = "/ethereum/mainnet_rpc/keycard/pairings.json" DefaultDataDir = "/ethereum/mainnet_rpc" DefaultNodeName = "StatusIM" DefaultLogFile = "geth.log" DefaultLogLevel = "ERROR" DefaultMaxPeers = 20 DefaultMaxPendingPeers = 20 DefaultListenAddr = ":0" DefaultMaxMessageDeliveryAttempts = 3 DefaultVerifyTransactionChainID = 1 DefaultCurrentNetwork = "mainnet_rpc" ) var ( paths = []string{pathWalletRoot, pathEIP1581, pathDefaultChat, pathDefaultWallet, pathEncryption} DefaultFleet = params.FleetStatusProd overrideApiConfig = overrideApiConfigProd ) func defaultSettings(keyUID string, address string, derivedAddresses map[string]generator.AccountInfo) (*settings.Settings, error) { chatKeyString := derivedAddresses[pathDefaultChat].PublicKey s := &settings.Settings{} s.BackupEnabled = true logLevel := "INFO" s.LogLevel = &logLevel s.ProfilePicturesShowTo = settings.ProfilePicturesShowToEveryone s.ProfilePicturesVisibility = settings.ProfilePicturesVisibilityEveryone s.KeyUID = keyUID s.Address = types.HexToAddress(address) s.WalletRootAddress = types.HexToAddress(derivedAddresses[pathWalletRoot].Address) s.URLUnfurlingMode = settings.URLUnfurlingAlwaysAsk // Set chat key & name name, err := alias.GenerateFromPublicKeyString(chatKeyString) if err != nil { return nil, err } s.Name = name s.PublicKey = chatKeyString s.DappsAddress = types.HexToAddress(derivedAddresses[pathDefaultWallet].Address) s.EIP1581Address = types.HexToAddress(derivedAddresses[pathEIP1581].Address) signingPhrase, err := buildSigningPhrase() if err != nil { return nil, err } s.SigningPhrase = signingPhrase s.SendPushNotifications = true s.InstallationID = multidevice.GenerateInstallationID() s.UseMailservers = true s.PreviewPrivacy = true s.PeerSyncingEnabled = false s.Currency = "usd" s.LinkPreviewRequestEnabled = true visibleTokens := make(map[string][]string) visibleTokens["mainnet"] = []string{"SNT"} visibleTokensJSON, err := json.Marshal(visibleTokens) if err != nil { return nil, err } visibleTokenJSONRaw := json.RawMessage(visibleTokensJSON) s.WalletVisibleTokens = &visibleTokenJSONRaw // TODO: fix this networks := make([]map[string]string, 0) networksJSON, err := json.Marshal(networks) if err != nil { return nil, err } networkRawMessage := json.RawMessage(networksJSON) s.Networks = &networkRawMessage s.CurrentNetwork = DefaultCurrentNetwork s.TokenGroupByCommunity = false s.ShowCommunityAssetWhenSendingTokens = true s.DisplayAssetsBelowBalance = false s.TestNetworksEnabled = false // Default user status currentUserStatus, err := json.Marshal(protocol.UserStatus{ PublicKey: chatKeyString, StatusType: int(protobuf.StatusUpdate_AUTOMATIC), Clock: 0, CustomText: "", }) if err != nil { return nil, err } userRawMessage := json.RawMessage(currentUserStatus) s.CurrentUserStatus = &userRawMessage return s, nil } func SetDefaultFleet(nodeConfig *params.NodeConfig) error { return SetFleet(DefaultFleet, nodeConfig) } func SetFleet(fleet string, nodeConfig *params.NodeConfig) error { specifiedWakuV2Config := nodeConfig.WakuV2Config nodeConfig.WakuV2Config = params.WakuV2Config{ Enabled: true, EnableDiscV5: true, DiscoveryLimit: 20, Host: "0.0.0.0", AutoUpdate: true, // mobile may need override following options LightClient: specifiedWakuV2Config.LightClient, EnableStoreConfirmationForMessagesSent: specifiedWakuV2Config.EnableStoreConfirmationForMessagesSent, EnableMissingMessageVerification: specifiedWakuV2Config.EnableMissingMessageVerification, Nameserver: specifiedWakuV2Config.Nameserver, } clusterConfig, err := params.LoadClusterConfigFromFleet(fleet) if err != nil { return err } nodeConfig.ClusterConfig = *clusterConfig nodeConfig.ClusterConfig.Fleet = fleet nodeConfig.ClusterConfig.WakuNodes = params.DefaultWakuNodes(fleet) nodeConfig.ClusterConfig.DiscV5BootstrapNodes = params.DefaultDiscV5Nodes(fleet) if fleet == params.FleetStatusProd { nodeConfig.ClusterConfig.ClusterID = shardsTestClusterID } return nil } func buildWalletConfig(request *requests.WalletSecretsConfig, statusProxyEnabled bool) params.WalletConfig { walletConfig := params.WalletConfig{ Enabled: true, AlchemyAPIKeys: make(map[uint64]string), } if request.StatusProxyStageName != "" { walletConfig.StatusProxyStageName = request.StatusProxyStageName } if request.OpenseaAPIKey != "" { walletConfig.OpenseaAPIKey = request.OpenseaAPIKey } if request.RaribleMainnetAPIKey != "" { walletConfig.RaribleMainnetAPIKey = request.RaribleMainnetAPIKey } if request.RaribleTestnetAPIKey != "" { walletConfig.RaribleTestnetAPIKey = request.RaribleTestnetAPIKey } if request.InfuraToken != "" { walletConfig.InfuraAPIKey = request.InfuraToken } if request.InfuraSecret != "" { walletConfig.InfuraAPIKeySecret = request.InfuraSecret } if request.AlchemyEthereumMainnetToken != "" { walletConfig.AlchemyAPIKeys[mainnetChainID] = request.AlchemyEthereumMainnetToken } if request.AlchemyEthereumGoerliToken != "" { walletConfig.AlchemyAPIKeys[goerliChainID] = request.AlchemyEthereumGoerliToken } if request.AlchemyEthereumSepoliaToken != "" { walletConfig.AlchemyAPIKeys[sepoliaChainID] = request.AlchemyEthereumSepoliaToken } if request.AlchemyArbitrumMainnetToken != "" { walletConfig.AlchemyAPIKeys[arbitrumChainID] = request.AlchemyArbitrumMainnetToken } if request.AlchemyArbitrumGoerliToken != "" { walletConfig.AlchemyAPIKeys[arbitrumGoerliChainID] = request.AlchemyArbitrumGoerliToken } if request.AlchemyArbitrumSepoliaToken != "" { walletConfig.AlchemyAPIKeys[arbitrumSepoliaChainID] = request.AlchemyArbitrumSepoliaToken } if request.AlchemyOptimismMainnetToken != "" { walletConfig.AlchemyAPIKeys[optimismChainID] = request.AlchemyOptimismMainnetToken } if request.AlchemyOptimismGoerliToken != "" { walletConfig.AlchemyAPIKeys[optimismGoerliChainID] = request.AlchemyOptimismGoerliToken } if request.AlchemyOptimismSepoliaToken != "" { walletConfig.AlchemyAPIKeys[optimismSepoliaChainID] = request.AlchemyOptimismSepoliaToken } if request.StatusProxyMarketUser != "" { walletConfig.StatusProxyMarketUser = request.StatusProxyMarketUser } if request.StatusProxyMarketPassword != "" { walletConfig.StatusProxyMarketPassword = request.StatusProxyMarketPassword } if request.StatusProxyBlockchainUser != "" { walletConfig.StatusProxyBlockchainUser = request.StatusProxyBlockchainUser } if request.StatusProxyBlockchainPassword != "" { walletConfig.StatusProxyBlockchainPassword = request.StatusProxyBlockchainPassword } walletConfig.StatusProxyEnabled = statusProxyEnabled return walletConfig } func overrideApiConfigProd(nodeConfig *params.NodeConfig, config *requests.APIConfig) { nodeConfig.APIModules = config.APIModules nodeConfig.ConnectorConfig.Enabled = config.ConnectorEnabled nodeConfig.HTTPEnabled = config.HTTPEnabled nodeConfig.HTTPHost = config.HTTPHost nodeConfig.HTTPPort = config.HTTPPort nodeConfig.HTTPVirtualHosts = config.HTTPVirtualHosts nodeConfig.WSEnabled = config.WSEnabled nodeConfig.WSHost = config.WSHost nodeConfig.WSPort = config.WSPort } func DefaultNodeConfig(installationID string, request *requests.CreateAccount, opts ...params.Option) (*params.NodeConfig, error) { // Set mainnet nodeConfig := ¶ms.NodeConfig{} nodeConfig.RootDataDir = request.RootDataDir nodeConfig.LogEnabled = request.LogEnabled nodeConfig.LogFile = DefaultLogFile nodeConfig.LogDir = request.LogFilePath nodeConfig.LogLevel = DefaultLogLevel nodeConfig.DataDir = DefaultDataDir nodeConfig.ProcessBackedupMessages = false nodeConfig.KeycardPairingDataFile = DefaultKeycardPairingDataFile if request.KeycardPairingDataFile != nil { nodeConfig.KeycardPairingDataFile = *request.KeycardPairingDataFile } if request.LogLevel != nil { nodeConfig.LogLevel = *request.LogLevel nodeConfig.LogEnabled = true } else { nodeConfig.LogEnabled = false } if request.TestOverrideNetworks != nil { nodeConfig.Networks = request.TestOverrideNetworks } else { nodeConfig.Networks = BuildDefaultNetworks(&request.WalletSecretsConfig) } if request.NetworkID != nil { nodeConfig.NetworkID = *request.NetworkID } else { nodeConfig.NetworkID = nodeConfig.Networks[0].ChainID } if request.UpstreamConfig != "" { nodeConfig.UpstreamConfig = params.UpstreamRPCConfig{ Enabled: true, URL: request.UpstreamConfig, } } else { nodeConfig.UpstreamConfig.URL = mainnet(request.WalletSecretsConfig.StatusProxyStageName).RPCURL nodeConfig.UpstreamConfig.Enabled = true } nodeConfig.Name = DefaultNodeName nodeConfig.NoDiscovery = true nodeConfig.MaxPeers = DefaultMaxPeers nodeConfig.MaxPendingPeers = DefaultMaxPendingPeers nodeConfig.WalletConfig = buildWalletConfig(&request.WalletSecretsConfig, request.StatusProxyEnabled) nodeConfig.LocalNotificationsConfig = params.LocalNotificationsConfig{Enabled: true} nodeConfig.BrowsersConfig = params.BrowsersConfig{Enabled: true} nodeConfig.PermissionsConfig = params.PermissionsConfig{Enabled: true} nodeConfig.MailserversConfig = params.MailserversConfig{Enabled: true} nodeConfig.ListenAddr = DefaultListenAddr fleet := request.WakuV2Fleet if fleet == "" { fleet = DefaultFleet } err := SetFleet(fleet, nodeConfig) if err != nil { return nil, err } if request.WakuV2LightClient { nodeConfig.WakuV2Config.LightClient = true } if request.WakuV2EnableMissingMessageVerification { nodeConfig.WakuV2Config.EnableMissingMessageVerification = true } if request.WakuV2EnableStoreConfirmationForMessagesSent { nodeConfig.WakuV2Config.EnableStoreConfirmationForMessagesSent = true } if request.WakuV2Nameserver != nil { nodeConfig.WakuV2Config.Nameserver = *request.WakuV2Nameserver } if request.TelemetryServerURL != "" { nodeConfig.WakuV2Config.TelemetryServerURL = request.TelemetryServerURL } nodeConfig.ShhextConfig = params.ShhextConfig{ InstallationID: installationID, MaxMessageDeliveryAttempts: DefaultMaxMessageDeliveryAttempts, MailServerConfirmations: true, VerifyTransactionChainID: DefaultVerifyTransactionChainID, DataSyncEnabled: true, PFSEnabled: true, } if request.VerifyTransactionURL != nil { nodeConfig.ShhextConfig.VerifyTransactionURL = *request.VerifyTransactionURL } else { nodeConfig.ShhextConfig.VerifyTransactionURL = mainnet(request.WalletSecretsConfig.StatusProxyStageName).FallbackURL } if request.VerifyENSURL != nil { nodeConfig.ShhextConfig.VerifyENSURL = *request.VerifyENSURL } else { nodeConfig.ShhextConfig.VerifyENSURL = mainnet(request.WalletSecretsConfig.StatusProxyStageName).FallbackURL } if request.VerifyTransactionChainID != nil { nodeConfig.ShhextConfig.VerifyTransactionChainID = *request.VerifyTransactionChainID } if request.VerifyENSContractAddress != nil { nodeConfig.ShhextConfig.VerifyENSContractAddress = *request.VerifyENSContractAddress } if request.NetworkID != nil { nodeConfig.NetworkID = *request.NetworkID } nodeConfig.TorrentConfig = params.TorrentConfig{ Enabled: false, Port: 0, DataDir: filepath.Join(nodeConfig.RootDataDir, params.ArchivesRelativePath), TorrentDir: filepath.Join(nodeConfig.RootDataDir, params.TorrentTorrentsRelativePath), } if request.TorrentConfigEnabled != nil { nodeConfig.TorrentConfig.Enabled = *request.TorrentConfigEnabled } if request.TorrentConfigPort != nil { nodeConfig.TorrentConfig.Port = *request.TorrentConfigPort } if request.APIConfig != nil { overrideApiConfig(nodeConfig, request.APIConfig) } for _, opt := range opts { if err := opt(nodeConfig); err != nil { return nil, err } } return nodeConfig, nil } func DefaultKeystorePath(rootDataDir string, keyUID string) (string, string) { relativePath := filepath.Join(DefaultKeystoreRelativePath, keyUID) absolutePath := filepath.Join(rootDataDir, relativePath) return relativePath, absolutePath } func buildSigningPhrase() (string, error) { length := big.NewInt(int64(len(dictionary))) a, err := rand.Int(rand.Reader, length) if err != nil { return "", err } b, err := rand.Int(rand.Reader, length) if err != nil { return "", err } c, err := rand.Int(rand.Reader, length) if err != nil { return "", err } return dictionary[a.Int64()] + " " + dictionary[b.Int64()] + " " + dictionary[c.Int64()], nil } func randomWalletEmoji() (string, error) { count := big.NewInt(int64(len(animalsAndNatureEmojis))) index, err := rand.Int(rand.Reader, count) if err != nil { return "", err } return animalsAndNatureEmojis[index.Int64()], nil } var animalsAndNatureEmojis = []string{ "đŸĩ", "🐒", "đŸĻ", "đŸĻ§", "đŸĻŖ", "đŸĻ", "đŸĻ›", "đŸĒ", "đŸĢ", "đŸĻ™", "🐃", "🐂", "🐄", "🐎", "đŸĻ„", "đŸĻ“", "đŸĻŒ", "🐐", "🐏", "🐑", "đŸĻ™", "🐘", "đŸĻŖ", "đŸĻ›", "đŸĻ", "đŸĻ’", "🐁", "🐀", "🐹", "🐰", "🐇", "đŸŋī¸", "đŸĻ”", "đŸĻ‡", "đŸģ", "đŸģ‍❄ī¸", "🐨", "đŸŧ", "đŸĻĨ", "đŸĻĻ", "đŸĻ¨", "đŸĻ˜", "đŸĻĄ", "🐾", "🐉", "🐲", "đŸŒĩ", "🎄", "🌲", "đŸŒŗ", "🌴", "🌱", "đŸŒŋ", "☘ī¸", "🍀", "🎍", "🎋", "🍃", "🍂", "🍁", "🍄", "🐚", "đŸĒ¨", "🌾", "💐", "🌷", "🌹", "đŸĨ€", "đŸŒē", "🌸", "đŸŒŧ", "đŸŒģ", "🌞", "🌝", "🌛", "🌜", "🌚", "🌕", "🌖", "🌗", "🌘", "🌑", "🌒", "🌓", "🌔", "🌙", "🌎", "🌍", "🌏", "đŸĒ", "đŸ’Ģ", "⭐", "🌟", "✨", "⚡", "☄ī¸", "đŸ’Ĩ", "đŸ”Ĩ", "đŸŒĒī¸", "🌈", "☀ī¸", "🌤ī¸", "⛅", "đŸŒĨī¸", "☁ī¸", "đŸŒĻī¸", "🌧ī¸", "⛈ī¸", "🌩ī¸", "🌨ī¸", "❄ī¸", "☃ī¸", "⛄", "đŸŒŦī¸", "💨", "💧", "đŸ’Ļ", "🌊", }