db refactoring.

This commit is contained in:
Christopher Jeffrey 2016-04-04 18:45:02 -07:00
parent 849f1d842e
commit 4b20a87b4f
13 changed files with 252 additions and 284 deletions

View File

@ -18,9 +18,9 @@ var VerifyError = utils.VerifyError;
* Chain * Chain
*/ */
function Chain(node, options) { function Chain(options) {
if (!(this instanceof Chain)) if (!(this instanceof Chain))
return new Chain(node, options); return new Chain(options);
EventEmitter.call(this); EventEmitter.call(this);
@ -29,9 +29,7 @@ function Chain(node, options) {
this.options = options; this.options = options;
this.node = node;
this.loaded = false; this.loaded = false;
this.mempool = node.mempool;
this.db = new bcoin.chaindb(this, options); this.db = new bcoin.chaindb(this, options);
this.total = 0; this.total = 0;
this.orphanLimit = options.orphanLimit || (20 << 20); this.orphanLimit = options.orphanLimit || (20 << 20);
@ -43,7 +41,6 @@ function Chain(node, options) {
this.tip = null; this.tip = null;
this.height = -1; this.height = -1;
this.segwitActive = null; this.segwitActive = null;
this.spv = !!options.spv;
this.orphan = { this.orphan = {
map: {}, map: {},
@ -52,8 +49,6 @@ function Chain(node, options) {
size: 0 size: 0
}; };
Chain.global = this;
this._init(); this._init();
} }
@ -63,17 +58,7 @@ Chain.prototype._init = function _init() {
var self = this; var self = this;
function getHost() { function getHost() {
var peer; return 'unknown';
if (!self.node || !self.node.pool)
return;
peer = self.node.pool.peers.load;
if (!peer)
return 'unknown';
return peer.host;
} }
// Hook into events for debugging // Hook into events for debugging
@ -242,7 +227,7 @@ Chain.prototype._preload = function _preload(callback) {
if (!this.options.preload) if (!this.options.preload)
return callback(); return callback();
if (!this.spv) if (!this.options.spv)
return callback(); return callback();
if (network.type !== 'main') if (network.type !== 'main')
@ -592,7 +577,7 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba
var self = this; var self = this;
var height = prev.height + 1; var height = prev.height + 1;
if (this.spv || block.type !== 'block') if (this.options.spv || block.type !== 'block')
return callback(); return callback();
if (block.isGenesis()) if (block.isGenesis())
@ -629,7 +614,7 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac
var scriptCheck = true; var scriptCheck = true;
var historical = false; var historical = false;
if (this.spv || block.type !== 'block') if (this.options.spv || block.type !== 'block')
return callback(); return callback();
if (block.isGenesis()) if (block.isGenesis())

View File

@ -27,19 +27,12 @@ function ChainDB(chain, options) {
EventEmitter.call(this); EventEmitter.call(this);
this.options = options; this.options = options;
this.node = chain.node;
this.network = chain.node.network;
this.chain = chain; this.chain = chain;
this.queue = {};
this.queueSize = 0;
this.size = 0;
this.fd = null;
this.loaded = false;
this.keepBlocks = options.keepBlocks || 288; this.keepBlocks = options.keepBlocks || 288;
this.prune = !!options.prune; this.prune = !!options.prune;
this.spv = !!options.spv;
this.loaded = false;
// Need to cache up to the retarget interval // Need to cache up to the retarget interval
// if we're going to be checking the damn // if we're going to be checking the damn
@ -66,7 +59,9 @@ ChainDB.prototype._init = function _init() {
if (this.loaded) if (this.loaded)
return; return;
this.db = bcoin.ldb(this.spv ? 'spvchain' : 'chain', { this.db = bcoin.ldb({
name: this.options.name || (this.options.spv ? 'spvchain' : 'chain'),
location: this.options.location,
compression: true, compression: true,
cacheSize: 16 << 20, cacheSize: 16 << 20,
writeBufferSize: 8 << 20 writeBufferSize: 8 << 20
@ -82,10 +77,10 @@ ChainDB.prototype._init = function _init() {
if (err) if (err)
return self.emit('error', err); return self.emit('error', err);
utils.debug('Chain successfully loaded.');
self.loaded = true; self.loaded = true;
self.emit('open'); self.emit('open');
utils.debug('Chain successfully loaded.');
} }
self.db.get('c/b/' + network.genesis.hash, function(err, exists) { self.db.get('c/b/' + network.genesis.hash, function(err, exists) {
@ -578,7 +573,7 @@ ChainDB.prototype.has = function has(height, callback) {
}; };
ChainDB.prototype.saveBlock = function saveBlock(block, batch, connect, callback) { ChainDB.prototype.saveBlock = function saveBlock(block, batch, connect, callback) {
if (this.spv) if (this.options.spv)
return utils.nextTick(callback); return utils.nextTick(callback);
batch.put('b/b/' + block.hash('hex'), block.toCompact()); batch.put('b/b/' + block.hash('hex'), block.toCompact());
@ -596,7 +591,7 @@ ChainDB.prototype.saveBlock = function saveBlock(block, batch, connect, callback
ChainDB.prototype.removeBlock = function removeBlock(hash, batch, callback) { ChainDB.prototype.removeBlock = function removeBlock(hash, batch, callback) {
var self = this; var self = this;
if (this.spv) if (this.options.spv)
return utils.nextTick(callback); return utils.nextTick(callback);
this._ensureHistory(hash, function(err, block) { this._ensureHistory(hash, function(err, block) {
@ -619,7 +614,7 @@ ChainDB.prototype.removeBlock = function removeBlock(hash, batch, callback) {
ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) { ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) {
var self = this; var self = this;
if (this.spv) { if (this.options.spv) {
self.emit('add block', block); self.emit('add block', block);
return utils.nextTick(callback); return utils.nextTick(callback);
} }
@ -690,7 +685,7 @@ ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) {
ChainDB.prototype.disconnectBlock = function disconnectBlock(hash, batch, callback) { ChainDB.prototype.disconnectBlock = function disconnectBlock(hash, batch, callback) {
var self = this; var self = this;
if (this.spv) if (this.options.spv)
return utils.nextTick(callback); return utils.nextTick(callback);
this._ensureHistory(hash, function(err, block) { this._ensureHistory(hash, function(err, block) {
@ -1258,7 +1253,7 @@ ChainDB.prototype._getTX = function _getTX(hash, callback) {
ChainDB.prototype.isUnspentTX = function isUnspentTX(hash, callback) { ChainDB.prototype.isUnspentTX = function isUnspentTX(hash, callback) {
return callback(null, false); return callback(null, false);
if (this.spv) if (this.options.spv)
return callback(null, false); return callback(null, false);
return this.isSpentTX(hash, function(err, spent) { return this.isSpentTX(hash, function(err, spent) {
@ -1300,7 +1295,7 @@ ChainDB.prototype.isSpentTX = function isSpentTX(hash, callback) {
ChainDB.prototype._pruneBlock = function _pruneBlock(block, batch, callback) { ChainDB.prototype._pruneBlock = function _pruneBlock(block, batch, callback) {
var futureHeight; var futureHeight;
if (this.spv) if (this.options.spv)
return callback(); return callback();
if (!this.prune) if (!this.prune)

View File

@ -23,8 +23,6 @@ function Fullnode(options) {
this.loaded = false; this.loaded = false;
Fullnode.global = this;
this._init(); this._init();
} }
@ -36,15 +34,16 @@ Fullnode.prototype._init = function _init() {
this.wallet = null; this.wallet = null;
this.chain = new bcoin.chain(this, { this.chain = new bcoin.chain({
preload: false, preload: false,
spv: false, spv: false,
prune: this.options.prune, prune: this.options.prune,
useCheckpoints: this.options.useCheckpoints useCheckpoints: this.options.useCheckpoints
}); });
// Mempool needs access to blockdb. // Mempool needs access to the chain.
this.mempool = new bcoin.mempool(this, { this.mempool = new bcoin.mempool({
chain: this.chain,
limitFree: this.options.limitFree, limitFree: this.options.limitFree,
limitFreeRelay: this.options.limitFreeRelay, limitFreeRelay: this.options.limitFreeRelay,
requireStandard: this.options.requireStandard, requireStandard: this.options.requireStandard,
@ -52,26 +51,31 @@ Fullnode.prototype._init = function _init() {
replaceByFee: this.options.replaceByFee replaceByFee: this.options.replaceByFee
}); });
// Pool needs access to the chain. // Pool needs access to the chain and mempool.
this.pool = new bcoin.pool(this, { this.pool = new bcoin.pool({
chain: this.chain,
mempool: this.mempool,
witness: this.network.witness, witness: this.network.witness,
listen: this.options.listen, listen: this.options.listen,
selfish: this.options.selfish, selfish: this.options.selfish,
spv: false spv: false
}); });
// Miner needs access to the mempool. // Miner needs access to the chain and mempool.
this.miner = new bcoin.miner(this, { this.miner = new bcoin.miner({
chain: this.chain,
mempool: this.mempool,
address: this.options.payoutAddress, address: this.options.payoutAddress,
coinbaseFlags: this.options.coinbaseFlags coinbaseFlags: this.options.coinbaseFlags
}); });
// WalletDB needs access to the network type. this.walletdb = new bcoin.walletdb({
this.walletdb = new bcoin.walletdb(this); verify: false
});
// HTTP needs access to the mempool // HTTP needs access to the node.
// and blockdb. this.http = new bcoin.http.server({
this.http = new bcoin.http.server(this, { node: this,
key: this.options.sslKey, key: this.options.sslKey,
cert: this.options.sslCert, cert: this.options.sslCert,
port: this.options.httpPort || 8080, port: this.options.httpPort || 8080,
@ -141,6 +145,10 @@ Fullnode.prototype._init = function _init() {
}); });
}); });
this.miner.on('block', function(block) {
self.pool.sendBlock(block);
});
function load(err) { function load(err) {
if (err) if (err)
return self.emit('error', err); return self.emit('error', err);

View File

@ -16,14 +16,17 @@ var assert = utils.assert;
* NodeServer * NodeServer
*/ */
function NodeServer(node, options) { function NodeServer(options) {
if (!options) if (!options)
options = {}; options = {};
this.options = options; this.options = options;
this.node = node; this.node = options.node;
this.walletdb = node.walletdb;
this.pool = node.pool; assert(this.node, 'HTTP requires a Node.');
this.walletdb = this.node.walletdb;
this.pool = this.node.pool;
this.loaded = false; this.loaded = false;
this.server = new HTTPServer(options); this.server = new HTTPServer(options);
@ -145,7 +148,7 @@ NodeServer.prototype._init = function _init() {
this.get('/', function(req, res, next, send) { this.get('/', function(req, res, next, send) {
send(200, { send(200, {
version: constants.userAgent, version: constants.userAgent,
network: self.node.network.type network: network.type
}); });
}); });

View File

@ -14,8 +14,8 @@ var db = {};
* LDB * LDB
*/ */
function ldb(name, options) { function ldb(options) {
var file = getLocation(name); var file = getLocation(options);
if (!db[file]) { if (!db[file]) {
if (!options) if (!options)
@ -49,8 +49,10 @@ function ldb(name, options) {
return db[file]; return db[file];
} }
function getLocation(name) { function getLocation(options) {
return bcoin.prefix + '/' + name + '-' + network.type + '.db'; if (options.location)
return options.location;
return bcoin.prefix + '/' + options.name + '-' + network.type + '.db';
} }
function getBackend(backend) { function getBackend(backend) {
@ -77,15 +79,9 @@ function getBackend(backend) {
return require(backend); return require(backend);
} }
function destroy(name, backend, callback) { function destroy(options, callback) {
var file = getLocation(name); var file = getLocation(options);
var backend = getBackend(options.db);
if (!callback) {
callback = backend;
backend = null;
}
backend = getBackend(backend);
if (!backend.destroy) if (!backend.destroy)
return utils.nextTick(callback); return utils.nextTick(callback);
@ -93,15 +89,9 @@ function destroy(name, backend, callback) {
backend.destroy(file, callback); backend.destroy(file, callback);
} }
function repair(name, backend, callback) { function repair(options, callback) {
var file = getLocation(name); var file = getLocation(options);
var backend = getBackend(options.db);
if (!callback) {
callback = backend;
backend = null;
}
backend = getBackend(backend);
if (!backend.repair) if (!backend.repair)
return utils.asyncify(callback)(new Error('Cannot repair.')); return utils.asyncify(callback)(new Error('Cannot repair.'));

View File

@ -15,14 +15,16 @@ var assert = utils.assert;
var BufferWriter = require('./writer'); var BufferWriter = require('./writer');
var BufferReader = require('./reader'); var BufferReader = require('./reader');
var VerifyError = utils.VerifyError; var VerifyError = utils.VerifyError;
var pad32 = utils.pad32;
var DUMMY = new Buffer([0]);
/** /**
* Mempool * Mempool
*/ */
function Mempool(node, options) { function Mempool(options) {
if (!(this instanceof Mempool)) if (!(this instanceof Mempool))
return new Mempool(node, options); return new Mempool(options);
EventEmitter.call(this); EventEmitter.call(this);
@ -30,12 +32,14 @@ function Mempool(node, options) {
options = {}; options = {};
this.options = options; this.options = options;
this.node = node; this.chain = options.chain;
this.chain = node.chain;
assert(this.chain, 'Mempool requires a blockchain.');
this.loaded = false; this.loaded = false;
this.locker = new bcoin.locker(this, this.addTX, 20 << 20); this.locker = new bcoin.locker(this, this.addTX, 20 << 20);
this.writeLock = new bcoin.locker(this);
this.db = null; this.db = null;
this.tx = null; this.tx = null;
@ -55,8 +59,6 @@ function Mempool(node, options) {
// Use an in-memory binary search tree by default // Use an in-memory binary search tree by default
this.backend = this.options.memory === false ? 'leveldb' : 'memory'; this.backend = this.options.memory === false ? 'leveldb' : 'memory';
Mempool.global = this;
this._init(); this._init();
} }
@ -82,6 +84,11 @@ Mempool.prototype.purgePending = function purgePending() {
Mempool.prototype._init = function _init() { Mempool.prototype._init = function _init() {
var self = this; var self = this;
var unlock = this._lock(utils.nop, []); var unlock = this._lock(utils.nop, []);
var options = {
name: this.options.name || 'mempool',
location: this.options.location,
db: this.backend
};
assert(unlock); assert(unlock);
@ -89,22 +96,16 @@ Mempool.prototype._init = function _init() {
// reason for using an on-disk db for the mempool // reason for using an on-disk db for the mempool
// is not for persistence, but to keep ~300mb of // is not for persistence, but to keep ~300mb of
// txs out of main memory. // txs out of main memory.
bcoin.ldb.destroy('mempool', this.backend, function(err) { bcoin.ldb.destroy(options, function(err) {
if (err) { if (err) {
unlock(); unlock();
return self.emit('error', err); return self.emit('error', err);
} }
self.db = bcoin.ldb('mempool', { self.db = bcoin.ldb(options);
db: self.backend
});
self.tx = new bcoin.txdb('m', self.db, { // Use the txdb object for its get methods.
indexExtra: false, self.tx = new bcoin.txdb('m', self.db);
indexAddress: false,
mapAddress: false,
verify: false
});
self.db.open(function(err) { self.db.open(function(err) {
if (err) { if (err) {
@ -456,7 +457,7 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) {
Mempool.prototype.addUnchecked = function addUnchecked(tx, callback) { Mempool.prototype.addUnchecked = function addUnchecked(tx, callback) {
var self = this; var self = this;
this.tx.addUnchecked(tx, function(err) { this._addUnchecked(tx, function(err) {
if (err) if (err)
return callback(err); return callback(err);
@ -466,9 +467,6 @@ Mempool.prototype.addUnchecked = function addUnchecked(tx, callback) {
utils.debug('Added tx %s to the mempool.', tx.rhash); utils.debug('Added tx %s to the mempool.', tx.rhash);
if (self.options.relay)
self.node.broadcast(tx);
self.resolveOrphans(tx, function(err, resolved) { self.resolveOrphans(tx, function(err, resolved) {
if (err) if (err)
return callback(err); return callback(err);
@ -509,7 +507,7 @@ Mempool.prototype.removeUnchecked = function removeUnchecked(tx, callback) {
if (err) if (err)
return callback(err); return callback(err);
self.tx.removeUnchecked(tx, function(err) { self._removeUnchecked(tx, function(err) {
if (err) if (err)
return callback(err); return callback(err);
self.size -= tx.getSize(); self.size -= tx.getSize();
@ -1088,6 +1086,137 @@ Mempool.prototype.getConfidence = function getConfidence(hash, callback) {
}); });
}; };
Mempool.prototype._addUnchecked = function addUnchecked(tx, callback, force) {
var self = this;
var prefix = 'm/';
var hash = tx.hash('hex');
var batch;
var unlock = this.writeLock.lock(addUnchecked, [tx, callback], force);
if (!unlock)
return;
callback = utils.wrap(callback, unlock);
batch = this.db.batch();
batch.put(prefix + 't/t/' + hash, tx.toExtended());
batch.put(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash, DUMMY);
tx.getAddresses().forEach(function(address) {
batch.put(prefix + 't/a/' + address + '/' + hash, DUMMY);
});
tx.inputs.forEach(function(input) {
var key = input.prevout.hash + '/' + input.prevout.index;
var address;
if (tx.isCoinbase())
return;
assert(input.coin);
address = input.getAddress();
batch.del(prefix + 'u/t/' + key);
batch.put(prefix + 's/t/' + key, tx.hash());
if (address)
batch.del(prefix + 'u/a/' + address + '/' + key);
});
tx.outputs.forEach(function(output, i) {
var key = hash + '/' + i;
var address = output.getAddress();
var coin = bcoin.coin(tx, i).toRaw();
batch.put(prefix + 'u/t/' + key, coin);
if (address)
batch.put(prefix + 'u/a/' + address + '/' + key, DUMMY);
});
return batch.write(callback);
};
Mempool.prototype._removeUnchecked = function removeUnchecked(hash, callback, force) {
var self = this;
var prefix = 'm/';
var batch;
var unlock = this.writeLock.lock(removeUnchecked, [hash, callback], force);
if (!unlock)
return;
callback = utils.wrap(callback, unlock);
if (hash.hash)
hash = hash.hash('hex');
this.getTX(hash, function(err, tx) {
if (err)
return callback(err);
if (!tx)
return callback();
batch = self.db.batch();
batch.del(prefix + 't/t/' + hash);
batch.del(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash);
tx.getAddresses().forEach(function(address) {
batch.del(prefix + 't/a/' + address + '/' + hash);
});
utils.forEachSerial(tx.inputs, function(input, next) {
var key = input.prevout.hash + '/' + input.prevout.index;
var address;
if (tx.isCoinbase())
return next();
if (!input.coin)
return next();
address = input.getAddress();
batch.del(prefix + 's/t/' + key);
self.hasTX(input.prevout.hash, function(err, result) {
if (err)
return next(err);
if (result) {
batch.put(prefix + 'u/t/' + key, input.coin.toRaw());
if (address)
batch.put(prefix + 'u/a/' + address + '/' + key, DUMMY);
} else {
batch.del(prefix + 'u/t/' + key);
if (address)
batch.del(prefix + 'u/a/' + address + '/' + key);
}
next();
});
}, function(err) {
if (err)
return callback(err);
tx.outputs.forEach(function(output, i) {
var key = hash + '/' + i;
var address = output.getAddress();
batch.del(prefix + 'u/t/' + key);
if (address)
batch.del(prefix + 'u/a/' + address + '/' + key);
});
return batch.write(callback);
});
});
};
/** /**
* Expose * Expose
*/ */

View File

@ -17,9 +17,9 @@ var EventEmitter = require('events').EventEmitter;
* Miner * Miner
*/ */
function Miner(node, options) { function Miner(options) {
if (!(this instanceof Miner)) if (!(this instanceof Miner))
return new Miner(node, options); return new Miner(options);
EventEmitter.call(this); EventEmitter.call(this);
@ -34,10 +34,11 @@ function Miner(node, options) {
// wants to pass in a faster linked in function. // wants to pass in a faster linked in function.
this.dsha256 = this.options.dsha256 || utils.dsha256; this.dsha256 = this.options.dsha256 || utils.dsha256;
this.node = node; this.pool = options.pool;
this.pool = node.pool; this.chain = options.chain;
this.chain = node.chain; this.mempool = options.mempool;
this.mempool = node.mempool;
assert(this.chain, 'Miner requires a blockchain.');
this.running = false; this.running = false;
this.timeout = null; this.timeout = null;
@ -69,7 +70,7 @@ Miner.prototype._init = function _init() {
return; return;
self.addTX(tx); self.addTX(tx);
}); });
} else { } else if (this.pool) {
this.pool.on('tx', function(tx) { this.pool.on('tx', function(tx) {
if (!self.running) if (!self.running)
return; return;
@ -93,7 +94,6 @@ Miner.prototype._init = function _init() {
block.hash('hex')); block.hash('hex'));
// Emit the block hex as a failsafe (in case we can't send it) // Emit the block hex as a failsafe (in case we can't send it)
utils.debug('Raw: %s', utils.toHex(block.render())); utils.debug('Raw: %s', utils.toHex(block.render()));
self.pool.sendBlock(block);
}); });
this.on('status', function(stat) { this.on('status', function(stat) {

View File

@ -39,8 +39,6 @@ function Node(options) {
this.chain = null; this.chain = null;
this.miner = null; this.miner = null;
this.walletdb = null; this.walletdb = null;
Node.global = this;
} }
utils.inherits(Node, EventEmitter); utils.inherits(Node, EventEmitter);

View File

@ -637,7 +637,7 @@ Peer.prototype._handleGetHeaders = function _handleGetHeaders(payload) {
if (this.chain.db.options.spv) if (this.chain.db.options.spv)
return; return;
if (this.chain.db.prune) if (this.chain.db.options.prune)
return; return;
function collect(err, hash) { function collect(err, hash) {
@ -705,7 +705,7 @@ Peer.prototype._handleGetBlocks = function _handleGetBlocks(payload) {
if (this.chain.db.options.spv) if (this.chain.db.options.spv)
return; return;
if (this.chain.db.prune) if (this.chain.db.options.prune)
return; return;
function done(err) { function done(err) {
@ -907,7 +907,7 @@ Peer.prototype._handleGetData = function handleGetData(items) {
notfound.push({ type: constants.inv.block, hash: hash }); notfound.push({ type: constants.inv.block, hash: hash });
return next(); return next();
} }
if (self.chain.db.prune) { if (self.chain.db.options.prune) {
notfound.push({ type: constants.inv.block, hash: hash }); notfound.push({ type: constants.inv.block, hash: hash });
return; return;
} }
@ -944,7 +944,7 @@ Peer.prototype._handleGetData = function handleGetData(items) {
notfound.push({ type: constants.inv.block, hash: hash }); notfound.push({ type: constants.inv.block, hash: hash });
return next(); return next();
} }
if (self.chain.db.prune) { if (self.chain.db.options.prune) {
notfound.push({ type: constants.inv.block, hash: hash }); notfound.push({ type: constants.inv.block, hash: hash });
return; return;
} }

View File

@ -16,12 +16,12 @@ var constants = bcoin.protocol.constants;
* Pool * Pool
*/ */
function Pool(node, options) { function Pool(options) {
var self = this; var self = this;
var seeds; var seeds;
if (!(this instanceof Pool)) if (!(this instanceof Pool))
return new Pool(node, options); return new Pool(options);
EventEmitter.call(this); EventEmitter.call(this);
@ -29,7 +29,10 @@ function Pool(node, options) {
options = {}; options = {};
this.options = options; this.options = options;
this.network = node.network; this.chain = options.chain;
this.mempool = options.mempool;
assert(this.chain, 'Pool requires a blockchain.');
if (options.relay == null) { if (options.relay == null) {
if (options.spv) if (options.spv)
@ -54,9 +57,6 @@ function Pool(node, options) {
this.size = options.size || 8; this.size = options.size || 8;
this.connected = false; this.connected = false;
this.chain = node.chain;
this.mempool = node.mempool;
if (options.spv) { if (options.spv) {
if (options.headers == null) if (options.headers == null)
options.headers = true; options.headers = true;
@ -138,8 +138,6 @@ function Pool(node, options) {
timeout: options.invTimeout || 60000 timeout: options.invTimeout || 60000
}; };
Pool.global = this;
this.chain.open(function(err) { this.chain.open(function(err) {
if (err) if (err)
return self.emit('error', err); return self.emit('error', err);
@ -161,11 +159,19 @@ Pool.prototype.open = function open(callback) {
}; };
Pool.prototype.connect = function connect() { Pool.prototype.connect = function connect() {
var self = this;
assert(this.loaded, 'Pool is not loaded.'); assert(this.loaded, 'Pool is not loaded.');
if (this.connected) if (this.connected)
return; return;
if (this.mempool && this.options.broadcast) {
this.mempool.on('tx', function(tx) {
self.broadcast(tx);
});
}
if (this.originalSeeds.length > 0) { if (this.originalSeeds.length > 0) {
this._addLoader(); this._addLoader();

View File

@ -23,8 +23,6 @@ function SPVNode(options) {
this.loaded = false; this.loaded = false;
SPVNode.global = this;
this._init(); this._init();
} }
@ -36,24 +34,26 @@ SPVNode.prototype._init = function _init() {
this.wallet = null; this.wallet = null;
this.chain = new bcoin.chain(this, { this.chain = new bcoin.chain({
preload: this.options.preload, preload: this.options.preload,
spv: true, useCheckpoints: this.options.useCheckpoints,
useCheckpoints: this.options.useCheckpoints spv: true
}); });
// Pool needs access to the chain. this.pool = new bcoin.pool({
this.pool = new bcoin.pool(this, { chain: this.chain,
witness: this.network.witness, witness: this.network.witness,
selfish: true, selfish: true,
listen: false, listen: false,
spv: true spv: true
}); });
// WalletDB needs access to the network type. this.walletdb = new bcoin.walletdb({
this.walletdb = new bcoin.walletdb(this); verify: true
});
this.http = new bcoin.http.server(this, { this.http = new bcoin.http.server({
node: this,
key: this.options.sslKey, key: this.options.sslKey,
cert: this.options.sslCert, cert: this.options.sslCert,
port: this.options.httpPort || 8080, port: this.options.httpPort || 8080,

View File

@ -1621,146 +1621,6 @@ TXPool.prototype.getBalanceByAddress = function getBalanceByAddress(address, cal
return this.getBalance(address, callback); return this.getBalance(address, callback);
}; };
TXPool.prototype.addUnchecked = function addUnchecked(tx, callback, force) {
var self = this;
var prefix = this.prefix + '/';
var hash = tx.hash('hex');
var batch;
var unlock = this._lock(addUnchecked, [tx, callback], force);
if (!unlock)
return;
callback = utils.wrap(callback, unlock);
batch = this.db.batch();
batch.put(prefix + 't/t/' + hash, tx.toExtended());
batch.put(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash, DUMMY);
tx.getAddresses().forEach(function(address) {
batch.put(prefix + 't/a/' + address + '/' + hash, DUMMY);
});
tx.inputs.forEach(function(input) {
var key = input.prevout.hash + '/' + input.prevout.index;
var address;
if (tx.isCoinbase())
return;
assert(input.coin);
address = input.getAddress();
batch.del(prefix + 'u/t/' + key);
batch.put(prefix + 's/t/' + key, tx.hash());
if (address)
batch.del(prefix + 'u/a/' + address + '/' + key);
});
tx.outputs.forEach(function(output, i) {
var key = hash + '/' + i;
var address = output.getAddress();
var coin = bcoin.coin(tx, i).toRaw();
batch.put(prefix + 'u/t/' + key, coin);
if (address)
batch.put(prefix + 'u/a/' + address + '/' + key, DUMMY);
});
return batch.write(function(err) {
if (err)
return callback(err);
self.emit('add tx', tx);
return callback();
});
};
TXPool.prototype.removeUnchecked = function removeUnchecked(hash, callback, force) {
var self = this;
var prefix = this.prefix + '/';
var batch;
var unlock = this._lock(removeUnchecked, [hash, callback], force);
if (!unlock)
return;
callback = utils.wrap(callback, unlock);
if (hash.hash)
hash = hash.hash('hex');
this.getTX(hash, function(err, tx) {
if (err)
return callback(err);
if (!tx)
return callback();
batch = self.db.batch();
batch.del(prefix + 't/t/' + hash);
batch.del(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash);
tx.getAddresses().forEach(function(address) {
batch.del(prefix + 't/a/' + address + '/' + hash);
});
utils.forEachSerial(tx.inputs, function(input, next) {
var key = input.prevout.hash + '/' + input.prevout.index;
var address;
if (tx.isCoinbase())
return next();
if (!input.coin)
return next();
address = input.getAddress();
batch.del(prefix + 's/t/' + key);
self.hasTX(input.prevout.hash, function(err, result) {
if (err)
return next(err);
if (result) {
batch.put(prefix + 'u/t/' + key, input.coin.toRaw());
if (address)
batch.put(prefix + 'u/a/' + address + '/' + key, DUMMY);
} else {
batch.del(prefix + 'u/t/' + key);
if (address)
batch.del(prefix + 'u/a/' + address + '/' + key);
}
next();
});
}, function(err) {
if (err)
return callback(err);
tx.outputs.forEach(function(output, i) {
var key = hash + '/' + i;
var address = output.getAddress();
batch.del(prefix + 'u/t/' + key);
if (address)
batch.del(prefix + 'u/a/' + address + '/' + key);
});
batch.write(function(err) {
if (err)
return callback(err);
self.emit('remove tx', tx);
return callback();
});
});
});
};
TXPool.prototype.zap = function zap(address, now, age, callback, force) { TXPool.prototype.zap = function zap(address, now, age, callback, force) {
var self = this; var self = this;

View File

@ -15,31 +15,23 @@ var DUMMY = new Buffer([0]);
* WalletDB * WalletDB
*/ */
function WalletDB(node, options) { function WalletDB(options) {
if (!(this instanceof WalletDB)) if (!(this instanceof WalletDB))
return new WalletDB(node, options); return new WalletDB(options);
if (WalletDB.global)
return WalletDB.global;
if (!options) if (!options)
options = {}; options = {};
EventEmitter.call(this); EventEmitter.call(this);
this.node = node;
this.options = options; this.options = options;
this.loaded = false; this.loaded = false;
WalletDB.global = this;
this._init(); this._init();
} }
utils.inherits(WalletDB, EventEmitter); utils.inherits(WalletDB, EventEmitter);
WalletDB._db = {};
WalletDB.prototype.dump = function dump(callback) { WalletDB.prototype.dump = function dump(callback) {
var records = {}; var records = {};
@ -84,7 +76,9 @@ WalletDB.prototype._init = function _init() {
if (this.loaded) if (this.loaded)
return; return;
this.db = bcoin.ldb('wallet', { this.db = bcoin.ldb({
name: this.options.name || 'wallet',
location: this.options.location,
cacheSize: 8 << 20, cacheSize: 8 << 20,
writeBufferSize: 4 << 20 writeBufferSize: 4 << 20
}); });