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