Skip to content
This repository has been archived by the owner on Mar 7, 2023. It is now read-only.

Commit

Permalink
Merge pull request #209 from blockchain/restore-fix
Browse files Browse the repository at this point in the history
fix(restore): restore is checking for further accounts.
  • Loading branch information
Sjors committed May 9, 2016
2 parents 671856a + cf47424 commit b53fd80
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 83 deletions.
20 changes: 4 additions & 16 deletions src/blockchain-wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand Down
5 changes: 1 addition & 4 deletions src/hd-wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -169,6 +165,7 @@ HDWallet.restore = function (seedHex, bip39Password, cipher) {
if (cipher) {
hd.encrypt(cipher).persist();
}

return hd;
};

Expand Down
10 changes: 1 addition & 9 deletions src/wallet-signup.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
31 changes: 25 additions & 6 deletions src/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -513,18 +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);
success({guid: guid, sharedKey: sharedKey, password: password});
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
Expand Down
22 changes: 11 additions & 11 deletions tests/hdwallet_spec.js.coffee
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
proxyquire = require('proxyquireify')(require)
MyWallet = undefined
HDWallet = undefined
BIP39 = undefined

describe "HDWallet", ->
wallet = undefined
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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 =
Expand All @@ -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)
Expand All @@ -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)
Expand Down
29 changes: 0 additions & 29 deletions tests/wallet_signup_spec.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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', '[email protected]', 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
80 changes: 72 additions & 8 deletions tests/wallet_spec.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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)
Expand All @@ -53,6 +82,10 @@ describe "Wallet", ->
callbacks = undefined

beforeEach ->
JasminePromiseMatchers.install()

afterEach ->
JasminePromiseMatchers.uninstall()

describe "makePairingCode()", ->
success = undefined
Expand Down Expand Up @@ -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", ->
Expand All @@ -328,14 +359,24 @@ describe "Wallet", ->
MyWallet.recoverFromMnemonic("[email protected]", "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("[email protected]", "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("[email protected]", "secret", "nuclear bunker sphaghetti monster dim sum sauce", undefined, (() ->))
expect(hdwallet.scanBip44).toHaveBeenCalled()

describe "createNewWallet", ->
beforeEach ->
Expand All @@ -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('[email protected]', "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

0 comments on commit b53fd80

Please sign in to comment.