diff --git a/src/blockchain-wallet.js b/src/blockchain-wallet.js index 24b7d2138..c5c71fa20 100644 --- a/src/blockchain-wallet.js +++ b/src/blockchain-wallet.js @@ -608,18 +608,11 @@ function isAccountNonUsed (account, progress) { return API.getHistory([account.extendedPublicKey], 0, 0, 50).then(isNonUsed); } -Wallet.prototype.restoreHDWallet = function (mnemonic, bip39Password, pw, startedRestoreHDWallet, progress) { - assert(mnemonic, "BIP 39 mnemonic required") +Wallet.prototype.scanBip44 = function (secondPassword, startedRestoreHDWallet, progress) { // wallet restoration startedRestoreHDWallet && startedRestoreHDWallet(); var self = this; - var seedHex = BIP39.mnemonicToEntropy(mnemonic); - var pass39 = Helpers.isString(bip39Password) ? bip39Password : ''; - var encoder = WalletCrypto.cipherFunction(pw, this._sharedKey, this._pbkdf2_iterations, 'enc'); - var hd = HDWallet.restore(seedHex, pass39, encoder); - this._hd_wallets[0] = hd; - var account = this.newAccount('My Bitcoin Wallet 1', pw, 0, undefined, true); - API.getHistory([account.extendedPublicKey], 0, 0, 50).then(progress); + API.getHistory([self.hdwallet._accounts[0].extendedPublicKey], 0, 0, 50).then(progress); var accountIndex = 1; var AccountsGap = 10; @@ -636,18 +629,13 @@ Wallet.prototype.restoreHDWallet = function (mnemonic, bip39Password, pw, starte return true; } else { accountIndex++; - account = self.newAccount('My Bitcoin Wallet ' + accountIndex.toString(), pw, 0, undefined, true); + var account = self.newAccount('My Bitcoin Wallet ' + accountIndex.toString(), secondPassword, 0, undefined, true); return isAccountNonUsed(account, progress).then(go); } }; - var saveAndReturn = function () { - return new Promise(MyWallet.syncWallet); - }; - // it returns a promise of the new HDWallet - return untilNEmptyAccounts(AccountsGap) - .then(saveAndReturn); + return untilNEmptyAccounts(AccountsGap); }; // Enables email notifications for receiving bitcoins. Only for imported diff --git a/src/hd-wallet.js b/src/hd-wallet.js index f9b38d268..46edd2d3a 100644 --- a/src/hd-wallet.js +++ b/src/hd-wallet.js @@ -152,11 +152,7 @@ function getMasterHex (seedHex, bip39Password, cipher) { HDWallet.new = function (mnemonic, bip39Password, cipher) { assert(mnemonic, "BIP 39 mnemonic required") var seedHex = BIP39.mnemonicToEntropy(mnemonic); - return HDWallet.restore(seedHex, '', cipher); -}; -HDWallet.restore = function (seedHex, bip39Password, cipher) { - assert(Helpers.isSeedHex(seedHex), 'hdwallet.seedHex must exist and be a seed hex'); if (!Helpers.isString(bip39Password)) bip39Password = ''; var hdwallet = { seed_hex: seedHex, @@ -169,6 +165,7 @@ HDWallet.restore = function (seedHex, bip39Password, cipher) { if (cipher) { hd.encrypt(cipher).persist(); } + return hd; }; diff --git a/src/wallet-signup.js b/src/wallet-signup.js index cdd15fdc9..f61e83e51 100644 --- a/src/wallet-signup.js +++ b/src/wallet-signup.js @@ -20,15 +20,7 @@ function generateNewWallet (password, email, mnemonic, bip39Password, firstAccou error('Error generating wallet identifier'); } - var saveWallet = function () { - WalletNetwork.insertWallet(guid, sharedKey, password, {email: email}, decryptWalletProgress).then(function () { - success(guid, sharedKey, password); - }, function (e) { - error(e); - }); - }; - - Wallet.new(guid, sharedKey, mnemonic, bip39Password, firstAccountName, saveWallet, error); + Wallet.new(guid, sharedKey, mnemonic, bip39Password, firstAccountName, success, error); }).catch(error); } diff --git a/src/wallet.js b/src/wallet.js index 46c64aa39..add7c7ebc 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -513,19 +513,37 @@ MyWallet.createNewWallet = function (inputedEmail, inputedPassword, firstAccount successCallback(createdGuid, createdSharedKey, createdPassword); }; + + var saveWallet = function (wallet) { + WalletNetwork.insertWallet(wallet.guid, wallet.sharedKey, inputedPassword, {email: inputedEmail}).then(function () { + success(wallet.guid, wallet.sharedKey, inputedPassword); + }).catch(function (e) { + errorCallback(e); + }); + }; + var mnemonic = BIP39.generateMnemonic(undefined, RNG.run.bind(RNG)); - WalletSignup.generateNewWallet(inputedPassword, inputedEmail, mnemonic, undefined, firstAccountName, success, errorCallback); + WalletSignup.generateNewWallet(inputedPassword, inputedEmail, mnemonic, undefined, firstAccountName, saveWallet, errorCallback); }; // used on frontend -MyWallet.recoverFromMnemonic = function (inputedEmail, inputedPassword, mnemonic, bip39Password, success, error, startedRestoreHDWallet, accountProgress, generateUUIDProgress, decryptWalletProgress) { - var walletSuccess = function (guid, sharedKey, password) { - WalletStore.unsafeSetPassword(password); - var runSuccess = function () { success({guid: guid, sharedKey: sharedKey, password: password}); }; - MyWallet.wallet.restoreHDWallet(recoveryMnemonic, bip39Password, undefined, startedRestoreHDWallet, accountProgress).then(runSuccess).catch(error); +MyWallet.recoverFromMnemonic = function (inputedEmail, inputedPassword, mnemonic, bip39Password, successCallback, error, startedRestoreHDWallet, accountProgress, generateUUIDProgress, decryptWalletProgress) { + var walletGenerated = function (wallet) { + + var saveWallet = function () { + WalletNetwork.insertWallet(wallet.guid, wallet.sharedKey, inputedPassword, {email: inputedEmail}, decryptWalletProgress).then(function () { + successCallback({guid: wallet.guid, sharedKey: wallet.sharedKey, password: inputedPassword}); + }, function (e) { + error(e); + }); + }; + + WalletStore.unsafeSetPassword(inputedPassword); + wallet.scanBip44(undefined, startedRestoreHDWallet, accountProgress).then(saveWallet).catch(error); }; - WalletSignup.generateNewWallet(inputedPassword, inputedEmail, mnemonic, bip39Password, null, walletSuccess, error, generateUUIDProgress, decryptWalletProgress); + + WalletSignup.generateNewWallet(inputedPassword, inputedEmail, mnemonic, bip39Password, null, walletGenerated, error, generateUUIDProgress, decryptWalletProgress); }; // used frontend and mywallet diff --git a/tests/hdwallet_spec.js.coffee b/tests/hdwallet_spec.js.coffee index 8d4bd8146..fa898c2e3 100644 --- a/tests/hdwallet_spec.js.coffee +++ b/tests/hdwallet_spec.js.coffee @@ -1,6 +1,7 @@ proxyquire = require('proxyquireify')(require) MyWallet = undefined HDWallet = undefined +BIP39 = undefined describe "HDWallet", -> wallet = undefined @@ -55,8 +56,15 @@ describe "HDWallet", -> describe "instance", -> + BIP39 = + mnemonicToEntropy: (mnemonic) -> + "0123456789abcdef0123456789abcdef" + beforeEach -> - stubs = { './wallet': MyWallet} + stubs = { + './wallet': MyWallet, + 'bip39' : BIP39 + } HDWallet = proxyquire('../src/hd-wallet', stubs) wallet = new HDWallet(object) @@ -316,14 +324,6 @@ describe "HDWallet", -> fromFactory = HDWallet.factory(wallet) expect(fromFactory).toEqual(wallet) - describe ".restore", -> - it "should not restore an invalid hex seed", -> - expect(() -> HDWallet.restore("i'm not valid", "password")).toThrow() - - it "should set the password to '' if not a string", -> - wallet = HDWallet.restore("0123456789abcdef0123456789abcdef", 4334) - expect(wallet._bip39Password).toEqual("") - describe ".newAccount", -> observer = @@ -349,7 +349,7 @@ describe "HDWallet", -> expect(wallet.accounts[wallet.accounts.length - 1].label).toEqual('Savings') it "should create a new account without a cipher and with a password", -> - wallet = HDWallet.restore("0123456789abcdef0123456789abcdef", "password") + wallet = HDWallet.new("mnemonic", "password") wallet = wallet.newAccount("Savings") expect(wallet.accounts.length).toEqual(1) @@ -362,7 +362,7 @@ describe "HDWallet", -> expect(wallet.accounts[wallet.accounts.length - 1].label).toEqual('Savings') it "should create a new account with a cipher and with a password", -> - wallet = HDWallet.restore("0123456789abcdef0123456789abcdef", "password") + wallet = HDWallet.new("mnemonic", "password") wallet = wallet.newAccount("Savings", observer.cipher) expect(wallet.accounts.length).toEqual(1) diff --git a/tests/wallet_signup_spec.js.coffee b/tests/wallet_signup_spec.js.coffee index 8861d19bd..a50e17ff6 100644 --- a/tests/wallet_signup_spec.js.coffee +++ b/tests/wallet_signup_spec.js.coffee @@ -7,11 +7,6 @@ WalletNetwork = new Promise((resolve) -> resolve(['a8098c1a-f8', 'b77e160355e'])) else new Promise((resolve) -> resolve(['a8098c1a-f86e-11da-bd1a-00112444be1e', '6fa459ea-ee8a-3ca4-894e-db77e160355e'])) - insertWallet: () -> - if WalletNetwork.failInsertion - new Promise((resolve, reject) -> reject()) - else - new Promise((resolve) -> resolve()) Wallet = new: (guid, sharedKey, mnemonic, bip39Password, firstAccountLabel, success, error) -> success() @@ -75,27 +70,3 @@ describe "WalletSignup", -> expect(observers.success).toHaveBeenCalled() expect(observers.error).not.toHaveBeenCalled() expect(observers.progress).toHaveBeenCalled() - - describe "should fail when the wallet insertion fails", -> - - observers = null - - beforeEach (done) -> - observers = - success: () -> done() - error: () -> done() - progress: () -> - - spyOn(observers, "success").and.callThrough() - spyOn(observers, "error").and.callThrough() - spyOn(observers, "progress") - - WalletNetwork.failInsertion = true - WalletSignup.generateNewWallet('totot', 'a@a.co', undefined, undefined, 'My Wallet', observers.success, observers.error, observers.progress) - - it "", -> - expect(observers.success).not.toHaveBeenCalled() - expect(observers.error).toHaveBeenCalled() - expect(observers.progress).toHaveBeenCalled() - - WalletNetwork.failInsertion = false diff --git a/tests/wallet_spec.js.coffee b/tests/wallet_spec.js.coffee index 487a8cb69..02f4f4d79 100644 --- a/tests/wallet_spec.js.coffee +++ b/tests/wallet_spec.js.coffee @@ -4,12 +4,31 @@ WalletStore = { setGuid: () -> setRealAuthType: () -> setSyncPubKeys: () -> + setLanguage: () -> +} + +BlockchainSettingsAPI = { + change_language: () -> + change_local_currency: () -> } WalletCrypto = {} +hdwallet = { + guid: "1234", + sharedKey: "shared" + scanBip44: () -> { + then: (cb) -> + cb() + { + catch: () -> + } + } +} + WalletSignup = { - generateNewWallet: () -> + generateNewWallet: (inputedPassword, inputedEmail, mnemonic, bip39Password, firstAccountName, successCallback, errorCallback) -> + successCallback(hdwallet) } API = @@ -37,13 +56,23 @@ RNG = { "random" } +WalletNetwork = + insertWallet: () -> + console.log(WalletNetwork.failInsertion) + if WalletNetwork.failInsertion + new Promise((resolve, reject) -> reject()) + else + new Promise((resolve) -> resolve()) + stubs = { './wallet-store': WalletStore, './wallet-crypto': WalletCrypto, './wallet-signup': WalletSignup, './api': API, 'bip39': BIP39, - './rng' : RNG + './rng' : RNG, + './wallet-network': WalletNetwork, + './blockchain-settings-api' : BlockchainSettingsAPI } MyWallet = proxyquire('../src/wallet', stubs) @@ -53,6 +82,10 @@ describe "Wallet", -> callbacks = undefined beforeEach -> + JasminePromiseMatchers.install() + + afterEach -> + JasminePromiseMatchers.uninstall() describe "makePairingCode()", -> success = undefined @@ -312,9 +345,7 @@ describe "Wallet", -> describe "recoverFromMnemonic", -> beforeEach -> - spyOn(WalletSignup, "generateNewWallet").and.callFake((password, email, mnemonic, bip39Password, firstAccountName, success, error, generateUUIDProgress, decryptWalletProgress) -> - success("1234", "shared", password) - ) + spyOn(WalletSignup, "generateNewWallet").and.callThrough() spyOn(WalletStore, "unsafeSetPassword") it "should generate a new wallet", -> @@ -328,14 +359,24 @@ describe "Wallet", -> MyWallet.recoverFromMnemonic("a@b.com", "secret", "nuclear bunker sphaghetti monster dim sum sauce", undefined, (() ->)) expect(WalletStore.unsafeSetPassword).toHaveBeenCalledWith("secret") - it "should pass guid, shared key and password upon success", -> + it "should pass guid, shared key and password upon success", (done) -> obs = { success: () -> } - spyOn(obs, "success") + spyOn(obs, "success").and.callThrough() MyWallet.recoverFromMnemonic("a@b.com", "secret", "nuclear bunker sphaghetti monster dim sum sauce", undefined, obs.success) - expect(obs.success).toHaveBeenCalledWith({ guid: '1234', sharedKey: 'shared', password: 'secret' }) + + result = () -> + expect(obs.success).toHaveBeenCalledWith({ guid: '1234', sharedKey: 'shared', password: 'secret' }) + done() + + setTimeout(result, 1) + + it "should scan address space", -> + spyOn(hdwallet, "scanBip44").and.callThrough() + MyWallet.recoverFromMnemonic("a@b.com", "secret", "nuclear bunker sphaghetti monster dim sum sauce", undefined, (() ->)) + expect(hdwallet.scanBip44).toHaveBeenCalled() describe "createNewWallet", -> beforeEach -> @@ -353,3 +394,26 @@ describe "Wallet", -> # inside the RNG RNG.shouldThrow = true expect(() -> MyWallet.createNewWallet()).toThrow('Connection failed') + RNG.shouldThrow = false + + describe "when the wallet insertion fails", -> + + observers = null + + beforeEach (done) -> + observers = + success: () -> done() + error: () -> done() + + spyOn(observers, "success").and.callThrough() + spyOn(observers, "error").and.callThrough() + + WalletNetwork.failInsertion = true + MyWallet.createNewWallet('a@b.com', "1234", 'My Wallet', 'en', 'usd', observers.success, observers.error) + + it "should fail", -> + expect(observers.success).not.toHaveBeenCalled() + expect(observers.error).toHaveBeenCalled() + + afterEach -> + WalletNetwork.failInsertion = false