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

Commit b146240

Browse files
committed
support multiple open wallets
1 parent 740310d commit b146240

File tree

5 files changed

+65
-77
lines changed

5 files changed

+65
-77
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ dev/
33

44
npm-debug.log
55
spec.config.js
6+
artillery*
67
.env

src/api.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ MerchantAPI.prototype.makePayment = function (guid, options) {
7373
var from = isNaN(options.from) ?
7474
options.from : parseInt(options.from);
7575

76-
var payment = this.cache.walletPayment()
76+
var payment = this.cache.walletPayment(wallet.guid)
7777
.to(options.to)
7878
.amount(options.amount)
7979
.from(from);

src/error-codes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module.exports = {
1919
ERR_HISTORY : 'Experienced an error while fetching wallet history',
2020
ERR_UNEXPECT : 'Unexpected error, please try again',
2121
ERR_BUILDTX : 'Error building transaction',
22+
ERR_PAYMENT : 'Failed to create wallet payment',
2223
ERR_PUSHTX : 'Error signing and pushing transaction',
2324
ERR_JSON : 'Invalid JSON',
2425
ERR_ADDRESS : 'Address not found in this wallet',

src/rpc-server.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ function move(params, wallet) {
292292
, to = getAccountAddresses(wallet, params.toAccount)[0]
293293
, amt = btcToSatoshi(params.amount);
294294

295-
var payment = api.cache.walletPayment().from(from).to(to).amount(amt);
295+
var payment = api.cache.walletPayment(wallet.guid).from(from).to(to).amount(amt);
296296
return publishPayment(payment, pass);
297297
}
298298

@@ -303,7 +303,7 @@ function sendfrom(params, wallet) {
303303
, to = params.bitcoinAddress
304304
, amt = btcToSatoshi(params.amount);
305305

306-
var payment = api.cache.walletPayment().from(from).to(to).amount(amt);
306+
var payment = api.cache.walletPayment(wallet.guid).from(from).to(to).amount(amt);
307307
return publishPayment(payment, pass);
308308
}
309309

@@ -318,7 +318,7 @@ function sendmany(params, wallet) {
318318
amts.push(btcToSatoshi(params.addressAmountPairs[address]));
319319
});
320320

321-
var payment = api.cache.walletPayment().from(from).to(to).amount(amts);
321+
var payment = api.cache.walletPayment(wallet.guid).from(from).to(to).amount(amts);
322322
return publishPayment(payment, pass);
323323
}
324324

@@ -329,7 +329,7 @@ function sendtoaddress(params, wallet) {
329329
, to = params.bitcoinAddress
330330
, amt = btcToSatoshi(params.amount);
331331

332-
var payment = api.cache.walletPayment().from(from).to(to).amount(amt);
332+
var payment = api.cache.walletPayment(wallet.guid).from(from).to(to).amount(amt);
333333
return publishPayment(payment, pass);
334334
}
335335

src/wallet-cache.js

Lines changed: 58 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -10,56 +10,53 @@ var crypto = require('crypto')
1010
, q = require('q')
1111
, request = require('request-promise');
1212

13-
var bc
14-
, validatePassword = function () { return false; }
15-
, randomBytes = crypto.randomBytes(BYTES_PER_HASH);
13+
var randomBytes = crypto.randomBytes(BYTES_PER_HASH);
1614

1715
function WalletCache() {
18-
this.loggingIn = false;
16+
this.loggingIn = {};
17+
this.instanceStore = {};
18+
this.pwHashStore = {};
1919
}
2020

2121
WalletCache.prototype.login = function (guid, options) {
22-
if (this.loggingIn) return q.reject('ERR_LOGIN_BUSY');
22+
if (this.loggingIn[guid]) return q.reject('ERR_LOGIN_BUSY');
2323

2424
var deferred = q.defer()
2525
, needs2FA = deferred.reject.bind(null, 'ERR_2FA')
2626
, needsAuth = deferred.reject.bind(null, 'ERR_AUTH')
2727
, error = deferred.reject
28+
, fetched = deferred.resolve.bind(null, { guid: guid, success: true })
2829
, timeout = setTimeout(deferred.reject.bind(null, 'ERR_TIMEOUT'), TIMEOUT_MS);
2930

3031
var success = function () {
31-
var fetchedHistory = deferred.resolve.bind(null, { guid: guid, success: true })
32-
, pwHash = generatePwHash(options.password);
33-
validatePassword = function (p) { return generatePwHash(p).compare(pwHash) === 0; };
34-
bc.MyWallet.wallet.getHistory().then(fetchedHistory).catch(error);
35-
};
32+
var pwHash = generatePwHash(options.password);
33+
this.pwHashStore[guid] = pwHash;
34+
this.instanceStore[guid].MyWallet.wallet.getHistory().then(fetched).catch(error);
35+
}.bind(this);
3636

3737
var login = function () {
38-
this.loggingIn = true;
39-
safeReset().then(function () {
40-
bc.API.API_CODE = options.api_code;
41-
bc.WalletStore.setAPICode(options.api_code);
42-
bc.WalletStore.isLogoutDisabled = function () { return true; };
43-
bc.MyWallet.login(guid, null, options.password, null, success, needs2FA, null, needsAuth, error);
44-
});
38+
this.loggingIn[guid] = true;
39+
var instance = generateInstance();
40+
instance.API.API_CODE = options.api_code;
41+
instance.WalletStore.setAPICode(options.api_code);
42+
instance.WalletStore.isLogoutDisabled = function () { return true; };
43+
instance.MyWallet.login(guid, null, options.password, null, success, needs2FA, null, needsAuth, error);
44+
this.instanceStore[guid] = instance;
4545
}.bind(this);
4646

4747
var done = function () {
4848
clearTimeout(timeout);
49-
this.loggingIn = false;
49+
this.loggingIn[guid] = false;
5050
}.bind(this);
5151

5252
this.getWallet(guid, options).then(function (wallet) {
53-
var fetchedHistory = deferred.resolve.bind(null, { guid: guid, success: true });
54-
wallet.guid === guid ? wallet.getHistory().then(fetchedHistory) : login();
53+
wallet.guid === guid ? wallet.getHistory().then(fetched) : login();
5554
}).catch(login);
5655

5756
return deferred.promise.fin(done);
5857
};
5958

6059
WalletCache.prototype.createWallet = function (options) {
61-
if (this.loggingIn) return q.reject('ERR_LOGIN_BUSY');
62-
6360
var lang, currency
6461
, email = options.email || ''
6562
, pass = options.password
@@ -68,76 +65,65 @@ WalletCache.prototype.createWallet = function (options) {
6865
, deferred = q.defer()
6966
, timeout = setTimeout(deferred.reject.bind(null, 'ERR_TIMEOUT'), TIMEOUT_MS);
7067

68+
var instance = generateInstance();
69+
instance.API.API_CODE = options.api_code;
70+
instance.WalletStore.setAPICode(options.api_code);
71+
instance.WalletStore.isLogoutDisabled = function () { return true; };
72+
7173
var success = function (guid, sharedKey, password) {
72-
var fetchedHistory = deferred.resolve.bind(null, guid)
73-
, errorHistory = deferred.reject.bind(null, 'ERR_HISTORY')
74-
, pwHash = generatePwHash(password);
74+
var fetched = deferred.resolve.bind(null, guid)
75+
, error = deferred.reject.bind(null, 'ERR_HISTORY')
76+
, pwHash = generatePwHash(password);
7577

76-
if (bc.MyWallet.detectPrivateKeyFormat(options.priv) !== null) {
77-
bc.MyWallet.wallet.deleteLegacyAddress(bc.MyWallet.wallet.keys[0]);
78-
bc.MyWallet.wallet.importLegacyAddress(options.priv, options.label);
79-
}
78+
this.pwHashStore[guid] = pwHash;
79+
this.instanceStore[guid] = instance;
8080

81-
validatePassword = function (p) { return generatePwHash(p).compare(pwHash) === 0; };
82-
bc.MyWallet.wallet.getHistory().then(fetchedHistory).catch(errorHistory);
83-
};
81+
if (instance.MyWallet.detectPrivateKeyFormat(options.priv) !== null) {
82+
instance.MyWallet.wallet.deleteLegacyAddress(instance.MyWallet.wallet.keys[0]);
83+
instance.MyWallet.wallet.importLegacyAddress(options.priv, options.label);
84+
}
8485

85-
var done = function () {
86-
clearTimeout(timeout);
87-
this.loggingIn = false;
86+
instance.MyWallet.wallet.getHistory().then(fetched).catch(error);
8887
}.bind(this);
8988

90-
safeReset().then(function () {
91-
this.loggingIn = true;
92-
bc.API.API_CODE = options.api_code;
93-
bc.WalletStore.setAPICode(options.api_code);
94-
bc.WalletStore.isLogoutDisabled = function () { return true; };
95-
bc.MyWallet.createNewWallet(email, pass, label, lang, currency, success, deferred.reject, isHD);
96-
}.bind(this));
89+
instance.MyWallet.createNewWallet(
90+
email, pass, label, lang, currency, success, deferred.reject, isHD);
9791

98-
return deferred.promise.fin(done);
92+
return deferred.promise.fin(clearTimeout.bind(null, timeout));
9993
};
10094

10195
WalletCache.prototype.getWallet = function (guid, options) {
102-
var exists = bc && bc.MyWallet && bc.MyWallet.wallet && bc.MyWallet.wallet.guid === guid
103-
, validpw = validatePassword(options.password)
96+
var inst = this.instanceStore[guid]
97+
, exists = inst && inst.MyWallet.wallet && inst.MyWallet.wallet.guid === guid
98+
, validpw = validatePassword(this.pwHashStore[guid], options.password)
10499
, err = !exists && 'ERR_WALLET_ID' || !validpw && 'ERR_PASSWORD';
105-
return err ? q.reject(err) : q(bc.MyWallet.wallet);
100+
return err ? q.reject(err) : q(inst.MyWallet.wallet);
106101
};
107102

108-
WalletCache.prototype.walletPayment = function () {
109-
return new bc.Payment();
103+
WalletCache.prototype.walletPayment = function (guid) {
104+
var instance = this.instanceStore[guid];
105+
if (!instance || !instance.Payment) throw 'ERR_PAYMENT';
106+
return new instance.Payment();
110107
};
111108

112109
module.exports = WalletCache;
113110

114-
function safeReset() {
115-
var deferred = q.defer();
116-
if (bc && bc.MyWallet && bc.MyWallet.wallet) {
117-
if (!bc.WalletStore.isSynchronizedWithServer()) {
118-
bc.MyWallet.syncWallet(refreshCache, deferred.reject);
119-
} else {
120-
refreshCache();
121-
}
122-
} else {
123-
refreshCache();
124-
}
125-
function refreshCache() {
126-
if (require.cache) {
127-
Object.keys(require.cache)
128-
.filter(function (module) {
129-
return (module.indexOf('blockchain-wallet-client-prebuilt/index') > -1 ||
130-
module.indexOf('blockchain-wallet-client-prebuilt/src') > -1);
131-
})
132-
.forEach(function (module) { delete require.cache[module]; });
133-
}
134-
bc = require('blockchain-wallet-client-prebuilt');
135-
deferred.resolve(true);
136-
}
137-
return deferred.promise;
111+
function generateInstance() {
112+
Object.keys(require.cache)
113+
.filter(function (module) {
114+
return (module.indexOf('blockchain-wallet-client-prebuilt/index') > -1 ||
115+
module.indexOf('blockchain-wallet-client-prebuilt/src') > -1);
116+
})
117+
.forEach(function (module) { require.cache[module] = undefined; });
118+
return require('blockchain-wallet-client-prebuilt');
138119
}
139120

140121
function generatePwHash(pw) {
141122
var iterations = 5000;
142123
return crypto.pbkdf2Sync(pw, randomBytes, iterations, BYTES_PER_HASH, 'sha256');
143124
}
125+
126+
function validatePassword(hash, maybePw) {
127+
if (!Buffer.isBuffer(hash) || !maybePw) return false;
128+
return generatePwHash(maybePw).compare(hash) === 0;
129+
}

0 commit comments

Comments
 (0)