Completed
Pull Request — master (#83)
by Ruben de
02:25
created

APIClient.promisedEncrypt   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 1
dl 0
loc 3
rs 10
nop 0
1
var _ = require('lodash'),
2
    q = require('q'),
3
    bitcoin = require('bitcoinjs-lib'),
4
    bitcoinMessage = require('bitcoinjs-message'),
5
6
    bip39 = require("bip39"),
7
    Wallet = require('./wallet'),
8
    RestClient = require('./rest_client'),
9
    Encryption = require('./encryption'),
10
    KeyDerivation = require('./keyderivation'),
11
    EncryptionMnemonic = require('./encryption_mnemonic'),
12
    blocktrail = require('./blocktrail'),
13
    randomBytes = require('randombytes'),
14
    CryptoJS = require('crypto-js'),
15
    webworkifier = require('./webworkifier');
16
17
var useWebWorker = require('./use-webworker')();
18
19
/**
20
 * Bindings to conssume the BlockTrail API
21
 *
22
 * @param options       object{
23
 *                          apiKey: 'API_KEY',
24
 *                          apiSecret: 'API_SECRET',
25
 *                          host: 'defaults to api.blocktrail.com',
26
 *                          network: 'BTC|LTC',
27
 *                          testnet: true|false
28
 *                      }
29
 * @constructor
30
 */
31
var APIClient = function(options) {
32
    var self = this;
33
34
    // handle constructor call without 'new'
35
    if (!(this instanceof APIClient)) {
36
        return new APIClient(options);
37
    }
38
    self.bitcoinCash = options.network && options.network === "BCC";
39
40
    self.testnet = options.testnet = options.testnet || false;
41
    if (self.bitcoinCash) {
42
        if (self.testnet) {
43
            self.network = bitcoin.networks.bitcoincashtestnet;
44
        } else {
45
            self.network = bitcoin.networks.bitcoincash;
46
        }
47
    } else {
48
        if (self.testnet) {
49
            self.network = bitcoin.networks.testnet;
50
        } else {
51
            self.network = bitcoin.networks.bitcoin;
52
        }
53
    }
54
55
    self.feeSanityCheck = typeof options.feeSanityCheck !== "undefined" ? options.feeSanityCheck : true;
56
    self.feeSanityCheckBaseFeeMultiplier = options.feeSanityCheckBaseFeeMultiplier || 200;
57
58
    options.apiNetwork = options.apiNetwork || ((self.testnet ? "t" : "") + (options.network || 'BTC').toUpperCase());
59
60
    /**
61
     * @type RestClient
62
     */
63
    self.client = APIClient.initRestClient(options);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
64
};
65
66
APIClient.initRestClient = function(options) {
67
    // BLOCKTRAIL_SDK_API_ENDPOINT overwrite for development
68
    if (process.env.BLOCKTRAIL_SDK_API_ENDPOINT) {
69
        options.host = process.env.BLOCKTRAIL_SDK_API_ENDPOINT;
70
    }
71
72
    // trim off leading https?://
73
    if (options.host && options.host.indexOf("https://") === 0) {
74
        options.https = true;
75
        options.host = options.host.substr(8);
76
    } else if (options.host && options.host.indexOf("http://") === 0) {
77
        options.https = false;
78
        options.host = options.host.substr(7);
79
    }
80
81
    if (typeof options.https === "undefined") {
82
        options.https = true;
83
    }
84
85
    if (!options.host) {
86
        options.host = 'api.blocktrail.com';
87
    }
88
89
    if (!options.port) {
90
        options.port = options.https ? 443 : 80;
91
    }
92
93
    if (!options.endpoint) {
94
        options.endpoint = "/" + (options.apiVersion || "v1") + (options.apiNetwork ? ("/" + options.apiNetwork) : "");
95
    }
96
97
    return new RestClient(options);
98
};
99
100
var determineDataStorageV2_3 = function(options) {
101
    return q.when(options)
102
        .then(function(options) {
103
            // legacy
104
            if (options.storePrimaryMnemonic) {
105
                options.storeDataOnServer = options.storePrimaryMnemonic;
106
            }
107
108
            // storeDataOnServer=false when primarySeed is provided
109
            if (typeof options.storeDataOnServer === "undefined") {
110
                options.storeDataOnServer = !options.primarySeed;
111
            }
112
113
            return options;
114
        });
115
};
116
117
var produceEncryptedDataV2 = function(options, notify) {
118
    return q.when(options)
119
        .then(function(options) {
120
            if (options.storeDataOnServer) {
121
                if (!options.secret) {
122
                    if (!options.passphrase) {
123
                        throw new blocktrail.WalletCreateError("Can't encrypt data without a passphrase");
124
                    }
125
126
                    notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET);
127
128
                    options.secret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8).toString('hex'); // string because we use it as passphrase
129
                    options.encryptedSecret = CryptoJS.AES.encrypt(options.secret, options.passphrase).toString(CryptoJS.format.OpenSSL); // 'base64' string
130
                }
131
132
                notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY);
133
134
                options.encryptedPrimarySeed = CryptoJS.AES.encrypt(options.primarySeed.toString('base64'), options.secret)
135
                    .toString(CryptoJS.format.OpenSSL); // 'base64' string
136
                options.recoverySecret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8).toString('hex'); // string because we use it as passphrase
137
138
                notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY);
139
140
                options.recoveryEncryptedSecret = CryptoJS.AES.encrypt(options.secret, options.recoverySecret)
141
                                                              .toString(CryptoJS.format.OpenSSL); // 'base64' string
142
            }
143
144
            return options;
145
        });
146
};
147
148
APIClient.prototype.promisedEncrypt = function(pt, pw, iter) {
149
    if (useWebWorker && onLoadWorkerLoadAsmCrypto) {
0 ignored issues
show
Best Practice introduced by
If you intend to check if the variable onLoadWorkerLoadAsmCrypto is declared in the current environment, consider using typeof onLoadWorkerLoadAsmCrypto === "undefined" instead. This is safe if the variable is not actually declared.
Loading history...
150
        // generate randomness outside of webworker because many browsers don't have crypto.getRandomValues inside webworkers
151
        var saltBuf = Encryption.generateSalt();
152
        var iv = Encryption.generateIV();
153
154
        return webworkifier.workify(APIClient.prototype.promisedEncrypt, function factory() {
155
            return require('./webworker');
156
        }, onLoadWorkerLoadAsmCrypto, {
157
            method: 'Encryption.encryptWithSaltAndIV',
158
            pt: pt,
159
            pw: pw,
160
            saltBuf: saltBuf,
161
            iv: iv,
162
            iterations: iter
163
        })
164
            .then(function(data) {
165
                return Buffer.from(data.cipherText.buffer);
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
166
            });
167
    } else {
168
        try {
169
            return q.when(Encryption.encrypt(pt, pw, iter));
170
        } catch (e) {
171
            return q.reject(e);
172
        }
173
    }
174
};
175
176
APIClient.prototype.promisedDecrypt = function(ct, pw) {
177
    if (useWebWorker && onLoadWorkerLoadAsmCrypto) {
0 ignored issues
show
Best Practice introduced by
If you intend to check if the variable onLoadWorkerLoadAsmCrypto is declared in the current environment, consider using typeof onLoadWorkerLoadAsmCrypto === "undefined" instead. This is safe if the variable is not actually declared.
Loading history...
178
        return webworkifier.workify(APIClient.prototype.promisedDecrypt, function() {
179
            return require('./webworker');
180
        }, onLoadWorkerLoadAsmCrypto, {
181
            method: 'Encryption.decrypt',
182
            ct: ct,
183
            pw: pw
184
        })
185
            .then(function(data) {
186
                return Buffer.from(data.plainText.buffer);
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
187
            });
188
    } else {
189
        try {
190
            return q.when(Encryption.decrypt(ct, pw));
191
        } catch (e) {
192
            return q.reject(e);
193
        }
194
    }
195
};
196
197
APIClient.prototype.produceEncryptedDataV3 = function(options, notify) {
198
    var self = this;
199
200
    return q.when(options)
201
        .then(function(options) {
202
            if (options.storeDataOnServer) {
203
                return q.when()
204
                    .then(function() {
205
                        if (!options.secret) {
206
                            if (!options.passphrase) {
207
                                throw new blocktrail.WalletCreateError("Can't encrypt data without a passphrase");
208
                            }
209
210
                            notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET);
211
212
                            // -> now a buffer
213
                            options.secret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
214
215
                            // -> now a buffer
216
                            return self.promisedEncrypt(options.secret, new Buffer(options.passphrase), KeyDerivation.defaultIterations)
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
217
                                .then(function(encryptedSecret) {
218
                                    options.encryptedSecret = encryptedSecret;
219
                                });
220
                        } else {
221
                            if (!(options.secret instanceof Buffer)) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if !(options.secret instanceof Buffer) is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
222
                                throw new Error('Secret must be a buffer');
223
                            }
224
                        }
225
                    })
226
                    .then(function() {
227
                        notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY);
228
229
                        return self.promisedEncrypt(options.primarySeed, options.secret, KeyDerivation.subkeyIterations)
230
                            .then(function(encryptedPrimarySeed) {
231
                                options.encryptedPrimarySeed = encryptedPrimarySeed;
232
                            });
233
                    })
234
                    .then(function() {
235
                        // skip generating recovery secret when explicitly set to false
236
                        if (options.recoverySecret === false) {
237
                            return;
238
                        }
239
240
                        notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY);
241
                        if (!options.recoverySecret) {
242
                            options.recoverySecret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
243
                        }
244
245
                        return self.promisedEncrypt(options.secret, options.recoverySecret, KeyDerivation.defaultIterations)
246
                            .then(function(recoveryEncryptedSecret) {
247
                                options.recoveryEncryptedSecret = recoveryEncryptedSecret;
248
                            });
249
                    })
250
                    .then(function() {
251
                        return options;
252
                    });
253
            } else {
254
                return options;
255
            }
256
        });
257
};
258
259
var doRemainingWalletDataV2_3 = function(options, network, notify) {
260
    return q.when(options)
261
        .then(function(options) {
262
            if (!options.backupPublicKey) {
263
                options.backupSeed = options.backupSeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
264
            }
265
266
            notify(APIClient.CREATE_WALLET_PROGRESS_PRIMARY);
267
268
            options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.primarySeed, network);
269
270
            notify(APIClient.CREATE_WALLET_PROGRESS_BACKUP);
271
272
            if (!options.backupPublicKey) {
273
                options.backupPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.backupSeed, network);
274
                options.backupPublicKey = options.backupPrivateKey.neutered();
275
            }
276
277
            options.primaryPublicKey = options.primaryPrivateKey.deriveHardened(options.keyIndex).neutered();
278
279
            notify(APIClient.CREATE_WALLET_PROGRESS_SUBMIT);
280
281
            return options;
282
        });
283
};
284
285
APIClient.prototype.mnemonicToPrivateKey = function(mnemonic, passphrase, cb) {
286
    var self = this;
287
288
    var deferred = q.defer();
289
    deferred.promise.spreadNodeify(cb);
290
291
    deferred.resolve(q.fcall(function() {
292
        return self.mnemonicToSeedHex(mnemonic, passphrase).then(function(seedHex) {
293
            return bitcoin.HDNode.fromSeedHex(seedHex, self.network);
294
        });
295
    }));
296
297
    return deferred.promise;
298
};
299
300
APIClient.prototype.mnemonicToSeedHex = function(mnemonic, passphrase) {
301
    var self = this;
302
303
    if (useWebWorker) {
304
        return webworkifier.workify(self.mnemonicToSeedHex, function() {
305
            return require('./webworker');
306
        }, {method: 'mnemonicToSeedHex', mnemonic: mnemonic, passphrase: passphrase})
307
            .then(function(data) {
308
                return data.seed;
309
            });
310
    } else {
311
        try {
312
            return q.when(bip39.mnemonicToSeedHex(mnemonic, passphrase));
313
        } catch (e) {
314
            return q.reject(e);
315
        }
316
    }
317
};
318
319
APIClient.prototype.resolvePrimaryPrivateKeyFromOptions = function(options, cb) {
320
    var self = this;
321
322
    var deferred = q.defer();
323
    deferred.promise.nodeify(cb);
324
325
    try {
326
        // avoid conflicting options
327
        if (options.passphrase && options.password) {
328
            throw new blocktrail.WalletCreateError("Can't specify passphrase and password");
329
        }
330
        // normalize passphrase/password
331
        options.passphrase = options.passphrase || options.password;
332
        delete options.password;
333
334
        // avoid conflicting options
335
        if (options.primaryMnemonic && options.primarySeed) {
336
            throw new blocktrail.WalletInitError("Can only specify one of; Primary Mnemonic or Primary Seed");
337
        }
338
339
        // avoid deprecated options
340
        if (options.primaryPrivateKey) {
341
            throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
342
        }
343
344
        // make sure we have at least one thing to use
345
        if (!options.primaryMnemonic && !options.primarySeed) {
346
            throw new blocktrail.WalletInitError("Need to specify at least one of; Primary Mnemonic or Primary Seed");
347
        }
348
349
        if (options.primarySeed) {
350
            self.primarySeed = options.primarySeed;
351
            options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(self.primarySeed, self.network);
352
            deferred.resolve(options);
353
        } else {
354
            if (!options.passphrase) {
355
                throw new blocktrail.WalletInitError("Can't init wallet with Primary Mnemonic without a passphrase");
356
            }
357
358
            self.mnemonicToSeedHex(options.primaryMnemonic, options.passphrase)
359
                .then(function(seedHex) {
360
                    try {
361
                        options.primarySeed = new Buffer(seedHex, 'hex');
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
362
                        options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.primarySeed, self.network);
363
                        deferred.resolve(options);
364
                    } catch (e) {
365
                        deferred.reject(e);
366
                    }
367
                }, function(e) {
368
                    deferred.reject(e);
369
                });
370
        }
371
    } catch (e) {
372
        deferred.reject(e);
373
    }
374
375
    return deferred.promise;
376
};
377
378
APIClient.prototype.resolveBackupPublicKeyFromOptions = function(options, cb) {
379
    var self = this;
380
381
    var deferred = q.defer();
382
    deferred.promise.nodeify(cb);
383
384
    try {
385
        // avoid conflicting options
386
        if (options.backupMnemonic && options.backupPublicKey) {
387
            throw new blocktrail.WalletInitError("Can only specify one of; Backup Mnemonic or Backup PublicKey");
388
        }
389
390
        // make sure we have at least one thing to use
391
        if (!options.backupMnemonic && !options.backupPublicKey) {
392
            throw new blocktrail.WalletInitError("Need to specify at least one of; Backup Mnemonic or Backup PublicKey");
393
        }
394
395
        if (options.backupPublicKey) {
396
            if (options.backupPublicKey instanceof bitcoin.HDNode) {
397
                deferred.resolve(options);
398
            } else {
399
                options.backupPublicKey = bitcoin.HDNode.fromBase58(options.backupPublicKey, self.network);
400
                deferred.resolve(options);
401
            }
402
        } else {
403
            self.mnemonicToPrivateKey(options.backupMnemonic, "").then(function(backupPrivateKey) {
404
                options.backupPublicKey = backupPrivateKey.neutered();
405
                deferred.resolve(options);
406
            }, function(e) {
407
                deferred.reject(e);
408
            });
409
        }
410
    } catch (e) {
411
        deferred.reject(e);
412
    }
413
414
    return deferred.promise;
415
};
416
417
APIClient.prototype.debugAuth = function(cb) {
418
    var self = this;
419
420
    return self.client.get("/debug/http-signature", null, true, cb);
421
};
422
423
/**
424
 * get a single address
425
 *
426
 * @param address      string       address hash
427
 * @param [cb]          function    callback function to call when request is complete
428
 * @return q.Promise
429
 */
430
APIClient.prototype.address = function(address, cb) {
431
    var self = this;
432
433
    return self.client.get("/address/" + address, null, cb);
434
};
435
436
APIClient.prototype.addresses = function(addresses, cb) {
437
    var self = this;
438
439
    return self.client.post("/address", null, {"addresses": addresses}, cb);
440
};
441
442
/**
443
 * get all transactions for an address (paginated)
444
 *
445
 * @param address       string      address hash
446
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
447
 * @param [cb]          function    callback function to call when request is complete
448
 * @return q.Promise
449
 */
450
APIClient.prototype.addressTransactions = function(address, params, cb) {
451
    var self = this;
452
453
    if (typeof params === "function") {
454
        cb = params;
455
        params = null;
456
    }
457
458
    return self.client.get("/address/" + address + "/transactions", params, cb);
459
};
460
461
/**
462
 * get all transactions for a batch of addresses (paginated)
463
 *
464
 * @param addresses     array       address hashes
465
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
466
 * @param [cb]          function    callback function to call when request is complete
467
 * @return q.Promise
468
 */
469
APIClient.prototype.batchAddressHasTransactions = function(addresses, params, cb) {
470
    var self = this;
471
472
    if (typeof params === "function") {
473
        cb = params;
474
        params = null;
475
    }
476
477
    return self.client.post("/address/has-transactions", params, {"addresses": addresses}, cb);
478
};
479
480
/**
481
 * get all unconfirmed transactions for an address (paginated)
482
 *
483
 * @param address       string      address hash
484
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
485
 * @param [cb]          function    callback function to call when request is complete
486
 * @return q.Promise
487
 */
488
APIClient.prototype.addressUnconfirmedTransactions = function(address, params, cb) {
489
    var self = this;
490
491
    if (typeof params === "function") {
492
        cb = params;
493
        params = null;
494
    }
495
496
    return self.client.get("/address/" + address + "/unconfirmed-transactions", params, cb);
497
};
498
499
/**
500
 * get all unspent outputs for an address (paginated)
501
 *
502
 * @param address       string      address hash
503
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
504
 * @param [cb]          function    callback function to call when request is complete
505
 * @return q.Promise
506
 */
507
APIClient.prototype.addressUnspentOutputs = function(address, params, cb) {
508
    var self = this;
509
510
    if (typeof params === "function") {
511
        cb = params;
512
        params = null;
513
    }
514
515
    return self.client.get("/address/" + address + "/unspent-outputs", params, cb);
516
};
517
518
/**
519
 * get all unspent outputs for a batch of addresses (paginated)
520
 *
521
 * @param addresses     array       address hashes
522
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
523
 * @param [cb]          function    callback function to call when request is complete
524
 * @return q.Promise
525
 */
526
APIClient.prototype.batchAddressUnspentOutputs = function(addresses, params, cb) {
527
    var self = this;
528
529
    if (typeof params === "function") {
530
        cb = params;
531
        params = null;
532
    }
533
534
    return self.client.post("/address/unspent-outputs", params, {"addresses": addresses}, cb);
535
};
536
537
/**
538
 * verify ownership of an address
539
 *
540
 * @param address       string      address hash
541
 * @param signature     string      a signed message (the address hash) using the private key of the address
542
 * @param [cb]          function    callback function to call when request is complete
543
 * @return q.Promise
544
 */
545
APIClient.prototype.verifyAddress = function(address, signature, cb) {
546
    var self = this;
547
548
    return self.client.post("/address/" + address + "/verify", null, {signature: signature}, cb);
549
};
550
551
/**
552
 * get all blocks (paginated)
553
 *
554
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
555
 * @param [cb]          function    callback function to call when request is complete
556
 * @return q.Promise
557
 */
558
APIClient.prototype.allBlocks = function(params, cb) {
559
    var self = this;
560
561
    if (typeof params === "function") {
562
        cb = params;
563
        params = null;
564
    }
565
566
    return self.client.get("/all-blocks", params, cb);
567
};
568
569
/**
570
 * get a block
571
 *
572
 * @param block         string|int  a block hash or a block height
573
 * @param [cb]          function    callback function to call when request is complete
574
 * @return q.Promise
575
 */
576
APIClient.prototype.block = function(block, cb) {
577
    var self = this;
578
579
    return self.client.get("/block/" + block, null, cb);
580
};
581
582
/**
583
 * get the latest block
584
 *
585
 * @param [cb]          function    callback function to call when request is complete
586
 * @return q.Promise
587
 */
588
APIClient.prototype.blockLatest = function(cb) {
589
    var self = this;
590
591
    return self.client.get("/block/latest", null, cb);
592
};
593
594
/**
595
 * get all transactions for a block (paginated)
596
 *
597
 * @param block         string|int  a block hash or a block height
598
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
599
 * @param [cb]          function    callback function to call when request is complete
600
 * @return q.Promise
601
 */
602
APIClient.prototype.blockTransactions = function(block, params, cb) {
603
    var self = this;
604
605
    if (typeof params === "function") {
606
        cb = params;
607
        params = null;
608
    }
609
610
    return self.client.get("/block/" + block + "/transactions", params, cb);
611
};
612
613
/**
614
 * get a single transaction
615
 *
616
 * @param tx            string      transaction hash
617
 * @param [cb]          function    callback function to call when request is complete
618
 * @return q.Promise
619
 */
620
APIClient.prototype.transaction = function(tx, cb) {
621
    var self = this;
622
623
    return self.client.get("/transaction/" + tx, null, cb);
624
};
625
626
/**
627
 * get a batch of transactions
628
 *
629
 * @param txs           string[]    list of transaction hashes (txId)
630
 * @param [cb]          function    callback function to call when request is complete
631
 * @return q.Promise
632
 */
633
APIClient.prototype.transactions = function(txs, cb) {
634
    var self = this;
635
636
    return self.client.post("/transactions", null, txs, cb, false);
637
};
638
639
/**
640
 * get a paginated list of all webhooks associated with the api user
641
 *
642
 * @param [params]      object      pagination: {page: 1, limit: 20}
643
 * @param [cb]          function    callback function to call when request is complete
644
 * @return q.Promise
645
 */
646
APIClient.prototype.allWebhooks = function(params, cb) {
647
    var self = this;
648
649
    if (typeof params === "function") {
650
        cb = params;
651
        params = null;
652
    }
653
654
    return self.client.get("/webhooks", params, cb);
655
};
656
657
/**
658
 * create a new webhook
659
 *
660
 * @param url           string      the url to receive the webhook events
661
 * @param [identifier]  string      a unique identifier associated with the webhook
662
 * @param [cb]          function    callback function to call when request is complete
663
 * @return q.Promise
664
 */
665
APIClient.prototype.setupWebhook = function(url, identifier, cb) {
666
    var self = this;
667
668
    if (typeof identifier === "function") {
669
        //mimic function overloading
670
        cb = identifier;
671
        identifier = null;
672
    }
673
674
    return self.client.post("/webhook", null, {url: url, identifier: identifier}, cb);
675
};
676
677
/**
678
 * get an existing webhook by it's identifier
679
 *
680
 * @param identifier    string      the unique identifier of the webhook to get
681
 * @param [cb]          function    callback function to call when request is complete
682
 * @return q.Promise
683
 */
684
APIClient.prototype.getWebhook = function(identifier, cb) {
685
    var self = this;
686
687
    return self.client.get("/webhook/" + identifier, null, cb);
688
};
689
690
/**
691
 * update an existing webhook
692
 *
693
 * @param identifier    string      the unique identifier of the webhook
694
 * @param webhookData   object      the data to update: {identifier: newIdentifier, url:newUrl}
695
 * @param [cb]          function    callback function to call when request is complete
696
 * @return q.Promise
697
 */
698
APIClient.prototype.updateWebhook = function(identifier, webhookData, cb) {
699
    var self = this;
700
701
    return self.client.put("/webhook/" + identifier, null, webhookData, cb);
702
};
703
704
/**
705
 * deletes an existing webhook and any event subscriptions associated with it
706
 *
707
 * @param identifier    string      the unique identifier of the webhook
708
 * @param [cb]          function    callback function to call when request is complete
709
 * @return q.Promise
710
 */
711
APIClient.prototype.deleteWebhook = function(identifier, cb) {
712
    var self = this;
713
714
    return self.client.delete("/webhook/" + identifier, null, null, cb);
715
};
716
717
/**
718
 * get a paginated list of all the events a webhook is subscribed to
719
 *
720
 * @param identifier    string      the unique identifier of the webhook
721
 * @param [params]      object      pagination: {page: 1, limit: 20}
722
 * @param [cb]          function    callback function to call when request is complete
723
 * @return q.Promise
724
 */
725
APIClient.prototype.getWebhookEvents = function(identifier, params, cb) {
726
    var self = this;
727
728
    if (typeof params === "function") {
729
        cb = params;
730
        params = null;
731
    }
732
733
    return self.client.get("/webhook/" + identifier + "/events", params, cb);
734
};
735
736
/**
737
 * subscribes a webhook to transaction events for a particular transaction
738
 *
739
 * @param identifier    string      the unique identifier of the webhook
740
 * @param transaction   string      the transaction hash
741
 * @param confirmations integer     the amount of confirmations to send
742
 * @param [cb]          function    callback function to call when request is complete
743
 * @return q.Promise
744
 */
745
APIClient.prototype.subscribeTransaction = function(identifier, transaction, confirmations, cb) {
746
    var self = this;
747
    var postData = {
748
        'event_type': 'transaction',
749
        'transaction': transaction,
750
        'confirmations': confirmations
751
    };
752
753
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
754
};
755
756
/**
757
 * subscribes a webhook to transaction events on a particular address
758
 *
759
 * @param identifier    string      the unique identifier of the webhook
760
 * @param address       string      the address hash
761
 * @param confirmations integer     the amount of confirmations to send
762
 * @param [cb]          function    callback function to call when request is complete
763
 * @return q.Promise
764
 */
765
APIClient.prototype.subscribeAddressTransactions = function(identifier, address, confirmations, cb) {
766
    var self = this;
767
    var postData = {
768
        'event_type': 'address-transactions',
769
        'address': address,
770
        'confirmations': confirmations
771
    };
772
773
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
774
};
775
776
/**
777
 * batch subscribes a webhook to multiple transaction events
778
 *
779
 * @param  identifier   string      the unique identifier of the webhook
780
 * @param  batchData    array       An array of objects containing batch event data:
781
 *                                  {address : 'address', confirmations : 'confirmations']
782
 *                                  where address is the address to subscribe to and confirmations (optional) is the amount of confirmations to send
783
 * @param [cb]          function    callback function to call when request is complete
784
 * @return q.Promise
785
 */
786
APIClient.prototype.batchSubscribeAddressTransactions = function(identifier, batchData, cb) {
787
    var self = this;
788
    batchData.forEach(function(record) {
789
        record.event_type = 'address-transactions';
790
    });
791
792
    return self.client.post("/webhook/" + identifier + "/events/batch", null, batchData, cb);
793
};
794
795
/**
796
 * subscribes a webhook to a new block event
797
 *
798
 * @param identifier    string      the unique identifier of the webhook
799
 * @param [cb]          function    callback function to call when request is complete
800
 * @return q.Promise
801
 */
802
APIClient.prototype.subscribeNewBlocks = function(identifier, cb) {
803
    var self = this;
804
    var postData = {
805
        'event_type': 'block'
806
    };
807
808
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
809
};
810
811
/**
812
 * removes an address transaction event subscription from a webhook
813
 *
814
 * @param identifier    string      the unique identifier of the webhook
815
 * @param address       string      the address hash
816
 * @param [cb]          function    callback function to call when request is complete
817
 * @return q.Promise
818
 */
819
APIClient.prototype.unsubscribeAddressTransactions = function(identifier, address, cb) {
820
    var self = this;
821
822
    return self.client.delete("/webhook/" + identifier + "/address-transactions/" + address, null, null, cb);
823
};
824
825
/**
826
 * removes an transaction event subscription from a webhook
827
 *
828
 * @param identifier    string      the unique identifier of the webhook
829
 * @param transaction   string      the transaction hash
830
 * @param [cb]          function    callback function to call when request is complete
831
 * @return q.Promise
832
 */
833
APIClient.prototype.unsubscribeTransaction = function(identifier, transaction, cb) {
834
    var self = this;
835
836
    return self.client.delete("/webhook/" + identifier + "/transaction/" + transaction, null, null, cb);
837
};
838
839
/**
840
 * removes a block event subscription from a webhook
841
 *
842
 * @param identifier    string      the unique identifier of the webhook
843
 * @param [cb]          function    callback function to call when request is complete
844
 * @return q.Promise
845
 */
846
APIClient.prototype.unsubscribeNewBlocks = function(identifier, cb) {
847
    var self = this;
848
849
    return self.client.delete("/webhook/" + identifier + "/block", null, null, cb);
850
};
851
852
/**
853
 * initialize an existing wallet
854
 *
855
 * Either takes two argument:
856
 * @param options       object      {}
857
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
858
 *
859
 * Or takes three arguments (old, deprecated syntax):
860
 * @param identifier    string      the wallet identifier to be initialized
0 ignored issues
show
Documentation introduced by
The parameter identifier does not exist. Did you maybe forget to remove this comment?
Loading history...
861
 * @param passphrase    string      the password to decrypt the mnemonic with
0 ignored issues
show
Documentation introduced by
The parameter passphrase does not exist. Did you maybe forget to remove this comment?
Loading history...
862
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 857. The second definition is ignored.
Loading history...
863
 *
864
 * @returns {q.Promise}
865
 */
866
APIClient.prototype.initWallet = function(options, cb) {
867
    var self = this;
868
869
    if (typeof options !== "object") {
870
        // get the old-style arguments
871
        options = {
872
            identifier: arguments[0],
873
            passphrase: arguments[1]
874
        };
875
876
        cb = arguments[2];
877
    }
878
879
    if (options.check_backup_key) {
880
        if (typeof options.check_backup_key !== "string") {
881
            throw new Error("Invalid input, must provide the backup key as a string (the xpub)");
882
        }
883
    }
884
885
    var deferred = q.defer();
886
    deferred.promise.spreadNodeify(cb);
887
888
    var identifier = options.identifier;
889
890
    if (!identifier) {
891
        deferred.reject(new blocktrail.WalletInitError("Identifier is required"));
892
        return deferred.promise;
893
    }
894
895
    deferred.resolve(self.client.get("/wallet/" + identifier, null, true).then(function(result) {
896
        var keyIndex = options.keyIndex || result.key_index;
897
898
        options.walletVersion = result.wallet_version;
899
900
        if (options.check_backup_key) {
901
            if (options.check_backup_key !== result.backup_public_key[0]) {
902
                throw new Error("Backup key returned from server didn't match our own copy");
903
            }
904
        }
905
        var backupPublicKey = bitcoin.HDNode.fromBase58(result.backup_public_key[0], self.network);
906
        var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
907
            return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
908
        });
909
        var primaryPublicKeys = _.mapValues(result.primary_public_keys, function(primaryPublicKey) {
910
            return bitcoin.HDNode.fromBase58(primaryPublicKey[0], self.network);
911
        });
912
913
        // initialize wallet
914
        var wallet = new Wallet(
915
            self,
916
            identifier,
917
            options.walletVersion,
918
            result.primary_mnemonic,
919
            result.encrypted_primary_seed,
920
            result.encrypted_secret,
921
            primaryPublicKeys,
922
            backupPublicKey,
923
            blocktrailPublicKeys,
924
            keyIndex,
925
            result.segwit || 0,
926
            self.testnet,
927
            result.checksum,
928
            result.upgrade_key_index,
929
            options.bypassNewAddressCheck
930
        );
931
932
        wallet.recoverySecret = result.recovery_secret;
933
934
        if (!options.readOnly) {
935
            return wallet.unlock(options).then(function() {
936
                return wallet;
937
            });
938
        } else {
939
            return wallet;
940
        }
941
    }));
942
943
    return deferred.promise;
944
};
945
946
APIClient.CREATE_WALLET_PROGRESS_START = 0;
947
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET = 4;
948
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY = 5;
949
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY = 6;
950
APIClient.CREATE_WALLET_PROGRESS_PRIMARY = 10;
951
APIClient.CREATE_WALLET_PROGRESS_BACKUP = 20;
952
APIClient.CREATE_WALLET_PROGRESS_SUBMIT = 30;
953
APIClient.CREATE_WALLET_PROGRESS_INIT = 40;
954
APIClient.CREATE_WALLET_PROGRESS_DONE = 100;
955
956
/**
957
 * create a new wallet
958
 *   - will generate a new primary seed and backup seed
959
 *
960
 * Either takes two argument:
961
 * @param options       object      {}
962
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys) // nocommit @TODO
963
 *
964
 * For v1 wallets (explicitly specify options.walletVersion=v1):
965
 * @param options       object      {}
0 ignored issues
show
Documentation introduced by
The parameter options has already been documented on line 961. The second definition is ignored.
Loading history...
966
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 962. The second definition is ignored.
Loading history...
967
 *
968
 * Or takes four arguments (old, deprecated syntax):
969
 * @param identifier    string      the wallet identifier to be initialized
0 ignored issues
show
Documentation introduced by
The parameter identifier does not exist. Did you maybe forget to remove this comment?
Loading history...
970
 * @param passphrase    string      the password to decrypt the mnemonic with
0 ignored issues
show
Documentation introduced by
The parameter passphrase does not exist. Did you maybe forget to remove this comment?
Loading history...
971
 * @param keyIndex      int         override for the blocktrail cosign key to use (for development purposes)
0 ignored issues
show
Documentation introduced by
The parameter keyIndex does not exist. Did you maybe forget to remove this comment?
Loading history...
972
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 962. The second definition is ignored.
Loading history...
973
 * @returns {q.Promise}
974
 */
975
APIClient.prototype.createNewWallet = function(options, cb) {
976
    /* jshint -W071, -W074 */
977
978
    var self = this;
979
980
    if (typeof options !== "object") {
981
        // get the old-style arguments
982
        var identifier = arguments[0];
983
        var passphrase = arguments[1];
984
        var keyIndex = arguments[2];
985
        cb = arguments[3];
986
987
        // keyIndex is optional
988
        if (typeof keyIndex === "function") {
989
            cb = keyIndex;
990
            keyIndex = null;
991
        }
992
993
        options = {
994
            identifier: identifier,
995
            passphrase: passphrase,
996
            keyIndex: keyIndex
997
        };
998
    }
999
1000
    // default to v3
1001
    options.walletVersion = options.walletVersion || Wallet.WALLET_VERSION_V3;
1002
1003
    var deferred = q.defer();
1004
    deferred.promise.spreadNodeify(cb);
1005
1006
    q.nextTick(function() {
1007
        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_START);
1008
1009
        options.keyIndex = options.keyIndex || 0;
1010
        options.passphrase = options.passphrase || options.password;
1011
        delete options.password;
1012
1013
        if (!options.identifier) {
1014
            deferred.reject(new blocktrail.WalletCreateError("Identifier is required"));
1015
            return deferred.promise;
1016
        }
1017
1018
        if (options.walletVersion === Wallet.WALLET_VERSION_V1) {
1019
            self._createNewWalletV1(options)
1020
                .progress(function(p) { deferred.notify(p); })
1021
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1022
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1023
        } else if (options.walletVersion === Wallet.WALLET_VERSION_V2) {
1024
            self._createNewWalletV2(options)
1025
                .progress(function(p) { deferred.notify(p); })
1026
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1027
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1028
        } else if (options.walletVersion === Wallet.WALLET_VERSION_V3) {
1029
            self._createNewWalletV3(options)
1030
                .progress(function(p) { deferred.notify(p); })
1031
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1032
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1033
        } else {
1034
            deferred.reject(new blocktrail.WalletCreateError("Invalid wallet version!"));
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1035
        }
1036
    });
1037
1038
    return deferred.promise;
1039
};
1040
1041
APIClient.prototype._createNewWalletV1 = function(options) {
1042
    var self = this;
1043
1044
    var deferred = q.defer();
1045
1046
    q.nextTick(function() {
1047
1048
        if (!options.primaryMnemonic && !options.primarySeed) {
1049
            if (!options.passphrase && !options.password) {
1050
                deferred.reject(new blocktrail.WalletCreateError("Can't generate Primary Mnemonic without a passphrase"));
1051
                return deferred.promise;
1052
            } else {
1053
                options.primaryMnemonic = bip39.generateMnemonic(Wallet.WALLET_ENTROPY_BITS);
1054
                if (options.storePrimaryMnemonic !== false) {
1055
                    options.storePrimaryMnemonic = true;
1056
                }
1057
            }
1058
        }
1059
1060
        if (!options.backupMnemonic && !options.backupPublicKey) {
1061
            options.backupMnemonic = bip39.generateMnemonic(Wallet.WALLET_ENTROPY_BITS);
1062
        }
1063
1064
        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_PRIMARY);
1065
1066
        self.resolvePrimaryPrivateKeyFromOptions(options)
1067
            .then(function(options) {
1068
                deferred.notify(APIClient.CREATE_WALLET_PROGRESS_BACKUP);
1069
1070
                return self.resolveBackupPublicKeyFromOptions(options)
1071
                    .then(function(options) {
1072
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_SUBMIT);
1073
1074
                        // create a checksum of our private key which we'll later use to verify we used the right password
1075
                        var checksum = options.primaryPrivateKey.getAddress();
1076
                        var keyIndex = options.keyIndex;
1077
1078
                        var primaryPublicKey = options.primaryPrivateKey.deriveHardened(keyIndex).neutered();
1079
1080
                        // send the public keys to the server to store them
1081
                        //  and the mnemonic, which is safe because it's useless without the password
1082
                        return self.storeNewWalletV1(
1083
                            options.identifier,
1084
                            [primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1085
                            [options.backupPublicKey.toBase58(), "M"],
1086
                            options.storePrimaryMnemonic ? options.primaryMnemonic : false,
1087
                            checksum,
1088
                            keyIndex,
1089
                            options.segwit || null
1090
                        )
1091
                            .then(function(result) {
1092
                                deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1093
1094
                                var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1095
                                    return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1096
                                });
1097
1098
                                var wallet = new Wallet(
1099
                                    self,
1100
                                    options.identifier,
1101
                                    Wallet.WALLET_VERSION_V1,
1102
                                    options.primaryMnemonic,
1103
                                    null,
1104
                                    null,
1105
                                    {keyIndex: primaryPublicKey},
1106
                                    options.backupPublicKey,
1107
                                    blocktrailPublicKeys,
1108
                                    keyIndex,
1109
                                    result.segwit || 0,
1110
                                    self.testnet,
1111
                                    checksum,
1112
                                    result.upgrade_key_index,
1113
                                    options.bypassNewAddressCheck
1114
                                );
1115
1116
                                return wallet.unlock({
1117
                                    walletVersion: Wallet.WALLET_VERSION_V1,
1118
                                    passphrase: options.passphrase,
1119
                                    primarySeed: options.primarySeed,
1120
                                    primaryMnemonic: null // explicit null
1121
                                }).then(function() {
1122
                                    deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1123
                                    return [
1124
                                        wallet,
1125
                                        {
1126
                                            walletVersion: wallet.walletVersion,
1127
                                            primaryMnemonic: options.primaryMnemonic,
1128
                                            backupMnemonic: options.backupMnemonic,
1129
                                            blocktrailPublicKeys: blocktrailPublicKeys
1130
                                        }
1131
                                    ];
1132
                                });
1133
                            });
1134
                    }
1135
                );
1136
            })
1137
            .then(
1138
            function(r) {
1139
                deferred.resolve(r);
1140
            },
1141
            function(e) {
1142
                deferred.reject(e);
1143
            }
1144
        )
1145
        ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1146
    });
1147
1148
    return deferred.promise;
1149
};
1150
1151 View Code Duplication
APIClient.prototype._createNewWalletV2 = function(options) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1152
    var self = this;
1153
1154
    var deferred = q.defer();
1155
1156
    // avoid modifying passed options
1157
    options = _.merge({}, options);
1158
1159
    determineDataStorageV2_3(options)
1160
        .then(function(options) {
1161
            options.passphrase = options.passphrase || options.password;
1162
            delete options.password;
1163
1164
            // avoid deprecated options
1165
            if (options.primaryPrivateKey) {
1166
                throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
1167
            }
1168
1169
            // seed should be provided or generated
1170
            options.primarySeed = options.primarySeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
1171
1172
            return options;
1173
        })
1174
        .then(function(options) {
1175
            return produceEncryptedDataV2(options, deferred.notify.bind(deferred));
1176
        })
1177
        .then(function(options) {
1178
            return doRemainingWalletDataV2_3(options, self.network, deferred.notify.bind(deferred));
1179
        })
1180
        .then(function(options) {
1181
            // create a checksum of our private key which we'll later use to verify we used the right password
1182
            var checksum = options.primaryPrivateKey.getAddress();
1183
            var keyIndex = options.keyIndex;
1184
1185
            // send the public keys and encrypted data to server
1186
            return self.storeNewWalletV2(
1187
                options.identifier,
1188
                [options.primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1189
                [options.backupPublicKey.toBase58(), "M"],
1190
                options.storeDataOnServer ? options.encryptedPrimarySeed : false,
1191
                options.storeDataOnServer ? options.encryptedSecret : false,
1192
                options.storeDataOnServer ? options.recoverySecret : false,
1193
                checksum,
1194
                keyIndex,
1195
                options.support_secret || null,
1196
                options.segwit || null
1197
            )
1198
                .then(
1199
                function(result) {
1200
                    deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1201
1202
                    var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1203
                        return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1204
                    });
1205
1206
                    var wallet = new Wallet(
1207
                        self,
1208
                        options.identifier,
1209
                        Wallet.WALLET_VERSION_V2,
1210
                        null,
1211
                        options.storeDataOnServer ? options.encryptedPrimarySeed : null,
1212
                        options.storeDataOnServer ? options.encryptedSecret : null,
1213
                        {keyIndex: options.primaryPublicKey},
1214
                        options.backupPublicKey,
1215
                        blocktrailPublicKeys,
1216
                        keyIndex,
1217
                        result.segwit || 0,
1218
                        self.testnet,
1219
                        checksum,
1220
                        result.upgrade_key_index,
1221
                        options.bypassNewAddressCheck
1222
                    );
1223
1224
                    // pass along decrypted data to avoid extra work
1225
                    return wallet.unlock({
1226
                        walletVersion: Wallet.WALLET_VERSION_V2,
1227
                        passphrase: options.passphrase,
1228
                        primarySeed: options.primarySeed,
1229
                        secret: options.secret
1230
                    }).then(function() {
1231
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1232
                        return [
1233
                            wallet,
1234
                            {
1235
                                walletVersion: wallet.walletVersion,
1236
                                encryptedPrimarySeed: options.encryptedPrimarySeed ?
1237
                                    bip39.entropyToMnemonic(blocktrail.convert(options.encryptedPrimarySeed, 'base64', 'hex')) :
1238
                                    null,
1239
                                backupSeed: options.backupSeed ? bip39.entropyToMnemonic(options.backupSeed.toString('hex')) : null,
1240
                                recoveryEncryptedSecret: options.recoveryEncryptedSecret ?
1241
                                    bip39.entropyToMnemonic(blocktrail.convert(options.recoveryEncryptedSecret, 'base64', 'hex')) :
1242
                                    null,
1243
                                encryptedSecret: options.encryptedSecret ?
1244
                                    bip39.entropyToMnemonic(blocktrail.convert(options.encryptedSecret, 'base64', 'hex')) :
1245
                                    null,
1246
                                blocktrailPublicKeys: blocktrailPublicKeys
1247
                            }
1248
                        ];
1249
                    });
1250
                }
1251
            );
1252
        })
1253
       .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); });
1254
1255
    return deferred.promise;
1256
};
1257
1258 View Code Duplication
APIClient.prototype._createNewWalletV3 = function(options) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1259
    var self = this;
1260
1261
    var deferred = q.defer();
1262
1263
    // avoid modifying passed options
1264
    options = _.merge({}, options);
1265
1266
    determineDataStorageV2_3(options)
1267
        .then(function(options) {
1268
            options.passphrase = options.passphrase || options.password;
1269
            delete options.password;
1270
1271
            // avoid deprecated options
1272
            if (options.primaryPrivateKey) {
1273
                throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
1274
            }
1275
1276
            // seed should be provided or generated
1277
            options.primarySeed = options.primarySeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
1278
1279
            return options;
1280
        })
1281
        .then(function(options) {
1282
            return self.produceEncryptedDataV3(options, deferred.notify.bind(deferred));
1283
        })
1284
        .then(function(options) {
1285
            return doRemainingWalletDataV2_3(options, self.network, deferred.notify.bind(deferred));
1286
        })
1287
        .then(function(options) {
1288
            // create a checksum of our private key which we'll later use to verify we used the right password
1289
            var checksum = options.primaryPrivateKey.getAddress();
1290
            var keyIndex = options.keyIndex;
1291
1292
            // send the public keys and encrypted data to server
1293
            return self.storeNewWalletV3(
1294
                options.identifier,
1295
                [options.primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1296
                [options.backupPublicKey.toBase58(), "M"],
1297
                options.storeDataOnServer ? options.encryptedPrimarySeed : false,
1298
                options.storeDataOnServer ? options.encryptedSecret : false,
1299
                options.storeDataOnServer ? options.recoverySecret : false,
1300
                checksum,
1301
                keyIndex,
1302
                options.support_secret || null,
1303
                options.segwit || null
1304
            )
1305
                .then(
1306
                    // result, deferred, self(apiclient)
1307
                    function(result) {
1308
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1309
1310
                        var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1311
                            return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1312
                        });
1313
1314
                        var wallet = new Wallet(
1315
                            self,
1316
                            options.identifier,
1317
                            Wallet.WALLET_VERSION_V3,
1318
                            null,
1319
                            options.storeDataOnServer ? options.encryptedPrimarySeed : null,
1320
                            options.storeDataOnServer ? options.encryptedSecret : null,
1321
                            {keyIndex: options.primaryPublicKey},
1322
                            options.backupPublicKey,
1323
                            blocktrailPublicKeys,
1324
                            keyIndex,
1325
                            result.segwit || 0,
1326
                            self.testnet,
1327
                            checksum,
1328
                            result.upgrade_key_index,
1329
                            options.bypassNewAddressCheck
1330
                        );
1331
1332
                        // pass along decrypted data to avoid extra work
1333
                        return wallet.unlock({
1334
                            walletVersion: Wallet.WALLET_VERSION_V3,
1335
                            passphrase: options.passphrase,
1336
                            primarySeed: options.primarySeed,
1337
                            secret: options.secret
1338
                        }).then(function() {
1339
                            deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1340
                            return [
1341
                                wallet,
1342
                                {
1343
                                    walletVersion: wallet.walletVersion,
1344
                                    encryptedPrimarySeed: options.encryptedPrimarySeed ? EncryptionMnemonic.encode(options.encryptedPrimarySeed) : null,
1345
                                    backupSeed: options.backupSeed ? bip39.entropyToMnemonic(options.backupSeed) : null,
1346
                                    recoveryEncryptedSecret: options.recoveryEncryptedSecret ?
1347
                                        EncryptionMnemonic.encode(options.recoveryEncryptedSecret) : null,
1348
                                    encryptedSecret: options.encryptedSecret ? EncryptionMnemonic.encode(options.encryptedSecret) : null,
1349
                                    blocktrailPublicKeys: blocktrailPublicKeys
1350
                                }
1351
                            ];
1352
                        });
1353
                    }
1354
                );
1355
        })
1356
        .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); });
1357
1358
    return deferred.promise;
1359
};
1360
1361
function verifyPublicBip32Key(bip32Key, network) {
1362
    var hk = bitcoin.HDNode.fromBase58(bip32Key[0], network);
1363
    if (typeof hk.keyPair.d !== "undefined") {
1364
        throw new Error('BIP32Key contained private key material - abort');
1365
    }
1366
1367
    if (bip32Key[1].slice(0, 1) !== "M") {
1368
        throw new Error("BIP32Key contained non-public path - abort");
1369
    }
1370
}
1371
1372
function verifyPublicOnly(walletData, network) {
1373
    verifyPublicBip32Key(walletData.primary_public_key, network);
1374
    verifyPublicBip32Key(walletData.backup_public_key, network);
1375
}
1376
1377
/**
1378
 * create wallet using the API
1379
 *
1380
 * @param identifier            string      the wallet identifier to create
1381
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1382
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1383
 * @param primaryMnemonic       string      mnemonic to store
1384
 * @param checksum              string      checksum to store
1385
 * @param keyIndex              int         keyIndex that was used to create wallet
1386
 * @param segwit                bool
1387
 * @returns {q.Promise}
1388
 */
1389
APIClient.prototype.storeNewWalletV1 = function(identifier, primaryPublicKey, backupPublicKey, primaryMnemonic,
1390
                                                checksum, keyIndex, segwit) {
1391
    var self = this;
1392
1393
    var postData = {
1394
        identifier: identifier,
1395
        wallet_version: Wallet.WALLET_VERSION_V1,
1396
        primary_public_key: primaryPublicKey,
1397
        backup_public_key: backupPublicKey,
1398
        primary_mnemonic: primaryMnemonic,
1399
        checksum: checksum,
1400
        key_index: keyIndex,
1401
        segwit: segwit
1402
    };
1403
1404
    verifyPublicOnly(postData, self.network);
1405
1406
    return self.client.post("/wallet", null, postData);
1407
};
1408
1409
/**
1410
 * create wallet using the API
1411
 *
1412
 * @param identifier            string      the wallet identifier to create
1413
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1414
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1415
 * @param encryptedPrimarySeed  string      openssl format
1416
 * @param encryptedSecret       string      openssl format
1417
 * @param recoverySecret        string      openssl format
1418
 * @param checksum              string      checksum to store
1419
 * @param keyIndex              int         keyIndex that was used to create wallet
1420
 * @param supportSecret         string
1421
 * @param segwit                bool
1422
 * @returns {q.Promise}
1423
 */
1424
APIClient.prototype.storeNewWalletV2 = function(identifier, primaryPublicKey, backupPublicKey, encryptedPrimarySeed, encryptedSecret,
1425
                                                recoverySecret, checksum, keyIndex, supportSecret, segwit) {
1426
    var self = this;
1427
1428
    var postData = {
1429
        identifier: identifier,
1430
        wallet_version: Wallet.WALLET_VERSION_V2,
1431
        primary_public_key: primaryPublicKey,
1432
        backup_public_key: backupPublicKey,
1433
        encrypted_primary_seed: encryptedPrimarySeed,
1434
        encrypted_secret: encryptedSecret,
1435
        recovery_secret: recoverySecret,
1436
        checksum: checksum,
1437
        key_index: keyIndex,
1438
        support_secret: supportSecret || null,
1439
        segwit: segwit
1440
    };
1441
1442
    verifyPublicOnly(postData, self.network);
1443
1444
    return self.client.post("/wallet", null, postData);
1445
};
1446
1447
/**
1448
 * create wallet using the API
1449
 *
1450
 * @param identifier            string      the wallet identifier to create
1451
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1452
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1453
 * @param encryptedPrimarySeed  Buffer      buffer of ciphertext
1454
 * @param encryptedSecret       Buffer      buffer of ciphertext
1455
 * @param recoverySecret        Buffer      buffer of recovery secret
1456
 * @param checksum              string      checksum to store
1457
 * @param keyIndex              int         keyIndex that was used to create wallet
1458
 * @param supportSecret         string
1459
 * @param segwit                bool
1460
 * @returns {q.Promise}
1461
 */
1462
APIClient.prototype.storeNewWalletV3 = function(identifier, primaryPublicKey, backupPublicKey, encryptedPrimarySeed, encryptedSecret,
1463
                                                recoverySecret, checksum, keyIndex, supportSecret, segwit) {
1464
    var self = this;
1465
1466
    var postData = {
1467
        identifier: identifier,
1468
        wallet_version: Wallet.WALLET_VERSION_V3,
1469
        primary_public_key: primaryPublicKey,
1470
        backup_public_key: backupPublicKey,
1471
        encrypted_primary_seed: encryptedPrimarySeed.toString('base64'),
1472
        encrypted_secret: encryptedSecret.toString('base64'),
1473
        recovery_secret: recoverySecret.toString('hex'),
1474
        checksum: checksum,
1475
        key_index: keyIndex,
1476
        support_secret: supportSecret || null,
1477
        segwit: segwit
1478
    };
1479
1480
    verifyPublicOnly(postData, self.network);
1481
1482
    return self.client.post("/wallet", null, postData);
1483
};
1484
1485
/**
1486
 * create wallet using the API
1487
 *
1488
 * @param identifier            string      the wallet identifier to create
1489
 * @param postData              object
1490
 * @param [cb]                  function    callback(err, result)
1491
 * @returns {q.Promise}
1492
 */
1493
APIClient.prototype.updateWallet = function(identifier, postData, cb) {
1494
    var self = this;
1495
1496
    return self.client.post("/wallet/" + identifier, null, postData, cb);
1497
};
1498
1499
/**
1500
 * upgrade wallet to use a new account number
1501
 *  the account number specifies which blocktrail cosigning key is used
1502
 *
1503
 * @param identifier            string      the wallet identifier
1504
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1505
 * @param keyIndex              int         keyIndex that was used to create wallet
1506
 * @param [cb]                  function    callback(err, result)
1507
 * @returns {q.Promise}
1508
 */
1509
APIClient.prototype.upgradeKeyIndex = function(identifier, keyIndex, primaryPublicKey, cb) {
1510
    var self = this;
1511
1512
    return self.client.post("/wallet/" + identifier + "/upgrade", null, {
1513
        key_index: keyIndex,
1514
        primary_public_key: primaryPublicKey
1515
    }, cb);
1516
};
1517
1518
/**
1519
 * get the balance for the wallet
1520
 *
1521
 * @param identifier            string      the wallet identifier
1522
 * @param [cb]                  function    callback(err, result)
1523
 * @returns {q.Promise}
1524
 */
1525
APIClient.prototype.getWalletBalance = function(identifier, cb) {
1526
    var self = this;
1527
1528
    return self.client.get("/wallet/" + identifier + "/balance", null, true, cb);
1529
};
1530
1531
/**
1532
 * do HD wallet discovery for the wallet
1533
 *
1534
 * @param identifier            string      the wallet identifier
1535
 * @param [cb]                  function    callback(err, result)
1536
 * @returns {q.Promise}
1537
 */
1538
APIClient.prototype.doWalletDiscovery = function(identifier, gap, cb) {
1539
    var self = this;
1540
1541
    return self.client.get("/wallet/" + identifier + "/discovery", {gap: gap}, true, cb);
1542
};
1543
1544
1545
/**
1546
 * get a new derivation number for specified parent path
1547
 *  eg; m/44'/1'/0/0 results in m/44'/1'/0/0/0 and next time in m/44'/1'/0/0/1 and next time in m/44'/1'/0/0/2
1548
 *
1549
 * @param identifier            string      the wallet identifier
1550
 * @param path                  string      the parent path for which to get a new derivation,
1551
 *                                           can be suffixed with /* to make it clear on which level the derivations hould be
1552
 * @param [cb]                  function    callback(err, result)
1553
 * @returns {q.Promise}
1554
 */
1555
APIClient.prototype.getNewDerivation = function(identifier, path, cb) {
1556
    var self = this;
1557
1558
    return self.client.post("/wallet/" + identifier + "/path", null, {path: path}, cb);
1559
};
1560
1561
1562
/**
1563
 * delete the wallet
1564
 *  the checksum address and a signature to verify you ownership of the key of that checksum address
1565
 *  is required to be able to delete a wallet
1566
 *
1567
 * @param identifier            string      the wallet identifier
1568
 * @param checksumAddress       string      the address for your master private key (and the checksum used when creating the wallet)
1569
 * @param checksumSignature     string      a signature of the checksum address as message signed by the private key matching that address
1570
 * @param [force]               bool        ignore warnings (such as a non-zero balance)
1571
 * @param [cb]                  function    callback(err, result)
1572
 * @returns {q.Promise}
1573
 */
1574
APIClient.prototype.deleteWallet = function(identifier, checksumAddress, checksumSignature, force, cb) {
1575
    var self = this;
1576
1577
    if (typeof force === "function") {
1578
        cb = force;
1579
        force = false;
1580
    }
1581
1582
    return self.client.delete("/wallet/" + identifier, {force: force}, {
1583
        checksum: checksumAddress,
1584
        signature: checksumSignature
1585
    }, cb);
1586
};
1587
1588
/**
1589
 * use the API to get the best inputs to use based on the outputs
1590
 *
1591
 * the return array has the following format:
1592
 * [
1593
 *  "utxos" => [
1594
 *      [
1595
 *          "hash" => "<txHash>",
1596
 *          "idx" => "<index of the output of that <txHash>",
1597
 *          "scriptpubkey_hex" => "<scriptPubKey-hex>",
1598
 *          "value" => 32746327,
1599
 *          "address" => "1address",
1600
 *          "path" => "m/44'/1'/0'/0/13",
1601
 *          "redeem_script" => "<redeemScript-hex>",
1602
 *      ],
1603
 *  ],
1604
 *  "fee"   => 10000,
1605
 *  "change"=> 1010109201,
1606
 * ]
1607
 *
1608
 * @param identifier        string      the wallet identifier
1609
 * @param pay               array       {'address': (int)value}     coins to send
1610
 * @param lockUTXO          bool        lock UTXOs for a few seconds to allow for transaction to be created
1611
 * @param allowZeroConf     bool        allow zero confirmation unspent outputs to be used in coin selection
1612
 * @param feeStrategy       string      defaults to
1613
 * @param options
1614
 * @param [cb]              function    callback(err, utxos, fee, change)
1615
 * @returns {q.Promise}
1616
 */
1617
APIClient.prototype.coinSelection = function(identifier, pay, lockUTXO, allowZeroConf, feeStrategy, options, cb) {
1618
    var self = this;
1619
1620
    if (typeof feeStrategy === "function") {
1621
        cb = feeStrategy;
1622
        feeStrategy = null;
1623
        options = {};
1624
    } else if (typeof options === "function") {
1625
        cb = options;
1626
        options = {};
1627
    }
1628
1629
    feeStrategy = feeStrategy || Wallet.FEE_STRATEGY_OPTIMAL;
1630
    options = options || {};
1631
1632
    var deferred = q.defer();
1633
    deferred.promise.spreadNodeify(cb);
1634
1635
    var params = {
1636
        lock: lockUTXO,
1637
        zeroconf: allowZeroConf ? 1 : 0,
1638
        zeroconfself: (typeof options.allowZeroConfSelf !== "undefined" ? options.allowZeroConfSelf : true) ? 1 : 0,
1639
        fee_strategy: feeStrategy
1640
    };
1641
1642
    if (options.forcefee) {
1643
        params['forcefee'] = options.forcefee;
1644
    }
1645
1646
    deferred.resolve(
1647
        self.client.post("/wallet/" + identifier + "/coin-selection", params, pay).then(
1648
            function(result) {
1649
                return [result.utxos, result.fee, result.change, result];
1650
            },
1651
            function(err) {
1652
                if (err.message.match(/too low to pay the fee/)) {
1653
                    throw blocktrail.WalletFeeError(err);
1654
                }
1655
1656
                throw err;
1657
            }
1658
        )
1659
    );
1660
1661
    return deferred.promise;
1662
};
1663
1664
/**
1665
 * @param [cb]              function    callback(err, utxos, fee, change)
1666
 * @returns {q.Promise}
1667
 */
1668
APIClient.prototype.feePerKB = function(cb) {
1669
    var self = this;
1670
1671
    var deferred = q.defer();
1672
    deferred.promise.spreadNodeify(cb);
1673
1674
    deferred.resolve(self.client.get("/fee-per-kb"));
1675
1676
    return deferred.promise;
1677
};
1678
1679
/**
1680
 * send the transaction using the API
1681
 *
1682
 * @param identifier        string      the wallet identifier
1683
 * @param txHex             string      partially signed transaction as hex string
1684
 * @param paths             array       list of paths used in inputs which should be cosigned by the API
1685
 * @param checkFee          bool        when TRUE the API will verify if the fee is 100% correct and otherwise throw an exception
1686
 * @param [twoFactorToken]  string      2FA token
1687
 * @param [prioboost]       bool
1688
 * @param [cb]              function    callback(err, txHash)
1689
 * @returns {q.Promise}
1690
 */
1691
APIClient.prototype.sendTransaction = function(identifier, txHex, paths, checkFee, twoFactorToken, prioboost, cb) {
1692
    var self = this;
1693
1694
    if (typeof twoFactorToken === "function") {
1695
        cb = twoFactorToken;
1696
        twoFactorToken = null;
1697
        prioboost = false;
1698
    } else if (typeof prioboost === "function") {
1699
        cb = prioboost;
1700
        prioboost = false;
1701
    }
1702
1703
    var data = {
1704
        paths: paths,
1705
        two_factor_token: twoFactorToken
1706
    };
1707
    if (typeof txHex === "string") {
1708
        data.raw_transaction = txHex;
1709
    } else if (typeof txHex === "object") {
1710
        Object.keys(txHex).map(function(key) {
1711
            data[key] = txHex[key];
1712
        });
1713
    }
1714
1715
    return self.client.post(
1716
        "/wallet/" + identifier + "/send",
1717
        {
1718
            check_fee: checkFee ? 1 : 0,
1719
            prioboost: prioboost ? 1 : 0
1720
        },
1721
        data,
1722
        cb
1723
    );
1724
};
1725
1726
/**
1727
 * setup a webhook for this wallet
1728
 *
1729
 * @param identifier        string      the wallet identifier
1730
 * @param webhookIdentifier string      identifier for the webhook
1731
 * @param url               string      URL to receive webhook events
1732
 * @param [cb]              function    callback(err, webhook)
1733
 * @returns {q.Promise}
1734
 */
1735
APIClient.prototype.setupWalletWebhook = function(identifier, webhookIdentifier, url, cb) {
1736
    var self = this;
1737
1738
    return self.client.post("/wallet/" + identifier + "/webhook", null, {url: url, identifier: webhookIdentifier}, cb);
1739
};
1740
1741
/**
1742
 * delete a webhook that was created for this wallet
1743
 *
1744
 * @param identifier        string      the wallet identifier
1745
 * @param webhookIdentifier string      identifier for the webhook
1746
 * @param [cb]              function    callback(err, success)
1747
 * @returns {q.Promise}
1748
 */
1749
APIClient.prototype.deleteWalletWebhook = function(identifier, webhookIdentifier, cb) {
1750
    var self = this;
1751
1752
    return self.client.delete("/wallet/" + identifier + "/webhook/" + webhookIdentifier, null, null, cb);
1753
};
1754
1755
/**
1756
 * get all transactions for an wallet (paginated)
1757
 *
1758
 * @param identifier    string      wallet identifier
1759
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1760
 * @param [cb]          function    callback function to call when request is complete
1761
 * @return q.Promise
1762
 */
1763
APIClient.prototype.walletTransactions = function(identifier, params, cb) {
1764
    var self = this;
1765
1766
    if (typeof params === "function") {
1767
        cb = params;
1768
        params = null;
1769
    }
1770
1771
    return self.client.get("/wallet/" + identifier + "/transactions", params, true, cb);
1772
};
1773
1774
/**
1775
 * get all addresses for an wallet (paginated)
1776
 *
1777
 * @param identifier    string      wallet identifier
1778
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1779
 * @param [cb]          function    callback function to call when request is complete
1780
 * @return q.Promise
1781
 */
1782
APIClient.prototype.walletAddresses = function(identifier, params, cb) {
1783
    var self = this;
1784
1785
    if (typeof params === "function") {
1786
        cb = params;
1787
        params = null;
1788
    }
1789
1790
    return self.client.get("/wallet/" + identifier + "/addresses", params, true, cb);
1791
};
1792
1793
/**
1794
 * @param identifier    string      wallet identifier
1795
 * @param address       string      the address to label
1796
 * @param label         string      the label
1797
 * @param [cb]          function    callback(err, res)
1798
 * @return q.Promise
1799
 */
1800
APIClient.prototype.labelWalletAddress = function(identifier, address, label, cb) {
1801
    var self = this;
1802
1803
    return self.client.post("/wallet/" + identifier + "/address/" + address + "/label", null, {label: label}, cb);
1804
};
1805
1806
APIClient.prototype.walletMaxSpendable = function(identifier, allowZeroConf, feeStrategy, options, cb) {
1807
    var self = this;
1808
1809
    if (typeof feeStrategy === "function") {
1810
        cb = feeStrategy;
1811
        feeStrategy = null;
1812
    } else if (typeof options === "function") {
1813
        cb = options;
1814
        options = {};
1815
    }
1816
1817
    feeStrategy = feeStrategy || Wallet.FEE_STRATEGY_OPTIMAL;
1818
    options = options || {};
1819
1820
    var params = {
1821
        outputs: options.outputs ? options.outputs : 1,
1822
        zeroconf: allowZeroConf ? 1 : 0,
1823
        zeroconfself: (typeof options.allowZeroConfSelf !== "undefined" ? options.allowZeroConfSelf : true) ? 1 : 0,
1824
        fee_strategy: feeStrategy
1825
    };
1826
1827
    if (options.forcefee) {
1828
        params['forcefee'] = options.forcefee;
1829
    }
1830
1831
    return self.client.get("/wallet/" + identifier + "/max-spendable", params, true, cb);
1832
};
1833
1834
/**
1835
 * get all UTXOs for an wallet (paginated)
1836
 *
1837
 * @param identifier    string      wallet identifier
1838
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1839
 * @param [cb]          function    callback function to call when request is complete
1840
 * @return q.Promise
1841
 */
1842
APIClient.prototype.walletUTXOs = function(identifier, params, cb) {
1843
    var self = this;
1844
1845
    if (typeof params === "function") {
1846
        cb = params;
1847
        params = null;
1848
    }
1849
1850
    return self.client.get("/wallet/" + identifier + "/utxos", params, true, cb);
1851
};
1852
1853
/**
1854
 * get a paginated list of all wallets associated with the api user
1855
 *
1856
 * @param [params]      object      pagination: {page: 1, limit: 20}
1857
 * @param [cb]          function    callback function to call when request is complete
1858
 * @return q.Promise
1859
 */
1860
APIClient.prototype.allWallets = function(params, cb) {
1861
    var self = this;
1862
1863
    if (typeof params === "function") {
1864
        cb = params;
1865
        params = null;
1866
    }
1867
1868
    return self.client.get("/wallets", params, true, cb);
1869
};
1870
1871
/**
1872
 * verify a message signed bitcoin-core style
1873
 *
1874
 * @param message        string
1875
 * @param address        string
1876
 * @param signature      string
1877
 * @param [cb]          function    callback function to call when request is complete
1878
 * @return q.Promise
1879
 */
1880
APIClient.prototype.verifyMessage = function(message, address, signature, cb) {
1881
    var self = this;
1882
1883
    // we could also use the API instead of the using bitcoinjs-lib to verify
1884
    // return self.client.post("/verify_message", null, {message: message, address: address, signature: signature}, cb);
1885
1886
    var deferred = q.defer();
1887
    deferred.promise.nodeify(cb);
1888
    try {
1889
        var result = bitcoinMessage.verify(address, self.network.messagePrefix, message, new Buffer(signature, 'base64'));
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1890
        deferred.resolve(result);
1891
    } catch (e) {
1892
        deferred.reject(e);
1893
    }
1894
1895
    return deferred.promise;
1896
};
1897
1898
/**
1899
 * max is 0.001
1900
 * testnet only
1901
 *
1902
 * @param address
1903
 * @param amount
1904
 * @param cb
1905
 */
1906
APIClient.prototype.faucetWithdrawl = function(address, amount, cb) {
1907
    var self = this;
1908
1909
    return self.client.post("/faucet/withdrawl", null, {address: address, amount: amount}, cb);
1910
};
1911
1912
/**
1913
 * send a raw transaction
1914
 *
1915
 * @param rawTransaction    string      raw transaction as HEX
1916
 * @param [cb]              function    callback function to call when request is complete
1917
 * @return q.Promise
1918
 */
1919
APIClient.prototype.sendRawTransaction = function(rawTransaction, cb) {
1920
    var self = this;
1921
1922
    return self.client.post("/send-raw-tx", null, rawTransaction, cb);
1923
};
1924
1925
/**
1926
 * get the current price index
1927
 *
1928
 * @param [cb]          function    callback({'USD': 287.30})
1929
 * @return q.Promise
1930
 */
1931
APIClient.prototype.price = function(cb) {
1932
    var self = this;
1933
1934
    return self.client.get("/price", null, false, cb);
1935
};
1936
1937
module.exports = APIClient;
1938