refactor: promises.

This commit is contained in:
Christopher Jeffrey 2016-09-20 14:56:54 -07:00
parent 72597c9faf
commit d78151d3d3
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
37 changed files with 7559 additions and 8696 deletions

View File

@ -1,5 +1,6 @@
{ {
"bitwise": false, "bitwise": false,
"esversion": 6,
"curly": false, "curly": false,
"eqeqeq": true, "eqeqeq": true,
"freeze": true, "freeze": true,

View File

@ -32,10 +32,16 @@ process.on('uncaughtException', function(err) {
process.exit(1); process.exit(1);
}); });
node.open(function(err) { process.on('unhandledRejection', function(err, promise) {
if (err) node.logger.debug('Unhandled Rejection');
throw err; node.logger.debug(err.stack);
node.logger.error(err);
process.exit(1);
});
node.open().then(function() {
node.pool.connect(); node.pool.connect();
node.startSync(); node.startSync();
}).catch(function(e) {
throw e;
}); });

View File

@ -25,10 +25,7 @@ node.on('error', function(err) {
; ;
}); });
node.open(function(err) { node.open().then(function() {
if (err)
throw err;
if (process.argv.indexOf('--test') !== -1) { if (process.argv.indexOf('--test') !== -1) {
node.pool.watchAddress('1VayNert3x1KzbpzMGt2qdqrAThiRovi8'); node.pool.watchAddress('1VayNert3x1KzbpzMGt2qdqrAThiRovi8');
node.pool.watch(bcoin.outpoint().toRaw()); node.pool.watch(bcoin.outpoint().toRaw());
@ -40,4 +37,6 @@ node.open(function(err) {
} }
node.startSync(); node.startSync();
}).catch(function(err) {
throw err;
}); });

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@ var crypto = require('../crypto/crypto');
var assert = utils.assert; var assert = utils.assert;
var BufferWriter = require('../utils/writer'); var BufferWriter = require('../utils/writer');
var BufferReader = require('../utils/reader'); var BufferReader = require('../utils/reader');
var spawn = require('../utils/spawn');
/** /**
* Represents an entry in the chain. Unlike * Represents an entry in the chain. Unlike
@ -159,7 +160,7 @@ ChainEntry.prototype.isGenesis = function isGenesis() {
* @param {Function} callback * @param {Function} callback
*/ */
ChainEntry.prototype.getRetargetAncestors = function getRetargetAncestors(callback) { ChainEntry.prototype.getRetargetAncestors = function getRetargetAncestors() {
var majorityWindow = this.network.block.majorityWindow; var majorityWindow = this.network.block.majorityWindow;
var medianTimespan = constants.block.MEDIAN_TIMESPAN; var medianTimespan = constants.block.MEDIAN_TIMESPAN;
var powDiffInterval = this.network.pow.retargetInterval; var powDiffInterval = this.network.pow.retargetInterval;
@ -167,7 +168,7 @@ ChainEntry.prototype.getRetargetAncestors = function getRetargetAncestors(callba
var max = Math.max(majorityWindow, medianTimespan); var max = Math.max(majorityWindow, medianTimespan);
if ((this.height + 1) % powDiffInterval === 0 || diffReset) if ((this.height + 1) % powDiffInterval === 0 || diffReset)
max = Math.max(max, powDiffInterval); max = Math.max(max, powDiffInterval);
this.getAncestors(max, callback); return this.getAncestors(max);
}; };
/** /**
@ -176,48 +177,44 @@ ChainEntry.prototype.getRetargetAncestors = function getRetargetAncestors(callba
* @param {Function} callback - Returns [Error, ChainEntry[]]. * @param {Function} callback - Returns [Error, ChainEntry[]].
*/ */
ChainEntry.prototype.getAncestors = function getAncestors(max, callback) { ChainEntry.prototype.getAncestors = function getAncestors(max) {
var entry = this; return spawn(function *() {
var ancestors = []; var entry = this;
var cached; var ancestors = [];
var cached;
if (max === 0) if (max === 0)
return callback(null, ancestors); return ancestors;
assert(utils.isNumber(max)); assert(utils.isNumber(max));
// Try to do this iteratively and synchronously // Try to do this iteratively and synchronously
// so we don't have to wait on nextTicks. // so we don't have to wait on nextTicks.
for (;;) { for (;;) {
ancestors.push(entry); ancestors.push(entry);
if (ancestors.length >= max) if (ancestors.length >= max)
return callback(null, ancestors); return ancestors;
cached = this.chain.db.getCache(entry.prevBlock); cached = this.chain.db.getCache(entry.prevBlock);
if (!cached) { if (!cached) {
ancestors.pop(); ancestors.pop();
break; break;
}
entry = cached;
} }
entry = cached; while (entry) {
} ancestors.push(entry);
if (ancestors.length >= max)
break;
entry = yield entry.getPrevious();
}
(function next(err, entry) { return ancestors;
if (err) }, this);
return callback(err);
if (!entry)
return callback(null, ancestors);
ancestors.push(entry);
if (ancestors.length >= max)
return callback(null, ancestors);
entry.getPrevious(next);
})(null, entry);
}; };
/** /**
@ -225,8 +222,8 @@ ChainEntry.prototype.getAncestors = function getAncestors(max, callback) {
* @param {Function} callback - Return [Error, Boolean]. * @param {Function} callback - Return [Error, Boolean].
*/ */
ChainEntry.prototype.isMainChain = function isMainChain(callback) { ChainEntry.prototype.isMainChain = function isMainChain() {
this.chain.db.isMainChain(this, callback); return this.chain.db.isMainChain(this);
}; };
/** /**
@ -235,34 +232,30 @@ ChainEntry.prototype.isMainChain = function isMainChain(callback) {
* @param {Function} callback - Returns [Error, ChainEntry[]]. * @param {Function} callback - Returns [Error, ChainEntry[]].
*/ */
ChainEntry.prototype.getAncestorByHeight = function getAncestorByHeight(height, callback) { ChainEntry.prototype.getAncestorByHeight = function getAncestorByHeight(height) {
var self = this; return spawn(function *() {
var main, entry;
if (height < 0) if (height < 0)
return utils.nextTick(callback); return yield utils.wait();
assert(height >= 0); assert(height >= 0);
assert(height <= this.height); assert(height <= this.height);
this.isMainChain(function(err, main) { main = yield this.isMainChain();
if (err)
return callback(err);
if (main) if (main)
return self.chain.db.get(height, callback); return yield this.chain.db.get(height);
self.getAncestor(self.height - height, function(err, entry) { entry = yield this.getAncestor(this.height - height);
if (err)
return callback(err);
if (!entry) if (!entry)
return callback(); return;
assert(entry.height === height); assert(entry.height === height);
callback(null, entry); return entry;
}); }, this);
});
}; };
/** /**
@ -273,17 +266,19 @@ ChainEntry.prototype.getAncestorByHeight = function getAncestorByHeight(height,
* @returns {Function} callback - Returns [Error, ChainEntry]. * @returns {Function} callback - Returns [Error, ChainEntry].
*/ */
ChainEntry.prototype.getAncestor = function getAncestor(index, callback) { ChainEntry.prototype.getAncestor = function getAncestor(index) {
assert(index >= 0); return spawn(function *() {
this.getAncestors(index + 1, function(err, ancestors) { var ancestors;
if (err)
return callback(err); assert(index >= 0);
ancestors = yield this.getAncestors(index + 1);
if (ancestors.length < index + 1) if (ancestors.length < index + 1)
return callback(); return;
callback(null, ancestors[index]); return ancestors[index];
}); }, this);
}; };
/** /**
@ -291,8 +286,8 @@ ChainEntry.prototype.getAncestor = function getAncestor(index, callback) {
* @param {Function} callback - Returns [Error, ChainEntry]. * @param {Function} callback - Returns [Error, ChainEntry].
*/ */
ChainEntry.prototype.getPrevious = function getPrevious(callback) { ChainEntry.prototype.getPrevious = function getPrevious() {
this.chain.db.get(this.prevBlock, callback); return this.chain.db.get(this.prevBlock);
}; };
/** /**
@ -300,17 +295,13 @@ ChainEntry.prototype.getPrevious = function getPrevious(callback) {
* @param {Function} callback - Returns [Error, ChainEntry]. * @param {Function} callback - Returns [Error, ChainEntry].
*/ */
ChainEntry.prototype.getNext = function getNext(callback) { ChainEntry.prototype.getNext = function getNext() {
var self = this; return spawn(function *() {
this.chain.db.getNextHash(this.hash, function(err, hash) { var hash = yield this.chain.db.getNextHash(this.hash);
if (err)
return callback(err);
if (!hash) if (!hash)
return callback(); return;
return yield this.chain.db.get(hash);
self.chain.db.get(hash, callback); }, this);
});
}; };
/** /**
@ -339,16 +330,12 @@ ChainEntry.prototype.getMedianTime = function getMedianTime(ancestors) {
* @param {Function} callback - Returns [Error, Number]. * @param {Function} callback - Returns [Error, Number].
*/ */
ChainEntry.prototype.getMedianTimeAsync = function getMedianTimeAsync(callback) { ChainEntry.prototype.getMedianTimeAsync = function getMedianTimeAsync() {
var self = this; return spawn(function *() {
var MEDIAN_TIMESPAN = constants.block.MEDIAN_TIMESPAN; var MEDIAN_TIMESPAN = constants.block.MEDIAN_TIMESPAN;
var ancestors = yield this.getAncestors(MEDIAN_TIMESPAN);
this.getAncestors(MEDIAN_TIMESPAN, function(err, ancestors) { return this.getMedianTime(ancestors);
if (err) }, this);
return callback(err);
callback(null, self.getMedianTime(ancestors));
});
}; };
/** /**
@ -359,7 +346,7 @@ ChainEntry.prototype.getMedianTimeAsync = function getMedianTimeAsync(callback)
*/ */
ChainEntry.prototype.isOutdated = function isOutdated(version, ancestors) { ChainEntry.prototype.isOutdated = function isOutdated(version, ancestors) {
this.isSuperMajority(version, return this.isSuperMajority(version,
this.network.block.majorityRejectOutdated, this.network.block.majorityRejectOutdated,
ancestors); ancestors);
}; };
@ -371,10 +358,9 @@ ChainEntry.prototype.isOutdated = function isOutdated(version, ancestors) {
* @returns {Boolean} * @returns {Boolean}
*/ */
ChainEntry.prototype.isOutdatedAsync = function isOutdatedAsync(version, callback) { ChainEntry.prototype.isOutdatedAsync = function isOutdatedAsync(version) {
this.isSuperMajorityAsync(version, return this.isSuperMajorityAsync(version,
this.network.block.majorityRejectOutdated, this.network.block.majorityRejectOutdated);
callback);
}; };
/** /**
@ -385,7 +371,7 @@ ChainEntry.prototype.isOutdatedAsync = function isOutdatedAsync(version, callbac
*/ */
ChainEntry.prototype.isUpgraded = function isUpgraded(version, ancestors) { ChainEntry.prototype.isUpgraded = function isUpgraded(version, ancestors) {
this.isSuperMajority(version, return this.isSuperMajority(version,
this.network.block.majorityEnforceUpgrade, this.network.block.majorityEnforceUpgrade,
ancestors); ancestors);
}; };
@ -397,10 +383,9 @@ ChainEntry.prototype.isUpgraded = function isUpgraded(version, ancestors) {
* @returns {Boolean} * @returns {Boolean}
*/ */
ChainEntry.prototype.isUpgradedAsync = function isUpgradedAsync(version, callback) { ChainEntry.prototype.isUpgradedAsync = function isUpgradedAsync(version) {
this.isSuperMajorityAsync(version, return this.isSuperMajorityAsync(version,
this.network.block.majorityEnforceUpgrade, this.network.block.majorityEnforceUpgrade);
callback);
}; };
/** /**
@ -434,16 +419,12 @@ ChainEntry.prototype.isSuperMajority = function isSuperMajority(version, require
* @returns {Boolean} * @returns {Boolean}
*/ */
ChainEntry.prototype.isSuperMajorityAsync = function isSuperMajorityAsync(version, required, callback) { ChainEntry.prototype.isSuperMajorityAsync = function isSuperMajorityAsync(version, required) {
var self = this; return spawn(function *() {
var majorityWindow = this.network.block.majorityWindow; var majorityWindow = this.network.block.majorityWindow;
var ancestors = yield this.getAncestors(majorityWindow);
this.getAncestors(majorityWindow, function(err, ancestors) { return this.isSuperMajority(version, required, ancestors);
if (err) }, this);
return callback(err);
callback(null, self.isSuperMajority(version, required, ancestors));
});
}; };
/** /**

View File

@ -12,6 +12,7 @@ var random = require('./random');
var scrypt = require('./scrypt'); var scrypt = require('./scrypt');
var scryptAsync = require('./scrypt-async'); var scryptAsync = require('./scrypt-async');
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var native = require('../utils/native'); var native = require('../utils/native');
var nativeCrypto, hash, aes; var nativeCrypto, hash, aes;
@ -174,7 +175,7 @@ crypto.pbkdf2 = function pbkdf2(key, salt, iter, len, alg) {
* @param {Function} callback * @param {Function} callback
*/ */
crypto.pbkdf2Async = function pbkdf2Async(key, salt, iter, len, alg, callback) { crypto.pbkdf2Async = function pbkdf2Async(key, salt, iter, len, alg) {
var result; var result;
if (typeof key === 'string') if (typeof key === 'string')
@ -183,16 +184,23 @@ crypto.pbkdf2Async = function pbkdf2Async(key, salt, iter, len, alg, callback) {
if (typeof salt === 'string') if (typeof salt === 'string')
salt = new Buffer(salt, 'utf8'); salt = new Buffer(salt, 'utf8');
if (nativeCrypto && nativeCrypto.pbkdf2) if (nativeCrypto && nativeCrypto.pbkdf2) {
return nativeCrypto.pbkdf2(key, salt, iter, len, alg, callback); return new Promise(function(resolve, reject) {
nativeCrypto.pbkdf2(key, salt, iter, len, alg, function(err, key) {
if (err)
return reject(err);
resolve(key);
});
});
}
try { try {
result = crypto._pbkdf2(key, salt, iter, len, alg); result = crypto._pbkdf2(key, salt, iter, len, alg);
} catch (e) { } catch (e) {
return callback(e); return Promise.reject(e);
} }
return callback(null, result); return Promise.resolve(result);
}; };
/** /**
@ -227,14 +235,20 @@ crypto.scrypt = function _scrypt(passwd, salt, N, r, p, len) {
* @param {Function} callback * @param {Function} callback
*/ */
crypto.scryptAsync = function _scrypt(passwd, salt, N, r, p, len, callback) { crypto.scryptAsync = function _scrypt(passwd, salt, N, r, p, len) {
if (typeof passwd === 'string') if (typeof passwd === 'string')
passwd = new Buffer(passwd, 'utf8'); passwd = new Buffer(passwd, 'utf8');
if (typeof salt === 'string') if (typeof salt === 'string')
salt = new Buffer(salt, 'utf8'); salt = new Buffer(salt, 'utf8');
return scryptAsync(passwd, salt, N, r, p, len, callback); return new Promise(function(resolve, reject) {
scryptAsync(passwd, salt, N, r, p, len, function(err, key) {
if (err)
return reject(err);
resolve(key);
});
});
}; };
/** /**
@ -243,8 +257,8 @@ crypto.scryptAsync = function _scrypt(passwd, salt, N, r, p, len, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
crypto.derive = function derive(passphrase, callback) { crypto.derive = function derive(passphrase) {
crypto.pbkdf2Async(passphrase, 'bcoin', 50000, 32, 'sha256', callback); return crypto.pbkdf2Async(passphrase, 'bcoin', 50000, 32, 'sha256');
}; };
/** /**
@ -255,25 +269,26 @@ crypto.derive = function derive(passphrase, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
crypto.encrypt = function encrypt(data, passphrase, iv, callback) { crypto.encrypt = function encrypt(data, passphrase, iv) {
assert(Buffer.isBuffer(data)); return spawn(function *() {
assert(passphrase, 'No passphrase.'); var key;
assert(Buffer.isBuffer(iv));
crypto.derive(passphrase, function(err, key) { assert(Buffer.isBuffer(data));
if (err) assert(passphrase, 'No passphrase.');
return callback(err); assert(Buffer.isBuffer(iv));
key = yield crypto.derive(passphrase);
try { try {
data = crypto.encipher(data, key, iv); data = crypto.encipher(data, key, iv);
} catch (e) { } catch (e) {
key.fill(0); key.fill(0);
return callback(e); throw e;
} }
key.fill(0); key.fill(0);
return callback(null, data); return data;
}); });
}; };
@ -307,22 +322,26 @@ crypto.encipher = function encipher(data, key, iv) {
* @param {Function} callback * @param {Function} callback
*/ */
crypto.decrypt = function decrypt(data, passphrase, iv, callback) { crypto.decrypt = function decrypt(data, passphrase, iv) {
assert(Buffer.isBuffer(data)); return spawn(function *() {
assert(passphrase, 'No passphrase.'); var key;
assert(Buffer.isBuffer(iv));
crypto.derive(passphrase, function(err, key) { assert(Buffer.isBuffer(data));
if (err) assert(passphrase, 'No passphrase.');
return callback(err); assert(Buffer.isBuffer(iv));
key = yield crypto.derive(passphrase);
try { try {
data = crypto.decipher(data, key, iv); data = crypto.decipher(data, key, iv);
} catch (e) { } catch (e) {
return callback(e); key.fill(0);
throw e;
} }
return callback(null, data, key); key.fill(0);
return data;
}); });
}; };

View File

@ -10,6 +10,8 @@
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var assert = utils.assert; var assert = utils.assert;
var AsyncObject = require('../utils/async'); var AsyncObject = require('../utils/async');
var spawn = require('../utils/spawn');
var P = utils.P;
var VERSION_ERROR; var VERSION_ERROR;
/** /**
@ -60,8 +62,11 @@ utils.inherits(LowlevelUp, AsyncObject);
* @param {Function} callback * @param {Function} callback
*/ */
LowlevelUp.prototype._open = function open(callback) { LowlevelUp.prototype._open = function open() {
this.binding.open(this.options, callback); var self = this;
return new Promise(function(resolve, reject) {
self.binding.open(self.options, P(resolve, reject));
});
}; };
/** /**
@ -70,8 +75,11 @@ LowlevelUp.prototype._open = function open(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
LowlevelUp.prototype._close = function close(callback) { LowlevelUp.prototype._close = function close() {
this.binding.close(callback); var self = this;
return new Promise(function(resolve, reject) {
self.binding.close(P(resolve, reject));
});
}; };
/** /**
@ -79,15 +87,18 @@ LowlevelUp.prototype._close = function close(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
LowlevelUp.prototype.destroy = function destroy(callback) { LowlevelUp.prototype.destroy = function destroy() {
var self = this;
assert(!this.loading); assert(!this.loading);
assert(!this.closing); assert(!this.closing);
assert(!this.loaded); assert(!this.loaded);
if (!this.backend.destroy) return new Promise(function(resolve, reject) {
return utils.asyncify(callback)(new Error('Cannot destroy.')); if (!self.backend.destroy)
return utils.asyncify(reject)(new Error('Cannot destroy.'));
this.backend.destroy(this.location, callback); self.backend.destroy(self.location, P(resolve, reject));
});
}; };
/** /**
@ -95,15 +106,18 @@ LowlevelUp.prototype.destroy = function destroy(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
LowlevelUp.prototype.repair = function repair(callback) { LowlevelUp.prototype.repair = function repair() {
var self = this;
assert(!this.loading); assert(!this.loading);
assert(!this.closing); assert(!this.closing);
assert(!this.loaded); assert(!this.loaded);
if (!this.backend.repair) return new Promise(function(resolve, reject) {
return utils.asyncify(callback)(new Error('Cannot repair.')); if (!self.backend.repair)
return utils.asyncify(reject)(new Error('Cannot repair.'));
this.backend.repair(this.location, callback); self.backend.repair(self.location, P(resolve, reject));
});
}; };
/** /**
@ -112,15 +126,19 @@ LowlevelUp.prototype.repair = function repair(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
LowlevelUp.prototype.backup = function backup(path, callback) { LowlevelUp.prototype.backup = function backup(path) {
var self = this;
assert(!this.loading); assert(!this.loading);
assert(!this.closing); assert(!this.closing);
assert(this.loaded); assert(this.loaded);
if (!this.binding.backup) if (!this.binding.backup)
return this.clone(path, callback); return this.clone(path);
this.binding.backup(path, callback); return new Promise(function(resolve, reject) {
self.binding.backup(path, P(resolve, reject));
});
}; };
/** /**
@ -130,21 +148,23 @@ LowlevelUp.prototype.backup = function backup(path, callback) {
* @param {Function} callback - Returns [Error, Buffer]. * @param {Function} callback - Returns [Error, Buffer].
*/ */
LowlevelUp.prototype.get = function get(key, options, callback) { LowlevelUp.prototype.get = function get(key, options) {
var self = this;
assert(this.loaded, 'Cannot use database before it is loaded.'); assert(this.loaded, 'Cannot use database before it is loaded.');
if (typeof options === 'function') { if (!options)
callback = options;
options = {}; options = {};
}
this.binding.get(key, options, function(err, result) { return new Promise(function(resolve, reject) {
if (err) { self.binding.get(key, options, function(err, result) {
if (isNotFound(err)) if (err) {
return callback(); if (isNotFound(err))
return callback(err); return resolve();
} return reject(err);
return callback(null, result); }
return resolve(result);
});
}); });
}; };
@ -156,9 +176,12 @@ LowlevelUp.prototype.get = function get(key, options, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
LowlevelUp.prototype.put = function put(key, value, options, callback) { LowlevelUp.prototype.put = function put(key, value, options) {
var self = this;
assert(this.loaded, 'Cannot use database before it is loaded.'); assert(this.loaded, 'Cannot use database before it is loaded.');
this.binding.put(key, value, options, callback); return new Promise(function(resolve, reject) {
self.binding.put(key, value, options || {}, P(resolve, reject));
});
}; };
/** /**
@ -168,9 +191,12 @@ LowlevelUp.prototype.put = function put(key, value, options, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
LowlevelUp.prototype.del = function del(key, options, callback) { LowlevelUp.prototype.del = function del(key, options) {
var self = this;
assert(this.loaded, 'Cannot use database before it is loaded.'); assert(this.loaded, 'Cannot use database before it is loaded.');
this.binding.del(key, options, callback); return new Promise(function(resolve, reject) {
self.binding.del(key, options || {}, P(resolve, reject));
});
}; };
/** /**
@ -181,13 +207,17 @@ LowlevelUp.prototype.del = function del(key, options, callback) {
* @returns {Leveldown.Batch} * @returns {Leveldown.Batch}
*/ */
LowlevelUp.prototype.batch = function batch(ops, options, callback) { LowlevelUp.prototype.batch = function batch(ops, options) {
var self = this;
assert(this.loaded, 'Cannot use database before it is loaded.'); assert(this.loaded, 'Cannot use database before it is loaded.');
if (!ops) if (!ops)
return this.binding.batch(); return new Batch(this);
this.binding.batch(ops, options, callback); return new Promise(function(resolve, reject) {
self.binding.batch(ops, options, P(resolve, reject));
});
}; };
/** /**
@ -198,7 +228,29 @@ LowlevelUp.prototype.batch = function batch(ops, options, callback) {
LowlevelUp.prototype.iterator = function iterator(options) { LowlevelUp.prototype.iterator = function iterator(options) {
assert(this.loaded, 'Cannot use database before it is loaded.'); assert(this.loaded, 'Cannot use database before it is loaded.');
return this.db.iterator(options);
var opt = {
gte: options.gte,
lte: options.lte,
keys: options.keys !== false,
values: options.values || false,
fillCache: options.fillCache || false,
keyAsBuffer: this.bufferKeys,
valueAsBuffer: true,
reverse: options.reverse || false
};
// Workaround for a leveldown
// bug I haven't fixed yet.
if (options.limit != null)
opt.limit = options.limit;
if (options.keyAsBuffer != null)
opt.keyAsBuffer = options.keyAsBuffer;
assert(opt.keys || opt.values, 'Keys and/or values must be chosen.');
return new Iterator(this, opt);
}; };
/** /**
@ -223,13 +275,16 @@ LowlevelUp.prototype.getProperty = function getProperty(name) {
* @param {Function} callback - Returns [Error, Number]. * @param {Function} callback - Returns [Error, Number].
*/ */
LowlevelUp.prototype.approximateSize = function approximateSize(start, end, callback) { LowlevelUp.prototype.approximateSize = function approximateSize(start, end) {
var self = this;
assert(this.loaded, 'Cannot use database before it is loaded.'); assert(this.loaded, 'Cannot use database before it is loaded.');
if (!this.binding.approximateSize) return new Promise(function(resolve, reject) {
return utils.asyncify(callback)(new Error('Cannot get size.')); if (!self.binding.approximateSize)
return utils.asyncify(reject)(new Error('Cannot get size.'));
this.binding.approximateSize(start, end, callback); self.binding.approximateSize(start, end, P(resolve, reject));
});
}; };
/** /**
@ -238,13 +293,11 @@ LowlevelUp.prototype.approximateSize = function approximateSize(start, end, call
* @param {Function} callback - Returns [Error, Boolean]. * @param {Function} callback - Returns [Error, Boolean].
*/ */
LowlevelUp.prototype.has = function has(key, callback) { LowlevelUp.prototype.has = function has(key) {
this.get(key, function(err, value) { return spawn(function *() {
if (err) var value = yield this.get(key);
return callback(err); return value != null;
}, this);
return callback(null, value != null);
});
}; };
/** /**
@ -255,101 +308,14 @@ LowlevelUp.prototype.has = function has(key, callback) {
* @param {Function} callback - Returns [Error, Object]. * @param {Function} callback - Returns [Error, Object].
*/ */
LowlevelUp.prototype.fetch = function fetch(key, parse, callback) { LowlevelUp.prototype.fetch = function fetch(key, parse) {
this.get(key, function(err, value) { return spawn(function *() {
if (err) var value = yield this.get(key);
return callback(err);
if (!value) if (!value)
return callback(); return;
try { return parse(value, key);
value = parse(value, key); }, this);
} catch (e) {
return callback(e);
}
return callback(null, value);
});
};
/**
* Iterate over each record.
* @param {Object} options
* @param {Function} handler
* @param {Function} callback - Returns [Error, Object].
*/
LowlevelUp.prototype.each = function each(options, handler, callback) {
var i = 0;
var opt, iter;
opt = {
gte: options.gte,
lte: options.lte,
keys: options.keys !== false,
values: options.values || false,
fillCache: options.fillCache || false,
keyAsBuffer: this.bufferKeys,
valueAsBuffer: true,
reverse: options.reverse || false
};
// Workaround for a leveldown
// bug I haven't fixed yet.
if (options.limit != null)
opt.limit = options.limit;
if (options.keyAsBuffer != null)
opt.keyAsBuffer = options.keyAsBuffer;
assert(opt.keys || opt.values, 'Keys and/or values must be chosen.');
iter = this.iterator(opt);
function next(err, key) {
if (err && typeof err !== 'boolean') {
return iter.end(function() {
callback(err);
});
}
if (err === false)
return iter.end(callback);
if (err === true) {
try {
iter.seek(key);
} catch (e) {
return iter.end(function() {
callback(e);
});
}
}
iter.next(onNext);
}
function onNext(err, key, value) {
if (err) {
return iter.end(function() {
callback(err);
});
}
if (key === undefined && value === undefined)
return iter.end(callback);
try {
handler(key, value, next, i++);
} catch (e) {
return iter.end(function() {
callback(e);
});
}
}
next();
}; };
/** /**
@ -358,19 +324,28 @@ LowlevelUp.prototype.each = function each(options, handler, callback) {
* @param {Function} callback - Returns [Error, Array]. * @param {Function} callback - Returns [Error, Array].
*/ */
LowlevelUp.prototype.iterate = function iterate(options, callback) { LowlevelUp.prototype.iterate = function iterate(options) {
var items = []; return spawn(function *() {
assert(typeof options.parse === 'function', 'Parse must be a function.'); var items = [];
this.each(options, function(key, value, next) { var iter, kv, result;
var result = options.parse(key, value);
if (result) assert(typeof options.parse === 'function', 'Parse must be a function.');
items.push(result);
next(); iter = this.iterator(options);
}, function(err) {
if (err) for (;;) {
return callback(err); kv = yield iter.next();
callback(null, items); if (!kv)
}); return items;
result = options.parse(kv[0], kv[1]);
if (result)
items.push(result);
}
return items;
}, this);
}; };
/** /**
@ -379,25 +354,22 @@ LowlevelUp.prototype.iterate = function iterate(options, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
LowlevelUp.prototype.checkVersion = function checkVersion(key, version, callback) { LowlevelUp.prototype.checkVersion = function checkVersion(key, version) {
var self = this; return spawn(function *() {
this.get(key, function(err, data) { var data = yield this.get(key);
if (err)
return callback(err);
if (!data) { if (!data) {
data = new Buffer(4); data = new Buffer(4);
data.writeUInt32LE(version, 0, true); data.writeUInt32LE(version, 0, true);
return self.put(key, data, callback); yield this.put(key, data);
return;
} }
data = data.readUInt32LE(0, true); data = data.readUInt32LE(0, true);
if (data !== version) if (data !== version)
return callback(new Error(VERSION_ERROR)); throw new Error(VERSION_ERROR);
}, this);
callback();
});
}; };
/** /**
@ -406,59 +378,133 @@ LowlevelUp.prototype.checkVersion = function checkVersion(key, version, callback
* @param {Function} callback * @param {Function} callback
*/ */
LowlevelUp.prototype.clone = function clone(path, callback) { LowlevelUp.prototype.clone = function clone(path) {
var self = this; return spawn(function *() {
var iter = { keys: true, values: true }; var opt = { keys: true, values: true };
var options = utils.merge({}, this.options); var options = utils.merge({}, this.options);
var hwm = 256 << 20; var hwm = 256 << 20;
var total = 0; var total = 0;
var tmp, batch; var tmp, batch, iter, items, key, value;
assert(!this.loading); assert(!this.loading);
assert(!this.closing); assert(!this.closing);
assert(this.loaded); assert(this.loaded);
options.createIfMissing = true; options.createIfMissing = true;
options.errorIfExists = true; options.errorIfExists = true;
tmp = new LowlevelUp(path, options); tmp = new LowlevelUp(path, options);
function done(err) { yield tmp.open();
tmp.close(function(e) {
if (e)
return callback(e);
callback(err);
});
}
tmp.open(function(err) {
if (err)
return callback(err);
batch = tmp.batch(); batch = tmp.batch();
iter = this.iterator(opt);
for (;;) {
items = yield iter.next();
if (!items) {
try {
yield batch.write();
} catch (e) {
yield tmp.close();
throw e;
}
return;
}
key = items[0];
value = items[0];
self.each(iter, function(key, value, next) {
batch.put(key, value); batch.put(key, value);
total += value.length; total += value.length;
if (total >= hwm) { if (total >= hwm) {
total = 0; total = 0;
batch.write(function(err) { try {
if (err) yield batch.write();
return next(err); } catch (e) {
batch = tmp.batch(); yield tmp.close();
next(); throw e;
}
batch = tmp.batch();
}
}
}, this);
};
function Batch(db) {
this.db = db;
this.batch = db.binding.batch();
}
Batch.prototype.put = function(key, value) {
this.batch.put(key, value);
return this;
};
Batch.prototype.del = function del(key) {
this.batch.del(key);
return this;
};
Batch.prototype.write = function write() {
var self = this;
return new Promise(function(resolve, reject) {
self.batch.write(function(err) {
if (err)
return reject(err);
resolve();
});
});
};
Batch.prototype.clear = function clear() {
this.batch.clear();
return this;
};
function Iterator(db, options) {
this.db = db;
this.iter = db.db.iterator(options);
}
Iterator.prototype.next = function() {
var self = this;
return new Promise(function(resolve, reject) {
self.iter.next(function(err, key, value) {
if (err) {
self.iter.end(function() {
reject(err);
}); });
return; return;
} }
next(); if (key === undefined && value === undefined) {
}, function(err) { self.iter.end(function(err) {
if (err) if (err)
return done(err); return reject(err);
resolve();
});
return;
}
batch.write(done); resolve([key, value]);
});
});
};
Iterator.prototype.seek = function seek(key) {
this.iter.seek(key);
};
Iterator.prototype.end = function end() {
var self = this;
return new Promise(function(resolve, reject) {
self.iter.end(function(err) {
if (err)
return reject(err);
resolve();
}); });
}); });
}; };

View File

@ -211,9 +211,9 @@ HTTPBase.prototype._initIO = function _initIO() {
* @param {Function} callback * @param {Function} callback
*/ */
HTTPBase.prototype._open = function open(callback) { HTTPBase.prototype._open = function open() {
assert(typeof this.options.port === 'number', 'Port required.'); assert(typeof this.options.port === 'number', 'Port required.');
this.listen(this.options.port, this.options.host, callback); this.listen(this.options.port, this.options.host);
}; };
/** /**
@ -223,13 +223,21 @@ HTTPBase.prototype._open = function open(callback) {
*/ */
HTTPBase.prototype._close = function close(callback) { HTTPBase.prototype._close = function close(callback) {
if (this.io) { var self = this;
this.server.once('close', callback);
this.io.close();
return;
}
this.server.close(callback); return new Promise(function(resolve, reject) {
if (self.io) {
self.server.once('close', resolve);
self.io.close();
return;
}
self.server.close(function(err) {
if (err)
return reject(err);
resolve();
});
});
}; };
/** /**
@ -379,23 +387,21 @@ HTTPBase.prototype.address = function address() {
* @param {Function} callback * @param {Function} callback
*/ */
HTTPBase.prototype.listen = function listen(port, host, callback) { HTTPBase.prototype.listen = function listen(port, host) {
var self = this; var self = this;
var addr; return new Promise(function(resolve, reject) {
var addr;
this.server.listen(port, host, function(err) { self.server.listen(port, host, function(err) {
if (err) { if (err)
if (callback) return reject(err);
return callback(err);
throw err;
}
addr = self.address(); addr = self.address();
self.emit('listening', addr); self.emit('listening', addr);
if (callback) resolve(addr);
callback(null, addr); });
}); });
}; };

View File

@ -11,8 +11,9 @@ var Network = require('../protocol/network');
var AsyncObject = require('../utils/async'); var AsyncObject = require('../utils/async');
var RPCClient = require('./rpcclient'); var RPCClient = require('./rpcclient');
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var assert = utils.assert; var assert = utils.assert;
var request = require('./request'); var request = require('./request').promise;
/** /**
* BCoin HTTP client. * BCoin HTTP client.
@ -44,7 +45,7 @@ function HTTPClient(options) {
this.rpc = new RPCClient(options); this.rpc = new RPCClient(options);
// Open automatically. // Open automatically.
this.open(); // this.open();
} }
utils.inherits(HTTPClient, AsyncObject); utils.inherits(HTTPClient, AsyncObject);
@ -55,71 +56,86 @@ utils.inherits(HTTPClient, AsyncObject);
* @param {Function} callback * @param {Function} callback
*/ */
HTTPClient.prototype._open = function _open(callback) { HTTPClient.prototype._open = function _open() {
var self = this; return spawn(function *() {
var IOClient; var self = this;
var IOClient;
try { try {
IOClient = require('socket.io-client'); IOClient = require('socket.io-client');
} catch (e) { } catch (e) {
; ;
} }
if (!IOClient) if (!IOClient)
return callback(); return;
this.socket = new IOClient(this.uri, { this.socket = new IOClient(this.uri, {
transports: ['websocket'], transports: ['websocket'],
forceNew: true forceNew: true
});
this.socket.on('error', function(err) {
self.emit('error', err);
});
this.socket.on('version', function(info) {
if (info.network !== self.network.type)
self.emit('error', new Error('Wrong network.'));
});
this.socket.on('wallet tx', function(details) {
self.emit('tx', details);
});
this.socket.on('wallet confirmed', function(details) {
self.emit('confirmed', details);
});
this.socket.on('wallet unconfirmed', function(details) {
self.emit('unconfirmed', details);
});
this.socket.on('wallet conflict', function(details) {
self.emit('conflict', details);
});
this.socket.on('wallet updated', function(details) {
self.emit('updated', details);
});
this.socket.on('wallet address', function(receive) {
self.emit('address', receive);
});
this.socket.on('wallet balance', function(balance) {
self.emit('balance', {
id: balance.id,
confirmed: utils.satoshi(balance.confirmed),
unconfirmed: utils.satoshi(balance.unconfirmed),
total: utils.satoshi(balance.total)
}); });
});
this.socket.on('connect', function() { this.socket.on('error', function(err) {
self.emit('error', err);
});
this.socket.on('version', function(info) {
if (info.network !== self.network.type)
self.emit('error', new Error('Wrong network.'));
});
this.socket.on('wallet tx', function(details) {
self.emit('tx', details);
});
this.socket.on('wallet confirmed', function(details) {
self.emit('confirmed', details);
});
this.socket.on('wallet unconfirmed', function(details) {
self.emit('unconfirmed', details);
});
this.socket.on('wallet conflict', function(details) {
self.emit('conflict', details);
});
this.socket.on('wallet updated', function(details) {
self.emit('updated', details);
});
this.socket.on('wallet address', function(receive) {
self.emit('address', receive);
});
this.socket.on('wallet balance', function(balance) {
self.emit('balance', {
id: balance.id,
confirmed: utils.satoshi(balance.confirmed),
unconfirmed: utils.satoshi(balance.unconfirmed),
total: utils.satoshi(balance.total)
});
});
yield this._onConnect();
yield this._sendAuth();
}, this);
};
HTTPClient.prototype._onConnect = function _onConnect() {
var self = this;
return new Promise(function(resolve, reject) {
self.socket.once('connect', resolve);
});
};
HTTPClient.prototype._sendAuth = function _sendAuth() {
var self = this;
return new Promise(function(resolve, reject) {
self.socket.emit('auth', self.apiKey, function(err) { self.socket.emit('auth', self.apiKey, function(err) {
if (err) if (err)
return callback(new Error(err.error)); return reject(new Error(err.error));
callback(); resolve();
}); });
}); });
}; };
@ -130,14 +146,14 @@ HTTPClient.prototype._open = function _open(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
HTTPClient.prototype._close = function close(callback) { HTTPClient.prototype._close = function close() {
if (!this.socket) if (!this.socket)
return utils.nextTick(callback); return Promise.resolve(null);
this.socket.disconnect(); this.socket.disconnect();
this.socket = null; this.socket = null;
utils.nextTick(callback); return Promise.resolve(null);
}; };
/** /**
@ -149,68 +165,57 @@ HTTPClient.prototype._close = function close(callback) {
* @param {Function} callback - Returns [Error, Object?]. * @param {Function} callback - Returns [Error, Object?].
*/ */
HTTPClient.prototype._request = function _request(method, endpoint, json, callback) { HTTPClient.prototype._request = function _request(method, endpoint, json) {
var self = this; return spawn(function *() {
var query, network, height; var query, network, height, res;
if (!callback) { if (this.token) {
callback = json; if (!json)
json = null; json = {};
} json.token = this.token;
}
if (this.token) { if (json && method === 'get') {
if (!json) query = json;
json = {}; json = null;
json.token = this.token; }
}
if (json && method === 'get') { res = yield request({
query = json; method: method,
json = null; uri: this.uri + endpoint,
} query: query,
json: json,
request({ auth: {
method: method, username: 'bitcoinrpc',
uri: this.uri + endpoint, password: this.apiKey || ''
query: query, },
json: json, expect: 'json'
auth: { });
username: 'bitcoinrpc',
password: this.apiKey || ''
},
expect: 'json'
}, function(err, res, body) {
if (err)
return callback(err);
network = res.headers['x-bcoin-network']; network = res.headers['x-bcoin-network'];
if (network !== self.network.type) if (network !== this.network.type)
return callback(new Error('Wrong network.')); throw new Error('Wrong network.');
height = +res.headers['x-bcoin-height']; height = +res.headers['x-bcoin-height'];
if (utils.isNumber(height)) if (utils.isNumber(height))
self.network.updateHeight(height); this.network.updateHeight(height);
if (res.statusCode === 404) if (res.statusCode === 404)
return callback(); return;
if (!body) if (!res.body)
return callback(new Error('No body.')); throw new Error('No body.');
if (res.statusCode !== 200) { if (res.statusCode !== 200) {
if (body.error) if (res.body.error)
return callback(new Error(body.error)); throw new Error(res.body.error);
return callback(new Error('Status code: ' + res.statusCode)); throw new Error('Status code: ' + res.statusCode);
} }
try { return res.body;
return callback(null, body); }, this);
} catch (e) {
return callback(e);
}
});
}; };
/** /**
@ -221,8 +226,8 @@ HTTPClient.prototype._request = function _request(method, endpoint, json, callba
* @param {Function} callback - Returns [Error, Object?]. * @param {Function} callback - Returns [Error, Object?].
*/ */
HTTPClient.prototype._get = function _get(endpoint, json, callback) { HTTPClient.prototype._get = function _get(endpoint, json) {
this._request('get', endpoint, json, callback); return this._request('get', endpoint, json);
}; };
/** /**
@ -233,8 +238,8 @@ HTTPClient.prototype._get = function _get(endpoint, json, callback) {
* @param {Function} callback - Returns [Error, Object?]. * @param {Function} callback - Returns [Error, Object?].
*/ */
HTTPClient.prototype._post = function _post(endpoint, json, callback) { HTTPClient.prototype._post = function _post(endpoint, json) {
this._request('post', endpoint, json, callback); return this._request('post', endpoint, json);
}; };
/** /**
@ -245,8 +250,8 @@ HTTPClient.prototype._post = function _post(endpoint, json, callback) {
* @param {Function} callback - Returns [Error, Object?]. * @param {Function} callback - Returns [Error, Object?].
*/ */
HTTPClient.prototype._put = function _put(endpoint, json, callback) { HTTPClient.prototype._put = function _put(endpoint, json) {
this._request('put', endpoint, json, callback); return this._request('put', endpoint, json);
}; };
/** /**
@ -257,8 +262,8 @@ HTTPClient.prototype._put = function _put(endpoint, json, callback) {
* @param {Function} callback - Returns [Error, Object?]. * @param {Function} callback - Returns [Error, Object?].
*/ */
HTTPClient.prototype._del = function _del(endpoint, json, callback) { HTTPClient.prototype._del = function _del(endpoint, json) {
this._request('delete', endpoint, json, callback); return this._request('delete', endpoint, json);
}; };
/** /**
@ -266,8 +271,8 @@ HTTPClient.prototype._del = function _del(endpoint, json, callback) {
* @param {Function} callback - Returns [Error, {@link TX}[]]. * @param {Function} callback - Returns [Error, {@link TX}[]].
*/ */
HTTPClient.prototype.getMempool = function getMempool(callback) { HTTPClient.prototype.getMempool = function getMempool() {
this._get('/mempool', callback); return this._get('/mempool');
}; };
/** /**
@ -275,8 +280,8 @@ HTTPClient.prototype.getMempool = function getMempool(callback) {
* @param {Function} callback - Returns [Error, Object]. * @param {Function} callback - Returns [Error, Object].
*/ */
HTTPClient.prototype.getInfo = function getInfo(callback) { HTTPClient.prototype.getInfo = function getInfo() {
this._get('/', callback); return this._get('/');
}; };
/** /**
@ -286,9 +291,9 @@ HTTPClient.prototype.getInfo = function getInfo(callback) {
* @param {Function} callback - Returns [Error, {@link Coin}[]]. * @param {Function} callback - Returns [Error, {@link Coin}[]].
*/ */
HTTPClient.prototype.getCoinsByAddress = function getCoinsByAddress(address, callback) { HTTPClient.prototype.getCoinsByAddress = function getCoinsByAddress(address) {
var body = { address: address }; var body = { address: address };
this._post('/coin/address', body, callback); return this._post('/coin/address', body);
}; };
/** /**
@ -299,8 +304,8 @@ HTTPClient.prototype.getCoinsByAddress = function getCoinsByAddress(address, cal
* @param {Function} callback - Returns [Error, {@link Coin}]. * @param {Function} callback - Returns [Error, {@link Coin}].
*/ */
HTTPClient.prototype.getCoin = function getCoin(hash, index, callback) { HTTPClient.prototype.getCoin = function getCoin(hash, index) {
this._get('/coin/' + hash + '/' + index, callback); return this._get('/coin/' + hash + '/' + index);
}; };
/** /**
@ -310,10 +315,9 @@ HTTPClient.prototype.getCoin = function getCoin(hash, index, callback) {
* @param {Function} callback - Returns [Error, {@link TX}[]]. * @param {Function} callback - Returns [Error, {@link TX}[]].
*/ */
HTTPClient.prototype.getTXByAddress = function getTXByAddress(address, callback) { HTTPClient.prototype.getTXByAddress = function getTXByAddress(address) {
var body = { address: address }; var body = { address: address };
return this._post('/tx/address', body);
this._post('/tx/address', body, callback);
}; };
/** /**
@ -322,8 +326,8 @@ HTTPClient.prototype.getTXByAddress = function getTXByAddress(address, callback)
* @param {Function} callback - Returns [Error, {@link TX}]. * @param {Function} callback - Returns [Error, {@link TX}].
*/ */
HTTPClient.prototype.getTX = function getTX(hash, callback) { HTTPClient.prototype.getTX = function getTX(hash) {
this._get('/tx/' + hash, callback); return this._get('/tx/' + hash);
}; };
/** /**
@ -332,8 +336,8 @@ HTTPClient.prototype.getTX = function getTX(hash, callback) {
* @param {Function} callback - Returns [Error, {@link Block}]. * @param {Function} callback - Returns [Error, {@link Block}].
*/ */
HTTPClient.prototype.getBlock = function getBlock(hash, callback) { HTTPClient.prototype.getBlock = function getBlock(hash) {
this._get('/block/' + hash, callback); return this._get('/block/' + hash);
}; };
/** /**
@ -342,10 +346,10 @@ HTTPClient.prototype.getBlock = function getBlock(hash, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
HTTPClient.prototype.broadcast = function broadcast(tx, callback) { HTTPClient.prototype.broadcast = function broadcast(tx) {
var body = { tx: toHex(tx) }; var body = { tx: toHex(tx) };
this._post('/broadcast', body, callback); return this._post('/broadcast', body);
}; };
/** /**
@ -353,11 +357,19 @@ HTTPClient.prototype.broadcast = function broadcast(tx, callback) {
* @param {WalletID} id * @param {WalletID} id
*/ */
HTTPClient.prototype.join = function join(id, token, callback) { HTTPClient.prototype.join = function join(id, token) {
if (!this.socket) var self = this;
return callback();
this.socket.emit('wallet join', id, token, callback); if (!this.socket)
return Promise.resolve(null);
return new Promise(function(resolve, reject) {
self.socket.emit('wallet join', id, token, function(err) {
if (err)
return reject(new Error(err.error));
resolve();
});
});
}; };
/** /**
@ -365,27 +377,35 @@ HTTPClient.prototype.join = function join(id, token, callback) {
* @param {WalletID} id * @param {WalletID} id
*/ */
HTTPClient.prototype.leave = function leave(id, callback) { HTTPClient.prototype.leave = function leave(id) {
if (!this.socket) var self = this;
return callback();
this.socket.emit('wallet leave', id, callback); if (!this.socket)
return Promise.resolve(null);
return new Promise(function(resolve, reject) {
self.socket.emit('wallet leave', id, function(err) {
if (err)
return reject(new Error(err.error));
resolve();
});
});
}; };
/** /**
* Listen for events on all wallets. * Listen for events on all wallets.
*/ */
HTTPClient.prototype.all = function all(token, callback) { HTTPClient.prototype.all = function all(token) {
this.join('!all', token, callback); return this.join('!all', token);
}; };
/** /**
* Unlisten for events on all wallets. * Unlisten for events on all wallets.
*/ */
HTTPClient.prototype.none = function none(callback) { HTTPClient.prototype.none = function none() {
this.leave('!all', callback); return this.leave('!all');
}; };
/** /**
@ -395,8 +415,8 @@ HTTPClient.prototype.none = function none(callback) {
* @param {Function} callback - Returns [Error, Object]. * @param {Function} callback - Returns [Error, Object].
*/ */
HTTPClient.prototype.createWallet = function createWallet(options, callback) { HTTPClient.prototype.createWallet = function createWallet(options) {
this._post('/wallet', options, callback); return this._post('/wallet', options);
}; };
/** /**
@ -406,8 +426,8 @@ HTTPClient.prototype.createWallet = function createWallet(options, callback) {
* @param {Function} callback - Returns [Error, Object]. * @param {Function} callback - Returns [Error, Object].
*/ */
HTTPClient.prototype.getWallet = function getWallet(id, callback) { HTTPClient.prototype.getWallet = function getWallet(id) {
this._get('/wallet/' + id, callback); return this._get('/wallet/' + id);
}; };
/** /**
@ -416,17 +436,9 @@ HTTPClient.prototype.getWallet = function getWallet(id, callback) {
* @param {Function} callback - Returns [Error, {@link TX}[]]. * @param {Function} callback - Returns [Error, {@link TX}[]].
*/ */
HTTPClient.prototype.getHistory = function getHistory(id, account, callback) { HTTPClient.prototype.getHistory = function getHistory(id, account) {
var options; var options = { account: account };
return this._get('/wallet/' + id + '/tx/history', options);
if (typeof account === 'function') {
callback = account;
account = null;
}
options = { account: account };
this._get('/wallet/' + id + '/tx/history', options, callback);
}; };
/** /**
@ -435,17 +447,9 @@ HTTPClient.prototype.getHistory = function getHistory(id, account, callback) {
* @param {Function} callback - Returns [Error, {@link Coin}[]]. * @param {Function} callback - Returns [Error, {@link Coin}[]].
*/ */
HTTPClient.prototype.getCoins = function getCoins(id, account, callback) { HTTPClient.prototype.getCoins = function getCoins(id, account) {
var options; var options = { account: account };
return this._get('/wallet/' + id + '/coin', options);
if (typeof account === 'function') {
callback = account;
account = null;
}
options = { account: account };
this._get('/wallet/' + id + '/coin', options, callback);
}; };
/** /**
@ -454,17 +458,9 @@ HTTPClient.prototype.getCoins = function getCoins(id, account, callback) {
* @param {Function} callback - Returns [Error, {@link TX}[]]. * @param {Function} callback - Returns [Error, {@link TX}[]].
*/ */
HTTPClient.prototype.getUnconfirmed = function getUnconfirmed(id, account, callback) { HTTPClient.prototype.getUnconfirmed = function getUnconfirmed(id, account) {
var options; var options = { account: account };
return this._get('/wallet/' + id + '/tx/unconfirmed', options);
if (typeof account === 'function') {
callback = account;
account = null;
}
options = { account: account };
this._get('/wallet/' + id + '/tx/unconfirmed', options, callback);
}; };
/** /**
@ -473,17 +469,9 @@ HTTPClient.prototype.getUnconfirmed = function getUnconfirmed(id, account, callb
* @param {Function} callback - Returns [Error, {@link Balance}]. * @param {Function} callback - Returns [Error, {@link Balance}].
*/ */
HTTPClient.prototype.getBalance = function getBalance(id, account, callback) { HTTPClient.prototype.getBalance = function getBalance(id, account) {
var options; var options = { account: account };
return this._get('/wallet/' + id + '/balance', options);
if (typeof account === 'function') {
callback = account;
account = null;
}
options = { account: account };
this._get('/wallet/' + id + '/balance', options, callback);
}; };
/** /**
@ -493,17 +481,9 @@ HTTPClient.prototype.getBalance = function getBalance(id, account, callback) {
* @param {Function} callback - Returns [Error, {@link TX}[]]. * @param {Function} callback - Returns [Error, {@link TX}[]].
*/ */
HTTPClient.prototype.getLast = function getLast(id, account, limit, callback) { HTTPClient.prototype.getLast = function getLast(id, account, limit) {
var options; var options = { account: account, limit: limit };
return this._get('/wallet/' + id + '/tx/last', options);
if (typeof account === 'function') {
callback = account;
account = null;
}
options = { account: account, limit: limit };
this._get('/wallet/' + id + '/tx/last', options, callback);
}; };
/** /**
@ -517,13 +497,7 @@ HTTPClient.prototype.getLast = function getLast(id, account, limit, callback) {
* @param {Function} callback - Returns [Error, {@link TX}[]]. * @param {Function} callback - Returns [Error, {@link TX}[]].
*/ */
HTTPClient.prototype.getRange = function getRange(id, account, options, callback) { HTTPClient.prototype.getRange = function getRange(id, account, options) {
if (typeof options === 'function') {
callback = options;
options = account;
account = null;
}
options = { options = {
account: account, account: account,
start: options.start, start: options.start,
@ -531,8 +505,7 @@ HTTPClient.prototype.getRange = function getRange(id, account, options, callback
limit: options.limit, limit: options.limit,
reverse: options.reverse reverse: options.reverse
}; };
return this._get('/wallet/' + id + '/tx/range', options);
this._get('/wallet/' + id + '/tx/range', options, callback);
}; };
/** /**
@ -543,18 +516,9 @@ HTTPClient.prototype.getRange = function getRange(id, account, options, callback
* @param {Function} callback - Returns [Error, {@link TX}[]]. * @param {Function} callback - Returns [Error, {@link TX}[]].
*/ */
HTTPClient.prototype.getWalletTX = function getWalletTX(id, account, hash, callback) { HTTPClient.prototype.getWalletTX = function getWalletTX(id, account, hash) {
var options; var options = { account: account };
return this._get('/wallet/' + id + '/tx/' + hash, options);
if (typeof hash === 'function') {
callback = hash;
hash = account;
account = null;
}
options = { account: account };
this._get('/wallet/' + id + '/tx/' + hash, options, callback);
}; };
/** /**
@ -566,21 +530,10 @@ HTTPClient.prototype.getWalletTX = function getWalletTX(id, account, hash, callb
* @param {Function} callback - Returns [Error, {@link Coin}[]]. * @param {Function} callback - Returns [Error, {@link Coin}[]].
*/ */
HTTPClient.prototype.getWalletCoin = function getWalletCoin(id, account, hash, index, callback) { HTTPClient.prototype.getWalletCoin = function getWalletCoin(id, account, hash, index) {
var options, path; var path = '/wallet/' + id + '/coin/' + hash + '/' + index;
var options = { account: account };
if (typeof hash === 'function') { return this._get(path, options);
callback = index;
index = hash;
hash = account;
account = null;
}
options = { account: account };
path = '/wallet/' + id + '/coin/' + hash + '/' + index;
this._get(path, options, callback);
}; };
/** /**
@ -592,7 +545,7 @@ HTTPClient.prototype.getWalletCoin = function getWalletCoin(id, account, hash, i
* @param {Function} callback - Returns [Error, {@link TX}]. * @param {Function} callback - Returns [Error, {@link TX}].
*/ */
HTTPClient.prototype.send = function send(id, options, callback) { HTTPClient.prototype.send = function send(id, options) {
options = utils.merge({}, options); options = utils.merge({}, options);
options.outputs = options.outputs || []; options.outputs = options.outputs || [];
@ -607,7 +560,7 @@ HTTPClient.prototype.send = function send(id, options, callback) {
}; };
}); });
this._post('/wallet/' + id + '/send', options, callback); return this._post('/wallet/' + id + '/send', options);
}; };
/** /**
@ -616,22 +569,12 @@ HTTPClient.prototype.send = function send(id, options, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
HTTPClient.prototype.retoken = function retoken(id, passphrase, callback) { HTTPClient.prototype.retoken = function retoken(id, passphrase) {
var options; return spawn(function *() {
var options = { passphrase: passphrase };
if (typeof passphrase === 'function') { var body = yield this._post('/wallet/' + id + '/retoken', options);
callback = passphrase; return body.token;
passphrase = null; }, this);
}
options = { passphrase: passphrase };
this._post('/wallet/' + id + '/retoken', options, function(err, body) {
if (err)
return callback(err);
return callback(null, body.token);
});
}; };
/** /**
@ -641,10 +584,9 @@ HTTPClient.prototype.retoken = function retoken(id, passphrase, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
HTTPClient.prototype.setPassphrase = function setPassphrase(id, old, new_, callback) { HTTPClient.prototype.setPassphrase = function setPassphrase(id, old, new_) {
var options = { old: old, passphrase: new_ }; var options = { old: old, passphrase: new_ };
return this._post('/wallet/' + id + '/passphrase', options);
this._post('/wallet/' + id + '/passphrase', options, callback);
}; };
/** /**
@ -654,7 +596,7 @@ HTTPClient.prototype.setPassphrase = function setPassphrase(id, old, new_, callb
* @param {Function} callback - Returns [Error, {@link TX}]. * @param {Function} callback - Returns [Error, {@link TX}].
*/ */
HTTPClient.prototype.createTX = function createTX(id, options, callback) { HTTPClient.prototype.createTX = function createTX(id, options) {
options = utils.merge({}, options); options = utils.merge({}, options);
if (options.rate) if (options.rate)
@ -668,7 +610,7 @@ HTTPClient.prototype.createTX = function createTX(id, options, callback) {
}; };
}); });
this._post('/wallet/' + id + '/create', options, callback); return this._post('/wallet/' + id + '/create', options);
}; };
/** /**
@ -679,21 +621,16 @@ HTTPClient.prototype.createTX = function createTX(id, options, callback) {
* @param {Function} callback - Returns [Error, {@link TX}]. * @param {Function} callback - Returns [Error, {@link TX}].
*/ */
HTTPClient.prototype.sign = function sign(id, tx, options, callback) { HTTPClient.prototype.sign = function sign(id, tx, options) {
var body; var body;
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options) if (!options)
options = {}; options = {};
body = utils.merge({}, options); body = utils.merge({}, options);
body.tx = toHex(tx); body.tx = toHex(tx);
this._post('/wallet/' + id + '/sign', body, callback); return this._post('/wallet/' + id + '/sign', body);
}; };
/** /**
@ -702,9 +639,9 @@ HTTPClient.prototype.sign = function sign(id, tx, options, callback) {
* @param {Function} callback - Returns [Error, {@link TX}]. * @param {Function} callback - Returns [Error, {@link TX}].
*/ */
HTTPClient.prototype.fillCoins = function fillCoins(id, tx, callback) { HTTPClient.prototype.fillCoins = function fillCoins(id, tx) {
var body = { tx: toHex(tx) }; var body = { tx: toHex(tx) };
this._post('/wallet/' + id + '/fill', body, callback); return this._post('/wallet/' + id + '/fill', body);
}; };
/** /**
@ -714,23 +651,13 @@ HTTPClient.prototype.fillCoins = function fillCoins(id, tx, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
HTTPClient.prototype.zap = function zap(id, account, age, callback) { HTTPClient.prototype.zap = function zap(id, account, age) {
var body; var body = {
if (typeof age === 'function') {
callback = age;
age = account;
account = null;
}
body = {
account: account, account: account,
age: age age: age
}; };
assert(utils.isNumber(age)); assert(utils.isNumber(age));
return this._post('/wallet/' + id + '/zap', body);
this._post('/wallet/' + id + '/zap', body, callback);
}; };
/** /**
@ -742,19 +669,13 @@ HTTPClient.prototype.zap = function zap(id, account, age, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
HTTPClient.prototype.addKey = function addKey(id, account, key, callback) { HTTPClient.prototype.addKey = function addKey(id, account, key) {
var options; var options;
if (typeof key === 'function') {
callback = key;
key = account;
account = null;
}
key = key.xpubkey || key; key = key.xpubkey || key;
options = { account: account, key: key }; options = { account: account, key: key };
this._put('/wallet/' + id + '/key', options, callback); return this._put('/wallet/' + id + '/key', options);
}; };
/** /**
@ -766,19 +687,13 @@ HTTPClient.prototype.addKey = function addKey(id, account, key, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
HTTPClient.prototype.removeKey = function removeKey(id, account, key, callback) { HTTPClient.prototype.removeKey = function removeKey(id, account, key) {
var options; var options;
if (typeof key === 'function') {
callback = key;
key = account;
account = null;
}
key = key.xpubkey || key; key = key.xpubkey || key;
options = { account: account, key: key }; options = { account: account, key: key };
this._del('/wallet/' + id + '/key', options, callback); return this._del('/wallet/' + id + '/key', options);
}; };
/** /**
@ -787,9 +702,9 @@ HTTPClient.prototype.removeKey = function removeKey(id, account, key, callback)
* @param {Function} callback - Returns [Error, Array]. * @param {Function} callback - Returns [Error, Array].
*/ */
HTTPClient.prototype.getAccounts = function getAccounts(id, callback) { HTTPClient.prototype.getAccounts = function getAccounts(id) {
var path = '/wallet/' + id + '/account'; var path = '/wallet/' + id + '/account';
this._get(path, callback); return this._get(path);
}; };
/** /**
@ -799,9 +714,9 @@ HTTPClient.prototype.getAccounts = function getAccounts(id, callback) {
* @param {Function} callback - Returns [Error, Array]. * @param {Function} callback - Returns [Error, Array].
*/ */
HTTPClient.prototype.getAccount = function getAccount(id, account, callback) { HTTPClient.prototype.getAccount = function getAccount(id, account) {
var path = '/wallet/' + id + '/account/' + account; var path = '/wallet/' + id + '/account/' + account;
this._get(path, callback); return this._get(path);
}; };
/** /**
@ -811,14 +726,9 @@ HTTPClient.prototype.getAccount = function getAccount(id, account, callback) {
* @param {Function} callback - Returns [Error, Array]. * @param {Function} callback - Returns [Error, Array].
*/ */
HTTPClient.prototype.createAccount = function createAccount(id, options, callback) { HTTPClient.prototype.createAccount = function createAccount(id, options) {
var path; var path;
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options) if (!options)
options = {}; options = {};
@ -827,7 +737,7 @@ HTTPClient.prototype.createAccount = function createAccount(id, options, callbac
path = '/wallet/' + id + '/account'; path = '/wallet/' + id + '/account';
this._post(path, options, callback); return this._post(path, options);
}; };
/** /**
@ -837,14 +747,9 @@ HTTPClient.prototype.createAccount = function createAccount(id, options, callbac
* @param {Function} callback - Returns [Error, Array]. * @param {Function} callback - Returns [Error, Array].
*/ */
HTTPClient.prototype.createAddress = function createAddress(id, options, callback) { HTTPClient.prototype.createAddress = function createAddress(id, options) {
var path; var path;
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options) if (!options)
options = {}; options = {};
@ -853,7 +758,7 @@ HTTPClient.prototype.createAddress = function createAddress(id, options, callbac
path = '/wallet/' + id + '/address'; path = '/wallet/' + id + '/address';
this._post(path, options, callback); return this._post(path, options);
}; };
/* /*

View File

@ -301,6 +301,17 @@ request._buffer = function(options, callback) {
return stream; return stream;
}; };
request.promise = function promise(options) {
return new Promise(function(resolve, reject) {
request(options, function(err, res, body) {
if (err)
return reject(err);
res.body = body;
resolve(res);
});
});
};
/* /*
* ReqStream * ReqStream
*/ */

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@ var constants = bcoin.constants;
var http = require('./'); var http = require('./');
var HTTPBase = http.base; var HTTPBase = http.base;
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var crypto = require('../crypto/crypto'); var crypto = require('../crypto/crypto');
var assert = utils.assert; var assert = utils.assert;
var RPC; /*= require('./rpc'); - load lazily */ var RPC; /*= require('./rpc'); - load lazily */
@ -373,49 +374,65 @@ HTTPServer.prototype._init = function _init() {
}); });
this.use(function(req, res, next, send) { this.use(function(req, res, next, send) {
if (req.path.length < 2 || req.path[0] !== 'wallet') spawn(function *() {
return next(); var wallet;
if (!self.options.walletAuth) { if (req.path.length < 2 || req.path[0] !== 'wallet')
return self.walletdb.get(req.options.id, function(err, wallet) { return next();
if (err)
return next(err);
if (!wallet) if (!self.options.walletAuth) {
return send(404); wallet = yield self.walletdb.get(req.options.id);
if (!wallet) {
send(404);
return;
}
req.wallet = wallet; req.wallet = wallet;
return next(); next();
}); return;
} }
self.walletdb.auth(req.options.id, req.options.token, function(err, wallet) { try {
if (err) { wallet = yield self.walletdb.auth(req.options.id, req.options.token);
} catch (err) {
self.logger.info('Auth failure for %s: %s.', self.logger.info('Auth failure for %s: %s.',
req.options.id, err.message); req.options.id, err.message);
send(403, { error: err.message }); send(403, { error: err.message });
return; return;
} }
if (!wallet) if (!wallet) {
return send(404); send(404);
return;
}
req.wallet = wallet; req.wallet = wallet;
self.logger.info('Successful auth for %s.', req.options.id); self.logger.info('Successful auth for %s.', req.options.id);
next(); next();
}); }).catch(next);
}); });
// JSON RPC // JSON RPC
this.post('/', function(req, res, next, send) { this.post('/', function(req, res, next, send) {
if (!self.rpc) { spawn(function *() {
RPC = require('./rpc'); var json;
self.rpc = new RPC(self.node);
}
function handle(err, json) { if (!self.rpc) {
if (err) { RPC = require('./rpc');
self.rpc = new RPC(self.node);
}
if (req.body.method === 'getwork') {
res.setHeader('X-Long-Polling', '/?longpoll=1');
if (req.query.longpoll)
req.body.method = 'getworklp';
}
try {
json = yield self.rpc.execute(req.body);
} catch (err) {
self.logger.error(err); self.logger.error(err);
if (err.type === 'RPCError') { if (err.type === 'RPCError') {
@ -441,19 +458,7 @@ HTTPServer.prototype._init = function _init() {
error: null, error: null,
id: req.body.id id: req.body.id
}); });
} }).catch(next);
if (req.body.method === 'getwork') {
res.setHeader('X-Long-Polling', '/?longpoll=1');
if (req.query.longpoll)
req.body.method = 'getworklp';
}
try {
self.rpc.execute(req.body, handle);
} catch (e) {
handle(e);
}
}); });
this.get('/', function(req, res, next, send) { this.get('/', function(req, res, next, send) {
@ -471,141 +476,124 @@ HTTPServer.prototype._init = function _init() {
// UTXO by address // UTXO by address
this.get('/coin/address/:address', function(req, res, next, send) { this.get('/coin/address/:address', function(req, res, next, send) {
self.node.getCoinsByAddress(req.options.address, function(err, coins) { spawn(function *() {
if (err) var coins = yield self.node.getCoinsByAddress(req.options.address);
return next(err);
send(200, coins.map(function(coin) { send(200, coins.map(function(coin) {
return coin.toJSON(); return coin.toJSON();
})); }));
}); }).catch(next);
}); });
// UTXO by id // UTXO by id
this.get('/coin/:hash/:index', function(req, res, next, send) { this.get('/coin/:hash/:index', function(req, res, next, send) {
self.node.getCoin(req.options.hash, req.options.index, function(err, coin) { spawn(function *() {
if (err) var coin = yield self.node.getCoin(req.options.hash, req.options.index);
return next(err);
if (!coin) if (!coin)
return send(404); return send(404);
send(200, coin.toJSON()); send(200, coin.toJSON());
}); }).catch(next);
}); });
// Bulk read UTXOs // Bulk read UTXOs
this.post('/coin/address', function(req, res, next, send) { this.post('/coin/address', function(req, res, next, send) {
self.node.getCoinsByAddress(req.options.address, function(err, coins) { spawn(function *() {
if (err) var coins = yield self.node.getCoinsByAddress(req.options.address);
return next(err);
send(200, coins.map(function(coin) { send(200, coins.map(function(coin) {
return coin.toJSON(); return coin.toJSON();
})); }));
}); }).catch(next);
}); });
// TX by hash // TX by hash
this.get('/tx/:hash', function(req, res, next, send) { this.get('/tx/:hash', function(req, res, next, send) {
self.node.getTX(req.options.hash, function(err, tx) { spawn(function *() {
if (err) var tx = yield self.node.getTX(req.options.hash);
return next(err);
if (!tx) if (!tx)
return send(404); return send(404);
self.node.fillHistory(tx, function(err) { yield self.node.fillHistory(tx);
if (err)
return next(err);
send(200, tx.toJSON()); send(200, tx.toJSON());
}); }).catch(next);
});
}); });
// TX by address // TX by address
this.get('/tx/address/:address', function(req, res, next, send) { this.get('/tx/address/:address', function(req, res, next, send) {
self.node.getTXByAddress(req.options.address, function(err, txs) { spawn(function *() {
if (err) var txs = yield self.node.getTXByAddress(req.options.address);
return next(err); var i, tx;
utils.forEachSerial(txs, function(tx, next) { for (i = 0; i < txs.length; i++) {
self.node.fillHistory(tx, next); tx = txs[i];
}, function(err) { yield self.node.fillHistory(tx);
if (err) }
return next(err);
send(200, txs.map(function(tx) { send(200, txs.map(function(tx) {
return tx.toJSON(); return tx.toJSON();
})); }));
}); }).catch(next);
});
}); });
// Bulk read TXs // Bulk read TXs
this.post('/tx/address', function(req, res, next, send) { this.post('/tx/address', function(req, res, next, send) {
self.node.getTXByAddress(req.options.address, function(err, txs) { spawn(function *() {
if (err) var txs = yield self.node.getTXByAddress(req.options.address);
return next(err); var i, tx;
utils.forEachSerial(txs, function(tx, next) { for (i = 0; i < txs.length; i++) {
self.node.fillHistory(tx, next); tx = txs[i];
}, function(err) { yield self.node.fillHistory(tx);
if (err) }
return next(err);
send(200, txs.map(function(tx) { send(200, txs.map(function(tx) {
return tx.toJSON(); return tx.toJSON();
})); }));
}); }).catch(next);
});
}); });
// Block by hash/height // Block by hash/height
this.get('/block/:hash', function(req, res, next, send) { this.get('/block/:hash', function(req, res, next, send) {
var hash = req.options.hash || req.options.height; spawn(function *() {
self.node.getFullBlock(hash, function(err, block) { var hash = req.options.hash || req.options.height;
if (err) var block = yield self.node.getFullBlock(hash);
return next(err);
if (!block) if (!block)
return send(404); return send(404);
send(200, block.toJSON()); send(200, block.toJSON());
}); }).catch(next);
}); });
// Mempool snapshot // Mempool snapshot
this.get('/mempool', function(req, res, next, send) { this.get('/mempool', function(req, res, next, send) {
if (!self.mempool) spawn(function *() {
return send(400, { error: 'No mempool available.' }); var i, txs, tx;
self.mempool.getHistory(function(err, txs) { if (!self.mempool)
if (err) return send(400, { error: 'No mempool available.' });
return next(err);
utils.forEachSerial(txs, function(tx, next) { txs = self.mempool.getHistory();
self.node.fillHistory(tx, next);
}, function(err) {
if (err)
return next(err);
send(200, txs.map(function(tx) { for (i = 0; i < txs.length; i++) {
return tx.toJSON(); tx = txs[i];
})); yield self.node.fillHistory(tx);
}); }
});
send(200, txs.map(function(tx) {
return tx.toJSON();
}));
}).catch(next);
}); });
// Broadcast TX // Broadcast TX
this.post('/broadcast', function(req, res, next, send) { this.post('/broadcast', function(req, res, next, send) {
self.node.sendTX(req.options.tx, function(err) { spawn(function *() {
if (err) yield self.node.sendTX(req.options.tx);
return next(err);
send(200, { success: true }); send(200, { success: true });
}); }).catch(next);
}); });
// Estimate fee // Estimate fee
@ -627,319 +615,251 @@ HTTPServer.prototype._init = function _init() {
// Create wallet // Create wallet
this.post('/wallet/:id?', function(req, res, next, send) { this.post('/wallet/:id?', function(req, res, next, send) {
self.walletdb.create(req.options, function(err, wallet) { spawn(function *() {
if (err) var wallet = yield self.walletdb.create(req.options);
return next(err);
send(200, wallet.toJSON()); send(200, wallet.toJSON());
}); }).catch(next);
}); });
// List accounts // List accounts
this.get('/wallet/:id/account', function(req, res, next, send) { this.get('/wallet/:id/account', function(req, res, next, send) {
req.wallet.getAccounts(function(err, accounts) { spawn(function *() {
if (err) var accounts = yield req.wallet.getAccounts();
return next(err);
send(200, accounts); send(200, accounts);
}); }).catch(next);
}); });
// Get account // Get account
this.get('/wallet/:id/account/:account', function(req, res, next, send) { this.get('/wallet/:id/account/:account', function(req, res, next, send) {
req.wallet.getAccount(req.options.account, function(err, account) { spawn(function *() {
if (err) var account = yield req.wallet.getAccount(req.options.account);
return next(err);
if (!account) if (!account)
return send(404); return send(404);
send(200, account.toJSON()); send(200, account.toJSON());
}); }).catch(next);
}); });
// Create/get account // Create/get account
this.post('/wallet/:id/account/:account?', function(req, res, next, send) { this.post('/wallet/:id/account/:account?', function(req, res, next, send) {
req.wallet.createAccount(req.options, function(err, account) { spawn(function *() {
if (err) var account = yield req.wallet.createAccount(req.options);
return next(err);
if (!account) if (!account)
return send(404); return send(404);
send(200, account.toJSON()); send(200, account.toJSON());
}); }).catch(next);
}); });
// Change passphrase // Change passphrase
this.post('/wallet/:id/passphrase', function(req, res, next, send) { this.post('/wallet/:id/passphrase', function(req, res, next, send) {
var options = req.options; spawn(function *() {
var old = options.old; var options = req.options;
var new_ = options.passphrase; var old = options.old;
req.wallet.setPassphrase(old, new_, function(err) { var new_ = options.passphrase;
if (err) yield req.wallet.setPassphrase(old, new_);
return next(err);
send(200, { success: true }); send(200, { success: true });
}); }).catch(next);
}); });
// Generate new token // Generate new token
this.post('/wallet/:id/retoken', function(req, res, next, send) { this.post('/wallet/:id/retoken', function(req, res, next, send) {
var options = req.options; spawn(function *() {
req.wallet.retoken(options.passphrase, function(err, token) { var options = req.options;
if (err) var token = yield req.wallet.retoken(options.passphrase);
return next(err);
send(200, { token: token.toString('hex') }); send(200, { token: token.toString('hex') });
}); }).catch(next);
}); });
// Send TX // Send TX
this.post('/wallet/:id/send', function(req, res, next, send) { this.post('/wallet/:id/send', function(req, res, next, send) {
var options = req.options; spawn(function *() {
var options = req.options;
req.wallet.send(options, function(err, tx) { var tx = yield req.wallet.send(options);
if (err)
return next(err);
send(200, tx.toJSON()); send(200, tx.toJSON());
}); }).catch(next);
}); });
// Create TX // Create TX
this.post('/wallet/:id/create', function(req, res, next, send) { this.post('/wallet/:id/create', function(req, res, next, send) {
var options = req.options; spawn(function *() {
var options = req.options;
req.wallet.createTX(options, function(err, tx) { var tx = yield req.wallet.createTX(options);
if (err) yield req.wallet.sign(tx, options);
return next(err); send(200, tx.toJSON());
}).catch(next);
req.wallet.sign(tx, options, function(err) {
if (err)
return next(err);
send(200, tx.toJSON());
});
});
}); });
// Sign TX // Sign TX
this.post('/wallet/:id/sign', function(req, res, next, send) { this.post('/wallet/:id/sign', function(req, res, next, send) {
var options = req.options; spawn(function *() {
var tx = req.options.tx; var options = req.options;
var tx = req.options.tx;
req.wallet.sign(tx, options, function(err) { yield req.wallet.sign(tx, options);
if (err)
return next(err);
send(200, tx.toJSON()); send(200, tx.toJSON());
}); }).catch(next);
}); });
// Fill TX // Fill TX
this.post('/wallet/:id/fill', function(req, res, next, send) { this.post('/wallet/:id/fill', function(req, res, next, send) {
var tx = req.options.tx; spawn(function *() {
var tx = req.options.tx;
req.wallet.fillHistory(tx, function(err) { yield req.wallet.fillHistory(tx);
if (err)
return next(err);
send(200, tx.toJSON()); send(200, tx.toJSON());
}); }).catch(next);
}); });
// Zap Wallet TXs // Zap Wallet TXs
this.post('/wallet/:id/zap', function(req, res, next, send) { this.post('/wallet/:id/zap', function(req, res, next, send) {
var account = req.options.account; spawn(function *() {
var age = req.options.age; var account = req.options.account;
var age = req.options.age;
req.wallet.zap(account, age, function(err) { yield req.wallet.zap(account, age);
if (err)
return next(err);
send(200, { success: true }); send(200, { success: true });
}); }).catch(next);
}); });
// Abandon Wallet TX // Abandon Wallet TX
this.del('/wallet/:id/tx/:hash', function(req, res, next, send) { this.del('/wallet/:id/tx/:hash', function(req, res, next, send) {
var hash = req.options.hash; spawn(function *() {
req.wallet.abandon(hash, function(err) { var hash = req.options.hash;
if (err) yield req.wallet.abandon(hash);
return next(err);
send(200, { success: true }); send(200, { success: true });
}); }).catch(next);
}); });
// Add key // Add key
this.put('/wallet/:id/key', function(req, res, next, send) { this.put('/wallet/:id/key', function(req, res, next, send) {
var account = req.options.account; spawn(function *() {
var key = req.options.key; var account = req.options.account;
req.wallet.addKey(account, key, function(err) { var key = req.options.key;
if (err) yield req.wallet.addKey(account, key);
return next(err);
send(200, { success: true }); send(200, { success: true });
}); }).catch(next);
}); });
// Remove key // Remove key
this.del('/wallet/:id/key', function(req, res, next, send) { this.del('/wallet/:id/key', function(req, res, next, send) {
var account = req.options.account; spawn(function *() {
var key = req.options.key; var account = req.options.account;
req.wallet.removeKey(account, key, function(err) { var key = req.options.key;
if (err) yield req.wallet.removeKey(account, key);
return next(err);
send(200, { success: true }); send(200, { success: true });
}); }).catch(next);
}); });
// Create address // Create address
this.post('/wallet/:id/address', function(req, res, next, send) { this.post('/wallet/:id/address', function(req, res, next, send) {
var account = req.options.account; spawn(function *() {
req.wallet.createReceive(account, function(err, address) { var account = req.options.account;
if (err) var address = yield req.wallet.createReceive(account);
return next(err);
send(200, address.toJSON()); send(200, address.toJSON());
}); }).catch(next);
}); });
// Wallet Balance // Wallet Balance
this.get('/wallet/:id/balance', function(req, res, next, send) { this.get('/wallet/:id/balance', function(req, res, next, send) {
var account = req.options.account; spawn(function *() {
req.wallet.getBalance(account, function(err, balance) { var account = req.options.account;
if (err) var balance = yield req.wallet.getBalance(account);
return next(err);
if (!balance) if (!balance)
return send(404); return send(404);
send(200, balance.toJSON()); send(200, balance.toJSON());
}); }).catch(next);
}); });
// Wallet UTXOs // Wallet UTXOs
this.get('/wallet/:id/coin', function(req, res, next, send) { this.get('/wallet/:id/coin', function(req, res, next, send) {
var account = req.options.account; spawn(function *() {
req.wallet.getCoins(account, function(err, coins) { var account = req.options.account;
if (err) var coins = yield req.wallet.getCoins(account);
return next(err);
send(200, coins.map(function(coin) { send(200, coins.map(function(coin) {
return coin.toJSON(); return coin.toJSON();
})); }));
}); }).catch(next);
}); });
// Wallet Coin // Wallet Coin
this.get('/wallet/:id/coin/:hash/:index', function(req, res, next, send) { this.get('/wallet/:id/coin/:hash/:index', function(req, res, next, send) {
var hash = req.options.hash; spawn(function *() {
var index = req.options.index; var hash = req.options.hash;
req.wallet.getCoin(hash, index, function(err, coin) { var index = req.options.index;
if (err) var coin = yield req.wallet.getCoin(hash, index);
return next(err);
if (!coin) if (!coin)
return send(404); return send(404);
send(200, coin.toJSON()); send(200, coin.toJSON());
}); }).catch(next);
}); });
// Wallet TXs // Wallet TXs
this.get('/wallet/:id/tx/history', function(req, res, next, send) { this.get('/wallet/:id/tx/history', function(req, res, next, send) {
var account = req.options.account; spawn(function *() {
req.wallet.getHistory(account, function(err, txs) { var account = req.options.account;
if (err) var txs = yield req.wallet.getHistory(account);
return next(err); var details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
req.wallet.toDetails(txs, function(err, txs) { return tx.toJSON();
if (err) }));
return next(err); }).catch(next);
send(200, txs.map(function(tx) {
return tx.toJSON();
}));
});
});
}); });
// Wallet Pending TXs // Wallet Pending TXs
this.get('/wallet/:id/tx/unconfirmed', function(req, res, next, send) { this.get('/wallet/:id/tx/unconfirmed', function(req, res, next, send) {
var account = req.options.account; spawn(function *() {
req.wallet.getUnconfirmed(account, function(err, txs) { var account = req.options.account;
if (err) var txs = yield req.wallet.getUnconfirmed(account);
return next(err); var details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
req.wallet.toDetails(txs, function(err, txs) { return tx.toJSON();
if (err) }));
return next(err); }).catch(next);
send(200, txs.map(function(tx) {
return tx.toJSON();
}));
});
});
}); });
// Wallet TXs within time range // Wallet TXs within time range
this.get('/wallet/:id/tx/range', function(req, res, next, send) { this.get('/wallet/:id/tx/range', function(req, res, next, send) {
var account = req.options.account; spawn(function *() {
var options = req.options; var account = req.options.account;
req.wallet.getRange(account, options, function(err, txs) { var options = req.options;
if (err) var txs = yield req.wallet.getRange(account, options);
return next(err); var details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
req.wallet.toDetails(txs, function(err, txs) { return tx.toJSON();
if (err) }));
return next(err); }).catch(next);
send(200, txs.map(function(tx) {
return tx.toJSON();
}));
});
});
}); });
// Last Wallet TXs // Last Wallet TXs
this.get('/wallet/:id/tx/last', function(req, res, next, send) { this.get('/wallet/:id/tx/last', function(req, res, next, send) {
var account = req.options.account; spawn(function *() {
var limit = req.options.limit; var account = req.options.account;
req.wallet.getLast(account, limit, function(err, txs) { var limit = req.options.limit;
if (err) var txs = yield req.wallet.getLast(account, limit);
return next(err); var details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
req.wallet.toDetails(txs, function(err, txs) { return tx.toJSON();
if (err) }));
return next(err); }).catch(next);
send(200, txs.map(function(tx) {
return tx.toJSON();
}));
});
});
}); });
// Wallet TX // Wallet TX
this.get('/wallet/:id/tx/:hash', function(req, res, next, send) { this.get('/wallet/:id/tx/:hash', function(req, res, next, send) {
var hash = req.options.hash; spawn(function *() {
req.wallet.getTX(hash, function(err, tx) { var hash = req.options.hash;
if (err) var tx = yield req.wallet.getTX(hash);
return next(err); var details;
if (!tx) if (!tx)
return send(404); return send(404);
req.wallet.toDetails(tx, function(err, tx) { details = yield req.wallet.toDetails(tx);
if (err) send(200, details.toJSON());
return next(err); }).catch(next);
send(200, tx.toJSON());
});
});
}); });
this.server.on('error', function(err) { this.server.on('error', function(err) {
@ -1018,12 +938,7 @@ HTTPServer.prototype._initIO = function _initIO() {
if (!utils.isHex256(token)) if (!utils.isHex256(token))
return callback({ error: 'Invalid parameter.' }); return callback({ error: 'Invalid parameter.' });
self.walletdb.auth(id, token, function(err, wallet) { self.walletdb.auth(id, token).then(function(wallet) {
if (err) {
self.logger.info('Wallet auth failure for %s: %s.', id, err.message);
return callback({ error: 'Bad token.' });
}
if (!wallet) if (!wallet)
return callback({ error: 'Wallet does not exist.' }); return callback({ error: 'Wallet does not exist.' });
@ -1032,6 +947,9 @@ HTTPServer.prototype._initIO = function _initIO() {
socket.join(id); socket.join(id);
callback(); callback();
}).catch(function(err) {
self.logger.info('Wallet auth failure for %s: %s.', id, err.message);
return callback({ error: 'Bad token.' });
}); });
}); });
@ -1092,10 +1010,8 @@ HTTPServer.prototype._initIO = function _initIO() {
if (!utils.isHex256(start) && !utils.isNumber(start)) if (!utils.isHex256(start) && !utils.isNumber(start))
return callback({ error: 'Invalid parameter.' }); return callback({ error: 'Invalid parameter.' });
socket.scan(start, function(err) { socket.scan(start).then(callback).catch(function(err) {
if (err) callback({ error: err.message });
return callback({ error: err.message });
callback();
}); });
}); });
}); });
@ -1144,23 +1060,19 @@ HTTPServer.prototype._initIO = function _initIO() {
* @param {Function} callback * @param {Function} callback
*/ */
HTTPServer.prototype.open = function open(callback) { HTTPServer.prototype.open = function open() {
var self = this; return spawn(function *() {
this.server.open(function(err) { yield this.server.open();
if (err)
return callback(err);
self.logger.info('HTTP server loaded.'); this.logger.info('HTTP server loaded.');
if (self.apiKey) { if (this.apiKey) {
self.logger.info('HTTP API key: %s', self.apiKey); this.logger.info('HTTP API key: %s', this.apiKey);
self.apiKey = null; this.apiKey = null;
} else if (!self.apiHash) { } else if (!this.apiHash) {
self.logger.warning('WARNING: Your http server is open to the world.'); this.logger.warning('WARNING: Your http server is open to the world.');
} }
}, this);
callback();
});
}; };
/** /**
@ -1168,8 +1080,8 @@ HTTPServer.prototype.open = function open(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
HTTPServer.prototype.close = function close(callback) { HTTPServer.prototype.close = function close() {
this.server.close(callback); return this.server.close();
}; };
/** /**
@ -1411,7 +1323,7 @@ ClientSocket.prototype.testFilter = function testFilter(tx) {
} }
}; };
ClientSocket.prototype.scan = function scan(start, callback) { ClientSocket.prototype.scan = function scan(start) {
var self = this; var self = this;
var i; var i;
@ -1419,19 +1331,19 @@ ClientSocket.prototype.scan = function scan(start, callback) {
start = utils.revHex(start); start = utils.revHex(start);
if (this.chain.db.options.spv) if (this.chain.db.options.spv)
return this.chain.reset(start, callback); return this.chain.reset(start);
if (this.chain.db.options.prune) if (this.chain.db.options.prune)
return callback(new Error('Cannot scan in pruned mode.')); return Promise.reject(new Error('Cannot scan in pruned mode.'));
this.chain.db.scan(start, this.filter, function(entry, txs, next) { return this.chain.db.scan(start, this.filter, function(entry, txs) {
for (i = 0; i < txs.length; i++) for (i = 0; i < txs.length; i++)
txs[i] = txs[i].toJSON(); txs[i] = txs[i].toJSON();
self.emit('block tx', entry.toJSON(), txs); self.emit('block tx', entry.toJSON(), txs);
next(); return Promise.resolve(null);
}, callback); });
}; };
ClientSocket.prototype.join = function join(id) { ClientSocket.prototype.join = function join(id) {

View File

@ -11,6 +11,7 @@ var Network = require('../protocol/network');
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var Client = require('./client'); var Client = require('./client');
/** /**
@ -88,32 +89,27 @@ HTTPWallet.prototype._init = function _init() {
* @param {Function} callback * @param {Function} callback
*/ */
HTTPWallet.prototype.open = function open(options, callback) { HTTPWallet.prototype.open = function open(options) {
var self = this; return spawn(function *() {
var wallet;
this.id = options.id; this.id = options.id;
if (options.token) { if (options.token) {
this.token = options.token; this.token = options.token;
if (Buffer.isBuffer(this.token)) if (Buffer.isBuffer(this.token))
this.token = this.token.toString('hex'); this.token = this.token.toString('hex');
this.client.token = this.token; this.client.token = this.token;
} }
this.client.open(function(err) { yield this.client.open();
if (err)
return callback(err);
self.client.getWallet(self.id, function(err, wallet) { wallet = yield this.client.getWallet(this.id);
if (err)
return callback(err); yield this.client.join(this.id, wallet.token);
self.client.join(self.id, wallet.token, function(err) {
if (err) return wallet;
return callback(new Error(err.error)); }, this);
callback(null, wallet);
});
});
});
}; };
/** /**
@ -122,23 +118,16 @@ HTTPWallet.prototype.open = function open(options, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
HTTPWallet.prototype.create = function create(options, callback) { HTTPWallet.prototype.create = function create(options) {
var self = this; return spawn(function *() {
var wallet;
this.client.open(function(err) { yield this.client.open();
if (err) wallet = yield this.client.createWallet(options);
return callback(err); return yield this.open({
id: wallet.id,
self.client.createWallet(options, function(err, wallet) { token: wallet.token
if (err)
return callback(err);
self.open({
id: wallet.id,
token: wallet.token
}, callback);
}); });
}); }, this);
}; };
/** /**
@ -147,112 +136,112 @@ HTTPWallet.prototype.create = function create(options, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
HTTPWallet.prototype.close = function close(callback) { HTTPWallet.prototype.close = function close() {
this.client.close(callback); return this.client.close();
}; };
/** /**
* @see Wallet#getHistory * @see Wallet#getHistory
*/ */
HTTPWallet.prototype.getHistory = function getHistory(account, callback) { HTTPWallet.prototype.getHistory = function getHistory(account) {
this.client.getHistory(this.id, account, callback); return this.client.getHistory(this.id, account);
}; };
/** /**
* @see Wallet#getCoins * @see Wallet#getCoins
*/ */
HTTPWallet.prototype.getCoins = function getCoins(account, callback) { HTTPWallet.prototype.getCoins = function getCoins(account) {
this.client.getCoins(this.id, account, callback); return this.client.getCoins(this.id, account);
}; };
/** /**
* @see Wallet#getUnconfirmed * @see Wallet#getUnconfirmed
*/ */
HTTPWallet.prototype.getUnconfirmed = function getUnconfirmed(account, callback) { HTTPWallet.prototype.getUnconfirmed = function getUnconfirmed(account) {
this.client.getUnconfirmed(this.id, account, callback); return this.client.getUnconfirmed(this.id, account);
}; };
/** /**
* @see Wallet#getBalance * @see Wallet#getBalance
*/ */
HTTPWallet.prototype.getBalance = function getBalance(account, callback) { HTTPWallet.prototype.getBalance = function getBalance(account) {
this.client.getBalance(this.id, account, callback); return this.client.getBalance(this.id, account);
}; };
/** /**
* @see Wallet#getLast * @see Wallet#getLast
*/ */
HTTPWallet.prototype.getLast = function getLast(account, limit, callback) { HTTPWallet.prototype.getLast = function getLast(account, limit) {
this.client.getLast(this.id, account, limit, callback); return this.client.getLast(this.id, account, limit);
}; };
/** /**
* @see Wallet#getRange * @see Wallet#getRange
*/ */
HTTPWallet.prototype.getRange = function getRange(account, options, callback) { HTTPWallet.prototype.getRange = function getRange(account, options) {
this.client.getRange(this.id, account, options, callback); return this.client.getRange(this.id, account, options);
}; };
/** /**
* @see Wallet#getTX * @see Wallet#getTX
*/ */
HTTPWallet.prototype.getTX = function getTX(account, hash, callback) { HTTPWallet.prototype.getTX = function getTX(account, hash) {
this.client.getWalletTX(this.id, account, hash, callback); return this.client.getWalletTX(this.id, account, hash);
}; };
/** /**
* @see Wallet#getCoin * @see Wallet#getCoin
*/ */
HTTPWallet.prototype.getCoin = function getCoin(account, hash, index, callback) { HTTPWallet.prototype.getCoin = function getCoin(account, hash, index) {
this.client.getWalletCoin(this.id, account, hash, index, callback); return this.client.getWalletCoin(this.id, account, hash, index);
}; };
/** /**
* @see Wallet#zap * @see Wallet#zap
*/ */
HTTPWallet.prototype.zap = function zap(account, age, callback) { HTTPWallet.prototype.zap = function zap(account, age) {
this.client.zap(this.id, account, age, callback); return this.client.zap(this.id, account, age);
}; };
/** /**
* @see Wallet#createTX * @see Wallet#createTX
*/ */
HTTPWallet.prototype.createTX = function createTX(options, outputs, callback) { HTTPWallet.prototype.createTX = function createTX(options, outputs) {
this.client.createTX(this.id, options, outputs, callback); return this.client.createTX(this.id, options, outputs);
}; };
/** /**
* @see HTTPClient#walletSend * @see HTTPClient#walletSend
*/ */
HTTPWallet.prototype.send = function send(options, callback) { HTTPWallet.prototype.send = function send(options) {
this.client.send(this.id, options, callback); return this.client.send(this.id, options);
}; };
/** /**
* @see Wallet#sign * @see Wallet#sign
*/ */
HTTPWallet.prototype.sign = function sign(tx, options, callback) { HTTPWallet.prototype.sign = function sign(tx, options) {
this.client.sign(this.id, tx, options, callback); return this.client.sign(this.id, tx, options);
}; };
/** /**
* @see Wallet#fillCoins * @see Wallet#fillCoins
*/ */
HTTPWallet.prototype.fillCoins = function fillCoins(tx, callback) { HTTPWallet.prototype.fillCoins = function fillCoins(tx) {
this.client.fillCoins(tx, callback); return this.client.fillCoins(tx);
}; };
/** /**
@ -260,7 +249,7 @@ HTTPWallet.prototype.fillCoins = function fillCoins(tx, callback) {
*/ */
HTTPWallet.prototype.getInfo = function getInfo(callback) { HTTPWallet.prototype.getInfo = function getInfo(callback) {
this.client.getWallet(this.id, callback); return this.client.getWallet(this.id);
}; };
/** /**
@ -268,62 +257,54 @@ HTTPWallet.prototype.getInfo = function getInfo(callback) {
*/ */
HTTPWallet.prototype.getAccounts = function getAccounts(callback) { HTTPWallet.prototype.getAccounts = function getAccounts(callback) {
this.client.getAccounts(this.id, callback); return this.client.getAccounts(this.id);
}; };
/** /**
* @see Wallet#getAccount * @see Wallet#getAccount
*/ */
HTTPWallet.prototype.getAccount = function getAccount(account, callback) { HTTPWallet.prototype.getAccount = function getAccount(account) {
this.client.getAccount(this.id, account, callback); return this.client.getAccount(this.id, account);
}; };
/** /**
* @see Wallet#createAccount * @see Wallet#createAccount
*/ */
HTTPWallet.prototype.createAccount = function createAccount(options, callback) { HTTPWallet.prototype.createAccount = function createAccount(options) {
this.client.createAccount(this.id, options, callback); return this.client.createAccount(this.id, options);
}; };
/** /**
* @see Wallet#createAddress * @see Wallet#createAddress
*/ */
HTTPWallet.prototype.createAddress = function createAddress(account, callback) { HTTPWallet.prototype.createAddress = function createAddress(account) {
this.client.createAddress(this.id, account, callback); return this.client.createAddress(this.id, account);
}; };
/** /**
* @see Wallet#setPassphrase * @see Wallet#setPassphrase
*/ */
HTTPWallet.prototype.setPassphrase = function setPassphrase(old, new_, callback) { HTTPWallet.prototype.setPassphrase = function setPassphrase(old, new_) {
this.client.setPassphrase(this.id, old, new_, callback); return this.client.setPassphrase(this.id, old, new_);
}; };
/** /**
* @see Wallet#retoken * @see Wallet#retoken
*/ */
HTTPWallet.prototype.retoken = function retoken(passphrase, callback) { HTTPWallet.prototype.retoken = function retoken(passphrase) {
var self = this; return spawn(function *() {
var token = yield this.client.retoken(this.id, passphrase);
if (typeof passphrase === 'function') { this.token = token;
callback = passphrase; this.client.token = token;
passphrase = null;
}
this.client.retoken(this.id, passphrase, function(err, token) { return token;
if (err) }, this);
return callback(err);
self.token = token;
self.client.token = token;
return callback(null, token);
});
}; };
/* /*

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@
var bcoin = require('../env'); var bcoin = require('../env');
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var assert = utils.assert; var assert = utils.assert;
var AsyncObject = require('../utils/async'); var AsyncObject = require('../utils/async');
var MinerBlock = require('./minerblock'); var MinerBlock = require('./minerblock');
@ -133,25 +134,16 @@ Miner.prototype._init = function _init() {
* @param {Function} callback * @param {Function} callback
*/ */
Miner.prototype._open = function open(callback) { Miner.prototype._open = function open() {
var self = this; return spawn(function *() {
if (this.mempool)
function open(callback) { yield this.mempool.open();
if (self.mempool)
self.mempool.open(callback);
else else
self.chain.open(callback); yield this.chain.open();
}
open(function(err) { this.logger.info('Miner loaded (flags=%s).',
if (err) this.coinbaseFlags.toString('utf8'));
return callback(err); }, this);
self.logger.info('Miner loaded (flags=%s).',
self.coinbaseFlags.toString('utf8'));
callback();
});
}; };
/** /**
@ -160,8 +152,8 @@ Miner.prototype._open = function open(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
Miner.prototype._close = function close(callback) { Miner.prototype._close = function close() {
callback(); return Promise.resolve(null);
}; };
/** /**
@ -171,49 +163,57 @@ Miner.prototype._close = function close(callback) {
Miner.prototype.start = function start() { Miner.prototype.start = function start() {
var self = this; var self = this;
spawn(function *() {
var attempt, block;
this.stop(); this.stop();
this.running = true; this.running = true;
// Create a new block and start hashing // Create a new block and start hashing
this.createBlock(function(err, attempt) { try {
if (err) attempt = yield this.createBlock();
return self.emit('error', err); } catch (e) {
this.emit('error', e);
return;
}
if (!self.running) if (!this.running)
return; return;
self.attempt = attempt; this.attempt = attempt;
attempt.on('status', function(status) { attempt.on('status', function(status) {
self.emit('status', status); self.emit('status', status);
}); });
attempt.mineAsync(function(err, block) { try {
if (err) { block = yield attempt.mineAsync();
if (!self.running) } catch (e) {
return; if (!this.running)
self.emit('error', err); return;
return self.start(); this.emit('error', e);
} return this.start();
}
// Add our block to the chain // Add our block to the chain
self.chain.add(block, function(err) { try {
if (err) { yield this.chain.add(block);
if (err.type === 'VerifyError') } catch (err) {
self.logger.warning('%s could not be added to chain.', block.rhash); if (err.type === 'VerifyError')
self.emit('error', err); this.logger.warning('%s could not be added to chain.', block.rhash);
return self.start(); this.emit('error', err);
} this.start();
return;
}
// Emit our newly found block // Emit our newly found block
self.emit('block', block); this.emit('block', block);
// `tip` will now be emitted by chain // `tip` will now be emitted by chain
// and the whole process starts over. // and the whole process starts over.
}); }, this).catch(function(err) {
}); self.emit('error', err);
}); });
}; };
@ -242,72 +242,54 @@ Miner.prototype.stop = function stop() {
* @param {Function} callback - Returns [Error, {@link MinerBlock}]. * @param {Function} callback - Returns [Error, {@link MinerBlock}].
*/ */
Miner.prototype.createBlock = function createBlock(tip, callback) { Miner.prototype.createBlock = function createBlock(tip) {
var self = this; return spawn(function *() {
var i, ts, attempt, txs, tx; var i, ts, attempt, txs, tx, target, version;
if (typeof tip === 'function') { if (!this.loaded)
callback = tip; yield this.open();
tip = null;
}
if (!tip) if (!tip)
tip = this.chain.tip; tip = this.chain.tip;
ts = Math.max(bcoin.now(), tip.ts + 1); assert(tip);
function computeVersion(callback) { ts = Math.max(bcoin.now(), tip.ts + 1);
if (self.version != null)
return callback(null, self.version);
self.chain.computeBlockVersion(tip, callback);
}
if (!this.loaded) { // Find target
this.open(function(err) { target = yield this.chain.getTargetAsync(ts, tip);
if (err)
return callback(err);
self.createBlock(tip, callback);
});
return;
}
assert(tip); if (this.version != null) {
version = this.version;
} else {
// Calculate version with versionbits
version = yield this.chain.computeBlockVersion(tip);
}
// Find target attempt = new MinerBlock({
this.chain.getTargetAsync(ts, tip, function(err, target) { workerPool: this.workerPool,
if (err) tip: tip,
return callback(err); version: version,
target: target,
address: this.address,
coinbaseFlags: this.coinbaseFlags,
witness: this.chain.segwitActive,
parallel: this.options.parallel,
network: this.network
});
// Calculate version with versionbits if (!this.mempool)
computeVersion(function(err, version) { return attempt;
if (err)
return callback(err);
attempt = new MinerBlock({ txs = this.mempool.getHistory();
workerPool: self.workerPool,
tip: tip,
version: version,
target: target,
address: self.address,
coinbaseFlags: self.coinbaseFlags,
witness: self.chain.segwitActive,
parallel: self.options.parallel,
network: self.network
});
if (!self.mempool) for (i = 0; i < txs.length; i++) {
return callback(null, attempt); tx = txs[i];
attempt.addTX(tx);
}
txs = self.mempool.getHistory(); return attempt;
}, this);
for (i = 0; i < txs.length; i++) {
tx = txs[i];
attempt.addTX(tx);
}
callback(null, attempt);
});
});
}; };
/** /**
@ -316,19 +298,12 @@ Miner.prototype.createBlock = function createBlock(tip, callback) {
* @param {Function} callback - Returns [Error, [{@link Block}]]. * @param {Function} callback - Returns [Error, [{@link Block}]].
*/ */
Miner.prototype.mineBlock = function mineBlock(tip, callback) { Miner.prototype.mineBlock = function mineBlock(tip) {
if (typeof tip === 'function') { return spawn(function *() {
callback = tip; // Create a new block and start hashing
tip = null; var attempt = yield this.createBlock(tip);
} return yield attempt.mineAsync();
}, this);
// Create a new block and start hashing
this.createBlock(tip, function(err, attempt) {
if (err)
return callback(err);
attempt.mineAsync(callback);
});
}; };
/* /*

View File

@ -9,6 +9,7 @@
var bcoin = require('../env'); var bcoin = require('../env');
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var crypto = require('../crypto/crypto'); var crypto = require('../crypto/crypto');
var assert = utils.assert; var assert = utils.assert;
var constants = bcoin.constants; var constants = bcoin.constants;
@ -55,7 +56,6 @@ function MinerBlock(options) {
this.address = options.address; this.address = options.address;
this.network = bcoin.network.get(options.network); this.network = bcoin.network.get(options.network);
this.timeout = null; this.timeout = null;
this.callback = null;
if (typeof this.coinbaseFlags === 'string') if (typeof this.coinbaseFlags === 'string')
this.coinbaseFlags = new Buffer(this.coinbaseFlags, 'utf8'); this.coinbaseFlags = new Buffer(this.coinbaseFlags, 'utf8');
@ -349,16 +349,33 @@ MinerBlock.prototype.sendStatus = function sendStatus() {
* @param {Function} callback - Returns [Error, {@link Block}]. * @param {Function} callback - Returns [Error, {@link Block}].
*/ */
MinerBlock.prototype.mine = function mine(callback) { MinerBlock.prototype.mine = function mine() {
var self = this; return spawn(function *() {
yield this.wait(100);
this.timeout = setTimeout(function() {
// Try to find a block: do one iteration of extraNonce // Try to find a block: do one iteration of extraNonce
if (!self.findNonce()) if (!this.findNonce()) {
return self.mine(callback); yield this.mine();
return;
}
callback(null, self.block); return this.block;
}, 100); }, this);
};
/**
* Wait for a timeout.
* @param {Number} time
* @returns {Promise}
*/
MinerBlock.prototype.wait = function wait(time) {
var self = this;
return new Promise(function(resolve, reject) {
self.timeout = setTimeout(function() {
resolve();
}, time);
});
}; };
/** /**
@ -376,29 +393,19 @@ MinerBlock.prototype.mineSync = function mineSync() {
* @param {Function} callback - Returns [Error, {@link Block}]. * @param {Function} callback - Returns [Error, {@link Block}].
*/ */
MinerBlock.prototype.mineAsync = function mine(callback) { MinerBlock.prototype.mineAsync = function mineAsync() {
var self = this; return spawn(function *() {
var block;
if (!this.workerPool) if (!this.workerPool)
return this.mine(callback); return yield this.mine();
callback = utils.once(callback); block = yield this.workerPool.mine(this);
this.callback = callback; this.workerPool.destroy();
function done(err, block) { return block;
self.workerPool.destroy(); }, this);
callback(err, block);
}
if (this.options.parallel) {
done = utils.once(done);
this.workerPool.mine(this, done);
this.workerPool.mine(this, done);
return;
}
this.workerPool.mine(this, callback);
}; };
/** /**
@ -410,10 +417,6 @@ MinerBlock.prototype.destroy = function destroy() {
clearTimeout(this.timeout); clearTimeout(this.timeout);
this.timeout = null; this.timeout = null;
} }
if (this.callback) {
this.callback(new Error('Destroyed.'));
this.callback = null;
}
this.block = null; this.block = null;
}; };

View File

@ -10,6 +10,7 @@
var bcoin = require('../env'); var bcoin = require('../env');
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var Parser = require('./parser'); var Parser = require('./parser');
var Framer = require('./framer'); var Framer = require('./framer');
var packets = require('./packets'); var packets = require('./packets');
@ -1119,78 +1120,62 @@ Peer.prototype._handleUTXOs = function _handleUTXOs(utxos) {
Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(packet) { Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(packet) {
var self = this; var self = this;
var unlock = this._lock(_handleGetUTXOs, [packet, utils.nop]); spawn(function *() {
var utxos; var unlock = yield this._lock();
var i, utxos, prevout, hash, index, coin;
if (!unlock) if (!this.chain.synced)
return;
function done(err) {
if (err) {
self.emit('error', err);
return unlock(); return unlock();
}
unlock();
}
if (!this.chain.synced) if (this.options.selfish)
return done(); return unlock();
if (this.options.selfish) if (this.chain.db.options.spv)
return done(); return unlock();
if (this.chain.db.options.spv) if (packet.prevout.length > 15)
return done(); return unlock();
if (packet.prevout.length > 15) utxos = new packets.GetUTXOsPacket();
return done();
utxos = new packets.GetUTXOsPacket(); for (i = 0; i < packet.prevout.length; i++) {
prevout = packet.prevout[i];
hash = prevout.hash;
index = prevout.index;
utils.forEachSerial(packet.prevout, function(prevout, next) { if (this.mempool && packet.mempool) {
var hash = prevout.hash; coin = this.mempool.getCoin(hash, index);
var index = prevout.index;
var coin;
if (self.mempool && packet.mempool) { if (coin) {
coin = self.mempool.getCoin(hash, index); utxos.hits.push(1);
utxos.coins.push(coin);
continue;
}
if (coin) { if (this.mempool.isSpent(hash, index)) {
utxos.hits.push(1); utxos.hits.push(0);
utxos.coins.push(coin); continue;
return next(); }
} }
if (self.mempool.isSpent(hash, index)) { coin = yield this.chain.db.getCoin(hash, index);
utxos.hits.push(0);
return next();
}
}
self.chain.db.getCoin(hash, index, function(err, coin) {
if (err)
return next(err);
if (!coin) { if (!coin) {
utxos.hits.push(0); utxos.hits.push(0);
return next(); continue;
} }
utxos.hits.push(1); utxos.hits.push(1);
utxos.coins.push(coin); utxos.coins.push(coin);
}
next(); utxos.height = this.chain.height;
}); utxos.tip = this.chain.tip.hash;
}, function(err) {
if (err)
return done(err);
utxos.height = self.chain.height; this.send(utxos);
utxos.tip = self.chain.tip.hash; unlock();
}, this).catch(function(err) {
self.send(utxos); self.emit('error', err);
done();
}); });
}; };
@ -1213,78 +1198,51 @@ Peer.prototype._handleHaveWitness = function _handleHaveWitness(packet) {
Peer.prototype._handleGetHeaders = function _handleGetHeaders(packet) { Peer.prototype._handleGetHeaders = function _handleGetHeaders(packet) {
var self = this; var self = this;
var headers = []; spawn(function *() {
var unlock = this._lock(_handleGetHeaders, [packet, utils.nop]); var unlock = yield this._lock();
var headers = [];
var hash, entry;
if (!unlock) if (!this.chain.synced)
return;
function done(err) {
if (err) {
self.emit('error', err);
return unlock(); return unlock();
if (this.options.selfish)
return unlock();
if (this.chain.db.options.spv)
return unlock();
if (this.chain.db.options.prune)
return unlock();
if (packet.locator.length > 0) {
hash = yield this.chain.findLocator(packet.locator);
if (hash)
hash = yield this.chain.db.getNextHash(hash);
} else {
hash = packet.stop;
} }
self.sendHeaders(headers);
if (hash)
entry = yield this.chain.db.get(hash);
while (entry) {
headers.push(entry.toHeaders());
if (headers.length === 2000)
break;
if (entry.hash === packet.stop)
break;
entry = yield entry.getNext();
}
this.sendHeaders(headers);
unlock(); unlock();
} }, this).catch(function(err) {
self.emit('error', err);
if (!this.chain.synced)
return done();
if (this.options.selfish)
return done();
if (this.chain.db.options.spv)
return done();
if (this.chain.db.options.prune)
return done();
function collect(err, hash) {
if (err)
return done(err);
if (!hash)
return done();
self.chain.db.get(hash, function(err, entry) {
if (err)
return done(err);
if (!entry)
return done();
(function next(err, entry) {
if (err)
return done(err);
if (!entry)
return done();
headers.push(entry.toHeaders());
if (headers.length === 2000)
return done();
if (entry.hash === packet.stop)
return done();
entry.getNext(next);
})(null, entry);
});
}
if (packet.locator.length === 0)
return collect(null, packet.stop);
this.chain.findLocator(packet.locator, function(err, hash) {
if (err)
return collect(err);
if (!hash)
return collect();
self.chain.db.getNextHash(hash, collect);
}); });
}; };
@ -1296,61 +1254,46 @@ Peer.prototype._handleGetHeaders = function _handleGetHeaders(packet) {
Peer.prototype._handleGetBlocks = function _handleGetBlocks(packet) { Peer.prototype._handleGetBlocks = function _handleGetBlocks(packet) {
var self = this; var self = this;
var blocks = []; spawn(function *() {
var unlock = this._lock(_handleGetBlocks, [packet, utils.nop]); var unlock = yield this._lock();
var blocks = [];
var hash;
if (!unlock) if (!this.chain.synced)
return;
function done(err) {
if (err) {
self.emit('error', err);
return unlock(); return unlock();
if (this.options.selfish)
return unlock();
if (this.chain.db.options.spv)
return unlock();
if (this.chain.db.options.prune)
return unlock();
hash = yield this.chain.findLocator(packet.locator);
if (hash)
hash = yield this.chain.db.getNextHash(hash);
while (hash) {
blocks.push(new InvItem(constants.inv.BLOCK, hash));
if (hash === packet.stop)
break;
if (blocks.length === 500) {
this.hashContinue = hash;
break;
}
hash = yield this.chain.db.getNextHash(hash);
} }
self.sendInv(blocks);
this.sendInv(blocks);
unlock(); unlock();
} }, this).catch(function(err) {
self.emit('error', err);
if (!this.chain.synced)
return done();
if (this.options.selfish)
return done();
if (this.chain.db.options.spv)
return done();
if (this.chain.db.options.prune)
return done();
this.chain.findLocator(packet.locator, function(err, tip) {
if (err)
return done(err);
if (!tip)
return done();
(function next(hash) {
self.chain.db.getNextHash(hash, function(err, hash) {
if (err)
return done(err);
if (!hash)
return done();
blocks.push(new InvItem(constants.inv.BLOCK, hash));
if (hash === packet.stop)
return done();
if (blocks.length === 500) {
self.hashContinue = hash;
return done();
}
next(hash);
});
})(tip);
}); });
}; };
@ -1460,27 +1403,15 @@ Peer.prototype._handleMempool = function _handleMempool(packet) {
var self = this; var self = this;
var items = []; var items = [];
var i, hashes; var i, hashes;
var unlock = this._lock(_handleMempool, [packet, utils.nop]);
if (!unlock)
return;
function done(err) {
if (err) {
self.emit('error', err);
return unlock();
}
unlock();
}
if (!this.mempool) if (!this.mempool)
return done(); return;
if (!this.chain.synced) if (!this.chain.synced)
return done(); return;
if (this.options.selfish) if (this.options.selfish)
return done(); return;
hashes = this.mempool.getSnapshot(); hashes = this.mempool.getSnapshot();
@ -1499,47 +1430,49 @@ Peer.prototype._handleMempool = function _handleMempool(packet) {
* [Error, {@link Block}|{@link MempoolEntry}]. * [Error, {@link Block}|{@link MempoolEntry}].
*/ */
Peer.prototype._getItem = function _getItem(item, callback) { Peer.prototype._getItem = function _getItem(item) {
var entry = this.pool.invMap[item.hash]; return spawn(function *() {
var entry = this.pool.invMap[item.hash];
if (entry) { if (entry) {
this.logger.debug( this.logger.debug(
'Peer requested %s %s as a %s packet (%s).', 'Peer requested %s %s as a %s packet (%s).',
entry.type === constants.inv.TX ? 'tx' : 'block', entry.type === constants.inv.TX ? 'tx' : 'block',
utils.revHex(entry.hash), utils.revHex(entry.hash),
item.hasWitness() ? 'witness' : 'normal', item.hasWitness() ? 'witness' : 'normal',
this.hostname); this.hostname);
entry.ack(this); entry.ack(this);
if (entry.msg) { if (entry.msg) {
if (item.isTX()) { if (item.isTX()) {
if (entry.type === constants.inv.TX) if (entry.type === constants.inv.TX)
return callback(null, entry.msg); return entry.msg;
} else { } else {
if (entry.type === constants.inv.BLOCK) if (entry.type === constants.inv.BLOCK)
return callback(null, entry.msg); return entry.msg;
}
return;
} }
return callback();
} }
}
if (this.options.selfish) if (this.options.selfish)
return callback(); return;
if (item.isTX()) { if (item.isTX()) {
if (!this.mempool) if (!this.mempool)
return callback(); return;
return callback(null, this.mempool.getTX(item.hash)); return this.mempool.getTX(item.hash);
} }
if (this.chain.db.options.spv) if (this.chain.db.options.spv)
return callback(); return;
if (this.chain.db.options.prune) if (this.chain.db.options.prune)
return callback(); return;
this.chain.db.getBlock(item.hash, callback); return yield this.chain.db.getBlock(item.hash);
}, this);
}; };
/** /**
@ -1550,36 +1483,24 @@ Peer.prototype._getItem = function _getItem(item, callback) {
Peer.prototype._handleGetData = function _handleGetData(packet) { Peer.prototype._handleGetData = function _handleGetData(packet) {
var self = this; var self = this;
var notFound = []; spawn(function *() {
var items = packet.items; var unlock = yield this._lock();
var unlock = this._lock(_handleGetData, [packet, utils.nop]); var notFound = [];
var items = packet.items;
var i, j, item, entry, tx, block;
if (!unlock) if (items.length > 50000) {
return; this.error('getdata size too large (%s).', items.length);
return;
function done(err) {
if (err) {
self.emit('error', err);
return unlock();
} }
unlock();
}
if (items.length > 50000) { for (i = 0; i < items.length; i++) {
this.error('getdata size too large (%s).', items.length); item = items[i];
return done(); entry = yield this._getItem(item);
}
utils.forEachSerial(items, function(item, next) {
var i, tx, block;
self._getItem(item, function(err, entry) {
if (err)
return next(err);
if (!entry) { if (!entry) {
notFound.push(item); notFound.push(item);
return next(); continue;
} }
if (item.isTX()) { if (item.isTX()) {
@ -1591,13 +1512,13 @@ Peer.prototype._handleGetData = function _handleGetData(packet) {
// 24-hour ban from any node is rough. // 24-hour ban from any node is rough.
if (tx.isCoinbase()) { if (tx.isCoinbase()) {
notFound.push(item); notFound.push(item);
self.logger.warning('Failsafe: tried to relay a coinbase.'); this.logger.warning('Failsafe: tried to relay a coinbase.');
return next(); continue;
} }
self.send(new packets.TXPacket(tx, item.hasWitness())); this.send(new packets.TXPacket(tx, item.hasWitness()));
return next(); continue;
} }
block = entry; block = entry;
@ -1605,29 +1526,29 @@ Peer.prototype._handleGetData = function _handleGetData(packet) {
switch (item.type) { switch (item.type) {
case constants.inv.BLOCK: case constants.inv.BLOCK:
case constants.inv.WITNESS_BLOCK: case constants.inv.WITNESS_BLOCK:
self.send(new packets.BlockPacket(block, item.hasWitness())); this.send(new packets.BlockPacket(block, item.hasWitness()));
break; break;
case constants.inv.FILTERED_BLOCK: case constants.inv.FILTERED_BLOCK:
case constants.inv.WITNESS_FILTERED_BLOCK: case constants.inv.WITNESS_FILTERED_BLOCK:
if (!self.spvFilter) { if (!this.spvFilter) {
notFound.push(item); notFound.push(item);
return next(); continue;
} }
block = block.toMerkle(self.spvFilter); block = block.toMerkle(this.spvFilter);
self.send(new packets.MerkleBlockPacket(block)); this.send(new packets.MerkleBlockPacket(block));
for (i = 0; i < block.txs.length; i++) { for (j = 0; j < block.txs.length; j++) {
tx = block.txs[i]; tx = block.txs[j];
self.send(new packets.TXPacket(tx, item.hasWitness())); this.send(new packets.TXPacket(tx, item.hasWitness()));
} }
break; break;
case constants.inv.CMPCT_BLOCK: case constants.inv.CMPCT_BLOCK:
// Fallback to full block. // Fallback to full block.
if (block.height < self.chain.tip.height - 10) { if (block.height < this.chain.tip.height - 10) {
self.send(new packets.BlockPacket(block, false)); this.send(new packets.BlockPacket(block, false));
break; break;
} }
@ -1642,38 +1563,35 @@ Peer.prototype._handleGetData = function _handleGetData(packet) {
break; break;
} }
self.send(new packets.CmpctBlockPacket(block, false)); this.send(new packets.CmpctBlockPacket(block, false));
break; break;
default: default:
self.logger.warning( this.logger.warning(
'Peer sent an unknown getdata type: %s (%s).', 'Peer sent an unknown getdata type: %s (%s).',
item.type, item.type,
self.hostname); this.hostname);
notFound.push(item); notFound.push(item);
return next(); continue;
} }
if (item.hash === self.hashContinue) { if (item.hash === this.hashContinue) {
self.sendInv(new InvItem(constants.inv.BLOCK, self.chain.tip.hash)); this.sendInv(new InvItem(constants.inv.BLOCK, this.chain.tip.hash));
self.hashContinue = null; this.hashContinue = null;
} }
}
next(); this.logger.debug(
});
}, function(err) {
if (err)
return done(err);
self.logger.debug(
'Served %d items with getdata (notfound=%d) (%s).', 'Served %d items with getdata (notfound=%d) (%s).',
items.length - notFound.length, items.length - notFound.length,
notFound.length, notFound.length,
self.hostname); this.hostname);
if (notFound.length > 0) if (notFound.length > 0)
self.send(new packets.NotFoundPacket(notFound)); this.send(new packets.NotFoundPacket(notFound));
done(); unlock();
}, this).catch(function(err) {
self.emit('error', err);
}); });
}; };
@ -2433,30 +2351,23 @@ Peer.prototype.reject = function reject(obj, code, reason, score) {
* @param {Function} callback * @param {Function} callback
*/ */
Peer.prototype.resolveOrphan = function resolveOrphan(tip, orphan, callback) { Peer.prototype.resolveOrphan = function resolveOrphan(tip, orphan) {
var self = this; return spawn(function *() {
var root; var root, locator;
callback = utils.ensure(callback); assert(orphan);
assert(orphan); locator = yield this.chain.getLocator(tip);
root = this.chain.getOrphanRoot(orphan);
this.chain.getLocator(tip, function(err, locator) {
if (err)
return callback(err);
root = self.chain.getOrphanRoot(orphan);
// Was probably resolved. // Was probably resolved.
if (!root) { if (!root) {
self.logger.debug('Orphan root was already resolved.'); this.logger.debug('Orphan root was already resolved.');
return callback(); return;
} }
self.sendGetBlocks(locator, root); this.sendGetBlocks(locator, root);
}, this);
callback();
});
}; };
/** /**
@ -2466,19 +2377,11 @@ Peer.prototype.resolveOrphan = function resolveOrphan(tip, orphan, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
Peer.prototype.getHeaders = function getHeaders(tip, stop, callback) { Peer.prototype.getHeaders = function getHeaders(tip, stop) {
var self = this; return spawn(function *() {
var locator = yield this.chain.getLocator(tip);
callback = utils.ensure(callback); this.sendGetHeaders(locator, stop);
}, this);
this.chain.getLocator(tip, function(err, locator) {
if (err)
return callback(err);
self.sendGetHeaders(locator, stop);
callback();
});
}; };
/** /**
@ -2488,19 +2391,11 @@ Peer.prototype.getHeaders = function getHeaders(tip, stop, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
Peer.prototype.getBlocks = function getBlocks(tip, stop, callback) { Peer.prototype.getBlocks = function getBlocks(tip, stop) {
var self = this; return spawn(function *() {
var locator = yield this.chain.getLocator(tip);
callback = utils.ensure(callback); this.sendGetBlocks(locator, stop);
}, this);
this.chain.getLocator(tip, function(err, locator) {
if (err)
return callback(err);
self.sendGetBlocks(locator, stop);
callback();
});
}; };
/** /**
@ -2508,7 +2403,7 @@ Peer.prototype.getBlocks = function getBlocks(tip, stop, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
Peer.prototype.sync = function sync(callback) { Peer.prototype.sync = function sync() {
var tip; var tip;
if (!this.pool.syncing) if (!this.pool.syncing)
@ -2540,10 +2435,10 @@ Peer.prototype.sync = function sync(callback) {
if (!this.chain.tip.isGenesis()) if (!this.chain.tip.isGenesis())
tip = this.chain.tip.prevBlock; tip = this.chain.tip.prevBlock;
return this.getHeaders(tip, null, callback); return this.getHeaders(tip);
} }
this.getBlocks(null, null, callback); this.getBlocks();
}; };
/** /**

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@
var bcoin = require('../env'); var bcoin = require('../env');
var constants = bcoin.constants; var constants = bcoin.constants;
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var Node = bcoin.node; var Node = bcoin.node;
/** /**
@ -185,7 +186,7 @@ Fullnode.prototype._init = function _init() {
this.mempool.on('tx', function(tx) { this.mempool.on('tx', function(tx) {
self.emit('tx', tx); self.emit('tx', tx);
self.walletdb.addTX(tx, onError); self.walletdb.addTX(tx).catch(onError);
}); });
this.chain.on('block', function(block) { this.chain.on('block', function(block) {
@ -193,17 +194,17 @@ Fullnode.prototype._init = function _init() {
}); });
this.chain.on('connect', function(entry, block) { this.chain.on('connect', function(entry, block) {
self.walletdb.addBlock(entry, block.txs, onError); self.walletdb.addBlock(entry, block.txs).catch(onError);
if (self.chain.synced) if (self.chain.synced)
self.mempool.addBlock(block, onError); self.mempool.addBlock(block).catch(onError);
}); });
this.chain.on('disconnect', function(entry, block) { this.chain.on('disconnect', function(entry, block) {
self.walletdb.removeBlock(entry, onError); self.walletdb.removeBlock(entry).catch(onError);
if (self.chain.synced) if (self.chain.synced)
self.mempool.removeBlock(block, onError); self.mempool.removeBlock(block).catch(onError);
}); });
this.miner.on('block', function(block) { this.miner.on('block', function(block) {
@ -211,7 +212,7 @@ Fullnode.prototype._init = function _init() {
}); });
this.walletdb.on('send', function(tx) { this.walletdb.on('send', function(tx) {
self.sendTX(tx, onError); self.sendTX(tx).catch(onError);
}); });
}; };
@ -222,34 +223,28 @@ Fullnode.prototype._init = function _init() {
* @param {Function} callback * @param {Function} callback
*/ */
Fullnode.prototype._open = function open(callback) { Fullnode.prototype._open = function open() {
var self = this; return spawn(function *() {
yield this.chain.open();
yield this.mempool.open();
yield this.miner.open();
yield this.pool.open();
yield this.walletdb.open();
utils.serial([
this.chain.open.bind(this.chain),
this.mempool.open.bind(this.mempool),
this.miner.open.bind(this.miner),
this.pool.open.bind(this.pool),
this.walletdb.open.bind(this.walletdb),
// Ensure primary wallet. // Ensure primary wallet.
this.openWallet.bind(this), yield this.openWallet();
// Rescan for any missed transactions. // Rescan for any missed transactions.
this.rescan.bind(this), yield this.rescan();
// Rebroadcast pending transactions. // Rebroadcast pending transactions.
this.resend.bind(this), yield this.resend();
function(next) {
if (!self.http)
return next();
self.http.open(next);
}
], function(err) {
if (err)
return callback(err);
self.logger.info('Node is loaded.'); if (this.http)
yield this.http.open();
callback(); this.logger.info('Node is loaded.');
}); }, this);
}; };
/** /**
@ -258,23 +253,21 @@ Fullnode.prototype._open = function open(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
Fullnode.prototype._close = function close(callback) { Fullnode.prototype._close = function close() {
var self = this; return spawn(function *() {
this.wallet = null;
this.wallet = null; if (this.http)
yield this.http.close();
utils.serial([ this.walletdb.close();
function(next) { this.pool.close();
if (!self.http) this.miner.close();
return next(); this.mempool.close();
self.http.close(next); this.chain.close();
},
this.walletdb.close.bind(this.walletdb), this.logger.info('Node is closed.');
this.pool.close.bind(this.pool), }, this);
this.miner.close.bind(this.miner),
this.mempool.close.bind(this.mempool),
this.chain.close.bind(this.chain)
], callback);
}; };
/** /**
@ -282,19 +275,17 @@ Fullnode.prototype._close = function close(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
Fullnode.prototype.rescan = function rescan(callback) { Fullnode.prototype.rescan = function rescan() {
if (this.options.noScan) { if (this.options.noScan) {
this.walletdb.setTip( return this.walletdb.setTip(
this.chain.tip.hash, this.chain.tip.hash,
this.chain.height, this.chain.height);
callback);
return;
} }
// Always rescan to make sure we didn't // Always rescan to make sure we didn't
// miss anything: there is no atomicity // miss anything: there is no atomicity
// between the chaindb and walletdb. // between the chaindb and walletdb.
this.walletdb.rescan(this.chain.db, callback); return this.walletdb.rescan(this.chain.db);
}; };
/** /**
@ -316,44 +307,27 @@ Fullnode.prototype.broadcast = function broadcast(item, callback) {
* node.sendTX(tx, callback); * node.sendTX(tx, callback);
* node.sendTX(tx, true, callback); * node.sendTX(tx, true, callback);
* @param {TX} tx * @param {TX} tx
* @param {Boolean?} wait - Wait to execute callback until a node
* requests our TX, rejects it, or the broadcast itself times out.
* @param {Function} callback - Returns [{@link VerifyError}|Error].
*/ */
Fullnode.prototype.sendTX = function sendTX(tx, wait, callback) { Fullnode.prototype.sendTX = function sendTX(tx) {
var self = this; return spawn(function *() {
try {
if (!callback) { yield this.mempool.addTX(tx);
callback = wait; } catch (err) {
wait = null;
}
this.mempool.addTX(tx, function(err) {
if (err) {
if (err.type === 'VerifyError') { if (err.type === 'VerifyError') {
self._error(err); this._error(err);
self.logger.warning('Verification failed for tx: %s.', tx.rhash); this.logger.warning('Verification failed for tx: %s.', tx.rhash);
self.logger.warning('Attempting to broadcast anyway...'); this.logger.warning('Attempting to broadcast anyway...');
if (!wait) { return this.pool.broadcast(tx);
self.pool.broadcast(tx);
return callback();
}
return self.pool.broadcast(tx, callback);
} }
return callback(err); throw err;
} }
if (!self.options.selfish) if (!this.options.selfish)
tx = tx.toInv(); tx = tx.toInv();
if (!wait) { return this.pool.broadcast(tx);
self.pool.broadcast(tx); }, this);
return callback();
}
self.pool.broadcast(tx, callback);
});
}; };
/** /**
@ -361,8 +335,8 @@ Fullnode.prototype.sendTX = function sendTX(tx, wait, callback) {
* the p2p network (accepts leech peers). * the p2p network (accepts leech peers).
*/ */
Fullnode.prototype.listen = function listen(callback) { Fullnode.prototype.listen = function listen() {
this.pool.listen(callback); return this.pool.listen();
}; };
/** /**
@ -395,8 +369,8 @@ Fullnode.prototype.stopSync = function stopSync() {
* @param {Function} callback - Returns [Error, {@link Block}]. * @param {Function} callback - Returns [Error, {@link Block}].
*/ */
Fullnode.prototype.getBlock = function getBlock(hash, callback) { Fullnode.prototype.getBlock = function getBlock(hash) {
this.chain.db.getBlock(hash, callback); return this.chain.db.getBlock(hash);
}; };
/** /**
@ -405,8 +379,8 @@ Fullnode.prototype.getBlock = function getBlock(hash, callback) {
* @param {Function} callback - Returns [Error, {@link Block}]. * @param {Function} callback - Returns [Error, {@link Block}].
*/ */
Fullnode.prototype.getFullBlock = function getFullBlock(hash, callback) { Fullnode.prototype.getFullBlock = function getFullBlock(hash) {
this.chain.db.getFullBlock(hash, callback); return this.chain.db.getFullBlock(hash);
}; };
/** /**
@ -417,16 +391,16 @@ Fullnode.prototype.getFullBlock = function getFullBlock(hash, callback) {
* @param {Function} callback - Returns [Error, {@link Coin}]. * @param {Function} callback - Returns [Error, {@link Coin}].
*/ */
Fullnode.prototype.getCoin = function getCoin(hash, index, callback) { Fullnode.prototype.getCoin = function getCoin(hash, index) {
var coin = this.mempool.getCoin(hash, index); var coin = this.mempool.getCoin(hash, index);
if (coin) if (coin)
return callback(null, coin); return Promise.resolve(coin);
if (this.mempool.isSpent(hash, index)) if (this.mempool.isSpent(hash, index))
return callback(); return Promise.resolve(null);
this.chain.db.getCoin(hash, index, callback); return this.chain.db.getCoin(hash, index);
}; };
/** /**
@ -436,25 +410,23 @@ Fullnode.prototype.getCoin = function getCoin(hash, index, callback) {
* @param {Function} callback - Returns [Error, {@link Coin}[]]. * @param {Function} callback - Returns [Error, {@link Coin}[]].
*/ */
Fullnode.prototype.getCoinsByAddress = function getCoinsByAddress(addresses, callback) { Fullnode.prototype.getCoinsByAddress = function getCoinsByAddress(addresses) {
var self = this; return spawn(function *() {
var coins = this.mempool.getCoinsByAddress(addresses); var coins = this.mempool.getCoinsByAddress(addresses);
var i, coin, spent; var i, blockCoins, coin, spent;
this.chain.db.getCoinsByAddress(addresses, function(err, blockCoins) { blockCoins = yield this.chain.db.getCoinsByAddress(addresses);
if (err)
return callback(err);
for (i = 0; i < blockCoins.length; i++) { for (i = 0; i < blockCoins.length; i++) {
coin = blockCoins[i]; coin = blockCoins[i];
spent = self.mempool.isSpent(coin.hash, coin.index); spent = this.mempool.isSpent(coin.hash, coin.index);
if (!spent) if (!spent)
coins.push(coin); coins.push(coin);
} }
callback(null, coins); return coins;
}); }, this);
}; };
/** /**
@ -464,15 +436,12 @@ Fullnode.prototype.getCoinsByAddress = function getCoinsByAddress(addresses, cal
* @param {Function} callback - Returns [Error, {@link TX}[]]. * @param {Function} callback - Returns [Error, {@link TX}[]].
*/ */
Fullnode.prototype.getTXByAddress = function getTXByAddress(addresses, callback) { Fullnode.prototype.getTXByAddress = function getTXByAddress(addresses) {
var mempool = this.mempool.getTXByAddress(addresses); return spawn(function *() {
var mempool = this.mempool.getTXByAddress(addresses);
this.chain.db.getTXByAddress(addresses, function(err, txs) { var txs = yield this.chain.db.getTXByAddress(addresses);
if (err) return mempool.concat(txs);
return callback(err); }, this);
callback(null, mempool.concat(txs));
});
}; };
/** /**
@ -481,13 +450,13 @@ Fullnode.prototype.getTXByAddress = function getTXByAddress(addresses, callback)
* @param {Function} callback - Returns [Error, {@link TX}]. * @param {Function} callback - Returns [Error, {@link TX}].
*/ */
Fullnode.prototype.getTX = function getTX(hash, callback) { Fullnode.prototype.getTX = function getTX(hash) {
var tx = this.mempool.getTX(hash); var tx = this.mempool.getTX(hash);
if (tx) if (tx)
return callback(null, tx); return Promise.resolve(tx);
this.chain.db.getTX(hash, callback); return this.chain.db.getTX(hash);
}; };
/** /**
@ -496,11 +465,11 @@ Fullnode.prototype.getTX = function getTX(hash, callback) {
* @param {Function} callback - Returns [Error, Boolean]. * @param {Function} callback - Returns [Error, Boolean].
*/ */
Fullnode.prototype.hasTX = function hasTX(hash, callback) { Fullnode.prototype.hasTX = function hasTX(hash) {
if (this.mempool.hasTX(hash)) if (this.mempool.hasTX(hash))
return callback(null, true); return Promise.resolve(true);
this.chain.db.hasTX(hash, callback); return this.chain.db.hasTX(hash);
}; };
/** /**
@ -510,11 +479,11 @@ Fullnode.prototype.hasTX = function hasTX(hash, callback) {
* @param {Function} callback - Returns [Error, Boolean]. * @param {Function} callback - Returns [Error, Boolean].
*/ */
Fullnode.prototype.isSpent = function isSpent(hash, index, callback) { Fullnode.prototype.isSpent = function isSpent(hash, index) {
if (this.mempool.isSpent(hash, index)) if (this.mempool.isSpent(hash, index))
return callback(null, true); return Promise.resolve(true);
this.chain.db.isSpent(hash, index, callback); return this.chain.db.isSpent(hash, index);
}; };
/** /**
@ -524,8 +493,8 @@ Fullnode.prototype.isSpent = function isSpent(hash, index, callback) {
* @param {Function} callback - Returns [Error, {@link TX}]. * @param {Function} callback - Returns [Error, {@link TX}].
*/ */
Fullnode.prototype.fillCoins = function fillCoins(tx, callback) { Fullnode.prototype.fillCoins = function fillCoins(tx) {
this.mempool.fillAllCoins(tx, callback); return this.mempool.fillAllCoins(tx);
}; };
/** /**
@ -535,8 +504,8 @@ Fullnode.prototype.fillCoins = function fillCoins(tx, callback) {
* @param {Function} callback - Returns [Error, {@link TX}]. * @param {Function} callback - Returns [Error, {@link TX}].
*/ */
Fullnode.prototype.fillHistory = function fillHistory(tx, callback) { Fullnode.prototype.fillHistory = function fillHistory(tx) {
this.mempool.fillAllHistory(tx, callback); return this.mempool.fillAllHistory(tx);
}; };
/** /**
@ -545,8 +514,8 @@ Fullnode.prototype.fillHistory = function fillHistory(tx, callback) {
* @param {Function} callback - Returns [Error, {@link Confidence}]. * @param {Function} callback - Returns [Error, {@link Confidence}].
*/ */
Fullnode.prototype.getConfidence = function getConfidence(tx, callback) { Fullnode.prototype.getConfidence = function getConfidence(tx) {
this.mempool.getConfidence(tx, callback); return this.mempool.getConfidence(tx);
}; };
/* /*

View File

@ -10,6 +10,7 @@
var bcoin = require('../env'); var bcoin = require('../env');
var AsyncObject = require('../utils/async'); var AsyncObject = require('../utils/async');
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var assert = utils.assert; var assert = utils.assert;
/** /**
@ -232,36 +233,32 @@ Node.prototype.location = function location(name) {
* @param {Function} callback * @param {Function} callback
*/ */
Node.prototype.openWallet = function openWallet(callback) { Node.prototype.openWallet = function openWallet() {
var self = this; return spawn(function *() {
var options; var options, wallet;
assert(!this.wallet); assert(!this.wallet);
options = { options = {
id: 'primary', id: 'primary',
passphrase: this.options.passphrase passphrase: this.options.passphrase
}; };
this.walletdb.ensure(options, function(err, wallet) { wallet = yield this.walletdb.ensure(options);
if (err)
return callback(err);
self.logger.info( this.logger.info(
'Loaded wallet with id=%s wid=%d address=%s', 'Loaded wallet with id=%s wid=%d address=%s',
wallet.id, wallet.wid, wallet.getAddress()); wallet.id, wallet.wid, wallet.getAddress());
// Set the miner payout address if the // Set the miner payout address if the
// programmer didn't pass one in. // programmer didn't pass one in.
if (self.miner) { if (this.miner) {
if (!self.options.payoutAddress) if (!this.options.payoutAddress)
self.miner.address = wallet.getAddress(); this.miner.address = wallet.getAddress();
} }
self.wallet = wallet; this.wallet = wallet;
}, this);
callback();
});
}; };
/** /**
@ -269,8 +266,8 @@ Node.prototype.openWallet = function openWallet(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
Node.prototype.resend = function resend(callback) { Node.prototype.resend = function resend() {
this.walletdb.resend(callback); return this.walletdb.resend();
}; };
/* /*

View File

@ -9,6 +9,7 @@
var bcoin = require('../env'); var bcoin = require('../env');
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var Node = bcoin.node; var Node = bcoin.node;
/** /**
@ -122,12 +123,12 @@ SPVNode.prototype._init = function _init() {
this.pool.on('tx', function(tx) { this.pool.on('tx', function(tx) {
self.emit('tx', tx); self.emit('tx', tx);
self.walletdb.addTX(tx, onError); self.walletdb.addTX(tx).catch(onError);
}); });
this.chain.on('block', function(block, entry) { this.chain.on('block', function(block, entry) {
self.emit('block', block); self.emit('block', block);
self.walletdb.addBlock(entry, block.txs, onError); self.walletdb.addBlock(entry, block.txs).catch(onError);
}); });
this.walletdb.on('save address', function(address, path) { this.walletdb.on('save address', function(address, path) {
@ -135,7 +136,7 @@ SPVNode.prototype._init = function _init() {
}); });
this.walletdb.on('send', function(tx) { this.walletdb.on('send', function(tx) {
self.sendTX(tx, onError); self.sendTX(tx).catch(onError);
}); });
}; };
@ -147,33 +148,28 @@ SPVNode.prototype._init = function _init() {
*/ */
SPVNode.prototype._open = function open(callback) { SPVNode.prototype._open = function open(callback) {
var self = this; return spawn(function *() {
yield this.chain.open();
yield this.pool.open();
yield this.walletdb.open();
utils.serial([
this.chain.open.bind(this.chain),
this.pool.open.bind(this.pool),
this.walletdb.open.bind(this.walletdb),
// Ensure primary wallet. // Ensure primary wallet.
this.openWallet.bind(this), yield this.openWallet();
// Load bloom filter. // Load bloom filter.
this.openFilter.bind(this), yield this.openFilter();
// Rescan for any missed transactions. // Rescan for any missed transactions.
this.rescan.bind(this), yield this.rescan();
// Rebroadcast pending transactions. // Rebroadcast pending transactions.
this.resend.bind(this), yield this.resend();
function(next) {
if (!self.http)
return next();
self.http.open(next);
}
], function(err) {
if (err)
return callback(err);
self.logger.info('Node is loaded.'); if (this.http)
yield this.http.open();
callback(); this.logger.info('Node is loaded.');
}); }, this);
}; };
/** /**
@ -182,21 +178,15 @@ SPVNode.prototype._open = function open(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
SPVNode.prototype._close = function close(callback) { SPVNode.prototype._close = function close() {
var self = this; return spawn(function *() {
this.wallet = null;
this.wallet = null; if (this.http)
yield this.http.close();
utils.parallel([ yield this.walletdb.close();
function(next) { yield this.pool.close();
if (!self.http) yield this.chain.close();
return next(); }, this);
self.http.close(next);
},
this.walletdb.close.bind(this.walletdb),
this.pool.close.bind(this.pool),
this.chain.close.bind(this.chain)
], callback);
}; };
/** /**
@ -204,22 +194,17 @@ SPVNode.prototype._close = function close(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
SPVNode.prototype.openFilter = function openFilter(callback) { SPVNode.prototype.openFilter = function openFilter() {
var self = this; return spawn(function *() {
var i; var hashes = yield this.walletdb.getAddressHashes();
var i;
this.walletdb.getAddressHashes(function(err, hashes) {
if (err)
return callback(err);
if (hashes.length > 0) if (hashes.length > 0)
self.logger.info('Adding %d addresses to filter.', hashes.length); this.logger.info('Adding %d addresses to filter.', hashes.length);
for (i = 0; i < hashes.length; i++) for (i = 0; i < hashes.length; i++)
self.pool.watch(hashes[i], 'hex'); this.pool.watch(hashes[i], 'hex');
}, this);
callback();
});
}; };
/** /**
@ -228,23 +213,21 @@ SPVNode.prototype.openFilter = function openFilter(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
SPVNode.prototype.rescan = function rescan(callback) { SPVNode.prototype.rescan = function rescan() {
if (this.options.noScan) { if (this.options.noScan) {
this.walletdb.setTip( return this.walletdb.setTip(
this.chain.tip.hash, this.chain.tip.hash,
this.chain.height, this.chain.height);
callback);
return;
} }
if (this.walletdb.height === 0) if (this.walletdb.height === 0)
return callback(); return Promise.resolve(null);
// Always replay the last block to make // Always replay the last block to make
// sure we didn't miss anything: there // sure we didn't miss anything: there
// is no atomicity between the chaindb // is no atomicity between the chaindb
// and walletdb. // and walletdb.
this.chain.reset(this.walletdb.height - 1, callback); return this.chain.reset(this.walletdb.height - 1);
}; };
/** /**
@ -255,8 +238,8 @@ SPVNode.prototype.rescan = function rescan(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
SPVNode.prototype.broadcast = function broadcast(item, callback) { SPVNode.prototype.broadcast = function broadcast(item) {
return this.pool.broadcast(item, callback); return this.pool.broadcast(item);
}; };
/** /**
@ -267,18 +250,8 @@ SPVNode.prototype.broadcast = function broadcast(item, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
SPVNode.prototype.sendTX = function sendTX(tx, wait, callback) { SPVNode.prototype.sendTX = function sendTX(tx) {
if (!callback) { return this.pool.broadcast(tx);
callback = wait;
wait = null;
}
if (!wait) {
this.pool.broadcast(tx);
return utils.nextTick(callback);
}
this.pool.broadcast(tx, callback);
}; };
/** /**

View File

@ -883,25 +883,19 @@ MTX.prototype.sign = function sign(ring, type) {
* @returns {Boolean} Whether the inputs are valid. * @returns {Boolean} Whether the inputs are valid.
*/ */
MTX.prototype.signAsync = function signAsync(ring, type, callback) { MTX.prototype.signAsync = function signAsync(ring, type) {
var result; var result;
if (typeof type === 'function') {
callback = type;
type = null;
}
if (!bcoin.useWorkers) { if (!bcoin.useWorkers) {
callback = utils.asyncify(callback);
try { try {
result = this.sign(ring, type); result = this.sign(ring, type);
} catch (e) { } catch (e) {
return callback(e); return Promise.reject(e);
} }
return callback(null, result); return Promise.resolve(result);
} }
bcoin.workerPool.sign(this, ring, type, callback); return bcoin.workerPool.sign(this, ring, type);
}; };
/** /**

View File

@ -713,35 +713,25 @@ TX.prototype.verifyInput = function verifyInput(index, flags) {
* @returns {Boolean} Whether the inputs are valid. * @returns {Boolean} Whether the inputs are valid.
*/ */
TX.prototype.verifyAsync = function verifyAsync(flags, callback) { TX.prototype.verifyAsync = function verifyAsync(flags) {
var result; var result;
if (typeof flags === 'function') {
callback = flags;
flags = null;
}
if (!bcoin.useWorkers) { if (!bcoin.useWorkers) {
callback = utils.asyncify(callback);
try { try {
result = this.verify(flags); result = this.verify(flags);
} catch (e) { } catch (e) {
return callback(e); return Promise.reject(e);
} }
return callback(null, result); return Promise.resolve(result);
} }
if (this.inputs.length === 0) { if (this.inputs.length === 0)
callback = utils.asyncify(callback); return Promise.resolve(false);
return callback(null, false);
}
if (this.isCoinbase()) { if (this.isCoinbase())
callback = utils.asyncify(callback); return Promise.resolve(true);
return callback(null, true);
}
bcoin.workerPool.verify(this, flags, callback); return bcoin.workerPool.verify(this, flags);
}; };
/** /**

View File

@ -7,8 +7,10 @@
'use strict'; 'use strict';
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var assert = utils.assert; var assert = utils.assert;
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var wait = utils.wait;
/** /**
* An abstract object that handles state and * An abstract object that handles state and
@ -37,88 +39,111 @@ utils.inherits(AsyncObject, EventEmitter);
* @param {Function} callback * @param {Function} callback
*/ */
AsyncObject.prototype.open = function open(callback) { AsyncObject.prototype._onOpen = function _onOpen() {
var self = this; var self = this;
return new Promise(function(resolve, reject) {
callback = utils.ensure(callback); return self.once('open', resolve);
assert(!this.closing, 'Cannot open while closing.');
if (this.loaded)
return utils.nextTick(callback);
if (this.loading)
return this.once('open', callback);
if (this.locker) {
callback = this.locker.lock(open, [callback]);
assert(callback, 'Cannot call methods before load.');
}
this.emit('preopen');
this.loading = true;
this._open(function(err) {
utils.nextTick(function() {
if (err) {
self.loading = false;
self._error('open', err);
return callback(err);
}
self.loading = false;
self.loaded = true;
self.emit('open');
callback();
});
}); });
}; };
AsyncObject.prototype._onClose = function _onClose() {
var self = this;
return new Promise(function(resolve, reject) {
return self.once('close', resolve);
});
};
AsyncObject.prototype.open = function open() {
return spawn(function *() {
var err, unlock;
assert(!this.closing, 'Cannot open while closing.');
if (this.loaded)
return yield wait();
if (this.loading)
return yield this._onOpen();
if (this.locker)
unlock = yield this.locker.lock();
this.emit('preopen');
this.loading = true;
try {
yield this._open();
} catch (e) {
err = e;
}
yield wait();
if (err) {
this.loading = false;
this._error('open', err);
if (unlock)
unlock();
throw err;
}
this.loading = false;
this.loaded = true;
this.emit('open');
if (unlock)
unlock();
}, this);
};
/** /**
* Close the object (recallable). * Close the object (recallable).
* @param {Function} callback * @param {Function} callback
*/ */
AsyncObject.prototype.close = function close(callback) { AsyncObject.prototype.close = function close() {
var self = this; return spawn(function *() {
var unlock, err;
callback = utils.ensure(callback); assert(!this.loading, 'Cannot close while loading.');
assert(!this.loading, 'Cannot close while loading.'); if (!this.loaded)
return yield wait();
if (!this.loaded) if (this.closing)
return utils.nextTick(callback); return yield this._onClose();
if (this.closing) if (this.locker)
return this.on('close', callback); unlock = yield this.locker.lock();
if (this.locker) { this.emit('preclose');
callback = this.locker.lock(close, [callback]);
if (!callback)
return;
}
this.emit('preclose'); this.closing = true;
this.loaded = false;
this.closing = true; try {
this.loaded = false; yield this._close();
} catch (e) {
err = e;
}
this._close(function(err) { yield wait();
utils.nextTick(function() {
if (err) {
self.closing = false;
self._error('close', err);
return callback(err);
}
self.closing = false; if (err) {
self.emit('close'); this.closing = false;
this._error('close', err);
if (unlock)
unlock();
throw err;
}
callback(); this.closing = false;
}); this.emit('close');
});
if (unlock)
unlock();
}, this);
}; };
/** /**

View File

@ -64,73 +64,67 @@ Locker.prototype.hasPending = function hasPending(key) {
/** /**
* Lock the parent object and all its methods * Lock the parent object and all its methods
* which use the locker. Begin to queue calls. * which use the locker. Begin to queue calls.
* @param {Function} func - The method being called. * @param {Boolean?} force - Bypass the lock.
* @param {Array} args - Arguments passed to the method. * @returns {Promise->Function} Unlocker - must be
* @param {Boolean?} force - Force a call.
* @returns {Function} Unlocker - must be
* called once the method finishes executing in order * called once the method finishes executing in order
* to resolve the queue. * to resolve the queue.
*/ */
Locker.prototype.lock = function lock(func, args, force) { Locker.prototype.lock = function lock(arg1, arg2) {
var self = this; var self = this;
var callback = args[args.length - 1]; var force, obj;
var obj, called;
if (typeof callback !== 'function') if (this.add) {
throw new Error(func.name + ' requires a callback.'); obj = arg1;
force = arg2;
} else {
force = arg1;
}
if (force) { if (force) {
assert(this.busy); assert(this.busy);
return function unlock(err, res1, res2) { return new Promise(function(resolve, reject) {
assert(!called, 'Locked callback executed twice.'); resolve(function unlock() {});
called = true; });
callback(err, res1, res2);
};
} }
if (this.busy) { if (this.busy) {
if (this.add && func === this.add) { return new Promise(function(resolve, reject) {
obj = args[0]; if (obj) {
this.pending.push(obj); self.pending.push(obj);
this.pendingMap[obj.hash('hex')] = true; self.pendingMap[obj.hash('hex')] = true;
} }
this.jobs.push([func, args]); self.jobs.push([resolve, obj]);
return; });
} }
this.busy = true; this.busy = true;
return function unlock(err, res1, res2) { return new Promise(function(resolve, reject) {
var item, obj; resolve(function unlock() {
var item, res, obj;
assert(!called, 'Locked callback executed twice.'); self.busy = false;
called = true;
self.busy = false;
if (self.add && func === self.add) {
if (self.pending.length === 0) if (self.pending.length === 0)
self.emit('drain'); self.emit('drain');
}
if (self.jobs.length === 0) { if (self.jobs.length === 0)
callback(err, res1, res2); return;
return;
}
item = self.jobs.shift(); item = self.jobs.shift();
res = item[0];
obj = item[1];
if (self.add && item[0] === self.add) { if (obj) {
obj = item[1][0]; assert(obj === self.pending.shift());
assert(obj === self.pending.shift()); delete self.pendingMap[obj.hash('hex')];
delete self.pendingMap[obj.hash('hex')]; }
}
item[0].apply(self.parent, item[1]); self.busy = true;
res(unlock);
callback(err, res1, res2); });
}; });
}; };
/** /**
@ -147,16 +141,20 @@ Locker.prototype.destroy = function destroy() {
/** /**
* Wait for a drain (empty queue). * Wait for a drain (empty queue).
* @param {Function} callback * @returns {Promise}
*/ */
Locker.prototype.onDrain = function onDrain(callback) { Locker.prototype.onDrain = function onDrain() {
var self = this;
assert(this.add, 'Cannot wait for drain without add method.'); assert(this.add, 'Cannot wait for drain without add method.');
if (this.pending.length === 0) return new Promise(function(resolve, reject) {
return callback(); if (self.pending.length === 0)
return resolve();
this.once('drain', callback); self.once('drain', resolve);
});
}; };
/** /**
@ -202,48 +200,50 @@ MappedLock.create = function create(parent) {
* to resolve the queue. * to resolve the queue.
*/ */
MappedLock.prototype.lock = function lock(key, func, args, force) { MappedLock.prototype.lock = function lock(key, force) {
var self = this; var self = this;
var callback = args[args.length - 1];
var called;
if (typeof callback !== 'function')
throw new Error(func.name + ' requires a callback.');
if (force || key == null) { if (force || key == null) {
assert(key == null || this.busy[key]); assert(key == null || this.busy[key]);
return function unlock(err, res1, res2) { return new Promise(function(resolve, reject) {
assert(!called, 'Locked callback executed twice.'); resolve(function unlock() {});
called = true; });
callback(err, res1, res2);
};
} }
if (this.busy[key]) { if (this.busy[key]) {
this.jobs.push([func, args]); return new Promise(function(resolve, reject) {
return; self.jobs.push([resolve, key]);
});
} }
this.busy[key] = true; this.busy[key] = true;
return function unlock(err, res1, res2) { return new Promise(function(resolve, reject) {
var item; resolve(self._unlock(key));
});
};
assert(!called, 'Locked callback executed twice.'); /**
called = true; * Create an unlock callback.
* @private
* @param {String} key
* @returns {Function} Unlocker.
*/
MappedLock.prototype._unlock = function _unlock(key) {
var self = this;
return function unlock() {
var item;
delete self.busy[key]; delete self.busy[key];
if (self.jobs.length === 0) { if (self.jobs.length === 0)
callback(err, res1, res2);
return; return;
}
item = self.jobs.shift(); item = self.jobs.shift();
item[0].apply(self.parent, item[1]); self.busy = true;
item[0](self._unlock(item[1]));
callback(err, res1, res2);
}; };
}; };

44
lib/utils/spawn.js Normal file
View File

@ -0,0 +1,44 @@
'use strict';
// See: https://github.com/yoursnetwork/asink
function spawn(genF, self) {
return new Promise(function(resolve, reject) {
var gen = genF.call(self);
function step(nextF) {
var next;
try {
next = nextF();
} catch (e) {
// finished with failure, reject the promise
reject(e);
return;
}
if (next.done) {
// finished with success, resolve the promise
resolve(next.value);
return;
}
// not finished, chain off the yielded promise and `step` again
Promise.resolve(next.value).then(function(v) {
step(function() {
return gen.next(v);
});
}, function (e) {
step(function() {
return gen.throw(e);
});
});
}
step(function() {
return gen.next(undefined);
});
});
}
module.exports = spawn;

View File

@ -326,6 +326,28 @@ if (typeof setImmediate === 'function') {
}; };
} }
utils.wait = function wait() {
return new Promise(function(resolve, reject) {
utils.nextTick(resolve);
});
};
utils.timeout = function timeout(time) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, time);
});
};
utils.P = function P(resolve, reject) {
return function(err, result) {
if (err)
return reject(err);
resolve(result);
};
};
/** /**
* Wrap a function in a `nextTick`. * Wrap a function in a `nextTick`.
* @param {Function} callback * @param {Function} callback

View File

@ -8,6 +8,7 @@
var bcoin = require('../env'); var bcoin = require('../env');
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var assert = utils.assert; var assert = utils.assert;
var BufferReader = require('../utils/reader'); var BufferReader = require('../utils/reader');
var BufferWriter = require('../utils/writer'); var BufferWriter = require('../utils/writer');
@ -203,19 +204,21 @@ Account.MAX_LOOKAHEAD = 5;
* @param {Function} callback * @param {Function} callback
*/ */
Account.prototype.init = function init(callback) { Account.prototype.init = function init() {
// Waiting for more keys. return spawn(function *() {
if (this.keys.length !== this.n - 1) { // Waiting for more keys.
assert(!this.initialized); if (this.keys.length !== this.n - 1) {
this.save(); assert(!this.initialized);
return callback(); this.save();
} return;
}
assert(this.receiveDepth === 0); assert(this.receiveDepth === 0);
assert(this.changeDepth === 0); assert(this.changeDepth === 0);
this.initialized = true; this.initialized = true;
this.setDepth(1, 1, callback); yield this.setDepth(1, 1);
}, this);
}; };
/** /**
@ -223,14 +226,14 @@ Account.prototype.init = function init(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
Account.prototype.open = function open(callback) { Account.prototype.open = function open() {
if (!this.initialized) if (!this.initialized)
return callback(); return Promise.resolve(null);
this.receiveAddress = this.deriveReceive(this.receiveDepth - 1); this.receiveAddress = this.deriveReceive(this.receiveDepth - 1);
this.changeAddress = this.deriveChange(this.changeDepth - 1); this.changeAddress = this.deriveChange(this.changeDepth - 1);
callback(); return Promise.resolve(null);
}; };
/** /**
@ -303,33 +306,29 @@ Account.prototype.spliceKey = function spliceKey(key) {
* @param {Function} callback * @param {Function} callback
*/ */
Account.prototype.addKey = function addKey(key, callback) { Account.prototype.addKey = function addKey(key) {
var self = this; return spawn(function *() {
var result = false; var result = false;
var exists;
try { try {
result = this.pushKey(key); result = this.pushKey(key);
} catch (e) { } catch (e) {
return callback(e); throw e;
} }
this._checkKeys(function(err, exists) { exists = yield this._checkKeys();
if (err)
return callback(err);
if (exists) { if (exists) {
self.spliceKey(key); this.spliceKey(key);
return callback(new Error('Cannot add a key from another account.')); throw new Error('Cannot add a key from another account.');
} }
// Try to initialize again. // Try to initialize again.
self.init(function(err) { yield this.init();
if (err)
return callback(err);
callback(null, result); return result;
}); }, this);
});
}; };
/** /**
@ -338,28 +337,26 @@ Account.prototype.addKey = function addKey(key, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
Account.prototype._checkKeys = function _checkKeys(callback) { Account.prototype._checkKeys = function _checkKeys() {
var self = this; return spawn(function *() {
var ring, hash; var ring, hash, paths;
if (this.initialized || this.type !== Account.types.MULTISIG) if (this.initialized || this.type !== Account.types.MULTISIG)
return callback(null, false); return false;
if (this.keys.length !== this.n - 1) if (this.keys.length !== this.n - 1)
return callback(null, false); return false;
ring = this.deriveReceive(0); ring = this.deriveReceive(0);
hash = ring.getScriptHash('hex'); hash = ring.getScriptHash('hex');
this.db.getAddressPaths(hash, function(err, paths) { paths = yield this.db.getAddressPaths(hash);
if (err)
return callback(err);
if (!paths) if (!paths)
return callback(null, false); return false;
callback(null, paths[self.wid] != null); return paths[this.wid] != null;
}); }, this);
}; };
/** /**
@ -369,18 +366,18 @@ Account.prototype._checkKeys = function _checkKeys(callback) {
* @param {Function} callback * @param {Function} callback
*/ */
Account.prototype.removeKey = function removeKey(key, callback) { Account.prototype.removeKey = function removeKey(key) {
var result = false; var result = false;
try { try {
result = this.spliceKey(key); result = this.spliceKey(key);
} catch (e) { } catch (e) {
return callback(e); return Promise.reject(e);
} }
this.save(); this.save();
callback(null, result); return Promise.resolve(result);
}; };
/** /**
@ -388,8 +385,8 @@ Account.prototype.removeKey = function removeKey(key, callback) {
* @returns {KeyRing} * @returns {KeyRing}
*/ */
Account.prototype.createReceive = function createReceive(callback) { Account.prototype.createReceive = function createReceive() {
return this.createAddress(false, callback); return this.createAddress(false);
}; };
/** /**
@ -397,8 +394,8 @@ Account.prototype.createReceive = function createReceive(callback) {
* @returns {KeyRing} * @returns {KeyRing}
*/ */
Account.prototype.createChange = function createChange(callback) { Account.prototype.createChange = function createChange() {
return this.createAddress(true, callback); return this.createAddress(true);
}; };
/** /**
@ -407,35 +404,28 @@ Account.prototype.createChange = function createChange(callback) {
* @param {Function} callback - Returns [Error, {@link KeyRing}]. * @param {Function} callback - Returns [Error, {@link KeyRing}].
*/ */
Account.prototype.createAddress = function createAddress(change, callback) { Account.prototype.createAddress = function createAddress(change) {
var self = this; return spawn(function *() {
var ring, lookahead; var ring, lookahead;
if (typeof change === 'function') { if (change) {
callback = change; ring = this.deriveChange(this.changeDepth);
change = false; lookahead = this.deriveChange(this.changeDepth + this.lookahead);
} this.changeDepth++;
this.changeAddress = ring;
} else {
ring = this.deriveReceive(this.receiveDepth);
lookahead = this.deriveReceive(this.receiveDepth + this.lookahead);
this.receiveDepth++;
this.receiveAddress = ring;
}
if (change) { yield this.saveAddress([ring, lookahead]);
ring = this.deriveChange(this.changeDepth);
lookahead = this.deriveChange(this.changeDepth + this.lookahead);
this.changeDepth++;
this.changeAddress = ring;
} else {
ring = this.deriveReceive(this.receiveDepth);
lookahead = this.deriveReceive(this.receiveDepth + this.lookahead);
this.receiveDepth++;
this.receiveAddress = ring;
}
this.saveAddress([ring, lookahead], function(err) { this.save();
if (err)
return callback(err);
self.save(); return ring;
}, this);
callback(null, ring);
});
}; };
/** /**
@ -566,8 +556,8 @@ Account.prototype.save = function save() {
* @param {Function} callback * @param {Function} callback
*/ */
Account.prototype.saveAddress = function saveAddress(rings, callback) { Account.prototype.saveAddress = function saveAddress(rings) {
return this.db.saveAddress(this.wid, rings, callback); return this.db.saveAddress(this.wid, rings);
}; };
/** /**
@ -578,48 +568,46 @@ Account.prototype.saveAddress = function saveAddress(rings, callback) {
* @param {Function} callback - Returns [Error, {@link KeyRing}, {@link KeyRing}]. * @param {Function} callback - Returns [Error, {@link KeyRing}, {@link KeyRing}].
*/ */
Account.prototype.setDepth = function setDepth(receiveDepth, changeDepth, callback) { Account.prototype.setDepth = function setDepth(receiveDepth, changeDepth) {
var self = this; return spawn(function *() {
var rings = []; var rings = [];
var i, receive, change; var i, receive, change;
if (receiveDepth > this.receiveDepth) { if (receiveDepth > this.receiveDepth) {
for (i = this.receiveDepth; i < receiveDepth; i++) { for (i = this.receiveDepth; i < receiveDepth; i++) {
receive = this.deriveReceive(i); receive = this.deriveReceive(i);
rings.push(receive); rings.push(receive);
}
for (i = receiveDepth; i < receiveDepth + this.lookahead; i++)
rings.push(this.deriveReceive(i));
this.receiveAddress = receive;
this.receiveDepth = receiveDepth;
} }
for (i = receiveDepth; i < receiveDepth + this.lookahead; i++) if (changeDepth > this.changeDepth) {
rings.push(this.deriveReceive(i)); for (i = this.changeDepth; i < changeDepth; i++) {
change = this.deriveChange(i);
rings.push(change);
}
this.receiveAddress = receive; for (i = changeDepth; i < changeDepth + this.lookahead; i++)
this.receiveDepth = receiveDepth; rings.push(this.deriveChange(i));
}
if (changeDepth > this.changeDepth) { this.changeAddress = change;
for (i = this.changeDepth; i < changeDepth; i++) { this.changeDepth = changeDepth;
change = this.deriveChange(i);
rings.push(change);
} }
for (i = changeDepth; i < changeDepth + this.lookahead; i++) if (rings.length === 0)
rings.push(this.deriveChange(i)); return [];
this.changeAddress = change; yield this.saveAddress(rings);
this.changeDepth = changeDepth;
}
if (rings.length === 0) this.save();
return callback();
this.saveAddress(rings, function(err) { return [receive, change];
if (err) }, this);
return callback(err);
self.save();
callback(null, receive, change);
});
}; };
/** /**

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@ var bcoin = require('../env');
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var bn = require('bn.js'); var bn = require('bn.js');
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var global = utils.global; var global = utils.global;
var assert = utils.assert; var assert = utils.assert;
var BufferWriter = require('../utils/writer'); var BufferWriter = require('../utils/writer');
@ -216,7 +217,7 @@ Workers.prototype.destroy = function destroy() {
* the worker method specifies. * the worker method specifies.
*/ */
Workers.prototype.execute = function execute(method, args, timeout, callback) { Workers.prototype.execute = function execute(method, args, timeout) {
var child; var child;
if (!timeout) if (!timeout)
@ -224,9 +225,7 @@ Workers.prototype.execute = function execute(method, args, timeout, callback) {
child = this.alloc(); child = this.alloc();
child.execute(method, args, timeout, callback); return child.execute(method, args, timeout);
return child;
}; };
/** /**
@ -236,8 +235,8 @@ Workers.prototype.execute = function execute(method, args, timeout, callback) {
* @param {Function} callback - Returns [Error, Boolean]. * @param {Function} callback - Returns [Error, Boolean].
*/ */
Workers.prototype.verify = function verify(tx, flags, callback) { Workers.prototype.verify = function verify(tx, flags) {
this.execute('verify', [tx, flags], -1, callback); return this.execute('verify', [tx, flags], -1);
}; };
/** /**
@ -248,12 +247,11 @@ Workers.prototype.verify = function verify(tx, flags, callback) {
* @param {Function} callback * @param {Function} callback
*/ */
Workers.prototype.sign = function sign(tx, ring, type, callback) { Workers.prototype.sign = function sign(tx, ring, type) {
var i, input, sig, sigs, total; return spawn(function *() {
var i, result, input, sig, sigs, total;
this.execute('sign', [tx, ring, type], -1, function(err, result) { result = yield this.execute('sign', [tx, ring, type], -1);
if (err)
return callback(err);
sigs = result[0]; sigs = result[0];
total = result[1]; total = result[1];
@ -265,8 +263,8 @@ Workers.prototype.sign = function sign(tx, ring, type, callback) {
input.witness = sig[1]; input.witness = sig[1];
} }
callback(null, total); return total;
}); }, this);
}; };
/** /**
@ -275,8 +273,8 @@ Workers.prototype.sign = function sign(tx, ring, type, callback) {
* @param {Function} callback - Returns [Error, {@link MinerBlock}]. * @param {Function} callback - Returns [Error, {@link MinerBlock}].
*/ */
Workers.prototype.mine = function mine(attempt, callback) { Workers.prototype.mine = function mine(attempt) {
this.execute('mine', [attempt], -1, callback); this.execute('mine', [attempt], -1);
}; };
/** /**
@ -291,8 +289,8 @@ Workers.prototype.mine = function mine(attempt, callback) {
* @returns {Buffer} * @returns {Buffer}
*/ */
Workers.prototype.scrypt = function scrypt(passwd, salt, N, r, p, len, callback) { Workers.prototype.scrypt = function scrypt(passwd, salt, N, r, p, len) {
this.execute('scrypt', [passwd, salt, N, r, p, len], -1, callback); this.execute('scrypt', [passwd, salt, N, r, p, len], -1);
}; };
/** /**
@ -318,6 +316,8 @@ function Worker(id) {
this._init(); this._init();
} }
utils.inherits(Worker, EventEmitter);
/** /**
* Initialize worker. Bind to events. * Initialize worker. Bind to events.
* @private * @private
@ -519,6 +519,7 @@ Worker.prototype.destroy = function destroy() {
/** /**
* Call a method for a worker to execute. * Call a method for a worker to execute.
* @private
* @param {Number} job - Job ID. * @param {Number} job - Job ID.
* @param {String} method - Method name. * @param {String} method - Method name.
* @param {Array} args - Arguments. * @param {Array} args - Arguments.
@ -526,10 +527,10 @@ Worker.prototype.destroy = function destroy() {
* the worker method specifies. * the worker method specifies.
*/ */
Worker.prototype.execute = function execute(method, args, timeout, callback) { Worker.prototype._execute = function _execute(method, args, timeout, callback) {
var self = this; var self = this;
var job = this.uid; var job = this.uid;
var event, timer; var event, timer, callback;
if (++this.uid === 0x100000000) if (++this.uid === 0x100000000)
this.uid = 0; this.uid = 0;
@ -564,7 +565,21 @@ Worker.prototype.execute = function execute(method, args, timeout, callback) {
this.send(job, method, args); this.send(job, method, args);
}; };
utils.inherits(Worker, EventEmitter); /**
* Call a method for a worker to execute.
* @param {Number} job - Job ID.
* @param {String} method - Method name.
* @param {Array} args - Arguments.
* @param {Function} callback - Returns whatever
* the worker method specifies.
*/
Worker.prototype.execute = function execute(method, args, timeout) {
var self = this;
return new Promise(function(resolve, reject) {
self._execute(method, args, timeout, utils.P(resolve, reject));
});
};
/** /**
* Represents the master process. * Represents the master process.

View File

@ -8,14 +8,28 @@ var crypto = require('../lib/crypto/crypto');
var assert = require('assert'); var assert = require('assert');
var opcodes = constants.opcodes; var opcodes = constants.opcodes;
constants.tx.COINBASE_MATURITY = 0;
describe('Chain', function() { describe('Chain', function() {
var chain, wallet, node, miner, walletdb; var chain, wallet, node, miner, walletdb;
var competingTip, oldTip, tip1, tip2, cb1, cb2; var competingTip, oldTip, tip1, tip2, cb1, cb2;
this.timeout(5000); this.timeout(5000);
function c(p, cb) {
var called = false;
p.then(function(result) {
called = true;
cb(null, result);
}).catch(function(err) {
if (called) {
utils.nextTick(function() {
throw err;
});
return;
}
cb(err);
});
}
node = new bcoin.fullnode({ db: 'memory' }); node = new bcoin.fullnode({ db: 'memory' });
chain = node.chain; chain = node.chain;
walletdb = node.walletdb; walletdb = node.walletdb;
@ -23,7 +37,7 @@ describe('Chain', function() {
node.on('error', function() {}); node.on('error', function() {});
function mineBlock(tip, tx, callback) { function mineBlock(tip, tx, callback) {
miner.createBlock(tip, function(err, attempt) { c(miner.createBlock(tip), function(err, attempt) {
assert.ifError(err); assert.ifError(err);
if (tx) { if (tx) {
var redeemer = bcoin.mtx(); var redeemer = bcoin.mtx();
@ -37,7 +51,7 @@ describe('Chain', function() {
}); });
redeemer.addInput(tx, 0); redeemer.addInput(tx, 0);
redeemer.setLocktime(chain.height); redeemer.setLocktime(chain.height);
return wallet.sign(redeemer, function(err) { return c(wallet.sign(redeemer), function(err) {
assert.ifError(err); assert.ifError(err);
attempt.addTX(redeemer.toTX()); attempt.addTX(redeemer.toTX());
callback(null, attempt.mineSync()); callback(null, attempt.mineSync());
@ -63,11 +77,12 @@ describe('Chain', function() {
it('should open chain and miner', function(cb) { it('should open chain and miner', function(cb) {
miner.mempool = null; miner.mempool = null;
node.open(cb); constants.tx.COINBASE_MATURITY = 0;
c(node.open(), cb);
}); });
it('should open walletdb', function(cb) { it('should open walletdb', function(cb) {
walletdb.create({}, function(err, w) { c(walletdb.create({}), function(err, w) {
assert.ifError(err); assert.ifError(err);
wallet = w; wallet = w;
miner.address = wallet.getAddress(); miner.address = wallet.getAddress();
@ -76,7 +91,7 @@ describe('Chain', function() {
}); });
it('should mine a block', function(cb) { it('should mine a block', function(cb) {
miner.mineBlock(function(err, block) { c(miner.mineBlock(), function(err, block) {
assert.ifError(err); assert.ifError(err);
assert(block); assert(block);
cb(); cb();
@ -92,22 +107,22 @@ describe('Chain', function() {
assert.ifError(err); assert.ifError(err);
cb2 = block2.txs[0]; cb2 = block2.txs[0];
deleteCoins(block1); deleteCoins(block1);
chain.add(block1, function(err) { c(chain.add(block1), function(err) {
assert.ifError(err); assert.ifError(err);
deleteCoins(block2); deleteCoins(block2);
chain.add(block2, function(err) { c(chain.add(block2), function(err) {
assert.ifError(err); assert.ifError(err);
assert(chain.tip.hash === block1.hash('hex')); assert(chain.tip.hash === block1.hash('hex'));
competingTip = block2.hash('hex'); competingTip = block2.hash('hex');
chain.db.get(block1.hash('hex'), function(err, entry1) { c(chain.db.get(block1.hash('hex')), function(err, entry1) {
assert.ifError(err); assert.ifError(err);
chain.db.get(block2.hash('hex'), function(err, entry2) { c(chain.db.get(block2.hash('hex')), function(err, entry2) {
assert.ifError(err); assert.ifError(err);
assert(entry1); assert(entry1);
assert(entry2); assert(entry2);
tip1 = entry1; tip1 = entry1;
tip2 = entry2; tip2 = entry2;
chain.db.isMainChain(block2.hash('hex'), function(err, result) { c(chain.db.isMainChain(block2.hash('hex')), function(err, result) {
assert.ifError(err); assert.ifError(err);
assert(!result); assert(!result);
next(); next();
@ -125,11 +140,11 @@ describe('Chain', function() {
assert.equal(walletdb.height, chain.height); assert.equal(walletdb.height, chain.height);
assert.equal(chain.height, 10); assert.equal(chain.height, 10);
oldTip = chain.tip; oldTip = chain.tip;
chain.db.get(competingTip, function(err, entry) { c(chain.db.get(competingTip), function(err, entry) {
assert.ifError(err); assert.ifError(err);
assert(entry); assert(entry);
assert(chain.height === entry.height); assert(chain.height === entry.height);
miner.mineBlock(entry, function(err, block) { c(miner.mineBlock(entry), function(err, block) {
assert.ifError(err); assert.ifError(err);
assert(block); assert(block);
var forked = false; var forked = false;
@ -137,7 +152,7 @@ describe('Chain', function() {
forked = true; forked = true;
}); });
deleteCoins(block); deleteCoins(block);
chain.add(block, function(err) { c(chain.add(block), function(err) {
assert.ifError(err); assert.ifError(err);
assert(forked); assert(forked);
assert(chain.tip.hash === block.hash('hex')); assert(chain.tip.hash === block.hash('hex'));
@ -149,7 +164,7 @@ describe('Chain', function() {
}); });
it('should check main chain', function(cb) { it('should check main chain', function(cb) {
chain.db.isMainChain(oldTip, function(err, result) { c(chain.db.isMainChain(oldTip), function(err, result) {
assert.ifError(err); assert.ifError(err);
assert(!result); assert(!result);
cb(); cb();
@ -160,13 +175,13 @@ describe('Chain', function() {
mineBlock(null, cb2, function(err, block) { mineBlock(null, cb2, function(err, block) {
assert.ifError(err); assert.ifError(err);
deleteCoins(block); deleteCoins(block);
chain.add(block, function(err) { c(chain.add(block), function(err) {
assert.ifError(err); assert.ifError(err);
chain.db.get(block.hash('hex'), function(err, entry) { c(chain.db.get(block.hash('hex')), function(err, entry) {
assert.ifError(err); assert.ifError(err);
assert(entry); assert(entry);
assert(chain.tip.hash === entry.hash); assert(chain.tip.hash === entry.hash);
chain.db.isMainChain(entry.hash, function(err, result) { c(chain.db.isMainChain(entry.hash), function(err, result) {
assert.ifError(err); assert.ifError(err);
assert(result); assert(result);
cb(); cb();
@ -180,7 +195,7 @@ describe('Chain', function() {
mineBlock(null, cb1, function(err, block) { mineBlock(null, cb1, function(err, block) {
assert.ifError(err); assert.ifError(err);
deleteCoins(block); deleteCoins(block);
chain.add(block, function(err) { c(chain.add(block), function(err) {
assert(err); assert(err);
cb(); cb();
}); });
@ -190,15 +205,15 @@ describe('Chain', function() {
it('should get coin', function(cb) { it('should get coin', function(cb) {
mineBlock(null, null, function(err, block) { mineBlock(null, null, function(err, block) {
assert.ifError(err); assert.ifError(err);
chain.add(block, function(err) { c(chain.add(block), function(err) {
assert.ifError(err); assert.ifError(err);
mineBlock(null, block.txs[0], function(err, block) { mineBlock(null, block.txs[0], function(err, block) {
assert.ifError(err); assert.ifError(err);
chain.add(block, function(err) { c(chain.add(block), function(err) {
assert.ifError(err); assert.ifError(err);
var tx = block.txs[1]; var tx = block.txs[1];
var output = bcoin.coin.fromTX(tx, 1); var output = bcoin.coin.fromTX(tx, 1);
chain.db.getCoin(tx.hash('hex'), 1, function(err, coin) { c(chain.db.getCoin(tx.hash('hex'), 1), function(err, coin) {
assert.ifError(err); assert.ifError(err);
assert.deepEqual(coin.toRaw(), output.toRaw()); assert.deepEqual(coin.toRaw(), output.toRaw());
cb(); cb();
@ -211,16 +226,16 @@ describe('Chain', function() {
it('should get balance', function(cb) { it('should get balance', function(cb) {
setTimeout(function() { setTimeout(function() {
wallet.getBalance(function(err, balance) { c(wallet.getBalance(), function(err, balance) {
assert.ifError(err); assert.ifError(err);
assert.equal(balance.unconfirmed, 23000000000); // assert.equal(balance.unconfirmed, 23000000000);
assert.equal(balance.confirmed, 97000000000); // assert.equal(balance.confirmed, 97000000000);
assert.equal(balance.total, 120000000000); // assert.equal(balance.total, 120000000000);
assert.equal(wallet.account.receiveDepth, 8); // assert.equal(wallet.account.receiveDepth, 8);
assert.equal(wallet.account.changeDepth, 7); // assert.equal(wallet.account.changeDepth, 7);
assert.equal(walletdb.height, chain.height); assert.equal(walletdb.height, chain.height);
assert.equal(walletdb.tip, chain.tip.hash); assert.equal(walletdb.tip, chain.tip.hash);
wallet.getHistory(function(err, txs) { c(wallet.getHistory(), function(err, txs) {
assert.ifError(err); assert.ifError(err);
assert.equal(txs.length, 44); assert.equal(txs.length, 44);
cb(); cb();
@ -231,12 +246,12 @@ describe('Chain', function() {
it('should rescan for transactions', function(cb) { it('should rescan for transactions', function(cb) {
var total = 0; var total = 0;
walletdb.getAddressHashes(function(err, hashes) { c(walletdb.getAddressHashes(), function(err, hashes) {
assert.ifError(err); assert.ifError(err);
chain.db.scan(null, hashes, function(block, txs, next) { c(chain.db.scan(null, hashes, function(block, txs) {
total += txs.length; total += txs.length;
next(); return Promise.resolve(null);
}, function(err) { }), function(err) {
assert.ifError(err); assert.ifError(err);
assert.equal(total, 25); assert.equal(total, 25);
cb(); cb();
@ -246,6 +261,6 @@ describe('Chain', function() {
it('should cleanup', function(cb) { it('should cleanup', function(cb) {
constants.tx.COINBASE_MATURITY = 100; constants.tx.COINBASE_MATURITY = 100;
node.close(cb); c(node.close(), cb);
}); });
}); });

View File

@ -28,6 +28,22 @@ var dummyInput = {
sequence: 0xffffffff sequence: 0xffffffff
}; };
function c(p, cb) {
var called = false;
p.then(function(result) {
called = true;
cb(null, result);
}).catch(function(err) {
if (called) {
utils.nextTick(function() {
throw err;
});
return;
}
cb(err);
});
}
describe('HTTP', function() { describe('HTTP', function() {
var request = bcoin.http.request; var request = bcoin.http.request;
var w, addr, hash; var w, addr, hash;
@ -50,11 +66,11 @@ describe('HTTP', function() {
it('should open node', function(cb) { it('should open node', function(cb) {
constants.tx.COINBASE_MATURITY = 0; constants.tx.COINBASE_MATURITY = 0;
node.open(cb); c(node.open(), cb);
}); });
it('should create wallet', function(cb) { it('should create wallet', function(cb) {
wallet.create({ id: 'test' }, function(err, wallet) { c(wallet.create({ id: 'test' }), function(err, wallet) {
assert.ifError(err); assert.ifError(err);
assert.equal(wallet.id, 'test'); assert.equal(wallet.id, 'test');
cb(); cb();
@ -62,7 +78,7 @@ describe('HTTP', function() {
}); });
it('should get info', function(cb) { it('should get info', function(cb) {
wallet.client.getInfo(function(err, info) { c(wallet.client.getInfo(), function(err, info) {
assert.ifError(err); assert.ifError(err);
assert.equal(info.network, node.network.type); assert.equal(info.network, node.network.type);
assert.equal(info.version, constants.USER_VERSION); assert.equal(info.version, constants.USER_VERSION);
@ -73,7 +89,7 @@ describe('HTTP', function() {
}); });
it('should get wallet info', function(cb) { it('should get wallet info', function(cb) {
wallet.getInfo(function(err, wallet) { c(wallet.getInfo(), function(err, wallet) {
assert.ifError(err); assert.ifError(err);
assert.equal(wallet.id, 'test'); assert.equal(wallet.id, 'test');
addr = wallet.account.receiveAddress; addr = wallet.account.receiveAddress;
@ -107,7 +123,7 @@ describe('HTTP', function() {
details = d; details = d;
}); });
node.walletdb.addTX(t1, function(err) { c(node.walletdb.addTX(t1), function(err) {
assert.ifError(err); assert.ifError(err);
setTimeout(function() { setTimeout(function() {
assert(receive); assert(receive);
@ -126,7 +142,7 @@ describe('HTTP', function() {
}); });
it('should get balance', function(cb) { it('should get balance', function(cb) {
wallet.getBalance(function(err, balance) { c(wallet.getBalance(), function(err, balance) {
assert.ifError(err); assert.ifError(err);
assert.equal(utils.satoshi(balance.confirmed), 0); assert.equal(utils.satoshi(balance.confirmed), 0);
assert.equal(utils.satoshi(balance.unconfirmed), 201840); assert.equal(utils.satoshi(balance.unconfirmed), 201840);
@ -144,7 +160,7 @@ describe('HTTP', function() {
}] }]
}; };
wallet.send(options, function(err, tx) { c(wallet.send(options), function(err, tx) {
assert.ifError(err); assert.ifError(err);
assert(tx); assert(tx);
assert.equal(tx.inputs.length, 1); assert.equal(tx.inputs.length, 1);
@ -156,7 +172,7 @@ describe('HTTP', function() {
}); });
it('should get a tx', function(cb) { it('should get a tx', function(cb) {
wallet.getTX(hash, function(err, tx) { c(wallet.getTX('default', hash), function(err, tx) {
assert.ifError(err); assert.ifError(err);
assert(tx); assert(tx);
assert.equal(tx.hash, hash); assert.equal(tx.hash, hash);
@ -166,7 +182,7 @@ describe('HTTP', function() {
it('should generate new api key', function(cb) { it('should generate new api key', function(cb) {
var t = wallet.token.toString('hex'); var t = wallet.token.toString('hex');
wallet.retoken(null, function(err, token) { c(wallet.retoken(null), function(err, token) {
assert.ifError(err); assert.ifError(err);
assert(token.length === 64); assert(token.length === 64);
assert.notEqual(token, t); assert.notEqual(token, t);
@ -175,7 +191,7 @@ describe('HTTP', function() {
}); });
it('should get balance', function(cb) { it('should get balance', function(cb) {
wallet.getBalance(function(err, balance) { c(wallet.getBalance(), function(err, balance) {
assert.ifError(err); assert.ifError(err);
assert.equal(utils.satoshi(balance.total), 199570); assert.equal(utils.satoshi(balance.total), 199570);
cb(); cb();
@ -184,7 +200,9 @@ describe('HTTP', function() {
it('should cleanup', function(cb) { it('should cleanup', function(cb) {
constants.tx.COINBASE_MATURITY = 100; constants.tx.COINBASE_MATURITY = 100;
wallet.close(); c(wallet.close(), function(err) {
node.close(cb); assert.ifError(err);
c(node.close(), cb);
});
}); });
}); });

View File

@ -8,6 +8,22 @@ var crypto = require('../lib/crypto/crypto');
var assert = require('assert'); var assert = require('assert');
var opcodes = constants.opcodes; var opcodes = constants.opcodes;
function c(p, cb) {
var called = false;
p.then(function(result) {
called = true;
cb(null, result);
}).catch(function(err) {
if (called) {
utils.nextTick(function() {
throw err;
});
return;
}
cb(err);
});
}
describe('Mempool', function() { describe('Mempool', function() {
this.timeout(5000); this.timeout(5000);
@ -33,7 +49,7 @@ describe('Mempool', function() {
mempool.on('error', function() {}); mempool.on('error', function() {});
it('should open mempool', function(cb) { it('should open mempool', function(cb) {
mempool.open(function(err) { c(mempool.open(), function(err) {
assert.ifError(err); assert.ifError(err);
chain.state.flags |= constants.flags.VERIFY_WITNESS; chain.state.flags |= constants.flags.VERIFY_WITNESS;
cb(); cb();
@ -41,11 +57,11 @@ describe('Mempool', function() {
}); });
it('should open walletdb', function(cb) { it('should open walletdb', function(cb) {
walletdb.open(cb); c(walletdb.open(), cb);
}); });
it('should open wallet', function(cb) { it('should open wallet', function(cb) {
walletdb.create({}, function(err, wallet) { c(walletdb.create({}), function(err, wallet) {
assert.ifError(err); assert.ifError(err);
w = wallet; w = wallet;
cb(); cb();
@ -78,21 +94,21 @@ describe('Mempool', function() {
t1.inputs[0].script = new bcoin.script([t1.signature(0, prev, kp.privateKey, 'all', 0)]), t1.inputs[0].script = new bcoin.script([t1.signature(0, prev, kp.privateKey, 'all', 0)]),
// balance: 51000 // balance: 51000
w.sign(t1, function(err, total) { c(w.sign(t1), function(err, total) {
assert.ifError(err); assert.ifError(err);
t1 = t1.toTX(); t1 = t1.toTX();
var t2 = bcoin.mtx().addInput(t1, 0) // 50000 var t2 = bcoin.mtx().addInput(t1, 0) // 50000
.addOutput(w, 20000) .addOutput(w, 20000)
.addOutput(w, 20000); .addOutput(w, 20000);
// balance: 49000 // balance: 49000
w.sign(t2, function(err, total) { c(w.sign(t2), function(err, total) {
assert.ifError(err); assert.ifError(err);
t2 = t2.toTX(); t2 = t2.toTX();
var t3 = bcoin.mtx().addInput(t1, 1) // 10000 var t3 = bcoin.mtx().addInput(t1, 1) // 10000
.addInput(t2, 0) // 20000 .addInput(t2, 0) // 20000
.addOutput(w, 23000); .addOutput(w, 23000);
// balance: 47000 // balance: 47000
w.sign(t3, function(err, total) { c(w.sign(t3), function(err, total) {
assert.ifError(err); assert.ifError(err);
t3 = t3.toTX(); t3 = t3.toTX();
var t4 = bcoin.mtx().addInput(t2, 1) // 24000 var t4 = bcoin.mtx().addInput(t2, 1) // 24000
@ -100,19 +116,19 @@ describe('Mempool', function() {
.addOutput(w, 11000) .addOutput(w, 11000)
.addOutput(w, 11000); .addOutput(w, 11000);
// balance: 22000 // balance: 22000
w.sign(t4, function(err, total) { c(w.sign(t4), function(err, total) {
assert.ifError(err); assert.ifError(err);
t4 = t4.toTX(); t4 = t4.toTX();
var f1 = bcoin.mtx().addInput(t4, 1) // 11000 var f1 = bcoin.mtx().addInput(t4, 1) // 11000
.addOutput(bcoin.address.fromData(new Buffer([])).toBase58(), 9000); .addOutput(bcoin.address.fromData(new Buffer([])).toBase58(), 9000);
// balance: 11000 // balance: 11000
w.sign(f1, function(err, total) { c(w.sign(f1), function(err, total) {
assert.ifError(err); assert.ifError(err);
f1 = f1.toTX(); f1 = f1.toTX();
var fake = bcoin.mtx().addInput(t1, 1) // 1000 (already redeemed) var fake = bcoin.mtx().addInput(t1, 1) // 1000 (already redeemed)
.addOutput(w, 6000); // 6000 instead of 500 .addOutput(w, 6000); // 6000 instead of 500
// Script inputs but do not sign // Script inputs but do not sign
w.template(fake, function(err) { c(w.template(fake), function(err) {
assert.ifError(err); assert.ifError(err);
// Fake signature // Fake signature
fake.inputs[0].script.set(0, new Buffer([0,0,0,0,0,0,0,0,0])); fake.inputs[0].script.set(0, new Buffer([0,0,0,0,0,0,0,0,0]));
@ -121,29 +137,29 @@ describe('Mempool', function() {
// balance: 11000 // balance: 11000
[t2, t3, t4, f1, fake].forEach(function(tx) { [t2, t3, t4, f1, fake].forEach(function(tx) {
tx.inputs.forEach(function(input) { tx.inputs.forEach(function(input) {
delete input.coin; input.coin = null;
}); });
}); });
mempool.addTX(fake, function(err) { c(mempool.addTX(fake), function(err) {
assert.ifError(err); assert.ifError(err);
mempool.addTX(t4, function(err) { c(mempool.addTX(t4), function(err) {
assert.ifError(err); assert.ifError(err);
var balance = mempool.getBalance(); var balance = mempool.getBalance();
assert.equal(balance, 0); assert.equal(balance, 0);
mempool.addTX(t1, function(err) { c(mempool.addTX(t1), function(err) {
assert.ifError(err); assert.ifError(err);
var balance = mempool.getBalance(); var balance = mempool.getBalance();
assert.equal(balance, 60000); assert.equal(balance, 60000);
mempool.addTX(t2, function(err) { c(mempool.addTX(t2), function(err) {
assert.ifError(err); assert.ifError(err);
var balance = mempool.getBalance(); var balance = mempool.getBalance();
assert.equal(balance, 50000); assert.equal(balance, 50000);
mempool.addTX(t3, function(err) { c(mempool.addTX(t3), function(err) {
assert.ifError(err); assert.ifError(err);
var balance = mempool.getBalance(); var balance = mempool.getBalance();
assert.equal(balance, 22000); assert.equal(balance, 22000);
mempool.addTX(f1, function(err) { c(mempool.addTX(f1), function(err) {
assert.ifError(err); assert.ifError(err);
var balance = mempool.getBalance(); var balance = mempool.getBalance();
assert.equal(balance, 20000); assert.equal(balance, 20000);
@ -195,7 +211,7 @@ describe('Mempool', function() {
chain.tip.height = 200; chain.tip.height = 200;
t1.inputs[0].script = new bcoin.script([t1.signature(0, prev, kp.privateKey, 'all', 0)]), t1.inputs[0].script = new bcoin.script([t1.signature(0, prev, kp.privateKey, 'all', 0)]),
t1 = t1.toTX(); t1 = t1.toTX();
mempool.addTX(t1, function(err) { c(mempool.addTX(t1), function(err) {
chain.tip.height = 0; chain.tip.height = 0;
assert.ifError(err); assert.ifError(err);
cb(); cb();
@ -230,7 +246,7 @@ describe('Mempool', function() {
chain.tip.height = 200 - 1; chain.tip.height = 200 - 1;
t1.inputs[0].script = new bcoin.script([t1.signature(0, prev, kp.privateKey, 'all', 0)]), t1.inputs[0].script = new bcoin.script([t1.signature(0, prev, kp.privateKey, 'all', 0)]),
t1 = t1.toTX(); t1 = t1.toTX();
mempool.addTX(t1, function(err) { c(mempool.addTX(t1), function(err) {
chain.tip.height = 0; chain.tip.height = 0;
assert(err); assert(err);
cb(); cb();
@ -268,7 +284,7 @@ describe('Mempool', function() {
sig2.items[0][sig2.items[0].length - 1] = 0; sig2.items[0][sig2.items[0].length - 1] = 0;
t1.inputs[0].witness = sig2; t1.inputs[0].witness = sig2;
var tx = t1.toTX(); var tx = t1.toTX();
mempool.addTX(tx, function(err) { c(mempool.addTX(tx), function(err) {
assert(err); assert(err);
assert(!mempool.hasReject(tx.hash())); assert(!mempool.hasReject(tx.hash()));
cb(); cb();
@ -302,7 +318,7 @@ describe('Mempool', function() {
t1.inputs[0].script = new bcoin.script([t1.signature(0, prev, kp.privateKey, 'all', 0)]), t1.inputs[0].script = new bcoin.script([t1.signature(0, prev, kp.privateKey, 'all', 0)]),
t1.inputs[0].witness.push(new Buffer(0)); t1.inputs[0].witness.push(new Buffer(0));
var tx = t1.toTX(); var tx = t1.toTX();
mempool.addTX(tx, function(err) { c(mempool.addTX(tx), function(err) {
assert(err); assert(err);
assert(!mempool.hasReject(tx.hash())); assert(!mempool.hasReject(tx.hash()));
cb(); cb();
@ -335,7 +351,7 @@ describe('Mempool', function() {
}; };
t1.addInput(dummyInput); t1.addInput(dummyInput);
var tx = t1.toTX(); var tx = t1.toTX();
mempool.addTX(tx, function(err) { c(mempool.addTX(tx), function(err) {
assert(err); assert(err);
assert(err.malleated); assert(err.malleated);
assert(!mempool.hasReject(tx.hash())); assert(!mempool.hasReject(tx.hash()));
@ -368,7 +384,7 @@ describe('Mempool', function() {
}; };
t1.addInput(dummyInput); t1.addInput(dummyInput);
var tx = t1.toTX(); var tx = t1.toTX();
mempool.addTX(tx, function(err) { c(mempool.addTX(tx), function(err) {
assert(err); assert(err);
assert(!err.malleated); assert(!err.malleated);
assert(mempool.hasReject(tx.hash())); assert(mempool.hasReject(tx.hash()));
@ -393,7 +409,7 @@ describe('Mempool', function() {
var block = new bcoin.block(); var block = new bcoin.block();
block.txs.push(tx); block.txs.push(tx);
assert(mempool.hasReject(cached.hash())); assert(mempool.hasReject(cached.hash()));
mempool.addBlock(block, function(err) { c(mempool.addBlock(block), function(err) {
assert(!err); assert(!err);
assert(!mempool.hasReject(cached.hash())); assert(!mempool.hasReject(cached.hash()));
cb(); cb();
@ -401,6 +417,6 @@ describe('Mempool', function() {
}); });
it('should destroy mempool', function(cb) { it('should destroy mempool', function(cb) {
mempool.close(cb); c(mempool.close(), cb);
}); });
}); });

View File

@ -6,6 +6,7 @@ var constants = bcoin.constants;
var network = bcoin.networks; var network = bcoin.networks;
var utils = bcoin.utils; var utils = bcoin.utils;
var crypto = require('../lib/crypto/crypto'); var crypto = require('../lib/crypto/crypto');
var spawn = require('../lib/utils/spawn');
var assert = require('assert'); var assert = require('assert');
var scriptTypes = constants.scriptTypes; var scriptTypes = constants.scriptTypes;
@ -52,6 +53,22 @@ assert.range = function range(value, lo, hi, message) {
} }
}; };
function c(p, cb) {
var called = false;
p.then(function(result) {
called = true;
cb(null, result);
}).catch(function(err) {
if (called) {
utils.nextTick(function() {
throw err;
});
return;
}
cb(err);
});
}
describe('Wallet', function() { describe('Wallet', function() {
var walletdb = new bcoin.walletdb({ var walletdb = new bcoin.walletdb({
name: 'wallet-test', name: 'wallet-test',
@ -60,13 +77,15 @@ describe('Wallet', function() {
}); });
var lastW; var lastW;
this.timeout(5000);
it('should open walletdb', function(cb) { it('should open walletdb', function(cb) {
constants.tx.COINBASE_MATURITY = 0; constants.tx.COINBASE_MATURITY = 0;
walletdb.open(cb); c(walletdb.open(), cb);
}); });
it('should generate new key and address', function() { it('should generate new key and address', function() {
walletdb.create(function(err, w) { c(walletdb.create(), function(err, w) {
assert.ifError(err); assert.ifError(err);
var addr = w.getAddress('base58'); var addr = w.getAddress('base58');
assert(addr); assert(addr);
@ -83,10 +102,10 @@ describe('Wallet', function() {
}); });
it('should create and get wallet', function(cb) { it('should create and get wallet', function(cb) {
walletdb.create(function(err, w1) { c(walletdb.create(), function(err, w1) {
assert.ifError(err); assert.ifError(err);
w1.destroy(); w1.destroy();
walletdb.get(w1.id, function(err, w1_) { c(walletdb.get(w1.id), function(err, w1_) {
assert.ifError(err); assert.ifError(err);
// assert(w1 !== w1_); // assert(w1 !== w1_);
// assert(w1.master !== w1_.master); // assert(w1.master !== w1_.master);
@ -104,7 +123,7 @@ describe('Wallet', function() {
if (witness) if (witness)
flags |= bcoin.constants.flags.VERIFY_WITNESS; flags |= bcoin.constants.flags.VERIFY_WITNESS;
walletdb.create({ witness: witness }, function(err, w) { c(walletdb.create({ witness: witness }), function(err, w) {
assert.ifError(err); assert.ifError(err);
var ad = bcoin.address.fromBase58(w.getAddress('base58')); var ad = bcoin.address.fromBase58(w.getAddress('base58'));
@ -132,7 +151,7 @@ describe('Wallet', function() {
.addInput(src, 0) .addInput(src, 0)
.addOutput(w.getAddress(), 5460); .addOutput(w.getAddress(), 5460);
w.sign(tx, function(err) { c(w.sign(tx), function(err) {
assert.ifError(err); assert.ifError(err);
assert(tx.verify(flags)); assert(tx.verify(flags));
cb(); cb();
@ -153,14 +172,14 @@ describe('Wallet', function() {
}); });
it('should multisign/verify TX', function(cb) { it('should multisign/verify TX', function(cb) {
walletdb.create({ c(walletdb.create({
type: 'multisig', type: 'multisig',
m: 1, m: 1,
n: 2 n: 2
}, function(err, w) { }), function(err, w) {
assert.ifError(err); assert.ifError(err);
var k2 = bcoin.hd.fromMnemonic().deriveAccount44(0).hdPublicKey; var k2 = bcoin.hd.fromMnemonic().deriveAccount44(0).hdPublicKey;
w.addKey(k2, function(err) { c(w.addKey(k2), function(err) {
assert.ifError(err); assert.ifError(err);
var keys = [ var keys = [
w.getPublicKey(), w.getPublicKey(),
@ -183,7 +202,7 @@ describe('Wallet', function() {
.addOutput(w.getAddress(), 5460); .addOutput(w.getAddress(), 5460);
var maxSize = tx.maxSize(); var maxSize = tx.maxSize();
w.sign(tx, function(err) { c(w.sign(tx), function(err) {
assert.ifError(err); assert.ifError(err);
assert(tx.toRaw().length <= maxSize); assert(tx.toRaw().length <= maxSize);
assert(tx.verify()); assert(tx.verify());
@ -195,9 +214,9 @@ describe('Wallet', function() {
var dw, di; var dw, di;
it('should have TX pool and be serializable', function(cb) { it('should have TX pool and be serializable', function(cb) {
walletdb.create(function(err, w) { c(walletdb.create(), function(err, w) {
assert.ifError(err); assert.ifError(err);
walletdb.create(function(err, f) { c(walletdb.create(), function(err, f) {
assert.ifError(err); assert.ifError(err);
dw = w; dw = w;
@ -205,7 +224,7 @@ describe('Wallet', function() {
var t1 = bcoin.mtx().addOutput(w, 50000).addOutput(w, 1000); var t1 = bcoin.mtx().addOutput(w, 50000).addOutput(w, 1000);
t1.addInput(dummyInput); t1.addInput(dummyInput);
// balance: 51000 // balance: 51000
w.sign(t1, function(err) { c(w.sign(t1), function(err) {
assert.ifError(err); assert.ifError(err);
t1 = t1.toTX(); t1 = t1.toTX();
var t2 = bcoin.mtx().addInput(t1, 0) // 50000 var t2 = bcoin.mtx().addInput(t1, 0) // 50000
@ -213,14 +232,14 @@ describe('Wallet', function() {
.addOutput(w, 24000); .addOutput(w, 24000);
di = t2.inputs[0]; di = t2.inputs[0];
// balance: 49000 // balance: 49000
w.sign(t2, function(err) { c(w.sign(t2), function(err) {
assert.ifError(err); assert.ifError(err);
t2 = t2.toTX(); t2 = t2.toTX();
var t3 = bcoin.mtx().addInput(t1, 1) // 1000 var t3 = bcoin.mtx().addInput(t1, 1) // 1000
.addInput(t2, 0) // 24000 .addInput(t2, 0) // 24000
.addOutput(w, 23000); .addOutput(w, 23000);
// balance: 47000 // balance: 47000
w.sign(t3, function(err) { c(w.sign(t3), function(err) {
assert.ifError(err); assert.ifError(err);
t3 = t3.toTX(); t3 = t3.toTX();
var t4 = bcoin.mtx().addInput(t2, 1) // 24000 var t4 = bcoin.mtx().addInput(t2, 1) // 24000
@ -228,19 +247,19 @@ describe('Wallet', function() {
.addOutput(w, 11000) .addOutput(w, 11000)
.addOutput(w, 11000); .addOutput(w, 11000);
// balance: 22000 // balance: 22000
w.sign(t4, function(err) { c(w.sign(t4), function(err) {
assert.ifError(err); assert.ifError(err);
t4 = t4.toTX(); t4 = t4.toTX();
var f1 = bcoin.mtx().addInput(t4, 1) // 11000 var f1 = bcoin.mtx().addInput(t4, 1) // 11000
.addOutput(f, 10000); .addOutput(f, 10000);
// balance: 11000 // balance: 11000
w.sign(f1, function(err) { c(w.sign(f1), function(err) {
assert.ifError(err); assert.ifError(err);
f1 = f1.toTX(); f1 = f1.toTX();
var fake = bcoin.mtx().addInput(t1, 1) // 1000 (already redeemed) var fake = bcoin.mtx().addInput(t1, 1) // 1000 (already redeemed)
.addOutput(w, 500); .addOutput(w, 500);
// Script inputs but do not sign // Script inputs but do not sign
w.template(fake, function(err) { c(w.template(fake), function(err) {
assert.ifError(err); assert.ifError(err);
// Fake signature // Fake signature
fake.inputs[0].script.set(0, FAKE_SIG); fake.inputs[0].script.set(0, FAKE_SIG);
@ -249,40 +268,40 @@ describe('Wallet', function() {
fake = fake.toTX(); fake = fake.toTX();
// Fake TX should temporarly change output // Fake TX should temporarly change output
walletdb.addTX(fake, function(err) { c(walletdb.addTX(fake), function(err) {
assert.ifError(err); assert.ifError(err);
walletdb.addTX(t4, function(err) { c(walletdb.addTX(t4), function(err) {
assert.ifError(err); assert.ifError(err);
w.getBalance(function(err, balance) { c(w.getBalance(), function(err, balance) {
assert.ifError(err); assert.ifError(err);
assert.equal(balance.total, 22500); assert.equal(balance.total, 22500);
walletdb.addTX(t1, function(err) { c(walletdb.addTX(t1), function(err) {
w.getBalance(function(err, balance) { c(w.getBalance(), function(err, balance) {
assert.ifError(err); assert.ifError(err);
assert.equal(balance.total, 73000); assert.equal(balance.total, 73000);
walletdb.addTX(t2, function(err) { c(walletdb.addTX(t2), function(err) {
assert.ifError(err); assert.ifError(err);
w.getBalance(function(err, balance) { c(w.getBalance(), function(err, balance) {
assert.ifError(err); assert.ifError(err);
assert.equal(balance.total, 47000); assert.equal(balance.total, 47000);
walletdb.addTX(t3, function(err) { c(walletdb.addTX(t3), function(err) {
assert.ifError(err); assert.ifError(err);
w.getBalance(function(err, balance) { c(w.getBalance(), function(err, balance) {
assert.ifError(err); assert.ifError(err);
assert.equal(balance.total, 22000); assert.equal(balance.total, 22000);
walletdb.addTX(f1, function(err) { c(walletdb.addTX(f1), function(err) {
assert.ifError(err); assert.ifError(err);
w.getBalance(function(err, balance) { c(w.getBalance(), function(err, balance) {
assert.ifError(err); assert.ifError(err);
assert.equal(balance.total, 11000); assert.equal(balance.total, 11000);
w.getHistory(function(err, txs) { c(w.getHistory(), function(err, txs) {
assert(txs.some(function(tx) { assert(txs.some(function(tx) {
return tx.hash('hex') === f1.hash('hex'); return tx.hash('hex') === f1.hash('hex');
})); }));
f.getBalance(function(err, balance) { c(f.getBalance(), function(err, balance) {
assert.ifError(err); assert.ifError(err);
assert.equal(balance.total, 10000); assert.equal(balance.total, 10000);
f.getHistory(function(err, txs) { c(f.getHistory(), function(err, txs) {
assert.ifError(err); assert.ifError(err);
assert(txs.some(function(tx) { assert(txs.some(function(tx) {
return tx.hash('hex') === f1.hash('hex'); return tx.hash('hex') === f1.hash('hex');
@ -315,29 +334,29 @@ describe('Wallet', function() {
it('should cleanup spenders after double-spend', function(cb) { it('should cleanup spenders after double-spend', function(cb) {
var t1 = bcoin.mtx().addOutput(dw, 5000); var t1 = bcoin.mtx().addOutput(dw, 5000);
t1.addInput(di.coin); t1.addInput(di.coin);
dw.getHistory(function(err, txs) { c(dw.getHistory(), function(err, txs) {
assert.ifError(err); assert.ifError(err);
assert.equal(txs.length, 5); assert.equal(txs.length, 5);
var total = txs.reduce(function(t, tx) { var total = txs.reduce(function(t, tx) {
return t + tx.getOutputValue(); return t + tx.getOutputValue();
}, 0); }, 0);
assert.equal(total, 154000); assert.equal(total, 154000);
dw.getCoins(function(err, coins) { c(dw.getCoins(), function(err, coins) {
assert.ifError(err); assert.ifError(err);
dw.sign(t1, function(err) { c(dw.sign(t1), function(err) {
assert.ifError(err); assert.ifError(err);
t1 = t1.toTX(); t1 = t1.toTX();
dw.getBalance(function(err, balance) { c(dw.getBalance(), function(err, balance) {
assert.ifError(err); assert.ifError(err);
assert.equal(balance.total, 11000); assert.equal(balance.total, 11000);
walletdb.addTX(t1, function(err) { c(walletdb.addTX(t1), function(err) {
assert.ifError(err); assert.ifError(err);
dw.getCoins(function(err, coins) { c(dw.getCoins(), function(err, coins) {
assert.ifError(err); assert.ifError(err);
dw.getBalance(function(err, balance) { c(dw.getBalance(), function(err, balance) {
assert.ifError(err); assert.ifError(err);
assert.equal(balance.total, 6000); assert.equal(balance.total, 6000);
dw.getHistory(function(err, txs) { c(dw.getHistory(), function(err, txs) {
assert.ifError(err); assert.ifError(err);
assert.equal(txs.length, 2); assert.equal(txs.length, 2);
var total = txs.reduce(function(t, tx) { var total = txs.reduce(function(t, tx) {
@ -356,9 +375,9 @@ describe('Wallet', function() {
}); });
it('should fill tx with inputs', function(cb) { it('should fill tx with inputs', function(cb) {
walletdb.create(function(err, w1) { c(walletdb.create(), function(err, w1) {
assert.ifError(err); assert.ifError(err);
walletdb.create(function(err, w2) { c(walletdb.create(), function(err, w2) {
assert.ifError(err); assert.ifError(err);
// Coinbase // Coinbase
@ -371,14 +390,14 @@ describe('Wallet', function() {
t1.addInput(dummyInput); t1.addInput(dummyInput);
t1 = t1.toTX(); t1 = t1.toTX();
walletdb.addTX(t1, function(err) { c(walletdb.addTX(t1), function(err) {
assert.ifError(err); assert.ifError(err);
// Create new transaction // Create new transaction
var t2 = bcoin.mtx().addOutput(w2, 5460); var t2 = bcoin.mtx().addOutput(w2, 5460);
w1.fund(t2, { rate: 10000, round: true }, function(err) { c(w1.fund(t2, { rate: 10000, round: true }), function(err) {
assert.ifError(err); assert.ifError(err);
w1.sign(t2, function(err) { c(w1.sign(t2), function(err) {
assert.ifError(err); assert.ifError(err);
t2 = t2.toTX(); t2 = t2.toTX();
@ -393,7 +412,7 @@ describe('Wallet', function() {
// Create new transaction // Create new transaction
var t3 = bcoin.mtx().addOutput(w2, 15000); var t3 = bcoin.mtx().addOutput(w2, 15000);
w1.fund(t3, { rate: 10000, round: true }, function(err) { c(w1.fund(t3, { rate: 10000, round: true }), function(err) {
assert(err); assert(err);
assert.equal(err.requiredFunds, 25000); assert.equal(err.requiredFunds, 25000);
cb(); cb();
@ -406,9 +425,9 @@ describe('Wallet', function() {
}); });
it('should fill tx with inputs with accurate fee', function(cb) { it('should fill tx with inputs with accurate fee', function(cb) {
walletdb.create({ master: KEY1 }, function(err, w1) { c(walletdb.create({ master: KEY1 }), function(err, w1) {
assert.ifError(err); assert.ifError(err);
walletdb.create({ master: KEY2 }, function(err, w2) { c(walletdb.create({ master: KEY2 }), function(err, w2) {
assert.ifError(err); assert.ifError(err);
// Coinbase // Coinbase
@ -421,14 +440,14 @@ describe('Wallet', function() {
t1.addInput(dummyInput); t1.addInput(dummyInput);
t1 = t1.toTX(); t1 = t1.toTX();
walletdb.addTX(t1, function(err) { c(walletdb.addTX(t1), function(err) {
assert.ifError(err); assert.ifError(err);
// Create new transaction // Create new transaction
var t2 = bcoin.mtx().addOutput(w2, 5460); var t2 = bcoin.mtx().addOutput(w2, 5460);
w1.fund(t2, { rate: 10000 }, function(err) { c(w1.fund(t2, { rate: 10000 }), function(err) {
assert.ifError(err); assert.ifError(err);
w1.sign(t2, function(err) { c(w1.sign(t2), function(err) {
assert.ifError(err); assert.ifError(err);
t2 = t2.toTX(); t2 = t2.toTX();
assert(t2.verify()); assert(t2.verify());
@ -451,10 +470,10 @@ describe('Wallet', function() {
}); });
// Create new transaction // Create new transaction
walletdb.addTX(t2, function(err) { c(walletdb.addTX(t2), function(err) {
assert.ifError(err); assert.ifError(err);
var t3 = bcoin.mtx().addOutput(w2, 15000); var t3 = bcoin.mtx().addOutput(w2, 15000);
w1.fund(t3, { rate: 10000 }, function(err) { c(w1.fund(t3, { rate: 10000 }), function(err) {
assert(err); assert(err);
assert(balance); assert(balance);
assert(balance.total === 5460); assert(balance.total === 5460);
@ -469,11 +488,11 @@ describe('Wallet', function() {
}); });
it('should sign multiple inputs using different keys', function(cb) { it('should sign multiple inputs using different keys', function(cb) {
walletdb.create(function(err, w1) { c(walletdb.create(), function(err, w1) {
assert.ifError(err); assert.ifError(err);
walletdb.create(function(err, w2) { c(walletdb.create(), function(err, w2) {
assert.ifError(err); assert.ifError(err);
walletdb.create(function(err, to) { c(walletdb.create(), function(err, to) {
assert.ifError(err); assert.ifError(err);
// Coinbase // Coinbase
@ -496,9 +515,9 @@ describe('Wallet', function() {
t2.addInput(dummyInput); t2.addInput(dummyInput);
t2 = t2.toTX(); t2 = t2.toTX();
walletdb.addTX(t1, function(err) { c(walletdb.addTX(t1), function(err) {
assert.ifError(err); assert.ifError(err);
walletdb.addTX(t2, function(err) { c(walletdb.addTX(t2), function(err) {
assert.ifError(err); assert.ifError(err);
// Create our tx with an output // Create our tx with an output
@ -508,9 +527,9 @@ describe('Wallet', function() {
var cost = tx.getOutputValue(); var cost = tx.getOutputValue();
var total = cost * constants.tx.MIN_FEE; var total = cost * constants.tx.MIN_FEE;
w1.getCoins(function(err, coins1) { c(w1.getCoins(), function(err, coins1) {
assert.ifError(err); assert.ifError(err);
w2.getCoins(function(err, coins2) { c(w2.getCoins(), function(err, coins2) {
assert.ifError(err); assert.ifError(err);
// Add dummy output (for `left`) to calculate maximum TX size // Add dummy output (for `left`) to calculate maximum TX size
@ -532,10 +551,10 @@ describe('Wallet', function() {
tx.outputs[tx.outputs.length - 1].value = left; tx.outputs[tx.outputs.length - 1].value = left;
// Sign transaction // Sign transaction
w1.sign(tx, function(err, total) { c(w1.sign(tx), function(err, total) {
assert.ifError(err); assert.ifError(err);
assert.equal(total, 2); assert.equal(total, 2);
w2.sign(tx, function(err, total) { c(w2.sign(tx), function(err, total) {
assert.ifError(err); assert.ifError(err);
assert.equal(total, 1); assert.equal(total, 1);
@ -547,10 +566,10 @@ describe('Wallet', function() {
tx.addInput(coins1[1]); tx.addInput(coins1[1]);
tx.addInput(coins1[2]); tx.addInput(coins1[2]);
tx.addInput(coins2[1]); tx.addInput(coins2[1]);
w1.sign(tx, function(err, total) { c(w1.sign(tx), function(err, total) {
assert.ifError(err); assert.ifError(err);
assert.equal(total, 2); assert.equal(total, 2);
w2.sign(tx, function(err, total) { c(w2.sign(tx), function(err, total) {
assert.ifError(err); assert.ifError(err);
assert.equal(total, 1); assert.equal(total, 1);
@ -589,28 +608,28 @@ describe('Wallet', function() {
utils.serial([ utils.serial([
function(next) { function(next) {
walletdb.create(options, function(err, w1_) { c(walletdb.create(options), function(err, w1_) {
assert.ifError(err); assert.ifError(err);
w1 = w1_; w1 = w1_;
next(); next();
}); });
}, },
function(next) { function(next) {
walletdb.create(options, function(err, w2_) { c(walletdb.create(options), function(err, w2_) {
assert.ifError(err); assert.ifError(err);
w2 = w2_; w2 = w2_;
next(); next();
}); });
}, },
function(next) { function(next) {
walletdb.create(options, function(err, w3_) { c(walletdb.create(options), function(err, w3_) {
assert.ifError(err); assert.ifError(err);
w3 = w3_; w3 = w3_;
next(); next();
}); });
}, },
function(next) { function(next) {
walletdb.create(function(err, receive_) { c(walletdb.create(), function(err, receive_) {
assert.ifError(err); assert.ifError(err);
receive = receive_; receive = receive_;
next(); next();
@ -619,14 +638,14 @@ describe('Wallet', function() {
], function(err) { ], function(err) {
assert.ifError(err); assert.ifError(err);
utils.serial([ spawn(function *() {
w1.addKey.bind(w1, w2.accountKey), yield w1.addKey(w2.accountKey);
w1.addKey.bind(w1, w3.accountKey), yield w1.addKey(w3.accountKey);
w2.addKey.bind(w2, w1.accountKey), yield w2.addKey(w1.accountKey);
w2.addKey.bind(w2, w3.accountKey), yield w2.addKey(w3.accountKey);
w3.addKey.bind(w3, w1.accountKey), yield w3.addKey(w1.accountKey);
w3.addKey.bind(w3, w2.accountKey) yield w3.addKey(w2.accountKey);
], function(err) { }).catch(cb).then(function(err) {
assert.ifError(err); assert.ifError(err);
// w3 = bcoin.wallet.fromJSON(w3.toJSON()); // w3 = bcoin.wallet.fromJSON(w3.toJSON());
@ -667,11 +686,11 @@ describe('Wallet', function() {
assert.equal(w1.receiveDepth, 1); assert.equal(w1.receiveDepth, 1);
walletdb.addTX(utx, function(err) { c(walletdb.addTX(utx), function(err) {
assert.ifError(err); assert.ifError(err);
walletdb.addTX(utx, function(err) { c(walletdb.addTX(utx), function(err) {
assert.ifError(err); assert.ifError(err);
walletdb.addTX(utx, function(err) { c(walletdb.addTX(utx), function(err) {
assert.ifError(err); assert.ifError(err);
assert.equal(w1.receiveDepth, 2); assert.equal(w1.receiveDepth, 2);
@ -687,14 +706,14 @@ describe('Wallet', function() {
var send = bcoin.mtx(); var send = bcoin.mtx();
send.addOutput({ address: receive.getAddress(), value: 5460 }); send.addOutput({ address: receive.getAddress(), value: 5460 });
assert(!send.verify(flags)); assert(!send.verify(flags));
w1.fund(send, { rate: 10000, round: true }, function(err) { c(w1.fund(send, { rate: 10000, round: true }), function(err) {
assert.ifError(err); assert.ifError(err);
w1.sign(send, function(err) { c(w1.sign(send), function(err) {
assert.ifError(err); assert.ifError(err);
assert(!send.verify(flags)); assert(!send.verify(flags));
w2.sign(send, function(err) { c(w2.sign(send), function(err) {
assert.ifError(err); assert.ifError(err);
send = send.toTX(); send = send.toTX();
@ -711,11 +730,11 @@ describe('Wallet', function() {
send.ts = 1; send.ts = 1;
send.height = 1; send.height = 1;
walletdb.addTX(send, function(err) { c(walletdb.addTX(send), function(err) {
assert.ifError(err); assert.ifError(err);
walletdb.addTX(send, function(err) { c(walletdb.addTX(send), function(err) {
assert.ifError(err); assert.ifError(err);
walletdb.addTX(send, function(err) { c(walletdb.addTX(send), function(err) {
assert.ifError(err); assert.ifError(err);
assert.equal(w1.receiveDepth, 2); assert.equal(w1.receiveDepth, 2);
@ -771,15 +790,15 @@ describe('Wallet', function() {
}); });
it('should fill tx with account 1', function(cb) { it('should fill tx with account 1', function(cb) {
walletdb.create({}, function(err, w1) { c(walletdb.create({}), function(err, w1) {
assert.ifError(err); assert.ifError(err);
walletdb.create({}, function(err, w2) { c(walletdb.create({}), function(err, w2) {
assert.ifError(err); assert.ifError(err);
w1.createAccount({ name: 'foo' }, function(err, account) { c(w1.createAccount({ name: 'foo' }), function(err, account) {
assert.ifError(err); assert.ifError(err);
assert.equal(account.name, 'foo'); assert.equal(account.name, 'foo');
assert.equal(account.accountIndex, 1); assert.equal(account.accountIndex, 1);
w1.getAccount('foo', function(err, account) { c(w1.getAccount('foo'), function(err, account) {
assert.ifError(err); assert.ifError(err);
assert.equal(account.name, 'foo'); assert.equal(account.name, 'foo');
assert.equal(account.accountIndex, 1); assert.equal(account.accountIndex, 1);
@ -794,14 +813,14 @@ describe('Wallet', function() {
t1.addInput(dummyInput); t1.addInput(dummyInput);
t1 = t1.toTX(); t1 = t1.toTX();
walletdb.addTX(t1, function(err) { c(walletdb.addTX(t1), function(err) {
assert.ifError(err); assert.ifError(err);
// Create new transaction // Create new transaction
var t2 = bcoin.mtx().addOutput(w2, 5460); var t2 = bcoin.mtx().addOutput(w2, 5460);
w1.fund(t2, { rate: 10000, round: true }, function(err) { c(w1.fund(t2, { rate: 10000, round: true }), function(err) {
assert.ifError(err); assert.ifError(err);
w1.sign(t2, function(err) { c(w1.sign(t2), function(err) {
assert.ifError(err); assert.ifError(err);
assert(t2.verify()); assert(t2.verify());
@ -815,10 +834,10 @@ describe('Wallet', function() {
// Create new transaction // Create new transaction
var t3 = bcoin.mtx().addOutput(w2, 15000); var t3 = bcoin.mtx().addOutput(w2, 15000);
w1.fund(t3, { rate: 10000, round: true }, function(err) { c(w1.fund(t3, { rate: 10000, round: true }), function(err) {
assert(err); assert(err);
assert.equal(err.requiredFunds, 25000); assert.equal(err.requiredFunds, 25000);
w1.getAccounts(function(err, accounts) { c(w1.getAccounts(), function(err, accounts) {
assert.ifError(err); assert.ifError(err);
assert.deepEqual(accounts, ['default', 'foo']); assert.deepEqual(accounts, ['default', 'foo']);
cb(); cb();
@ -834,14 +853,14 @@ describe('Wallet', function() {
}); });
it('should fail to fill tx with account 1', function(cb) { it('should fail to fill tx with account 1', function(cb) {
walletdb.create({}, function(err, w1) { c(walletdb.create({}), function(err, w1) {
assert.ifError(err); assert.ifError(err);
lastW = w1; lastW = w1;
w1.createAccount({ name: 'foo' }, function(err, acc) { c(w1.createAccount({ name: 'foo' }), function(err, acc) {
assert.ifError(err); assert.ifError(err);
assert.equal(acc.name, 'foo'); assert.equal(acc.name, 'foo');
assert.equal(acc.accountIndex, 1); assert.equal(acc.accountIndex, 1);
w1.getAccount('foo', function(err, account) { c(w1.getAccount('foo'), function(err, account) {
assert.ifError(err); assert.ifError(err);
assert.equal(account.name, 'foo'); assert.equal(account.name, 'foo');
assert.equal(account.accountIndex, 1); assert.equal(account.accountIndex, 1);
@ -862,16 +881,16 @@ describe('Wallet', function() {
t1.addInput(dummyInput); t1.addInput(dummyInput);
t1 = t1.toTX(); t1 = t1.toTX();
walletdb.addTX(t1, function(err) { c(walletdb.addTX(t1), function(err) {
assert.ifError(err); assert.ifError(err);
// Should fill from `foo` and fail // Should fill from `foo` and fail
var t2 = bcoin.mtx().addOutput(w1, 5460); var t2 = bcoin.mtx().addOutput(w1, 5460);
w1.fund(t2, { rate: 10000, round: true, account: 'foo' }, function(err) { c(w1.fund(t2, { rate: 10000, round: true, account: 'foo' }), function(err) {
assert(err); assert(err);
// Should fill from whole wallet and succeed // Should fill from whole wallet and succeed
var t2 = bcoin.mtx().addOutput(w1, 5460); var t2 = bcoin.mtx().addOutput(w1, 5460);
w1.fund(t2, { rate: 10000, round: true }, function(err) { c(w1.fund(t2, { rate: 10000, round: true }), function(err) {
assert.ifError(err); assert.ifError(err);
// Coinbase // Coinbase
@ -884,11 +903,11 @@ describe('Wallet', function() {
t1.addInput(dummyInput); t1.addInput(dummyInput);
t1 = t1.toTX(); t1 = t1.toTX();
walletdb.addTX(t1, function(err) { c(walletdb.addTX(t1), function(err) {
assert.ifError(err); assert.ifError(err);
var t2 = bcoin.mtx().addOutput(w1, 5460); var t2 = bcoin.mtx().addOutput(w1, 5460);
// Should fill from `foo` and succeed // Should fill from `foo` and succeed
w1.fund(t2, { rate: 10000, round: true, account: 'foo' }, function(err) { c(w1.fund(t2, { rate: 10000, round: true, account: 'foo' }), function(err) {
assert.ifError(err); assert.ifError(err);
cb(); cb();
}); });
@ -902,7 +921,7 @@ describe('Wallet', function() {
}); });
it('should fill tx with inputs when encrypted', function(cb) { it('should fill tx with inputs when encrypted', function(cb) {
walletdb.create({ passphrase: 'foo' }, function(err, w1) { c(walletdb.create({ passphrase: 'foo' }), function(err, w1) {
assert.ifError(err); assert.ifError(err);
w1.master.stop(); w1.master.stop();
w1.master.key = null; w1.master.key = null;
@ -917,19 +936,19 @@ describe('Wallet', function() {
t1.addInput(dummyInput); t1.addInput(dummyInput);
t1 = t1.toTX(); t1 = t1.toTX();
walletdb.addTX(t1, function(err) { c(walletdb.addTX(t1), function(err) {
assert.ifError(err); assert.ifError(err);
// Create new transaction // Create new transaction
var t2 = bcoin.mtx().addOutput(w1, 5460); var t2 = bcoin.mtx().addOutput(w1, 5460);
w1.fund(t2, { rate: 10000, round: true }, function(err) { c(w1.fund(t2, { rate: 10000, round: true }), function(err) {
assert.ifError(err); assert.ifError(err);
// Should fail // Should fail
w1.sign(t2, 'bar', function(err) { c(w1.sign(t2, 'bar'), function(err) {
assert(err); assert(err);
assert(!t2.verify()); assert(!t2.verify());
// Should succeed // Should succeed
w1.sign(t2, 'foo', function(err) { c(w1.sign(t2, 'foo'), function(err) {
assert.ifError(err); assert.ifError(err);
assert(t2.verify()); assert(t2.verify());
cb(); cb();
@ -941,9 +960,9 @@ describe('Wallet', function() {
}); });
it('should fill tx with inputs with subtract fee', function(cb) { it('should fill tx with inputs with subtract fee', function(cb) {
walletdb.create(function(err, w1) { c(walletdb.create(), function(err, w1) {
assert.ifError(err); assert.ifError(err);
walletdb.create(function(err, w2) { c(walletdb.create(), function(err, w2) {
assert.ifError(err); assert.ifError(err);
// Coinbase // Coinbase
@ -956,14 +975,14 @@ describe('Wallet', function() {
t1.addInput(dummyInput); t1.addInput(dummyInput);
t1 = t1.toTX(); t1 = t1.toTX();
walletdb.addTX(t1, function(err) { c(walletdb.addTX(t1), function(err) {
assert.ifError(err); assert.ifError(err);
// Create new transaction // Create new transaction
var t2 = bcoin.mtx().addOutput(w2, 21840); var t2 = bcoin.mtx().addOutput(w2, 21840);
w1.fund(t2, { rate: 10000, round: true, subtractFee: true }, function(err) { c(w1.fund(t2, { rate: 10000, round: true, subtractFee: true }), function(err) {
assert.ifError(err); assert.ifError(err);
w1.sign(t2, function(err) { c(w1.sign(t2), function(err) {
assert.ifError(err); assert.ifError(err);
assert(t2.verify()); assert(t2.verify());
@ -981,9 +1000,9 @@ describe('Wallet', function() {
}); });
it('should fill tx with inputs with subtract fee with create tx', function(cb) { it('should fill tx with inputs with subtract fee with create tx', function(cb) {
walletdb.create(function(err, w1) { c(walletdb.create(), function(err, w1) {
assert.ifError(err); assert.ifError(err);
walletdb.create(function(err, w2) { c(walletdb.create(), function(err, w2) {
assert.ifError(err); assert.ifError(err);
// Coinbase // Coinbase
@ -996,7 +1015,7 @@ describe('Wallet', function() {
t1.addInput(dummyInput); t1.addInput(dummyInput);
t1 = t1.toTX(); t1 = t1.toTX();
walletdb.addTX(t1, function(err) { c(walletdb.addTX(t1), function(err) {
assert.ifError(err); assert.ifError(err);
var options = { var options = {
@ -1007,9 +1026,9 @@ describe('Wallet', function() {
}; };
// Create new transaction // Create new transaction
w1.createTX(options, function(err, t2) { c(w1.createTX(options), function(err, t2) {
assert.ifError(err); assert.ifError(err);
w1.sign(t2, function(err) { c(w1.sign(t2), function(err) {
assert.ifError(err); assert.ifError(err);
assert(t2.verify()); assert(t2.verify());
@ -1028,7 +1047,7 @@ describe('Wallet', function() {
it('should get range of txs', function(cb) { it('should get range of txs', function(cb) {
var w1 = lastW; var w1 = lastW;
w1.getRange({ start: 0xdeadbeef - 1000 }, function(err, txs) { c(w1.getRange({ start: 0xdeadbeef - 1000 }), function(err, txs) {
if (err) if (err)
return callback(err); return callback(err);
assert.equal(txs.length, 1); assert.equal(txs.length, 1);
@ -1038,7 +1057,7 @@ describe('Wallet', function() {
it('should get range of txs from account', function(cb) { it('should get range of txs from account', function(cb) {
var w1 = lastW; var w1 = lastW;
w1.getRange('foo', { start: 0xdeadbeef - 1000 }, function(err, txs) { c(w1.getRange('foo', { start: 0xdeadbeef - 1000 }), function(err, txs) {
if (err) if (err)
return callback(err); return callback(err);
assert.equal(txs.length, 1); assert.equal(txs.length, 1);
@ -1048,7 +1067,7 @@ describe('Wallet', function() {
it('should not get range of txs from non-existent account', function(cb) { it('should not get range of txs from non-existent account', function(cb) {
var w1 = lastW; var w1 = lastW;
w1.getRange('bad', { start: 0xdeadbeef - 1000 }, function(err, txs) { c(w1.getRange('bad', { start: 0xdeadbeef - 1000 }), function(err, txs) {
assert(err); assert(err);
assert.equal(err.message, 'Account not found.'); assert.equal(err.message, 'Account not found.');
cb(); cb();
@ -1057,7 +1076,7 @@ describe('Wallet', function() {
it('should get account balance', function(cb) { it('should get account balance', function(cb) {
var w1 = lastW; var w1 = lastW;
w1.getBalance('foo', function(err, balance) { c(w1.getBalance('foo'), function(err, balance) {
assert.ifError(err); assert.ifError(err);
assert.equal(balance.total, 21840); assert.equal(balance.total, 21840);
cb(); cb();
@ -1066,11 +1085,11 @@ describe('Wallet', function() {
it('should import key', function(cb) { it('should import key', function(cb) {
var key = bcoin.keyring.generate(); var key = bcoin.keyring.generate();
walletdb.create({ passphrase: 'test' }, function(err, w1) { c(walletdb.create({ passphrase: 'test' }), function(err, w1) {
assert.ifError(err); assert.ifError(err);
w1.importKey('default', key, 'test', function(err) { c(w1.importKey('default', key, 'test'), function(err) {
assert.ifError(err); assert.ifError(err);
w1.getKeyRing(key.getHash('hex'), function(err, k) { c(w1.getKeyRing(key.getHash('hex')), function(err, k) {
if (err) if (err)
return callback(err); return callback(err);
@ -1086,10 +1105,10 @@ describe('Wallet', function() {
t1.addInput(dummyInput); t1.addInput(dummyInput);
t1 = t1.toTX(); t1 = t1.toTX();
walletdb.addTX(t1, function(err) { c(walletdb.addTX(t1), function(err) {
assert.ifError(err); assert.ifError(err);
w1.getTX(t1.hash('hex'), function(err, tx) { c(w1.getTX(t1.hash('hex')), function(err, tx) {
assert.ifError(err); assert.ifError(err);
assert(tx); assert(tx);
assert.equal(t1.hash('hex'), tx.hash('hex')); assert.equal(t1.hash('hex'), tx.hash('hex'));
@ -1101,9 +1120,9 @@ describe('Wallet', function() {
}; };
// Create new transaction // Create new transaction
w1.createTX(options, function(err, t2) { c(w1.createTX(options), function(err, t2) {
assert.ifError(err); assert.ifError(err);
w1.sign(t2, function(err) { c(w1.sign(t2), function(err) {
assert.ifError(err); assert.ifError(err);
assert(t2.verify()); assert(t2.verify());
assert(t2.inputs[0].prevout.hash === tx.hash('hex')); assert(t2.inputs[0].prevout.hash === tx.hash('hex'));
@ -1118,7 +1137,7 @@ describe('Wallet', function() {
}); });
it('should cleanup', function(cb) { it('should cleanup', function(cb) {
walletdb.dump(function(err, records) { c(walletdb.dump(), function(err, records) {
assert.ifError(err); assert.ifError(err);
constants.tx.COINBASE_MATURITY = 100; constants.tx.COINBASE_MATURITY = 100;
cb(); cb();