Completed
Pull Request — master (#91)
by thomas
01:09
created

APIClient._createNewWalletV1   B

Complexity

Conditions 1
Paths 64

Size

Total Lines 111

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 64
dl 0
loc 111
rs 8.2857
nop 1

1 Function

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