From 7102e63933a24afd708e8c40c65b21457c6518b5 Mon Sep 17 00:00:00 2001 From: eordano Date: Thu, 9 Apr 2015 18:53:03 -0300 Subject: [PATCH] Restore blockchain from database --- lib/blockchain.js | 18 +++--- lib/node.js | 11 ++-- lib/services/block.js | 126 ++++++++++++++++++++++++++++++++---------- 3 files changed, 111 insertions(+), 44 deletions(-) diff --git a/lib/blockchain.js b/lib/blockchain.js index f313b7cc..4bd822dc 100644 --- a/lib/blockchain.js +++ b/lib/blockchain.js @@ -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; diff --git a/lib/node.js b/lib/node.js index 248317cf..75deaed6 100644 --- a/lib/node.js +++ b/lib/node.js @@ -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]); diff --git a/lib/services/block.js b/lib/services/block.js index 9cc5beb3..f0480b8f 100644 --- a/lib/services/block.js +++ b/lib/services/block.js @@ -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 for the next block in the main chain that is a child height: 'bh-', // bh- -> height (-1 means disconnected) tip: 'tip', // tip -> { hash: hex, height: int }, the latest tip - blockchain: 'chain' + work: 'wk-' // wk- -> 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} 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} 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} 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;