Restore blockchain from database

This commit is contained in:
eordano 2015-04-09 18:53:03 -03:00
parent db17accf98
commit 7102e63933
3 changed files with 111 additions and 44 deletions

View File

@ -4,21 +4,19 @@ var bitcore = require('bitcore');
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
var NULL = '0000000000000000000000000000000000000000000000000000000000000000';
function BlockChain() {
this.tip = '0000000000000000000000000000000000000000000000000000000000000000';
this.work = {
'0000000000000000000000000000000000000000000000000000000000000000': 0
};
this.height = {
'0000000000000000000000000000000000000000000000000000000000000000': -1
};
this.hashByHeight = {
'-1': '0000000000000000000000000000000000000000000000000000000000000000'
};
this.tip = NULL;
this.work = { NULL: 0 };
this.height = { NULL: -1 };
this.hashByHeight = { '-1': NULL };
this.next = {};
this.prev = {};
}
BlockChain.NULL = NULL;
BlockChain.fromObject = function(obj) {
var blockchain = new BlockChain();
blockchain.tip = obj.tip;

View File

@ -61,11 +61,14 @@ var BitcoreNode = function(bus, networkMonitor, blockService, transactionService
return;
}
var blockchainChanges = self.blockchain.proposeNewBlock(block);
var height = self.blockchain.height[block.id];
if (height % 100 === 0) {
console.log('block', block.id, 'height', height);
// Annotate block with extra data from the chain
block.height = self.blockchain.height[block.id];
block.work = self.blockchain.work[block.id];
if (block.height % 100 === 0) {
console.log('block', block.id, 'height', block.height);
}
block.height = height;
return Promise.each(blockchainChanges.unconfirmed, function(hash) {
return self.blockService.unconfirm(self.blockCache[hash]);

View File

@ -9,6 +9,7 @@ var Transaction = bitcore.Transaction;
var config = require('config');
var errors = require('../errors');
var BlockChain = require('../blockchain');
var $ = bitcore.util.preconditions;
var JSUtil = bitcore.util.js;
@ -40,7 +41,7 @@ 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
blockchain: 'chain'
work: 'wk-' // wk-<hash> -> amount of work for block
};
_.extend(Index, {
getNextBlock: helper(Index.next),
@ -220,29 +221,20 @@ BlockService.prototype.onBlock = function(block) {
});
};
/**
* Save a new block
*
* @param {bitcore.Block} block
* @return {Promise<Block>} a promise of the same block, for chaining
*/
BlockService.prototype.unconfirm = function(block) {
// TODO: unconfirm previous tip, confirm new tip.
};
/**
* Set a block as the current tip of the blockchain
*
* @param {bitcore.Block} block
* @param {Array=} ops
* @return {Promise<Block>} a promise of the same block, for chaining
*/
BlockService.prototype.confirm = function(block) {
BlockService.prototype.confirm = function(block, ops) {
$.checkArgument(block instanceof bitcore.Block);
var self = this;
var ops = [];
ops = ops || [];
//console.log(0);
return Promise.try(function() {
@ -250,7 +242,10 @@ BlockService.prototype.confirm = function(block) {
self._setNextBlock(ops, block.header.prevHash, block);
//console.log(3);
self._setBlockHeight(ops, block, block.height);
self._setBlockHeight(ops, block);
//console.log(3);
self._setBlockWork(ops, block);
//console.log(4);
self._setBlockByTs(ops, block);
@ -287,11 +282,19 @@ BlockService.prototype._setNextBlock = function(ops, prevBlockHash, block) {
});
};
BlockService.prototype._setBlockHeight = function(ops, block, height) {
BlockService.prototype._setBlockHeight = function(ops, block) {
ops.push({
type: 'put',
key: Index.getBlockHeight(block),
value: height
value: block.height
});
};
BlockService.prototype._setBlockWork = function(ops, block) {
ops.push({
type: 'put',
key: Index.getBlockWork(block),
value: block.work
});
};
@ -333,6 +336,36 @@ BlockService.prototype._setBlockByTs = function(ops, block) {
*/
};
/**
* Unconfirm a block
*
* @param {bitcore.Block} block
* @param {Array=} ops
* @return {Promise<Block>} a promise of the same block, for chaining
*/
BlockService.prototype.unconfirm = function(block, ops) {
ops = ops || [];
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) {
return self.transactionService._unconfirmTransaction(ops, block, transaction);
}));
}).then(function() {
return self.database.batchAsync(ops);
});
};
/**
* Retrieve the block hash that forms part of the current main chain that confirmed a given
* transaction.
@ -368,24 +401,57 @@ BlockService.prototype.getBlockForTransaction = function(transaction) {
BlockService.prototype.getBlockchain = function() {
var self = this;
return new Promise(function(resolve, reject) {
self.database.getAsync(Index.blockchain).then(function(blockchain) {
blockchain = BlockChain.fromObject(JSON.parse(blockchain));
return resolve(blockchain);
}).catch(function(arg) {
if (arg.message.indexOf('Key not found in database') !== -1) {
return resolve();
var blockchain = new BlockChain();
var fetchBlock = function(blockHash) {
return Promise.all([
function() {
return self.database.getAsync(Index.getPreviousBlock(blockHash)).then(function(prevHash) {
blockchain.prev[blockHash] = prevHash;
blockchain.next[prevHash] = blockHash;
});
},
function() {
return self.database.getAsync(Index.getBlockHeight(blockHash)).then(function(height) {
blockchain.height[blockHash] = height;
blockchain.hashByHeight[height] = blockHash;
});
},
function() {
return self.database.getAsync(Index.getWork(blockHash)).then(function(work) {
blockchain.work[blockHash] = work;
});
}
]).then(function() {
return blockHash;
});
};
var fetchUnlessGenesis = function(blockHash) {
return fetchBlock(blockHash).then(function() {
if (blockchain.prev[blockHash] === BlockChain.NULL) {
return;
} else {
return fetchUnlessGenesis(blockchain.prev[blockHash]);
}
});
};
return self.database.getAsync(Index.tip)
.catch(function(err) {
if (err.notFound) {
return undefined;
}
return reject(arg);
throw err;
})
.then(function(tip) {
blockchain.tip = tip;
return fetchUnlessGenesis(tip);
});
});
};
BlockService.prototype.saveBlockchain = function(blockchain) {
return this.database.putAsync({
key: Index.blockchain,
value: blockchain.toJSON()
});
};
module.exports = BlockService;