memory leak fixed :D
This commit is contained in:
parent
1986cb40ef
commit
8a52947637
@ -3,6 +3,7 @@
|
||||
var bitcore = require('bitcore');
|
||||
var $ = bitcore.util.preconditions;
|
||||
var _ = bitcore.deps._;
|
||||
var BufferUtil = bitcore.util.buffer;
|
||||
|
||||
var NULL = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
@ -12,7 +13,9 @@ function BlockChain() {
|
||||
this.work[NULL] = 0;
|
||||
this.height = {};
|
||||
this.height[NULL] = -1;
|
||||
this.hashByHeight = { '-1': NULL };
|
||||
this.hashByHeight = {
|
||||
'-1': NULL
|
||||
};
|
||||
this.next = {};
|
||||
this.prev = {};
|
||||
}
|
||||
@ -35,66 +38,74 @@ var getWork = function(bits) {
|
||||
return ((bits & 0xffffff) << (8 * (bytes - 3))) >>> 0;
|
||||
};
|
||||
|
||||
BlockChain.prototype.addData = function(block) {
|
||||
$.checkArgument(block instanceof bitcore.Block, 'Argument is not a Block instance');
|
||||
BlockChain.prototype.addData = function(header) {
|
||||
$.checkArgument(header instanceof bitcore.Block.BlockHeader, 'Argument is not a BlockHeader instance');
|
||||
|
||||
var prevHash = bitcore.util.buffer.reverse(block.header.prevHash).toString('hex');
|
||||
var prevHash = BufferUtil.reverse(header.prevHash).toString('hex');
|
||||
var hash = header.hash;
|
||||
|
||||
this.work[block.hash] = this.work[prevHash] + getWork(block.header.bits);
|
||||
this.prev[block.hash] = prevHash;
|
||||
this.work[hash] = this.work[prevHash] + getWork(header.bits);
|
||||
this.prev[hash] = prevHash;
|
||||
};
|
||||
|
||||
BlockChain.prototype.proposeNewBlock = function(block) {
|
||||
$.checkArgument(block instanceof bitcore.Block, 'Argument is not a Block instance');
|
||||
var prevHash = bitcore.util.buffer.reverse(block.header.prevHash).toString('hex');
|
||||
BlockChain.prototype._appendNewBlock = function(hash) {
|
||||
var toUnconfirm = [];
|
||||
var toConfirm = [];
|
||||
var self = this;
|
||||
|
||||
if (_.isUndefined(this.work[prevHash])) {
|
||||
throw new Error('No previous data to estimate work');
|
||||
var pointer = hash;
|
||||
while (_.isUndefined(this.height[pointer])) {
|
||||
toConfirm.push(pointer);
|
||||
pointer = this.prev[pointer];
|
||||
}
|
||||
this.addData(block);
|
||||
var commonAncestor = pointer;
|
||||
|
||||
if (this.work[block.hash] > this.work[this.tip]) {
|
||||
pointer = this.tip;
|
||||
while (pointer !== commonAncestor) {
|
||||
toUnconfirm.push(pointer);
|
||||
pointer = this.prev[pointer];
|
||||
}
|
||||
|
||||
var toUnconfirm = [];
|
||||
var toConfirm = [];
|
||||
var commonAncestor;
|
||||
toConfirm.reverse();
|
||||
toUnconfirm.map(function(hash) {
|
||||
self.unconfirm(hash);
|
||||
});
|
||||
toConfirm.map(function(hash) {
|
||||
self.confirm(hash);
|
||||
});
|
||||
return {
|
||||
unconfirmed: toUnconfirm,
|
||||
confirmed: toConfirm
|
||||
};
|
||||
};
|
||||
|
||||
var pointer = block.hash;
|
||||
while (_.isUndefined(this.height[pointer])) {
|
||||
toConfirm.push(pointer);
|
||||
pointer = this.prev[pointer];
|
||||
}
|
||||
commonAncestor = pointer;
|
||||
BlockChain.prototype.proposeNewHeader = function(header) {
|
||||
$.checkArgument(header instanceof bitcore.Block.BlockHeader, 'Argument is not a BlockHeader instance');
|
||||
var prevHash = BufferUtil.reverse(header.prevHash).toString('hex');
|
||||
var hash = header.hash;
|
||||
|
||||
pointer = this.tip;
|
||||
while (pointer !== commonAncestor) {
|
||||
toUnconfirm.push(pointer);
|
||||
pointer = this.prev[pointer];
|
||||
}
|
||||
|
||||
toConfirm.reverse();
|
||||
|
||||
var self = this;
|
||||
toUnconfirm.map(function(hash) {
|
||||
self.unconfirm(hash);
|
||||
});
|
||||
toConfirm.map(function(hash) {
|
||||
self.confirm(hash);
|
||||
});
|
||||
return {
|
||||
unconfirmed: toUnconfirm,
|
||||
confirmed: toConfirm
|
||||
};
|
||||
$.checkState(this.hasData(prevHash), 'No previous data to estimate work');
|
||||
this.addData(header);
|
||||
var work = this.work[hash];
|
||||
var tipWork = this.work[this.tip];
|
||||
$.checkState(!_.isUndefined(work), 'No work found for ' + hash);
|
||||
$.checkState(!_.isUndefined(tipWork), 'No work found for tip ' + this.tip);
|
||||
if (work > tipWork) {
|
||||
return this._appendNewBlock(hash);
|
||||
}
|
||||
return {
|
||||
unconfirmed: [],
|
||||
confirmed: []
|
||||
};
|
||||
};
|
||||
BlockChain.prototype.proposeNewBlock = function(block) {
|
||||
$.checkArgument(block instanceof bitcore.Block, 'Argument is not a Block instance');
|
||||
return this.proposeNewHeader(block.header);
|
||||
};
|
||||
|
||||
BlockChain.prototype.confirm = function(hash) {
|
||||
var prevHash = this.prev[hash];
|
||||
$.checkState(prevHash === this.tip);
|
||||
$.checkState(prevHash === this.tip, 'Attempting to confirm a non-contiguous block.');
|
||||
|
||||
this.tip = hash;
|
||||
var height = this.height[prevHash] + 1;
|
||||
@ -105,7 +116,7 @@ BlockChain.prototype.confirm = function(hash) {
|
||||
|
||||
BlockChain.prototype.unconfirm = function(hash) {
|
||||
var prevHash = this.prev[hash];
|
||||
$.checkState(hash === this.tip);
|
||||
$.checkState(hash === this.tip, 'Attempting to unconfirm a non-tip block');
|
||||
|
||||
this.tip = prevHash;
|
||||
var height = this.height[hash];
|
||||
@ -114,26 +125,6 @@ BlockChain.prototype.unconfirm = function(hash) {
|
||||
delete this.height[hash];
|
||||
};
|
||||
|
||||
BlockChain.prototype.getBlockLocator = function() {
|
||||
$.checkState(this.tip);
|
||||
$.checkState(!_.isUndefined(this.height[this.tip]));
|
||||
|
||||
var result = [];
|
||||
var currentHeight = this.height[this.tip];
|
||||
var exponentialBackOff = 1;
|
||||
for (var i = 0; i < 10; i++) {
|
||||
if (currentHeight >= 0) {
|
||||
result.push(this.hashByHeight[currentHeight--]);
|
||||
}
|
||||
}
|
||||
while (currentHeight > 0) {
|
||||
result.push(this.hashByHeight[currentHeight]);
|
||||
currentHeight -= exponentialBackOff;
|
||||
exponentialBackOff *= 2;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
BlockChain.prototype.hasData = function(hash) {
|
||||
return !_.isUndefined(this.work[hash]);
|
||||
};
|
||||
@ -163,4 +154,24 @@ BlockChain.prototype.toJSON = function() {
|
||||
return JSON.stringify(this.toObject());
|
||||
};
|
||||
|
||||
BlockChain.prototype.getBlockLocator = function() {
|
||||
$.checkState(this.tip);
|
||||
$.checkState(!_.isUndefined(this.height[this.tip]));
|
||||
|
||||
var result = [];
|
||||
var currentHeight = this.height[this.tip];
|
||||
var exponentialBackOff = 1;
|
||||
for (var i = 0; i < 10; i++) {
|
||||
if (currentHeight >= 0) {
|
||||
result.push(this.hashByHeight[currentHeight--]);
|
||||
}
|
||||
}
|
||||
while (currentHeight > 0) {
|
||||
result.push(this.hashByHeight[currentHeight]);
|
||||
currentHeight -= exponentialBackOff;
|
||||
exponentialBackOff *= 2;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
module.exports = BlockChain;
|
||||
|
||||
@ -94,7 +94,6 @@ BitcoreNode.prototype.initialize = function() {
|
||||
var delta = block.height - prevHeight;
|
||||
prevHeight = block.height;
|
||||
console.log(block.id, block.height, 'vel', delta * 1000 / statTimer, 'b/s');
|
||||
console.log('cache', Object.keys(self.blockCache).length);
|
||||
}, statTimer);
|
||||
|
||||
this.bus.register(bitcore.Block, function(block) {
|
||||
@ -158,7 +157,7 @@ BitcoreNode.prototype.start = function() {
|
||||
|
||||
console.log('getting blockchain');
|
||||
this.blockService.getBlockchain().then(function(blockchain) {
|
||||
console.log('got blockchain', _.isUndefined(blockchain));
|
||||
console.log('got blockchain', !!blockchain);
|
||||
if (!blockchain) {
|
||||
self.blockchain = new BlockChain();
|
||||
self.bus.process(genesis);
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
var config = require('config');
|
||||
var LevelUp = require('levelup');
|
||||
var Promise = require('bluebird');
|
||||
var RPC = require('bitcoind-rpc');
|
||||
var TransactionService = require('./transaction');
|
||||
var bitcore = require('bitcore');
|
||||
var Transaction = bitcore.Transaction;
|
||||
var config = require('config');
|
||||
var BufferUtil = bitcore.util.buffer;
|
||||
|
||||
var errors = require('../errors');
|
||||
var BlockChain = require('../blockchain');
|
||||
@ -41,7 +42,8 @@ var Index = {
|
||||
next: 'nxt-', // nxt-<hash> -> hash for the next block in the main chain that is a child
|
||||
height: 'bh-', // bh-<hash> -> height (-1 means disconnected)
|
||||
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
|
||||
};
|
||||
_.extend(Index, {
|
||||
getNextBlock: helper(Index.next),
|
||||
@ -50,7 +52,8 @@ _.extend(Index, {
|
||||
getBlockWork: helper(Index.work),
|
||||
getBlockByTs: function(block) {
|
||||
return Index.timestamp + block.header.time;
|
||||
}
|
||||
},
|
||||
getBlockHeader: helper(Index.header),
|
||||
});
|
||||
|
||||
function BlockService(opts) {
|
||||
@ -240,6 +243,8 @@ BlockService.prototype.confirm = function(block, ops) {
|
||||
|
||||
//console.log(0);
|
||||
return Promise.try(function() {
|
||||
//console.log(0.5);
|
||||
self._setHeader(ops, block);
|
||||
//console.log(1);
|
||||
self._setNextBlock(ops, block.header.prevHash, block);
|
||||
|
||||
@ -271,6 +276,14 @@ BlockService.prototype.confirm = function(block, ops) {
|
||||
});
|
||||
};
|
||||
|
||||
BlockService.prototype._setHeader = function(ops, block) {
|
||||
ops.push({
|
||||
type: 'put',
|
||||
key: Index.getBlockHeader(block.hash),
|
||||
value: block.header.toJSON(),
|
||||
});
|
||||
};
|
||||
|
||||
BlockService.prototype._setNextBlock = function(ops, prevBlockHash, block) {
|
||||
if (bitcore.util.buffer.isBuffer(prevBlockHash)) {
|
||||
prevBlockHash = bitcore.util.buffer.reverse(prevBlockHash).toString('hex');
|
||||
@ -357,15 +370,13 @@ BlockService.prototype._setBlockByTs = function(ops, block) {
|
||||
* @return {Promise<Block>} a promise of the same block, for chaining
|
||||
*/
|
||||
BlockService.prototype.unconfirm = function(block, ops) {
|
||||
|
||||
ops = ops || [];
|
||||
var self = this;
|
||||
|
||||
return Promise.try(function() {
|
||||
|
||||
self._removeNextBlock(ops, block.header.prevHash, block);
|
||||
|
||||
self._unsetBlockHeight(ops, block, block.height);
|
||||
|
||||
self._dropBlockByTs(ops, block);
|
||||
|
||||
return Promise.all(block.transactions.map(function(transaction) {
|
||||
@ -373,21 +384,19 @@ BlockService.prototype.unconfirm = function(block, ops) {
|
||||
}));
|
||||
|
||||
}).then(function() {
|
||||
|
||||
return self.database.batchAsync(ops);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
BlockService.prototype._removeNextBlock = function(ops, prevHash, block) {
|
||||
|
||||
if (bitcore.util.buffer.isBuffer(prevBlockHash)) {
|
||||
prevBlockHash = bitcore.util.buffer.reverse(prevBlockHash).toString('hex');
|
||||
if (bitcore.util.buffer.isBuffer(prevHash)) {
|
||||
prevHash = bitcore.util.buffer.reverse(prevHash).toString('hex');
|
||||
}
|
||||
|
||||
ops.push({
|
||||
type: 'del',
|
||||
key: Index.getNextBlock(prevBlockHash)
|
||||
key: Index.getNextBlock(prevHash)
|
||||
});
|
||||
ops.push({
|
||||
type: 'del',
|
||||
@ -395,7 +404,7 @@ BlockService.prototype._removeNextBlock = function(ops, prevHash, block) {
|
||||
});
|
||||
};
|
||||
|
||||
BlockService.prototype._unsetBlockHeight = function(ops, block, height) {
|
||||
BlockService.prototype._unsetBlockHeight = function(ops, block) {
|
||||
ops.push({
|
||||
type: 'del',
|
||||
key: Index.getBlockHeight(block)
|
||||
@ -443,44 +452,23 @@ BlockService.prototype.getBlockchain = function() {
|
||||
var self = this;
|
||||
|
||||
var blockchain = new BlockChain();
|
||||
console.log(process.memoryUsage().heapTotal / 1024 / 1024, 'mb used');
|
||||
var headers = [];
|
||||
|
||||
var amount = 0;
|
||||
var limit = 100;
|
||||
var fetchBlock = function(blockHash) {
|
||||
//console.log(process.memoryUsage().heapTotal / 1024 / 1024);
|
||||
console.log('Fetching headers from db...');
|
||||
var fetchHeader = function(blockHash) {
|
||||
if (blockHash === BlockChain.NULL) {
|
||||
console.log('All headers fetched, total =', headers.length);
|
||||
console.log(process.memoryUsage().heapTotal / 1024 / 1024, 'mb used');
|
||||
return;
|
||||
}
|
||||
amount += 1;
|
||||
var prevKey = Index.getPreviousBlock(blockHash);
|
||||
var heightKey = Index.getBlockHeight(blockHash);
|
||||
var workKey = Index.getBlockWork(blockHash);
|
||||
|
||||
return self.database.getAsync(prevKey)
|
||||
.then(function(prevHash) {
|
||||
blockchain.prev[blockHash] = prevHash;
|
||||
blockchain.next[prevHash] = blockHash;
|
||||
var headerKey = Index.getBlockHeader(blockHash);
|
||||
return self.database.getAsync(headerKey)
|
||||
.then(function(json) {
|
||||
return bitcore.Block.BlockHeader.fromJSON(json);
|
||||
})
|
||||
.then(function() {
|
||||
return self.database.getAsync(heightKey)
|
||||
.then(function(height) {
|
||||
blockchain.height[blockHash] = +height;
|
||||
blockchain.hashByHeight[height] = blockHash;
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
if (amount >= limit) {
|
||||
return;
|
||||
}
|
||||
return self.database.getAsync(workKey)
|
||||
.then(function(work) {
|
||||
blockchain.work[blockHash] = work;
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
return fetchBlock(blockchain.prev[blockHash]);
|
||||
.then(function(header) {
|
||||
headers.push(header);
|
||||
return fetchHeader(BufferUtil.reverse(header.prevHash).toString('hex'));
|
||||
});
|
||||
};
|
||||
|
||||
@ -488,12 +476,15 @@ BlockService.prototype.getBlockchain = function() {
|
||||
.then(function(tip) {
|
||||
if (!tip) {
|
||||
console.log('No tip found');
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
console.log('Tip is', tip);
|
||||
blockchain.tip = tip;
|
||||
return fetchBlock(tip)
|
||||
return fetchHeader(tip)
|
||||
.then(function() {
|
||||
while (headers.length !== 0) {
|
||||
var header = headers.pop();
|
||||
blockchain.proposeNewHeader(header);
|
||||
}
|
||||
return blockchain;
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user