Completed
Pull Request — master (#83)
by Ruben de
03:18 queued 01:24
created

APIClient._createNewWalletV1   D

Complexity

Conditions 1
Paths 64

Size

Total Lines 109

Duplication

Lines 1
Ratio 0.92 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 64
dl 1
loc 109
rs 4.2439
nop 1

1 Function

Rating   Name   Duplication   Size   Complexity  
C 0 101 8

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
/* globals onLoadWorkerLoadAsmCrypto */
2
3
var _ = require('lodash'),
4
    q = require('q'),
5
    bitcoin = require('bitcoinjs-lib'),
6
    bitcoinMessage = require('bitcoinjs-message'),
7
8
    bip39 = require("bip39"),
9
    Wallet = require('./wallet'),
10
    RestClient = require('./rest_client'),
11
    Encryption = require('./encryption'),
12
    KeyDerivation = require('./keyderivation'),
13
    EncryptionMnemonic = require('./encryption_mnemonic'),
14
    blocktrail = require('./blocktrail'),
15
    randomBytes = require('randombytes'),
16
    CryptoJS = require('crypto-js'),
17
    webworkifier = require('./webworkifier');
18
19
var useWebWorker = require('./use-webworker')();
20
21
/**
22
 * Bindings to conssume the BlockTrail API
23
 *
24
 * @param options       object{
25
 *                          apiKey: 'API_KEY',
26
 *                          apiSecret: 'API_SECRET',
27
 *                          host: 'defaults to api.blocktrail.com',
28
 *                          network: 'BTC|LTC',
29
 *                          testnet: true|false
30
 *                      }
31
 * @constructor
32
 */
33
var APIClient = function(options) {
34
    var self = this;
35
36
    // handle constructor call without 'new'
37
    if (!(this instanceof APIClient)) {
38
        return new APIClient(options);
39
    }
40
    self.bitcoinCash = options.network && options.network === "BCC";
41
42
    self.testnet = options.testnet = options.testnet || false;
43
    if (self.bitcoinCash) {
44
        if (self.testnet) {
45
            self.network = bitcoin.networks.bitcoincashtestnet;
46
        } else {
47
            self.network = bitcoin.networks.bitcoincash;
48
        }
49
    } else {
50
        if (self.testnet) {
51
            self.network = bitcoin.networks.testnet;
52
        } else {
53
            self.network = bitcoin.networks.bitcoin;
54
        }
55
    }
56
57
    self.feeSanityCheck = typeof options.feeSanityCheck !== "undefined" ? options.feeSanityCheck : true;
58
    self.feeSanityCheckBaseFeeMultiplier = options.feeSanityCheckBaseFeeMultiplier || 200;
59
60
    options.apiNetwork = options.apiNetwork || ((self.testnet ? "t" : "") + (options.network || 'BTC').toUpperCase());
61
62
    /**
63
     * @type RestClient
64
     */
65
    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...
66
};
67
68
APIClient.initRestClient = function(options) {
69
    // BLOCKTRAIL_SDK_API_ENDPOINT overwrite for development
70
    if (process.env.BLOCKTRAIL_SDK_API_ENDPOINT) {
71
        options.host = process.env.BLOCKTRAIL_SDK_API_ENDPOINT;
72
    }
73
74
    // trim off leading https?://
75
    if (options.host && options.host.indexOf("https://") === 0) {
76
        options.https = true;
77
        options.host = options.host.substr(8);
78
    } else if (options.host && options.host.indexOf("http://") === 0) {
79
        options.https = false;
80
        options.host = options.host.substr(7);
81
    }
82
83
    if (typeof options.https === "undefined") {
84
        options.https = true;
85
    }
86
87
    if (!options.host) {
88
        options.host = 'api.blocktrail.com';
89
    }
90
91
    if (!options.port) {
92
        options.port = options.https ? 443 : 80;
93
    }
94
95
    if (!options.endpoint) {
96
        options.endpoint = "/" + (options.apiVersion || "v1") + (options.apiNetwork ? ("/" + options.apiNetwork) : "");
97
    }
98
99
    return new RestClient(options);
100
};
101
102
var determineDataStorageV2_3 = function(options) {
103
    return q.when(options)
104
        .then(function(options) {
105
            // legacy
106
            if (options.storePrimaryMnemonic) {
107
                options.storeDataOnServer = options.storePrimaryMnemonic;
108
            }
109
110
            // storeDataOnServer=false when primarySeed is provided
111
            if (typeof options.storeDataOnServer === "undefined") {
112
                options.storeDataOnServer = !options.primarySeed;
113
            }
114
115
            return options;
116
        });
117
};
118
119
var produceEncryptedDataV2 = function(options, notify) {
120
    return q.when(options)
121
        .then(function(options) {
122
            if (options.storeDataOnServer) {
123
                if (!options.secret) {
124
                    if (!options.passphrase) {
125
                        throw new blocktrail.WalletCreateError("Can't encrypt data without a passphrase");
126
                    }
127
128
                    notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET);
129
130
                    options.secret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8).toString('hex'); // string because we use it as passphrase
131
                    options.encryptedSecret = CryptoJS.AES.encrypt(options.secret, options.passphrase).toString(CryptoJS.format.OpenSSL); // 'base64' string
132
                }
133
134
                notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY);
135
136
                options.encryptedPrimarySeed = CryptoJS.AES.encrypt(options.primarySeed.toString('base64'), options.secret)
137
                    .toString(CryptoJS.format.OpenSSL); // 'base64' string
138
                options.recoverySecret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8).toString('hex'); // string because we use it as passphrase
139
140
                notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY);
141
142
                options.recoveryEncryptedSecret = CryptoJS.AES.encrypt(options.secret, options.recoverySecret)
143
                                                              .toString(CryptoJS.format.OpenSSL); // 'base64' string
144
            }
145
146
            return options;
147
        });
148
};
149
150
APIClient.prototype.promisedEncrypt = function(pt, pw, iter) {
151
    if (useWebWorker && typeof onLoadWorkerLoadAsmCrypto === "function") {
152
        // generate randomness outside of webworker because many browsers don't have crypto.getRandomValues inside webworkers
153
        var saltBuf = Encryption.generateSalt();
154
        var iv = Encryption.generateIV();
155
156
        return webworkifier.workify(APIClient.prototype.promisedEncrypt, function factory() {
157
            return require('./webworker');
158
        }, onLoadWorkerLoadAsmCrypto, {
159
            method: 'Encryption.encryptWithSaltAndIV',
160
            pt: pt,
161
            pw: pw,
162
            saltBuf: saltBuf,
163
            iv: iv,
164
            iterations: iter
165
        })
166
            .then(function(data) {
167
                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...
168
            });
169
    } else {
170
        try {
171
            return q.when(Encryption.encrypt(pt, pw, iter));
172
        } catch (e) {
173
            return q.reject(e);
174
        }
175
    }
176
};
177
178
APIClient.prototype.promisedDecrypt = function(ct, pw) {
179
    if (useWebWorker && typeof onLoadWorkerLoadAsmCrypto === "function") {
180
        return webworkifier.workify(APIClient.prototype.promisedDecrypt, function() {
181
            return require('./webworker');
182
        }, onLoadWorkerLoadAsmCrypto, {
183
            method: 'Encryption.decrypt',
184
            ct: ct,
185
            pw: pw
186
        })
187
            .then(function(data) {
188
                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...
189
            });
190
    } else {
191
        try {
192
            return q.when(Encryption.decrypt(ct, pw));
193
        } catch (e) {
194
            return q.reject(e);
195
        }
196
    }
197
};
198
199
APIClient.prototype.produceEncryptedDataV3 = function(options, notify) {
200
    var self = this;
201
202
    return q.when(options)
203
        .then(function(options) {
204
            if (options.storeDataOnServer) {
205
                return q.when()
206
                    .then(function() {
207
                        if (!options.secret) {
208
                            if (!options.passphrase) {
209
                                throw new blocktrail.WalletCreateError("Can't encrypt data without a passphrase");
210
                            }
211
212
                            notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET);
213
214
                            // -> now a buffer
215
                            options.secret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
216
217
                            // -> now a buffer
218
                            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...
219
                                .then(function(encryptedSecret) {
220
                                    options.encryptedSecret = encryptedSecret;
221
                                });
222
                        } else {
223
                            if (!(options.secret instanceof 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...
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...
224
                                throw new Error('Secret must be a buffer');
225
                            }
226
                        }
227
                    })
228
                    .then(function() {
229
                        notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY);
230
231
                        return self.promisedEncrypt(options.primarySeed, options.secret, KeyDerivation.subkeyIterations)
232
                            .then(function(encryptedPrimarySeed) {
233
                                options.encryptedPrimarySeed = encryptedPrimarySeed;
234
                            });
235
                    })
236
                    .then(function() {
237
                        // skip generating recovery secret when explicitly set to false
238
                        if (options.recoverySecret === false) {
239
                            return;
240
                        }
241
242
                        notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY);
243
                        if (!options.recoverySecret) {
244
                            options.recoverySecret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
245
                        }
246
247
                        return self.promisedEncrypt(options.secret, options.recoverySecret, KeyDerivation.defaultIterations)
248
                            .then(function(recoveryEncryptedSecret) {
249
                                options.recoveryEncryptedSecret = recoveryEncryptedSecret;
250
                            });
251
                    })
252
                    .then(function() {
253
                        return options;
254
                    });
255
            } else {
256
                return options;
257
            }
258
        });
259
};
260
261
var doRemainingWalletDataV2_3 = function(options, network, notify) {
262
    return q.when(options)
263
        .then(function(options) {
264
            if (!options.backupPublicKey) {
265
                options.backupSeed = options.backupSeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
266
            }
267
268
            notify(APIClient.CREATE_WALLET_PROGRESS_PRIMARY);
269
270
            options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.primarySeed, network);
271
272
            notify(APIClient.CREATE_WALLET_PROGRESS_BACKUP);
273
274
            if (!options.backupPublicKey) {
275
                options.backupPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.backupSeed, network);
276
                options.backupPublicKey = options.backupPrivateKey.neutered();
277
            }
278
279
            options.primaryPublicKey = options.primaryPrivateKey.deriveHardened(options.keyIndex).neutered();
280
281
            notify(APIClient.CREATE_WALLET_PROGRESS_SUBMIT);
282
283
            return options;
284
        });
285
};
286
287
APIClient.prototype.mnemonicToPrivateKey = function(mnemonic, passphrase, cb) {
288
    var self = this;
289
290
    var deferred = q.defer();
291
    deferred.promise.spreadNodeify(cb);
292
293
    deferred.resolve(q.fcall(function() {
294
        return self.mnemonicToSeedHex(mnemonic, passphrase).then(function(seedHex) {
295
            return bitcoin.HDNode.fromSeedHex(seedHex, self.network);
296
        });
297
    }));
298
299
    return deferred.promise;
300
};
301
302
APIClient.prototype.mnemonicToSeedHex = function(mnemonic, passphrase) {
303
    var self = this;
304
305
    if (useWebWorker) {
306
        return webworkifier.workify(self.mnemonicToSeedHex, function() {
307
            return require('./webworker');
308
        }, {method: 'mnemonicToSeedHex', mnemonic: mnemonic, passphrase: passphrase})
309
            .then(function(data) {
310
                return data.seed;
311
            });
312
    } else {
313
        try {
314
            return q.when(bip39.mnemonicToSeedHex(mnemonic, passphrase));
315
        } catch (e) {
316
            return q.reject(e);
317
        }
318
    }
319
};
320
321
APIClient.prototype.resolvePrimaryPrivateKeyFromOptions = function(options, cb) {
322
    var self = this;
323
324
    var deferred = q.defer();
325
    deferred.promise.nodeify(cb);
326
327
    try {
328
        // avoid conflicting options
329
        if (options.passphrase && options.password) {
330
            throw new blocktrail.WalletCreateError("Can't specify passphrase and password");
331
        }
332
        // normalize passphrase/password
333
        options.passphrase = options.passphrase || options.password;
334
        delete options.password;
335
336
        // avoid conflicting options
337
        if (options.primaryMnemonic && options.primarySeed) {
338
            throw new blocktrail.WalletInitError("Can only specify one of; Primary Mnemonic or Primary Seed");
339
        }
340
341
        // avoid deprecated options
342
        if (options.primaryPrivateKey) {
343
            throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
344
        }
345
346
        // make sure we have at least one thing to use
347
        if (!options.primaryMnemonic && !options.primarySeed) {
348
            throw new blocktrail.WalletInitError("Need to specify at least one of; Primary Mnemonic or Primary Seed");
349
        }
350
351
        if (options.primarySeed) {
352
            self.primarySeed = options.primarySeed;
353
            options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(self.primarySeed, self.network);
354
            deferred.resolve(options);
355
        } else {
356
            if (!options.passphrase) {
357
                throw new blocktrail.WalletInitError("Can't init wallet with Primary Mnemonic without a passphrase");
358
            }
359
360
            self.mnemonicToSeedHex(options.primaryMnemonic, options.passphrase)
361
                .then(function(seedHex) {
362
                    try {
363
                        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...
364
                        options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.primarySeed, self.network);
365
                        deferred.resolve(options);
366
                    } catch (e) {
367
                        deferred.reject(e);
368
                    }
369
                }, function(e) {
370
                    deferred.reject(e);
371
                });
372
        }
373
    } catch (e) {
374
        deferred.reject(e);
375
    }
376
377
    return deferred.promise;
378
};
379
380
APIClient.prototype.resolveBackupPublicKeyFromOptions = function(options, cb) {
381
    var self = this;
382
383
    var deferred = q.defer();
384
    deferred.promise.nodeify(cb);
385
386
    try {
387
        // avoid conflicting options
388
        if (options.backupMnemonic && options.backupPublicKey) {
389
            throw new blocktrail.WalletInitError("Can only specify one of; Backup Mnemonic or Backup PublicKey");
390
        }
391
392
        // make sure we have at least one thing to use
393
        if (!options.backupMnemonic && !options.backupPublicKey) {
394
            throw new blocktrail.WalletInitError("Need to specify at least one of; Backup Mnemonic or Backup PublicKey");
395
        }
396
397
        if (options.backupPublicKey) {
398
            if (options.backupPublicKey instanceof bitcoin.HDNode) {
399
                deferred.resolve(options);
400
            } else {
401
                options.backupPublicKey = bitcoin.HDNode.fromBase58(options.backupPublicKey, self.network);
402
                deferred.resolve(options);
403
            }
404
        } else {
405
            self.mnemonicToPrivateKey(options.backupMnemonic, "").then(function(backupPrivateKey) {
406
                options.backupPublicKey = backupPrivateKey.neutered();
407
                deferred.resolve(options);
408
            }, function(e) {
409
                deferred.reject(e);
410
            });
411
        }
412
    } catch (e) {
413
        deferred.reject(e);
414
    }
415
416
    return deferred.promise;
417
};
418
419
APIClient.prototype.debugAuth = function(cb) {
420
    var self = this;
421
422
    return self.client.get("/debug/http-signature", null, true, cb);
423
};
424
425
/**
426
 * get a single address
427
 *
428
 * @param address      string       address hash
429
 * @param [cb]          function    callback function to call when request is complete
430
 * @return q.Promise
431
 */
432
APIClient.prototype.address = function(address, cb) {
433
    var self = this;
434
435
    return self.client.get("/address/" + address, null, cb);
436
};
437
438
APIClient.prototype.addresses = function(addresses, cb) {
439
    var self = this;
440
441
    return self.client.post("/address", null, {"addresses": addresses}, cb);
442
};
443
444
/**
445
 * get all transactions for an address (paginated)
446
 *
447
 * @param address       string      address hash
448
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
449
 * @param [cb]          function    callback function to call when request is complete
450
 * @return q.Promise
451
 */
452
APIClient.prototype.addressTransactions = function(address, params, cb) {
453
    var self = this;
454
455
    if (typeof params === "function") {
456
        cb = params;
457
        params = null;
458
    }
459
460
    return self.client.get("/address/" + address + "/transactions", params, cb);
461
};
462
463
/**
464
 * get all transactions for a batch of addresses (paginated)
465
 *
466
 * @param addresses     array       address hashes
467
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
468
 * @param [cb]          function    callback function to call when request is complete
469
 * @return q.Promise
470
 */
471
APIClient.prototype.batchAddressHasTransactions = function(addresses, params, cb) {
472
    var self = this;
473
474
    if (typeof params === "function") {
475
        cb = params;
476
        params = null;
477
    }
478
479
    return self.client.post("/address/has-transactions", params, {"addresses": addresses}, cb);
480
};
481
482
/**
483
 * get all unconfirmed transactions for an address (paginated)
484
 *
485
 * @param address       string      address hash
486
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
487
 * @param [cb]          function    callback function to call when request is complete
488
 * @return q.Promise
489
 */
490
APIClient.prototype.addressUnconfirmedTransactions = function(address, params, cb) {
491
    var self = this;
492
493
    if (typeof params === "function") {
494
        cb = params;
495
        params = null;
496
    }
497
498
    return self.client.get("/address/" + address + "/unconfirmed-transactions", params, cb);
499
};
500
501
/**
502
 * get all unspent outputs for an address (paginated)
503
 *
504
 * @param address       string      address hash
505
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
506
 * @param [cb]          function    callback function to call when request is complete
507
 * @return q.Promise
508
 */
509
APIClient.prototype.addressUnspentOutputs = function(address, params, cb) {
510
    var self = this;
511
512
    if (typeof params === "function") {
513
        cb = params;
514
        params = null;
515
    }
516
517
    return self.client.get("/address/" + address + "/unspent-outputs", params, cb);
518
};
519
520
/**
521
 * get all unspent outputs for a batch of addresses (paginated)
522
 *
523
 * @param addresses     array       address hashes
524
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
525
 * @param [cb]          function    callback function to call when request is complete
526
 * @return q.Promise
527
 */
528
APIClient.prototype.batchAddressUnspentOutputs = function(addresses, params, cb) {
529
    var self = this;
530
531
    if (typeof params === "function") {
532
        cb = params;
533
        params = null;
534
    }
535
536
    return self.client.post("/address/unspent-outputs", params, {"addresses": addresses}, cb);
537
};
538
539
/**
540
 * verify ownership of an address
541
 *
542
 * @param address       string      address hash
543
 * @param signature     string      a signed message (the address hash) using the private key of the address
544
 * @param [cb]          function    callback function to call when request is complete
545
 * @return q.Promise
546
 */
547
APIClient.prototype.verifyAddress = function(address, signature, cb) {
548
    var self = this;
549
550
    return self.client.post("/address/" + address + "/verify", null, {signature: signature}, cb);
551
};
552
553
/**
554
 * get all blocks (paginated)
555
 *
556
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
557
 * @param [cb]          function    callback function to call when request is complete
558
 * @return q.Promise
559
 */
560
APIClient.prototype.allBlocks = function(params, cb) {
561
    var self = this;
562
563
    if (typeof params === "function") {
564
        cb = params;
565
        params = null;
566
    }
567
568
    return self.client.get("/all-blocks", params, cb);
569
};
570
571
/**
572
 * get a block
573
 *
574
 * @param block         string|int  a block hash or a block height
575
 * @param [cb]          function    callback function to call when request is complete
576
 * @return q.Promise
577
 */
578
APIClient.prototype.block = function(block, cb) {
579
    var self = this;
580
581
    return self.client.get("/block/" + block, null, cb);
582
};
583
584
/**
585
 * get the latest block
586
 *
587
 * @param [cb]          function    callback function to call when request is complete
588
 * @return q.Promise
589
 */
590
APIClient.prototype.blockLatest = function(cb) {
591
    var self = this;
592
593
    return self.client.get("/block/latest", null, cb);
594
};
595
596
/**
597
 * get all transactions for a block (paginated)
598
 *
599
 * @param block         string|int  a block hash or a block height
600
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
601
 * @param [cb]          function    callback function to call when request is complete
602
 * @return q.Promise
603
 */
604
APIClient.prototype.blockTransactions = function(block, params, cb) {
605
    var self = this;
606
607
    if (typeof params === "function") {
608
        cb = params;
609
        params = null;
610
    }
611
612
    return self.client.get("/block/" + block + "/transactions", params, cb);
613
};
614
615
/**
616
 * get a single transaction
617
 *
618
 * @param tx            string      transaction hash
619
 * @param [cb]          function    callback function to call when request is complete
620
 * @return q.Promise
621
 */
622
APIClient.prototype.transaction = function(tx, cb) {
623
    var self = this;
624
625
    return self.client.get("/transaction/" + tx, null, cb);
626
};
627
628
/**
629
 * get a batch of transactions
630
 *
631
 * @param txs           string[]    list of transaction hashes (txId)
632
 * @param [cb]          function    callback function to call when request is complete
633
 * @return q.Promise
634
 */
635
APIClient.prototype.transactions = function(txs, cb) {
636
    var self = this;
637
638
    return self.client.post("/transactions", null, txs, cb, false);
639
};
640
641
/**
642
 * get a paginated list of all webhooks associated with the api user
643
 *
644
 * @param [params]      object      pagination: {page: 1, limit: 20}
645
 * @param [cb]          function    callback function to call when request is complete
646
 * @return q.Promise
647
 */
648
APIClient.prototype.allWebhooks = function(params, cb) {
649
    var self = this;
650
651
    if (typeof params === "function") {
652
        cb = params;
653
        params = null;
654
    }
655
656
    return self.client.get("/webhooks", params, cb);
657
};
658
659
/**
660
 * create a new webhook
661
 *
662
 * @param url           string      the url to receive the webhook events
663
 * @param [identifier]  string      a unique identifier associated with the webhook
664
 * @param [cb]          function    callback function to call when request is complete
665
 * @return q.Promise
666
 */
667
APIClient.prototype.setupWebhook = function(url, identifier, cb) {
668
    var self = this;
669
670
    if (typeof identifier === "function") {
671
        //mimic function overloading
672
        cb = identifier;
673
        identifier = null;
674
    }
675
676
    return self.client.post("/webhook", null, {url: url, identifier: identifier}, cb);
677
};
678
679
/**
680
 * get an existing webhook by it's identifier
681
 *
682
 * @param identifier    string      the unique identifier of the webhook to get
683
 * @param [cb]          function    callback function to call when request is complete
684
 * @return q.Promise
685
 */
686
APIClient.prototype.getWebhook = function(identifier, cb) {
687
    var self = this;
688
689
    return self.client.get("/webhook/" + identifier, null, cb);
690
};
691
692
/**
693
 * update an existing webhook
694
 *
695
 * @param identifier    string      the unique identifier of the webhook
696
 * @param webhookData   object      the data to update: {identifier: newIdentifier, url:newUrl}
697
 * @param [cb]          function    callback function to call when request is complete
698
 * @return q.Promise
699
 */
700
APIClient.prototype.updateWebhook = function(identifier, webhookData, cb) {
701
    var self = this;
702
703
    return self.client.put("/webhook/" + identifier, null, webhookData, cb);
704
};
705
706
/**
707
 * deletes an existing webhook and any event subscriptions associated with it
708
 *
709
 * @param identifier    string      the unique identifier of the webhook
710
 * @param [cb]          function    callback function to call when request is complete
711
 * @return q.Promise
712
 */
713
APIClient.prototype.deleteWebhook = function(identifier, cb) {
714
    var self = this;
715
716
    return self.client.delete("/webhook/" + identifier, null, null, cb);
717
};
718
719
/**
720
 * get a paginated list of all the events a webhook is subscribed to
721
 *
722
 * @param identifier    string      the unique identifier of the webhook
723
 * @param [params]      object      pagination: {page: 1, limit: 20}
724
 * @param [cb]          function    callback function to call when request is complete
725
 * @return q.Promise
726
 */
727
APIClient.prototype.getWebhookEvents = function(identifier, params, cb) {
728
    var self = this;
729
730
    if (typeof params === "function") {
731
        cb = params;
732
        params = null;
733
    }
734
735
    return self.client.get("/webhook/" + identifier + "/events", params, cb);
736
};
737
738
/**
739
 * subscribes a webhook to transaction events for a particular transaction
740
 *
741
 * @param identifier    string      the unique identifier of the webhook
742
 * @param transaction   string      the transaction hash
743
 * @param confirmations integer     the amount of confirmations to send
744
 * @param [cb]          function    callback function to call when request is complete
745
 * @return q.Promise
746
 */
747
APIClient.prototype.subscribeTransaction = function(identifier, transaction, confirmations, cb) {
748
    var self = this;
749
    var postData = {
750
        'event_type': 'transaction',
751
        'transaction': transaction,
752
        'confirmations': confirmations
753
    };
754
755
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
756
};
757
758
/**
759
 * subscribes a webhook to transaction events on a particular address
760
 *
761
 * @param identifier    string      the unique identifier of the webhook
762
 * @param address       string      the address hash
763
 * @param confirmations integer     the amount of confirmations to send
764
 * @param [cb]          function    callback function to call when request is complete
765
 * @return q.Promise
766
 */
767
APIClient.prototype.subscribeAddressTransactions = function(identifier, address, confirmations, cb) {
768
    var self = this;
769
    var postData = {
770
        'event_type': 'address-transactions',
771
        'address': address,
772
        'confirmations': confirmations
773
    };
774
775
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
776
};
777
778
/**
779
 * batch subscribes a webhook to multiple transaction events
780
 *
781
 * @param  identifier   string      the unique identifier of the webhook
782
 * @param  batchData    array       An array of objects containing batch event data:
783
 *                                  {address : 'address', confirmations : 'confirmations']
784
 *                                  where address is the address to subscribe to and confirmations (optional) is the amount of confirmations to send
785
 * @param [cb]          function    callback function to call when request is complete
786
 * @return q.Promise
787
 */
788
APIClient.prototype.batchSubscribeAddressTransactions = function(identifier, batchData, cb) {
789
    var self = this;
790
    batchData.forEach(function(record) {
791
        record.event_type = 'address-transactions';
792
    });
793
794
    return self.client.post("/webhook/" + identifier + "/events/batch", null, batchData, cb);
795
};
796
797
/**
798
 * subscribes a webhook to a new block event
799
 *
800
 * @param identifier    string      the unique identifier of the webhook
801
 * @param [cb]          function    callback function to call when request is complete
802
 * @return q.Promise
803
 */
804
APIClient.prototype.subscribeNewBlocks = function(identifier, cb) {
805
    var self = this;
806
    var postData = {
807
        'event_type': 'block'
808
    };
809
810
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
811
};
812
813
/**
814
 * removes an address transaction event subscription from a webhook
815
 *
816
 * @param identifier    string      the unique identifier of the webhook
817
 * @param address       string      the address hash
818
 * @param [cb]          function    callback function to call when request is complete
819
 * @return q.Promise
820
 */
821
APIClient.prototype.unsubscribeAddressTransactions = function(identifier, address, cb) {
822
    var self = this;
823
824
    return self.client.delete("/webhook/" + identifier + "/address-transactions/" + address, null, null, cb);
825
};
826
827
/**
828
 * removes an transaction event subscription from a webhook
829
 *
830
 * @param identifier    string      the unique identifier of the webhook
831
 * @param transaction   string      the transaction hash
832
 * @param [cb]          function    callback function to call when request is complete
833
 * @return q.Promise
834
 */
835
APIClient.prototype.unsubscribeTransaction = function(identifier, transaction, cb) {
836
    var self = this;
837
838
    return self.client.delete("/webhook/" + identifier + "/transaction/" + transaction, null, null, cb);
839
};
840
841
/**
842
 * removes a block event subscription from a webhook
843
 *
844
 * @param identifier    string      the unique identifier of the webhook
845
 * @param [cb]          function    callback function to call when request is complete
846
 * @return q.Promise
847
 */
848
APIClient.prototype.unsubscribeNewBlocks = function(identifier, cb) {
849
    var self = this;
850
851
    return self.client.delete("/webhook/" + identifier + "/block", null, null, cb);
852
};
853
854
/**
855
 * initialize an existing wallet
856
 *
857
 * Either takes two argument:
858
 * @param options       object      {}
859
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
860
 *
861
 * Or takes three arguments (old, deprecated syntax):
862
 * @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...
863
 * @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...
864
 * @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 859. The second definition is ignored.
Loading history...
865
 *
866
 * @returns {q.Promise}
867
 */
868
APIClient.prototype.initWallet = function(options, cb) {
869
    var self = this;
870
871
    if (typeof options !== "object") {
872
        // get the old-style arguments
873
        options = {
874
            identifier: arguments[0],
875
            passphrase: arguments[1]
876
        };
877
878
        cb = arguments[2];
879
    }
880
881
    if (options.check_backup_key) {
882
        if (typeof options.check_backup_key !== "string") {
883
            throw new Error("Invalid input, must provide the backup key as a string (the xpub)");
884
        }
885
    }
886
887
    var deferred = q.defer();
888
    deferred.promise.spreadNodeify(cb);
889
890
    var identifier = options.identifier;
891
892
    if (!identifier) {
893
        deferred.reject(new blocktrail.WalletInitError("Identifier is required"));
894
        return deferred.promise;
895
    }
896
897
    deferred.resolve(self.client.get("/wallet/" + identifier, null, true).then(function(result) {
898
        var keyIndex = options.keyIndex || result.key_index;
899
900
        options.walletVersion = result.wallet_version;
901
902
        if (options.check_backup_key) {
903
            if (options.check_backup_key !== result.backup_public_key[0]) {
904
                throw new Error("Backup key returned from server didn't match our own copy");
905
            }
906
        }
907
        var backupPublicKey = bitcoin.HDNode.fromBase58(result.backup_public_key[0], self.network);
908
        var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
909
            return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
910
        });
911
        var primaryPublicKeys = _.mapValues(result.primary_public_keys, function(primaryPublicKey) {
912
            return bitcoin.HDNode.fromBase58(primaryPublicKey[0], self.network);
913
        });
914
915
        // initialize wallet
916
        var wallet = new Wallet(
917
            self,
918
            identifier,
919
            options.walletVersion,
920
            result.primary_mnemonic,
921
            result.encrypted_primary_seed,
922
            result.encrypted_secret,
923
            primaryPublicKeys,
924
            backupPublicKey,
925
            blocktrailPublicKeys,
926
            keyIndex,
927
            result.segwit || 0,
928
            self.testnet,
929
            result.checksum,
930
            result.upgrade_key_index,
931
            options.bypassNewAddressCheck
932
        );
933
934
        wallet.recoverySecret = result.recovery_secret;
935
936
        if (!options.readOnly) {
937
            return wallet.unlock(options).then(function() {
938
                return wallet;
939
            });
940
        } else {
941
            return wallet;
942
        }
943
    }));
944
945
    return deferred.promise;
946
};
947
948
APIClient.CREATE_WALLET_PROGRESS_START = 0;
949
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET = 4;
950
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY = 5;
951
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY = 6;
952
APIClient.CREATE_WALLET_PROGRESS_PRIMARY = 10;
953
APIClient.CREATE_WALLET_PROGRESS_BACKUP = 20;
954
APIClient.CREATE_WALLET_PROGRESS_SUBMIT = 30;
955
APIClient.CREATE_WALLET_PROGRESS_INIT = 40;
956
APIClient.CREATE_WALLET_PROGRESS_DONE = 100;
957
958
/**
959
 * create a new wallet
960
 *   - will generate a new primary seed and backup seed
961
 *
962
 * Either takes two argument:
963
 * @param options       object      {}
964
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys) // nocommit @TODO
965
 *
966
 * For v1 wallets (explicitly specify options.walletVersion=v1):
967
 * @param options       object      {}
0 ignored issues
show
Documentation introduced by
The parameter options has already been documented on line 963. The second definition is ignored.
Loading history...
968
 * @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 964. The second definition is ignored.
Loading history...
969
 *
970
 * Or takes four arguments (old, deprecated syntax):
971
 * @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...
972
 * @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...
973
 * @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...
974
 * @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 964. The second definition is ignored.
Loading history...
975
 * @returns {q.Promise}
976
 */
977
APIClient.prototype.createNewWallet = function(options, cb) {
978
    /* jshint -W071, -W074 */
979
980
    var self = this;
981
982
    if (typeof options !== "object") {
983
        // get the old-style arguments
984
        var identifier = arguments[0];
985
        var passphrase = arguments[1];
986
        var keyIndex = arguments[2];
987
        cb = arguments[3];
988
989
        // keyIndex is optional
990
        if (typeof keyIndex === "function") {
991
            cb = keyIndex;
992
            keyIndex = null;
993
        }
994
995
        options = {
996
            identifier: identifier,
997
            passphrase: passphrase,
998
            keyIndex: keyIndex
999
        };
1000
    }
1001
1002
    // default to v3
1003
    options.walletVersion = options.walletVersion || Wallet.WALLET_VERSION_V3;
1004
1005
    var deferred = q.defer();
1006
    deferred.promise.spreadNodeify(cb);
1007
1008
    q.nextTick(function() {
1009
        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_START);
1010
1011
        options.keyIndex = options.keyIndex || 0;
1012
        options.passphrase = options.passphrase || options.password;
1013
        delete options.password;
1014
1015
        if (!options.identifier) {
1016
            deferred.reject(new blocktrail.WalletCreateError("Identifier is required"));
1017
            return deferred.promise;
1018
        }
1019
1020
        if (options.walletVersion === Wallet.WALLET_VERSION_V1) {
1021
            self._createNewWalletV1(options)
1022
                .progress(function(p) { deferred.notify(p); })
1023
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1024
            ;
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...
1025
        } else if (options.walletVersion === Wallet.WALLET_VERSION_V2) {
1026
            self._createNewWalletV2(options)
1027
                .progress(function(p) { deferred.notify(p); })
1028
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1029
            ;
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...
1030
        } else if (options.walletVersion === Wallet.WALLET_VERSION_V3) {
1031
            self._createNewWalletV3(options)
1032
                .progress(function(p) { deferred.notify(p); })
1033
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1034
            ;
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
        } else {
1036
            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...
1037
        }
1038
    });
1039
1040
    return deferred.promise;
1041
};
1042
1043
APIClient.prototype._createNewWalletV1 = function(options) {
1044
    var self = this;
1045
1046
    var deferred = q.defer();
1047
1048
    q.nextTick(function() {
1049
1050
        if (!options.primaryMnemonic && !options.primarySeed) {
1051
            if (!options.passphrase && !options.password) {
1052
                deferred.reject(new blocktrail.WalletCreateError("Can't generate Primary Mnemonic without a passphrase"));
1053
                return deferred.promise;
1054
            } else {
1055
                options.primaryMnemonic = bip39.generateMnemonic(Wallet.WALLET_ENTROPY_BITS);
1056
                if (options.storePrimaryMnemonic !== false) {
1057
                    options.storePrimaryMnemonic = true;
1058
                }
1059
            }
1060
        }
1061
1062
        if (!options.backupMnemonic && !options.backupPublicKey) {
1063
            options.backupMnemonic = bip39.generateMnemonic(Wallet.WALLET_ENTROPY_BITS);
1064
        }
1065
1066
        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_PRIMARY);
1067
1068
        self.resolvePrimaryPrivateKeyFromOptions(options)
1069
            .then(function(options) {
1070
                deferred.notify(APIClient.CREATE_WALLET_PROGRESS_BACKUP);
1071
1072
                return self.resolveBackupPublicKeyFromOptions(options)
1073
                    .then(function(options) {
1074
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_SUBMIT);
1075
1076
                        // create a checksum of our private key which we'll later use to verify we used the right password
1077
                        var checksum = options.primaryPrivateKey.getAddress();
1078
                        var keyIndex = options.keyIndex;
1079
1080
                        var primaryPublicKey = options.primaryPrivateKey.deriveHardened(keyIndex).neutered();
1081
1082
                        // send the public keys to the server to store them
1083
                        //  and the mnemonic, which is safe because it's useless without the password
1084
                        return self.storeNewWalletV1(
1085
                            options.identifier,
1086
                            [primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1087
                            [options.backupPublicKey.toBase58(), "M"],
1088
                            options.storePrimaryMnemonic ? options.primaryMnemonic : false,
1089
                            checksum,
1090
                            keyIndex,
1091
                            options.segwit || null
1092
                        )
1093
                            .then(function(result) {
1094
                                deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1095
1096
                                var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1097
                                    return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1098
                                });
1099
1100
                                var wallet = new Wallet(
1101
                                    self,
1102
                                    options.identifier,
1103
                                    Wallet.WALLET_VERSION_V1,
1104
                                    options.primaryMnemonic,
1105
                                    null,
1106
                                    null,
1107
                                    {keyIndex: primaryPublicKey},
1108
                                    options.backupPublicKey,
1109
                                    blocktrailPublicKeys,
1110
                                    keyIndex,
1111
                                    result.segwit || 0,
1112
                                    self.testnet,
1113
                                    checksum,
1114
                                    result.upgrade_key_index,
1115
                                    options.bypassNewAddressCheck
1116
                                );
1117
1118
                                return wallet.unlock({
1119
                                    walletVersion: Wallet.WALLET_VERSION_V1,
1120
                                    passphrase: options.passphrase,
1121
                                    primarySeed: options.primarySeed,
1122
                                    primaryMnemonic: null // explicit null
1123
                                }).then(function() {
1124
                                    deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1125
                                    return [
1126
                                        wallet,
1127
                                        {
1128
                                            walletVersion: wallet.walletVersion,
1129
                                            primaryMnemonic: options.primaryMnemonic,
1130
                                            backupMnemonic: options.backupMnemonic,
1131
                                            blocktrailPublicKeys: blocktrailPublicKeys
1132
                                        }
1133
                                    ];
1134
                                });
1135
                            });
1136
                    }
1137
                );
1138
            })
1139
            .then(
1140
            function(r) {
1141
                deferred.resolve(r);
1142
            },
1143
            function(e) {
1144
                deferred.reject(e);
1145
            }
1146
        )
1147
        ;
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...
1148
    });
1149
1150
    return deferred.promise;
1151 View Code Duplication
};
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1152
1153
APIClient.prototype._createNewWalletV2 = function(options) {
1154
    var self = this;
1155
1156
    var deferred = q.defer();
1157
1158
    // avoid modifying passed options
1159
    options = _.merge({}, options);
1160
1161
    determineDataStorageV2_3(options)
1162
        .then(function(options) {
1163
            options.passphrase = options.passphrase || options.password;
1164
            delete options.password;
1165
1166
            // avoid deprecated options
1167
            if (options.primaryPrivateKey) {
1168
                throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
1169
            }
1170
1171
            // seed should be provided or generated
1172
            options.primarySeed = options.primarySeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
1173
1174
            return options;
1175
        })
1176
        .then(function(options) {
1177
            return produceEncryptedDataV2(options, deferred.notify.bind(deferred));
1178
        })
1179
        .then(function(options) {
1180
            return doRemainingWalletDataV2_3(options, self.network, deferred.notify.bind(deferred));
1181
        })
1182
        .then(function(options) {
1183
            // create a checksum of our private key which we'll later use to verify we used the right password
1184
            var checksum = options.primaryPrivateKey.getAddress();
1185
            var keyIndex = options.keyIndex;
1186
1187
            // send the public keys and encrypted data to server
1188
            return self.storeNewWalletV2(
1189
                options.identifier,
1190
                [options.primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1191
                [options.backupPublicKey.toBase58(), "M"],
1192
                options.storeDataOnServer ? options.encryptedPrimarySeed : false,
1193
                options.storeDataOnServer ? options.encryptedSecret : false,
1194
                options.storeDataOnServer ? options.recoverySecret : false,
1195
                checksum,
1196
                keyIndex,
1197
                options.support_secret || null,
1198
                options.segwit || null
1199
            )
1200
                .then(
1201
                function(result) {
1202
                    deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1203
1204
                    var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1205
                        return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1206
                    });
1207
1208
                    var wallet = new Wallet(
1209
                        self,
1210
                        options.identifier,
1211
                        Wallet.WALLET_VERSION_V2,
1212
                        null,
1213
                        options.storeDataOnServer ? options.encryptedPrimarySeed : null,
1214
                        options.storeDataOnServer ? options.encryptedSecret : null,
1215
                        {keyIndex: options.primaryPublicKey},
1216
                        options.backupPublicKey,
1217
                        blocktrailPublicKeys,
1218
                        keyIndex,
1219
                        result.segwit || 0,
1220
                        self.testnet,
1221
                        checksum,
1222
                        result.upgrade_key_index,
1223
                        options.bypassNewAddressCheck
1224
                    );
1225
1226
                    // pass along decrypted data to avoid extra work
1227
                    return wallet.unlock({
1228
                        walletVersion: Wallet.WALLET_VERSION_V2,
1229
                        passphrase: options.passphrase,
1230
                        primarySeed: options.primarySeed,
1231
                        secret: options.secret
1232
                    }).then(function() {
1233
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1234
                        return [
1235
                            wallet,
1236
                            {
1237
                                walletVersion: wallet.walletVersion,
1238
                                encryptedPrimarySeed: options.encryptedPrimarySeed ?
1239
                                    bip39.entropyToMnemonic(blocktrail.convert(options.encryptedPrimarySeed, 'base64', 'hex')) :
1240
                                    null,
1241
                                backupSeed: options.backupSeed ? bip39.entropyToMnemonic(options.backupSeed.toString('hex')) : null,
1242
                                recoveryEncryptedSecret: options.recoveryEncryptedSecret ?
1243
                                    bip39.entropyToMnemonic(blocktrail.convert(options.recoveryEncryptedSecret, 'base64', 'hex')) :
1244
                                    null,
1245
                                encryptedSecret: options.encryptedSecret ?
1246
                                    bip39.entropyToMnemonic(blocktrail.convert(options.encryptedSecret, 'base64', 'hex')) :
1247
                                    null,
1248
                                blocktrailPublicKeys: blocktrailPublicKeys
1249
                            }
1250
                        ];
1251
                    });
1252
                }
1253
            );
1254
        })
1255
       .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); });
1256
1257
    return deferred.promise;
1258 View Code Duplication
};
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1259
1260
APIClient.prototype._createNewWalletV3 = function(options) {
1261
    var self = this;
1262
1263
    var deferred = q.defer();
1264
1265
    // avoid modifying passed options
1266
    options = _.merge({}, options);
1267
1268
    determineDataStorageV2_3(options)
1269
        .then(function(options) {
1270
            options.passphrase = options.passphrase || options.password;
1271
            delete options.password;
1272
1273
            // avoid deprecated options
1274
            if (options.primaryPrivateKey) {
1275
                throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
1276
            }
1277
1278
            // seed should be provided or generated
1279
            options.primarySeed = options.primarySeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
1280
1281
            return options;
1282
        })
1283
        .then(function(options) {
1284
            return self.produceEncryptedDataV3(options, deferred.notify.bind(deferred));
1285
        })
1286
        .then(function(options) {
1287
            return doRemainingWalletDataV2_3(options, self.network, deferred.notify.bind(deferred));
1288
        })
1289
        .then(function(options) {
1290
            // create a checksum of our private key which we'll later use to verify we used the right password
1291
            var checksum = options.primaryPrivateKey.getAddress();
1292
            var keyIndex = options.keyIndex;
1293
1294
            // send the public keys and encrypted data to server
1295
            return self.storeNewWalletV3(
1296
                options.identifier,
1297
                [options.primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1298
                [options.backupPublicKey.toBase58(), "M"],
1299
                options.storeDataOnServer ? options.encryptedPrimarySeed : false,
1300
                options.storeDataOnServer ? options.encryptedSecret : false,
1301
                options.storeDataOnServer ? options.recoverySecret : false,
1302
                checksum,
1303
                keyIndex,
1304
                options.support_secret || null,
1305
                options.segwit || null
1306
            )
1307
                .then(
1308
                    // result, deferred, self(apiclient)
1309
                    function(result) {
1310
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1311
1312
                        var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1313
                            return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1314
                        });
1315
1316
                        var wallet = new Wallet(
1317
                            self,
1318
                            options.identifier,
1319
                            Wallet.WALLET_VERSION_V3,
1320
                            null,
1321
                            options.storeDataOnServer ? options.encryptedPrimarySeed : null,
1322
                            options.storeDataOnServer ? options.encryptedSecret : null,
1323
                            {keyIndex: options.primaryPublicKey},
1324
                            options.backupPublicKey,
1325
                            blocktrailPublicKeys,
1326
                            keyIndex,
1327
                            result.segwit || 0,
1328
                            self.testnet,
1329
                            checksum,
1330
                            result.upgrade_key_index,
1331
                            options.bypassNewAddressCheck
1332
                        );
1333
1334
                        // pass along decrypted data to avoid extra work
1335
                        return wallet.unlock({
1336
                            walletVersion: Wallet.WALLET_VERSION_V3,
1337
                            passphrase: options.passphrase,
1338
                            primarySeed: options.primarySeed,
1339
                            secret: options.secret
1340
                        }).then(function() {
1341
                            deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1342
                            return [
1343
                                wallet,
1344
                                {
1345
                                    walletVersion: wallet.walletVersion,
1346
                                    encryptedPrimarySeed: options.encryptedPrimarySeed ? EncryptionMnemonic.encode(options.encryptedPrimarySeed) : null,
1347
                                    backupSeed: options.backupSeed ? bip39.entropyToMnemonic(options.backupSeed) : null,
1348
                                    recoveryEncryptedSecret: options.recoveryEncryptedSecret ?
1349
                                        EncryptionMnemonic.encode(options.recoveryEncryptedSecret) : null,
1350
                                    encryptedSecret: options.encryptedSecret ? EncryptionMnemonic.encode(options.encryptedSecret) : null,
1351
                                    blocktrailPublicKeys: blocktrailPublicKeys
1352
                                }
1353
                            ];
1354
                        });
1355
                    }
1356
                );
1357
        })
1358
        .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); });
1359
1360
    return deferred.promise;
1361
};
1362
1363
function verifyPublicBip32Key(bip32Key, network) {
1364
    var hk = bitcoin.HDNode.fromBase58(bip32Key[0], network);
1365
    if (typeof hk.keyPair.d !== "undefined") {
1366
        throw new Error('BIP32Key contained private key material - abort');
1367
    }
1368
1369
    if (bip32Key[1].slice(0, 1) !== "M") {
1370
        throw new Error("BIP32Key contained non-public path - abort");
1371
    }
1372
}
1373
1374
function verifyPublicOnly(walletData, network) {
1375
    verifyPublicBip32Key(walletData.primary_public_key, network);
1376
    verifyPublicBip32Key(walletData.backup_public_key, network);
1377
}
1378
1379
/**
1380
 * create wallet using the API
1381
 *
1382
 * @param identifier            string      the wallet identifier to create
1383
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1384
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1385
 * @param primaryMnemonic       string      mnemonic to store
1386
 * @param checksum              string      checksum to store
1387
 * @param keyIndex              int         keyIndex that was used to create wallet
1388
 * @param segwit                bool
1389
 * @returns {q.Promise}
1390
 */
1391
APIClient.prototype.storeNewWalletV1 = function(identifier, primaryPublicKey, backupPublicKey, primaryMnemonic,
1392
                                                checksum, keyIndex, segwit) {
1393
    var self = this;
1394
1395
    var postData = {
1396
        identifier: identifier,
1397
        wallet_version: Wallet.WALLET_VERSION_V1,
1398
        primary_public_key: primaryPublicKey,
1399
        backup_public_key: backupPublicKey,
1400
        primary_mnemonic: primaryMnemonic,
1401
        checksum: checksum,
1402
        key_index: keyIndex,
1403
        segwit: segwit
1404
    };
1405
1406
    verifyPublicOnly(postData, self.network);
1407
1408
    return self.client.post("/wallet", null, postData);
1409
};
1410
1411
/**
1412
 * create wallet using the API
1413
 *
1414
 * @param identifier            string      the wallet identifier to create
1415
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1416
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1417
 * @param encryptedPrimarySeed  string      openssl format
1418
 * @param encryptedSecret       string      openssl format
1419
 * @param recoverySecret        string      openssl format
1420
 * @param checksum              string      checksum to store
1421
 * @param keyIndex              int         keyIndex that was used to create wallet
1422
 * @param supportSecret         string
1423
 * @param segwit                bool
1424
 * @returns {q.Promise}
1425
 */
1426
APIClient.prototype.storeNewWalletV2 = function(identifier, primaryPublicKey, backupPublicKey, encryptedPrimarySeed, encryptedSecret,
1427
                                                recoverySecret, checksum, keyIndex, supportSecret, segwit) {
1428
    var self = this;
1429
1430
    var postData = {
1431
        identifier: identifier,
1432
        wallet_version: Wallet.WALLET_VERSION_V2,
1433
        primary_public_key: primaryPublicKey,
1434
        backup_public_key: backupPublicKey,
1435
        encrypted_primary_seed: encryptedPrimarySeed,
1436
        encrypted_secret: encryptedSecret,
1437
        recovery_secret: recoverySecret,
1438
        checksum: checksum,
1439
        key_index: keyIndex,
1440
        support_secret: supportSecret || null,
1441
        segwit: segwit
1442
    };
1443
1444
    verifyPublicOnly(postData, self.network);
1445
1446
    return self.client.post("/wallet", null, postData);
1447
};
1448
1449
/**
1450
 * create wallet using the API
1451
 *
1452
 * @param identifier            string      the wallet identifier to create
1453
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1454
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1455
 * @param encryptedPrimarySeed  Buffer      buffer of ciphertext
1456
 * @param encryptedSecret       Buffer      buffer of ciphertext
1457
 * @param recoverySecret        Buffer      buffer of recovery secret
1458
 * @param checksum              string      checksum to store
1459
 * @param keyIndex              int         keyIndex that was used to create wallet
1460
 * @param supportSecret         string
1461
 * @param segwit                bool
1462
 * @returns {q.Promise}
1463
 */
1464
APIClient.prototype.storeNewWalletV3 = function(identifier, primaryPublicKey, backupPublicKey, encryptedPrimarySeed, encryptedSecret,
1465
                                                recoverySecret, checksum, keyIndex, supportSecret, segwit) {
1466
    var self = this;
1467
1468
    var postData = {
1469
        identifier: identifier,
1470
        wallet_version: Wallet.WALLET_VERSION_V3,
1471
        primary_public_key: primaryPublicKey,
1472
        backup_public_key: backupPublicKey,
1473
        encrypted_primary_seed: encryptedPrimarySeed.toString('base64'),
1474
        encrypted_secret: encryptedSecret.toString('base64'),
1475
        recovery_secret: recoverySecret.toString('hex'),
1476
        checksum: checksum,
1477
        key_index: keyIndex,
1478
        support_secret: supportSecret || null,
1479
        segwit: segwit
1480
    };
1481
1482
    verifyPublicOnly(postData, self.network);
1483
1484
    return self.client.post("/wallet", null, postData);
1485
};
1486
1487
/**
1488
 * create wallet using the API
1489
 *
1490
 * @param identifier            string      the wallet identifier to create
1491
 * @param postData              object
1492
 * @param [cb]                  function    callback(err, result)
1493
 * @returns {q.Promise}
1494
 */
1495
APIClient.prototype.updateWallet = function(identifier, postData, cb) {
1496
    var self = this;
1497
1498
    return self.client.post("/wallet/" + identifier, null, postData, cb);
1499
};
1500
1501
/**
1502
 * upgrade wallet to use a new account number
1503
 *  the account number specifies which blocktrail cosigning key is used
1504
 *
1505
 * @param identifier            string      the wallet identifier
1506
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1507
 * @param keyIndex              int         keyIndex that was used to create wallet
1508
 * @param [cb]                  function    callback(err, result)
1509
 * @returns {q.Promise}
1510
 */
1511
APIClient.prototype.upgradeKeyIndex = function(identifier, keyIndex, primaryPublicKey, cb) {
1512
    var self = this;
1513
1514
    return self.client.post("/wallet/" + identifier + "/upgrade", null, {
1515
        key_index: keyIndex,
1516
        primary_public_key: primaryPublicKey
1517
    }, cb);
1518
};
1519
1520
/**
1521
 * get the balance for the wallet
1522
 *
1523
 * @param identifier            string      the wallet identifier
1524
 * @param [cb]                  function    callback(err, result)
1525
 * @returns {q.Promise}
1526
 */
1527
APIClient.prototype.getWalletBalance = function(identifier, cb) {
1528
    var self = this;
1529
1530
    return self.client.get("/wallet/" + identifier + "/balance", null, true, cb);
1531
};
1532
1533
/**
1534
 * do HD wallet discovery for the wallet
1535
 *
1536
 * @param identifier            string      the wallet identifier
1537
 * @param [cb]                  function    callback(err, result)
1538
 * @returns {q.Promise}
1539
 */
1540
APIClient.prototype.doWalletDiscovery = function(identifier, gap, cb) {
1541
    var self = this;
1542
1543
    return self.client.get("/wallet/" + identifier + "/discovery", {gap: gap}, true, cb);
1544
};
1545
1546
1547
/**
1548
 * get a new derivation number for specified parent path
1549
 *  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
1550
 *
1551
 * @param identifier            string      the wallet identifier
1552
 * @param path                  string      the parent path for which to get a new derivation,
1553
 *                                           can be suffixed with /* to make it clear on which level the derivations hould be
1554
 * @param [cb]                  function    callback(err, result)
1555
 * @returns {q.Promise}
1556
 */
1557
APIClient.prototype.getNewDerivation = function(identifier, path, cb) {
1558
    var self = this;
1559
1560
    return self.client.post("/wallet/" + identifier + "/path", null, {path: path}, cb);
1561
};
1562
1563
1564
/**
1565
 * delete the wallet
1566
 *  the checksum address and a signature to verify you ownership of the key of that checksum address
1567
 *  is required to be able to delete a wallet
1568
 *
1569
 * @param identifier            string      the wallet identifier
1570
 * @param checksumAddress       string      the address for your master private key (and the checksum used when creating the wallet)
1571
 * @param checksumSignature     string      a signature of the checksum address as message signed by the private key matching that address
1572
 * @param [force]               bool        ignore warnings (such as a non-zero balance)
1573
 * @param [cb]                  function    callback(err, result)
1574
 * @returns {q.Promise}
1575
 */
1576
APIClient.prototype.deleteWallet = function(identifier, checksumAddress, checksumSignature, force, cb) {
1577
    var self = this;
1578
1579
    if (typeof force === "function") {
1580
        cb = force;
1581
        force = false;
1582
    }
1583
1584
    return self.client.delete("/wallet/" + identifier, {force: force}, {
1585
        checksum: checksumAddress,
1586
        signature: checksumSignature
1587
    }, cb);
1588
};
1589
1590
/**
1591
 * use the API to get the best inputs to use based on the outputs
1592
 *
1593
 * the return array has the following format:
1594
 * [
1595
 *  "utxos" => [
1596
 *      [
1597
 *          "hash" => "<txHash>",
1598
 *          "idx" => "<index of the output of that <txHash>",
1599
 *          "scriptpubkey_hex" => "<scriptPubKey-hex>",
1600
 *          "value" => 32746327,
1601
 *          "address" => "1address",
1602
 *          "path" => "m/44'/1'/0'/0/13",
1603
 *          "redeem_script" => "<redeemScript-hex>",
1604
 *      ],
1605
 *  ],
1606
 *  "fee"   => 10000,
1607
 *  "change"=> 1010109201,
1608
 * ]
1609
 *
1610
 * @param identifier        string      the wallet identifier
1611
 * @param pay               array       {'address': (int)value}     coins to send
1612
 * @param lockUTXO          bool        lock UTXOs for a few seconds to allow for transaction to be created
1613
 * @param allowZeroConf     bool        allow zero confirmation unspent outputs to be used in coin selection
1614
 * @param feeStrategy       string      defaults to
1615
 * @param options
1616
 * @param [cb]              function    callback(err, utxos, fee, change)
1617
 * @returns {q.Promise}
1618
 */
1619
APIClient.prototype.coinSelection = function(identifier, pay, lockUTXO, allowZeroConf, feeStrategy, options, cb) {
1620
    var self = this;
1621
1622
    if (typeof feeStrategy === "function") {
1623
        cb = feeStrategy;
1624
        feeStrategy = null;
1625
        options = {};
1626
    } else if (typeof options === "function") {
1627
        cb = options;
1628
        options = {};
1629
    }
1630
1631
    feeStrategy = feeStrategy || Wallet.FEE_STRATEGY_OPTIMAL;
1632
    options = options || {};
1633
1634
    var deferred = q.defer();
1635
    deferred.promise.spreadNodeify(cb);
1636
1637
    var params = {
1638
        lock: lockUTXO,
1639
        zeroconf: allowZeroConf ? 1 : 0,
1640
        zeroconfself: (typeof options.allowZeroConfSelf !== "undefined" ? options.allowZeroConfSelf : true) ? 1 : 0,
1641
        fee_strategy: feeStrategy
1642
    };
1643
1644
    if (options.forcefee) {
1645
        params['forcefee'] = options.forcefee;
1646
    }
1647
1648
    deferred.resolve(
1649
        self.client.post("/wallet/" + identifier + "/coin-selection", params, pay).then(
1650
            function(result) {
1651
                return [result.utxos, result.fee, result.change, result];
1652
            },
1653
            function(err) {
1654
                if (err.message.match(/too low to pay the fee/)) {
1655
                    throw blocktrail.WalletFeeError(err);
1656
                }
1657
1658
                throw err;
1659
            }
1660
        )
1661
    );
1662
1663
    return deferred.promise;
1664
};
1665
1666
/**
1667
 * @param [cb]              function    callback(err, utxos, fee, change)
1668
 * @returns {q.Promise}
1669
 */
1670
APIClient.prototype.feePerKB = function(cb) {
1671
    var self = this;
1672
1673
    var deferred = q.defer();
1674
    deferred.promise.spreadNodeify(cb);
1675
1676
    deferred.resolve(self.client.get("/fee-per-kb"));
1677
1678
    return deferred.promise;
1679
};
1680
1681
/**
1682
 * send the transaction using the API
1683
 *
1684
 * @param identifier        string      the wallet identifier
1685
 * @param txHex             string      partially signed transaction as hex string
1686
 * @param paths             array       list of paths used in inputs which should be cosigned by the API
1687
 * @param checkFee          bool        when TRUE the API will verify if the fee is 100% correct and otherwise throw an exception
1688
 * @param [twoFactorToken]  string      2FA token
1689
 * @param [prioboost]       bool
1690
 * @param [cb]              function    callback(err, txHash)
1691
 * @returns {q.Promise}
1692
 */
1693
APIClient.prototype.sendTransaction = function(identifier, txHex, paths, checkFee, twoFactorToken, prioboost, cb) {
1694
    var self = this;
1695
1696
    if (typeof twoFactorToken === "function") {
1697
        cb = twoFactorToken;
1698
        twoFactorToken = null;
1699
        prioboost = false;
1700
    } else if (typeof prioboost === "function") {
1701
        cb = prioboost;
1702
        prioboost = false;
1703
    }
1704
1705
    var data = {
1706
        paths: paths,
1707
        two_factor_token: twoFactorToken
1708
    };
1709
    if (typeof txHex === "string") {
1710
        data.raw_transaction = txHex;
1711
    } else if (typeof txHex === "object") {
1712
        Object.keys(txHex).map(function(key) {
1713
            data[key] = txHex[key];
1714
        });
1715
    }
1716
1717
    return self.client.post(
1718
        "/wallet/" + identifier + "/send",
1719
        {
1720
            check_fee: checkFee ? 1 : 0,
1721
            prioboost: prioboost ? 1 : 0
1722
        },
1723
        data,
1724
        cb
1725
    );
1726
};
1727
1728
/**
1729
 * setup a webhook for this wallet
1730
 *
1731
 * @param identifier        string      the wallet identifier
1732
 * @param webhookIdentifier string      identifier for the webhook
1733
 * @param url               string      URL to receive webhook events
1734
 * @param [cb]              function    callback(err, webhook)
1735
 * @returns {q.Promise}
1736
 */
1737
APIClient.prototype.setupWalletWebhook = function(identifier, webhookIdentifier, url, cb) {
1738
    var self = this;
1739
1740
    return self.client.post("/wallet/" + identifier + "/webhook", null, {url: url, identifier: webhookIdentifier}, cb);
1741
};
1742
1743
/**
1744
 * delete a webhook that was created for this wallet
1745
 *
1746
 * @param identifier        string      the wallet identifier
1747
 * @param webhookIdentifier string      identifier for the webhook
1748
 * @param [cb]              function    callback(err, success)
1749
 * @returns {q.Promise}
1750
 */
1751
APIClient.prototype.deleteWalletWebhook = function(identifier, webhookIdentifier, cb) {
1752
    var self = this;
1753
1754
    return self.client.delete("/wallet/" + identifier + "/webhook/" + webhookIdentifier, null, null, cb);
1755
};
1756
1757
/**
1758
 * get all transactions for an wallet (paginated)
1759
 *
1760
 * @param identifier    string      wallet identifier
1761
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1762
 * @param [cb]          function    callback function to call when request is complete
1763
 * @return q.Promise
1764
 */
1765
APIClient.prototype.walletTransactions = function(identifier, params, cb) {
1766
    var self = this;
1767
1768
    if (typeof params === "function") {
1769
        cb = params;
1770
        params = null;
1771
    }
1772
1773
    return self.client.get("/wallet/" + identifier + "/transactions", params, true, cb);
1774
};
1775
1776
/**
1777
 * get all addresses for an wallet (paginated)
1778
 *
1779
 * @param identifier    string      wallet identifier
1780
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1781
 * @param [cb]          function    callback function to call when request is complete
1782
 * @return q.Promise
1783
 */
1784
APIClient.prototype.walletAddresses = function(identifier, params, cb) {
1785
    var self = this;
1786
1787
    if (typeof params === "function") {
1788
        cb = params;
1789
        params = null;
1790
    }
1791
1792
    return self.client.get("/wallet/" + identifier + "/addresses", params, true, cb);
1793
};
1794
1795
/**
1796
 * @param identifier    string      wallet identifier
1797
 * @param address       string      the address to label
1798
 * @param label         string      the label
1799
 * @param [cb]          function    callback(err, res)
1800
 * @return q.Promise
1801
 */
1802
APIClient.prototype.labelWalletAddress = function(identifier, address, label, cb) {
1803
    var self = this;
1804
1805
    return self.client.post("/wallet/" + identifier + "/address/" + address + "/label", null, {label: label}, cb);
1806
};
1807
1808
APIClient.prototype.walletMaxSpendable = function(identifier, allowZeroConf, feeStrategy, options, cb) {
1809
    var self = this;
1810
1811
    if (typeof feeStrategy === "function") {
1812
        cb = feeStrategy;
1813
        feeStrategy = null;
1814
    } else if (typeof options === "function") {
1815
        cb = options;
1816
        options = {};
1817
    }
1818
1819
    feeStrategy = feeStrategy || Wallet.FEE_STRATEGY_OPTIMAL;
1820
    options = options || {};
1821
1822
    var params = {
1823
        outputs: options.outputs ? options.outputs : 1,
1824
        zeroconf: allowZeroConf ? 1 : 0,
1825
        zeroconfself: (typeof options.allowZeroConfSelf !== "undefined" ? options.allowZeroConfSelf : true) ? 1 : 0,
1826
        fee_strategy: feeStrategy
1827
    };
1828
1829
    if (options.forcefee) {
1830
        params['forcefee'] = options.forcefee;
1831
    }
1832
1833
    return self.client.get("/wallet/" + identifier + "/max-spendable", params, true, cb);
1834
};
1835
1836
/**
1837
 * get all UTXOs for an wallet (paginated)
1838
 *
1839
 * @param identifier    string      wallet identifier
1840
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1841
 * @param [cb]          function    callback function to call when request is complete
1842
 * @return q.Promise
1843
 */
1844
APIClient.prototype.walletUTXOs = function(identifier, params, cb) {
1845
    var self = this;
1846
1847
    if (typeof params === "function") {
1848
        cb = params;
1849
        params = null;
1850
    }
1851
1852
    return self.client.get("/wallet/" + identifier + "/utxos", params, true, cb);
1853
};
1854
1855
/**
1856
 * get a paginated list of all wallets associated with the api user
1857
 *
1858
 * @param [params]      object      pagination: {page: 1, limit: 20}
1859
 * @param [cb]          function    callback function to call when request is complete
1860
 * @return q.Promise
1861
 */
1862
APIClient.prototype.allWallets = function(params, cb) {
1863
    var self = this;
1864
1865
    if (typeof params === "function") {
1866
        cb = params;
1867
        params = null;
1868
    }
1869
1870
    return self.client.get("/wallets", params, true, cb);
1871
};
1872
1873
/**
1874
 * verify a message signed bitcoin-core style
1875
 *
1876
 * @param message        string
1877
 * @param address        string
1878
 * @param signature      string
1879
 * @param [cb]          function    callback function to call when request is complete
1880
 * @return q.Promise
1881
 */
1882
APIClient.prototype.verifyMessage = function(message, address, signature, cb) {
1883
    var self = this;
1884
1885
    // we could also use the API instead of the using bitcoinjs-lib to verify
1886
    // return self.client.post("/verify_message", null, {message: message, address: address, signature: signature}, cb);
1887
1888
    var deferred = q.defer();
1889
    deferred.promise.nodeify(cb);
1890
    try {
1891
        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...
1892
        deferred.resolve(result);
1893
    } catch (e) {
1894
        deferred.reject(e);
1895
    }
1896
1897
    return deferred.promise;
1898
};
1899
1900
/**
1901
 * max is 0.001
1902
 * testnet only
1903
 *
1904
 * @param address
1905
 * @param amount
1906
 * @param cb
1907
 */
1908
APIClient.prototype.faucetWithdrawl = function(address, amount, cb) {
1909
    var self = this;
1910
1911
    return self.client.post("/faucet/withdrawl", null, {address: address, amount: amount}, cb);
1912
};
1913
1914
/**
1915
 * send a raw transaction
1916
 *
1917
 * @param rawTransaction    string      raw transaction as HEX
1918
 * @param [cb]              function    callback function to call when request is complete
1919
 * @return q.Promise
1920
 */
1921
APIClient.prototype.sendRawTransaction = function(rawTransaction, cb) {
1922
    var self = this;
1923
1924
    return self.client.post("/send-raw-tx", null, rawTransaction, cb);
1925
};
1926
1927
/**
1928
 * get the current price index
1929
 *
1930
 * @param [cb]          function    callback({'USD': 287.30})
1931
 * @return q.Promise
1932
 */
1933
APIClient.prototype.price = function(cb) {
1934
    var self = this;
1935
1936
    return self.client.get("/price", null, false, cb);
1937
};
1938
1939
module.exports = APIClient;
1940