mempool work.

This commit is contained in:
Christopher Jeffrey 2016-03-31 20:25:25 -07:00
parent c30d19d3e0
commit 46bbde9af7

View File

@ -97,10 +97,6 @@ Mempool.prototype._init = function _init() {
}); });
}; };
Mempool.prototype.dynamicMemoryUsage = function dynamicMemoryUsage(callback) {
return utils.asyncify(callback)(null, this.totalSize);
};
Mempool.prototype.open = function open(callback) { Mempool.prototype.open = function open(callback) {
if (this.loaded) if (this.loaded)
return utils.nextTick(callback); return utils.nextTick(callback);
@ -168,25 +164,50 @@ Mempool.prototype.limitMempoolSize = function limitMempoolSize(callback) {
}); });
}; };
Mempool.prototype.getRange = function getRange(options, callback) { Mempool.prototype.getRangeSync = function getRangeSync(options) {
return callback(null, []); return [];
}; };
Mempool.prototype.purgeOrphans = function purgeOrphans(callback) { Mempool.prototype.getRange = function getRange(options, callback) {
var self = this; var ret;
var batch = this.db.batch();
callback = utils.ensure(callback); callback = utils.asyncify(callback);
try {
ret = this.getRangeSync(options);
} catch (e) {
return callback(e);
}
return callback(null, ret);
};
Mempool.prototype.purgeOrphansSync = function purgeOrphansSync() {
var keys = Object.keys(this.orphans);
var key, i;
this.waiting = {}; this.waiting = {};
this.totalOrphans = 0; this.totalOrphans = 0;
Object.keys(this.orphans).forEach(function(key) { for (i = 0; i < keys.length; i++) {
self.totalSize -= self.orphans[key].length; key = keys[i];
delete self.orphans[key]; this.totalSize -= this.orphans[key].length;
}); delete this.orphans[key];
}
};
return utils.nextTick(callback); Mempool.prototype.purgeOrphans = function purgeOrphans(callback) {
var ret;
callback = utils.asyncify(callback);
try {
ret = this.purgeOrphansSync();
} catch (e) {
return callback(e);
}
return callback(null, ret);
}; };
Mempool.prototype.getTXSync = function getTXSync(hash) { Mempool.prototype.getTXSync = function getTXSync(hash) {
@ -239,23 +260,31 @@ Mempool.prototype.isDoubleSpendSync = function isDoubleSpendSync(tx) {
Mempool.prototype.get = Mempool.prototype.get =
Mempool.prototype.getTX = function getTX(hash, callback) { Mempool.prototype.getTX = function getTX(hash, callback) {
var tx;
callback = utils.asyncify(callback); callback = utils.asyncify(callback);
try { try {
return callback(null, this.getTXSync(hash)); tx = this.getTXSync(hash);
} catch (e) { } catch (e) {
return callback(e); return callback(e);
} }
return callback(null, tx);
}; };
Mempool.prototype.getCoin = function getCoin(hash, index, callback) { Mempool.prototype.getCoin = function getCoin(hash, index, callback) {
var coin;
callback = utils.asyncify(callback); callback = utils.asyncify(callback);
try { try {
return callback(null, this.getCoinSync(hash, index)); coin = this.getCoinSync(hash, index);
} catch (e) { } catch (e) {
return callback(e); return callback(e);
} }
return callback(null, coin);
}; };
Mempool.prototype.isSpent = function isSpent(hash, index, callback) { Mempool.prototype.isSpent = function isSpent(hash, index, callback) {
@ -269,13 +298,11 @@ Mempool.prototype.isDoubleSpend = function isDoubleSpend(tx, callback) {
return callback(null, this.isDoubleSpendSync(tx)); return callback(null, this.isDoubleSpendSync(tx));
}; };
Mempool.prototype.getCoinsByAddress = function getCoinsByAddress(addresses, callback) { Mempool.prototype.getCoinsByAddressSync = function getCoinsByAddressSync(addresses) {
var uniq = {}; var uniq = {};
var coins = []; var coins = [];
var i, j, address, keys, key, coin, parts, hash, index; var i, j, address, keys, key, coin, parts, hash, index;
callback = utils.asyncify(callback);
if (!Array.isArray(addresses)) if (!Array.isArray(addresses))
addresses = [addresses]; addresses = [addresses];
@ -283,37 +310,40 @@ Mempool.prototype.getCoinsByAddress = function getCoinsByAddress(addresses, call
for (i = 0; i < addresses.length; i++) { for (i = 0; i < addresses.length; i++) {
address = addresses[i]; address = addresses[i];
keys = this.addressMap.getKeys(address); keys = this.addressMap.getCoins(address);
for (j = 0; j < keys.length; j++) { for (j = 0; j < keys.length; j++) {
key = keys[j]; key = keys[j];
parts = key.split('/'); parts = key.split('/');
hash = parts[0]; hash = parts[0];
index = +parts[1]; index = +parts[1];
coin = this.getCoinSync(hash, index);
try { if (coin)
coin = this.getCoinSync(hash, index); coins.push(coin);
} catch (e) {
return callback(e);
}
if (!coin)
continue;
coins.push(coin);
} }
} }
return coins;
};
Mempool.prototype.getCoinsByAddress = function getCoinsByAddress(addresses, callback) {
var coins;
callback = utils.asyncify(callback);
try {
coins = this.getCoinsByAddressSync(addresses);
} catch (e) {
return callback(e);
}
return callback(null, coins); return callback(null, coins);
}; };
Mempool.prototype.getByAddress = Mempool.prototype.getTXByAddressSync = function getTXByAddressSync(addresses, callback) {
Mempool.prototype.getTXByAddress = function getTXByAddress(addresses, callback) {
var uniq = {}; var uniq = {};
var txs = []; var txs = [];
var i, j, address, hashes, hash, tx; var i, j, address, hashes, hash, tx;
callback = utils.asyncify(callback);
if (!Array.isArray(addresses)) if (!Array.isArray(addresses))
addresses = [addresses]; addresses = [addresses];
@ -321,18 +351,14 @@ Mempool.prototype.getTXByAddress = function getTXByAddress(addresses, callback)
for (i = 0; i < addresses.length; i++) { for (i = 0; i < addresses.length; i++) {
address = addresses[i]; address = addresses[i];
hashes = this.addressMap.getKeys(address); hashes = this.addressMap.getTX(address);
for (j = 0; j < hashes.length; j++) { for (j = 0; j < hashes.length; j++) {
hash = hashes[j]; hash = hashes[j];
if (uniq[hash]) if (uniq[hash])
continue; continue;
try { tx = this.getTXSync(hash);
tx = this.getTXSync(hash);
} catch (e) {
return callback(e);
}
if (!tx) if (!tx)
continue; continue;
@ -343,16 +369,29 @@ Mempool.prototype.getTXByAddress = function getTXByAddress(addresses, callback)
} }
} }
return callback(null, txs); return txs;
}; };
Mempool.prototype.fillTX = function fillTX(tx, callback) { Mempool.prototype.getByAddress =
var i, input, tx; Mempool.prototype.getTXByAddress = function getTXByAddress(addresses, callback) {
var txs;
callback = utils.asyncify(callback); callback = utils.asyncify(callback);
try {
txs = this.getTXByAddressSync(addresses);
} catch (e) {
return callback(e);
}
return callback(null, txs);
};
Mempool.prototype.fillTXSync = function fillTXSync(tx) {
var i, input, tx;
if (tx.isCoinbase()) if (tx.isCoinbase())
return callback(null, tx); return tx;
for (i = 0; i < tx.inputs.length; i++) { for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i]; input = tx.inputs[i];
@ -360,11 +399,7 @@ Mempool.prototype.fillTX = function fillTX(tx, callback) {
if (input.coin) if (input.coin)
continue; continue;
try { tx = this.getTXSync(input.prevout.hash);
tx = this.getTXSync(input.prevout.hash);
} catch (e) {
return callback(e);
}
if (!tx) if (!tx)
continue; continue;
@ -372,16 +407,26 @@ Mempool.prototype.fillTX = function fillTX(tx, callback) {
input.coin = bcoin.coin(tx, input.prevout.index); input.coin = bcoin.coin(tx, input.prevout.index);
} }
return tx;
};
Mempool.prototype.fillTX = function fillTX(tx, callback) {
callback = utils.asyncify(callback);
try {
tx = this.fillTXSync(tx);
} catch (e) {
return callback(e);
}
return callback(null, tx); return callback(null, tx);
}; };
Mempool.prototype.fillCoins = function fillCoins(tx, callback) { Mempool.prototype.fillCoinsSync = function fillCoinsSync(tx) {
var input; var i, input;
callback = utils.asyncify(callback);
if (tx.isCoinbase()) if (tx.isCoinbase())
return callback(null, tx); return tx;
for (i = 0; i < tx.inputs.length; i++) { for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i]; input = tx.inputs[i];
@ -396,17 +441,32 @@ Mempool.prototype.fillCoins = function fillCoins(tx, callback) {
} }
} }
return tx;
};
Mempool.prototype.fillCoins = function fillCoins(tx, callback) {
callback = utils.asyncify(callback);
try {
tx = this.fillCoinsSync(tx);
} catch (e) {
return callback(e);
}
return callback(null, tx); return callback(null, tx);
}; };
Mempool.prototype.hasTXSync = function hasTXSync(hash) {
if (hash instanceof bcoin.tx)
hash = hash.hash('hex');
return this.txs[hash] != null;
};
Mempool.prototype.has = Mempool.prototype.has =
Mempool.prototype.hasTX = function hasTX(hash, callback) { Mempool.prototype.hasTX = function hasTX(hash, callback) {
callback = utils.asyncify(callback); callback = utils.asyncify(callback);
return callback(null, this.hasTXSync(hash));
if (hash instanceof bcoin.tx)
hash = hash.hash('hex');
return callback(null, !!this.txs[hash]);
}; };
Mempool.prototype.add = Mempool.prototype.add =
@ -459,7 +519,7 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) {
return callback(new VerifyError(tx, ret.reason, 0)); return callback(new VerifyError(tx, ret.reason, 0));
} }
self._hasTX(tx, function(err, exists) { self.seenTX(tx, function(err, exists) {
if (err) if (err)
return callback(err); return callback(err);
@ -524,21 +584,67 @@ function AddressMap() {
this.map = {}; this.map = {};
} }
AddressMap.prototype.getKeys = function getKeys(address) { AddressMap.prototype.getTX = function getTX(address) {
return this.map[address] || []; var map = this.map[address];
var keys = [];
var i, key;
if (!map)
return keys;
for (i = 0; i < map.length; i++) {
key = map[i];
if (key.length !== 32)
continue;
keys.push(key.toString('hex'));
}
return keys;
};
AddressMap.prototype.getCoins = function getCoins(address) {
var map = this.map[address];
var keys = [];
var i, p, key;
if (!map)
return keys;
for (i = 0; i < map.length; i++) {
key = map[i];
if (key.length !== 36)
continue;
p = new BufferReader(key);
p.start();
try {
key = p.readHash('hex') + '/' + p.readU32();
} catch (e) {
continue;
}
p.end();
keys.push(key);
}
return keys;
}; };
AddressMap.prototype.addTX = function addTX(tx) { AddressMap.prototype.addTX = function addTX(tx) {
var hash = tx.hash('hex'); var hash = tx.hash();
var addresses = tx.getAddresses(); var addresses = tx.getAddresses();
var address; var address, i, map, index;
var i;
for (i = 0; i < addresses.length; i++) { for (i = 0; i < addresses.length; i++) {
address = addresses[i]; address = addresses[i];
if (!this.map[address]) if (!this.map[address])
this.map[address] = []; this.map[address] = [];
this.map[address].push(hash); map = this.map[address];
index = binarySearch(map, hash, true);
map.splice(index + 1, 0, hash);
} }
}; };
@ -564,24 +670,24 @@ function binarySearch(items, key, insert) {
return -1; return -1;
if (start === 0) if (start === 0)
return 0; return -1;
return start - 1; return start - 1;
} }
AddressMap.prototype.removeTX = function removeTX(tx) { AddressMap.prototype.removeTX = function removeTX(tx) {
var hash = tx.hash();
var addresses = tx.getAddresses(); var addresses = tx.getAddresses();
var address; var address, map, index;
var map, i;
for (i = 0; i < addresses.length; i++) { for (i = 0; i < addresses.length; i++) {
address = addresses[i]; address = addresses[i];
map = this.map[address]; map = this.map[address];
if (map) { if (map) {
i = map.indexOf(hash); index = binarySearch(map, hash);
if (i !== -1) if (index !== -1)
map.splice(i , 1); map.splice(index, 1);
if (map.length === 0) if (map.length === 0)
delete this.map[address]; delete this.map[address];
} }
@ -590,32 +696,42 @@ AddressMap.prototype.removeTX = function removeTX(tx) {
AddressMap.prototype.addCoin = function addCoin(coin) { AddressMap.prototype.addCoin = function addCoin(coin) {
var address = coin.getAddress(); var address = coin.getAddress();
var key = coin.hash + '/' + coin.index; var key = this._coinKey(coin.hash, coin.index);
var map, index;
if (address) { if (address) {
if (!this.map[address]) if (!this.map[address])
this.map[address] = []; this.map[address] = [];
this.map[address].push(key); map = this.map[address];
index = binarySearch(map, key, true);
map.splice(index + 1, 0, key);
} }
}; };
AddressMap.prototype._coinKey = function _coinKey(hash, index) {
var p = new BufferWriter();
p.writeHash(hash);
p.writeU32(index);
return p.render();
};
AddressMap.prototype.removeCoin = function removeCoin(tx, i) { AddressMap.prototype.removeCoin = function removeCoin(tx, i) {
var address, key, map, i; var address, key, map, index;
if (tx instanceof bcoin.input) { if (tx instanceof bcoin.input) {
address = tx.getAddress(); address = tx.getAddress();
key = tx.prevout.hash + '/' + tx.prevout.index; key = this._coinKey(tx.prevout.hash, tx.prevout.index);
} else { } else {
address = tx.outputs[i].getAddress(); address = tx.outputs[i].getAddress();
key = tx.hash('hex') + '/' + i; key = this._coinKey(tx.hash(), i);
} }
map = this.map[address]; map = this.map[address];
if (map) { if (map) {
i = map.indexOf(key); index = binarySearch(map, key);
if (i !== -1) if (index !== -1)
map.splice(i, 1); map.splice(index, 1);
if (map.length === 0) if (map.length === 0)
delete this.map[address]; delete this.map[address];
} }
@ -624,35 +740,38 @@ AddressMap.prototype.removeCoin = function removeCoin(tx, i) {
Mempool.prototype.addUnchecked = function addUnchecked(tx, callback) { Mempool.prototype.addUnchecked = function addUnchecked(tx, callback) {
var self = this; var self = this;
var hash = tx.hash('hex'); var hash = tx.hash('hex');
var input, output, i, key, coin;
this.txs[hash] = tx.toExtended(); this.txs[hash] = tx.toExtended();
this.addressMap.addTX(tx); this.addressMap.addTX(tx);
tx.inputs.forEach(function(input, i) { for (i = 0; i < tx.inputs.length; i++) {
var key = input.prevout.hash + '/' + input.prevout.index; input = tx.inputs[i];
delete self.coins[key]; key = input.prevout.hash + '/' + input.prevout.index;
self.spent[key] = hash; delete this.coins[key];
self.addressMap.removeCoin(input); this.spent[key] = hash;
}); this.addressMap.removeCoin(input);
}
tx.outputs.forEach(function(output, i) { for (i = 0; i < tx.outputs.length; i++) {
var coin = bcoin.coin(tx, i); output = tx.outputs[i];
var key = coin.hash + '/' + coin.index; coin = bcoin.coin(tx, i);
self.coins[key] = coin.toRaw(); key = coin.hash + '/' + coin.index;
self.addressMap.addCoin(coin); this.coins[key] = coin.toRaw();
}); this.addressMap.addCoin(coin);
}
self.totalSize += tx.getSize(); this.totalSize += tx.getSize();
self.emit('tx', tx); this.emit('tx', tx);
self.emit('add tx', tx); this.emit('add tx', tx);
utils.debug('Added tx %s to the mempool.', tx.rhash); utils.debug('Added tx %s to the mempool.', tx.rhash);
if (self.options.relay) if (this.options.relay)
self.node.broadcast(tx); this.node.broadcast(tx);
self.resolveOrphans(tx, function(err, resolved) { this.resolveOrphans(tx, function(err, resolved) {
if (err) if (err)
return callback(err); return callback(err);
@ -669,7 +788,7 @@ Mempool.prototype.addUnchecked = function addUnchecked(tx, callback) {
Mempool.prototype.removeUnchecked = function removeUnchecked(tx, callback) { Mempool.prototype.removeUnchecked = function removeUnchecked(tx, callback) {
var self = this; var self = this;
var hash; var hash, input, output, i, key, coin;
callback = utils.asyncify(callback); callback = utils.asyncify(callback);
@ -689,24 +808,24 @@ Mempool.prototype.removeUnchecked = function removeUnchecked(tx, callback) {
this.addressMap.removeTX(tx); this.addressMap.removeTX(tx);
tx.inputs.forEach(function(input, i) { for (i = 0; i < tx.inputs.length; i++) {
var key = input.prevout.hash + '/' + input.prevout.index; inputs = tx.outputs[i];
delete self.coins[key]; key = input.prevout.hash + '/' + input.prevout.index;
delete self.spent[key]; delete this.coins[key];
self.addressMap.removeCoin(input); delete this.spent[key];
}); this.addressMap.removeCoin(input);
}
tx.outputs.forEach(function(output, i) { for (i = 0; i < tx.outputs.length; i++) {
var address = output.getAddress(); output = tx.outputs[i];
var key = hash + '/' + i; key = hash + '/' + i;
delete this.coins[key];
delete this.spent[key];
this.addressMap.removeCoin(tx, i);
}
delete self.coins[key]; this.totalSize -= tx.getSize();
delete self.spent[key]; this.emit('remove tx', tx);
self.addressMap.removeCoin(tx, i);
});
self.totalSize -= tx.getSize();
self.emit('remove tx', tx);
return callback(); return callback();
}; };
@ -874,7 +993,7 @@ Mempool.prototype.hasOrphanSync = function hasOrphanSync(hash) {
if (hash instanceof bcoin.tx) if (hash instanceof bcoin.tx)
hash = hash.hash('hex'); hash = hash.hash('hex');
return !!this.orphans[hash]; return this.orphans[hash] != null;
}; };
Mempool.prototype.getOrphanSync = function getOrphanSync(hash) { Mempool.prototype.getOrphanSync = function getOrphanSync(hash) {
@ -910,31 +1029,19 @@ Mempool.prototype.getOrphan = function getOrphan(hash, callback) {
return callback(null, orphan); return callback(null, orphan);
}; };
Mempool.prototype._hasTX = function hasTX(tx, callback) { Mempool.prototype.seenTX = function hasTX(tx, callback) {
var self = this;
var hash = tx.hash('hex'); var hash = tx.hash('hex');
this.node.hasTX(hash, function(err, result) { if (this.hasOrphanSync(hash))
if (err) return utils.asyncify(callback)(null, true);
return callback(err);
if (result) return this.node.hasTX(hash, callback);
return callback(null, result);
self.hasOrphan(hash, function(err, result) {
if (err)
return callback(err);
return callback(null, result);
});
});
}; };
Mempool.prototype.storeOrphan = function storeOrphan(tx, callback) { Mempool.prototype.storeOrphanSync = function storeOrphanSync(tx) {
var self = this;
var prevout = {}; var prevout = {};
var hash = tx.hash('hex'); var hash = tx.hash();
var i, input, p; var i, input, key;
for (i = 0; i < tx.inputs.length; i++) { for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i]; input = tx.inputs[i];
@ -946,74 +1053,99 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx, callback) {
assert(prevout.length > 0); assert(prevout.length > 0);
prevout.forEach(function(key) { for (i = 0; i < prevout.length; i++) {
if (!self.waiting[key]) key = prevout[i];
self.waiting[key] = [];
self.waiting[key].push(hash); if (!this.waiting[key])
}); this.waiting[key] = [];
self.totalOrphans++; this.waiting[key].push(hash);
}
self.orphans[hash] = tx.toExtended(true); this.totalOrphans++;
if (self.totalOrphans > Mempool.MAX_ORPHAN_TX) this.orphans[hash.toString('hex')] = tx.toExtended(true);
return self.purgeOrphans(callback);
return utils.nextTick(callback); if (this.totalOrphans > Mempool.MAX_ORPHAN_TX)
this.purgeOrphansSync();
return tx;
}; };
Mempool.prototype.getBalance = function getBalance(callback) { Mempool.prototype.storeOrphan = function storeOrphan(tx, callback) {
var coins = []; var ret;
var hashes = Object.keys(this.coins);
var parts, hash, index, i, coin;
var unconfirmed = new bn(0);
var confirmed = new bn(0);
callback = utils.asyncify(callback); callback = utils.asyncify(callback);
try {
ret = this.storeOrphanSync(tx);
} catch (e) {
return callback(e);
}
return callback(null, ret);
};
Mempool.prototype.getBalanceSync = function getBalanceSync() {
var coins = [];
var hashes = Object.keys(this.coins);
var balance = new bn(0);
var parts, hash, index, i, coin;
for (i = 0; i < hashes.length; i++) { for (i = 0; i < hashes.length; i++) {
parts = hashes[i].split('/'); parts = hashes[i].split('/');
hash = parts[0]; hash = parts[0];
index = +parts[1]; index = +parts[1];
coin = this.getCoinSync(hash, index);
try {
coin = this.getCoinSync(hash, index);
} catch (e) {
return callback(e);
}
coins.push(coin); coins.push(coin);
} }
for (i = 0; i < coins.length; i++) { for (i = 0; i < coins.length; i++)
coin = coins[i]; balance.iadd(coins[i].value);
if (coin.height !== -1)
confirmed.iadd(coin.value);
unconfirmed.iadd(coin.value);
}
return callback(null, { return {
unconfirmed: unconfirmed, unconfirmed: balance,
confirmed: confirmed confirmed: new bn(0)
}); };
}; };
Mempool.prototype.getAll = function getAll(callback) { Mempool.prototype.getBalance = function getBalance(callback) {
var balance;
callback = utils.asyncify(callback);
try {
balance = this.getBalanceSync();
} catch (e) {
return callback(e);
}
return callback(null, balance);
};
Mempool.prototype.getAllSync = function getAllSync() {
var txs = []; var txs = [];
var hashes = Object.keys(this.txs); var hashes = Object.keys(this.txs);
var i, tx; var i, tx;
for (i = 0; i < hashes.length; i++) {
tx = this.getTXSync(hashes[i]);
if (tx)
txs.push(tx);
}
return txs;
};
Mempool.prototype.getAll = function getAll(callback) {
var txs;
callback = utils.asyncify(callback); callback = utils.asyncify(callback);
for (i = 0; i < hashes.length; i++) { try {
try { txs = this.getAllSync();
tx = this.getTXSync(hashes[i]); } catch (e) {
} catch (e) { return callback(e);
return callback(e);
}
txs.push(tx);
} }
return callback(null, txs); return callback(null, txs);
@ -1029,7 +1161,10 @@ Mempool.prototype.resolveOrphans = function resolveOrphans(tx, callback) {
return callback(null, resolved); return callback(null, resolved);
utils.forEachSerial(hashes, function(orphanHash, next, i) { utils.forEachSerial(hashes, function(orphanHash, next, i) {
var orphan = self.orphans[orphanHash]; var orphan;
orphanHash = orphanHash.toString('hex');
orphan = self.orphans[orphanHash];
if (!orphan) if (!orphan)
return next(); return next();
@ -1072,8 +1207,12 @@ Mempool.prototype.resolveOrphans = function resolveOrphans(tx, callback) {
}); });
}; };
Mempool.prototype.getSnapshotSync = function getSnapshotSync() {
return Object.keys(this.txs);
};
Mempool.prototype.getSnapshot = function getSnapshot(callback) { Mempool.prototype.getSnapshot = function getSnapshot(callback) {
return utils.asyncify(callback)(null, Object.keys(this.txs)); return utils.asyncify(callback)(null, this.getSnapshotSync());
}; };
Mempool.prototype.checkLocks = function checkLocks(tx, flags, callback) { Mempool.prototype.checkLocks = function checkLocks(tx, flags, callback) {