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.mempool = node.mempool;
this.blockdb = node.blockdb;
this.db = new bcoin.chaindb(node, this);
this.db = new bcoin.chaindb(node, this, options);
this.busy = false;
this.jobs = [];
this.pending = [];

View File

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

View File

@ -30,6 +30,7 @@ function TXPool(prefix, db, options) {
this.options = options;
this.busy = false;
this.jobs = [];
this.ids = true;
if (options.addressFilter)
this._hasAddress = options.addressFilter;
@ -85,32 +86,111 @@ TXPool.prototype.add = function add(tx, 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) {
if (!address)
// Map of address->id.
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, 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
// looks like. It's a semi mempool in that it can handle
// 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 p = this.prefix + '/';
var hash = tx.hash('hex');
var updated = false;
var own = false;
var uniq = {};
var batch;
var unlock = this._lock(add, [tx, callback], force);
if (!unlock)
return;
// var unlock = this._lock(add, [tx, map, callback], force);
// if (!unlock)
// return;
function done(err, result) {
unlock();
//unlock();
if (callback)
callback(err, result);
};
@ -138,20 +218,16 @@ TXPool.prototype._add = function add(tx, callback, force) {
if (input.isCoinbase())
return;
if (type === 'pubkey' || type === 'multisig')
address = null;
uaddr = address;
if (uaddr) {
if (!uniq[uaddr])
uniq[uaddr] = true;
else
uaddr = null;
if (address && !uniq[address]) {
uniq[address] = true;
uaddr = address;
}
if (uaddr)
batch.del(p + 'p/a/' + uaddr + '/' + hash);
if (uaddr) {
map[uaddr].forEach(function(id) {
batch.del(p + 'p/a/' + id + '/' + hash);
});
}
});
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 uaddr;
if (type === 'pubkey' || type === 'multisig')
address = null;
uaddr = address;
if (uaddr) {
if (!uniq[uaddr])
uniq[uaddr] = true;
else
uaddr = null;
if (address && !uniq[address]) {
uniq[address] = true;
uaddr = address;
}
if (uaddr)
batch.del(p + 'p/a/' + uaddr + '/' + hash);
if (uaddr) {
map[uaddr].forEach(function(id) {
batch.del(p + 'p/a/' + id + '/' + hash);
});
}
self.getCoin(hash, i, function(err, coin) {
if (err)
@ -210,8 +282,10 @@ TXPool.prototype._add = function add(tx, callback, force) {
// Consume unspent money or add orphans
utils.forEachSerial(tx.inputs, function(input, next, i) {
var key = input.prevout.hash + '/' + input.prevout.index;
if (input.isCoinbase())
return next();
self.getCoin(input.prevout.hash, input.prevout.index, function(err, coin) {
var type, address;
var type, address, uaddr;
if (err)
return next(err);
@ -233,17 +307,26 @@ TXPool.prototype._add = function add(tx, callback, force) {
type = input.getType();
address = input.getAddress();
if (type === 'pubkey' || type === 'multisig')
address = null;
if (input.isCoinbase())
return next();
if (address && !uniq[address]) {
uniq[address] = true;
uaddr = address;
}
if (address) {
batch.del(
p + 'u/a/' + address
+ '/' + input.prevout.hash
+ '/' + input.prevout.index);
map[address].forEach(function(id) {
batch.del(
p + 'u/a/' + id
+ '/' + 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(
@ -255,7 +338,7 @@ TXPool.prototype._add = function add(tx, callback, force) {
}
// 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)
return done(err);
@ -308,7 +391,7 @@ TXPool.prototype._add = function add(tx, callback, force) {
// Add unspent outputs or resolve orphans
utils.forEachSerial(tx.outputs, function(output, next, i) {
// 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;
if (err)
@ -382,7 +465,7 @@ TXPool.prototype._add = function add(tx, callback, force) {
}
function finish(err) {
var type, adddress;
var type, adddress, uaddr;
if (err)
return next(err);
@ -391,14 +474,26 @@ TXPool.prototype._add = function add(tx, callback, force) {
type = output.getType();
address = output.getAddress();
if (type === 'pubkey' || type === 'multisig')
address = null;
if (address && !uniq[address]) {
uniq[address] = true;
uaddr = address;
}
if (address) {
batch.put(
p + 'u/a/' + address
+ '/' + hash + '/' + i,
new Buffer([]));
map[address].forEach(function(id) {
batch.put(
p + 'u/a/' + id
+ '/' + 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());
@ -424,23 +519,17 @@ TXPool.prototype._add = function add(tx, callback, force) {
else
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) {
if (err)
return done(err);
self.emit('tx', tx);
self.emit('tx', tx, map);
if (updated) {
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);
@ -452,12 +541,37 @@ TXPool.prototype._add = function add(tx, callback, force) {
TXPool.prototype.remove = function remove(hash, callback) {
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 uniq = {};
if (hash.hash)
hash = hash.hash('hex');
// XXX Remove this
this.getTX(hash, function(err, tx) {
var batch;
@ -489,29 +603,26 @@ TXPool.prototype.remove = function remove(hash, callback) {
if (input.isCoinbase())
return;
if (type === 'pubkey' || type === 'multisig')
address = null;
uaddr = address;
if (uaddr) {
if (!uniq[uaddr])
uniq[uaddr] = true;
else
uaddr = null;
if (address && !uniq[address]) {
uniq[address] = true;
uaddr = address;
}
if (uaddr) {
batch.del(p + 't/a/' + uaddr + '/' + hash);
if (tx.ts === 0)
batch.del(p + 'p/a/' + uaddr + '/' + hash);
map[uaddr].forEach(function(id) {
batch.del(p + 't/a/' + id + '/' + hash);
if (tx.ts === 0)
batch.del(p + 'p/a/' + id + '/' + hash);
});
}
if (address) {
batch.put(p + 'u/a/' + address
+ '/' + input.prevout.hash
+ '/' + input.prevout.index,
new Buffer([]));
map[address].forEach(function(id) {
batch.put(p + 'u/a/' + id
+ '/' + input.prevout.hash
+ '/' + input.prevout.index,
new Buffer([]));
});
}
if (input.output) {
@ -529,26 +640,24 @@ TXPool.prototype.remove = function remove(hash, callback) {
var address = output.getAddress();
var uaddr;
if (type === 'pubkey' || type === 'multisig')
address = null;
uaddr = address;
if (uaddr) {
if (!uniq[uaddr])
uniq[uaddr] = true;
else
uaddr = null;
if (address && !uniq[address]) {
uniq[address] = true;
uaddr = address;
}
if (uaddr) {
batch.del(p + 't/a/' + uaddr + '/' + hash);
if (tx.ts === 0)
batch.del(p + 'p/a/' + uaddr + '/' + hash);
map[uaddr].forEach(function(id) {
batch.del(p + 't/a/' + id + '/' + hash);
if (tx.ts === 0)
batch.del(p + 'p/a/' + id + '/' + hash);
});
}
if (address)
batch.del(p + 'u/a/' + address + '/' + hash + '/' + i);
if (address) {
map[address].forEach(function(id) {
batch.del(p + 'u/a/' + id + '/' + 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) {
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 uniq = {};
if (hash.hash)
hash = hash.hash('hex');
// XXX Remove this.
this.getTX(hash, function(err, tx) {
var batch, height;
@ -600,20 +734,16 @@ TXPool.prototype.unconfirm = function unconfirm(hash, callback) {
if (input.isCoinbase())
return;
if (type === 'pubkey' || type === 'multisig')
address = null;
uaddr = address;
if (uaddr) {
if (!uniq[uaddr])
uniq[uaddr] = true;
else
uaddr = null;
if (address && !uniq[address]) {
uniq[address] = true;
uaddr = address;
}
if (uaddr)
batch.put(p + 'p/a/' + uaddr + '/' + hash, new Buffer([]));
if (uaddr) {
map[uaddr].forEach(function(id) {
batch.put(p + 'p/a/' + id + '/' + hash, new Buffer([]));
});
}
});
utils.forEachSerial(tx.outputs, function(output, next, i) {
@ -621,20 +751,16 @@ TXPool.prototype.unconfirm = function unconfirm(hash, callback) {
var address = output.getAddress();
var uaddr;
if (type === 'pubkey' || type === 'multisig')
address = null;
uaddr = address;
if (uaddr) {
if (!uniq[uaddr])
uniq[uaddr] = true;
else
uaddr = null;
if (address && !uniq[address]) {
uniq[address] = true;
uaddr = address;
}
if (uaddr)
batch.put(p + 'p/a/' + uaddr + '/' + hash, new Buffer([]));
if (uaddr) {
map[uaddr].forEach(function(id) {
batch.put(p + 'p/a/' + id + '/' + hash, new Buffer([]));
});
}
self.getCoin(hash, i, function(err, coin) {
if (err)