memory leak fixed :D

This commit is contained in:
Manuel Araoz 2015-04-24 13:14:59 -03:00
parent 1986cb40ef
commit 8a52947637
3 changed files with 113 additions and 112 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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;
});
});