github.com/status-im/status-go@v1.1.0/server/pairing/client.go (about) 1 package pairing 2 3 import ( 4 "bytes" 5 "crypto/tls" 6 "crypto/x509" 7 "encoding/json" 8 "encoding/pem" 9 "fmt" 10 "io" 11 "net" 12 "net/http" 13 "net/http/cookiejar" 14 "net/url" 15 "runtime" 16 17 "go.uber.org/zap" 18 19 "github.com/status-im/status-go/api" 20 "github.com/status-im/status-go/logutils" 21 "github.com/status-im/status-go/server" 22 "github.com/status-im/status-go/signal" 23 "github.com/status-im/status-go/timesource" 24 ) 25 26 /* 27 |-------------------------------------------------------------------------- 28 | BaseClient 29 |-------------------------------------------------------------------------- 30 | 31 | 32 | 33 */ 34 35 // BaseClient is responsible for lower level pairing.Client functionality common to dependent Client types 36 type BaseClient struct { 37 *http.Client 38 serverCert *x509.Certificate 39 baseAddress *url.URL 40 challengeTaker *ChallengeTaker 41 } 42 43 func findServerCert(c *ConnectionParams, reachableIPs []net.IP) (*url.URL, *x509.Certificate, error) { 44 var baseAddress *url.URL 45 var serverCert *x509.Certificate 46 47 type connectionError struct { 48 ip net.IP 49 err error 50 } 51 errCh := make(chan connectionError, len(reachableIPs)) 52 53 type result struct { 54 u *url.URL 55 cert *x509.Certificate 56 } 57 successCh := make(chan result, 1) // as we close on the first success 58 59 for _, ip := range reachableIPs { 60 go func(ip net.IP) { 61 u := c.BuildURL(ip) 62 cert, err := getServerCert(u) 63 if err != nil { 64 errCh <- connectionError{ip: ip, err: fmt.Errorf("connecting to '%s' failed: %s", u, err.Error())} 65 return 66 } 67 // If no error, send the results to the success channel 68 successCh <- result{u: u, cert: cert} 69 }(ip) 70 } 71 72 // Keep track of error counts 73 errorCount := 0 74 var combinedErrors string 75 for { 76 select { 77 case success := <-successCh: 78 baseAddress = success.u 79 serverCert = success.cert 80 return baseAddress, serverCert, nil 81 case ipErr := <-errCh: 82 errorCount++ 83 combinedErrors += fmt.Sprintf("IP %s: %s; ", ipErr.ip, ipErr.err) 84 if errorCount == len(reachableIPs) { 85 return nil, nil, fmt.Errorf(combinedErrors) 86 } 87 } 88 } 89 } 90 91 // NewBaseClient returns a fully qualified BaseClient from the given ConnectionParams 92 func NewBaseClient(c *ConnectionParams, logger *zap.Logger) (*BaseClient, error) { 93 var baseAddress *url.URL 94 var serverCert *x509.Certificate 95 var certErrs error 96 97 netIPs, err := server.FindReachableAddressesForPairingClient(c.netIPs) 98 if err != nil { 99 logger.Error("[local pair client] failed to find reachable addresses", zap.Error(err), zap.Any("netIPs", netIPs)) 100 signal.SendLocalPairingEvent(Event{Type: EventConnectionError, Error: err.Error(), Action: ActionConnect}) 101 return nil, err 102 } 103 // if client and server aren't on the same network, netIPs maybe empty, we should check it before invoking findServerCert 104 if len(netIPs) == 0 { 105 logger.Error("[local pair client] no reachable addresses found") 106 signal.SendLocalPairingEvent(Event{Type: EventConnectionError, Error: "no reachable addresses found", Action: ActionConnect}) 107 return nil, fmt.Errorf("no reachable addresses found") 108 } 109 110 maxRetries := 3 111 for i := 0; i < maxRetries; i++ { 112 baseAddress, serverCert, certErrs = findServerCert(c, netIPs) 113 if serverCert == nil { 114 certErrs = fmt.Errorf("failed to connect to any of given addresses. %w", certErrs) 115 logger.Warn("failed to connect to any of given addresses. Retrying...", zap.Error(certErrs), zap.Any("netIPs", netIPs), zap.Int("retry", i+1)) 116 } else { 117 break 118 } 119 } 120 121 if serverCert == nil { 122 certErrs = fmt.Errorf("failed to connect to any of given addresses. %w", certErrs) 123 signal.SendLocalPairingEvent(Event{Type: EventConnectionError, Error: certErrs.Error(), Action: ActionConnect}) 124 return nil, certErrs 125 } 126 127 // No error on the dial out then the URL.Host is accessible 128 signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionConnect}) 129 130 err = verifyCert(serverCert, c.publicKey) 131 if err != nil { 132 return nil, err 133 } 134 135 certPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: serverCert.Raw}) 136 137 rootCAs, err := x509.SystemCertPool() 138 if err != nil { 139 return nil, err 140 } 141 if ok := rootCAs.AppendCertsFromPEM(certPem); !ok { 142 return nil, fmt.Errorf("failed to append certPem to rootCAs") 143 } 144 145 tr := &http.Transport{ 146 TLSClientConfig: &tls.Config{ 147 MinVersion: tls.VersionTLS12, 148 InsecureSkipVerify: false, // MUST BE FALSE 149 RootCAs: rootCAs, 150 Time: timesource.GetCurrentTime, 151 }, 152 } 153 154 cj, err := cookiejar.New(nil) 155 if err != nil { 156 return nil, err 157 } 158 159 return &BaseClient{ 160 Client: &http.Client{Transport: tr, Jar: cj}, 161 serverCert: serverCert, 162 challengeTaker: NewChallengeTaker(NewPayloadEncryptor(c.aesKey)), 163 baseAddress: baseAddress, 164 }, nil 165 } 166 167 // getChallenge makes a call to the identified Server and receives a [32]byte challenge 168 func (c *BaseClient) getChallenge() error { 169 c.baseAddress.Path = pairingChallenge 170 resp, err := c.Get(c.baseAddress.String()) 171 if err != nil { 172 return err 173 } 174 175 if resp.StatusCode != http.StatusOK { 176 return fmt.Errorf("[client] status not ok when getting challenge, received '%s'", resp.Status) 177 } 178 179 return c.challengeTaker.SetChallenge(resp) 180 } 181 182 /* 183 |-------------------------------------------------------------------------- 184 | SenderClient 185 |-------------------------------------------------------------------------- 186 | 187 | With AccountPayloadMounter, RawMessagePayloadMounter and InstallationPayloadMounterReceiver 188 | 189 */ 190 191 // SenderClient is responsible for sending pairing data to a ReceiverServer 192 type SenderClient struct { 193 *BaseClient 194 accountMounter PayloadMounter 195 rawMessageMounter PayloadMounter 196 installationMounter PayloadMounterReceiver 197 } 198 199 // NewSenderClient returns a fully qualified SenderClient created with the incoming parameters 200 func NewSenderClient(backend *api.GethStatusBackend, c *ConnectionParams, config *SenderClientConfig) (*SenderClient, error) { 201 logger := logutils.ZapLogger().Named("SenderClient") 202 pe := NewPayloadEncryptor(c.aesKey) 203 204 bc, err := NewBaseClient(c, logger) 205 if err != nil { 206 return nil, err 207 } 208 209 am, rmm, imr, err := NewPayloadMounters(logger, pe, backend, config.SenderConfig) 210 if err != nil { 211 return nil, err 212 } 213 214 return &SenderClient{ 215 BaseClient: bc, 216 accountMounter: am, 217 rawMessageMounter: rmm, 218 installationMounter: imr, 219 }, nil 220 } 221 222 func (c *SenderClient) sendAccountData() error { 223 err := c.accountMounter.Mount() 224 if err != nil { 225 return err 226 } 227 228 c.baseAddress.Path = pairingReceiveAccount 229 resp, err := c.Post(c.baseAddress.String(), "application/octet-stream", bytes.NewBuffer(c.accountMounter.ToSend())) 230 if err != nil { 231 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount}) 232 return err 233 } 234 235 if resp.StatusCode != http.StatusOK { 236 err = fmt.Errorf("[client] status not ok when sending account data, received '%s'", resp.Status) 237 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount}) 238 return err 239 } 240 241 signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount}) 242 243 c.accountMounter.LockPayload() 244 return nil 245 } 246 247 func (c *SenderClient) sendSyncDeviceData() error { 248 err := c.rawMessageMounter.Mount() 249 if err != nil { 250 return err 251 } 252 253 c.baseAddress.Path = pairingReceiveSyncDevice 254 resp, err := c.Post(c.baseAddress.String(), "application/octet-stream", bytes.NewBuffer(c.rawMessageMounter.ToSend())) 255 if err != nil { 256 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice}) 257 return err 258 } 259 260 if resp.StatusCode != http.StatusOK { 261 err = fmt.Errorf("[client] status not okay when sending sync device data, status: %s", resp.Status) 262 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice}) 263 return err 264 } 265 266 signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice}) 267 return nil 268 } 269 270 func (c *SenderClient) receiveInstallationData() error { 271 c.baseAddress.Path = pairingSendInstallation 272 req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil) 273 if err != nil { 274 return err 275 } 276 277 err = c.challengeTaker.DoChallenge(req) 278 if err != nil { 279 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation}) 280 return err 281 } 282 283 resp, err := c.Do(req) 284 if err != nil { 285 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation}) 286 return err 287 } 288 289 if resp.StatusCode != http.StatusOK { 290 err = fmt.Errorf("[client] status not ok when receiving installation data, received '%s'", resp.Status) 291 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation}) 292 return err 293 } 294 295 payload, err := io.ReadAll(resp.Body) 296 if err != nil { 297 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation}) 298 return err 299 } 300 signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation}) 301 302 err = c.installationMounter.Receive(payload) 303 if err != nil { 304 signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingInstallation}) 305 return err 306 } 307 signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionPairingInstallation}) 308 return nil 309 } 310 311 // setupSendingClient creates a new SenderClient after parsing string inputs 312 func setupSendingClient(backend *api.GethStatusBackend, cs, configJSON string) (*SenderClient, error) { 313 ccp := new(ConnectionParams) 314 err := ccp.FromString(cs) 315 if err != nil { 316 return nil, err 317 } 318 319 conf := NewSenderClientConfig() 320 err = json.Unmarshal([]byte(configJSON), conf) 321 if err != nil { 322 return nil, err 323 } 324 err = validateAndVerifyPassword(conf, conf.SenderConfig) 325 if err != nil { 326 return nil, err 327 } 328 329 conf.SenderConfig.DB = backend.GetMultiaccountDB() 330 331 return NewSenderClient(backend, ccp, conf) 332 } 333 334 // StartUpSendingClient creates a SenderClient and triggers all `send` calls in sequence to the ReceiverServer 335 func StartUpSendingClient(backend *api.GethStatusBackend, cs, configJSON string) error { 336 c, err := setupSendingClient(backend, cs, configJSON) 337 if err != nil { 338 return err 339 } 340 err = c.sendAccountData() 341 if err != nil { 342 return err 343 } 344 err = c.sendSyncDeviceData() 345 if err != nil { 346 return err 347 } 348 err = c.getChallenge() 349 if err != nil { 350 return err 351 } 352 return c.receiveInstallationData() 353 } 354 355 /* 356 |-------------------------------------------------------------------------- 357 | ReceiverClient 358 |-------------------------------------------------------------------------- 359 | 360 | With AccountPayloadReceiver, RawMessagePayloadReceiver, InstallationPayloadMounterReceiver 361 | 362 */ 363 364 // ReceiverClient is responsible for accepting pairing data to a SenderServer 365 type ReceiverClient struct { 366 *BaseClient 367 368 accountReceiver PayloadReceiver 369 rawMessageReceiver PayloadReceiver 370 installationReceiver PayloadMounterReceiver 371 } 372 373 // NewReceiverClient returns a fully qualified ReceiverClient created with the incoming parameters 374 func NewReceiverClient(backend *api.GethStatusBackend, c *ConnectionParams, config *ReceiverClientConfig) (*ReceiverClient, error) { 375 logger := logutils.ZapLogger().Named("ReceiverClient") 376 377 bc, err := NewBaseClient(c, logger) 378 if err != nil { 379 return nil, err 380 } 381 382 pe := NewPayloadEncryptor(c.aesKey) 383 384 ar, rmr, imr, err := NewPayloadReceivers(logger, pe, backend, config.ReceiverConfig) 385 if err != nil { 386 return nil, err 387 } 388 389 return &ReceiverClient{ 390 BaseClient: bc, 391 accountReceiver: ar, 392 rawMessageReceiver: rmr, 393 installationReceiver: imr, 394 }, nil 395 } 396 397 func (c *ReceiverClient) receiveAccountData() error { 398 c.baseAddress.Path = pairingSendAccount 399 req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil) 400 if err != nil { 401 return err 402 } 403 404 err = c.challengeTaker.DoChallenge(req) 405 if err != nil { 406 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount}) 407 return err 408 } 409 410 resp, err := c.Do(req) 411 if err != nil { 412 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount}) 413 return err 414 } 415 416 if resp.StatusCode != http.StatusOK { 417 err = fmt.Errorf("[client] status not ok when receiving account data, received '%s'", resp.Status) 418 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount}) 419 return err 420 } 421 422 payload, err := io.ReadAll(resp.Body) 423 if err != nil { 424 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount}) 425 return err 426 } 427 signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount}) 428 429 err = c.accountReceiver.Receive(payload) 430 if err != nil { 431 signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingAccount}) 432 return err 433 } 434 signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionPairingAccount}) 435 return nil 436 } 437 438 func (c *ReceiverClient) receiveSyncDeviceData() error { 439 c.baseAddress.Path = pairingSendSyncDevice 440 req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil) 441 if err != nil { 442 return err 443 } 444 445 err = c.challengeTaker.DoChallenge(req) 446 if err != nil { 447 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice}) 448 return err 449 } 450 451 resp, err := c.Do(req) 452 if err != nil { 453 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice}) 454 return err 455 } 456 457 if resp.StatusCode != http.StatusOK { 458 err = fmt.Errorf("[client] status not ok when receiving sync device data, received '%s'", resp.Status) 459 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice}) 460 return err 461 } 462 463 payload, err := io.ReadAll(resp.Body) 464 if err != nil { 465 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice}) 466 return err 467 } 468 signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice}) 469 470 err = c.rawMessageReceiver.Receive(payload) 471 if err != nil { 472 signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionSyncDevice}) 473 return err 474 } 475 signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionSyncDevice}) 476 return nil 477 } 478 479 func (c *ReceiverClient) sendInstallationData() error { 480 err := c.installationReceiver.Mount() 481 if err != nil { 482 return err 483 } 484 485 c.baseAddress.Path = pairingReceiveInstallation 486 req, err := http.NewRequest(http.MethodPost, c.baseAddress.String(), bytes.NewBuffer(c.installationReceiver.ToSend())) 487 if err != nil { 488 return err 489 } 490 req.Header.Set("Content-Type", "application/octet-stream") 491 492 err = c.challengeTaker.DoChallenge(req) 493 if err != nil { 494 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation}) 495 return err 496 } 497 498 resp, err := c.Do(req) 499 if err != nil { 500 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation}) 501 return err 502 } 503 504 if resp.StatusCode != http.StatusOK { 505 err = fmt.Errorf("[client] status not okay when sending installation data, status: %s", resp.Status) 506 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation}) 507 return err 508 } 509 510 signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation}) 511 return nil 512 } 513 514 // setupReceivingClient creates a new ReceiverClient after parsing string inputs 515 func setupReceivingClient(backend *api.GethStatusBackend, cs, configJSON string) (*ReceiverClient, error) { 516 ccp := new(ConnectionParams) 517 err := ccp.FromString(cs) 518 if err != nil { 519 return nil, err 520 } 521 522 conf := NewReceiverClientConfig() 523 err = json.Unmarshal([]byte(configJSON), conf) 524 if err != nil { 525 return nil, err 526 } 527 528 // This is a temporal solution to allow clients not to pass DeviceType. 529 // Check DeviceType deprecation reason for more info. 530 conf.ReceiverConfig.DeviceType = runtime.GOOS 531 532 err = validateReceiverConfig(conf, conf.ReceiverConfig) 533 if err != nil { 534 return nil, err 535 } 536 537 // ignore err because we allow no active account here 538 activeAccount, _ := backend.GetActiveAccount() 539 if activeAccount != nil { 540 conf.ReceiverConfig.LoggedInKeyUID = activeAccount.KeyUID 541 } 542 543 conf.ReceiverConfig.DB = backend.GetMultiaccountDB() 544 545 return NewReceiverClient(backend, ccp, conf) 546 } 547 548 // StartUpReceivingClient creates a ReceiverClient and triggers all `receive` calls in sequence to the SenderServer 549 func StartUpReceivingClient(backend *api.GethStatusBackend, cs, configJSON string) error { 550 c, err := setupReceivingClient(backend, cs, configJSON) 551 if err != nil { 552 return err 553 } 554 555 err = c.getChallenge() 556 if err != nil { 557 return err 558 } 559 err = c.receiveAccountData() 560 if err != nil { 561 return err 562 } 563 564 err = c.getChallenge() 565 if err != nil { 566 return err 567 } 568 err = c.receiveSyncDeviceData() 569 if err != nil { 570 return err 571 } 572 573 err = c.getChallenge() 574 if err != nil { 575 return err 576 } 577 return c.sendInstallationData() 578 } 579 580 /* 581 |-------------------------------------------------------------------------- 582 | ReceiverClient 583 |-------------------------------------------------------------------------- 584 */ 585 586 type KeystoreFilesReceiverClient struct { 587 *BaseClient 588 589 keystoreFilesReceiver PayloadReceiver 590 } 591 592 func NewKeystoreFilesReceiverClient(backend *api.GethStatusBackend, c *ConnectionParams, config *KeystoreFilesReceiverClientConfig) (*KeystoreFilesReceiverClient, error) { 593 logger := logutils.ZapLogger().Named("ReceiverClient") 594 bc, err := NewBaseClient(c, logger) 595 if err != nil { 596 return nil, err 597 } 598 pe := NewPayloadEncryptor(c.aesKey) 599 600 kfrc, err := NewKeystoreFilesPayloadReceiver(backend, pe, config.ReceiverConfig, logger) 601 if err != nil { 602 return nil, err 603 } 604 605 return &KeystoreFilesReceiverClient{ 606 BaseClient: bc, 607 keystoreFilesReceiver: kfrc, 608 }, nil 609 } 610 611 func (c *KeystoreFilesReceiverClient) receiveKeystoreFilesData() error { 612 c.baseAddress.Path = pairingSendAccount 613 req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil) 614 if err != nil { 615 return err 616 } 617 618 err = c.challengeTaker.DoChallenge(req) 619 if err != nil { 620 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionKeystoreFilesTransfer}) 621 return err 622 } 623 624 resp, err := c.Do(req) 625 if err != nil { 626 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionKeystoreFilesTransfer}) 627 return err 628 } 629 630 if resp.StatusCode != http.StatusOK { 631 err = fmt.Errorf("[client] status not ok when receiving account data, received '%s'", resp.Status) 632 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionKeystoreFilesTransfer}) 633 return err 634 } 635 636 payload, err := io.ReadAll(resp.Body) 637 if err != nil { 638 signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionKeystoreFilesTransfer}) 639 return err 640 } 641 signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionKeystoreFilesTransfer}) 642 643 err = c.keystoreFilesReceiver.Receive(payload) 644 if err != nil { 645 signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionKeystoreFilesTransfer}) 646 return err 647 } 648 signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionKeystoreFilesTransfer}) 649 return nil 650 } 651 652 // setupKeystoreFilesReceivingClient creates a new ReceiverClient after parsing string inputs 653 func setupKeystoreFilesReceivingClient(backend *api.GethStatusBackend, cs, configJSON string) (*KeystoreFilesReceiverClient, error) { 654 ccp := new(ConnectionParams) 655 err := ccp.FromString(cs) 656 if err != nil { 657 return nil, err 658 } 659 660 conf := NewKeystoreFilesReceiverClientConfig() 661 err = json.Unmarshal([]byte(configJSON), conf) 662 if err != nil { 663 return nil, err 664 } 665 err = validateKeystoreFilesConfig(backend, conf) 666 if err != nil { 667 return nil, err 668 } 669 670 return NewKeystoreFilesReceiverClient(backend, ccp, conf) 671 } 672 673 // StartUpKeystoreFilesReceivingClient creates a KeystoreFilesReceiverClient and triggers all `receive` calls in sequence to the KeystoreFilesSenderServer 674 func StartUpKeystoreFilesReceivingClient(backend *api.GethStatusBackend, cs, configJSON string) error { 675 c, err := setupKeystoreFilesReceivingClient(backend, cs, configJSON) 676 if err != nil { 677 return err 678 } 679 680 err = c.getChallenge() 681 if err != nil { 682 return err 683 } 684 685 return c.receiveKeystoreFilesData() 686 }