From 7d5994bf4f6ec03370dc9862daaed699883364ce Mon Sep 17 00:00:00 2001 From: Patrick Nagurny Date: Wed, 1 Jul 2015 13:27:54 -0400 Subject: [PATCH] fix bug where blocks are ignored if they were already written to disk --- lib/chain.js | 36 ++++++++++++++++-------------------- test/chain.unit.js | 46 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 53 insertions(+), 29 deletions(-) diff --git a/lib/chain.js b/lib/chain.js index 1723f32..7bf4d63 100644 --- a/lib/chain.js +++ b/lib/chain.js @@ -179,31 +179,28 @@ Chain.prototype._validateMerkleRoot = function(block) { ); }; -Chain.prototype._checkExisting = function(block, callback) { - this.db.getBlock(block.hash, function(err) { - if (err instanceof levelup.errors.NotFoundError) { - callback(); - } else if (err) { - callback(err); - } else { - return callback( - new errors.Consensus.BlockExists('Block ' + block.hash + ' already exists') - ); - } - }); -}; - Chain.prototype._validateBlock = function(block, callback) { log.debug('Chain is validating block: ' + block.hash); block.validate(this, callback); }; Chain.prototype._writeBlock = function(block, callback) { - log.debug('Chain is putting block to db:' + block.hash); - // Update hashes - this.cache.hashes[block.hash] = block.prevHash; - // Write to db - this.db.putBlock(block, callback); + var self = this; + + self.db.getBlock(block.hash, function(err) { + if (err instanceof levelup.errors.NotFoundError) { + log.debug('Chain is putting block to db:' + block.hash); + // Update hashes + self.cache.hashes[block.hash] = block.prevHash; + // Write to db + self.db.putBlock(block, callback); + } else if (err) { + callback(err); + } else { + log.debug('Block ' + block.hash + ' already exists, so not writing it again'); + callback(); + } + }); }; Chain.prototype._updateWeight = function(block, callback) { @@ -271,7 +268,6 @@ Chain.prototype._processBlock = function(block, callback) { async.series( [ - this._checkExisting.bind(this, block), this._writeBlock.bind(this, block), this._updateWeight.bind(this, block), this._updateTip.bind(this, block) diff --git a/test/chain.unit.js b/test/chain.unit.js index 56d3dc7..433f5c3 100644 --- a/test/chain.unit.js +++ b/test/chain.unit.js @@ -11,6 +11,7 @@ var bitcore = require('bitcore'); var BN = bitcore.crypto.BN; var Reorg = require('../lib/reorg'); var memdown = require('memdown'); +var levelup = require('levelup'); var chainData = require('./data/chain.json'); @@ -236,12 +237,10 @@ describe('Chain', function() { var block = {}; block.validate = sinon.stub().callsArg(1); var db = new DB({store: memdown}); - db.getBlock = sinon.stub().callsArg(1); - db.getBlock.onCall(1).callsArgWith(1, null, {height: 1}); + db.getBlock = sinon.stub().callsArgWith(1, new levelup.errors.NotFoundError()); db.putBlock = sinon.stub().callsArgWith(1, new Error('disk failure')); var chain = new Chain({db: db}); - chain._checkExisting = sinon.stub().callsArg(1); chain._validateMerkleRoot = sinon.stub(); chain._processBlock(block, function(err) { should.exist(err); @@ -251,6 +250,40 @@ describe('Chain', function() { }); it('should update the tip if this block makes the longest chain', function(done) { + var prevBlock = { + hash: 'oldtiphash', + height: 0 + }; + var block = {}; + block.hash = 'a84ca63feb41491d6a2032820cd078efce6f6c0344fe285c7c8bf77ae647718e'; + block.prevHash = 'oldtiphash'; + block.data = 'block2'; + block.validate = sinon.stub().callsArg(1); + var db = {}; + db.getBlock = sinon.stub().callsArgWith(1, new levelup.errors.NotFoundError()); + db.putBlock = sinon.stub().callsArg(1); + db._onChainAddBlock = sinon.stub().callsArg(1); + + var chain = new Chain({db: db}); + chain._validateMerkleRoot = sinon.stub(); + chain.tip = prevBlock; + chain.tip.__weight = new BN(1, 10); + chain._updateWeight = function(block, callback) { + block.__weight = new BN(2, 10); + callback(); + }; + chain.saveMetadata = sinon.spy(); + + chain._processBlock(block, function(err) { + should.not.exist(err); + should.exist(chain.tip); + db.putBlock.callCount.should.equal(1); + chain.tip.data.should.equal('block2'); + done(); + }); + }); + + it('should not write the block to disk if it already exists', function(done) { var prevBlock = { hash: 'oldtiphash', height: 0 @@ -262,12 +295,10 @@ describe('Chain', function() { block.validate = sinon.stub().callsArg(1); var db = {}; db.getBlock = sinon.stub().callsArg(1); - db.getBlock.onCall(1).callsArgWith(1, null, prevBlock); db.putBlock = sinon.stub().callsArg(1); db._onChainAddBlock = sinon.stub().callsArg(1); var chain = new Chain({db: db}); - chain._checkExisting = sinon.stub().callsArg(1); chain._validateMerkleRoot = sinon.stub(); chain.tip = prevBlock; chain.tip.__weight = new BN(1, 10); @@ -281,6 +312,7 @@ describe('Chain', function() { should.not.exist(err); should.exist(chain.tip); chain.tip.data.should.equal('block2'); + db.putBlock.callCount.should.equal(0); done(); }); }); @@ -293,11 +325,9 @@ describe('Chain', function() { block.validate = sinon.stub().callsArg(1); var db = {}; db.getBlock = sinon.stub().callsArg(1); - db.getBlock.onCall(1).callsArgWith(1, null, prevBlock); db.putBlock = sinon.stub().callsArg(1); var chain = new Chain({db: db}); - chain._checkExisting = sinon.stub().callsArg(1); chain._validateMerkleRoot = sinon.stub(); chain.tip = prevBlock; chain.tip.__weight = new BN(2, 10); @@ -327,11 +357,9 @@ describe('Chain', function() { block.validate = sinon.stub().callsArg(1); var db = {}; db.getBlock = sinon.stub().callsArg(1); - db.getBlock.onCall(1).callsArgWith(1, null, prevBlock); db.putBlock = sinon.stub().callsArg(1); var chain = new Chain({db: db}); - chain._checkExisting = sinon.stub().callsArg(1); chain._validateMerkleRoot = sinon.stub(); chain.tip = prevBlock; chain.tip.__weight = new BN(1, 10);