map addresses to ids.

This commit is contained in:
Christopher Jeffrey 2016-03-03 13:53:11 -08:00
parent 434fdc7a78
commit ef55d1cebc
3 changed files with 250 additions and 119 deletions

View File

@ -37,7 +37,7 @@ function Chain(node, options) {
this.loading = false; this.loading = false;
this.mempool = node.mempool; this.mempool = node.mempool;
this.blockdb = node.blockdb; this.blockdb = node.blockdb;
this.db = new bcoin.chaindb(node, this); this.db = new bcoin.chaindb(node, this, options);
this.busy = false; this.busy = false;
this.jobs = []; this.jobs = [];
this.pending = []; this.pending = [];

View File

@ -35,8 +35,13 @@ function ChainDB(node, chain, options) {
this.chain = chain; this.chain = chain;
this.file = options.file; this.file = options.file;
if (!this.file) if (!this.file) {
this.file = bcoin.prefix + '/chain-' + network.type + '.db'; this.file = bcoin.prefix
+ '/chain-'
+ (options.spv ? 'spv-' : '')
+ network.type
+ '.db';
}
this.heightLookup = {}; this.heightLookup = {};
this.queue = {}; this.queue = {};

View File

@ -30,6 +30,7 @@ function TXPool(prefix, db, options) {
this.options = options; this.options = options;
this.busy = false; this.busy = false;
this.jobs = []; this.jobs = [];
this.ids = true;
if (options.addressFilter) if (options.addressFilter)
this._hasAddress = options.addressFilter; this._hasAddress = options.addressFilter;
@ -85,32 +86,111 @@ TXPool.prototype.add = function add(tx, callback) {
}, callback); }, callback);
} }
return self._add(tx, callback); //if (!this.ids)
return this._add(tx, this.__getIDs(tx), callback);
return self._getIDs(tx.getAddresses(), function(err, ids) {
if (err)
return callback(err);
return self._add(tx, ids, callback);
});
}; };
TXPool.prototype._hasAddress = function _hasAddress(address, callback) { // Map of address->id.
if (!address) TXPool.prototype.__getIDs = function _getIDs(tx, callback) {
var map = tx.getAddresses();
return map.reduce(function(out, addr) {
out[addr] = [addr];
return out;
}, {});
};
TXPool.prototype.__hasAddress = function __hasAddress(map, address, callback) {
if (!address || !map[address] || !map[address].length)
return callback(null, false); return callback(null, false);
return callback(null, true); return callback(null, true);
}; };
TXPool.prototype._getIDs = function getIDs(address, callback) {
var p = this.prefix + '/';
var self = this;
var map = {};
if (Array.isArray(address)) {
address = utils.uniqs(address);
return utils.forEachSerial(address, function(address, next) {
self._getIDs(address, function(err, m) {
if (err)
return next(err);
map[address] = m[address];
next();
});
}, function(err) {
if (err)
return callback(err);
return callback(null, map);
});
}
var iter = this.db.db.iterator({
gte: p + 'a/' + address,
lte: p + 'a/' + address + '~',
keys: true,
values: false,
fillCache: false,
keyAsBuffer: false
});
callback = utils.ensure(callback);
map[address] = [];
(function next() {
iter.next(function(err, key, value) {
if (err) {
return iter.end(function() {
callback(err);
});
}
if (key === undefined) {
return iter.end(function(err) {
if (err)
return callback(err);
map[address] = utils.uniqs(map[address]);
return callback(null, map);
});
}
map[address].push(key.split('/')[3]);
next();
});
})();
};
// This big scary function is what a persistent tx pool // This big scary function is what a persistent tx pool
// looks like. It's a semi mempool in that it can handle // looks like. It's a semi mempool in that it can handle
// receiving txs out of order. // receiving txs out of order.
TXPool.prototype._add = function add(tx, callback, force) { TXPool.prototype._add = function add(tx, map, callback, force) {
var self = this; var self = this;
var p = this.prefix + '/'; var p = this.prefix + '/';
var hash = tx.hash('hex'); var hash = tx.hash('hex');
var updated = false; var updated = false;
var own = false; var own = false;
var uniq = {};
var batch; var batch;
var unlock = this._lock(add, [tx, callback], force); // var unlock = this._lock(add, [tx, map, callback], force);
if (!unlock) // if (!unlock)
return; // return;
function done(err, result) { function done(err, result) {
unlock(); //unlock();
if (callback) if (callback)
callback(err, result); callback(err, result);
}; };
@ -138,20 +218,16 @@ TXPool.prototype._add = function add(tx, callback, force) {
if (input.isCoinbase()) if (input.isCoinbase())
return; return;
if (type === 'pubkey' || type === 'multisig') if (address && !uniq[address]) {
address = null; uniq[address] = true;
uaddr = address;
uaddr = address;
if (uaddr) {
if (!uniq[uaddr])
uniq[uaddr] = true;
else
uaddr = null;
} }
if (uaddr) if (uaddr) {
batch.del(p + 'p/a/' + uaddr + '/' + hash); map[uaddr].forEach(function(id) {
batch.del(p + 'p/a/' + id + '/' + hash);
});
}
}); });
utils.forEachSerial(tx.outputs, function(output, next, i) { utils.forEachSerial(tx.outputs, function(output, next, i) {
@ -159,20 +235,16 @@ TXPool.prototype._add = function add(tx, callback, force) {
var address = output.getAddress(); var address = output.getAddress();
var uaddr; var uaddr;
if (type === 'pubkey' || type === 'multisig') if (address && !uniq[address]) {
address = null; uniq[address] = true;
uaddr = address;
uaddr = address;
if (uaddr) {
if (!uniq[uaddr])
uniq[uaddr] = true;
else
uaddr = null;
} }
if (uaddr) if (uaddr) {
batch.del(p + 'p/a/' + uaddr + '/' + hash); map[uaddr].forEach(function(id) {
batch.del(p + 'p/a/' + id + '/' + hash);
});
}
self.getCoin(hash, i, function(err, coin) { self.getCoin(hash, i, function(err, coin) {
if (err) if (err)
@ -210,8 +282,10 @@ TXPool.prototype._add = function add(tx, callback, force) {
// Consume unspent money or add orphans // Consume unspent money or add orphans
utils.forEachSerial(tx.inputs, function(input, next, i) { utils.forEachSerial(tx.inputs, function(input, next, i) {
var key = input.prevout.hash + '/' + input.prevout.index; var key = input.prevout.hash + '/' + input.prevout.index;
if (input.isCoinbase())
return next();
self.getCoin(input.prevout.hash, input.prevout.index, function(err, coin) { self.getCoin(input.prevout.hash, input.prevout.index, function(err, coin) {
var type, address; var type, address, uaddr;
if (err) if (err)
return next(err); return next(err);
@ -233,17 +307,26 @@ TXPool.prototype._add = function add(tx, callback, force) {
type = input.getType(); type = input.getType();
address = input.getAddress(); address = input.getAddress();
if (type === 'pubkey' || type === 'multisig') if (address && !uniq[address]) {
address = null; uniq[address] = true;
uaddr = address;
if (input.isCoinbase()) }
return next();
if (address) { if (address) {
batch.del( map[address].forEach(function(id) {
p + 'u/a/' + address batch.del(
+ '/' + input.prevout.hash p + 'u/a/' + id
+ '/' + input.prevout.index); + '/' + input.prevout.hash
+ '/' + input.prevout.index);
});
}
if (uaddr) {
map[uaddr].forEach(function(id) {
batch.put(p + 't/a/' + id + '/' + hash, new Buffer([]));
if (tx.ts === 0)
batch.put(p + 'p/a/' + id + '/' + hash, new Buffer([]));
});
} }
batch.del( batch.del(
@ -255,7 +338,7 @@ TXPool.prototype._add = function add(tx, callback, force) {
} }
// Only add orphans if this input is ours. // Only add orphans if this input is ours.
self._hasAddress(input.getAddress(), function(err, result) { self.__hasAddress(map, input.getAddress(), function(err, result) {
if (err) if (err)
return done(err); return done(err);
@ -308,7 +391,7 @@ TXPool.prototype._add = function add(tx, callback, force) {
// Add unspent outputs or resolve orphans // Add unspent outputs or resolve orphans
utils.forEachSerial(tx.outputs, function(output, next, i) { utils.forEachSerial(tx.outputs, function(output, next, i) {
// Do not add unspents for outputs that aren't ours. // Do not add unspents for outputs that aren't ours.
self._hasAddress(output.getAddress(), function(err, result) { self.__hasAddress(map, output.getAddress(), function(err, result) {
var key, coin; var key, coin;
if (err) if (err)
@ -382,7 +465,7 @@ TXPool.prototype._add = function add(tx, callback, force) {
} }
function finish(err) { function finish(err) {
var type, adddress; var type, adddress, uaddr;
if (err) if (err)
return next(err); return next(err);
@ -391,14 +474,26 @@ TXPool.prototype._add = function add(tx, callback, force) {
type = output.getType(); type = output.getType();
address = output.getAddress(); address = output.getAddress();
if (type === 'pubkey' || type === 'multisig') if (address && !uniq[address]) {
address = null; uniq[address] = true;
uaddr = address;
}
if (address) { if (address) {
batch.put( map[address].forEach(function(id) {
p + 'u/a/' + address batch.put(
+ '/' + hash + '/' + i, p + 'u/a/' + id
new Buffer([])); + '/' + hash + '/' + i,
new Buffer([]));
});
}
if (uaddr) {
map[uaddr].forEach(function(id) {
batch.put(p + 't/a/' + id + '/' + hash, new Buffer([]));
if (tx.ts === 0)
batch.put(p + 'p/a/' + id + '/' + hash, new Buffer([]));
});
} }
batch.put(p + 'u/t/' + hash + '/' + i, coin.toRaw()); batch.put(p + 'u/t/' + hash + '/' + i, coin.toRaw());
@ -424,23 +519,17 @@ TXPool.prototype._add = function add(tx, callback, force) {
else else
batch.put(p + 't/h/' + tx.height + '/' + hash, new Buffer([])); batch.put(p + 't/h/' + tx.height + '/' + hash, new Buffer([]));
tx.getAddresses().forEach(function(address) {
batch.put(p + 't/a/' + address + '/' + hash, new Buffer([]));
if (tx.ts === 0)
batch.put(p + 'p/a/' + address + '/' + hash, new Buffer([]));
});
batch.write(function(err) { batch.write(function(err) {
if (err) if (err)
return done(err); return done(err);
self.emit('tx', tx); self.emit('tx', tx, map);
if (updated) { if (updated) {
if (tx.ts !== 0) if (tx.ts !== 0)
self.emit('confirmed', tx); self.emit('confirmed', tx, map);
self.emit('updated', tx); self.emit('updated', tx, map);
} }
return done(null, true); return done(null, true);
@ -452,12 +541,37 @@ TXPool.prototype._add = function add(tx, callback, force) {
TXPool.prototype.remove = function remove(hash, callback) { TXPool.prototype.remove = function remove(hash, callback) {
var self = this; var self = this;
if (Array.isArray(hash)) {
return utils.forEachSerial(hash, function(hash, next) {
self.remove(hash, next);
}, callback);
}
if (hash.hash)
hash = hash.hash('hex');
this.getTX(hash, function(err, tx) {
if (err)
return callback(err);
return self._getIDs(tx.getAddresses(), function(err, map) {
if (err)
return callback(err);
return self._remove(tx, map, callback);
});
});
};
TXPool.prototype._remove = function remove(hash, map, callback) {
var self = this;
var p = this.prefix + '/'; var p = this.prefix + '/';
var uniq = {}; var uniq = {};
if (hash.hash) if (hash.hash)
hash = hash.hash('hex'); hash = hash.hash('hex');
// XXX Remove this
this.getTX(hash, function(err, tx) { this.getTX(hash, function(err, tx) {
var batch; var batch;
@ -489,29 +603,26 @@ TXPool.prototype.remove = function remove(hash, callback) {
if (input.isCoinbase()) if (input.isCoinbase())
return; return;
if (type === 'pubkey' || type === 'multisig') if (address && !uniq[address]) {
address = null; uniq[address] = true;
uaddr = address;
uaddr = address;
if (uaddr) {
if (!uniq[uaddr])
uniq[uaddr] = true;
else
uaddr = null;
} }
if (uaddr) { if (uaddr) {
batch.del(p + 't/a/' + uaddr + '/' + hash); map[uaddr].forEach(function(id) {
if (tx.ts === 0) batch.del(p + 't/a/' + id + '/' + hash);
batch.del(p + 'p/a/' + uaddr + '/' + hash); if (tx.ts === 0)
batch.del(p + 'p/a/' + id + '/' + hash);
});
} }
if (address) { if (address) {
batch.put(p + 'u/a/' + address map[address].forEach(function(id) {
+ '/' + input.prevout.hash batch.put(p + 'u/a/' + id
+ '/' + input.prevout.index, + '/' + input.prevout.hash
new Buffer([])); + '/' + input.prevout.index,
new Buffer([]));
});
} }
if (input.output) { if (input.output) {
@ -529,26 +640,24 @@ TXPool.prototype.remove = function remove(hash, callback) {
var address = output.getAddress(); var address = output.getAddress();
var uaddr; var uaddr;
if (type === 'pubkey' || type === 'multisig') if (address && !uniq[address]) {
address = null; uniq[address] = true;
uaddr = address;
uaddr = address;
if (uaddr) {
if (!uniq[uaddr])
uniq[uaddr] = true;
else
uaddr = null;
} }
if (uaddr) { if (uaddr) {
batch.del(p + 't/a/' + uaddr + '/' + hash); map[uaddr].forEach(function(id) {
if (tx.ts === 0) batch.del(p + 't/a/' + id + '/' + hash);
batch.del(p + 'p/a/' + uaddr + '/' + hash); if (tx.ts === 0)
batch.del(p + 'p/a/' + id + '/' + hash);
});
} }
if (address) if (address) {
batch.del(p + 'u/a/' + address + '/' + hash + '/' + i); map[address].forEach(function(id) {
batch.del(p + 'u/a/' + id + '/' + hash + '/' + i);
});
}
batch.del(p + 'u/t/' + hash + '/' + i); batch.del(p + 'u/t/' + hash + '/' + i);
}); });
@ -566,12 +675,37 @@ TXPool.prototype.remove = function remove(hash, callback) {
TXPool.prototype.unconfirm = function unconfirm(hash, callback) { TXPool.prototype.unconfirm = function unconfirm(hash, callback) {
var self = this; var self = this;
if (Array.isArray(hash)) {
return utils.forEachSerial(hash, function(hash, next) {
self.unconfirm(hash, next);
}, callback);
}
if (hash.hash)
hash = hash.hash('hex');
this.getTX(hash, function(err, tx) {
if (err)
return callback(err);
return self._getIDs(tx.getAddresses(), function(err, map) {
if (err)
return callback(err);
return self._unconfirm(tx, map, callback);
});
});
};
TXPool.prototype._unconfirm = function unconfirm(hash, map, callback) {
var self = this;
var p = this.prefix + '/'; var p = this.prefix + '/';
var uniq = {}; var uniq = {};
if (hash.hash) if (hash.hash)
hash = hash.hash('hex'); hash = hash.hash('hex');
// XXX Remove this.
this.getTX(hash, function(err, tx) { this.getTX(hash, function(err, tx) {
var batch, height; var batch, height;
@ -600,20 +734,16 @@ TXPool.prototype.unconfirm = function unconfirm(hash, callback) {
if (input.isCoinbase()) if (input.isCoinbase())
return; return;
if (type === 'pubkey' || type === 'multisig') if (address && !uniq[address]) {
address = null; uniq[address] = true;
uaddr = address;
uaddr = address;
if (uaddr) {
if (!uniq[uaddr])
uniq[uaddr] = true;
else
uaddr = null;
} }
if (uaddr) if (uaddr) {
batch.put(p + 'p/a/' + uaddr + '/' + hash, new Buffer([])); map[uaddr].forEach(function(id) {
batch.put(p + 'p/a/' + id + '/' + hash, new Buffer([]));
});
}
}); });
utils.forEachSerial(tx.outputs, function(output, next, i) { utils.forEachSerial(tx.outputs, function(output, next, i) {
@ -621,20 +751,16 @@ TXPool.prototype.unconfirm = function unconfirm(hash, callback) {
var address = output.getAddress(); var address = output.getAddress();
var uaddr; var uaddr;
if (type === 'pubkey' || type === 'multisig') if (address && !uniq[address]) {
address = null; uniq[address] = true;
uaddr = address;
uaddr = address;
if (uaddr) {
if (!uniq[uaddr])
uniq[uaddr] = true;
else
uaddr = null;
} }
if (uaddr) if (uaddr) {
batch.put(p + 'p/a/' + uaddr + '/' + hash, new Buffer([])); map[uaddr].forEach(function(id) {
batch.put(p + 'p/a/' + id + '/' + hash, new Buffer([]));
});
}
self.getCoin(hash, i, function(err, coin) { self.getCoin(hash, i, function(err, coin) {
if (err) if (err)