fix open and close for all async objects.
This commit is contained in:
parent
d346b36754
commit
1a32e66468
179
lib/bcoin/async.js
Normal file
179
lib/bcoin/async.js
Normal file
@ -0,0 +1,179 @@
|
||||
/*!
|
||||
* async.js - async object class for bcoin
|
||||
* Copyright (c) 2016, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
/**
|
||||
* An abstract object that handles state and
|
||||
* provides recallable open and close methods.
|
||||
* @constructor
|
||||
* @property {Boolean} loading
|
||||
* @property {Boolean} closing
|
||||
* @property {Boolean} loaded
|
||||
*/
|
||||
|
||||
function AsyncObject() {
|
||||
assert(this instanceof AsyncObject);
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.loading = false;
|
||||
this.closing = false;
|
||||
this.loaded = false;
|
||||
this.locker = null;
|
||||
}
|
||||
|
||||
utils.inherits(AsyncObject, EventEmitter);
|
||||
|
||||
/**
|
||||
* Open the object (recallable).
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
AsyncObject.prototype.open = function open(callback) {
|
||||
var self = this;
|
||||
var unlock;
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
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) {
|
||||
unlock = this.locker.lock(utils.nop, []);
|
||||
|
||||
assert(unlock, 'Cannot call methods before load.');
|
||||
|
||||
callback = utils.wrap(callback, unlock);
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the object (recallable).
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
AsyncObject.prototype.close = function close(callback) {
|
||||
var self = this;
|
||||
var unlock;
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
assert(!this.loading, 'Cannot close while loading.');
|
||||
|
||||
if (!this.loaded)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
if (this.closing)
|
||||
return this.on('close', callback);
|
||||
|
||||
if (this.locker) {
|
||||
unlock = this.locker.lock(close, [callback]);
|
||||
|
||||
if (!unlock)
|
||||
return;
|
||||
|
||||
callback = utils.wrap(callback, unlock);
|
||||
}
|
||||
|
||||
this.closing = true;
|
||||
this.loaded = false;
|
||||
|
||||
this._close(function(err) {
|
||||
utils.nextTick(function() {
|
||||
if (err) {
|
||||
self.closing = false;
|
||||
self._error('close', err);
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self.closing = false;
|
||||
self.emit('close');
|
||||
|
||||
callback();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the object (recallable).
|
||||
* @method
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
AsyncObject.prototype.destroy = AsyncObject.prototype.close;
|
||||
|
||||
/**
|
||||
* Emit an error for `open` or `close` listeners.
|
||||
* @private
|
||||
* @param {String} event
|
||||
* @param {Error} err
|
||||
*/
|
||||
|
||||
AsyncObject.prototype._error = function _error(event, err) {
|
||||
var listeners = this.listeners(event);
|
||||
var i, callback;
|
||||
|
||||
this.removeAllListeners(event);
|
||||
|
||||
for (i = 0; i < listeners.length; i++)
|
||||
listeners[i](err);
|
||||
|
||||
this.emit('error', err);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the object.
|
||||
* @private
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
AsyncObject.prototype._open = function _open(callback) {
|
||||
throw new Error('Abstract method.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the object.
|
||||
* @private
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
AsyncObject.prototype._close = function _close(callback) {
|
||||
throw new Error('Abstract method.');
|
||||
};
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = AsyncObject;
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
var bcoin = require('./env');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var AsyncObject = require('./async');
|
||||
var constants = bcoin.protocol.constants;
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
@ -55,17 +56,18 @@ var VerifyError = bcoin.errors.VerifyError;
|
||||
*/
|
||||
|
||||
function Chain(options) {
|
||||
var self = this;
|
||||
|
||||
if (!(this instanceof Chain))
|
||||
return new Chain(options);
|
||||
|
||||
EventEmitter.call(this);
|
||||
AsyncObject.call(this);
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
this.options = options;
|
||||
|
||||
this.loaded = false;
|
||||
this.network = bcoin.network.get(options.network);
|
||||
this.db = new bcoin.chaindb(this, options);
|
||||
this.total = 0;
|
||||
@ -91,7 +93,12 @@ function Chain(options) {
|
||||
this._init();
|
||||
}
|
||||
|
||||
utils.inherits(Chain, EventEmitter);
|
||||
utils.inherits(Chain, AsyncObject);
|
||||
|
||||
/**
|
||||
* Initialize the chain.
|
||||
* @private
|
||||
*/
|
||||
|
||||
Chain.prototype._init = function _init() {
|
||||
var self = this;
|
||||
@ -192,20 +199,30 @@ Chain.prototype._init = function _init() {
|
||||
this.db.on('remove block', function(block) {
|
||||
self.emit('remove block', block);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the chain, wait for the database to load.
|
||||
* @alias Chain#open
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Chain.prototype._open = function open(callback) {
|
||||
var self = this;
|
||||
|
||||
bcoin.debug('Chain is loading.');
|
||||
|
||||
self.db.open(function(err) {
|
||||
this.db.open(function(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
return callback(err);
|
||||
|
||||
self.preload(function(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
return callback(err);
|
||||
|
||||
self.db.getTip(function(err, tip) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
return callback(err);
|
||||
|
||||
assert(tip);
|
||||
|
||||
@ -221,7 +238,7 @@ Chain.prototype._init = function _init() {
|
||||
|
||||
self.getInitialState(function(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
return callback(err);
|
||||
|
||||
if (self.csvActive)
|
||||
bcoin.debug('CSV is active.');
|
||||
@ -231,41 +248,28 @@ Chain.prototype._init = function _init() {
|
||||
|
||||
bcoin.profiler.snapshot();
|
||||
|
||||
self.loaded = true;
|
||||
self.emit('open');
|
||||
self.emit('tip', tip);
|
||||
|
||||
if (!self.synced && self.isFull()) {
|
||||
self.synced = true;
|
||||
self.emit('full');
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the chain, wait for the database to load.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Chain.prototype.open = function open(callback) {
|
||||
if (this.loaded)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
this.once('open', callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the chain, wait for the database to close.
|
||||
* @method
|
||||
* @alias Chain#close
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Chain.prototype.close =
|
||||
Chain.prototype.destroy = function destroy(callback) {
|
||||
this.db.close(utils.ensure(callback));
|
||||
Chain.prototype._close = function close(callback) {
|
||||
this.db.close(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
var bcoin = require('./env');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var AsyncObject = require('./async');
|
||||
var constants = bcoin.protocol.constants;
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
@ -174,12 +175,22 @@ function ChainDB(chain, options) {
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
EventEmitter.call(this);
|
||||
AsyncObject.call(this);
|
||||
|
||||
this.options = options;
|
||||
this.chain = chain;
|
||||
this.network = this.chain.network;
|
||||
|
||||
this.db = bcoin.ldb({
|
||||
network: this.network,
|
||||
name: this.options.name || (this.options.spv ? 'spvchain' : 'chain'),
|
||||
location: this.options.location,
|
||||
db: this.options.db,
|
||||
compression: true,
|
||||
cacheSize: 16 << 20,
|
||||
writeBufferSize: 8 << 20
|
||||
});
|
||||
|
||||
this.keepBlocks = options.keepBlocks || 288;
|
||||
this.prune = !!options.prune;
|
||||
|
||||
@ -207,83 +218,59 @@ function ChainDB(chain, options) {
|
||||
this.coinCache = new NullCache(this.coinWindow);
|
||||
this.cacheHash = new bcoin.lru(this.cacheWindow, 1);
|
||||
this.cacheHeight = new bcoin.lru(this.cacheWindow, 1);
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
utils.inherits(ChainDB, EventEmitter);
|
||||
utils.inherits(ChainDB, AsyncObject);
|
||||
|
||||
ChainDB.prototype._init = function _init() {
|
||||
/**
|
||||
* Open the chain db, wait for the database to load.
|
||||
* @alias ChainDB#open
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
ChainDB.prototype._open = function open(callback) {
|
||||
var self = this;
|
||||
var genesis, block;
|
||||
|
||||
if (this.loaded)
|
||||
return;
|
||||
|
||||
this.db = bcoin.ldb({
|
||||
network: this.network,
|
||||
name: this.options.name || (this.options.spv ? 'spvchain' : 'chain'),
|
||||
location: this.options.location,
|
||||
db: this.options.db,
|
||||
compression: true,
|
||||
cacheSize: 16 << 20,
|
||||
writeBufferSize: 8 << 20
|
||||
});
|
||||
|
||||
bcoin.debug('Starting chain load.');
|
||||
|
||||
function done(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
bcoin.debug('Chain successfully loaded.');
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
this.db.open(function(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
function finish(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
bcoin.debug('Chain successfully loaded.');
|
||||
|
||||
self.loaded = true;
|
||||
self.emit('open');
|
||||
}
|
||||
return done(err);
|
||||
|
||||
self.db.has(layout.h(self.network.genesis.hash), function(err, exists) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
return done(err);
|
||||
|
||||
if (exists)
|
||||
return finish();
|
||||
return done();
|
||||
|
||||
block = bcoin.block.fromRaw(self.network.genesisBlock, 'hex');
|
||||
block.setHeight(0);
|
||||
|
||||
genesis = bcoin.chainentry.fromBlock(self.chain, block);
|
||||
|
||||
self.save(genesis, block, null, true, finish);
|
||||
self.save(genesis, block, null, true, done);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the chain, wait for the database to load.
|
||||
* Close the chain db, wait for the database to close.
|
||||
* @alias ChainDB#close
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
ChainDB.prototype.open = function open(callback) {
|
||||
if (this.loaded)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
this.once('open', callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the chain, wait for the database to close.
|
||||
* @method
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
ChainDB.prototype.close =
|
||||
ChainDB.prototype.destroy = function destroy(callback) {
|
||||
callback = utils.ensure(callback);
|
||||
ChainDB.prototype._close = function close(callback) {
|
||||
this.db.close(callback);
|
||||
};
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
var bcoin = require('./env');
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
var Node = bcoin.node;
|
||||
|
||||
/**
|
||||
* Create a fullnode complete with a chain,
|
||||
@ -47,20 +48,7 @@ function Fullnode(options) {
|
||||
if (!(this instanceof Fullnode))
|
||||
return new Fullnode(options);
|
||||
|
||||
bcoin.node.call(this, options);
|
||||
|
||||
this.loaded = false;
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
utils.inherits(Fullnode, bcoin.node);
|
||||
|
||||
Fullnode.prototype._init = function _init() {
|
||||
var self = this;
|
||||
var options;
|
||||
|
||||
this.wallet = null;
|
||||
Node.call(this, options);
|
||||
|
||||
this.chain = new bcoin.chain({
|
||||
network: this.network,
|
||||
@ -119,6 +107,19 @@ Fullnode.prototype._init = function _init() {
|
||||
});
|
||||
}
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
utils.inherits(Fullnode, Node);
|
||||
|
||||
/**
|
||||
* Initialize the node.
|
||||
* @private
|
||||
*/
|
||||
|
||||
Fullnode.prototype._init = function _init() {
|
||||
var self = this;
|
||||
|
||||
// Bind to errors
|
||||
this.mempool.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
@ -196,14 +197,26 @@ Fullnode.prototype._init = function _init() {
|
||||
this.miner.on('block', function(block) {
|
||||
self.pool.broadcast(block.toInv());
|
||||
});
|
||||
};
|
||||
|
||||
function load(err) {
|
||||
/**
|
||||
* Open the node and all its child objects,
|
||||
* wait for the database to load.
|
||||
* @alias Fullnode#open
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Fullnode.prototype._open = function open(callback) {
|
||||
var self = this;
|
||||
var options;
|
||||
|
||||
function done(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
return callback(err);
|
||||
|
||||
self.loaded = true;
|
||||
self.emit('open');
|
||||
bcoin.debug('Node is loaded.');
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
options = utils.merge({
|
||||
@ -256,7 +269,30 @@ Fullnode.prototype._init = function _init() {
|
||||
return next();
|
||||
self.http.open(next);
|
||||
}
|
||||
], load);
|
||||
], done);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the node, wait for the database to close.
|
||||
* @alias Fullnode#close
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Fullnode.prototype._close = function close(callback) {
|
||||
var self = this;
|
||||
|
||||
utils.serial([
|
||||
function(next) {
|
||||
if (!self.http)
|
||||
return next();
|
||||
self.http.close(next);
|
||||
},
|
||||
this.walletdb.close.bind(this.walletdb),
|
||||
this.pool.close.bind(this.pool),
|
||||
this.miner.close.bind(this.miner),
|
||||
this.mempool.close.bind(this.mempool),
|
||||
this.chain.close.bind(this.chain)
|
||||
], callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -340,45 +376,6 @@ Fullnode.prototype.stopSync = function stopSync() {
|
||||
return this.pool.stopSync();
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the node and all its child objects,
|
||||
* wait for the database to load.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Fullnode.prototype.open = function open(callback) {
|
||||
if (this.loaded)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
this.once('open', callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the node, wait for the database to close.
|
||||
* @method
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Fullnode.prototype.close =
|
||||
Fullnode.prototype.destroy = function destroy(callback) {
|
||||
var self = this;
|
||||
|
||||
this.wallet.destroy();
|
||||
|
||||
utils.serial([
|
||||
function(next) {
|
||||
if (!self.http)
|
||||
return next();
|
||||
self.http.close(next);
|
||||
},
|
||||
this.walletdb.close.bind(this.walletdb),
|
||||
this.pool.close.bind(this.pool),
|
||||
this.miner.close.bind(this.miner),
|
||||
this.mempool.close.bind(this.mempool),
|
||||
this.chain.close.bind(this.chain)
|
||||
], callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a {@link Wallet} in the wallet database.
|
||||
* @param {Object} options - See {@link Wallet}.
|
||||
|
||||
@ -8,7 +8,9 @@
|
||||
'use strict';
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var AsyncObject = require('../async');
|
||||
var utils = require('../utils');
|
||||
var assert = utils.assert;
|
||||
|
||||
/**
|
||||
* HTTPBase
|
||||
@ -25,6 +27,8 @@ function HTTPBase(options) {
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
AsyncObject.call(this);
|
||||
|
||||
this.options = options;
|
||||
this.io = null;
|
||||
|
||||
@ -44,7 +48,12 @@ function HTTPBase(options) {
|
||||
this._init();
|
||||
}
|
||||
|
||||
utils.inherits(HTTPBase, EventEmitter);
|
||||
utils.inherits(HTTPBase, AsyncObject);
|
||||
|
||||
/**
|
||||
* Initialize server.
|
||||
* @private
|
||||
*/
|
||||
|
||||
HTTPBase.prototype._init = function _init() {
|
||||
var self = this;
|
||||
@ -69,30 +78,10 @@ HTTPBase.prototype._init = function _init() {
|
||||
});
|
||||
};
|
||||
|
||||
HTTPBase.prototype._initIO = function _initIO() {
|
||||
var self = this;
|
||||
var IOServer;
|
||||
|
||||
if (!this.options.sockets)
|
||||
return;
|
||||
|
||||
try {
|
||||
IOServer = require('socket.io');
|
||||
} catch (e) {
|
||||
;
|
||||
}
|
||||
|
||||
if (!IOServer)
|
||||
return;
|
||||
|
||||
this.io = new IOServer();
|
||||
|
||||
this.io.attach(this.server);
|
||||
|
||||
this.io.on('connection', function(socket) {
|
||||
self.emit('websocket', socket);
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Initialize router.
|
||||
* @private
|
||||
*/
|
||||
|
||||
HTTPBase.prototype._initRouter = function _initRouter() {
|
||||
var self = this;
|
||||
@ -185,6 +174,66 @@ HTTPBase.prototype._initRouter = function _initRouter() {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize websockets.
|
||||
* @private
|
||||
*/
|
||||
|
||||
HTTPBase.prototype._initIO = function _initIO() {
|
||||
var self = this;
|
||||
var IOServer;
|
||||
|
||||
if (!this.options.sockets)
|
||||
return;
|
||||
|
||||
try {
|
||||
IOServer = require('socket.io');
|
||||
} catch (e) {
|
||||
;
|
||||
}
|
||||
|
||||
if (!IOServer)
|
||||
return;
|
||||
|
||||
this.io = new IOServer();
|
||||
|
||||
this.io.attach(this.server);
|
||||
|
||||
this.io.on('connection', function(socket) {
|
||||
self.emit('websocket', socket);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the server.
|
||||
* @alias HTTPBase#open
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPBase.prototype._open = function open(callback) {
|
||||
assert(typeof this.options.port === 'number', 'Port required.');
|
||||
this.listen(this.options.port, this.options.host, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the server.
|
||||
* @alias HTTPBase#close
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPBase.prototype._close = function close(callback) {
|
||||
this.server.close(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle middleware stack.
|
||||
* @param {HTTPRequest} req
|
||||
* @param {HTTPResponse} res
|
||||
* @param {Function} _send
|
||||
* @param {Function} callback
|
||||
* @private
|
||||
*/
|
||||
|
||||
HTTPBase.prototype._handle = function _handle(req, res, _send, callback) {
|
||||
var self = this;
|
||||
var i = 0;
|
||||
@ -339,15 +388,6 @@ HTTPBase.prototype.listen = function listen(port, host, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the the server.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPBase.prototype.close = function close(callback) {
|
||||
this.server.close(callback);
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
var bcoin = require('../env');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var AsyncObject = require('../async');
|
||||
var utils = require('../utils');
|
||||
var assert = utils.assert;
|
||||
var request = require('./request');
|
||||
@ -31,19 +32,28 @@ function HTTPClient(options) {
|
||||
if (typeof options === 'string')
|
||||
options = { uri: options };
|
||||
|
||||
EventEmitter.call(this);
|
||||
AsyncObject.call(this);
|
||||
|
||||
this.uri = options.uri;
|
||||
this.loaded = false;
|
||||
this.id = null;
|
||||
this.options = options;
|
||||
this.network = bcoin.network.get(options.network);
|
||||
this._init();
|
||||
|
||||
this.uri = options.uri;
|
||||
this.id = null;
|
||||
this.socket = null;
|
||||
|
||||
// Open automatically.
|
||||
this.open();
|
||||
}
|
||||
|
||||
utils.inherits(HTTPClient, EventEmitter);
|
||||
utils.inherits(HTTPClient, AsyncObject);
|
||||
|
||||
HTTPClient.prototype._init = function _init() {
|
||||
/**
|
||||
* Open the client, wait for socket to connect.
|
||||
* @alias HTTPClient#open
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPClient.prototype._open = function _open(callback) {
|
||||
var self = this;
|
||||
var IOClient;
|
||||
|
||||
@ -132,15 +142,19 @@ HTTPClient.prototype._init = function _init() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the client, wait for the socket to load.
|
||||
* Close the client, wait for the socket to close.
|
||||
* @alias HTTPClient#close
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPClient.prototype.open = function open(callback) {
|
||||
if (this.loaded)
|
||||
HTTPClient.prototype._close = function close(callback) {
|
||||
if (!this.socket)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
this.once('open', callback);
|
||||
this.socket.destroy();
|
||||
this.socket = null;
|
||||
|
||||
return utils.nextTick(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -183,25 +197,6 @@ HTTPClient.prototype.none = function none() {
|
||||
this.leave('!all');
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the client, wait for the socket to close.
|
||||
* @method
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPClient.prototype.close =
|
||||
HTTPClient.prototype.destroy = function destroy(callback) {
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
if (!this.socket)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
this.socket.destroy();
|
||||
this.socket = null;
|
||||
|
||||
return utils.nextTick(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Make an http request to endpoint.
|
||||
* @private
|
||||
|
||||
@ -52,6 +52,11 @@ function HTTPServer(options) {
|
||||
|
||||
utils.inherits(HTTPServer, EventEmitter);
|
||||
|
||||
/**
|
||||
* Initialize routes.
|
||||
* @private
|
||||
*/
|
||||
|
||||
HTTPServer.prototype._init = function _init() {
|
||||
var self = this;
|
||||
|
||||
@ -707,34 +712,13 @@ HTTPServer.prototype._init = function _init() {
|
||||
});
|
||||
|
||||
this._initIO();
|
||||
|
||||
if (this.options.port != null)
|
||||
this.listen(this.options.port, this.options.host);
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the server, wait for socket.
|
||||
* @param {Function} callback
|
||||
* Initialize websockets.
|
||||
* @private
|
||||
*/
|
||||
|
||||
HTTPServer.prototype.open = function open(callback) {
|
||||
if (this.loaded)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
this.once('open', callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the server, wait for server socket to close.
|
||||
* @method
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPServer.prototype.close =
|
||||
HTTPServer.prototype.destroy = function destroy(callback) {
|
||||
this.server.close(callback);
|
||||
};
|
||||
|
||||
HTTPServer.prototype._initIO = function _initIO() {
|
||||
var self = this;
|
||||
|
||||
@ -829,6 +813,24 @@ HTTPServer.prototype._initIO = function _initIO() {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the server, wait for socket.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPServer.prototype.open = function open(callback) {
|
||||
this.server.open(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the server, wait for server socket to close.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPServer.prototype.close = function close(callback) {
|
||||
this.server.close(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* @see HTTPBase#use
|
||||
*/
|
||||
|
||||
@ -33,14 +33,21 @@ function HTTPWallet(options) {
|
||||
options = { uri: options };
|
||||
|
||||
this.options = options;
|
||||
this.client = new http.client(options);
|
||||
this.network = bcoin.network.get(options.network);
|
||||
|
||||
this.client = new http.client(options);
|
||||
this.uri = options.uri;
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
utils.inherits(HTTPWallet, EventEmitter);
|
||||
|
||||
/**
|
||||
* Initialize the wallet.
|
||||
* @private
|
||||
*/
|
||||
|
||||
HTTPWallet.prototype._init = function _init() {
|
||||
var self = this;
|
||||
|
||||
@ -72,7 +79,9 @@ HTTPWallet.prototype._init = function _init() {
|
||||
};
|
||||
|
||||
/**
|
||||
* @see Wallet#open
|
||||
* Open the client and ensure a wallet.
|
||||
* @alias HTTPWallet#open
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPWallet.prototype.open = function open(callback) {
|
||||
@ -85,18 +94,13 @@ HTTPWallet.prototype.open = function open(callback) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @see Wallet#close
|
||||
* Close the client, wait for the socket to close.
|
||||
* @alias HTTPWallet#close
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
HTTPWallet.prototype.close =
|
||||
HTTPWallet.prototype.destroy = function destroy(callback) {
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
if (!this.client)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
this.client.destroy(callback);
|
||||
this.client = null;
|
||||
HTTPWallet.prototype.close = function close(callback) {
|
||||
this.client.close(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var AsyncObject = require('./async');
|
||||
|
||||
/**
|
||||
* Extremely low-level version of levelup.
|
||||
@ -33,9 +34,11 @@ function LowlevelUp(file, options) {
|
||||
if (!(this instanceof LowlevelUp))
|
||||
return new LowlevelUp(file, options);
|
||||
|
||||
EventEmitter.call(this);
|
||||
AsyncObject.call(this);
|
||||
|
||||
this.loaded = false;
|
||||
this.options = options;
|
||||
this.backend = options.db;
|
||||
this.location = file;
|
||||
|
||||
this.db = new options.db(file);
|
||||
|
||||
@ -48,39 +51,60 @@ function LowlevelUp(file, options) {
|
||||
|
||||
if (this.db.binding)
|
||||
this.binding = this.db.binding;
|
||||
|
||||
this.binding.open(options, function(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
self.loaded = true;
|
||||
self.emit('open');
|
||||
});
|
||||
}
|
||||
|
||||
utils.inherits(LowlevelUp, EventEmitter);
|
||||
utils.inherits(LowlevelUp, AsyncObject);
|
||||
|
||||
/**
|
||||
* Open the database (recallable).
|
||||
* @alias LowlevelUp#open
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.open = function open(callback) {
|
||||
if (this.loaded)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
this.once('open', callback);
|
||||
LowlevelUp.prototype._open = function open(callback) {
|
||||
this.binding.open(this.options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the database (not recallable).
|
||||
* Close the database (recallable).
|
||||
* @alias LowlevelUp#close
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.close = function close(callback) {
|
||||
assert(this.loaded, 'Cannot use database before it is loaded.');
|
||||
this.loaded = false;
|
||||
return this.binding.close(callback);
|
||||
LowlevelUp.prototype._close = function close(callback) {
|
||||
this.binding.close(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the database.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.destroy = function destroy(callback) {
|
||||
assert(!this.loading);
|
||||
assert(!this.closing);
|
||||
assert(!this.loaded);
|
||||
|
||||
if (!this.backend.destroy)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
this.backend.destroy(this.location, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Repair the database.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.repair = function repair(callback) {
|
||||
assert(!this.loading);
|
||||
assert(!this.closing);
|
||||
assert(!this.loaded);
|
||||
|
||||
if (!this.backend.repair)
|
||||
return utils.asyncify(callback)(new Error('Cannot repair.'));
|
||||
|
||||
this.backend.repair(this.location, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
|
||||
var bcoin = require('./env');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var AsyncObject = require('./async');
|
||||
var constants = bcoin.protocol.constants;
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
@ -63,7 +64,7 @@ function Mempool(options) {
|
||||
if (!(this instanceof Mempool))
|
||||
return new Mempool(options);
|
||||
|
||||
EventEmitter.call(this);
|
||||
AsyncObject.call(this);
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
@ -78,7 +79,13 @@ function Mempool(options) {
|
||||
|
||||
this.locker = new bcoin.locker(this, this.addTX);
|
||||
|
||||
this.db = null;
|
||||
this.db = bcoin.ldb({
|
||||
network: this.network,
|
||||
name: this.options.name || 'mempool',
|
||||
location: this.options.location,
|
||||
db: this.options.db || 'memory'
|
||||
});
|
||||
|
||||
this.size = 0;
|
||||
this.waiting = {};
|
||||
this.orphans = {};
|
||||
@ -105,64 +112,61 @@ function Mempool(options) {
|
||||
this.minFeeRate = 0;
|
||||
this.minReasonableFee = constants.tx.MIN_RELAY;
|
||||
this.minRelayFee = constants.tx.MIN_RELAY;
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
utils.inherits(Mempool, EventEmitter);
|
||||
utils.inherits(Mempool, AsyncObject);
|
||||
|
||||
Mempool.prototype._lock = function _lock(func, args, force) {
|
||||
return this.locker.lock(func, args, force);
|
||||
};
|
||||
/**
|
||||
* Open the chain, wait for the database to load.
|
||||
* @alias Mempool#open
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Mempool.prototype._init = function _init() {
|
||||
Mempool.prototype._open = function open(callback) {
|
||||
var self = this;
|
||||
var unlock = this._lock(utils.nop, []);
|
||||
var options = {
|
||||
network: this.network,
|
||||
name: this.options.name || 'mempool',
|
||||
location: this.options.location,
|
||||
db: this.options.db || 'memory'
|
||||
};
|
||||
|
||||
assert(unlock);
|
||||
|
||||
// Clean the database before loading. The only
|
||||
// reason for using an on-disk db for the mempool
|
||||
// is not for persistence, but to keep ~300mb of
|
||||
// txs out of main memory.
|
||||
bcoin.ldb.destroy(options, function(err) {
|
||||
if (err) {
|
||||
unlock();
|
||||
return self.emit('error', err);
|
||||
}
|
||||
|
||||
self.db = bcoin.ldb(options);
|
||||
this.db.destroy(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
self.db.open(function(err) {
|
||||
if (err) {
|
||||
unlock();
|
||||
return self.emit('error', err);
|
||||
}
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
self.initialMemoryUsage(function(err) {
|
||||
if (err) {
|
||||
unlock();
|
||||
return self.emit('error', err);
|
||||
}
|
||||
self.chain.open(function(err) {
|
||||
if (err) {
|
||||
unlock();
|
||||
return self.emit('error', err);
|
||||
}
|
||||
unlock();
|
||||
self.loaded = true;
|
||||
self.emit('open');
|
||||
});
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
self.chain.open(callback);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the chain, wait for the database to close.
|
||||
* @alias Mempool#close
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Mempool.prototype._close = function destroy(callback) {
|
||||
this.db.close(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoke mutex lock.
|
||||
* @private
|
||||
* @returns {Function} unlock
|
||||
*/
|
||||
|
||||
Mempool.prototype._lock = function _lock(func, args, force) {
|
||||
return this.locker.lock(func, args, force);
|
||||
};
|
||||
|
||||
/**
|
||||
* Tally up total memory usage from database.
|
||||
* @param {Function} callback - Returns [Error, Number].
|
||||
@ -187,29 +191,6 @@ Mempool.prototype.initialMemoryUsage = function initialMemoryUsage(callback) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the chain, wait for the database to load.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Mempool.prototype.open = function open(callback) {
|
||||
if (this.loaded)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
return this.once('open', callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the chain, wait for the database to close.
|
||||
* @method
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Mempool.prototype.close =
|
||||
Mempool.prototype.destroy = function destroy(callback) {
|
||||
this.db.close(utils.ensure(callback));
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify the mempool that a new block has come
|
||||
* in (removes all transactions contained in the
|
||||
|
||||
@ -13,6 +13,7 @@ var assert = utils.assert;
|
||||
var constants = bcoin.protocol.constants;
|
||||
var bn = require('bn.js');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var AsyncObject = require('./async');
|
||||
var BufferReader = require('./reader');
|
||||
var BufferWriter = require('./writer');
|
||||
|
||||
@ -33,7 +34,7 @@ function Miner(options) {
|
||||
if (!(this instanceof Miner))
|
||||
return new Miner(options);
|
||||
|
||||
EventEmitter.call(this);
|
||||
AsyncObject.call(this);
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
@ -51,7 +52,6 @@ function Miner(options) {
|
||||
this.network = this.chain.network;
|
||||
this.running = false;
|
||||
this.timeout = null;
|
||||
this.loaded = false;
|
||||
this.attempt = null;
|
||||
this.workerPool = null;
|
||||
|
||||
@ -66,31 +66,13 @@ function Miner(options) {
|
||||
this._init();
|
||||
}
|
||||
|
||||
utils.inherits(Miner, EventEmitter);
|
||||
utils.inherits(Miner, AsyncObject);
|
||||
|
||||
/**
|
||||
* Open the miner, wait for the chain and mempool to load.
|
||||
* @param {Function} callback
|
||||
* Initialize the miner.
|
||||
* @private
|
||||
*/
|
||||
|
||||
Miner.prototype.open = function open(callback) {
|
||||
if (this.loaded)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
return this.once('open', callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the miner.
|
||||
* @method
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Miner.prototype.close =
|
||||
Miner.prototype.destroy = function destroy(callback) {
|
||||
return utils.nextTick(callback);
|
||||
};
|
||||
|
||||
Miner.prototype._init = function _init() {
|
||||
var self = this;
|
||||
|
||||
@ -134,18 +116,29 @@ Miner.prototype._init = function _init() {
|
||||
stat.height,
|
||||
stat.best);
|
||||
});
|
||||
};
|
||||
|
||||
function done(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
self.loaded = true;
|
||||
self.emit('open');
|
||||
}
|
||||
/**
|
||||
* Open the miner, wait for the chain and mempool to load.
|
||||
* @alias Miner#open
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Miner.prototype._open = function open(callback) {
|
||||
if (this.mempool)
|
||||
this.mempool.open(done);
|
||||
this.mempool.open(callback);
|
||||
else
|
||||
this.chain.open(done);
|
||||
this.chain.open(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the miner.
|
||||
* @alias Miner#close
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Miner.prototype._close = function close(callback) {
|
||||
return utils.nextTick(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
var bcoin = require('./env');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var AsyncObject = require('./async');
|
||||
var utils = require('./utils');
|
||||
|
||||
/**
|
||||
@ -24,7 +25,7 @@ function Node(options) {
|
||||
if (!(this instanceof Node))
|
||||
return new Node(options);
|
||||
|
||||
EventEmitter.call(this);
|
||||
AsyncObject.call(this);
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
@ -37,9 +38,10 @@ function Node(options) {
|
||||
this.chain = null;
|
||||
this.miner = null;
|
||||
this.walletdb = null;
|
||||
this.wallet = null;
|
||||
}
|
||||
|
||||
utils.inherits(Node, EventEmitter);
|
||||
utils.inherits(Node, AsyncObject);
|
||||
|
||||
/*
|
||||
* Expose
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
'use strict';
|
||||
|
||||
var bcoin = require('./env');
|
||||
var AsyncObject = require('./async');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var utils = require('./utils');
|
||||
var IP = require('./ip');
|
||||
@ -85,7 +86,7 @@ function Pool(options) {
|
||||
if (!(this instanceof Pool))
|
||||
return new Pool(options);
|
||||
|
||||
EventEmitter.call(this);
|
||||
AsyncObject.call(this);
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
@ -129,8 +130,6 @@ function Pool(options) {
|
||||
});
|
||||
|
||||
this.server = null;
|
||||
this.destroyed = false;
|
||||
this.loaded = false;
|
||||
this.size = options.size || 8;
|
||||
this.maxLeeches = options.maxLeeches || 8;
|
||||
this.connected = false;
|
||||
@ -215,67 +214,12 @@ function Pool(options) {
|
||||
};
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
utils.inherits(Pool, EventEmitter);
|
||||
|
||||
/**
|
||||
* Open the pool, wait for the chain to load.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Pool.prototype.open = function open(callback) {
|
||||
if (this.loaded)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
this.once('open', callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect to the network.
|
||||
*/
|
||||
|
||||
Pool.prototype.connect = function connect() {
|
||||
var self = this;
|
||||
var i;
|
||||
|
||||
assert(this.loaded, 'Pool is not loaded.');
|
||||
|
||||
if (this.connected)
|
||||
return;
|
||||
|
||||
if (!this.options.selfish && !this.options.spv) {
|
||||
if (this.mempool) {
|
||||
this.mempool.on('tx', function(tx) {
|
||||
self.announce(tx);
|
||||
});
|
||||
}
|
||||
|
||||
// Normally we would also broadcast
|
||||
// competing chains, but we want to
|
||||
// avoid getting banned if an evil
|
||||
// miner sends us an invalid competing
|
||||
// chain that we can't connect and
|
||||
// verify yet.
|
||||
this.chain.on('block', function(block) {
|
||||
if (!self.synced)
|
||||
return;
|
||||
self.announce(block);
|
||||
});
|
||||
}
|
||||
|
||||
assert(this.seeds.length !== 0, 'No seeds available.');
|
||||
|
||||
this._addLoader();
|
||||
|
||||
for (i = 0; i < this.size - 1; i++)
|
||||
this._addPeer();
|
||||
|
||||
this.connected = true;
|
||||
};
|
||||
utils.inherits(Pool, AsyncObject);
|
||||
|
||||
/**
|
||||
* Initialize the pool. Bind to events.
|
||||
* Initialize the pool.
|
||||
* @private
|
||||
*/
|
||||
|
||||
@ -328,15 +272,16 @@ Pool.prototype._init = function _init() {
|
||||
|
||||
bcoin.debug('Chain is fully synced (height=%d).', self.chain.height);
|
||||
});
|
||||
};
|
||||
|
||||
function done(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
self.loaded = true;
|
||||
self.emit('open');
|
||||
}
|
||||
/**
|
||||
* Open the pool, wait for the chain to load.
|
||||
* @alias Pool#open
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Pool.prototype._open = function open(callback) {
|
||||
var self = this;
|
||||
this.getIP(function(err, ip) {
|
||||
if (err)
|
||||
bcoin.error(err);
|
||||
@ -347,12 +292,99 @@ Pool.prototype._init = function _init() {
|
||||
}
|
||||
|
||||
if (self.mempool)
|
||||
self.mempool.open(done);
|
||||
self.mempool.open(callback);
|
||||
else
|
||||
self.chain.open(done);
|
||||
self.chain.open(callback);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Close and destroy the pool.
|
||||
* @alias Pool#close
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Pool.prototype._close = function close(callback) {
|
||||
var i, items, peers, hashes, hash;
|
||||
|
||||
this.stopSync();
|
||||
|
||||
items = this.inv.items.slice();
|
||||
|
||||
for (i = 0; i < items.length; i++)
|
||||
items[i].finish();
|
||||
|
||||
hashes = Object.keys(this.request.map);
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
this.request.map[hash].finish(new Error('Pool closed.'));
|
||||
}
|
||||
|
||||
if (this.peers.load)
|
||||
this.peers.load.destroy();
|
||||
|
||||
peers = this.peers.regular.slice();
|
||||
|
||||
for (i = 0; i < peers.length; i++)
|
||||
peers[i].destroy();
|
||||
|
||||
peers = this.peers.pending.slice();
|
||||
|
||||
for (i = 0; i < peers.length; i++)
|
||||
peers[i].destroy();
|
||||
|
||||
peers = this.peers.leeches.slice();
|
||||
|
||||
for (i = 0; i < peers.length; i++)
|
||||
peers[i].destroy();
|
||||
|
||||
this.unlisten(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect to the network.
|
||||
*/
|
||||
|
||||
Pool.prototype.connect = function connect() {
|
||||
var self = this;
|
||||
var i;
|
||||
|
||||
assert(this.loaded, 'Pool is not loaded.');
|
||||
|
||||
if (this.connected)
|
||||
return;
|
||||
|
||||
if (!this.options.selfish && !this.options.spv) {
|
||||
if (this.mempool) {
|
||||
this.mempool.on('tx', function(tx) {
|
||||
self.announce(tx);
|
||||
});
|
||||
}
|
||||
|
||||
// Normally we would also broadcast
|
||||
// competing chains, but we want to
|
||||
// avoid getting banned if an evil
|
||||
// miner sends us an invalid competing
|
||||
// chain that we can't connect and
|
||||
// verify yet.
|
||||
this.chain.on('block', function(block) {
|
||||
if (!self.synced)
|
||||
return;
|
||||
self.announce(block);
|
||||
});
|
||||
}
|
||||
|
||||
assert(this.seeds.length !== 0, 'No seeds available.');
|
||||
|
||||
this._addLoader();
|
||||
|
||||
for (i = 0; i < this.size - 1; i++)
|
||||
this._addPeer();
|
||||
|
||||
this.connected = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Start listening on a server socket.
|
||||
* @param {Function} callback
|
||||
@ -524,7 +556,7 @@ Pool.prototype._addLoader = function _addLoader() {
|
||||
var self = this;
|
||||
var peer;
|
||||
|
||||
if (this.destroyed)
|
||||
if (!this.loaded)
|
||||
return;
|
||||
|
||||
if (this.peers.load)
|
||||
@ -861,7 +893,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
||||
block.rhash,
|
||||
utils.date(block.ts),
|
||||
self.chain.height,
|
||||
(Math.floor(self.chain.getProgress() * 10000) / 100) + '%',
|
||||
(self.chain.getProgress() * 100).toFixed(2) + '%',
|
||||
self.chain.total,
|
||||
self.chain.orphan.count,
|
||||
self.request.activeBlocks,
|
||||
@ -934,7 +966,7 @@ Pool.prototype._createPeer = function _createPeer(options) {
|
||||
peer.once('close', function() {
|
||||
self._removePeer(peer);
|
||||
|
||||
if (self.destroyed)
|
||||
if (!self.loaded)
|
||||
return;
|
||||
|
||||
if (!peer.loader)
|
||||
@ -1223,7 +1255,7 @@ Pool.prototype._addLeech = function _addLeech(socket) {
|
||||
var self = this;
|
||||
var peer;
|
||||
|
||||
if (this.destroyed)
|
||||
if (!this.loaded)
|
||||
return socket.destroy();
|
||||
|
||||
peer = this._createPeer({
|
||||
@ -1254,7 +1286,7 @@ Pool.prototype._addPeer = function _addPeer() {
|
||||
var self = this;
|
||||
var peer, host;
|
||||
|
||||
if (this.destroyed)
|
||||
if (!this.loaded)
|
||||
return;
|
||||
|
||||
if (this.peers.regular.length + this.peers.pending.length >= this.size - 1)
|
||||
@ -1395,7 +1427,7 @@ Pool.prototype.getData = function getData(peer, type, hash, options, callback) {
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
if (this.destroyed)
|
||||
if (!this.loaded)
|
||||
return callback();
|
||||
|
||||
if (options == null)
|
||||
@ -1664,58 +1696,6 @@ Pool.prototype.setFeeRate = function setFeeRate(rate) {
|
||||
this.peers.regular[i].setFeeRate(rate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close and destroy the pool.
|
||||
* @method
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Pool.prototype.close =
|
||||
Pool.prototype.destroy = function destroy(callback) {
|
||||
var i, items, peers, hashes, hash;
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
if (this.destroyed)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
this.destroyed = true;
|
||||
|
||||
this.stopSync();
|
||||
|
||||
items = this.inv.items.slice();
|
||||
|
||||
for (i = 0; i < items.length; i++)
|
||||
items[i].finish();
|
||||
|
||||
hashes = Object.keys(this.request.map);
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
this.request.map[hash].finish(new Error('Pool closed.'));
|
||||
}
|
||||
|
||||
if (this.peers.load)
|
||||
this.peers.load.destroy();
|
||||
|
||||
peers = this.peers.regular.slice();
|
||||
|
||||
for (i = 0; i < peers.length; i++)
|
||||
peers[i].destroy();
|
||||
|
||||
peers = this.peers.pending.slice();
|
||||
|
||||
for (i = 0; i < peers.length; i++)
|
||||
peers[i].destroy();
|
||||
|
||||
peers = this.peers.leeches.slice();
|
||||
|
||||
for (i = 0; i < peers.length; i++)
|
||||
peers[i].destroy();
|
||||
|
||||
this.unlisten(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get peer by host.
|
||||
* @param {String} addr
|
||||
@ -1859,7 +1839,7 @@ Pool.prototype.getRandom = function getRandom(hosts, unique) {
|
||||
|
||||
Pool.prototype.addHost = function addHost(host) {
|
||||
if (typeof host === 'string')
|
||||
host = NetworkAddress.fromHostname(host);
|
||||
host = NetworkAddress.fromHostname(host, this.network);
|
||||
|
||||
if (this.hosts.length > 500)
|
||||
return;
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
var bcoin = require('./env');
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
var Node = bcoin.node;
|
||||
|
||||
/**
|
||||
* Create an spv node which only maintains
|
||||
@ -37,20 +38,7 @@ function SPVNode(options) {
|
||||
if (!(this instanceof SPVNode))
|
||||
return new SPVNode(options);
|
||||
|
||||
bcoin.node.call(this, options);
|
||||
|
||||
this.loaded = false;
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
utils.inherits(SPVNode, bcoin.node);
|
||||
|
||||
SPVNode.prototype._init = function _init() {
|
||||
var self = this;
|
||||
var options;
|
||||
|
||||
this.wallet = null;
|
||||
Node.call(this, options);
|
||||
|
||||
this.chain = new bcoin.chain({
|
||||
network: this.network,
|
||||
@ -84,6 +72,19 @@ SPVNode.prototype._init = function _init() {
|
||||
});
|
||||
}
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
utils.inherits(SPVNode, Node);
|
||||
|
||||
/**
|
||||
* Initialize the node.
|
||||
* @private
|
||||
*/
|
||||
|
||||
SPVNode.prototype._init = function _init() {
|
||||
var self = this;
|
||||
|
||||
// Bind to errors
|
||||
this.pool.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
@ -136,14 +137,26 @@ SPVNode.prototype._init = function _init() {
|
||||
this.walletdb.on('save address', function(hash) {
|
||||
self.pool.watch(hash, 'hex');
|
||||
});
|
||||
};
|
||||
|
||||
function load(err) {
|
||||
/**
|
||||
* Open the node and all its child objects,
|
||||
* wait for the database to load.
|
||||
* @alias SPVNode#open
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
SPVNode.prototype._open = function open(callback) {
|
||||
var self = this;
|
||||
var options;
|
||||
|
||||
function done(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
return callback(err);
|
||||
|
||||
self.loaded = true;
|
||||
self.emit('open');
|
||||
bcoin.debug('Node is loaded.');
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
options = utils.merge({
|
||||
@ -223,7 +236,28 @@ SPVNode.prototype._init = function _init() {
|
||||
return next();
|
||||
self.http.open(next);
|
||||
}
|
||||
], load);
|
||||
], done);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the node, wait for the database to close.
|
||||
* @alias SPVNode#close
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
SPVNode.prototype._close = function close(callback) {
|
||||
var self = this;
|
||||
|
||||
utils.parallel([
|
||||
function(next) {
|
||||
if (!self.http)
|
||||
return next();
|
||||
self.http.close(next);
|
||||
},
|
||||
this.walletdb.close.bind(this.walletdb),
|
||||
this.pool.close.bind(this.pool),
|
||||
this.chain.close.bind(this.chain)
|
||||
], callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -284,43 +318,6 @@ SPVNode.prototype.stopSync = function stopSync() {
|
||||
return this.pool.stopSync();
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the node and all its child objects,
|
||||
* wait for the database to load.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
SPVNode.prototype.open = function open(callback) {
|
||||
if (this.loaded)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
this.once('open', callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the node, wait for the database to close.
|
||||
* @method
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
SPVNode.prototype.close =
|
||||
SPVNode.prototype.destroy = function destroy(callback) {
|
||||
var self = this;
|
||||
|
||||
this.wallet.destroy();
|
||||
|
||||
utils.parallel([
|
||||
function(next) {
|
||||
if (!self.http)
|
||||
return next();
|
||||
self.http.close(next);
|
||||
},
|
||||
this.walletdb.close.bind(this.walletdb),
|
||||
this.pool.close.bind(this.pool),
|
||||
this.chain.close.bind(this.chain)
|
||||
], callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a {@link Wallet} in the wallet database.
|
||||
* @param {Object} options - See {@link Wallet}.
|
||||
|
||||
@ -76,10 +76,22 @@ function TXDB(db, options) {
|
||||
|
||||
utils.inherits(TXDB, EventEmitter);
|
||||
|
||||
/**
|
||||
* Invoke the mutex lock.
|
||||
* @private
|
||||
* @returns {Function} unlock
|
||||
*/
|
||||
|
||||
TXDB.prototype._lock = function _lock(func, args, force) {
|
||||
return this.locker.lock(func, args, force);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the bloom filter into memory.
|
||||
* @private
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
TXDB.prototype._loadFilter = function loadFilter(callback) {
|
||||
var self = this;
|
||||
|
||||
@ -96,6 +108,13 @@ TXDB.prototype._loadFilter = function loadFilter(callback) {
|
||||
}, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test the bloom filter against an array of address hashes.
|
||||
* @private
|
||||
* @param {Hash[]} addresses
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
TXDB.prototype._testFilter = function _testFilter(addresses) {
|
||||
var i;
|
||||
|
||||
@ -1668,6 +1687,10 @@ TXDB.prototype.zap = function zap(id, age, callback, force) {
|
||||
* WalletMap
|
||||
* @constructor
|
||||
* @private
|
||||
* @property {WalletMember[]} inputs
|
||||
* @property {WalletMember[]} outputs
|
||||
* @property {WalletMember[]} accounts
|
||||
* @property {Object} table
|
||||
*/
|
||||
|
||||
function WalletMap() {
|
||||
@ -1680,6 +1703,13 @@ function WalletMap() {
|
||||
this.table = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject properties from table and tx.
|
||||
* @private
|
||||
* @param {Object} table
|
||||
* @param {TX} tx
|
||||
*/
|
||||
|
||||
WalletMap.prototype.fromTX = function fromTX(table, tx) {
|
||||
var i, members, input, output, key;
|
||||
|
||||
@ -1801,10 +1831,24 @@ WalletMap.prototype.fromTX = function fromTX(table, tx) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate wallet map from tx.
|
||||
* @param {Object} table
|
||||
* @param {TX} tx
|
||||
* @returns {WalletMap}
|
||||
*/
|
||||
|
||||
WalletMap.fromTX = function fromTX(table, tx) {
|
||||
return new WalletMap().fromTX(table, tx);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the map has paths
|
||||
* for a given address hash.
|
||||
* @param {Hash} address
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
WalletMap.prototype.hasPaths = function hasPaths(address) {
|
||||
var paths;
|
||||
|
||||
@ -1816,10 +1860,21 @@ WalletMap.prototype.hasPaths = function hasPaths(address) {
|
||||
return paths && paths.length !== 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get paths for a given address hash.
|
||||
* @param {Hash} address
|
||||
* @returns {Path[]|null}
|
||||
*/
|
||||
|
||||
WalletMap.prototype.getPaths = function getPaths(address) {
|
||||
return this.table[address];
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the map to a json-friendly object.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
WalletMap.prototype.toJSON = function toJSON() {
|
||||
return {
|
||||
inputs: this.inputs.map(function(input) {
|
||||
@ -1831,6 +1886,12 @@ WalletMap.prototype.toJSON = function toJSON() {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from json object.
|
||||
* @private
|
||||
* @param {Object}
|
||||
*/
|
||||
|
||||
WalletMap.prototype.fromJSON = function fromJSON(json) {
|
||||
var i, j, table, account, input, output, path;
|
||||
var hash, paths, hashes, accounts, values, key;
|
||||
@ -1904,6 +1965,12 @@ WalletMap.prototype.fromJSON = function fromJSON(json) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate map from json object.
|
||||
* @param {Object}
|
||||
* @returns {WalletMap}
|
||||
*/
|
||||
|
||||
WalletMap.fromJSON = function fromJSON(json) {
|
||||
return new WalletMap().fromJSON(json);
|
||||
};
|
||||
@ -1912,6 +1979,11 @@ WalletMap.fromJSON = function fromJSON(json) {
|
||||
* MapMember
|
||||
* @constructor
|
||||
* @private
|
||||
* @property {WalletID} id
|
||||
* @property {String} name - Account name.
|
||||
* @property {Number} account - Account index.
|
||||
* @property {Path[]} paths
|
||||
* @property {Amount} value
|
||||
*/
|
||||
|
||||
function MapMember() {
|
||||
@ -1925,10 +1997,20 @@ function MapMember() {
|
||||
this.value = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert member to a key in the form of (id|account).
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
MapMember.prototype.toKey = function toKey() {
|
||||
return this.id + '/' + this.account;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the member to a json-friendly object.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
MapMember.prototype.toJSON = function toJSON() {
|
||||
return {
|
||||
id: this.id,
|
||||
@ -1941,6 +2023,12 @@ MapMember.prototype.toJSON = function toJSON() {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from json object.
|
||||
* @private
|
||||
* @param {Object} json
|
||||
*/
|
||||
|
||||
MapMember.prototype.fromJSON = function fromJSON(json) {
|
||||
var i, path;
|
||||
|
||||
@ -1958,10 +2046,22 @@ MapMember.prototype.fromJSON = function fromJSON(json) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate member from json object.
|
||||
* @param {Object} json
|
||||
* @returns {MapMember}
|
||||
*/
|
||||
|
||||
MapMember.fromJSON = function fromJSON(json) {
|
||||
return new MapMember().fromJSON(json);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from path.
|
||||
* @private
|
||||
* @param {Path} path
|
||||
*/
|
||||
|
||||
MapMember.prototype.fromPath = function fromPath(path) {
|
||||
this.id = path.id;
|
||||
this.name = path.name;
|
||||
@ -1969,6 +2069,12 @@ MapMember.prototype.fromPath = function fromPath(path) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate member from path.
|
||||
* @param {Path} path
|
||||
* @returns {MapMember}
|
||||
*/
|
||||
|
||||
MapMember.fromPath = function fromPath(path) {
|
||||
return new MapMember().fromPath(path);
|
||||
};
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
|
||||
var bcoin = require('./env');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var AsyncObject = require('./async');
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
var constants = bcoin.protocol.constants;
|
||||
@ -44,17 +45,220 @@ function WalletDB(options) {
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
EventEmitter.call(this);
|
||||
AsyncObject.call(this);
|
||||
|
||||
this.watchers = {};
|
||||
this.options = options;
|
||||
this.loaded = false;
|
||||
this.network = bcoin.network.get(options.network);
|
||||
this.watchers = {};
|
||||
|
||||
this.db = bcoin.ldb({
|
||||
network: this.network,
|
||||
name: this.options.name || 'wallet',
|
||||
location: this.options.location,
|
||||
db: this.options.db,
|
||||
cacheSize: 8 << 20,
|
||||
writeBufferSize: 4 << 20
|
||||
});
|
||||
|
||||
this.tx = new bcoin.txdb(this, {
|
||||
network: this.network,
|
||||
verify: this.options.verify,
|
||||
useFilter: true
|
||||
});
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
utils.inherits(WalletDB, EventEmitter);
|
||||
utils.inherits(WalletDB, AsyncObject);
|
||||
|
||||
/**
|
||||
* Initialize wallet db.
|
||||
* @private
|
||||
*/
|
||||
|
||||
WalletDB.prototype._init = function _init() {
|
||||
var self = this;
|
||||
|
||||
this.tx.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
this.tx.on('tx', function(tx, map) {
|
||||
var i, path;
|
||||
|
||||
self.emit('tx', tx, map);
|
||||
|
||||
for (i = 0; i < map.accounts.length; i++) {
|
||||
path = map.accounts[i];
|
||||
self.fire(path.id, 'tx', tx, path.name);
|
||||
}
|
||||
});
|
||||
|
||||
this.tx.on('conflict', function(tx, map) {
|
||||
var i, path;
|
||||
|
||||
self.emit('conflict', tx, map);
|
||||
|
||||
for (i = 0; i < map.accounts.length; i++) {
|
||||
path = map.accounts[i];
|
||||
self.fire(path.id, 'conflict', tx, path.name);
|
||||
}
|
||||
});
|
||||
|
||||
this.tx.on('confirmed', function(tx, map) {
|
||||
var i, path;
|
||||
|
||||
self.emit('confirmed', tx, map);
|
||||
|
||||
for (i = 0; i < map.accounts.length; i++) {
|
||||
path = map.accounts[i];
|
||||
self.fire(path.id, 'confirmed', tx, path.name);
|
||||
}
|
||||
});
|
||||
|
||||
this.tx.on('unconfirmed', function(tx, map) {
|
||||
var i, path;
|
||||
|
||||
self.emit('unconfirmed', tx, map);
|
||||
|
||||
for (i = 0; i < map.accounts.length; i++) {
|
||||
path = map.accounts[i];
|
||||
self.fire(path.id, 'unconfirmed', tx, path.name);
|
||||
}
|
||||
});
|
||||
|
||||
this.tx.on('updated', function(tx, map) {
|
||||
var i, path, keys, id;
|
||||
|
||||
self.emit('updated', tx, map);
|
||||
|
||||
for (i = 0; i < map.accounts.length; i++) {
|
||||
path = map.accounts[i];
|
||||
self.fire(path.id, 'updated', tx, path.name);
|
||||
}
|
||||
|
||||
self.updateBalances(tx, map, function(err, balances) {
|
||||
if (err) {
|
||||
self.emit('error', err);
|
||||
return;
|
||||
}
|
||||
|
||||
keys = Object.keys(balances);
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
id = keys[i];
|
||||
self.fire(id, 'balance', balances[id]);
|
||||
}
|
||||
|
||||
self.emit('balances', balances, map);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the walletdb, wait for the database to load.
|
||||
* @alias WalletDB#open
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype._open = function open(callback) {
|
||||
var self = this;
|
||||
|
||||
this.db.open(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
self.tx._loadFilter(callback);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the walletdb, wait for the database to close.
|
||||
* @alias WalletDB#close
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype._close = function close(callback) {
|
||||
var self = this;
|
||||
var keys = Object.keys(this.watchers);
|
||||
var watcher;
|
||||
|
||||
utils.forEachSerial(keys, function(key, next) {
|
||||
watcher = self.watchers[key];
|
||||
watcher.refs = 1;
|
||||
watcher.object.destroy(next);
|
||||
}, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
self.db.close(callback);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit balance events after a tx is saved.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {WalletMap} map
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype.updateBalances = function updateBalances(tx, map, callback) {
|
||||
var self = this;
|
||||
var balances = {};
|
||||
var id;
|
||||
|
||||
utils.forEachSerial(map.outputs, function(output, next) {
|
||||
id = output.id;
|
||||
|
||||
if (self.listeners('balance').length === 0
|
||||
&& !self.hasListener(id, 'balance')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (balances[id] != null)
|
||||
return next();
|
||||
|
||||
self.getBalance(id, function(err, balance) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
balances[id] = balance;
|
||||
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
return callback(null, balances);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Derive new addresses after a tx is saved.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {WalletMap} map
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype.syncOutputs = function syncOutputs(tx, map, callback) {
|
||||
var self = this;
|
||||
var id;
|
||||
|
||||
utils.forEachSerial(map.outputs, function(output, next) {
|
||||
id = output.id;
|
||||
|
||||
self.syncOutputDepth(id, tx, function(err, receive, change) {
|
||||
if (err)
|
||||
return next(err);
|
||||
self.fire(id, 'address', receive, change);
|
||||
self.emit('address', receive, change, map);
|
||||
next();
|
||||
});
|
||||
}, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Dump database (for debugging).
|
||||
@ -99,159 +303,6 @@ WalletDB.prototype.dump = function dump(callback) {
|
||||
})();
|
||||
};
|
||||
|
||||
WalletDB.prototype._init = function _init() {
|
||||
var self = this;
|
||||
|
||||
if (this.loaded)
|
||||
return;
|
||||
|
||||
this.db = bcoin.ldb({
|
||||
network: this.network,
|
||||
name: this.options.name || 'wallet',
|
||||
location: this.options.location,
|
||||
db: this.options.db,
|
||||
cacheSize: 8 << 20,
|
||||
writeBufferSize: 4 << 20
|
||||
});
|
||||
|
||||
this.tx = new bcoin.txdb(this, {
|
||||
network: this.network,
|
||||
verify: this.options.verify,
|
||||
useFilter: true
|
||||
});
|
||||
|
||||
this.tx.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
this.tx.on('tx', function(tx, map) {
|
||||
self.emit('tx', tx, map);
|
||||
map.accounts.forEach(function(path) {
|
||||
self.fire(path.id, 'tx', tx, path.name);
|
||||
});
|
||||
});
|
||||
|
||||
this.tx.on('conflict', function(tx, map) {
|
||||
self.emit('conflict', tx, map);
|
||||
map.accounts.forEach(function(path) {
|
||||
self.fire(path.id, 'conflict', tx, path.name);
|
||||
});
|
||||
});
|
||||
|
||||
this.tx.on('confirmed', function(tx, map) {
|
||||
self.emit('confirmed', tx, map);
|
||||
map.accounts.forEach(function(path) {
|
||||
self.fire(path.id, 'confirmed', tx, path.name);
|
||||
});
|
||||
});
|
||||
|
||||
this.tx.on('unconfirmed', function(tx, map) {
|
||||
self.emit('unconfirmed', tx, map);
|
||||
map.accounts.forEach(function(path) {
|
||||
self.fire(path.id, 'unconfirmed', tx, path.name);
|
||||
});
|
||||
});
|
||||
|
||||
this.tx.on('updated', function(tx, map) {
|
||||
self.emit('updated', tx, map);
|
||||
map.accounts.forEach(function(path) {
|
||||
self.fire(path.id, 'updated', tx, path.name);
|
||||
});
|
||||
self.updateBalances(tx, map, function(err, balances) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
Object.keys(balances).forEach(function(id) {
|
||||
self.fire(id, 'balance', balances[id]);
|
||||
});
|
||||
|
||||
self.emit('balances', balances, map);
|
||||
});
|
||||
});
|
||||
|
||||
this.db.open(function(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
self.tx._loadFilter(function(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
self.emit('open');
|
||||
self.loaded = true;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.updateBalances = function updateBalances(tx, map, callback) {
|
||||
var self = this;
|
||||
var balances = {};
|
||||
|
||||
utils.forEachSerial(map.outputs, function(output, next) {
|
||||
var id = output.id;
|
||||
|
||||
if (self.listeners('balance').length === 0
|
||||
&& !self.hasListener(id, 'balance')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (balances[id] != null)
|
||||
return next();
|
||||
|
||||
self.getBalance(id, function(err, balance) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
balances[id] = balance;
|
||||
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
return callback(null, balances);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.syncOutputs = function syncOutputs(tx, map, callback) {
|
||||
var self = this;
|
||||
utils.forEachSerial(map.outputs, function(output, next) {
|
||||
var id = output.id;
|
||||
self.syncOutputDepth(id, tx, function(err, receive, change) {
|
||||
if (err)
|
||||
return next(err);
|
||||
self.fire(id, 'address', receive, change);
|
||||
self.emit('address', receive, change, map);
|
||||
next();
|
||||
});
|
||||
}, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the walletdb, wait for the database to load.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype.open = function open(callback) {
|
||||
if (this.loaded)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
this.once('open', callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the walletdb, wait for the database to close.
|
||||
* @method
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype.close =
|
||||
WalletDB.prototype.destroy = function destroy(callback) {
|
||||
callback = utils.ensure(callback);
|
||||
this.db.close(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register an object with the walletdb.
|
||||
* @param {Object} object
|
||||
@ -480,7 +531,8 @@ WalletDB.prototype.has = function has(id, callback) {
|
||||
|
||||
WalletDB.prototype.ensure = function ensure(options, callback) {
|
||||
var self = this;
|
||||
return this.get(options.id, function(err, wallet) {
|
||||
|
||||
this.get(options.id, function(err, wallet) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -502,7 +554,7 @@ WalletDB.prototype.getAccount = function getAccount(id, name, callback) {
|
||||
var self = this;
|
||||
var account;
|
||||
|
||||
return this.getAccountIndex(id, name, function(err, index) {
|
||||
this.getAccountIndex(id, name, function(err, index) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -540,6 +592,7 @@ WalletDB.prototype.getAccount = function getAccount(id, name, callback) {
|
||||
|
||||
WalletDB.prototype.getAccounts = function getAccounts(id, callback) {
|
||||
var accounts = [];
|
||||
|
||||
this.db.iterate({
|
||||
gte: 'i/' + id + '/',
|
||||
lte: 'i/' + id + '/~',
|
||||
@ -571,7 +624,7 @@ WalletDB.prototype.getAccountIndex = function getAccountIndex(id, name, callback
|
||||
if (typeof name === 'number')
|
||||
return callback(null, name);
|
||||
|
||||
return this.db.get('i/' + id + '/' + name, function(err, index) {
|
||||
this.db.get('i/' + id + '/' + name, function(err, index) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback();
|
||||
|
||||
@ -1149,6 +1202,12 @@ WalletDB.prototype.getRedeem = function getRedeem(id, hash, callback) {
|
||||
* Path
|
||||
* @constructor
|
||||
* @private
|
||||
* @property {WalletID} id
|
||||
* @property {String} name - Account name.
|
||||
* @property {Number} account - Account index.
|
||||
* @property {Number} change - Change index.
|
||||
* @property {Number} index - Address index.
|
||||
* @property {Address|null} address
|
||||
*/
|
||||
|
||||
function Path() {
|
||||
@ -1163,6 +1222,12 @@ function Path() {
|
||||
this.address = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject properties from serialized data.
|
||||
* @private
|
||||
* @param {Buffer} data
|
||||
*/
|
||||
|
||||
Path.prototype.fromRaw = function fromRaw(data) {
|
||||
var p = new BufferReader(data);
|
||||
this.id = p.readVarString('utf8');
|
||||
@ -1173,10 +1238,21 @@ Path.prototype.fromRaw = function fromRaw(data) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate path from serialized data.
|
||||
* @param {Buffer} data
|
||||
* @returns {Path}
|
||||
*/
|
||||
|
||||
Path.fromRaw = function fromRaw(data) {
|
||||
return new Path().fromRaw(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize path.
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Path.prototype.toRaw = function toRaw(writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
|
||||
@ -1192,6 +1268,13 @@ Path.prototype.toRaw = function toRaw(writer) {
|
||||
return p;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from keyring.
|
||||
* @private
|
||||
* @param {WalletID} id
|
||||
* @param {KeyRing} address
|
||||
*/
|
||||
|
||||
Path.prototype.fromKeyRing = function fromKeyRing(id, address) {
|
||||
this.id = id;
|
||||
this.name = address.name;
|
||||
@ -1201,22 +1284,32 @@ Path.prototype.fromKeyRing = function fromKeyRing(id, address) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate path from keyring.
|
||||
* @param {WalletID} id
|
||||
* @param {KeyRing} address
|
||||
* @returns {Path}
|
||||
*/
|
||||
|
||||
Path.fromKeyRing = function fromKeyRing(id, address) {
|
||||
return new Path().fromKeyRing(id, address);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert path object to string derivation path.
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
Path.prototype.toPath = function() {
|
||||
return 'm/' + this.account
|
||||
+ '\'/' + this.change
|
||||
+ '/' + this.index;
|
||||
};
|
||||
|
||||
Path.prototype.inspect = function() {
|
||||
return '<Path: ' + this.id
|
||||
+ '/' + this.name
|
||||
+ ': ' + this.toPath()
|
||||
+ '>';
|
||||
};
|
||||
/**
|
||||
* Convert path to a json-friendly object.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
Path.prototype.toJSON = function toJSON() {
|
||||
return {
|
||||
@ -1226,6 +1319,12 @@ Path.prototype.toJSON = function toJSON() {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from json object.
|
||||
* @private
|
||||
* @param {Object} json
|
||||
*/
|
||||
|
||||
Path.prototype.fromJSON = function fromJSON(json) {
|
||||
var indexes = bcoin.hd.parsePath(json.path, constants.hd.MAX_INDEX);
|
||||
|
||||
@ -1242,14 +1341,30 @@ Path.prototype.fromJSON = function fromJSON(json) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate path from json object.
|
||||
* @param {Object} json
|
||||
* @returns {Path}
|
||||
*/
|
||||
|
||||
Path.fromJSON = function fromJSON(json) {
|
||||
return new Path().fromJSON(json);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert path to a key in the form of (id|account).
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
Path.prototype.toKey = function toKey() {
|
||||
return this.id + '/' + this.account;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert path to a compact json object.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
Path.prototype.toCompact = function toCompact() {
|
||||
return {
|
||||
path: 'm/' + this.change + '/' + this.index,
|
||||
@ -1257,6 +1372,12 @@ Path.prototype.toCompact = function toCompact() {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from compact json object.
|
||||
* @private
|
||||
* @param {Object} json
|
||||
*/
|
||||
|
||||
Path.prototype.fromCompact = function fromCompact(json) {
|
||||
var indexes = bcoin.hd.parsePath(json.path, constants.hd.MAX_INDEX);
|
||||
|
||||
@ -1271,10 +1392,28 @@ Path.prototype.fromCompact = function fromCompact(json) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate path from compact json object.
|
||||
* @param {Object} json
|
||||
* @returns {Path}
|
||||
*/
|
||||
|
||||
Path.fromCompact = function fromCompact(json) {
|
||||
return new Path().fromCompact(json);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inspect the path.
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
Path.prototype.inspect = function() {
|
||||
return '<Path: ' + this.id
|
||||
+ '/' + this.name
|
||||
+ ': ' + this.toPath()
|
||||
+ '>';
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
@ -58,7 +58,6 @@ describe('Chain', function() {
|
||||
|
||||
function deleteCoins(tx) {
|
||||
if (tx.txs) {
|
||||
delete tx.view;
|
||||
deleteCoins(tx.txs);
|
||||
return;
|
||||
}
|
||||
@ -67,12 +66,16 @@ describe('Chain', function() {
|
||||
return;
|
||||
}
|
||||
tx.inputs.forEach(function(input) {
|
||||
delete input.coin;
|
||||
input.coin = null;
|
||||
});
|
||||
}
|
||||
|
||||
it('should open chain and miner', function(cb) {
|
||||
miner.open(function(err) {
|
||||
miner.open(cb);
|
||||
});
|
||||
|
||||
it('should open walletdb', function(cb) {
|
||||
walletdb.open(function(err) {
|
||||
assert.ifError(err);
|
||||
walletdb.create({}, function(err, w) {
|
||||
assert.ifError(err);
|
||||
|
||||
@ -21,7 +21,7 @@ describe('Mempool', function() {
|
||||
db: 'memory'
|
||||
});
|
||||
|
||||
var wdb = new bcoin.walletdb({
|
||||
var walletdb = new bcoin.walletdb({
|
||||
name: 'mempool-wallet-test',
|
||||
db: 'memory',
|
||||
verify: true
|
||||
@ -35,8 +35,12 @@ describe('Mempool', function() {
|
||||
mempool.open(cb);
|
||||
});
|
||||
|
||||
it('should open walletdb', function(cb) {
|
||||
walletdb.open(cb);
|
||||
});
|
||||
|
||||
it('should open wallet', function(cb) {
|
||||
wdb.create({}, function(err, wallet) {
|
||||
walletdb.create({}, function(err, wallet) {
|
||||
assert.ifError(err);
|
||||
w = wallet;
|
||||
cb();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user