github.com/status-im/status-go@v1.1.0/cmd/ping-community/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "encoding/json" 6 "flag" 7 "fmt" 8 stdlog "log" 9 "os" 10 "path/filepath" 11 "runtime" 12 "strings" 13 "time" 14 15 "github.com/google/uuid" 16 "golang.org/x/crypto/ssh/terminal" 17 18 "github.com/ethereum/go-ethereum/log" 19 20 "github.com/status-im/status-go/account/generator" 21 "github.com/status-im/status-go/api" 22 "github.com/status-im/status-go/common/dbsetup" 23 "github.com/status-im/status-go/eth-node/types" 24 "github.com/status-im/status-go/multiaccounts" 25 "github.com/status-im/status-go/multiaccounts/accounts" 26 "github.com/status-im/status-go/multiaccounts/settings" 27 28 "github.com/status-im/status-go/logutils" 29 "github.com/status-im/status-go/params" 30 "github.com/status-im/status-go/protocol" 31 "github.com/status-im/status-go/protocol/common" 32 "github.com/status-im/status-go/protocol/common/shard" 33 "github.com/status-im/status-go/protocol/identity/alias" 34 "github.com/status-im/status-go/protocol/protobuf" 35 wakuextn "github.com/status-im/status-go/services/wakuext" 36 ) 37 38 const ( 39 serverClientName = "Statusd" 40 ) 41 42 var ( 43 configFiles configFlags 44 logLevel = flag.String("log", "", `Log level, one of: "ERROR", "WARN", "INFO", "DEBUG", and "TRACE"`) 45 logWithoutColors = flag.Bool("log-without-color", false, "Disables log colors") 46 ipcEnabled = flag.Bool("ipc", false, "Enable IPC RPC endpoint") 47 ipcFile = flag.String("ipcfile", "", "Set IPC file path") 48 seedPhrase = flag.String("seed-phrase", "", "Seed phrase") 49 version = flag.Bool("version", false, "Print version and dump configuration") 50 communityID = flag.String("community-id", "", "The id of the community") 51 shardCluster = flag.Int("shard-cluster", shard.MainStatusShardCluster, "The shard cluster in which the of the community is published") 52 shardIndex = flag.Int("shard-index", shard.DefaultShardIndex, "The shard index in which the community is published") 53 chatID = flag.String("chat-id", "", "The id of the chat") 54 55 dataDir = flag.String("dir", getDefaultDataDir(), "Directory used by node to store data") 56 networkID = flag.Int( 57 "network-id", 58 params.GoerliNetworkID, 59 fmt.Sprintf( 60 "A network ID: %d (Mainnet), %d (Goerli)", 61 params.MainNetworkID, params.GoerliNetworkID, 62 ), 63 ) 64 listenAddr = flag.String("addr", "", "address to bind listener to") 65 ) 66 67 // All general log messages in this package should be routed through this logger. 68 var logger = log.New("package", "status-go/cmd/statusd") 69 70 func init() { 71 flag.Var(&configFiles, "c", "JSON configuration file(s). Multiple configuration files can be specified, and will be merged in occurrence order") 72 } 73 74 // nolint:gocyclo 75 func main() { 76 colors := terminal.IsTerminal(int(os.Stdin.Fd())) 77 if err := logutils.OverrideRootLog(true, "ERROR", logutils.FileOptions{}, colors); err != nil { 78 stdlog.Fatalf("Error initializing logger: %v", err) 79 } 80 81 flag.Usage = printUsage 82 flag.Parse() 83 if flag.NArg() > 0 { 84 printUsage() 85 logger.Error("Extra args in command line: %v", flag.Args()) 86 os.Exit(1) 87 } 88 89 opts := []params.Option{} 90 91 config, err := params.NewNodeConfigWithDefaultsAndFiles( 92 *dataDir, 93 uint64(*networkID), 94 opts, 95 configFiles, 96 ) 97 if err != nil { 98 printUsage() 99 logger.Error(err.Error()) 100 os.Exit(1) 101 } 102 103 // Use listenAddr if and only if explicitly provided in the arguments. 104 // The default value is set in params.NewNodeConfigWithDefaultsAndFiles(). 105 if *listenAddr != "" { 106 config.ListenAddr = *listenAddr 107 } 108 109 // enable IPC RPC 110 if *ipcEnabled { 111 config.IPCEnabled = true 112 config.IPCFile = *ipcFile 113 } 114 115 // set up logging options 116 setupLogging(config) 117 118 // We want statusd to be distinct from StatusIM client. 119 config.Name = serverClientName 120 121 if *version { 122 printVersion(config) 123 return 124 } 125 126 backend := api.NewGethStatusBackend() 127 err = ImportAccount(*seedPhrase, backend) 128 if err != nil { 129 logger.Error("failed import account", "err", err) 130 return 131 } 132 133 wakuextservice := backend.StatusNode().WakuExtService() 134 if wakuextservice == nil { 135 logger.Error("wakuext not available") 136 return 137 } 138 139 wakuext := wakuextn.NewPublicAPI(wakuextservice) 140 141 // This will start the push notification server as well as 142 // the config is set to Enabled 143 _, err = wakuext.StartMessenger() 144 if err != nil { 145 logger.Error("failed to start messenger", "error", err) 146 return 147 } 148 149 messenger := wakuextservice.Messenger() 150 151 var s *shard.Shard = nil 152 if shardCluster != nil && shardIndex != nil { 153 s = &shard.Shard{ 154 Cluster: uint16(*shardCluster), 155 Index: uint16(*shardIndex), 156 } 157 } 158 159 community, err := messenger.FetchCommunity(&protocol.FetchCommunityRequest{ 160 CommunityKey: *communityID, 161 Shard: s, 162 TryDatabase: true, 163 WaitForResponse: true, 164 }) 165 if err != nil { 166 167 logger.Error("community error", "error", err) 168 return 169 170 } 171 chat := community.Chats()[*chatID] 172 if chat == nil { 173 logger.Warn("Chat not found") 174 return 175 } 176 logger.Info("GOT community", "comm", chat) 177 178 response, err := messenger.JoinCommunity(context.Background(), community.ID(), false) 179 if err != nil { 180 logger.Error("failed to join community", "err", err) 181 } 182 183 var targetChat *protocol.Chat 184 185 for _, c := range response.Chats() { 186 if strings.Contains(c.ID, *chatID) { 187 targetChat = c 188 } 189 } 190 191 if targetChat == nil { 192 logger.Warn("chat not found") 193 return 194 } 195 196 id := uuid.New().String() 197 198 ticker := time.NewTicker(2 * time.Second) 199 count := 0 200 201 for { // nolint: gosimple 202 select { 203 case <-ticker.C: 204 count++ 205 timestamp := time.Now().UTC().Format(time.RFC3339) 206 logger.Info("Publishing", "id", id, "count", count, "time", timestamp) 207 inputMessage := common.NewMessage() 208 209 inputMessage.Text = fmt.Sprintf("%d\n%s\n%s", count, timestamp, id) 210 inputMessage.LocalChatID = targetChat.ID 211 inputMessage.ChatId = targetChat.ID 212 inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN 213 214 _, err := messenger.SendChatMessage(context.Background(), inputMessage) 215 if err != nil { 216 logger.Error("failed to send a message", "err", err) 217 } 218 219 } 220 } 221 222 } 223 224 func getDefaultDataDir() string { 225 if home := os.Getenv("HOME"); home != "" { 226 return filepath.Join(home, ".statusd") 227 } 228 return "./statusd-data" 229 } 230 231 func setupLogging(config *params.NodeConfig) { 232 if *logLevel != "" { 233 config.LogLevel = *logLevel 234 } 235 236 logSettings := logutils.LogSettings{ 237 Enabled: config.LogEnabled, 238 MobileSystem: config.LogMobileSystem, 239 Level: config.LogLevel, 240 File: config.LogFile, 241 MaxSize: config.LogMaxSize, 242 MaxBackups: config.LogMaxBackups, 243 CompressRotated: config.LogCompressRotated, 244 } 245 colors := !(*logWithoutColors) && terminal.IsTerminal(int(os.Stdin.Fd())) 246 if err := logutils.OverrideRootLogWithConfig(logSettings, colors); err != nil { 247 stdlog.Fatalf("Error initializing logger: %v", err) 248 } 249 } 250 251 // printVersion prints verbose output about version and config. 252 func printVersion(config *params.NodeConfig) { 253 fmt.Println(strings.Title(config.Name)) 254 fmt.Println("Version:", config.Version) 255 fmt.Println("Network ID:", config.NetworkID) 256 fmt.Println("Go Version:", runtime.Version()) 257 fmt.Println("OS:", runtime.GOOS) 258 fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH")) 259 fmt.Printf("GOROOT=%s\n", runtime.GOROOT()) 260 261 fmt.Println("Loaded Config: ", config) 262 } 263 264 func printUsage() { 265 usage := ` 266 Usage: ping-community [options] 267 Example: 268 ping-community --seed-phrase "your seed phrase" --community-id "community-id" --chat-id "chat-id" 269 Options: 270 ` 271 fmt.Fprint(os.Stderr, usage) 272 flag.PrintDefaults() 273 } 274 275 const pathWalletRoot = "m/44'/60'/0'/0" 276 const pathEIP1581 = "m/43'/60'/1581'" 277 const pathDefaultChat = pathEIP1581 + "/0'/0" 278 const pathDefaultWallet = pathWalletRoot + "/0" 279 280 var paths = []string{pathWalletRoot, pathEIP1581, pathDefaultChat, pathDefaultWallet} 281 282 func defaultSettings(generatedAccountInfo generator.GeneratedAccountInfo, derivedAddresses map[string]generator.AccountInfo, mnemonic *string) (*settings.Settings, error) { 283 chatKeyString := derivedAddresses[pathDefaultChat].PublicKey 284 285 defaultSettings := &settings.Settings{} 286 defaultSettings.KeyUID = generatedAccountInfo.KeyUID 287 defaultSettings.Address = types.HexToAddress(generatedAccountInfo.Address) 288 defaultSettings.WalletRootAddress = types.HexToAddress(derivedAddresses[pathWalletRoot].Address) 289 290 // Set chat key & name 291 name, err := alias.GenerateFromPublicKeyString(chatKeyString) 292 if err != nil { 293 return nil, err 294 } 295 defaultSettings.Name = name 296 defaultSettings.PublicKey = chatKeyString 297 298 defaultSettings.DappsAddress = types.HexToAddress(derivedAddresses[pathDefaultWallet].Address) 299 defaultSettings.EIP1581Address = types.HexToAddress(derivedAddresses[pathEIP1581].Address) 300 defaultSettings.Mnemonic = mnemonic 301 302 signingPhrase, err := buildSigningPhrase() 303 if err != nil { 304 return nil, err 305 } 306 defaultSettings.SigningPhrase = signingPhrase 307 308 defaultSettings.SendPushNotifications = true 309 defaultSettings.InstallationID = uuid.New().String() 310 defaultSettings.UseMailservers = true 311 312 defaultSettings.PreviewPrivacy = true 313 defaultSettings.PeerSyncingEnabled = false 314 defaultSettings.Currency = "usd" 315 defaultSettings.ProfilePicturesVisibility = settings.ProfilePicturesVisibilityEveryone 316 defaultSettings.LinkPreviewRequestEnabled = true 317 318 defaultSettings.TestNetworksEnabled = false 319 320 visibleTokens := make(map[string][]string) 321 visibleTokens["mainnet"] = []string{"SNT"} 322 visibleTokensJSON, err := json.Marshal(visibleTokens) 323 if err != nil { 324 return nil, err 325 } 326 visibleTokenJSONRaw := json.RawMessage(visibleTokensJSON) 327 defaultSettings.WalletVisibleTokens = &visibleTokenJSONRaw 328 329 // TODO: fix this 330 networks := make([]map[string]string, 0) 331 networksJSON, err := json.Marshal(networks) 332 if err != nil { 333 return nil, err 334 } 335 networkRawMessage := json.RawMessage(networksJSON) 336 defaultSettings.Networks = &networkRawMessage 337 defaultSettings.CurrentNetwork = "mainnet_rpc" 338 339 return defaultSettings, nil 340 } 341 342 func defaultNodeConfig(installationID string) (*params.NodeConfig, error) { 343 // Set mainnet 344 nodeConfig := ¶ms.NodeConfig{} 345 nodeConfig.NetworkID = 1 346 nodeConfig.LogLevel = "ERROR" 347 nodeConfig.DataDir = api.DefaultDataDir 348 nodeConfig.UpstreamConfig = params.UpstreamRPCConfig{ 349 Enabled: true, 350 URL: "https://mainnet.infura.io/v3/800c641949d64d768a5070a1b0511938", 351 } 352 353 nodeConfig.Name = "StatusIM" 354 clusterConfig, err := params.LoadClusterConfigFromFleet("eth.prod") 355 if err != nil { 356 return nil, err 357 } 358 nodeConfig.ClusterConfig = *clusterConfig 359 360 nodeConfig.WalletConfig = params.WalletConfig{Enabled: true} 361 nodeConfig.LocalNotificationsConfig = params.LocalNotificationsConfig{Enabled: true} 362 nodeConfig.BrowsersConfig = params.BrowsersConfig{Enabled: true} 363 nodeConfig.PermissionsConfig = params.PermissionsConfig{Enabled: true} 364 nodeConfig.MailserversConfig = params.MailserversConfig{Enabled: true} 365 nodeConfig.WakuConfig = params.WakuConfig{ 366 Enabled: true, 367 LightClient: true, 368 MinimumPoW: 0.000001, 369 } 370 371 nodeConfig.ShhextConfig = params.ShhextConfig{ 372 InstallationID: installationID, 373 MaxMessageDeliveryAttempts: api.DefaultMaxMessageDeliveryAttempts, 374 MailServerConfirmations: true, 375 VerifyTransactionURL: "", 376 VerifyENSURL: "", 377 VerifyENSContractAddress: "", 378 VerifyTransactionChainID: api.DefaultVerifyTransactionChainID, 379 DataSyncEnabled: true, 380 PFSEnabled: true, 381 } 382 383 // TODO: check topics 384 385 return nodeConfig, nil 386 } 387 388 func ImportAccount(seedPhrase string, backend *api.GethStatusBackend) error { 389 backend.UpdateRootDataDir("./tmp") 390 manager := backend.AccountManager() 391 if err := manager.InitKeystore("./tmp"); err != nil { 392 return err 393 } 394 err := backend.OpenAccounts() 395 if err != nil { 396 logger.Error("failed open accounts", err) 397 return err 398 } 399 generator := manager.AccountsGenerator() 400 generatedAccountInfo, err := generator.ImportMnemonic(seedPhrase, "") 401 if err != nil { 402 return err 403 } 404 405 derivedAddresses, err := generator.DeriveAddresses(generatedAccountInfo.ID, paths) 406 if err != nil { 407 return err 408 } 409 410 _, err = generator.StoreDerivedAccounts(generatedAccountInfo.ID, "", paths) 411 if err != nil { 412 return err 413 } 414 415 account := multiaccounts.Account{ 416 KeyUID: generatedAccountInfo.KeyUID, 417 KDFIterations: dbsetup.ReducedKDFIterationsNumber, 418 } 419 settings, err := defaultSettings(generatedAccountInfo, derivedAddresses, &seedPhrase) 420 if err != nil { 421 return err 422 } 423 424 nodeConfig, err := defaultNodeConfig(settings.InstallationID) 425 if err != nil { 426 return err 427 } 428 429 walletDerivedAccount := derivedAddresses[pathDefaultWallet] 430 walletAccount := &accounts.Account{ 431 PublicKey: types.Hex2Bytes(walletDerivedAccount.PublicKey), 432 KeyUID: generatedAccountInfo.KeyUID, 433 Address: types.HexToAddress(walletDerivedAccount.Address), 434 ColorID: "", 435 Wallet: true, 436 Path: pathDefaultWallet, 437 Name: "Ethereum account", 438 } 439 440 chatDerivedAccount := derivedAddresses[pathDefaultChat] 441 chatAccount := &accounts.Account{ 442 PublicKey: types.Hex2Bytes(chatDerivedAccount.PublicKey), 443 KeyUID: generatedAccountInfo.KeyUID, 444 Address: types.HexToAddress(chatDerivedAccount.Address), 445 Name: settings.Name, 446 Chat: true, 447 Path: pathDefaultChat, 448 } 449 450 fmt.Println(nodeConfig) 451 accounts := []*accounts.Account{walletAccount, chatAccount} 452 err = backend.StartNodeWithAccountAndInitialConfig(account, "", *settings, nodeConfig, accounts, nil) 453 if err != nil { 454 logger.Error("start node", err) 455 return err 456 } 457 458 return nil 459 }