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