simple coin views.

This commit is contained in:
Christopher Jeffrey 2016-05-26 23:47:54 -07:00
parent 2884a794c3
commit 5074e3100d
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

@ -92,8 +92,8 @@ function ChainDB(chain, options) {
// Average number of outputs per tx: 2.2 // Average number of outputs per tx: 2.2
// Average size of outputs per tx: 74b // Average size of outputs per tx: 74b
// Average number of txs: 2300 // Average number of txs: 2300
// Key size: 68b (* 2) // Key size: 66b (* 2)
this.coinWindow = ((165 * 1024 + 5000 * 9) + (5000 * 68 * 2)) * 5; this.coinWindow = ((165 * 1024 + 2300 * 4) + (2300 * 66 * 2)) * 5;
this.coinCache = new bcoin.lru(this.coinWindow); this.coinCache = new bcoin.lru(this.coinWindow);
this.cacheHash = new bcoin.lru(this.cacheWindow, 1); this.cacheHash = new bcoin.lru(this.cacheWindow, 1);
@ -782,7 +782,7 @@ 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 undo = new BufferWriter(); var undo = new BufferWriter();
var i, j, tx, input, output, key, addresses, address, hash, coin; var i, j, tx, input, output, key, addresses, address, hash, view, coins, raw;
if (this.options.spv) { if (this.options.spv) {
self.emit('add block', block); self.emit('add block', block);
@ -830,31 +830,38 @@ ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) {
batch.del('C/' + address + '/' + key); batch.del('C/' + address + '/' + key);
} }
batch.del('c/' + key);
Framer.coin(input.coin, false, undo); Framer.coin(input.coin, false, undo);
self.coinCache.remove(key); block.view.spend(input.prevout.hash, input.prevout.index);
} }
for (j = 0; j < tx.outputs.length; j++) { for (j = 0; j < tx.outputs.length; j++) {
output = tx.outputs[j]; output = tx.outputs[j];
key = hash + '/' + j;
if (output.script.isUnspendable()) if (output.script.isUnspendable())
continue; continue;
coin = bcoin.coin.fromTX(tx, j).toRaw();
if (self.options.indexAddress) { if (self.options.indexAddress) {
address = output.getHash(); address = output.getHash();
if (address) if (address)
batch.put('C/' + address + '/' + key, DUMMY); batch.put('C/' + address + '/' + key, DUMMY);
} }
}
batch.put('c/' + key, coin); block.view.add(tx.toCoins());
}
self.coinCache.set(key, coin); view = block.view.toArray();
for (i = 0; i < view.length; i++) {
coins = view[i];
if (coins.count() === 0) {
batch.del('c/' + coins.hash);
self.coinCache.remove(coins.hash);
} else {
raw = coins.toRaw();
batch.put('c/' + coins.hash, raw);
self.coinCache.set(coins.hash, raw);
} }
} }
@ -880,7 +887,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, addresses, address, hash, coin; var i, j, tx, input, output, key, addresses, address, hash, view, coins, raw;
if (this.options.spv) if (this.options.spv)
return utils.nextTick(callback); return utils.nextTick(callback);
@ -922,13 +929,11 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callb
batch.put('C/' + address + '/' + key, DUMMY); batch.put('C/' + address + '/' + key, DUMMY);
} }
coin = input.coin.toRaw(); block.view.addCoin(input.coin);
batch.put('c/' + key, coin);
self.coinCache.set(key, coin);
} }
block.view.add(tx.toCoins());
for (j = 0; j < tx.outputs.length; j++) { for (j = 0; j < tx.outputs.length; j++) {
output = tx.outputs[j]; output = tx.outputs[j];
key = hash + '/' + j; key = hash + '/' + j;
@ -942,9 +947,21 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callb
batch.del('C/' + address + '/' + key); batch.del('C/' + address + '/' + key);
} }
batch.del('c/' + key); block.view.spend(hash, j);
}
}
self.coinCache.remove(key); view = block.view.toArray();
for (i = 0; i < view.length; i++) {
coins = view[i];
if (coins.count() === 0) {
batch.del('c/' + coins.hash);
self.coinCache.remove(coins.hash);
} else {
raw = coins.toRaw();
batch.put('c/' + coins.hash, raw);
self.coinCache.set(coins.hash, raw);
} }
} }
@ -1048,29 +1065,62 @@ ChainDB.prototype.fillHistory = function fillHistory(tx, callback) {
*/ */
ChainDB.prototype.getCoin = function getCoin(hash, index, callback) { ChainDB.prototype.getCoin = function getCoin(hash, index, callback) {
var self = this;
var key = hash + '/' + index;
var coin; var coin;
coin = this.coinCache.get(key); this.getCoins(hash, true, function(err, coins) {
if (coin) { if (err)
callback = utils.asyncify(callback); return callback(err);
if (!coins)
return callback();
try { try {
coin = bcoin.coin.fromRaw(coin); coin = bcoin.coins.parseCoin(coins, hash, index);
coin.hash = hash;
coin.index = index;
} catch (e) { } catch (e) {
return callback(e); return callback(e);
} }
return callback(null, coin); return callback(null, coin);
});
};
/**
* Get coins (unspents only).
* @param {Hash} hash
* @param {Function} callback - Returns [Error, {@link Coins}].
*/
ChainDB.prototype.getCoins = function getCoins(hash, raw, callback) {
var self = this;
var coins;
if (!callback) {
callback = raw;
raw = false;
} }
this.db.fetch('c/' + key, function(data) { coins = this.coinCache.get(hash);
var coin = bcoin.coin.fromRaw(data);
coin.hash = hash; if (coins) {
coin.index = index; callback = utils.asyncify(callback);
self.coinCache.set(key, data);
return coin; if (raw)
return callback(null, coins);
try {
coins = bcoin.coins.fromRaw(coins, hash);
} catch (e) {
return callback(e);
}
return callback(null, coins);
}
this.db.fetch('c/' + hash, function(data) {
self.coinCache.set(hash, data);
if (raw)
return data;
return bcoin.coins.fromRaw(data, hash);
}, callback); }, callback);
}; };
@ -1128,27 +1178,32 @@ ChainDB.prototype.getCoinsByAddress = function getCoinsByAddress(addresses, call
utils.forEachSerial(addresses, function(address, next) { utils.forEachSerial(addresses, function(address, next) {
address = bcoin.address.getHash(address); address = bcoin.address.getHash(address);
if (!address) if (!address)
return next(); return next();
self.db.lookup({
self.db.iterate({
gte: 'C/' + address, gte: 'C/' + address,
lte: 'C/' + address + '~', lte: 'C/' + address + '~',
transform: function(key) { transform: function(key) {
key = key.split('/'); key = key.split('/');
return 'c/' + key[2] + '/' + key[3]; return [key[2], +key[3]];
},
parse: function(data, key) {
var coin = bcoin.coin.fromRaw(data);
var hash = key.split('/');
coin.hash = hash[1];
coin.index = +hash[2];
return coin;
} }
}, function(err, coin) { }, function(err, keys) {
if (err) if (err)
return next(err); return next(err);
coins = coins.concat(coin);
next(); utils.forEachSerial(keys, function(key, next) {
self.getCoin(key[0], key[1], function(err, coin) {
if (err)
return callback(err);
if (coin)
coins.push(coin);
return next();
});
}, next);
}); });
}, function(err) { }, function(err) {
if (err) if (err)
@ -1284,9 +1339,21 @@ ChainDB.prototype._ensureHistory = function _ensureHistory(hash, callback) {
*/ */
ChainDB.prototype.fillBlock = function fillBlock(block, callback) { ChainDB.prototype.fillBlock = function fillBlock(block, callback) {
var self = this;
var view = new bcoin.coinview();
var coins, spent, i, tx, hash, j, input, key; var coins, spent, i, tx, hash, j, input, key;
return this.fillCoins(block.txs, function(err) { utils.forEachSerial(block.getPrevout(), function(prevout, next) {
self.getCoins(prevout, function(err, coins) {
if (err)
return next(err);
if (coins)
view.add(coins);
next();
});
}, function(err) {
if (err) if (err)
return callback(err); return callback(err);
@ -1297,6 +1364,8 @@ ChainDB.prototype.fillBlock = function fillBlock(block, callback) {
tx = block.txs[i]; tx = block.txs[i];
hash = tx.hash('hex'); hash = tx.hash('hex');
view.fill(tx);
for (j = 0; j < tx.inputs.length; j++) { for (j = 0; j < tx.inputs.length; j++) {
input = tx.inputs[j]; input = tx.inputs[j];
key = input.prevout.hash + '/' + input.prevout.index; key = input.prevout.hash + '/' + input.prevout.index;
@ -1318,6 +1387,8 @@ ChainDB.prototype.fillBlock = function fillBlock(block, callback) {
coins[hash + '/' + j] = bcoin.coin.fromTX(tx, j); coins[hash + '/' + j] = bcoin.coin.fromTX(tx, j);
} }
block.view = view;
return callback(null, block); return callback(null, block);
}); });
}; };
@ -1350,30 +1421,36 @@ ChainDB.prototype.getUndoCoins = function getUndoCoins(hash, callback) {
*/ */
ChainDB.prototype.fillHistoryBlock = function fillHistoryBlock(block, callback) { ChainDB.prototype.fillHistoryBlock = function fillHistoryBlock(block, callback) {
var self = this;
var i, j, k, tx, input; var i, j, k, tx, input;
return this.getUndoCoins(block.hash('hex'), function(err, coins) { return this.fillBlock(block, function(err) {
if (err) if (err)
return callback(err); return callback(err);
if (!coins) return self.getUndoCoins(block.hash('hex'), function(err, coins) {
return callback(null, block); if (err)
return callback(err);
for (i = 0, k = 0; i < block.txs.length; i++) { if (!coins)
tx = block.txs[i]; return callback(null, block);
if (tx.isCoinbase()) for (i = 0, k = 0; i < block.txs.length; i++) {
continue; tx = block.txs[i];
for (j = 0; j < tx.inputs.length; j++) { if (tx.isCoinbase())
input = tx.inputs[j]; continue;
input.coin = coins[k++];
input.coin.hash = input.prevout.hash; for (j = 0; j < tx.inputs.length; j++) {
input.coin.index = input.prevout.index; input = tx.inputs[j];
input.coin = coins[k++];
input.coin.hash = input.prevout.hash;
input.coin.index = input.prevout.index;
}
} }
}
return callback(null, block); return callback(null, block);
});
}); });
}; };
@ -1427,32 +1504,13 @@ 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');
iter = this.db.iterator({ this.getCoins(hash, function(err, coins) {
gte: 'c/' + hash, if (err)
lte: 'c/' + hash + '~', return callback(err);
keys: true, return callback(null, !coins);
values: false,
fillCache: false,
keyAsBuffer: false
});
iter.next(function(err, key, value) {
if (err) {
return iter.end(function() {
callback(err);
});
}
iter.end(function(err) {
if (err)
return callback(err);
return callback(null, key === undefined);
});
}); });
}; };