undo coins.
This commit is contained in:
parent
8344879a16
commit
845a987e00
@ -635,71 +635,6 @@ Block.fromRaw = function fromRaw(data, enc, type) {
|
|||||||
return new Block(Block.parseRaw(data, enc));
|
return new Block(Block.parseRaw(data, enc));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize a block to BCoin "compact format".
|
|
||||||
* This is the serialization format BCoin uses internally
|
|
||||||
* to store blocks in the database. It includes the height,
|
|
||||||
* but does not include transaction data, only transaction
|
|
||||||
* hashes.
|
|
||||||
* @returns {Buffer}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Block.prototype.toCompact = function toCompact() {
|
|
||||||
var p = new BufferWriter();
|
|
||||||
var height = this.height;
|
|
||||||
|
|
||||||
if (height === -1)
|
|
||||||
height = 0x7fffffff;
|
|
||||||
|
|
||||||
p.writeBytes(this.abbr());
|
|
||||||
p.writeU32(height);
|
|
||||||
p.writeVarint(this.txs.length);
|
|
||||||
|
|
||||||
this.txs.forEach(function(tx) {
|
|
||||||
p.writeHash(tx.hash());
|
|
||||||
});
|
|
||||||
|
|
||||||
return p.render();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a transaction in "compact" serialization format.
|
|
||||||
* @param {Buffer} buf
|
|
||||||
* @returns {NakedBlock} - A "naked" block object with a `hashes` vector.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Block.parseCompact = function parseCompact(buf) {
|
|
||||||
var p = new BufferReader(buf);
|
|
||||||
var hashes = [];
|
|
||||||
var version = p.readU32(); // Technically signed
|
|
||||||
var prevBlock = p.readHash('hex');
|
|
||||||
var merkleRoot = p.readHash('hex');
|
|
||||||
var ts = p.readU32();
|
|
||||||
var bits = p.readU32();
|
|
||||||
var nonce = p.readU32();
|
|
||||||
var height = p.readU32();
|
|
||||||
var txCount = p.readVarint();
|
|
||||||
var i;
|
|
||||||
|
|
||||||
for (i = 0; i < txCount; i++)
|
|
||||||
hashes.push(p.readHash('hex'));
|
|
||||||
|
|
||||||
if (height === 0x7fffffff)
|
|
||||||
height = -1;
|
|
||||||
|
|
||||||
return {
|
|
||||||
version: version,
|
|
||||||
prevBlock: prevBlock,
|
|
||||||
merkleRoot: merkleRoot,
|
|
||||||
ts: ts,
|
|
||||||
bits: bits,
|
|
||||||
nonce: nonce,
|
|
||||||
height: height,
|
|
||||||
totalTX: txCount,
|
|
||||||
hashes: hashes
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the Block to a MerkleBlock.
|
* Convert the Block to a MerkleBlock.
|
||||||
* @param {Bloom} filter - Bloom filter for transactions
|
* @param {Bloom} filter - Bloom filter for transactions
|
||||||
|
|||||||
@ -1938,7 +1938,7 @@ Chain.prototype.getLocator = function getLocator(start, callback, force) {
|
|||||||
step *= 2;
|
step *= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.forEach(hashes, function(height, next, i) {
|
utils.forEachSerial(hashes, function(height, next, i) {
|
||||||
if (typeof height === 'string')
|
if (typeof height === 'string')
|
||||||
return next();
|
return next();
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,10 @@ var utils = require('./utils');
|
|||||||
var assert = utils.assert;
|
var assert = utils.assert;
|
||||||
var pad32 = utils.pad32;
|
var pad32 = utils.pad32;
|
||||||
var DUMMY = new Buffer([0]);
|
var DUMMY = new Buffer([0]);
|
||||||
|
var BufferWriter = require('./writer');
|
||||||
|
var BufferReader = require('./reader');
|
||||||
|
var Framer = bcoin.protocol.framer;
|
||||||
|
var Parser = bcoin.protocol.parser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The database backend for the {@link Chain} object.
|
* The database backend for the {@link Chain} object.
|
||||||
@ -716,17 +720,12 @@ ChainDB.prototype.has = function has(height, callback) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
ChainDB.prototype.saveBlock = function saveBlock(block, batch, connect, callback) {
|
ChainDB.prototype.saveBlock = function saveBlock(block, batch, connect, callback) {
|
||||||
var i, tx;
|
var i, j, tx, hash, addresses, address;
|
||||||
|
|
||||||
if (this.options.spv)
|
if (this.options.spv)
|
||||||
return utils.nextTick(callback);
|
return utils.nextTick(callback);
|
||||||
|
|
||||||
batch.put('b/b/' + block.hash('hex'), block.toCompact());
|
batch.put('b/b/' + block.hash('hex'), block.render());
|
||||||
|
|
||||||
for (i = 0; i < block.txs.length; i++) {
|
|
||||||
tx = block.txs[i];
|
|
||||||
batch.put('t/t/' + tx.hash('hex'), tx.toExtended());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!connect)
|
if (!connect)
|
||||||
return utils.nextTick(callback);
|
return utils.nextTick(callback);
|
||||||
@ -758,11 +757,6 @@ ChainDB.prototype.removeBlock = function removeBlock(hash, batch, callback) {
|
|||||||
|
|
||||||
batch.del('b/b/' + block.hash('hex'));
|
batch.del('b/b/' + block.hash('hex'));
|
||||||
|
|
||||||
for (i = 0; i < block.txs.length; i++) {
|
|
||||||
tx = block.txs[i];
|
|
||||||
batch.del('t/t/' + tx.hash('hex'));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.disconnectBlock(block, batch, callback);
|
self.disconnectBlock(block, batch, callback);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -776,7 +770,8 @@ ChainDB.prototype.removeBlock = function removeBlock(hash, batch, callback) {
|
|||||||
|
|
||||||
ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) {
|
ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var i, j, tx, input, output, key, address, hash, uniq, coin;
|
var undo = new BufferWriter();
|
||||||
|
var i, j, tx, input, output, key, addresses, address, hash, coin;
|
||||||
|
|
||||||
if (this.options.spv) {
|
if (this.options.spv) {
|
||||||
self.emit('add block', block);
|
self.emit('add block', block);
|
||||||
@ -797,7 +792,17 @@ ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) {
|
|||||||
for (i = 0; i < block.txs.length; i++) {
|
for (i = 0; i < block.txs.length; i++) {
|
||||||
tx = block.txs[i];
|
tx = block.txs[i];
|
||||||
hash = tx.hash('hex');
|
hash = tx.hash('hex');
|
||||||
uniq = {};
|
|
||||||
|
if (self.options.indexTX) {
|
||||||
|
batch.put('t/t/' + hash, tx.toExtended());
|
||||||
|
if (self.options.indexAddress) {
|
||||||
|
addresses = tx.getAddresses();
|
||||||
|
for (j = 0; j < addresses.length; j++) {
|
||||||
|
address = addresses[j];
|
||||||
|
batch.put('t/a/' + address + '/' + hash, DUMMY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (j = 0; j < tx.inputs.length; j++) {
|
for (j = 0; j < tx.inputs.length; j++) {
|
||||||
input = tx.inputs[j];
|
input = tx.inputs[j];
|
||||||
@ -810,17 +815,14 @@ ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) {
|
|||||||
|
|
||||||
if (self.options.indexAddress) {
|
if (self.options.indexAddress) {
|
||||||
address = input.getAddress();
|
address = input.getAddress();
|
||||||
|
|
||||||
if (address && !uniq[address] && !self.prune) {
|
|
||||||
uniq[address] = true;
|
|
||||||
batch.put('t/a/' + address + '/' + hash, DUMMY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address)
|
if (address)
|
||||||
batch.del('u/a/' + address + '/' + key);
|
batch.del('u/a/' + address + '/' + key);
|
||||||
}
|
}
|
||||||
|
|
||||||
batch.del('u/t/' + key);
|
batch.del('u/t/' + key);
|
||||||
|
|
||||||
|
Framer.coin(input.coin, false, undo);
|
||||||
|
|
||||||
self.coinCache.remove(key);
|
self.coinCache.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -831,21 +833,19 @@ ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) {
|
|||||||
|
|
||||||
if (self.options.indexAddress) {
|
if (self.options.indexAddress) {
|
||||||
address = output.getAddress();
|
address = output.getAddress();
|
||||||
|
|
||||||
if (address && !uniq[address] && !self.prune) {
|
|
||||||
uniq[address] = true;
|
|
||||||
batch.put('t/a/' + address + '/' + hash, DUMMY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address)
|
if (address)
|
||||||
batch.put('u/a/' + address + '/' + key, DUMMY);
|
batch.put('u/a/' + address + '/' + key, DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
batch.put('u/t/' + key, coin.toRaw());
|
batch.put('u/t/' + key, coin.toRaw());
|
||||||
|
|
||||||
self.coinCache.set(key, coin);
|
self.coinCache.set(key, coin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (undo.written > 0)
|
||||||
|
batch.put('b/u/' + block.hash('hex'), undo.render());
|
||||||
|
|
||||||
self.emit('add block', block);
|
self.emit('add block', block);
|
||||||
|
|
||||||
self._pruneBlock(block, batch, function(err) {
|
self._pruneBlock(block, batch, function(err) {
|
||||||
@ -865,7 +865,7 @@ ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) {
|
|||||||
|
|
||||||
ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callback) {
|
ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var i, j, tx, input, output, key, address, hash, uniq;
|
var i, j, tx, input, output, key, addresses, address, hash;
|
||||||
|
|
||||||
if (this.options.spv)
|
if (this.options.spv)
|
||||||
return utils.nextTick(callback);
|
return utils.nextTick(callback);
|
||||||
@ -880,7 +880,17 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callb
|
|||||||
for (i = block.txs.length - 1; i >= 0; i--) {
|
for (i = block.txs.length - 1; i >= 0; i--) {
|
||||||
tx = block.txs[i];
|
tx = block.txs[i];
|
||||||
hash = tx.hash('hex');
|
hash = tx.hash('hex');
|
||||||
uniq = {};
|
|
||||||
|
if (self.options.indexTX) {
|
||||||
|
batch.del('t/t/' + hash);
|
||||||
|
if (self.options.indexAddress) {
|
||||||
|
addresses = tx.getAddresses();
|
||||||
|
for (j = 0; j < addresses.length; j++) {
|
||||||
|
address = addresses[j];
|
||||||
|
batch.del('t/a/' + address + '/' + hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (j = 0; j < tx.inputs.length; j++) {
|
for (j = 0; j < tx.inputs.length; j++) {
|
||||||
input = tx.inputs[j];
|
input = tx.inputs[j];
|
||||||
@ -893,17 +903,12 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callb
|
|||||||
|
|
||||||
if (self.options.indexAddress) {
|
if (self.options.indexAddress) {
|
||||||
address = input.getAddress();
|
address = input.getAddress();
|
||||||
|
|
||||||
if (address && !uniq[address] && !self.prune) {
|
|
||||||
uniq[address] = true;
|
|
||||||
batch.del('t/a/' + address + '/' + hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address)
|
if (address)
|
||||||
batch.put('u/a/' + address + '/' + key, DUMMY);
|
batch.put('u/a/' + address + '/' + key, DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
batch.put('u/t/' + key, input.coin.toRaw());
|
batch.put('u/t/' + key, input.coin.toRaw());
|
||||||
|
|
||||||
self.coinCache.set(key, input.coin);
|
self.coinCache.set(key, input.coin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -913,21 +918,18 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callb
|
|||||||
|
|
||||||
if (self.options.indexAddress) {
|
if (self.options.indexAddress) {
|
||||||
address = output.getAddress();
|
address = output.getAddress();
|
||||||
|
|
||||||
if (address && !uniq[address] && !self.prune) {
|
|
||||||
uniq[address] = true;
|
|
||||||
batch.del('t/a/' + address + '/' + hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address)
|
if (address)
|
||||||
batch.del('u/a/' + address + '/' + key);
|
batch.del('u/a/' + address + '/' + key);
|
||||||
}
|
}
|
||||||
|
|
||||||
batch.del('u/t/' + key);
|
batch.del('u/t/' + key);
|
||||||
|
|
||||||
self.coinCache.remove(key);
|
self.coinCache.remove(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
batch.del('b/u/' + block.hash('hex'));
|
||||||
|
|
||||||
self.emit('remove block', block);
|
self.emit('remove block', block);
|
||||||
|
|
||||||
return callback(null, block);
|
return callback(null, block);
|
||||||
@ -998,27 +1000,6 @@ ChainDB.prototype.fillHistory = function fillHistory(tx, callback) {
|
|||||||
if (tx.isCoinbase())
|
if (tx.isCoinbase())
|
||||||
return utils.asyncify(callback)(null, tx);
|
return utils.asyncify(callback)(null, tx);
|
||||||
|
|
||||||
if (this.prune) {
|
|
||||||
return utils.forEachSerial(tx.inputs, function(input, next) {
|
|
||||||
if (input.coin)
|
|
||||||
return next();
|
|
||||||
|
|
||||||
self._getPruneCoin(input.prevout.hash, input.prevout.index, function(err, coin) {
|
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
if (coin)
|
|
||||||
input.coin = coin;
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
}, function(err) {
|
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
return callback(null, tx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.forEachSerial(tx.inputs, function(input, next) {
|
utils.forEachSerial(tx.inputs, function(input, next) {
|
||||||
if (input.coin)
|
if (input.coin)
|
||||||
return next();
|
return next();
|
||||||
@ -1055,7 +1036,7 @@ ChainDB.prototype.getCoinsByAddress = function getCoinsByAddress(addresses, call
|
|||||||
|
|
||||||
addresses = utils.uniqs(addresses);
|
addresses = utils.uniqs(addresses);
|
||||||
|
|
||||||
utils.forEach(addresses, function(address, done) {
|
utils.forEachSerial(addresses, function(address, done) {
|
||||||
var iter = self.db.iterator({
|
var iter = self.db.iterator({
|
||||||
gte: 'u/a/' + address + '/',
|
gte: 'u/a/' + address + '/',
|
||||||
lte: 'u/a/' + address + '/~',
|
lte: 'u/a/' + address + '/~',
|
||||||
@ -1092,7 +1073,7 @@ ChainDB.prototype.getCoinsByAddress = function getCoinsByAddress(addresses, call
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
utils.forEach(ids, function(item, next) {
|
utils.forEachSerial(ids, function(item, next) {
|
||||||
var hash = item[0];
|
var hash = item[0];
|
||||||
var index = item[1];
|
var index = item[1];
|
||||||
self.getCoin(hash, index, function(err, coin) {
|
self.getCoin(hash, index, function(err, coin) {
|
||||||
@ -1167,7 +1148,7 @@ ChainDB.prototype.getTXByAddress = function getTXByAddress(addresses, callback)
|
|||||||
|
|
||||||
addresses = utils.uniqs(addresses);
|
addresses = utils.uniqs(addresses);
|
||||||
|
|
||||||
utils.forEach(addresses, function(address, done) {
|
utils.forEachSerial(addresses, function(address, done) {
|
||||||
var iter = self.db.iterator({
|
var iter = self.db.iterator({
|
||||||
gte: 't/a/' + address + '/',
|
gte: 't/a/' + address + '/',
|
||||||
lte: 't/a/' + address + '/~',
|
lte: 't/a/' + address + '/~',
|
||||||
@ -1209,7 +1190,7 @@ ChainDB.prototype.getTXByAddress = function getTXByAddress(addresses, callback)
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
utils.forEach(hashes, function(hash, next) {
|
utils.forEachSerial(hashes, function(hash, next) {
|
||||||
self.getTX(hash, function(err, tx) {
|
self.getTX(hash, function(err, tx) {
|
||||||
if (err)
|
if (err)
|
||||||
return next(err);
|
return next(err);
|
||||||
@ -1240,11 +1221,11 @@ ChainDB.prototype.getTX = function getTX(hash, callback) {
|
|||||||
var tx;
|
var tx;
|
||||||
|
|
||||||
this.db.get(key, function(err, data) {
|
this.db.get(key, function(err, data) {
|
||||||
if (err) {
|
if (err && err.type !== 'NotFoundError')
|
||||||
if (err.type === 'NotFoundError')
|
|
||||||
return callback();
|
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
|
||||||
|
if (!data)
|
||||||
|
return callback();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
tx = bcoin.tx.fromExtended(data);
|
tx = bcoin.tx.fromExtended(data);
|
||||||
@ -1353,7 +1334,7 @@ ChainDB.prototype._ensureHistory = function _ensureHistory(hash, callback) {
|
|||||||
if (!block)
|
if (!block)
|
||||||
return callback();
|
return callback();
|
||||||
|
|
||||||
return self.fillHistoryBlock(block, callback);
|
return self.fillUndoBlock(block, callback);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1429,6 +1410,70 @@ ChainDB.prototype.fillHistoryBlock = function fillHistoryBlock(block, callback)
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get coins necessary to be resurrected during a reorg.
|
||||||
|
* @param {Hash} hash
|
||||||
|
* @param {Function} callback - Returns [Error, Object].
|
||||||
|
*/
|
||||||
|
|
||||||
|
ChainDB.prototype.getUndoCoins = function getUndoCoins(hash, callback) {
|
||||||
|
var coins, p, coin, i, tx;
|
||||||
|
|
||||||
|
return this.db.get('b/u/' + hash, function(err, data) {
|
||||||
|
if (err && err.type !== 'NotFoundError')
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
if (!data)
|
||||||
|
return callback();
|
||||||
|
|
||||||
|
coins = [];
|
||||||
|
p = new BufferReader(data);
|
||||||
|
|
||||||
|
p.start();
|
||||||
|
|
||||||
|
while (p.left()) {
|
||||||
|
coin = new bcoin.coin(Parser.parseCoin(p, false));
|
||||||
|
coins.push(coin);
|
||||||
|
}
|
||||||
|
|
||||||
|
p.end();
|
||||||
|
|
||||||
|
return callback(null, coins);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill a block with coins necessary to be resurrected during a reorg.
|
||||||
|
* @param {Block} block
|
||||||
|
* @param {Function} callback - Returns [Error, {@link Block}].
|
||||||
|
*/
|
||||||
|
|
||||||
|
ChainDB.prototype.fillUndoBlock = function fillUndoBlock(block, callback) {
|
||||||
|
var i, j, k, tx, input;
|
||||||
|
|
||||||
|
return this.getUndoCoins(block.hash('hex'), function(err, coins) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
if (!coins)
|
||||||
|
return callback(null, block);
|
||||||
|
|
||||||
|
for (i = 0, k = 0; i < block.txs.length; i++) {
|
||||||
|
tx = block.txs[i];
|
||||||
|
|
||||||
|
if (tx.isCoinbase())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (j = 0; j < tx.inputs.length; j++) {
|
||||||
|
input = tx.inputs[j];
|
||||||
|
input.coin = coins[k++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback(null, block);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a block from the database (not filled with coins).
|
* Retrieve a block from the database (not filled with coins).
|
||||||
* @param {Hash} hash
|
* @param {Hash} hash
|
||||||
@ -1439,7 +1484,7 @@ ChainDB.prototype.getBlock = function getBlock(hash, callback) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
var key, block;
|
var key, block;
|
||||||
|
|
||||||
return this.getHash(hash, function(err, hash) {
|
return this.getBoth(hash, function(err, hash, height) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
@ -1456,47 +1501,17 @@ ChainDB.prototype.getBlock = function getBlock(hash, callback) {
|
|||||||
return callback();
|
return callback();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
block = bcoin.block.parseCompact(data);
|
block = bcoin.block.fromRaw(data);
|
||||||
|
block.setHeight(height);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return callback(e);
|
return callback(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
block.txs = [];
|
return callback(null, block);
|
||||||
|
|
||||||
utils.forEach(block.hashes, function(hash, next, i) {
|
|
||||||
self.getTX(hash, function(err, tx) {
|
|
||||||
if (err)
|
|
||||||
return next(err);
|
|
||||||
|
|
||||||
if (!tx)
|
|
||||||
return next(new Error('TX not found.'));
|
|
||||||
|
|
||||||
block.txs[i] = tx;
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
}, function(err) {
|
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
delete block.hashes;
|
|
||||||
block = new bcoin.block(block);
|
|
||||||
|
|
||||||
if (self.options.paranoid)
|
|
||||||
assert(block.hash('hex') === hash, 'Database is corrupt.');
|
|
||||||
|
|
||||||
return callback(null, block);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
ChainDB.prototype._getTX = function _getTX(hash, callback) {
|
|
||||||
if (hash instanceof bcoin.tx)
|
|
||||||
return callback(null, hash);
|
|
||||||
return this.getTX(hash);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a transaction is unspent (i.e. not yet _fully_ spent).
|
* Check whether a transaction is unspent (i.e. not yet _fully_ spent).
|
||||||
* @see https://bitcointalk.org/index.php?topic=67738.0
|
* @see https://bitcointalk.org/index.php?topic=67738.0
|
||||||
@ -1524,10 +1539,12 @@ ChainDB.prototype.isUnspentTX = function isUnspentTX(hash, callback) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
ChainDB.prototype.isSpentTX = function isSpentTX(hash, callback) {
|
ChainDB.prototype.isSpentTX = function isSpentTX(hash, callback) {
|
||||||
|
var iter;
|
||||||
|
|
||||||
if (hash.hash)
|
if (hash.hash)
|
||||||
hash = hash.hash('hex');
|
hash = hash.hash('hex');
|
||||||
|
|
||||||
var iter = this.db.iterator({
|
iter = this.db.iterator({
|
||||||
gte: 'u/t/' + hash,
|
gte: 'u/t/' + hash,
|
||||||
lte: 'u/t/' + hash + '~',
|
lte: 'u/t/' + hash + '~',
|
||||||
keys: true,
|
keys: true,
|
||||||
@ -1567,30 +1584,7 @@ ChainDB.prototype._pruneBlock = function _pruneBlock(block, batch, callback) {
|
|||||||
|
|
||||||
batch.put('b/q/' + futureHeight, block.hash());
|
batch.put('b/q/' + futureHeight, block.hash());
|
||||||
|
|
||||||
for (i = 0; i < block.txs.length; i++) {
|
key = 'b/q/' + pad32(block.height);
|
||||||
tx = block.txs[i];
|
|
||||||
|
|
||||||
for (j = 0; j < tx.inputs.length; j++) {
|
|
||||||
input = tx.inputs[j];
|
|
||||||
key = input.prevout.hash + '/' + input.prevout.index;
|
|
||||||
|
|
||||||
if (tx.isCoinbase())
|
|
||||||
break;
|
|
||||||
|
|
||||||
assert(input.coin);
|
|
||||||
|
|
||||||
batch.put('u/x/' + key, input.coin.toRaw());
|
|
||||||
batch.put('u/q/' + futureHeight + '/' + key, DUMMY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._pruneQueue(block, batch, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
ChainDB.prototype._pruneQueue = function _pruneQueue(block, batch, callback) {
|
|
||||||
var self = this;
|
|
||||||
var key = 'b/q/' + pad32(block.height);
|
|
||||||
var i;
|
|
||||||
|
|
||||||
this.db.get(key, function(err, hash) {
|
this.db.get(key, function(err, hash) {
|
||||||
if (err && err.type !== 'NotFoundError')
|
if (err && err.type !== 'NotFoundError')
|
||||||
@ -1601,87 +1595,11 @@ ChainDB.prototype._pruneQueue = function _pruneQueue(block, batch, callback) {
|
|||||||
|
|
||||||
hash = hash.toString('hex');
|
hash = hash.toString('hex');
|
||||||
|
|
||||||
self.db.get('b/b/' + hash, function(err, compact) {
|
batch.del(key);
|
||||||
if (err && err.type !== 'NotFoundError')
|
batch.del('b/b/' + hash);
|
||||||
return callback(err);
|
batch.del('b/u/' + hash);
|
||||||
|
|
||||||
batch.del(key);
|
return callback();
|
||||||
|
|
||||||
if (!compact)
|
|
||||||
return callback();
|
|
||||||
|
|
||||||
try {
|
|
||||||
compact = bcoin.block.parseCompact(compact);
|
|
||||||
} catch (e) {
|
|
||||||
return callback(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.del('b/b/' + hash);
|
|
||||||
|
|
||||||
for (i = 0; i < compact.hashes.length; i++)
|
|
||||||
batch.del('t/t/' + compact.hashes[i]);
|
|
||||||
|
|
||||||
self._pruneCoinQueue(block, batch, callback);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
ChainDB.prototype._pruneCoinQueue = function _pruneQueue(block, batch, callback) {
|
|
||||||
var iter = this.db.iterator({
|
|
||||||
gte: 'u/q/' + pad32(block.height),
|
|
||||||
lte: 'u/q/' + pad32(block.height) + '~',
|
|
||||||
keys: true,
|
|
||||||
values: false,
|
|
||||||
fillCache: false,
|
|
||||||
keyAsBuffer: false
|
|
||||||
});
|
|
||||||
|
|
||||||
(function next() {
|
|
||||||
iter.next(function(err, key, value) {
|
|
||||||
var parts, hash, index;
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
return iter.end(function() {
|
|
||||||
callback(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === undefined)
|
|
||||||
return iter.end(callback);
|
|
||||||
|
|
||||||
parts = key.split('/');
|
|
||||||
hash = parts[3];
|
|
||||||
index = +parts[4];
|
|
||||||
|
|
||||||
batch.del(key);
|
|
||||||
batch.del('u/x/' + hash + '/' + index);
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
};
|
|
||||||
|
|
||||||
ChainDB.prototype._getPruneCoin = function _getPruneCoin(hash, index, callback) {
|
|
||||||
var self = this;
|
|
||||||
var key = 'u/x/' + hash + '/' + index;
|
|
||||||
var coin;
|
|
||||||
|
|
||||||
this.db.get(key, function(err, data) {
|
|
||||||
if (err && err.type !== 'NotFoundError')
|
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
if (!data)
|
|
||||||
return self.getCoin(hash, index, callback);
|
|
||||||
|
|
||||||
try {
|
|
||||||
coin = bcoin.coin.fromRaw(data);
|
|
||||||
coin.hash = hash;
|
|
||||||
coin.index = index;
|
|
||||||
} catch (e) {
|
|
||||||
return callback(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null, coin);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -838,7 +838,7 @@ TX.prototype.fillCoins = function fillCoins(coins) {
|
|||||||
var total = 0;
|
var total = 0;
|
||||||
var inputs, txs, key, i, input;
|
var inputs, txs, key, i, input;
|
||||||
|
|
||||||
if (!Array.isArray(coins))
|
if ((coins instanceof bcoin.coin) || (coins instanceof bcoin.tx))
|
||||||
coins = [coins];
|
coins = [coins];
|
||||||
|
|
||||||
if (Array.isArray(coins)) {
|
if (Array.isArray(coins)) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user