add block height to level DB

This commit is contained in:
Matias Alejo Garcia 2014-05-20 18:07:25 -03:00
parent 0410582d03
commit e61045f553
8 changed files with 71 additions and 55 deletions

View File

@ -107,7 +107,7 @@ Address.prototype.getUtxo = function(next) {
ts: txItem.ts, ts: txItem.ts,
scriptPubKey: scriptPubKey, scriptPubKey: scriptPubKey,
amount: txItem.value_sat / BitcoreUtil.COIN, amount: txItem.value_sat / BitcoreUtil.COIN,
confirmations: txItem.isConfirmed ? info.confirmations : 0, confirmations: txItem.height ? info.confirmations : 0,
}); });
} }
return a_c(err); return a_c(err);
@ -149,7 +149,7 @@ Address.prototype.update = function(next, notxlist) {
addSpend=1; addSpend=1;
} }
if (txItem.isConfirmed) { if (txItem.height) {
self.txApperances += add; self.txApperances += add;
self.totalReceivedSat += v; self.totalReceivedSat += v;
if (! txItem.spentTxId ) { if (! txItem.spentTxId ) {

View File

@ -1,11 +1,11 @@
'use strict'; 'use strict';
var imports = require('soop').imports(); var imports = require('soop').imports();
var ThisParent = imports.parent || require('events').EventEmitter; var ThisParent = imports.parent || require('events').EventEmitter;
var TIMESTAMP_PREFIX = 'bts-'; // b-ts-<ts> => <hash> var TIMESTAMP_PREFIX = 'bts-'; // bts-<ts> => <hash>
var PREV_PREFIX = 'bpr-'; // b-prev-<hash> => <prev_hash> var PREV_PREFIX = 'bpr-'; // bpr-<hash> => <prev_hash>
var NEXT_PREFIX = 'bne-'; // b-next-<hash> => <next_hash> var NEXT_PREFIX = 'bne-'; // bne-<hash> => <next_hash>
var MAIN_PREFIX = 'bma-'; // b-main-<hash> => 1/0 var MAIN_PREFIX = 'bma-'; // bma-<hash> => <height> (0 is unconnected)
var TIP = 'bti-'; // last block on the chain var TIP = 'bti-'; // bti = <hash>:<height> last block on the chain
var LAST_FILE_INDEX = 'file-'; // last processed file index var LAST_FILE_INDEX = 'file-'; // last processed file index
var MAX_OPEN_FILES = 500; var MAX_OPEN_FILES = 500;
@ -45,14 +45,14 @@ BlockDb.prototype.drop = function(cb) {
// adds a block. Does not update Next pointer in // adds a block. Does not update Next pointer in
// the block prev to the new block, nor TIP pointer // the block prev to the new block, nor TIP pointer
// //
BlockDb.prototype.add = function(b, cb) { BlockDb.prototype.add = function(b, height, cb) {
var self = this; var self = this;
var time_key = TIMESTAMP_PREFIX + var time_key = TIMESTAMP_PREFIX +
( b.time || Math.round(new Date().getTime() / 1000) ); ( b.time || Math.round(new Date().getTime() / 1000) );
return db.batch() return db.batch()
.put(time_key, b.hash) .put(time_key, b.hash)
.put(MAIN_PREFIX + b.hash, 1) .put(MAIN_PREFIX + b.hash, height)
.put(PREV_PREFIX + b.hash, b.previousblockhash) .put(PREV_PREFIX + b.hash, b.previousblockhash)
.write(function(err){ .write(function(err){
if (!err) { if (!err) {
@ -64,12 +64,16 @@ BlockDb.prototype.add = function(b, cb) {
BlockDb.prototype.getTip = function(cb) { BlockDb.prototype.getTip = function(cb) {
db.get(TIP, function(err, val) { db.get(TIP, function(err, val) {
return cb(err,val); if (!val) return cb();
var v = val.split(':');
return cb(err,v[0], parseInt(v[1]));
}); });
}; };
BlockDb.prototype.setTip = function(hash, cb) { BlockDb.prototype.setTip = function(hash, height, cb) {
db.put(TIP, hash, function(err) { //console.log('[BlockDb.js.75] TIP', hash, height); //TODO
db.put(TIP, hash + ':' + height, function(err) {
return cb(err); return cb(err);
}); });
}; };
@ -113,19 +117,20 @@ BlockDb.prototype.getNext = function(hash, cb) {
}); });
}; };
BlockDb.prototype.isMain = function(hash, cb) { BlockDb.prototype.getHeight = function(hash, cb) {
db.get(MAIN_PREFIX + hash, function(err, val) { db.get(MAIN_PREFIX + hash, function(err, val) {
if (err && err.notFound) { err = null; val = 0;} if (err && err.notFound) { err = null; val = 0;}
return cb(err,parseInt(val)); return cb(err,parseInt(val));
}); });
}; };
BlockDb.prototype.setMain = function(hash, isMain, cb) { BlockDb.prototype.setHeight = function(hash, height, cb) {
if (!isMain) console.log('\tNew orphan: %s',hash); if (!height) console.log('\tNew orphan: %s',hash);
db.put(MAIN_PREFIX + hash, isMain?1:0, function(err) { db.put(MAIN_PREFIX + hash, height, function(err) {
return cb(err); return cb(err);
}); });
}; };
BlockDb.prototype.setNext = function(hash, nextHash, cb) { BlockDb.prototype.setNext = function(hash, nextHash, cb) {
db.put(NEXT_PREFIX + hash, nextHash, function(err) { db.put(NEXT_PREFIX + hash, nextHash, function(err) {
return cb(err); return cb(err);
@ -184,10 +189,11 @@ BlockDb.prototype.fromHashWithInfo = function(hash, cb) {
Rpc.getBlock(hash, function(err, info) { Rpc.getBlock(hash, function(err, info) {
if (err || !info) return cb(err); if (err || !info) return cb(err);
self.isMain(hash, function(err, val) { //TODO can we get this from RPC .height?
self.getHeight(hash, function(err, height) {
if (err) return cb(err); if (err) return cb(err);
info.isMainChain = val ? true : false; info.isMainChain = height ? true : false;
return cb(null, { return cb(null, {
hash: hash, hash: hash,

View File

@ -408,13 +408,8 @@ HistoricSync.prototype.start = function(opts, next) {
self.syncedBlocks++; self.syncedBlocks++;
self.sync.storeTipBlock(blockInfo, self.allowReorgs, function(err) { self.sync.storeTipBlock(blockInfo, self.allowReorgs, function(err) {
if (err) return w_cb(self.setError(err)); if (err) return w_cb(self.setError(err));
setImmediate(function(){
self.sync.bDb.setTip(blockInfo.hash, function(err) { return w_cb(err);
if (err) return w_cb(self.setError(err));
setImmediate(function(){
return w_cb(err);
});
}); });
}); });
} }

View File

@ -76,6 +76,7 @@ PeerSync.prototype.handleTx = function(info) {
PeerSync.prototype.handleBlock = function(info) { PeerSync.prototype.handleBlock = function(info) {
var self = this; var self = this;
var block = info.message.block; var block = info.message.block;
console.log('[PeerSync.js.78:block:]',block); //TODO
var blockHash = bitcoreUtil.formatHashFull(block.calcHash()); var blockHash = bitcoreUtil.formatHashFull(block.calcHash());
console.log('[p2p_sync] Handle block: %s (allowReorgs: %s)', blockHash, self.allowReorgs); console.log('[p2p_sync] Handle block: %s (allowReorgs: %s)', blockHash, self.allowReorgs);

View File

@ -77,7 +77,7 @@ Sync.prototype.storeTipBlock = function(b, allowReorgs, cb) {
if (!b) return cb(); if (!b) return cb();
var self = this; var self = this;
var oldTip, oldNext, needReorg = false; var oldTip, oldNext, height, needReorg = false;
var newPrev = b.previousblockhash; var newPrev = b.previousblockhash;
async.series([ async.series([
@ -104,9 +104,14 @@ Sync.prototype.storeTipBlock = function(b, allowReorgs, cb) {
}, },
function(c) { function(c) {
if (!allowReorgs) return c(); if (!allowReorgs) return c();
self.bDb.getTip(function(err, val) { self.bDb.getTip(function(err, hash, h) {
oldTip = val; oldTip = hash;
if (oldTip && newPrev !== oldTip) needReorg = true; if (!hash) height = -1
else height = h || 0;
if (oldTip && newPrev !== oldTip) {
needReorg = true;
console.log('## REORG Triggered, tip mismatch');
}
return c(); return c();
}); });
}, },
@ -119,16 +124,16 @@ Sync.prototype.storeTipBlock = function(b, allowReorgs, cb) {
}); });
}, },
function(c) { function(c) {
self.bDb.add(b, c); self.bDb.add(b, height + 1, c);
}, },
function(c) { function(c) {
if (!needReorg) return c(); if (!needReorg) return c();
console.log('NEW TIP: %s NEED REORG (old tip: %s)', b.hash, oldTip); console.log('NEW TIP: %s #%d NEED REORG (old tip: %s)', b.hash, height + 1, oldTip);
self.processReorg(oldTip, oldNext, newPrev, c); self.processReorg(oldTip, oldNext, newPrev, height + 1, c);
}, },
function(c) { function(c) {
if (!allowReorgs) return c(); if (!allowReorgs) return c();
self.bDb.setTip(b.hash, function(err) { self.bDb.setTip(b.hash, height + 1, function(err) {
return c(err); return c(err);
}); });
}, },
@ -149,7 +154,7 @@ Sync.prototype.storeTipBlock = function(b, allowReorgs, cb) {
Sync.prototype.processReorg = function(oldTip, oldNext, newPrev, cb) { Sync.prototype.processReorg = function(oldTip, oldNext, newPrev, newHeight, cb) {
var self = this; var self = this;
var orphanizeFrom; var orphanizeFrom;
@ -170,7 +175,7 @@ Sync.prototype.processReorg = function(oldTip, oldNext, newPrev, cb) {
if (orphanizeFrom) return c(); if (orphanizeFrom) return c();
console.log('# Reorg Case 2)'); console.log('# Reorg Case 2)');
self.setBranchConnectedBackwards(newPrev, function(err, yHash, newYHashNext) { self.setBranchConnectedBackwards(newPrev, newHeight-1, function(err, yHash, newYHashNext) {
if (err) return c(err); if (err) return c(err);
self.bDb.getNext(yHash, function(err, yHashNext) { self.bDb.getNext(yHash, function(err, yHashNext) {
orphanizeFrom = yHashNext; orphanizeFrom = yHashNext;
@ -192,11 +197,12 @@ Sync.prototype.processReorg = function(oldTip, oldNext, newPrev, cb) {
}); });
}; };
Sync.prototype.setBlockMain = function(hash, isMain, cb) { //height = false => unconnnected
Sync.prototype.setBlockHeight = function(hash, height, cb) {
var self = this; var self = this;
self.bDb.setMain(hash, isMain, function(err) { self.bDb.setHeight(hash, height, function(err) {
if (err) return cb(err); if (err) return cb(err);
return self.txDb.handleBlockChange(hash, isMain, cb); return self.txDb.handleBlockChange(hash, height, cb);
}); });
}; };
@ -209,7 +215,7 @@ Sync.prototype.setBranchOrphan = function(fromHash, cb) {
return hashInterator; return hashInterator;
}, },
function(c) { function(c) {
self.setBlockMain(hashInterator, false, function(err) { self.setBlockHeight(hashInterator, false, function(err) {
if (err) return cb(err); if (err) return cb(err);
self.bDb.getNext(hashInterator, function(err, val) { self.bDb.getNext(hashInterator, function(err, val) {
hashInterator = val; hashInterator = val;
@ -219,7 +225,7 @@ Sync.prototype.setBranchOrphan = function(fromHash, cb) {
}, cb); }, cb);
}; };
Sync.prototype.setBranchConnectedBackwards = function(fromHash, cb) { Sync.prototype.setBranchConnectedBackwards = function(fromHash, initialHeight, cb) {
var self = this, var self = this,
hashInterator = fromHash, hashInterator = fromHash,
lastHash = fromHash, lastHash = fromHash,
@ -227,7 +233,7 @@ Sync.prototype.setBranchConnectedBackwards = function(fromHash, cb) {
async.doWhilst( async.doWhilst(
function(c) { function(c) {
self.setBlockMain(hashInterator, true, function(err) { self.setBlockMain(hashInterator, initialHeight--, function(err) {
if (err) return c(err); if (err) return c(err);
self.bDb.getPrev(hashInterator, function(err, val) { self.bDb.getPrev(hashInterator, function(err, val) {
if (err) return c(err); if (err) return c(err);

View File

@ -3,7 +3,7 @@
var imports = require('soop').imports(); var imports = require('soop').imports();
// blockHash -> txid mapping // blockHash -> txid mapping
var IN_BLK_PREFIX = 'txb-'; //txb-<txid>-<block> => 1/0 (connected or not) var IN_BLK_PREFIX = 'txb-'; //txb-<txid>-<block> => height/0 (connected:height or not connected:0)
// Only for orphan blocks // Only for orphan blocks
var FROM_BLK_PREFIX = 'tx-'; //tx-<block>-<txid> => 1 var FROM_BLK_PREFIX = 'tx-'; //tx-<block>-<txid> => 1
@ -306,7 +306,7 @@ TransactionDb.prototype.fromTxIdN = function(txid, n, confirmations, cb) {
/* /*
* If this TxID comes from an RPC request * If this TxID comes from an RPC request
* the .confirmations value from bitcoind is available * the .confirmations value from bitcoind is available
* so we could avoid checking if the input were double spented * so we could avoid checking if the input was double spent
* *
* This speed up address calculations by ~30% * This speed up address calculations by ~30%
* *
@ -337,30 +337,30 @@ TransactionDb.prototype.fromTxIdN = function(txid, n, confirmations, cb) {
TransactionDb.prototype.fillConfirmations = function(o, cb) { TransactionDb.prototype.fillConfirmations = function(o, cb) {
var self = this; var self = this;
self.isConfirmed(o.txid, function(err, is) { self.getHeight(o.txid, function(err, height) {
if (err) return cb(err); if (err) return cb(err);
o.isConfirmed = is; o.height = height;
if (!o.spentTxId) return cb(); if (!o.spentTxId) return cb();
if (o.multipleSpentAttempts) { if (o.multipleSpentAttempts) {
async.eachLimit(o.multipleSpentAttempts, CONCURRENCY, async.eachLimit(o.multipleSpentAttempts, CONCURRENCY,
function(oi, e_c) { function(oi, e_c) {
self.isConfirmed(oi.spentTxId, function(err, is) { self.getHeight(oi.spentTxId, function(err, height) {
if (err) return; if (err) return;
if (is) { if (height) {
o.spentTxId = oi.spentTxId; o.spentTxId = oi.spentTxId;
o.index = oi.index; o.index = oi.index;
o.spentIsConfirmed = 1; o.spentHeight = height;
} }
return e_c(); return e_c();
}); });
}, cb); }, cb);
} else { } else {
self.isConfirmed(o.spentTxId, function(err, is) { self.getHeight(o.spentTxId, function(err, height) {
if (err) return cb(err); if (err) return cb(err);
o.spentIsConfirmed = is; o.spentHeight = height;
return cb(); return cb();
}); });
} }
@ -598,7 +598,7 @@ TransactionDb.prototype.setConfirmation = function(txId, blockHash, confirmed, c
// This slowdown addr balance calculation by 100% // This slowdown addr balance calculation by 100%
TransactionDb.prototype.isConfirmed = function(txId, c) { TransactionDb.prototype.getHeight = function(txId, c) {
var k = IN_BLK_PREFIX + txId; var k = IN_BLK_PREFIX + txId;
var ret = false; var ret = false;
@ -607,7 +607,7 @@ TransactionDb.prototype.isConfirmed = function(txId, c) {
end: k + '~' end: k + '~'
}) })
.on('data', function(data) { .on('data', function(data) {
if (data.value === '1') ret = true; if (parseInt(data.value)>0) ret = true;
}) })
.on('error', function(err) { .on('error', function(err) {
return c(err); return c(err);
@ -617,7 +617,7 @@ TransactionDb.prototype.isConfirmed = function(txId, c) {
}); });
}; };
TransactionDb.prototype.handleBlockChange = function(hash, isMain, cb) { TransactionDb.prototype.handleBlockChange = function(hash, height, cb) {
var toChange = []; var toChange = [];
console.log('\tSearching Txs from block:' + hash); console.log('\tSearching Txs from block:' + hash);
@ -633,7 +633,7 @@ TransactionDb.prototype.handleBlockChange = function(hash, isMain, cb) {
toChange.push({ toChange.push({
key: k2 + ks[2] + '-' + ks[1], key: k2 + ks[2] + '-' + ks[1],
type: 'put', type: 'put',
value: isMain ? 1 : 0, value: height,
}); });
}) })
.on('error', function(err) { .on('error', function(err) {
@ -641,7 +641,7 @@ TransactionDb.prototype.handleBlockChange = function(hash, isMain, cb) {
}) })
.on('end', function(err) { .on('end', function(err) {
if (err) return cb(err); if (err) return cb(err);
console.log('\t%s %d Txs', isMain ? 'Confirming' : 'Invalidating', toChange.length); console.log('\t%s %d Txs', height ? 'Confirming' : 'Unconfirming', toChange.length);
db.batch(toChange, cb); db.batch(toChange, cb);
}); });
}; };

View File

@ -66,6 +66,7 @@ describe('Address utxo', function() {
var a = new Address(v.addr, txDb); var a = new Address(v.addr, txDb);
a.getUtxo(function(err, utxo) { a.getUtxo(function(err, utxo) {
console.log('[addr.js.68:utxo:]',utxo); //TODO
if (err) done(err); if (err) done(err);
assert.equal(v.addr, a.addrStr); assert.equal(v.addr, a.addrStr);
if (v.length) assert.equal(v.length, utxo.length, 'length: ' + utxo.length); if (v.length) assert.equal(v.length, utxo.length, 'length: ' + utxo.length);

View File

@ -5,5 +5,12 @@
"tx0id": "eeabc70063d3f266e190e8735bc4599c811d3a79d138da1364e88502069b029c", "tx0id": "eeabc70063d3f266e190e8735bc4599c811d3a79d138da1364e88502069b029c",
"tx0scriptPubKey": "76a9149e9f6515c70db535abdbbc983c7d8d1bff6c20cd88ac", "tx0scriptPubKey": "76a9149e9f6515c70db535abdbbc983c7d8d1bff6c20cd88ac",
"tx0amount": 0.38571339 "tx0amount": 0.38571339
},
{
"addr": "2N1pLkosf6o8Ciqs573iwwgVpuFS6NbNKx5",
"length": 1,
"tx0id": "eeabc70063d3f266e190e8735bc4599c811d3a79d138da1364e88502069b029c",
"tx0scriptPubKey": "76a9149e9f6515c70db535abdbbc983c7d8d1bff6c20cd88ac",
"tx0amount": 0.38571339
} }
] ]