diff --git a/lib/bcoin/chainblock.js b/lib/bcoin/chainblock.js index 9f15d933..bc31b19b 100644 --- a/lib/bcoin/chainblock.js +++ b/lib/bcoin/chainblock.js @@ -130,16 +130,7 @@ ChainBlock.prototype.free = function free() { }; ChainBlock.prototype.isMainChain = function isMainChain(callback) { - var self = this; - return this.chain.db.getHash(this.height, function(err, hash) { - if (err) - return callback(err); - - if (!hash) - return callback(null, false); - - return callback(null, self.hash === hash); - }); + return this.chain.db.isMainChain(this, callback); }; ChainBlock.prototype.getAncestorByHeight = function getAncestorByHeight(height, callback) { diff --git a/lib/bcoin/chaindb.js b/lib/bcoin/chaindb.js index 85c2149f..a46b1121 100644 --- a/lib/bcoin/chaindb.js +++ b/lib/bcoin/chaindb.js @@ -484,6 +484,33 @@ ChainDB.prototype.getNextHash = function getNextHash(hash, callback) { }); }; +ChainDB.prototype.isMainChain = function isMainChain(hash, callback) { + var self = this; + var query; + + if (hash instanceof bcoin.chainblock) { + query = hash.height; + hash = hash.hash; + } else { + query = hash; + } + + return this.getHeight(query, function(err, height) { + if (err) + return callback(err); + + return self.getHash(height, function(err, existing) { + if (err) + return callback(err); + + if (!existing) + return callback(null, false); + + return callback(null, hash === existing); + }); + }); +}; + ChainDB.prototype.reset = function reset(block, callback) { var self = this; var batch; @@ -1020,6 +1047,15 @@ ChainDB.prototype.getTX = function getTX(hash, callback) { }); }; +ChainDB.prototype.hasTX = function hasTX(hash, callback) { + return this.getTX(hash, function(err, tx) { + if (err) + return callback(err); + + return callback(null, tx != null); + }); +}; + ChainDB.prototype.getFullTX = function getFullTX(hash, callback) { var self = this; diff --git a/lib/bcoin/fullnode.js b/lib/bcoin/fullnode.js index bf0fc7ac..5613f7d8 100644 --- a/lib/bcoin/fullnode.js +++ b/lib/bcoin/fullnode.js @@ -369,31 +369,15 @@ Fullnode.prototype.getTXByAddress = function getTXByAddress(addresses, callback) }; Fullnode.prototype.fillCoins = function fillCoins(tx, callback) { - var self = this; - - this.mempool.fillCoins(tx, function(err) { - if (err) - return callback(err); - - if (tx.hasCoins()) - return callback(null, tx); - - self.chain.db.fillCoins(tx, callback); - }); + return this.mempool.fillAllCoins(tx, callback); }; Fullnode.prototype.fillTX = function fillTX(tx, callback) { - var self = this; + return this.mempool.fillAllTX(tx, callback); +}; - this.mempool.fillTX(tx, function(err) { - if (err) - return callback(err); - - if (tx.hasCoins()) - return callback(null, tx); - - self.chain.db.fillTX(tx, callback); - }); +Fullnode.prototype.getConfidence = function getConfidence(tx, callback) { + return this.mempool.getConfidence(tx, callback); }; /** diff --git a/lib/bcoin/ldb.js b/lib/bcoin/ldb.js index 3eddea3f..b182730f 100644 --- a/lib/bcoin/ldb.js +++ b/lib/bcoin/ldb.js @@ -54,6 +54,9 @@ function getLocation(name) { } function getBackend(backend) { + if (bcoin.isBrowser) + return require('level-js'); + if (typeof backend !== 'string') backend = process.env.BCOIN_DB; @@ -66,14 +69,8 @@ function getBackend(backend) { else if (backend === 'memory') backend = 'memdown'; - // Require directly for browserify - if (backend === 'memdown') - return require('memdown'); - - if (bcoin.isBrowser) - return require('level-js'); - - bcoin.ensurePrefix(); + if (backend !== 'memdown') + bcoin.ensurePrefix(); return require(backend); } @@ -94,6 +91,22 @@ function destroy(name, backend, callback) { backend.destroy(file, callback); } +function repair(name, backend, callback) { + var file = getLocation(name); + + if (!callback) { + callback = backend; + backend = null; + } + + backend = getBackend(backend); + + if (!backend.repair) + return utils.asyncify(callback)(new Error('Cannot repair.')); + + backend.repair(file, callback); +} + /** * LowlevelUp * @@ -204,5 +217,6 @@ LowlevelUp.prototype.approximateSize = function approximateSize(start, end, call exports = ldb; exports.LowlevelUp = LowlevelUp; exports.destroy = destroy; +exports.repair = repair; module.exports = exports; diff --git a/lib/bcoin/mempool.js b/lib/bcoin/mempool.js index f86d8690..c952bcd5 100644 --- a/lib/bcoin/mempool.js +++ b/lib/bcoin/mempool.js @@ -41,6 +41,7 @@ function Mempool(node, options) { this.totalOrphans = 0; this.coins = {}; this.txs = {}; + this.psIndex = new BinaryIndex; this.addressMap = new AddressMap; this.orphans = {}; this.waiting = {}; @@ -137,49 +138,48 @@ Mempool.prototype.removeBlock = function removeBlock(block, callback, force) { Mempool.prototype.limitMempoolSize = function limitMempoolSize(callback) { var self = this; + var txs; if (this.totalSize <= Mempool.MAX_MEMPOOL_SIZE) - return callback(null, true); + return utils.asyncify(callback)(null, true); - this.getRange({ - start: 0, - end: utils.now() - Mempool.MEMPOOL_EXPIRY - }, function(err, txs) { + try { + txs = this.getRangeSync({ + start: 0, + end: utils.now() - Mempool.MEMPOOL_EXPIRY + }); + } catch (e) { + return utils.asyncify(callback)(e); + } + + utils.forEachSerial(function(tx, next) { + self.removeUnchecked(tx, next); + }, function(err) { if (err) return callback(err); - utils.forEachSerial(function(tx, next) { - self.removeUnchecked(tx, next); - }, function(err) { - if (err) - return callback(err); + try { + self.purgeOrphansSync(); + } catch (e) { + return callback(e); + } - self.purgeOrphans(function(err) { - if (err) - return callback(err); - - return callback(self.totalSize <= Mempool.MAX_MEMPOOL_SIZE); - }); - }); + return callback(null, self.totalSize <= Mempool.MAX_MEMPOOL_SIZE); }); }; Mempool.prototype.getRangeSync = function getRangeSync(options) { - return []; -}; + var hashes = this.psIndex.range(options.start, options.end); + var txs = []; + var i, tx; -Mempool.prototype.getRange = function getRange(options, callback) { - var ret; - - callback = utils.asyncify(callback); - - try { - ret = this.getRangeSync(options); - } catch (e) { - return callback(e); + for (i = 0; i < hashes.length; i++) { + tx = this.getTXSync(hashes[i].toString('hex')); + if (tx) + txs.push(tx); } - return callback(null, ret); + return txs; }; Mempool.prototype.purgeOrphansSync = function purgeOrphansSync() { @@ -196,20 +196,7 @@ Mempool.prototype.purgeOrphansSync = function purgeOrphansSync() { } }; -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.getSync = Mempool.prototype.getTXSync = function getTXSync(hash) { var tx; @@ -258,50 +245,9 @@ Mempool.prototype.isDoubleSpendSync = function isDoubleSpendSync(tx) { return false; }; -Mempool.prototype.get = -Mempool.prototype.getTX = function getTX(hash, callback) { - var tx; - - callback = utils.asyncify(callback); - - try { - 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 { - coin = this.getCoinSync(hash, index); - } catch (e) { - return callback(e); - } - - return callback(null, coin); -}; - -Mempool.prototype.isSpent = function isSpent(hash, index, callback) { - callback = utils.asyncify(callback); - - return callback(null, this.isSpentSync(hash, index)); -}; - -Mempool.prototype.isDoubleSpend = function isDoubleSpend(tx, callback) { - callback = utils.asyncify(callback); - return callback(null, this.isDoubleSpendSync(tx)); -}; - Mempool.prototype.getCoinsByAddressSync = function getCoinsByAddressSync(addresses) { - var uniq = {}; var coins = []; - var i, j, address, keys, key, coin, parts, hash, index; + var i, j, address, keys, key, coin; if (!Array.isArray(addresses)) addresses = [addresses]; @@ -313,10 +259,7 @@ Mempool.prototype.getCoinsByAddressSync = function getCoinsByAddressSync(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]; - coin = this.getCoinSync(hash, index); + coin = this.getCoinSync(key[0], key[1]); if (coin) coins.push(coin); } @@ -325,20 +268,7 @@ Mempool.prototype.getCoinsByAddressSync = function getCoinsByAddressSync(address 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.getByAddressSync = Mempool.prototype.getTXByAddressSync = function getTXByAddressSync(addresses, callback) { var uniq = {}; var txs = []; @@ -372,21 +302,6 @@ Mempool.prototype.getTXByAddressSync = function getTXByAddressSync(addresses, ca return txs; }; -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; @@ -410,18 +325,6 @@ Mempool.prototype.fillTXSync = function fillTXSync(tx) { 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.fillCoinsSync = function fillCoinsSync(tx) { var i, input; @@ -444,18 +347,7 @@ Mempool.prototype.fillCoinsSync = function fillCoinsSync(tx) { 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.hasSync = Mempool.prototype.hasTXSync = function hasTXSync(hash) { if (hash instanceof bcoin.tx) hash = hash.hash('hex'); @@ -463,12 +355,6 @@ Mempool.prototype.hasTXSync = function hasTXSync(hash) { return this.txs[hash] != null; }; -Mempool.prototype.has = -Mempool.prototype.hasTX = function hasTX(hash, callback) { - callback = utils.asyncify(callback); - return callback(null, this.hasTXSync(hash)); -}; - Mempool.prototype.add = Mempool.prototype.addTX = function addTX(tx, callback, force) { var self = this; @@ -507,7 +393,7 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) { if (tx.isCoinbase()) return callback(new VerifyError(tx, 'invalid', 'coinbase', 100)); - self.chain.checkFinal(self.chain.tip, tx, lockFlags, function(err, isFinal) { + this.chain.checkFinal(this.chain.tip, tx, lockFlags, function(err, isFinal) { if (err) return callback(err); @@ -541,7 +427,7 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) { 0)); } - self.node.fillCoins(tx, function(err) { + self.fillAllCoins(tx, function(err) { if (err) return callback(err); @@ -580,169 +466,86 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) { }); }; -function AddressMap() { - this.map = { tx: {}, coin: {} }; -} +// Use bitcoinj-style confidence calculation +Mempool.prototype.getConfidence = function getConfidence(hash, callback) { + var tx; -AddressMap.prototype.getTX = function getTX(address) { - var map = this.map.tx[address]; - var keys = []; - var i, key; + callback = utils.asyncify(callback); - if (!map) - return keys; - - for (i = 0; i < map.length; i++) { - key = map[i]; - assert(key.length === 32); - keys.push(key.toString('hex')); - } - - return keys; -}; - -AddressMap.prototype.getCoins = function getCoins(address) { - var map = this.map.coin[address]; - var keys = []; - var i, p, key; - - if (!map) - return keys; - - for (i = 0; i < map.length; i++) { - key = map[i]; - - assert(key.length === 36); - - 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(); - var addresses = tx.getAddresses(); - var address, i, map, index; - - for (i = 0; i < addresses.length; i++) { - address = addresses[i]; - if (!this.map.tx[address]) - this.map.tx[address] = []; - map = this.map.tx[address]; - index = binarySearch(map, hash, true); - map.splice(index + 1, 0, hash); - } -}; - -function binarySearch(items, key, insert) { - var start = 0; - var end = items.length - 1; - var pos, cmp; - - while (start <= end) { - pos = (start + end) >>> 1; - cmp = utils.cmp(items[pos], key); - - if (cmp === 0) - return pos; - - if (cmp < 0) - start = pos + 1; - else - end = pos - 1; - } - - if (!insert) - return -1; - - if (start === 0) - return -1; - - return start - 1; -} - -AddressMap.prototype.removeTX = function removeTX(tx) { - var hash = tx.hash(); - var addresses = tx.getAddresses(); - var address, map, index; - - for (i = 0; i < addresses.length; i++) { - address = addresses[i]; - map = this.map.tx[address]; - - if (map) { - index = binarySearch(map, hash); - if (index !== -1) - map.splice(index, 1); - if (map.length === 0) - delete this.map.tx[address]; - } - } -}; - -AddressMap.prototype.addCoin = function addCoin(coin) { - var address = coin.getAddress(); - var key = this._coinKey(coin.hash, coin.index); - var map, index; - - if (address) { - if (!this.map.coin[address]) - this.map.coin[address] = []; - map = this.map.coin[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, index; - - if (tx instanceof bcoin.input) { - address = tx.getAddress(); - key = this._coinKey(tx.prevout.hash, tx.prevout.index); + if (hash instanceof bcoin.tx) { + tx = hash; + hash = tx.hash('hex'); } else { - address = tx.outputs[i].getAddress(); - key = this._coinKey(tx.hash(), i); + try { + tx = this.getTXSync(hash); + } catch (e) { + return callback(e); + } } - map = this.map.coin[address]; + if (tx && this.isDoubleSpendSync(tx)) + return callback(null, constants.confidence.INCONFLICT); - if (map) { - index = binarySearch(map, key); - if (index !== -1) - map.splice(index, 1); - if (map.length === 0) - delete this.map.coin[address]; - } + if (this.hasTXSync(hash)) + return callback(null, constants.confidence.PENDING); + + this.chain.db.getTX(hash, function(err, existing) { + if (err) + return callback(err); + + if (!existing) + return callback(null, constants.confidence.UNKNOWN); + + self.chain.db.isMainChain(existing.block, function(err, result) { + if (err) + return callback(err); + + if (result) + return callback(null, constants.confidence.BUILDING); + + return callback(null, constants.confidence.DEAD); + }); + }); +}; + +Mempool.prototype.fillAllTX = function fillAllTX(tx, callback) { + var self = this; + + this.fillTX(tx, function(err) { + if (err) + return callback(err); + + if (tx.hasCoins()) + return callback(null, tx); + + self.chain.db.fillTX(tx, callback); + }); +}; + +Mempool.prototype.fillAllCoins = function fillAllCoins(tx, callback) { + var self = this; + + this.fillCoins(tx, function(err) { + if (err) + return callback(err); + + if (tx.hasCoins()) + return callback(null, tx); + + self.chain.db.fillCoins(tx, callback); + }); }; Mempool.prototype.addUnchecked = function addUnchecked(tx, callback) { var self = this; - var hash = tx.hash('hex'); + var hash = tx.hash(); + var hex = hash.toString('hex'); var input, output, i, key, coin; - this.txs[hash] = tx.toExtended(); + this.txs[hex] = tx.toExtended(); this.addressMap.addTX(tx); + this.psIndex.insert(tx); for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; @@ -754,13 +557,14 @@ Mempool.prototype.addUnchecked = function addUnchecked(tx, callback) { for (i = 0; i < tx.outputs.length; i++) { output = tx.outputs[i]; - key = hash + '/' + i; + key = hex + '/' + i; coin = bcoin.coin(tx, i); this.coins[key] = coin.toRaw(); this.addressMap.addCoin(coin); } this.totalSize += tx.getSize(); + this.emit('tx', tx); this.emit('add tx', tx); @@ -810,6 +614,7 @@ Mempool.prototype.removeUnchecked = function removeUnchecked(tx, callback) { } this.addressMap.removeTX(tx); + this.psIndex.remove(tx); for (i = 0; i < tx.inputs.length; i++) { inputs = tx.outputs[i]; @@ -834,7 +639,7 @@ Mempool.prototype.removeUnchecked = function removeUnchecked(tx, callback) { }; Mempool.prototype.removeOrphanSync = function removeOrphanSync(tx) { - var prevout, i, hex, hash, prev, index; + var prevout, i, hex, hash, prev, map, index; if (typeof tx === 'string') tx = this.getOrphanSync(tx); @@ -850,13 +655,15 @@ Mempool.prototype.removeOrphanSync = function removeOrphanSync(tx) { for (i = 0; i < prevout.length; i++) { prev = prevout[i]; - if (!this.waiting[prev]) + map = this.waiting[prev]; + + if (!map) continue; - // TODO BINARY SEARCH/INSERT - index = utils.indexOf(this.waiting[prev], hash); + + index = binarySearch(map, hash); if (index !== -1) { - this.waiting[prev].splice(index, 1); - if (this.waiting[prev].length === 0) + map.splice(index, 1); + if (map.length === 0) delete this.waiting[prev]; } } @@ -864,20 +671,6 @@ Mempool.prototype.removeOrphanSync = function removeOrphanSync(tx) { return true; }; -Mempool.prototype.removeOrphan = function removeOrphan(tx, callback) { - var ret; - - callback = utils.asyncify(callback); - - try { - ret = this.removeOrphanSync(tx); - } catch (e) { - return callback(e); - } - - return callback(null, ret); -}; - Mempool.prototype.verify = function verify(tx, callback) { var self = this; var height = this.chain.height + 1; @@ -1023,20 +816,6 @@ Mempool.prototype.countAncestorsSync = function countAncestorsSync(tx) { return inputs.sort().pop(); }; -Mempool.prototype.countAncestors = function countAncestors(tx, callback) { - var count; - - callback = utils.asyncify(callback); - - try { - count = this.countAncestorsSync(tx); - } catch (e) { - return callback(e); - } - - return callback(null, count); -}; - Mempool.prototype.hasOrphanSync = function hasOrphanSync(hash) { if (hash instanceof bcoin.tx) hash = hash.hash('hex'); @@ -1058,38 +837,22 @@ Mempool.prototype.getOrphanSync = function getOrphanSync(hash) { return bcoin.tx.fromExtended(orphan, true); }; -Mempool.prototype.hasOrphan = function hasOrphan(hash, callback) { - callback = utils.asyncify(callback); - return callback(null, this.hasOrphanSync(hash)); -}; - -Mempool.prototype.getOrphan = function getOrphan(hash, callback) { - var orphan; - - callback = utils.asyncify(callback); - - try { - orphan = this.getOrphanSync(hash); - } catch (e) { - return callback(e); - } - - return callback(null, orphan); -}; - -Mempool.prototype.seenTX = function hasTX(tx, callback) { +Mempool.prototype.seenTX = function seenTX(tx, callback) { var hash = tx.hash('hex'); if (this.hasOrphanSync(hash)) return utils.asyncify(callback)(null, true); - return this.node.hasTX(hash, callback); + if (this.hasTXSync(hash)) + return utils.asyncify(callback)(null, true); + + return this.chain.db.hasTX(hash, callback); }; Mempool.prototype.storeOrphanSync = function storeOrphanSync(tx) { var prevout = {}; var hash = tx.hash(); - var i, input, key; + var i, input, key, map, index; for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; @@ -1107,7 +870,9 @@ Mempool.prototype.storeOrphanSync = function storeOrphanSync(tx) { if (!this.waiting[key]) this.waiting[key] = []; - this.waiting[key].push(hash); + map = this.waiting[key]; + index = binarySearch(map, hash, true); + map.splice(index + 1, 0, hash); } this.totalOrphans++; @@ -1120,20 +885,6 @@ Mempool.prototype.storeOrphanSync = function storeOrphanSync(tx) { return tx; }; -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); @@ -1157,20 +908,6 @@ Mempool.prototype.getBalanceSync = function getBalanceSync() { }; }; -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); @@ -1185,20 +922,6 @@ Mempool.prototype.getAllSync = function getAllSync() { return txs; }; -Mempool.prototype.getAll = function getAll(callback) { - var txs; - - callback = utils.asyncify(callback); - - try { - txs = this.getAllSync(); - } catch (e) { - return callback(e); - } - - return callback(null, txs); -}; - Mempool.prototype.resolveOrphans = function resolveOrphans(tx, callback) { var self = this; var hash = tx.hash('hex'); @@ -1223,10 +946,7 @@ Mempool.prototype.resolveOrphans = function resolveOrphans(tx, callback) { return next(e); } - orphan.inputs.forEach(function(input) { - if (!input.coin && input.prevout.hash === hash) - input.coin = bcoin.coin(tx, input.prevout.index); - }); + orphan.fillCoins(tx); if (orphan.hasCoins()) { self.totalOrphans--; @@ -1259,10 +979,6 @@ Mempool.prototype.getSnapshotSync = function getSnapshotSync() { return Object.keys(this.txs); }; -Mempool.prototype.getSnapshot = function getSnapshot(callback) { - return utils.asyncify(callback)(null, this.getSnapshotSync()); -}; - Mempool.prototype.checkLocks = function checkLocks(tx, flags, callback) { var self = this; var tip = this.chain.tip; @@ -1282,6 +998,446 @@ Mempool.prototype.checkLocks = function checkLocks(tx, flags, callback) { return this.chain.checkLocks(tx, flags, index, callback); }; +/** + * Async Wrappers + */ + +Mempool.prototype.getRange = function getRange(options, callback) { + var ret; + + callback = utils.asyncify(callback); + + try { + ret = this.getRangeSync(options); + } catch (e) { + return callback(e); + } + + return callback(null, ret); +}; + +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.get = +Mempool.prototype.getTX = function getTX(hash, callback) { + var tx; + + callback = utils.asyncify(callback); + + try { + 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 { + coin = this.getCoinSync(hash, index); + } catch (e) { + return callback(e); + } + + return callback(null, coin); +}; + +Mempool.prototype.isSpent = function isSpent(hash, index, callback) { + callback = utils.asyncify(callback); + + return callback(null, this.isSpentSync(hash, index)); +}; + +Mempool.prototype.isDoubleSpend = function isDoubleSpend(tx, callback) { + callback = utils.asyncify(callback); + return callback(null, this.isDoubleSpendSync(tx)); +}; + +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) { + var txs; + + callback = utils.asyncify(callback); + + try { + txs = this.getTXByAddressSync(addresses); + } catch (e) { + return callback(e); + } + + return callback(null, txs); +}; + +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) { + callback = utils.asyncify(callback); + + try { + tx = this.fillCoinsSync(tx); + } catch (e) { + return callback(e); + } + + return callback(null, tx); +}; + +Mempool.prototype.has = +Mempool.prototype.hasTX = function hasTX(hash, callback) { + callback = utils.asyncify(callback); + return callback(null, this.hasTXSync(hash)); +}; + +Mempool.prototype.removeOrphan = function removeOrphan(tx, callback) { + var ret; + + callback = utils.asyncify(callback); + + try { + ret = this.removeOrphanSync(tx); + } catch (e) { + return callback(e); + } + + return callback(null, ret); +}; + +Mempool.prototype.countAncestors = function countAncestors(tx, callback) { + var count; + + callback = utils.asyncify(callback); + + try { + count = this.countAncestorsSync(tx); + } catch (e) { + return callback(e); + } + + return callback(null, count); +}; + +Mempool.prototype.hasOrphan = function hasOrphan(hash, callback) { + callback = utils.asyncify(callback); + return callback(null, this.hasOrphanSync(hash)); +}; + +Mempool.prototype.getOrphan = function getOrphan(hash, callback) { + var orphan; + + callback = utils.asyncify(callback); + + try { + orphan = this.getOrphanSync(hash); + } catch (e) { + return callback(e); + } + + return callback(null, orphan); +}; + +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.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.getAll = function getAll(callback) { + var txs; + + callback = utils.asyncify(callback); + + try { + txs = this.getAllSync(); + } catch (e) { + return callback(e); + } + + return callback(null, txs); +}; + +Mempool.prototype.getSnapshot = function getSnapshot(callback) { + return utils.asyncify(callback)(null, this.getSnapshotSync()); +}; + +/** + * AddressMap + */ + +function AddressMap() { + this.map = { tx: {}, coin: {} }; +} + +AddressMap.prototype.getTX = function getTX(address) { + var map = this.map.tx[address]; + var keys = []; + var i, key; + + if (!map) + return keys; + + for (i = 0; i < map.length; i++) { + key = map[i]; + assert(key.length === 32); + keys.push(key.toString('hex')); + } + + return keys; +}; + +AddressMap.prototype.getCoins = function getCoins(address) { + var map = this.map.coin[address]; + var keys = []; + var i, p, key; + + if (!map) + return keys; + + for (i = 0; i < map.length; i++) { + key = map[i]; + + assert(key.length === 36); + + 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(); + var addresses = tx.getAddresses(); + var address, i, map, index; + + for (i = 0; i < addresses.length; i++) { + address = addresses[i]; + if (!this.map.tx[address]) + this.map.tx[address] = []; + map = this.map.tx[address]; + index = binarySearch(map, hash, true); + map.splice(index + 1, 0, hash); + } +}; + +AddressMap.prototype.removeTX = function removeTX(tx) { + var hash = tx.hash(); + var addresses = tx.getAddresses(); + var address, map, index; + + for (i = 0; i < addresses.length; i++) { + address = addresses[i]; + map = this.map.tx[address]; + + if (map) { + index = binarySearch(map, hash); + if (index !== -1) + map.splice(index, 1); + if (map.length === 0) + delete this.map.tx[address]; + } + } +}; + +AddressMap.prototype.addCoin = function addCoin(coin) { + var address = coin.getAddress(); + var key = this._coinKey(coin.hash, coin.index); + var map, index; + + if (address) { + if (!this.map.coin[address]) + this.map.coin[address] = []; + map = this.map.coin[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, index; + + if (tx instanceof bcoin.input) { + address = tx.getAddress(); + key = this._coinKey(tx.prevout.hash, tx.prevout.index); + } else { + address = tx.outputs[i].getAddress(); + key = this._coinKey(tx.hash(), i); + } + + map = this.map.coin[address]; + + if (map) { + index = binarySearch(map, key); + if (index !== -1) + map.splice(index, 1); + if (map.length === 0) + delete this.map.coin[address]; + } +}; + +/** + * BinaryIndex + */ + +function BinaryIndex() { + this.index = []; + this.data = []; +} + +BinaryIndex.prototype.insert = function insert(tx) { + var hash = tx.hash(); + var ps = new Buffer(4); + var index; + + utils.writeU32BE(ps, tx.ps, 0); + + index = binarySearch(this.index, ps, true); + + this.index.splice(index + 1, 0, ps); + this.data.splice(index + 1, 0, hash); +}; + +BinaryIndex.prototype.remove = function remove(tx) { + var hash = tx.hash(); + var index; + + index = binarySearch(this.data, hash); + + if (index !== -1) { + this.index.splice(index, 1); + this.data.splice(index, 1); + } +}; + +BinaryIndex.prototype.range = function range(start, end) { + var hashes = []; + var s = new Buffer(4); + var i, ps; + + utils.writeU32BE(s, start, 0); + + i = binarySearch(this.index, s, true); + + for (; i < this.index.length; i++) { + ps = utils.readU32BE(this.index[i], 0); + if (ps < start || ps > end) + return hashes; + hashes.push(this.data[i]); + } + + return hashes; +}; + +/** + * Helpers + */ + +function binarySearch(items, key, insert) { + var start = 0; + var end = items.length - 1; + var pos, cmp; + + while (start <= end) { + pos = (start + end) >>> 1; + cmp = utils.cmp(items[pos], key); + + if (cmp === 0) + return pos; + + if (cmp < 0) + start = pos + 1; + else + end = pos - 1; + } + + if (!insert) + return -1; + + if (start === 0) + return -1; + + return start - 1; +} + /** * Expose */ diff --git a/lib/bcoin/protocol/constants.js b/lib/bcoin/protocol/constants.js index e891d5b7..df556544 100644 --- a/lib/bcoin/protocol/constants.js +++ b/lib/bcoin/protocol/constants.js @@ -337,3 +337,12 @@ exports.thresholdStates = { ACTIVE: 3, FAILED: 4 }; + +// bitcoinj style confidence calculation +exports.confidence = { + BUILDING: 1, + PENDING: 2, + DEAD: 4, + INCONFLICT: 5, + UNKNOWN: 0 +}; diff --git a/package.json b/package.json index f8fa6536..a13f729b 100644 --- a/package.json +++ b/package.json @@ -30,19 +30,19 @@ "dependencies": { "bn.js": "4.11.0", "elliptic": "6.2.3", - "leveldown": "git://github.com/Level/leveldown.git#master", - "memdown": "1.1.2" + "leveldown": "git://github.com/Level/leveldown.git#master" }, "optionalDependencies": { "secp256k1": "3.0.0", "socket.io": "1.45.0" }, "devDependencies": { + "browserify": "13.0.0", "hash.js": "1.0.3", "level-js": "2.2.3", - "browserify": "13.0.0", - "uglify-js": "2.6.1", + "memdown": "1.1.2", "mocha": "1.21.5", + "uglify-js": "2.6.1", "v8-profiler": "5.6.0" } }