github.com/status-im/status-go@v1.1.0/api/backend_test.go (about) 1 package api 2 3 import ( 4 "context" 5 "crypto/sha256" 6 "database/sql" 7 "encoding/hex" 8 "encoding/json" 9 "fmt" 10 "io/ioutil" 11 "math/rand" 12 "os" 13 "path" 14 "path/filepath" 15 "strings" 16 "sync" 17 "testing" 18 "time" 19 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 23 gethcrypto "github.com/ethereum/go-ethereum/crypto" 24 25 "github.com/status-im/status-go/appdatabase" 26 "github.com/status-im/status-go/connection" 27 "github.com/status-im/status-go/eth-node/crypto" 28 "github.com/status-im/status-go/eth-node/types" 29 "github.com/status-im/status-go/multiaccounts" 30 "github.com/status-im/status-go/multiaccounts/accounts" 31 "github.com/status-im/status-go/multiaccounts/settings" 32 "github.com/status-im/status-go/node" 33 "github.com/status-im/status-go/params" 34 "github.com/status-im/status-go/protocol/requests" 35 "github.com/status-im/status-go/rpc" 36 "github.com/status-im/status-go/services/typeddata" 37 "github.com/status-im/status-go/services/wallet" 38 walletservice "github.com/status-im/status-go/services/wallet" 39 "github.com/status-im/status-go/signal" 40 "github.com/status-im/status-go/sqlite" 41 "github.com/status-im/status-go/t/helpers" 42 "github.com/status-im/status-go/t/utils" 43 "github.com/status-im/status-go/transactions" 44 "github.com/status-im/status-go/walletdatabase" 45 ) 46 47 var ( 48 networks = json.RawMessage("{}") 49 testSettings = settings.Settings{ 50 Address: types.HexToAddress("0xeC540f3745Ff2964AFC1171a5A0DD726d1F6B472"), 51 DisplayName: "UserDisplayName", 52 CurrentNetwork: "mainnet_rpc", 53 DappsAddress: types.HexToAddress("0xe1300f99fDF7346986CbC766903245087394ecd0"), 54 EIP1581Address: types.HexToAddress("0xe1DDDE9235a541d1344550d969715CF43982de9f"), 55 InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51", 56 KeyUID: "0x4e8129f3edfc004875be17bf468a784098a9f69b53c095be1f52deff286935ab", 57 LatestDerivedPath: 0, 58 Name: "Jittery Cornflowerblue Kingbird", 59 Networks: &networks, 60 PhotoPath: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==", 61 PreviewPrivacy: false, 62 PublicKey: "0x04211fe0f69772ecf7eb0b5bfc7678672508a9fb01f2d699096f0d59ef7fe1a0cb1e648a80190db1c0f5f088872444d846f2956d0bd84069f3f9f69335af852ac0", 63 SigningPhrase: "yurt joey vibe", 64 WalletRootAddress: types.HexToAddress("0xeB591fd819F86D0A6a2EF2Bcb94f77807a7De1a6")} 65 ) 66 67 func setupTestDB() (*sql.DB, func() error, error) { 68 return helpers.SetupTestSQLDB(appdatabase.DbInitializer{}, "tests") 69 } 70 71 func setupTestWalletDB() (*sql.DB, func() error, error) { 72 return helpers.SetupTestSQLDB(walletdatabase.DbInitializer{}, "tests") 73 } 74 75 func setupTestMultiDB() (*multiaccounts.Database, func() error, error) { 76 tmpfile, err := ioutil.TempFile("", "tests") 77 if err != nil { 78 return nil, nil, err 79 } 80 db, err := multiaccounts.InitializeDB(tmpfile.Name()) 81 if err != nil { 82 return nil, nil, err 83 } 84 return db, func() error { 85 err := db.Close() 86 if err != nil { 87 return err 88 } 89 return os.Remove(tmpfile.Name()) 90 }, nil 91 } 92 93 func setupGethStatusBackend() (*GethStatusBackend, func() error, func() error, func() error, error) { 94 db, stop1, err := setupTestDB() 95 if err != nil { 96 return nil, nil, nil, nil, err 97 } 98 backend := NewGethStatusBackend() 99 backend.StatusNode().SetAppDB(db) 100 101 ma, stop2, err := setupTestMultiDB() 102 if err != nil { 103 return nil, nil, nil, nil, err 104 } 105 backend.StatusNode().SetMultiaccountsDB(ma) 106 107 walletDb, stop3, err := setupTestWalletDB() 108 if err != nil { 109 return nil, nil, nil, nil, err 110 } 111 backend.StatusNode().SetWalletDB(walletDb) 112 113 return backend, stop1, stop2, stop3, err 114 } 115 116 func TestBackendStartNodeConcurrently(t *testing.T) { 117 utils.Init() 118 119 backend, stop1, stop2, stop3, err := setupGethStatusBackend() 120 defer func() { 121 err := stop1() 122 if err != nil { 123 require.NoError(t, backend.StopNode()) 124 } 125 }() 126 defer func() { 127 err := stop2() 128 if err != nil { 129 require.NoError(t, backend.StopNode()) 130 } 131 }() 132 defer func() { 133 err := stop3() 134 if err != nil { 135 require.NoError(t, backend.StopNode()) 136 } 137 }() 138 require.NoError(t, err) 139 140 config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID) 141 require.NoError(t, err) 142 require.NoError(t, backend.AccountManager().InitKeystore(config.KeyStoreDir)) 143 count := 2 144 resultCh := make(chan error) 145 146 var wg sync.WaitGroup 147 wg.Add(count) 148 149 for i := 0; i < count; i++ { 150 go func() { 151 resultCh <- backend.StartNode(config) 152 wg.Done() 153 }() 154 } 155 156 // close channel as otherwise for loop never finishes 157 go func() { wg.Wait(); close(resultCh) }() 158 159 var results []error 160 for err := range resultCh { 161 results = append(results, err) 162 } 163 164 require.Contains(t, results, nil) 165 require.Contains(t, results, node.ErrNodeRunning) 166 167 err = backend.StopNode() 168 require.NoError(t, err) 169 } 170 171 func TestBackendRestartNodeConcurrently(t *testing.T) { 172 utils.Init() 173 174 backend, stop1, stop2, stopWallet, err := setupGethStatusBackend() 175 defer func() { 176 err := stop1() 177 if err != nil { 178 require.NoError(t, backend.StopNode()) 179 } 180 }() 181 defer func() { 182 err := stop2() 183 if err != nil { 184 require.NoError(t, backend.StopNode()) 185 } 186 }() 187 defer func() { 188 err := stopWallet() 189 if err != nil { 190 require.NoError(t, backend.StopNode()) 191 } 192 }() 193 require.NoError(t, err) 194 195 config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID) 196 require.NoError(t, err) 197 count := 3 198 require.NoError(t, backend.AccountManager().InitKeystore(config.KeyStoreDir)) 199 require.NoError(t, backend.StartNode(config)) 200 defer func() { 201 require.NoError(t, backend.StopNode()) 202 }() 203 204 var wg sync.WaitGroup 205 wg.Add(count) 206 207 for i := 0; i < count; i++ { 208 go func(idx int) { 209 assert.NoError(t, backend.RestartNode()) 210 wg.Done() 211 }(i) 212 } 213 214 wg.Wait() 215 } 216 217 // TODO(adam): add concurrent tests for ResetChainData() 218 219 func TestBackendGettersConcurrently(t *testing.T) { 220 utils.Init() 221 222 backend, stop1, stop2, stopWallet, err := setupGethStatusBackend() 223 defer func() { 224 err := stop1() 225 if err != nil { 226 require.NoError(t, backend.StopNode()) 227 } 228 }() 229 defer func() { 230 err := stop2() 231 if err != nil { 232 require.NoError(t, backend.StopNode()) 233 } 234 }() 235 defer func() { 236 err := stopWallet() 237 if err != nil { 238 require.NoError(t, backend.StopNode()) 239 } 240 }() 241 require.NoError(t, err) 242 243 config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID) 244 require.NoError(t, err) 245 require.NoError(t, backend.AccountManager().InitKeystore(config.KeyStoreDir)) 246 err = backend.StartNode(config) 247 require.NoError(t, err) 248 defer func() { 249 require.NoError(t, backend.StopNode()) 250 }() 251 252 var wg sync.WaitGroup 253 254 wg.Add(1) 255 go func() { 256 assert.NotNil(t, backend.StatusNode()) 257 wg.Done() 258 }() 259 260 wg.Add(1) 261 go func() { 262 assert.NotNil(t, backend.AccountManager()) 263 wg.Done() 264 }() 265 266 wg.Add(1) 267 go func() { 268 assert.NotNil(t, backend.personalAPI) 269 wg.Done() 270 }() 271 272 wg.Add(1) 273 go func() { 274 assert.NotNil(t, backend.Transactor()) 275 wg.Done() 276 }() 277 278 wg.Add(1) 279 go func() { 280 assert.True(t, backend.IsNodeRunning()) 281 wg.Done() 282 }() 283 284 wg.Add(1) 285 go func() { 286 assert.True(t, backend.IsNodeRunning()) 287 wg.Done() 288 }() 289 290 wg.Wait() 291 } 292 293 func TestBackendConnectionChangesConcurrently(t *testing.T) { 294 connections := [...]string{connection.Wifi, connection.Cellular, connection.Unknown} 295 backend := NewGethStatusBackend() 296 count := 3 297 298 var wg sync.WaitGroup 299 300 for i := 0; i < count; i++ { 301 wg.Add(1) 302 go func() { 303 connIdx := rand.Intn(len(connections)) // nolint: gosec 304 backend.ConnectionChange(connections[connIdx], false) 305 wg.Done() 306 }() 307 } 308 309 wg.Wait() 310 } 311 312 func TestBackendConnectionChangesToOffline(t *testing.T) { 313 b := NewGethStatusBackend() 314 b.ConnectionChange(connection.None, false) 315 assert.True(t, b.connectionState.Offline) 316 317 b.ConnectionChange(connection.Wifi, false) 318 assert.False(t, b.connectionState.Offline) 319 320 b.ConnectionChange("unknown-state", false) 321 assert.False(t, b.connectionState.Offline) 322 } 323 324 func TestBackendCallRPCConcurrently(t *testing.T) { 325 utils.Init() 326 327 backend, stop1, stop2, stopWallet, err := setupGethStatusBackend() 328 defer func() { 329 err := stop1() 330 if err != nil { 331 require.NoError(t, backend.StopNode()) 332 } 333 }() 334 defer func() { 335 err := stop2() 336 if err != nil { 337 require.NoError(t, backend.StopNode()) 338 } 339 }() 340 defer func() { 341 err := stopWallet() 342 if err != nil { 343 require.NoError(t, backend.StopNode()) 344 } 345 }() 346 require.NoError(t, err) 347 348 config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID) 349 require.NoError(t, err) 350 require.NoError(t, backend.AccountManager().InitKeystore(config.KeyStoreDir)) 351 count := 3 352 353 err = backend.StartNode(config) 354 require.NoError(t, err) 355 defer func() { 356 require.NoError(t, backend.StopNode()) 357 }() 358 359 var wg sync.WaitGroup 360 361 for i := 0; i < count; i++ { 362 wg.Add(1) 363 go func(idx int) { 364 result, err := backend.CallRPC(fmt.Sprintf( 365 `{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":%d}`, 366 idx+1, 367 )) 368 assert.NoError(t, err) 369 assert.NotContains(t, result, "error") 370 wg.Done() 371 }(i) 372 373 wg.Add(1) 374 go func(idx int) { 375 result, err := backend.CallPrivateRPC(fmt.Sprintf( 376 `{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":%d}`, 377 idx+1, 378 )) 379 assert.NoError(t, err) 380 assert.NotContains(t, result, "error") 381 wg.Done() 382 }(i) 383 } 384 385 wg.Wait() 386 } 387 388 func TestAppStateChange(t *testing.T) { 389 backend := NewGethStatusBackend() 390 391 var testCases = []struct { 392 name string 393 fromState appState 394 toState appState 395 expectedState appState 396 }{ 397 { 398 name: "success", 399 fromState: appStateInactive, 400 toState: appStateBackground, 401 expectedState: appStateBackground, 402 }, 403 { 404 name: "invalid state", 405 fromState: appStateInactive, 406 toState: "unexisting", 407 expectedState: appStateInactive, 408 }, 409 } 410 411 for _, tc := range testCases { 412 t.Run(tc.name, func(t *testing.T) { 413 backend.appState = tc.fromState 414 backend.AppStateChange(tc.toState.String()) 415 assert.Equal(t, tc.expectedState.String(), backend.appState.String()) 416 }) 417 } 418 } 419 420 func TestBlockedRPCMethods(t *testing.T) { 421 utils.Init() 422 423 backend, stop1, stop2, stopWallet, err := setupGethStatusBackend() 424 defer func() { 425 err := stop1() 426 if err != nil { 427 require.NoError(t, backend.StopNode()) 428 } 429 }() 430 defer func() { 431 err := stop2() 432 if err != nil { 433 require.NoError(t, backend.StopNode()) 434 } 435 }() 436 defer func() { 437 err := stopWallet() 438 if err != nil { 439 require.NoError(t, backend.StopNode()) 440 } 441 }() 442 require.NoError(t, err) 443 444 config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID) 445 require.NoError(t, err) 446 require.NoError(t, backend.AccountManager().InitKeystore(config.KeyStoreDir)) 447 err = backend.StartNode(config) 448 require.NoError(t, err) 449 defer func() { require.NoError(t, backend.StopNode()) }() 450 451 for idx, m := range rpc.BlockedMethods() { 452 result, err := backend.CallRPC(fmt.Sprintf( 453 `{"jsonrpc":"2.0","method":"%s","params":[],"id":%d}`, 454 m, 455 idx+1, 456 )) 457 assert.NoError(t, err) 458 assert.Contains(t, result, fmt.Sprintf(`{"code":-32700,"message":"%s"}`, rpc.ErrMethodNotFound)) 459 } 460 } 461 462 func TestCallRPCWithStoppedNode(t *testing.T) { 463 backend := NewGethStatusBackend() 464 465 resp, err := backend.CallRPC( 466 `{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}`, 467 ) 468 assert.Equal(t, ErrRPCClientUnavailable, err) 469 assert.Equal(t, "", resp) 470 471 resp, err = backend.CallPrivateRPC( 472 `{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}`, 473 ) 474 assert.Equal(t, ErrRPCClientUnavailable, err) 475 assert.Equal(t, "", resp) 476 } 477 478 // TODO(adam): add concurrent tests for: SendTransaction 479 480 func TestStartStopMultipleTimes(t *testing.T) { 481 utils.Init() 482 483 backend, stop1, stop2, stopWallet, err := setupGethStatusBackend() 484 defer func() { 485 err := stop1() 486 if err != nil { 487 require.NoError(t, backend.StopNode()) 488 } 489 }() 490 defer func() { 491 err := stop2() 492 if err != nil { 493 require.NoError(t, backend.StopNode()) 494 } 495 }() 496 defer func() { 497 err := stopWallet() 498 if err != nil { 499 require.NoError(t, backend.StopNode()) 500 } 501 }() 502 require.NoError(t, err) 503 504 config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID) 505 require.NoError(t, err) 506 require.NoError(t, backend.AccountManager().InitKeystore(config.KeyStoreDir)) 507 config.NoDiscovery = false 508 // doesn't have to be running. just any valid enode to bypass validation. 509 config.ClusterConfig.BootNodes = []string{ 510 "enode://e8a7c03b58911e98bbd66accb2a55d57683f35b23bf9dfca89e5e244eb5cc3f25018b4112db507faca34fb69ffb44b362f79eda97a669a8df29c72e654416784@0.0.0.0:30404", 511 } 512 require.NoError(t, err) 513 require.NoError(t, backend.StartNode(config)) 514 require.NoError(t, backend.StopNode()) 515 require.NoError(t, backend.StartNode(config)) 516 require.NoError(t, backend.StopNode()) 517 } 518 519 func TestHashTypedData(t *testing.T) { 520 utils.Init() 521 522 backend, stop1, stop2, stopWallet, err := setupGethStatusBackend() 523 defer func() { 524 err := stop1() 525 if err != nil { 526 require.NoError(t, backend.StopNode()) 527 } 528 }() 529 defer func() { 530 err := stop2() 531 if err != nil { 532 require.NoError(t, backend.StopNode()) 533 } 534 }() 535 defer func() { 536 err := stopWallet() 537 if err != nil { 538 require.NoError(t, backend.StopNode()) 539 } 540 }() 541 require.NoError(t, err) 542 543 config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID) 544 require.NoError(t, err) 545 require.NoError(t, backend.AccountManager().InitKeystore(config.KeyStoreDir)) 546 err = backend.StartNode(config) 547 require.NoError(t, err) 548 defer func() { 549 require.NoError(t, backend.StopNode()) 550 }() 551 552 eip712Domain := "EIP712Domain" 553 mytypes := typeddata.Types{ 554 eip712Domain: []typeddata.Field{ 555 {Name: "name", Type: "string"}, 556 {Name: "version", Type: "string"}, 557 {Name: "chainId", Type: "uint256"}, 558 {Name: "verifyingContract", Type: "address"}, 559 }, 560 "Text": []typeddata.Field{ 561 {Name: "body", Type: "string"}, 562 }, 563 } 564 565 domain := map[string]json.RawMessage{ 566 "name": json.RawMessage(`"Ether Text"`), 567 "version": json.RawMessage(`"1"`), 568 "chainId": json.RawMessage(fmt.Sprintf("%d", params.StatusChainNetworkID)), 569 "verifyingContract": json.RawMessage(`"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"`), 570 } 571 msg := map[string]json.RawMessage{ 572 "body": json.RawMessage(`"Hello, Bob!"`), 573 } 574 575 typed := typeddata.TypedData{ 576 Types: mytypes, 577 PrimaryType: "Text", 578 Domain: domain, 579 Message: msg, 580 } 581 582 hash, err := backend.HashTypedData(typed) 583 require.NoError(t, err) 584 assert.NotEqual(t, types.Hash{}, hash) 585 } 586 587 func TestBackendGetVerifiedAccount(t *testing.T) { 588 utils.Init() 589 590 password := "test" 591 backend, defers, err := setupWalletTest(t, password) 592 require.NoError(t, err) 593 defer defers() 594 595 t.Run("AccountDoesntExist", func(t *testing.T) { 596 pkey, err := gethcrypto.GenerateKey() 597 require.NoError(t, err) 598 address := gethcrypto.PubkeyToAddress(pkey.PublicKey) 599 key, err := backend.getVerifiedWalletAccount(address.String(), password) 600 require.EqualError(t, err, transactions.ErrAccountDoesntExist.Error()) 601 require.Nil(t, key) 602 }) 603 604 t.Run("PasswordDoesntMatch", func(t *testing.T) { 605 pkey, err := crypto.GenerateKey() 606 require.NoError(t, err) 607 address := crypto.PubkeyToAddress(pkey.PublicKey) 608 keyUIDHex := sha256.Sum256(gethcrypto.FromECDSAPub(&pkey.PublicKey)) 609 keyUID := types.EncodeHex(keyUIDHex[:]) 610 611 db, err := accounts.NewDB(backend.appDB) 612 613 require.NoError(t, err) 614 _, err = backend.AccountManager().ImportAccount(pkey, password) 615 require.NoError(t, err) 616 require.NoError(t, db.SaveOrUpdateKeypair(&accounts.Keypair{ 617 KeyUID: keyUID, 618 Name: "private key keypair", 619 Type: accounts.KeypairTypeKey, 620 Accounts: []*accounts.Account{ 621 &accounts.Account{ 622 Address: address, 623 KeyUID: keyUID, 624 }, 625 }, 626 })) 627 key, err := backend.getVerifiedWalletAccount(address.String(), "wrong-password") 628 require.EqualError(t, err, "could not decrypt key with given password") 629 require.Nil(t, key) 630 }) 631 632 t.Run("PartialAccount", func(t *testing.T) { 633 // Create a derived wallet account without storing the keys 634 db, err := accounts.NewDB(backend.appDB) 635 require.NoError(t, err) 636 newPath := "m/0" 637 walletRootAddress, err := db.GetWalletRootAddress() 638 require.NoError(t, err) 639 640 walletInfo, err := backend.AccountManager().AccountsGenerator().LoadAccount(walletRootAddress.String(), password) 641 require.NoError(t, err) 642 derivedInfos, err := backend.AccountManager().AccountsGenerator().DeriveAddresses(walletInfo.ID, []string{newPath}) 643 require.NoError(t, err) 644 derivedInfo := derivedInfos[newPath] 645 646 keypair := &accounts.Keypair{ 647 KeyUID: walletInfo.KeyUID, 648 Name: "profile keypair", 649 Type: accounts.KeypairTypeProfile, 650 Accounts: []*accounts.Account{ 651 &accounts.Account{ 652 Address: types.HexToAddress(derivedInfo.Address), 653 KeyUID: walletInfo.KeyUID, 654 Type: accounts.AccountTypeGenerated, 655 PublicKey: types.Hex2Bytes(derivedInfo.PublicKey), 656 Path: newPath, 657 Wallet: false, 658 Name: "PartialAccount", 659 }, 660 }, 661 } 662 require.NoError(t, db.SaveOrUpdateKeypair(keypair)) 663 664 // With partial account we need to dynamically generate private key 665 key, err := backend.getVerifiedWalletAccount(keypair.Accounts[0].Address.Hex(), password) 666 require.NoError(t, err) 667 require.Equal(t, keypair.Accounts[0].Address, key.Address) 668 }) 669 670 t.Run("Success", func(t *testing.T) { 671 pkey, err := crypto.GenerateKey() 672 require.NoError(t, err) 673 address := crypto.PubkeyToAddress(pkey.PublicKey) 674 keyUIDHex := sha256.Sum256(gethcrypto.FromECDSAPub(&pkey.PublicKey)) 675 keyUID := types.EncodeHex(keyUIDHex[:]) 676 677 db, err := accounts.NewDB(backend.appDB) 678 require.NoError(t, err) 679 defer db.Close() 680 _, err = backend.AccountManager().ImportAccount(pkey, password) 681 require.NoError(t, err) 682 require.NoError(t, db.SaveOrUpdateKeypair(&accounts.Keypair{ 683 KeyUID: keyUID, 684 Name: "private key keypair", 685 Type: accounts.KeypairTypeKey, 686 Accounts: []*accounts.Account{ 687 &accounts.Account{ 688 Address: address, 689 KeyUID: keyUID, 690 }, 691 }, 692 })) 693 key, err := backend.getVerifiedWalletAccount(address.String(), password) 694 require.NoError(t, err) 695 require.Equal(t, address, key.Address) 696 }) 697 } 698 699 func TestRuntimeLogLevelIsNotWrittenToDatabase(t *testing.T) { 700 utils.Init() 701 702 b := NewGethStatusBackend() 703 chatKey, err := gethcrypto.GenerateKey() 704 require.NoError(t, err) 705 walletKey, err := gethcrypto.GenerateKey() 706 require.NoError(t, err) 707 keyUIDHex := sha256.Sum256(gethcrypto.FromECDSAPub(&chatKey.PublicKey)) 708 keyUID := types.EncodeHex(keyUIDHex[:]) 709 main := multiaccounts.Account{ 710 KeyUID: keyUID, 711 } 712 713 tmpdir := t.TempDir() 714 715 json := `{ 716 "NetworkId": 3, 717 "DataDir": "` + tmpdir + `", 718 "KeyStoreDir": "` + tmpdir + `", 719 "KeycardPairingDataFile": "` + path.Join(tmpdir, "keycard/pairings.json") + `", 720 "NoDiscovery": true, 721 "TorrentConfig": { 722 "Port": 9025, 723 "Enabled": false, 724 "DataDir": "` + tmpdir + `/archivedata", 725 "TorrentDir": "` + tmpdir + `/torrents" 726 }, 727 "RuntimeLogLevel": "INFO", 728 "LogLevel": "DEBUG" 729 }` 730 731 conf, err := params.NewConfigFromJSON(json) 732 require.NoError(t, err) 733 require.Equal(t, "INFO", conf.RuntimeLogLevel) 734 keyhex := hex.EncodeToString(gethcrypto.FromECDSA(chatKey)) 735 736 require.NoError(t, b.AccountManager().InitKeystore(conf.KeyStoreDir)) 737 b.UpdateRootDataDir(conf.DataDir) 738 require.NoError(t, b.OpenAccounts()) 739 require.NotNil(t, b.statusNode.HTTPServer()) 740 741 address := crypto.PubkeyToAddress(walletKey.PublicKey) 742 743 settings := testSettings 744 settings.KeyUID = keyUID 745 settings.Address = crypto.PubkeyToAddress(walletKey.PublicKey) 746 747 chatPubKey := crypto.FromECDSAPub(&chatKey.PublicKey) 748 require.NoError(t, b.SaveAccountAndStartNodeWithKey(main, "test-pass", settings, conf, 749 []*accounts.Account{ 750 {Address: address, KeyUID: keyUID, Wallet: true}, 751 {Address: crypto.PubkeyToAddress(chatKey.PublicKey), KeyUID: keyUID, Chat: true, PublicKey: chatPubKey}}, keyhex)) 752 require.NoError(t, b.Logout()) 753 require.NoError(t, b.StopNode()) 754 755 require.NoError(t, b.StartNodeWithKey(main, "test-pass", keyhex, conf)) 756 defer func() { 757 assert.NoError(t, b.Logout()) 758 assert.NoError(t, b.StopNode()) 759 }() 760 761 c, err := b.GetNodeConfig() 762 require.NoError(t, err) 763 require.Equal(t, "", c.RuntimeLogLevel) 764 require.Equal(t, "DEBUG", c.LogLevel) 765 } 766 767 func TestLoginWithKey(t *testing.T) { 768 utils.Init() 769 770 b := NewGethStatusBackend() 771 chatKey, err := gethcrypto.GenerateKey() 772 require.NoError(t, err) 773 walletKey, err := gethcrypto.GenerateKey() 774 require.NoError(t, err) 775 keyUIDHex := sha256.Sum256(gethcrypto.FromECDSAPub(&chatKey.PublicKey)) 776 keyUID := types.EncodeHex(keyUIDHex[:]) 777 main := multiaccounts.Account{ 778 KeyUID: keyUID, 779 } 780 tmpdir := t.TempDir() 781 conf, err := params.NewNodeConfig(tmpdir, 1777) 782 require.NoError(t, err) 783 keyhex := hex.EncodeToString(gethcrypto.FromECDSA(chatKey)) 784 785 require.NoError(t, b.AccountManager().InitKeystore(conf.KeyStoreDir)) 786 b.UpdateRootDataDir(conf.DataDir) 787 require.NoError(t, b.OpenAccounts()) 788 require.NotNil(t, b.statusNode.HTTPServer()) 789 790 address := crypto.PubkeyToAddress(walletKey.PublicKey) 791 792 settings := testSettings 793 settings.KeyUID = keyUID 794 settings.Address = crypto.PubkeyToAddress(walletKey.PublicKey) 795 796 chatPubKey := crypto.FromECDSAPub(&chatKey.PublicKey) 797 require.NoError(t, b.SaveAccountAndStartNodeWithKey(main, "test-pass", settings, conf, 798 []*accounts.Account{ 799 {Address: address, KeyUID: keyUID, Wallet: true}, 800 {Address: crypto.PubkeyToAddress(chatKey.PublicKey), KeyUID: keyUID, Chat: true, PublicKey: chatPubKey}}, keyhex)) 801 require.NoError(t, b.Logout()) 802 require.NoError(t, b.StopNode()) 803 804 require.NoError(t, b.AccountManager().InitKeystore(conf.KeyStoreDir)) 805 b.UpdateRootDataDir(conf.DataDir) 806 require.NoError(t, b.OpenAccounts()) 807 808 require.NoError(t, b.StartNodeWithKey(main, "test-pass", keyhex, conf)) 809 defer func() { 810 assert.NoError(t, b.Logout()) 811 assert.NoError(t, b.StopNode()) 812 }() 813 extkey, err := b.accountManager.SelectedChatAccount() 814 require.NoError(t, err) 815 require.Equal(t, crypto.PubkeyToAddress(chatKey.PublicKey), extkey.Address) 816 817 activeAccount, err := b.GetActiveAccount() 818 require.NoError(t, err) 819 require.NotNil(t, activeAccount.ColorHash) 820 } 821 822 func TestLoginAccount(t *testing.T) { 823 utils.Init() 824 password := "some-password" 825 tmpdir := t.TempDir() 826 nameserver := "8.8.8.8" 827 828 b := NewGethStatusBackend() 829 createAccountRequest := &requests.CreateAccount{ 830 DisplayName: "some-display-name", 831 CustomizationColor: "#ffffff", 832 Password: password, 833 RootDataDir: tmpdir, 834 LogFilePath: tmpdir + "/log", 835 WakuV2Nameserver: &nameserver, 836 WakuV2Fleet: "status.staging", 837 } 838 c := make(chan interface{}, 10) 839 signal.SetMobileSignalHandler(func(data []byte) { 840 if strings.Contains(string(data), signal.EventLoggedIn) { 841 require.Contains(t, string(data), "status.staging") 842 c <- struct{}{} 843 } 844 }) 845 t.Cleanup(signal.ResetMobileSignalHandler) 846 waitForLogin := func(chan interface{}) { 847 select { 848 case <-c: 849 break 850 case <-time.After(5 * time.Second): 851 t.FailNow() 852 } 853 } 854 855 acc, err := b.CreateAccountAndLogin(createAccountRequest) 856 require.NoError(t, err) 857 require.Equal(t, nameserver, b.config.WakuV2Config.Nameserver) 858 859 waitForLogin(c) 860 require.NoError(t, b.Logout()) 861 require.NoError(t, b.StopNode()) 862 863 accounts, err := b.GetAccounts() 864 require.NoError(t, err) 865 require.Len(t, accounts, 1) 866 867 require.NotEmpty(t, accounts[0].KeyUID) 868 require.Equal(t, acc.KeyUID, accounts[0].KeyUID) 869 870 loginAccountRequest := &requests.Login{ 871 KeyUID: accounts[0].KeyUID, 872 Password: password, 873 WakuV2Nameserver: nameserver, 874 } 875 err = b.LoginAccount(loginAccountRequest) 876 require.NoError(t, err) 877 waitForLogin(c) 878 879 require.Equal(t, nameserver, b.config.WakuV2Config.Nameserver) 880 } 881 882 func TestVerifyDatabasePassword(t *testing.T) { 883 utils.Init() 884 885 b := NewGethStatusBackend() 886 chatKey, err := gethcrypto.GenerateKey() 887 require.NoError(t, err) 888 walletKey, err := gethcrypto.GenerateKey() 889 require.NoError(t, err) 890 keyUIDHex := sha256.Sum256(gethcrypto.FromECDSAPub(&chatKey.PublicKey)) 891 keyUID := types.EncodeHex(keyUIDHex[:]) 892 main := multiaccounts.Account{ 893 KeyUID: keyUID, 894 } 895 tmpdir := t.TempDir() 896 conf, err := params.NewNodeConfig(tmpdir, 1777) 897 require.NoError(t, err) 898 keyhex := hex.EncodeToString(gethcrypto.FromECDSA(chatKey)) 899 900 require.NoError(t, b.AccountManager().InitKeystore(conf.KeyStoreDir)) 901 b.UpdateRootDataDir(conf.DataDir) 902 require.NoError(t, b.OpenAccounts()) 903 904 address := crypto.PubkeyToAddress(walletKey.PublicKey) 905 906 settings := testSettings 907 settings.KeyUID = keyUID 908 settings.Address = crypto.PubkeyToAddress(walletKey.PublicKey) 909 910 chatPubKey := crypto.FromECDSAPub(&chatKey.PublicKey) 911 912 require.NoError(t, b.SaveAccountAndStartNodeWithKey(main, "test-pass", settings, conf, []*accounts.Account{ 913 {Address: address, KeyUID: keyUID, Wallet: true}, 914 {Address: crypto.PubkeyToAddress(chatKey.PublicKey), KeyUID: keyUID, Chat: true, PublicKey: chatPubKey}}, keyhex)) 915 require.NoError(t, b.Logout()) 916 require.NoError(t, b.StopNode()) 917 918 require.Error(t, b.VerifyDatabasePassword(main.KeyUID, "wrong-pass")) 919 require.NoError(t, b.VerifyDatabasePassword(main.KeyUID, "test-pass")) 920 } 921 922 func TestDeleteMultiaccount(t *testing.T) { 923 backend := NewGethStatusBackend() 924 925 rootDataDir := t.TempDir() 926 927 keyStoreDir := filepath.Join(rootDataDir, "keystore") 928 929 backend.rootDataDir = rootDataDir 930 931 err := backend.AccountManager().InitKeystore(keyStoreDir) 932 require.NoError(t, err) 933 934 backend.AccountManager() 935 accs, err := backend.AccountManager(). 936 AccountsGenerator(). 937 GenerateAndDeriveAddresses(12, 1, "", []string{"m/44'/60'/0'/0"}) 938 require.NoError(t, err) 939 940 generateAccount := accs[0] 941 accountInfo, err := backend.AccountManager(). 942 AccountsGenerator(). 943 StoreAccount(generateAccount.ID, "123123") 944 require.NoError(t, err) 945 946 account := multiaccounts.Account{ 947 Name: "foo", 948 Timestamp: 1, 949 KeycardPairing: "pairing", 950 KeyUID: generateAccount.KeyUID, 951 } 952 953 err = backend.ensureAppDBOpened(account, "123123") 954 require.NoError(t, err) 955 956 s := settings.Settings{ 957 Address: types.HexToAddress(accountInfo.Address), 958 CurrentNetwork: "mainnet_rpc", 959 DappsAddress: types.HexToAddress(accountInfo.Address), 960 EIP1581Address: types.HexToAddress(accountInfo.Address), 961 InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51", 962 KeyUID: account.KeyUID, 963 LatestDerivedPath: 0, 964 Name: "Jittery Cornflowerblue Kingbird", 965 Networks: &networks, 966 PhotoPath: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==", 967 PreviewPrivacy: false, 968 PublicKey: accountInfo.PublicKey, 969 SigningPhrase: "yurt joey vibe", 970 WalletRootAddress: types.HexToAddress(accountInfo.Address)} 971 972 err = backend.saveAccountsAndSettings( 973 s, 974 ¶ms.NodeConfig{}, 975 nil) 976 require.Error(t, err) 977 require.True(t, err == accounts.ErrKeypairWithoutAccounts) 978 979 err = backend.OpenAccounts() 980 require.NoError(t, err) 981 982 err = backend.SaveAccount(account) 983 require.NoError(t, err) 984 985 files, err := ioutil.ReadDir(rootDataDir) 986 require.NoError(t, err) 987 require.NotEqual(t, 3, len(files)) 988 989 err = backend.DeleteMultiaccount(account.KeyUID, keyStoreDir) 990 require.NoError(t, err) 991 992 files, err = ioutil.ReadDir(rootDataDir) 993 require.NoError(t, err) 994 require.Equal(t, 3, len(files)) 995 } 996 997 func TestConvertAccount(t *testing.T) { 998 const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" 999 const password = "111111" // represents password for a regular user 1000 const keycardPassword = "222222" // represents password for a keycard user 1001 const keycardUID = "1234" 1002 const pathEIP1581Root = "m/43'/60'/1581'" 1003 const pathEIP1581Chat = pathEIP1581Root + "/0'/0" 1004 const pathWalletRoot = "m/44'/60'/0'/0" 1005 const pathDefaultWalletAccount = pathWalletRoot + "/0" 1006 const customWalletPath1 = pathWalletRoot + "/1" 1007 const customWalletPath2 = pathWalletRoot + "/2" 1008 var allGeneratedPaths []string 1009 allGeneratedPaths = append(allGeneratedPaths, pathEIP1581Root, pathEIP1581Chat, pathWalletRoot, pathDefaultWalletAccount, customWalletPath1, customWalletPath2) 1010 1011 var err error 1012 1013 keystoreContainsFileForAccount := func(keyStoreDir string, hexAddress string) bool { 1014 addrWithoutPrefix := strings.ToLower(hexAddress[2:]) 1015 found := false 1016 err = filepath.Walk(keyStoreDir, func(path string, fileInfo os.FileInfo, err error) error { 1017 if err != nil { 1018 return err 1019 } 1020 if !fileInfo.IsDir() && strings.Contains(strings.ToUpper(path), strings.ToUpper(addrWithoutPrefix)) { 1021 found = true 1022 } 1023 return nil 1024 }) 1025 return found 1026 } 1027 1028 rootDataDir := t.TempDir() 1029 1030 keyStoreDir := filepath.Join(rootDataDir, "keystore") 1031 1032 utils.Init() 1033 1034 backend, stop1, stop2, stopWallet, err := setupGethStatusBackend() 1035 defer func() { 1036 err := stop1() 1037 if err != nil { 1038 require.NoError(t, backend.StopNode()) 1039 } 1040 }() 1041 defer func() { 1042 err := stop2() 1043 if err != nil { 1044 require.NoError(t, backend.StopNode()) 1045 } 1046 }() 1047 defer func() { 1048 err := stopWallet() 1049 if err != nil { 1050 require.NoError(t, backend.StopNode()) 1051 } 1052 }() 1053 require.NoError(t, err) 1054 1055 backend.rootDataDir = rootDataDir 1056 require.NoError(t, backend.AccountManager().InitKeystore(keyStoreDir)) 1057 err = backend.OpenAccounts() 1058 require.NoError(t, err) 1059 1060 genAccInfo, err := backend.AccountManager().AccountsGenerator().ImportMnemonic(mnemonic, "") 1061 assert.NoError(t, err) 1062 1063 masterAddress := genAccInfo.Address 1064 1065 accountInfo, err := backend.AccountManager().AccountsGenerator().StoreAccount(genAccInfo.ID, password) 1066 assert.NoError(t, err) 1067 1068 found := keystoreContainsFileForAccount(keyStoreDir, accountInfo.Address) 1069 require.True(t, found) 1070 1071 derivedAccounts, err := backend.AccountManager().AccountsGenerator().StoreDerivedAccounts(genAccInfo.ID, password, allGeneratedPaths) 1072 assert.NoError(t, err) 1073 1074 chatKey := derivedAccounts[pathEIP1581Chat].PrivateKey[2:] 1075 chatAddress := derivedAccounts[pathEIP1581Chat].Address 1076 found = keystoreContainsFileForAccount(keyStoreDir, chatAddress) 1077 require.True(t, found) 1078 1079 defaultSettings, err := defaultSettings(genAccInfo.KeyUID, genAccInfo.Address, derivedAccounts) 1080 require.NoError(t, err) 1081 nodeConfig, err := DefaultNodeConfig(defaultSettings.InstallationID, &requests.CreateAccount{ 1082 LogLevel: defaultSettings.LogLevel, 1083 }) 1084 require.NoError(t, err) 1085 nodeConfig.DataDir = rootDataDir 1086 nodeConfig.KeyStoreDir = keyStoreDir 1087 1088 profileKeypair := &accounts.Keypair{ 1089 KeyUID: genAccInfo.KeyUID, 1090 Name: "Profile Name", 1091 Type: accounts.KeypairTypeProfile, 1092 DerivedFrom: masterAddress, 1093 } 1094 1095 profileKeypair.Accounts = append(profileKeypair.Accounts, &accounts.Account{ 1096 Address: types.HexToAddress(chatAddress), 1097 KeyUID: profileKeypair.KeyUID, 1098 Type: accounts.AccountTypeGenerated, 1099 PublicKey: types.Hex2Bytes(accountInfo.PublicKey), 1100 Path: pathEIP1581Chat, 1101 Wallet: false, 1102 Chat: true, 1103 Name: "GeneratedAccount", 1104 }) 1105 1106 for p, dAccInfo := range derivedAccounts { 1107 found = keystoreContainsFileForAccount(keyStoreDir, dAccInfo.Address) 1108 require.NoError(t, err) 1109 require.True(t, found) 1110 1111 if p == pathDefaultWalletAccount || 1112 p == customWalletPath1 || 1113 p == customWalletPath2 { 1114 wAcc := &accounts.Account{ 1115 Address: types.HexToAddress(dAccInfo.Address), 1116 KeyUID: genAccInfo.KeyUID, 1117 Wallet: false, 1118 Chat: false, 1119 Type: accounts.AccountTypeGenerated, 1120 Path: p, 1121 Name: "derivacc" + p, 1122 Hidden: false, 1123 Removed: false, 1124 } 1125 if p == pathDefaultWalletAccount { 1126 wAcc.Wallet = true 1127 } 1128 profileKeypair.Accounts = append(profileKeypair.Accounts, wAcc) 1129 } 1130 } 1131 1132 account := multiaccounts.Account{ 1133 Name: profileKeypair.Name, 1134 Timestamp: 1, 1135 KeyUID: profileKeypair.KeyUID, 1136 } 1137 1138 err = backend.ensureAppDBOpened(account, password) 1139 require.NoError(t, err) 1140 1141 err = backend.StartNodeWithAccountAndInitialConfig(account, password, *defaultSettings, nodeConfig, profileKeypair.Accounts, nil) 1142 require.NoError(t, err) 1143 multiaccounts, err := backend.GetAccounts() 1144 require.NoError(t, err) 1145 require.NotEmpty(t, multiaccounts[0].ColorHash) 1146 serverMessenger := backend.Messenger() 1147 require.NotNil(t, serverMessenger) 1148 1149 files, err := ioutil.ReadDir(rootDataDir) 1150 require.NoError(t, err) 1151 require.NotEqual(t, 3, len(files)) 1152 1153 keycardAccount := account 1154 keycardAccount.KeycardPairing = "pairing" 1155 1156 keycardSettings := settings.Settings{ 1157 KeycardInstanceUID: "0xdeadbeef", 1158 KeycardPairedOn: 1, 1159 KeycardPairing: "pairing", 1160 } 1161 1162 // Ensure we're able to open the DB 1163 err = backend.ensureAppDBOpened(keycardAccount, keycardPassword) 1164 require.NoError(t, err) 1165 1166 // db creation 1167 db, err := accounts.NewDB(backend.appDB) 1168 require.NoError(t, err) 1169 1170 // Check that there is no registered keycards 1171 keycards, err := db.GetKeycardsWithSameKeyUID(genAccInfo.KeyUID) 1172 require.NoError(t, err) 1173 require.Equal(t, 0, len(keycards)) 1174 1175 // Converting to a keycard account 1176 err = backend.ConvertToKeycardAccount(keycardAccount, keycardSettings, keycardUID, password, keycardPassword) 1177 require.NoError(t, err) 1178 1179 // Validating results of converting to a keycard account. 1180 // All keystore files for the account which is migrated need to be removed. 1181 found = keystoreContainsFileForAccount(keyStoreDir, masterAddress) 1182 require.False(t, found) 1183 1184 for _, dAccInfo := range derivedAccounts { 1185 found = keystoreContainsFileForAccount(keyStoreDir, dAccInfo.Address) 1186 require.False(t, found) 1187 } 1188 1189 require.NoError(t, backend.Logout()) 1190 require.NoError(t, backend.StopNode()) 1191 1192 require.NoError(t, backend.AccountManager().InitKeystore(keyStoreDir)) 1193 require.NoError(t, backend.OpenAccounts()) 1194 1195 require.NoError(t, backend.StartNodeWithKey(account, keycardPassword, chatKey, nodeConfig)) 1196 defer func() { 1197 assert.NoError(t, backend.Logout()) 1198 assert.NoError(t, backend.StopNode()) 1199 }() 1200 1201 // Ensure we're able to open the DB 1202 err = backend.ensureAppDBOpened(keycardAccount, keycardPassword) 1203 require.NoError(t, err) 1204 1205 // db creation after re-encryption 1206 db1, err := accounts.NewDB(backend.appDB) 1207 require.NoError(t, err) 1208 1209 // Check that there is a registered keycard 1210 keycards, err = db1.GetKeycardsWithSameKeyUID(genAccInfo.KeyUID) 1211 require.NoError(t, err) 1212 require.Equal(t, 1, len(keycards)) 1213 1214 // Converting to a regular account 1215 err = backend.ConvertToRegularAccount(mnemonic, keycardPassword, password) 1216 require.NoError(t, err) 1217 1218 // Validating results of converting to a regular account. 1219 // All keystore files for need to be created. 1220 found = keystoreContainsFileForAccount(keyStoreDir, accountInfo.Address) 1221 require.True(t, found) 1222 1223 for _, dAccInfo := range derivedAccounts { 1224 found = keystoreContainsFileForAccount(keyStoreDir, dAccInfo.Address) 1225 require.True(t, found) 1226 } 1227 1228 found = keystoreContainsFileForAccount(keyStoreDir, masterAddress) 1229 require.True(t, found) 1230 1231 // Ensure we're able to open the DB 1232 err = backend.ensureAppDBOpened(keycardAccount, password) 1233 require.NoError(t, err) 1234 1235 // db creation after re-encryption 1236 db2, err := accounts.NewDB(backend.appDB) 1237 require.NoError(t, err) 1238 1239 // Check that there is no registered keycards 1240 keycards, err = db2.GetKeycardsWithSameKeyUID(genAccInfo.KeyUID) 1241 require.NoError(t, err) 1242 require.Equal(t, 0, len(keycards)) 1243 } 1244 1245 func copyFile(srcFolder string, dstFolder string, fileName string, t *testing.T) { 1246 data, err := ioutil.ReadFile(path.Join(srcFolder, fileName)) 1247 if err != nil { 1248 t.Fail() 1249 } 1250 1251 err = ioutil.WriteFile(path.Join(dstFolder, fileName), data, 0600) 1252 if err != nil { 1253 t.Fail() 1254 } 1255 } 1256 1257 func copyDir(srcFolder string, dstFolder string, t *testing.T) { 1258 files, err := ioutil.ReadDir(srcFolder) 1259 require.NoError(t, err) 1260 for _, file := range files { 1261 if !file.IsDir() { 1262 copyFile(srcFolder, dstFolder, file.Name(), t) 1263 } else { 1264 childFolder := path.Join(srcFolder, file.Name()) 1265 newFolder := path.Join(dstFolder, file.Name()) 1266 err = os.MkdirAll(newFolder, os.ModePerm) 1267 require.NoError(t, err) 1268 copyDir(childFolder, newFolder, t) 1269 } 1270 } 1271 } 1272 1273 func loginDesktopUser(t *testing.T, conf *params.NodeConfig) { 1274 // The following passwords and DB used in this test unit are only 1275 // used to determine if login process works correctly after a migration 1276 1277 // Expected account data: 1278 keyUID := "0x7c46c8f6f059ab72d524f2a6d356904db30bb0392636172ab3929a6bd2220f84" // #nosec G101 1279 username := "TestUser" 1280 passwd := "0xC888C9CE9E098D5864D3DED6EBCC140A12142263BACE3A23A36F9905F12BD64A" // #nosec G101 1281 1282 b := NewGethStatusBackend() 1283 1284 require.NoError(t, b.AccountManager().InitKeystore(conf.KeyStoreDir)) 1285 b.UpdateRootDataDir(conf.DataDir) 1286 1287 require.NoError(t, b.OpenAccounts()) 1288 1289 accounts, err := b.GetAccounts() 1290 require.NoError(t, err) 1291 1292 require.Len(t, accounts, 1) 1293 require.Equal(t, username, accounts[0].Name) 1294 require.Equal(t, keyUID, accounts[0].KeyUID) 1295 1296 wg := sync.WaitGroup{} 1297 wg.Add(1) 1298 go func() { 1299 defer wg.Done() 1300 err := b.StartNodeWithAccount(accounts[0], passwd, conf, nil) 1301 require.NoError(t, err) 1302 }() 1303 1304 wg.Wait() 1305 require.NoError(t, b.Logout()) 1306 require.NotNil(t, b.statusNode.HTTPServer()) 1307 require.NoError(t, b.StopNode()) 1308 1309 } 1310 1311 func TestLoginAndMigrationsStillWorkWithExistingDesktopUser(t *testing.T) { 1312 utils.Init() 1313 1314 srcFolder := "../static/test-0.132.0-account/" 1315 1316 tmpdir := t.TempDir() 1317 1318 copyDir(srcFolder, tmpdir, t) 1319 1320 conf, err := params.NewNodeConfig(tmpdir, 1777) 1321 require.NoError(t, err) 1322 1323 loginDesktopUser(t, conf) 1324 loginDesktopUser(t, conf) // Login twice to catch weird errors that only appear after logout 1325 } 1326 1327 func TestChangeDatabasePassword(t *testing.T) { 1328 oldPassword := "password" 1329 newPassword := "newPassword" 1330 1331 backend := NewGethStatusBackend() 1332 backend.UpdateRootDataDir(t.TempDir()) 1333 1334 // Setup keystore to test decryption of it 1335 keyStoreDir := t.TempDir() 1336 require.NoError(t, backend.accountManager.InitKeystore(keyStoreDir)) 1337 1338 _, accountInfo, _, err := backend.accountManager.CreateAccount(oldPassword) 1339 require.NoError(t, err) 1340 1341 account := multiaccounts.Account{ 1342 Name: "TestAccount", 1343 Timestamp: 1, 1344 KeyUID: "0x7c46c8f6f059ab72d524f2a6d356904db30bb0392636172ab3929a6bd2220f84", 1345 KDFIterations: 1, 1346 } 1347 1348 // Initialize accounts DB 1349 err = backend.OpenAccounts() 1350 require.NoError(t, err) 1351 err = backend.SaveAccount(account) 1352 require.NoError(t, err) 1353 1354 // Created DBs with old password 1355 err = backend.ensureDBsOpened(account, oldPassword) 1356 require.NoError(t, err) 1357 1358 // Change password 1359 err = backend.ChangeDatabasePassword(account.KeyUID, oldPassword, newPassword) 1360 require.NoError(t, err) 1361 1362 // Test that DBs can be opened with new password 1363 appDbPath, err := backend.getAppDBPath(account.KeyUID) 1364 require.NoError(t, err) 1365 appDb, err := sqlite.OpenDB(appDbPath, newPassword, account.KDFIterations) 1366 require.NoError(t, err) 1367 appDb.Close() 1368 1369 walletDbPath, err := backend.getWalletDBPath(account.KeyUID) 1370 require.NoError(t, err) 1371 walletDb, err := sqlite.OpenDB(walletDbPath, newPassword, account.KDFIterations) 1372 require.NoError(t, err) 1373 walletDb.Close() 1374 1375 // Test that keystore can be decrypted with the new password 1376 acc, key, err := backend.accountManager.AddressToDecryptedAccount(accountInfo.WalletAddress, newPassword) 1377 require.NoError(t, err) 1378 require.NotNil(t, acc) 1379 require.NotNil(t, key) 1380 require.Equal(t, acc.Address, key.Address) 1381 } 1382 1383 func TestCreateWallet(t *testing.T) { 1384 utils.Init() 1385 password := "some-password2" // nolint: goconst 1386 tmpdir := t.TempDir() 1387 1388 b := NewGethStatusBackend() 1389 defer func() { 1390 require.NoError(t, b.StopNode()) 1391 }() 1392 1393 createAccountRequest := &requests.CreateAccount{ 1394 DisplayName: "some-display-name", 1395 CustomizationColor: "#ffffff", 1396 Password: password, 1397 RootDataDir: tmpdir, 1398 LogFilePath: tmpdir + "/log", 1399 } 1400 c := make(chan interface{}, 10) 1401 signal.SetMobileSignalHandler(func(data []byte) { 1402 if strings.Contains(string(data), "node.login") { 1403 c <- struct{}{} 1404 } 1405 }) 1406 t.Cleanup(signal.ResetMobileSignalHandler) 1407 1408 account, err := b.CreateAccountAndLogin(createAccountRequest) 1409 require.NoError(t, err) 1410 statusNode := b.statusNode 1411 require.NotNil(t, statusNode) 1412 1413 walletService := statusNode.WalletService() 1414 require.NotNil(t, walletService) 1415 walletAPI := walletservice.NewAPI(walletService) 1416 1417 paths := []string{"m/44'/60'/0'/0/1"} 1418 1419 db, err := accounts.NewDB(b.appDB) 1420 require.NoError(t, err) 1421 walletRootAddress, err := db.GetWalletRootAddress() 1422 require.NoError(t, err) 1423 1424 mnemonic, err := db.Mnemonic() 1425 require.NoError(t, err) 1426 require.NotEmpty(t, mnemonic) 1427 1428 derivedAddress, err := walletAPI.GetDerivedAddresses(context.Background(), password, walletRootAddress.String(), paths) 1429 require.NoError(t, err) 1430 require.Len(t, derivedAddress, 1) 1431 1432 accountsService := statusNode.AccountService() 1433 require.NotNil(t, accountsService) 1434 accountsAPI := accountsService.AccountsAPI() 1435 1436 err = accountsAPI.AddAccount(context.Background(), password, &accounts.Account{ 1437 KeyUID: account.KeyUID, 1438 Type: accounts.AccountTypeGenerated, 1439 PublicKey: derivedAddress[0].PublicKey, 1440 Emoji: "some", 1441 ColorID: "so", 1442 Name: "some name", 1443 Path: derivedAddress[0].Path, 1444 }) 1445 require.NoError(t, err) 1446 } 1447 1448 func TestSetFleet(t *testing.T) { 1449 utils.Init() 1450 password := "some-password2" // nolint: goconst 1451 tmpdir := t.TempDir() 1452 1453 b := NewGethStatusBackend() 1454 createAccountRequest := &requests.CreateAccount{ 1455 DisplayName: "some-display-name", 1456 CustomizationColor: "#ffffff", 1457 Password: password, 1458 RootDataDir: tmpdir, 1459 LogFilePath: tmpdir + "/log", 1460 } 1461 c := make(chan interface{}, 10) 1462 signal.SetMobileSignalHandler(func(data []byte) { 1463 if strings.Contains(string(data), "node.login") { 1464 c <- struct{}{} 1465 } 1466 }) 1467 t.Cleanup(signal.ResetMobileSignalHandler) 1468 1469 newAccount, err := b.CreateAccountAndLogin(createAccountRequest) 1470 require.NoError(t, err) 1471 statusNode := b.statusNode 1472 require.NotNil(t, statusNode) 1473 1474 savedSettings, err := b.GetSettings() 1475 require.NoError(t, err) 1476 require.Empty(t, savedSettings.Fleet) 1477 1478 accountsDB, err := b.accountsDB() 1479 require.NoError(t, err) 1480 err = accountsDB.SaveSettingField(settings.Fleet, params.FleetStatusProd) 1481 require.NoError(t, err) 1482 1483 savedSettings, err = b.GetSettings() 1484 require.NoError(t, err) 1485 require.NotEmpty(t, savedSettings.Fleet) 1486 require.Equal(t, params.FleetStatusProd, *savedSettings.Fleet) 1487 1488 require.NoError(t, b.Logout()) 1489 1490 loginAccountRequest := &requests.Login{ 1491 KeyUID: newAccount.KeyUID, 1492 Password: password, 1493 } 1494 require.NoError(t, b.LoginAccount(loginAccountRequest)) 1495 select { 1496 case <-c: 1497 break 1498 case <-time.After(5 * time.Second): 1499 t.FailNow() 1500 } 1501 // Check is using the right fleet 1502 require.Equal(t, b.config.ClusterConfig.WakuNodes, params.DefaultWakuNodes(params.FleetStatusProd)) 1503 1504 require.NoError(t, b.Logout()) 1505 } 1506 1507 func TestWalletConfigOnLoginAccount(t *testing.T) { 1508 utils.Init() 1509 password := "some-password2" // nolint: goconst 1510 tmpdir := t.TempDir() 1511 poktToken := "grove-token" // nolint: goconst 1512 infuraToken := "infura-token" // nolint: goconst 1513 alchemyEthereumMainnetToken := "alchemy-ethereum-mainnet-token" 1514 alchemyEthereumSepoliaToken := "alchemy-ethereum-sepolia-token" 1515 alchemyArbitrumMainnetToken := "alchemy-arbitrum-mainnet-token" 1516 alchemyArbitrumSepoliaToken := "alchemy-arbitrum-sepolia-token" 1517 alchemyOptimismMainnetToken := "alchemy-optimism-mainnet-token" 1518 alchemyOptimismSepoliaToken := "alchemy-optimism-sepolia-token" 1519 raribleMainnetAPIKey := "rarible-mainnet-api-key" // nolint: gosec 1520 raribleTestnetAPIKey := "rarible-testnet-api-key" // nolint: gosec 1521 1522 b := NewGethStatusBackend() 1523 createAccountRequest := &requests.CreateAccount{ 1524 DisplayName: "some-display-name", 1525 CustomizationColor: "#ffffff", 1526 Password: password, 1527 RootDataDir: tmpdir, 1528 LogFilePath: tmpdir + "/log", 1529 } 1530 c := make(chan interface{}, 10) 1531 signal.SetMobileSignalHandler(func(data []byte) { 1532 if strings.Contains(string(data), "node.login") { 1533 c <- struct{}{} 1534 } 1535 }) 1536 t.Cleanup(signal.ResetMobileSignalHandler) 1537 1538 newAccount, err := b.CreateAccountAndLogin(createAccountRequest) 1539 require.NoError(t, err) 1540 statusNode := b.statusNode 1541 require.NotNil(t, statusNode) 1542 1543 require.NoError(t, b.Logout()) 1544 1545 loginAccountRequest := &requests.Login{ 1546 KeyUID: newAccount.KeyUID, 1547 Password: password, 1548 WalletSecretsConfig: requests.WalletSecretsConfig{ 1549 PoktToken: poktToken, 1550 InfuraToken: infuraToken, 1551 AlchemyEthereumMainnetToken: alchemyEthereumMainnetToken, 1552 AlchemyEthereumSepoliaToken: alchemyEthereumSepoliaToken, 1553 AlchemyArbitrumMainnetToken: alchemyArbitrumMainnetToken, 1554 AlchemyArbitrumSepoliaToken: alchemyArbitrumSepoliaToken, 1555 AlchemyOptimismMainnetToken: alchemyOptimismMainnetToken, 1556 AlchemyOptimismSepoliaToken: alchemyOptimismSepoliaToken, 1557 RaribleMainnetAPIKey: raribleMainnetAPIKey, 1558 RaribleTestnetAPIKey: raribleTestnetAPIKey, 1559 }, 1560 } 1561 1562 require.NoError(t, b.LoginAccount(loginAccountRequest)) 1563 select { 1564 case <-c: 1565 break 1566 case <-time.After(5 * time.Second): 1567 t.FailNow() 1568 } 1569 1570 require.Equal(t, b.config.WalletConfig.InfuraAPIKey, infuraToken) 1571 require.Equal(t, b.config.WalletConfig.AlchemyAPIKeys[mainnetChainID], alchemyEthereumMainnetToken) 1572 require.Equal(t, b.config.WalletConfig.AlchemyAPIKeys[sepoliaChainID], alchemyEthereumSepoliaToken) 1573 require.Equal(t, b.config.WalletConfig.AlchemyAPIKeys[arbitrumChainID], alchemyArbitrumMainnetToken) 1574 require.Equal(t, b.config.WalletConfig.AlchemyAPIKeys[arbitrumSepoliaChainID], alchemyArbitrumSepoliaToken) 1575 require.Equal(t, b.config.WalletConfig.AlchemyAPIKeys[optimismChainID], alchemyOptimismMainnetToken) 1576 require.Equal(t, b.config.WalletConfig.AlchemyAPIKeys[optimismSepoliaChainID], alchemyOptimismSepoliaToken) 1577 require.Equal(t, b.config.WalletConfig.RaribleMainnetAPIKey, raribleMainnetAPIKey) 1578 require.Equal(t, b.config.WalletConfig.RaribleTestnetAPIKey, raribleTestnetAPIKey) 1579 1580 require.NoError(t, b.Logout()) 1581 } 1582 1583 func TestTestnetEnabledSettingOnCreateAccount(t *testing.T) { 1584 utils.Init() 1585 tmpdir := t.TempDir() 1586 1587 b := NewGethStatusBackend() 1588 1589 // Creating an account with test networks enabled 1590 createAccountRequest1 := &requests.CreateAccount{ 1591 DisplayName: "User-1", 1592 CustomizationColor: "#ffffff", 1593 Password: "password123", 1594 RootDataDir: tmpdir, 1595 LogFilePath: tmpdir + "/log", 1596 TestNetworksEnabled: true, 1597 } 1598 _, err := b.CreateAccountAndLogin(createAccountRequest1) 1599 require.NoError(t, err) 1600 statusNode := b.statusNode 1601 require.NotNil(t, statusNode) 1602 1603 settings, err := b.GetSettings() 1604 require.NoError(t, err) 1605 require.True(t, settings.TestNetworksEnabled) 1606 1607 require.NoError(t, b.Logout()) 1608 1609 // Creating an account with test networks disabled 1610 createAccountRequest2 := &requests.CreateAccount{ 1611 DisplayName: "User-2", 1612 CustomizationColor: "#ffffff", 1613 Password: "password", 1614 RootDataDir: tmpdir, 1615 LogFilePath: tmpdir + "/log", 1616 } 1617 _, err = b.CreateAccountAndLogin(createAccountRequest2) 1618 require.NoError(t, err) 1619 statusNode = b.statusNode 1620 require.NotNil(t, statusNode) 1621 1622 settings, err = b.GetSettings() 1623 require.NoError(t, err) 1624 require.False(t, settings.TestNetworksEnabled) 1625 1626 require.NoError(t, b.Logout()) 1627 } 1628 1629 func TestRestoreAccountAndLogin(t *testing.T) { 1630 utils.Init() 1631 tmpdir := t.TempDir() 1632 1633 backend := NewGethStatusBackend() 1634 1635 // Test case 1: Valid restore account request 1636 restoreRequest := &requests.RestoreAccount{ 1637 Mnemonic: "test test test test test test test test test test test test", 1638 FetchBackup: false, 1639 CreateAccount: requests.CreateAccount{ 1640 DisplayName: "Account1", 1641 DeviceName: "StatusIM", 1642 Password: "password", 1643 CustomizationColor: "0x000000", 1644 RootDataDir: tmpdir, 1645 }, 1646 } 1647 account, err := backend.RestoreAccountAndLogin(restoreRequest) 1648 require.NoError(t, err) 1649 require.NotNil(t, account) 1650 1651 // Test case 2: Invalid restore account request 1652 invalidRequest := &requests.RestoreAccount{} 1653 _, err = backend.RestoreAccountAndLogin(invalidRequest) 1654 require.Error(t, err) 1655 1656 db, err := accounts.NewDB(backend.appDB) 1657 require.NoError(t, err) 1658 mnemonic, err := db.Mnemonic() 1659 require.NoError(t, err) 1660 require.Empty(t, mnemonic) 1661 } 1662 1663 func TestCreateAccountPathsValidation(t *testing.T) { 1664 tmpdir := t.TempDir() 1665 1666 validation := &requests.CreateAccountValidation{ 1667 AllowEmptyDisplayName: false, 1668 } 1669 1670 request := &requests.CreateAccount{ 1671 DisplayName: "User-1", 1672 Password: "password123", 1673 CustomizationColor: "#ffffff", 1674 RootDataDir: tmpdir, 1675 } 1676 1677 err := request.Validate(validation) 1678 require.NoError(t, err) 1679 1680 request.RootDataDir = "" 1681 err = request.Validate(validation) 1682 require.ErrorIs(t, err, requests.ErrCreateAccountInvalidRootDataDir) 1683 } 1684 1685 func TestRestoreKeycardAccountAndLogin(t *testing.T) { 1686 utils.Init() 1687 tmpdir := t.TempDir() 1688 1689 exampleKeycardEvent := map[string]interface{}{ 1690 "error": "", 1691 "instanceUID": "a84599394887b742eed9a99d3834a797", 1692 "applicationInfo": map[string]interface{}{ 1693 "initialized": false, 1694 "instanceUID": "", 1695 "version": 0, 1696 "availableSlots": 0, 1697 "keyUID": "", 1698 }, 1699 "seedPhraseIndexes": []interface{}{}, 1700 "freePairingSlots": 0, 1701 "keyUid": "0x579324c53f347e18961c775a00ec13ed7d59a225b1859d5125ff36b450b8778d", 1702 "pinRetries": 0, 1703 "pukRetries": 0, 1704 "cardMetadata": map[string]interface{}{ 1705 "name": "", 1706 "walletAccounts": []interface{}{}, 1707 }, 1708 "generatedWalletAccount": map[string]interface{}{ 1709 "address": "", 1710 "publicKey": "", 1711 "privateKey": "", 1712 }, 1713 "generatedWalletAccounts": []interface{}{}, 1714 "txSignature": map[string]interface{}{ 1715 "r": "", 1716 "s": "", 1717 "v": "", 1718 }, 1719 "eip1581Key": map[string]interface{}{ 1720 "address": "0xA8d50f0B3bc581298446be8FBfF5c71684Ea6c01", 1721 "publicKey": "0x040d7e6e3761ab3d17c220e484ede2f3fa02998b859d4d0e9d34216c6e41b03dc94996fdea23a9233092cee50a768e7428d5de7bd42e8e32c10d6b0e36b10f0e7a", 1722 "privateKey": "", 1723 }, 1724 "encryptionKey": map[string]interface{}{ 1725 "address": "0x1ec12f2b323ddDD076A1127cEc8FA0B592c46cD3", 1726 "publicKey": "0x04c4b16f670b51702dc130673bf9c64ffd1f69383cef2127dfa05031b9b1359120f7342134af9a350465126a85e87cb003b7c4f93d2ba2ff98bb73277b119c7a87", 1727 "privateKey": "68c830d5b327382a65e6c302594744ec0d28b01d1ea8124f49714f05c9625ddd"}, 1728 "masterKey": map[string]interface{}{ 1729 "address": "0xbf9dE86774051537b2192Ce9c8d2496f129bA24b", 1730 "publicKey": "0x040d909a07ecca18bbfa7d53d10a86bd956f54b8b446eabd94940e642ae18421b516ec5b63677c4ce65e0e266b58bdb716d8266b25356154eb61713ecb23824075", 1731 "privateKey": "", 1732 }, 1733 "walletKey": map[string]interface{}{ 1734 "address": "0xB9E1998e1A8854887CA327D1aF5894B6CB0AC07D", 1735 "publicKey": "0x04c16e7748f34e0ab2c9c13350d7872d928e942934dd8b8abd3fb12b8c742a5ee8cf0919731e800907068afec25f577bde3a9c534795e359ee48097e4e55f4aaca", 1736 "privateKey": "", 1737 }, 1738 "walletRootKey": map[string]interface{}{ 1739 "address": "0xFf59db9F2f97Db7104A906C390D33C342a1309C8", 1740 "publicKey": "0x04c436532398e19ed14b4eb41545b82014435d60e7db4449a371fd80d0d5cd557f60d81f6c2b35ca5440aa60934c23b70489b0e7963e63ec66b51a7e52db711262", 1741 "privateKey": "", 1742 }, 1743 "whisperKey": map[string]interface{}{ 1744 "address": "0xBa122B9c0Ef560813b5D2C0961094aC36289f846", 1745 "publicKey": "0x0441468c39b579259676350b9736b01cdadb740f67bfd022fa2b985123b1d66fc3191cfe73205e3d3d84148f0248f9a2978afeeda16d7c3db90bd2579f0de33459", 1746 "privateKey": "5a42b4f15ff1a5da95d116442ce11a31e9020f562224bf60b1d8d3a99d90653d", 1747 }, 1748 "masterKeyAddress": "", 1749 } 1750 1751 exampleRequest := map[string]interface{}{ 1752 "mnemonic": "", 1753 "fetchBackup": true, 1754 "createAccountRequest": map[string]interface{}{ 1755 "rootDataDir": tmpdir, 1756 "kdfIterations": 256000, 1757 "deviceName": "", 1758 "displayName": "", 1759 "password": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 1760 "imagePath": "", 1761 "imageCropRectangle": map[string]interface{}{ 1762 "ax": 0, "ay": 0, "bx": 0, "by": 0}, 1763 "customizationColor": "primary", 1764 "emoji": "", 1765 "wakuV2Nameserver": nil, 1766 "wakuV2LightClient": false, 1767 "logLevel": "DEBUG", 1768 "logFilePath": "", 1769 "logEnabled": false, 1770 "previewPrivacy": true, 1771 "verifyTransactionURL": nil, 1772 "verifyENSURL": nil, 1773 "verifyENSContractAddress": nil, 1774 "verifyTransactionChainID": nil, 1775 "upstreamConfig": "", 1776 "networkID": nil, 1777 "walletSecretsConfig": map[string]interface{}{ 1778 "poktToken": "1234567890", 1779 "infuraToken": "1234567890", 1780 "infuraSecret": "", 1781 "openseaApiKey": "", 1782 "raribleMainnetApiKey": "", 1783 "raribleTestnetApiKey": "", 1784 "alchemyEthereumMainnetToken": "", 1785 "alchemyEthereumGoerliToken": "", 1786 "alchemyEthereumSepoliaToken": "", 1787 "alchemyArbitrumMainnetToken": "", 1788 "alchemyArbitrumGoerliToken": "", 1789 "alchemyArbitrumSepoliaToken": "", 1790 "alchemyOptimismMainnetToken": "", 1791 "alchemyOptimismGoerliToken": "", 1792 "alchemyOptimismSepoliaToken": "", 1793 }, 1794 "torrentConfigEnabled": false, 1795 "torrentConfigPort": 0, 1796 "keycardInstanceUID": "a84599394887b742eed9a99d3834a797", 1797 "keycardPairingDataFile": path.Join(tmpdir, DefaultKeycardPairingDataFile), 1798 }, 1799 } 1800 1801 require.NotNil(t, exampleKeycardEvent) 1802 require.NotNil(t, exampleRequest) 1803 1804 conf, err := params.NewNodeConfig(tmpdir, 1777) 1805 require.NoError(t, err) 1806 1807 backend := NewGethStatusBackend() 1808 1809 require.NoError(t, backend.AccountManager().InitKeystore(conf.KeyStoreDir)) 1810 backend.UpdateRootDataDir(conf.DataDir) 1811 1812 require.NoError(t, backend.OpenAccounts()) 1813 1814 keycardPairingDataFile := exampleRequest["createAccountRequest"].(map[string]interface{})["keycardPairingDataFile"].(string) 1815 1816 kp := wallet.NewKeycardPairings() 1817 kp.SetKeycardPairingsFile(keycardPairingDataFile) 1818 1819 err = kp.SetPairingsJSONFileContent([]byte(`{"a84599394887b742eed9a99d3834a797":{"key":"785d52957b05482477728380d9b4bbb5dc9a8ed978ab4a4098e1a279c855d3c6","index":1}}`)) 1820 require.NoError(t, err) 1821 1822 request := &requests.RestoreAccount{ 1823 Keycard: &requests.KeycardData{ 1824 KeyUID: exampleKeycardEvent["keyUid"].(string), 1825 Address: exampleKeycardEvent["masterKey"].(map[string]interface{})["address"].(string), 1826 WhisperPrivateKey: exampleKeycardEvent["whisperKey"].(map[string]interface{})["privateKey"].(string), 1827 WhisperPublicKey: exampleKeycardEvent["whisperKey"].(map[string]interface{})["publicKey"].(string), 1828 WhisperAddress: exampleKeycardEvent["whisperKey"].(map[string]interface{})["address"].(string), 1829 WalletPublicKey: exampleKeycardEvent["walletKey"].(map[string]interface{})["publicKey"].(string), 1830 WalletAddress: exampleKeycardEvent["walletKey"].(map[string]interface{})["address"].(string), 1831 WalletRootAddress: exampleKeycardEvent["walletRootKey"].(map[string]interface{})["address"].(string), 1832 Eip1581Address: exampleKeycardEvent["eip1581Key"].(map[string]interface{})["address"].(string), 1833 EncryptionPublicKey: exampleKeycardEvent["encryptionKey"].(map[string]interface{})["publicKey"].(string), 1834 }, 1835 CreateAccount: requests.CreateAccount{ 1836 DisplayName: "User-1", 1837 Password: "password123", 1838 CustomizationColor: "#ffffff", 1839 RootDataDir: tmpdir, 1840 KeycardInstanceUID: exampleKeycardEvent["instanceUID"].(string), 1841 KeycardPairingDataFile: &keycardPairingDataFile, 1842 }, 1843 } 1844 1845 acc, err := backend.RestoreKeycardAccountAndLogin(request) 1846 require.NoError(t, err) 1847 require.NotNil(t, acc) 1848 }