cached headers
This commit is contained in:
parent
a5d9858457
commit
4954be9aa2
@ -9,6 +9,7 @@ var NULL = '0000000000000000000000000000000000000000000000000000000000000000';
|
|||||||
|
|
||||||
function BlockChain() {
|
function BlockChain() {
|
||||||
this.tip = NULL;
|
this.tip = NULL;
|
||||||
|
this.header = {};
|
||||||
this.work = {};
|
this.work = {};
|
||||||
this.work[NULL] = 0;
|
this.work[NULL] = 0;
|
||||||
this.height = {};
|
this.height = {};
|
||||||
@ -18,6 +19,7 @@ function BlockChain() {
|
|||||||
};
|
};
|
||||||
this.next = {};
|
this.next = {};
|
||||||
this.prev = {};
|
this.prev = {};
|
||||||
|
this.cachedHeaders = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockChain.NULL = NULL;
|
BlockChain.NULL = NULL;
|
||||||
@ -44,6 +46,7 @@ BlockChain.prototype.addData = function(header) {
|
|||||||
var prevHash = BufferUtil.reverse(header.prevHash).toString('hex');
|
var prevHash = BufferUtil.reverse(header.prevHash).toString('hex');
|
||||||
var hash = header.hash;
|
var hash = header.hash;
|
||||||
|
|
||||||
|
this.header[hash] = header;
|
||||||
this.work[hash] = this.work[prevHash] + getWork(header.bits);
|
this.work[hash] = this.work[prevHash] + getWork(header.bits);
|
||||||
this.prev[hash] = prevHash;
|
this.prev[hash] = prevHash;
|
||||||
};
|
};
|
||||||
@ -112,6 +115,7 @@ BlockChain.prototype.confirm = function(hash) {
|
|||||||
this.next[prevHash] = hash;
|
this.next[prevHash] = hash;
|
||||||
this.hashByHeight[height] = hash;
|
this.hashByHeight[height] = hash;
|
||||||
this.height[hash] = height;
|
this.height[hash] = height;
|
||||||
|
this.cachedHeaders.unshift(this.header[hash]);
|
||||||
};
|
};
|
||||||
|
|
||||||
BlockChain.prototype.unconfirm = function(hash) {
|
BlockChain.prototype.unconfirm = function(hash) {
|
||||||
@ -123,6 +127,7 @@ BlockChain.prototype.unconfirm = function(hash) {
|
|||||||
delete this.next[prevHash];
|
delete this.next[prevHash];
|
||||||
delete this.hashByHeight[height];
|
delete this.hashByHeight[height];
|
||||||
delete this.height[hash];
|
delete this.height[hash];
|
||||||
|
this.cachedHeaders.shift();
|
||||||
};
|
};
|
||||||
|
|
||||||
BlockChain.prototype.hasData = function(hash) {
|
BlockChain.prototype.hasData = function(hash) {
|
||||||
@ -178,4 +183,8 @@ BlockChain.prototype.getCurrentHeight = function() {
|
|||||||
return this.height[this.tip];
|
return this.height[this.tip];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BlockChain.prototype.getHeaders = function() {
|
||||||
|
return this.cachedHeaders;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = BlockChain;
|
module.exports = BlockChain;
|
||||||
|
|||||||
@ -128,6 +128,12 @@ BitcoreNode.prototype.initialize = function() {
|
|||||||
delete self.blockCache[deleteHash];
|
delete self.blockCache[deleteHash];
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.then(function() {
|
||||||
|
// Update header cache every 100 blocks
|
||||||
|
if(block.height % 100 === 0) {
|
||||||
|
return self.blockService.saveHeaders(self.blockchain.getHeaders())
|
||||||
|
}
|
||||||
|
})
|
||||||
.catch(function(error) {
|
.catch(function(error) {
|
||||||
self.stop(error);
|
self.stop(error);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -38,7 +38,8 @@ var Index = {
|
|||||||
height: 'bh-', // bh-<hash> -> height (-1 means disconnected)
|
height: 'bh-', // bh-<hash> -> height (-1 means disconnected)
|
||||||
tip: 'tip', // tip -> { hash: hex, height: int }, the latest tip
|
tip: 'tip', // tip -> { hash: hex, height: int }, the latest tip
|
||||||
work: 'wk-', // wk-<hash> -> amount of work for block
|
work: 'wk-', // wk-<hash> -> amount of work for block
|
||||||
header: 'header-' // header-<hash> -> JSON for block header
|
header: 'header-', // header-<hash> -> JSON for block header
|
||||||
|
headersCached: 'hc-'
|
||||||
};
|
};
|
||||||
_.extend(Index, {
|
_.extend(Index, {
|
||||||
getNextBlock: helper(Index.next),
|
getNextBlock: helper(Index.next),
|
||||||
@ -48,7 +49,7 @@ _.extend(Index, {
|
|||||||
getBlockByTs: function(block) {
|
getBlockByTs: function(block) {
|
||||||
return Index.timestamp + block.header.time;
|
return Index.timestamp + block.header.time;
|
||||||
},
|
},
|
||||||
getBlockHeader: helper(Index.header),
|
getBlockHeader: helper(Index.header)
|
||||||
});
|
});
|
||||||
|
|
||||||
function BlockService(opts) {
|
function BlockService(opts) {
|
||||||
@ -466,39 +467,63 @@ BlockService.prototype.getBlockchain = function() {
|
|||||||
var blockchain = new BlockChain();
|
var blockchain = new BlockChain();
|
||||||
var headers = [];
|
var headers = [];
|
||||||
|
|
||||||
console.log('Fetching headers from db...');
|
console.log('Fetching hashes from db...');
|
||||||
var fetchHeader = function(blockHash) {
|
|
||||||
if (blockHash === BlockChain.NULL) {
|
|
||||||
console.log('All headers fetched, total =', headers.length);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var headerKey = Index.getBlockHeader(blockHash);
|
|
||||||
return self.database.getAsync(headerKey)
|
|
||||||
.then(function(json) {
|
|
||||||
return bitcore.Block.BlockHeader.fromJSON(json);
|
|
||||||
})
|
|
||||||
.then(function(header) {
|
|
||||||
headers.push(header);
|
|
||||||
return fetchHeader(BufferUtil.reverse(header.prevHash).toString('hex'));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return self._getLatestHash()
|
return self.database.getAsync(Index.headersCached, {valueEncoding: 'json'})
|
||||||
.then(function(tip) {
|
.catch(function(err) {
|
||||||
if (!tip) {
|
if(err instanceof LevelUp.errors.NotFoundError) {
|
||||||
console.log('No tip found, syncing blockchain from genesis block');
|
return [];
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
console.log('Tip is', tip);
|
throw err;
|
||||||
return fetchHeader(tip)
|
})
|
||||||
.then(function() {
|
.then(function(cachedHeaders) {
|
||||||
while (headers.length !== 0) {
|
console.log(cachedHeaders.length + ' headers cached');
|
||||||
var header = headers.pop();
|
cachedHeaders = cachedHeaders.map(function(json) {
|
||||||
blockchain.proposeNewHeader(header);
|
return bitcore.Block.BlockHeader.fromJSON(json);
|
||||||
|
});
|
||||||
|
console.log('Cached headers parsed. Loading remaining headers from DB');
|
||||||
|
|
||||||
|
var fetchHeader = function(blockHash) {
|
||||||
|
if (blockHash === BlockChain.NULL) {
|
||||||
|
console.log('All headers fetched, total =', headers.length);
|
||||||
|
return Promise.resolve();
|
||||||
|
} else if(cachedHeaders[0] && cachedHeaders[0].hash === blockHash) {
|
||||||
|
headers = headers.concat(cachedHeaders);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
var headerKey = Index.getBlockHeader(blockHash);
|
||||||
|
return self.database.getAsync(headerKey)
|
||||||
|
.then(function(json) {
|
||||||
|
return bitcore.Block.BlockHeader.fromJSON(json);
|
||||||
|
})
|
||||||
|
.then(function(header) {
|
||||||
|
headers.push(header);
|
||||||
|
return fetchHeader(BufferUtil.reverse(header.prevHash).toString('hex'));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return self._getLatestHash()
|
||||||
|
.then(function(tip) {
|
||||||
|
if (!tip) {
|
||||||
|
console.log('No tip found, syncing blockchain from genesis block');
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return blockchain;
|
console.log('Tip is', tip);
|
||||||
});
|
|
||||||
|
return fetchHeader(tip)
|
||||||
|
.then(function() {
|
||||||
|
while (headers.length !== 0) {
|
||||||
|
var header = headers.pop();
|
||||||
|
blockchain.proposeNewHeader(header);
|
||||||
|
}
|
||||||
|
return blockchain;
|
||||||
|
});
|
||||||
|
})
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BlockService.prototype.saveHeaders = function(headers) {
|
||||||
|
return this.database.putAsync(Index.headersCached, JSON.stringify(headers));
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = BlockService;
|
module.exports = BlockService;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user