diff --git a/logic/broadcaster.js b/logic/broadcaster.js index 12498b81329..49cbe5e6932 100644 --- a/logic/broadcaster.js +++ b/logic/broadcaster.js @@ -5,6 +5,9 @@ var constants = require('../helpers/constants.js'); var jobsQueue = require('../helpers/jobsQueue.js'); var extend = require('extend'); var _ = require('lodash'); +var BSON = require('bson'); + +var bson = new BSON(); // Private fields var modules, library, self, __private = {}; @@ -152,6 +155,9 @@ Broadcaster.prototype.broadcast = function (params, options, cb) { } }, function sendToPeer (peers, waterCb) { + if (options.data.block) { + options.data.block = bson.deserialize(options.data.block); + } library.logger.debug('Begin broadcast', options); if (params.limit === self.config.peerLimit) { peers = peers.slice(0, self.config.broadcastLimit); diff --git a/modules/blocks/chain.js b/modules/blocks/chain.js index e8e147d13eb..dd65977cff4 100644 --- a/modules/blocks/chain.js +++ b/modules/blocks/chain.js @@ -324,13 +324,12 @@ __private.applyTransaction = function (block, transaction, sender, cb) { * @method applyBlock * @emits SIGTERM * @param {Object} block Full normalized block - * @param {boolean} broadcast Indicator that block needs to be broadcasted - * @param {Function} cb Callback function * @param {boolean} saveBlock Indicator that block needs to be saved to database + * @param {Function} cb Callback function * @return {Function} cb Callback function from params (through setImmediate) * @return {Object} cb.err Error if occurred */ -Chain.prototype.applyBlock = function (block, broadcast, cb, saveBlock) { +Chain.prototype.applyBlock = function (block, saveBlock, cb) { // Prevent shutdown during database writes. modules.blocks.isActive.set(true); @@ -455,12 +454,10 @@ Chain.prototype.applyBlock = function (block, broadcast, cb, saveBlock) { } library.logger.debug('Block applied correctly with ' + block.transactions.length + ' transactions'); - library.bus.message('newBlock', block, broadcast); return seriesCb(); }); } else { - library.bus.message('newBlock', block, broadcast); return seriesCb(); } }, @@ -491,6 +488,15 @@ Chain.prototype.applyBlock = function (block, broadcast, cb, saveBlock) { }); }; +/** + * Broadcast reduced block to increase network performance. + * @param {Object} reducedBlock reduced block + * @param {boolean} broadcast Indicator that block needs to be broadcasted + */ +Chain.prototype.broadcastReducedBlock = function (reducedBlock, broadcast) { + library.bus.message('newBlock', reducedBlock, broadcast); + library.logger.debug(['reducedBlock', reducedBlock.id, 'broadcasted correctly'].join(' ')); +}; /** * Deletes last block, undo transactions, recalculate round diff --git a/modules/blocks/verify.js b/modules/blocks/verify.js index 3399940b984..625fa6e4c03 100644 --- a/modules/blocks/verify.js +++ b/modules/blocks/verify.js @@ -7,6 +7,9 @@ var crypto = require('crypto'); var slots = require('../../helpers/slots.js'); var sql = require('../../sql/blocks.js'); var exceptions = require('../../helpers/exceptions.js'); +var BSON = require('bson'); + +var bson = new BSON(); var modules, library, self, __private = {}; @@ -87,9 +90,77 @@ __private.checkTransaction = function (block, transaction, cb) { }); }; +/** + * Adds default properties to block. + * @private + * @param {Object} block Block object reduced + * @return {Object} Block object completed + */ +__private.addBlockProperties = function (block) { + if (block.version === undefined) { + block.version = 0; + } + if (block.numberOfTransactions === undefined) { + if (block.transactions === undefined) { + block.numberOfTransactions = 0; + } else { + block.numberOfTransactions = block.transactions.length; + } + } + if (block.totalAmount === undefined) { + block.totalAmount = 0; + } + if (block.totalFee === undefined) { + block.totalFee = 0; + } + if (block.payloadLength === undefined) { + block.payloadLength = 0; + } + if (block.reward === undefined) { + block.reward = 0; + } + if (block.transactions === undefined) { + block.transactions = []; + } + return block; +}; + +/** + * Deletes default properties from block. + * @private + * @param {Object} block Block object completed + * @return {Object} Block object reduced + */ +__private.deleteBlockProperties = function (block) { + var reducedBlock = JSON.parse(JSON.stringify(block)); + if (reducedBlock.version === 0) { + delete reducedBlock.version; + } + // verifyBlock ensures numberOfTransactions is transactions.length + if (typeof(reducedBlock.numberOfTransactions) === 'number') { + delete reducedBlock.numberOfTransactions; + } + if (reducedBlock.totalAmount === 0) { + delete reducedBlock.totalAmount; + } + if (reducedBlock.totalFee === 0) { + delete reducedBlock.totalFee; + } + if (reducedBlock.payloadLength === 0) { + delete reducedBlock.payloadLength; + } + if (reducedBlock.reward === 0) { + delete reducedBlock.reward; + } + if (reducedBlock.transactions && reducedBlock.transactions.length === 0) { + delete reducedBlock.transactions; + } + delete reducedBlock.id; + return reducedBlock; +}; + /** * Verify block and return all possible errors related to block - * * @public * @method verifyBlock * @param {Object} block Full block @@ -266,6 +337,18 @@ Verify.prototype.processBlock = function (block, broadcast, cb, saveBlock) { } async.series({ + addBlockProperties: function (seriesCb) { + if (!broadcast) { + try { + // set default properties + block = __private.addBlockProperties(block); + } catch (err) { + return setImmediate(seriesCb, err); + } + } + + return setImmediate(seriesCb); + }, normalizeBlock: function (seriesCb) { try { block = library.logic.block.objectNormalize(block); @@ -275,6 +358,20 @@ Verify.prototype.processBlock = function (block, broadcast, cb, saveBlock) { return setImmediate(seriesCb); }, + deleteBlockProperties: function (seriesCb) { + if (broadcast) { + try { + // delete default properties + var blockReduced = __private.deleteBlockProperties(block); + var serializedBlockReduced = bson.serialize(blockReduced); + modules.blocks.chain.broadcastReducedBlock(serializedBlockReduced, broadcast); + } catch (err) { + return setImmediate(seriesCb, err); + } + } + + return setImmediate(seriesCb); + }, verifyBlock: function (seriesCb) { // Sanity check of the block, if values are coherent. // No access to database @@ -328,7 +425,7 @@ Verify.prototype.processBlock = function (block, broadcast, cb, saveBlock) { // * Block and transactions have valid values (signatures, block slots, etc...) // * The check against database state passed (for instance sender has enough LSK, votes are under 101, etc...) // We thus update the database with the transactions values, save the block and tick it. - modules.blocks.chain.applyBlock(block, broadcast, cb, saveBlock); + modules.blocks.chain.applyBlock(block, saveBlock, cb); } }); }; diff --git a/package.json b/package.json index a7e36f44df6..86b9dbaecbc 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "bignumber.js": "=4.0.2", "bluebird": "=3.5.0", "body-parser": "=1.17.2", + "bson": "=1.0.4", "bytebuffer": "=5.0.1", "change-case": "=3.0.1", "colors": "=1.1.2", diff --git a/test/common/initModule.js b/test/common/initModule.js index 5b1818792ba..18844775049 100644 --- a/test/common/initModule.js +++ b/test/common/initModule.js @@ -40,7 +40,16 @@ var modulesLoader = new function () { schema: new z_schema(), ed: ed, bus: { - message: function () {} + argsMessages: [], + message: function () { + Array.prototype.push.apply(this.argsMessages, arguments); + }, + getMessages: function () { + return this.argsMessages; + }, + clearMessages: function () { + this.argsMessages = []; + } }, nonce: randomString.generate(16), dbSequence: new Sequence({ diff --git a/test/unit/modules/blocks/verify.js b/test/unit/modules/blocks/verify.js index 94e5be8b347..9e3f70cfe39 100644 --- a/test/unit/modules/blocks/verify.js +++ b/test/unit/modules/blocks/verify.js @@ -1,14 +1,17 @@ 'use strict'; -var chai = require('chai'); var expect = require('chai').expect; - -var express = require('express'); -var sinon = require('sinon'); +var async = require('async'); var modulesLoader = require('../../../common/initModule').modulesLoader; var BlockLogic = require('../../../../logic/block.js'); var exceptions = require('../../../../helpers/exceptions.js'); +var clearDatabaseTable = require('../../../common/globalBefore').clearDatabaseTable; + +var crypto = require('crypto'); +var BSON = require('bson'); + +var bson = new BSON(); var previousBlock = { blockSignature:'696f78bed4d02faae05224db64e964195c39f715471ebf416b260bc01fa0148f3bddf559127b2725c222b01cededb37c7652293eb1a81affe2acdc570266b501', @@ -122,11 +125,122 @@ var blockRewardInvalid = { id: '15635779876149546284' }; +var testAccount = { + account: { + username: 'test_verify', + isDelegate: 1, + address: '2737453412992791987L', + publicKey: 'c76a0e680e83f47cf07c0f46b410f3b97e424171057a0f8f0f420c613da2f7b5', + balance: 5300000000000000000, + }, + secret: 'message crash glance horror pear opera hedgehog monitor connect vague chuckle advice', +}; + +var userAccount = { + account: { + username: 'test_verify_user', + isDelegate: 0, + address: '2896019180726908125L', + publicKey: '684a0259a769a9bdf8b82c5fe3054182ba3e936cf027bb63be231cd25d942adb', + balance: 0, + }, + secret: 'joy ethics cruise churn ozone asset quote renew dutch erosion seed pioneer', +}; +var previousBlock1 = { + blockSignature:'696f78bed4d02faae05224db64e964195c39f715471ebf416b260bc01fa0148f3bddf559127b2725c222b01cededb37c7652293eb1a81affe2acdc570266b501', + generatorPublicKey:'86499879448d1b0215d59cbf078836e3d7d9d2782d56a2274a568761bff36f19', + height:488, + id:'6524861224470851795', + numberOfTransactions:0, + payloadHash:'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', + payloadLength:0, + previousBlock:'8805727971083409014', + relays:1, + reward:0, + timestamp:32578360, + totalAmount:0, + totalFee:0, + transactions: [], + version:0, +}; +var block1; +var transactionsBlock1 = [ + { + 'type': 0, + 'amount': 10000000000000000, + 'fee': 10000000, + 'timestamp': 33514086, + 'recipientId': '16313739661670634666L', + 'senderId': '2737453412992791987L', + 'senderPublicKey': 'c76a0e680e83f47cf07c0f46b410f3b97e424171057a0f8f0f420c613da2f7b5', + 'signature': '57bc34c092189e6520b1fcb5b8a1e911d5aed56910ae75d8bbf6145b780dce539949ba86a0ae8d6a33b3a2a68ce8c16eb39b448b4e53f5ca8b04a0da3b438907', + 'id': '7249285091378090017' + } +]; + +var block2; +var transactionsBlock2 = [ + { + 'type': 0, + 'amount': 100000000, + 'fee': 10000000, + 'timestamp': 33772862, + 'recipientId': '16313739661670634666L', + 'senderId': '2737453412992791987L', + 'senderPublicKey': 'c76a0e680e83f47cf07c0f46b410f3b97e424171057a0f8f0f420c613da2f7b5', + 'signature': 'd2b2cb8d09169bf9f22ef123361036ae096ad71155fc3afddc7f22d4118b56a949fb82ff12fd6e6a05f411fe7e9e7877f71989959f895a6de94c193fe078f80b', + 'id': '15250193673472372402' + } +]; + +var block3; + +function createBlock (blocksModule, blockLogic, secret, timestamp, transactions, previousBlock) { + var keypair = blockLogic.scope.ed.makeKeypair(crypto.createHash('sha256').update(secret, 'utf8').digest()); + blocksModule.lastBlock.set(previousBlock); + var newBlock = blockLogic.create({ + keypair: keypair, + timestamp: timestamp, + previousBlock: blocksModule.lastBlock.get(), + transactions: transactions + }); + //newBlock.id = blockLogic.getId(newBlock); + return newBlock; +} + +function deleteBlockProperties (block) { + // see modules/blocks/verify.js for deleted fields reference. + if (block.version === 0) { + delete block.version; + } + // verifyBlock ensures numberOfTransactions is transactions.length + if (typeof(block.numberOfTransactions) === 'number') { + delete block.numberOfTransactions; + } + if (block.totalAmount === 0) { + delete block.totalAmount; + } + if (block.totalFee === 0) { + delete block.totalFee; + } + if (block.payloadLength === 0) { + delete block.payloadLength; + } + if (block.reward === 0) { + delete block.reward; + } + if (block.transactions && block.transactions.length === 0) { + delete block.transactions; + } + delete block.id; +} + describe('blocks/verify', function () { var blocksVerify; var blocks; var blockLogic; + var accounts; before(function (done) { modulesLoader.initLogic(BlockLogic, modulesLoader.scope, function (err, __blockLogic) { @@ -140,16 +254,26 @@ describe('blocks/verify', function () { {accounts: require('../../../../modules/accounts')}, {delegates: require('../../../../modules/delegates')}, {transactions: require('../../../../modules/transactions')}, + {rounds: require('../../../../modules/rounds')}, + {transport: require('../../../../modules/transport')}, + {system: require('../../../../modules/system')}, ], [ {'block': require('../../../../logic/block')}, {'transaction': require('../../../../logic/transaction')}, - ], {}, function (err, __blocks) { + {'account': require('../../../../logic/account')}, + ], {}, function (err, __modules) { if (err) { return done(err); } - __blocks.blocks.verify.onBind(__blocks); - blocks = __blocks.blocks; - blocksVerify = __blocks.blocks.verify; + __modules.blocks.verify.onBind(__modules); + __modules.delegates.onBind(__modules); + __modules.transactions.onBind(__modules); + __modules.blocks.chain.onBind(__modules); + __modules.rounds.onBind(__modules); + __modules.transport.onBind(__modules); + blocks = __modules.blocks; + blocksVerify = __modules.blocks.verify; + accounts = __modules.accounts; done(); }); }); @@ -431,4 +555,344 @@ describe('blocks/verify', function () { }); }); }); + // Sends a block to network, save it locally. + describe('processBlock() for valid block {broadcast: true, saveBlock: true}', function () { + + it('should clear database', function (done) { + async.every([ + 'blocks where height > 1', + 'trs where "blockId" != \'6524861224470851795\'', + 'mem_accounts where address in (\'2737453412992791987L\', \'2896019180726908125L\')', + 'forks_stat', + 'votes where "transactionId" = \'17502993173215211070\'' + ], function (table, seriesCb) { + clearDatabaseTable(modulesLoader.db, modulesLoader.logger, table, seriesCb); + }, function (err, result) { + if (err) { + return done(err); + } + return done(); + }); + }); + + it('should generate account', function (done) { + accounts.setAccountAndGet(testAccount.account, function (err, newaccount) { + if (err) { + return done(err); + } + expect(newaccount.address).to.equal(testAccount.account.address); + done(); + }); + }); + + it('should generate block 1', function (done) { + var secret = 'famous weapon poverty blast announce observe discover prosper mystery adapt tuna office'; + + block1 = createBlock(blocks, blockLogic, secret, 32578370, transactionsBlock1, previousBlock1); + expect(block1.version).to.equal(0); + expect(block1.timestamp).to.equal(32578370); + expect(block1.numberOfTransactions).to.equal(1); + expect(block1.reward).to.equal(0); + expect(block1.totalFee).to.equal(10000000); + expect(block1.totalAmount).to.equal(10000000000000000); + expect(block1.payloadLength).to.equal(117); + expect(block1.transactions).to.deep.equal(transactionsBlock1); + expect(block1.previousBlock).to.equal(previousBlock1.id); + done(); + }); + + it('should be ok when process block 1', function (done) { + blocksVerify.processBlock(block1, true, function (err, result) { + if (err) { + return done(err); + } + expect(result).to.be.undefined; + var onMessage = modulesLoader.scope.bus.getMessages(); + expect(onMessage[0]).to.equal('newBlock'); + onMessage[1] = bson.deserialize(onMessage[1]); + expect(onMessage[1].version).to.be.undefined; + expect(onMessage[1].numberOfTransactions).to.be.undefined; + expect(onMessage[1].id).to.be.undefined; + expect(onMessage[2]).to.be.true; + expect(onMessage[3]).to.equal('transactionsSaved'); + expect(onMessage[4]).to.deep.equal(block1.transactions); + modulesLoader.scope.bus.clearMessages(); + done(); + }, true); + }); + }); + + describe('processBlock() for invalid block {broadcast: true, saveBlock: true}', function () { + + it('should fail when process block 1 again (checkExists)', function (done) { + blocks.lastBlock.set(previousBlock1); + + blocksVerify.processBlock(block1, true, function (err, result) { + expect(err).to.equal(['Block', block1.id, 'already exists'].join(' ')); + done(); + modulesLoader.scope.bus.clearMessages(); + }, true); + }); + }); + + // Receives a block from network, save it locally. + describe('processBlock() for invalid block {broadcast: false, saveBlock: true}', function () { + + var invalidBlock2; + + it('should generate block 2 with invalid generator slot', function (done) { + var secret = 'flip relief play educate address plastic doctor fix must frown oppose segment'; + + block2 = createBlock(blocks, blockLogic, secret, 33772882, transactionsBlock2, block1); + expect(block2.version).to.equal(0); + expect(block2.timestamp).to.equal(33772882); + expect(block2.numberOfTransactions).to.equal(1); + expect(block2.reward).to.equal(0); + expect(block2.totalFee).to.equal(10000000); + expect(block2.totalAmount).to.equal(100000000); + expect(block2.payloadLength).to.equal(117); + expect(block2.transactions).to.deep.equal(transactionsBlock2); + expect(block2.previousBlock).to.equal(block1.id); + done(); + }); + + describe('normalizeBlock validations', function () { + + it('should fail when timestamp property is missing', function (done) { + deleteBlockProperties(block2); + var timestamp = block2.timestamp; + delete block2.timestamp; + + blocksVerify.processBlock(block2, false, function (err, result) { + if (err) { + expect(err).equal('Failed to validate block schema: Missing required property: timestamp'); + block2.timestamp = timestamp; + done(); + } + }, true); + }); + + it('should fail when transactions property is missing', function (done) { + var transactions = block2.transactions; + delete block2.transactions; + + blocksVerify.processBlock(block2, false, function (err, result) { + if (err) { + expect(err).equal('Included transactions do not match block transactions count'); + block2.transactions = transactions; + done(); + } + }, true); + }); + + it('should fail when transaction type property is missing', function (done) { + var trsType = block2.transactions[0].type; + delete block2.transactions[0].type; + + blocksVerify.processBlock(block2, false, function (err, result) { + if (err) { + expect(err).equal('Unknown transaction type undefined'); + block2.transactions[0].type = trsType; + done(); + } + }, true); + }); + + it('should fail when transaction timestamp property is missing', function (done) { + var trsTimestamp = block2.transactions[0].timestamp; + delete block2.transactions[0].timestamp; + + blocksVerify.processBlock(block2, false, function (err, result) { + if (err) { + expect(err).equal('Failed to validate transaction schema: Missing required property: timestamp'); + block2.transactions[0].timestamp = trsTimestamp; + done(); + } + }, true); + }); + }); + + it('should fail when block generator is invalid (fork:3)', function (done) { + blocksVerify.processBlock(block2, false, function (err, result) { + if (err) { + expect(err).equal('Failed to verify slot: 3377288'); + done(); + } + }, true); + }); + + it('should generate block 2 with valid generator slot and processed trs', function (done) { + var secret = 'flip relief play educate address plastic doctor fix must frown oppose segment'; + + block2 = createBlock(blocks, blockLogic, secret, 33772862, transactionsBlock1, block1); + expect(block2.version).to.equal(0); + expect(block2.timestamp).to.equal(33772862); + expect(block2.numberOfTransactions).to.equal(1); + expect(block2.reward).to.equal(0); + expect(block2.totalFee).to.equal(10000000); + expect(block2.totalAmount).to.equal(10000000000000000); + expect(block2.payloadLength).to.equal(117); + expect(block2.transactions).to.deep.equal(transactionsBlock1); + expect(block2.previousBlock).to.equal(block1.id); + done(); + }); + + it('should fail when transaction is already confirmed (fork:2)', function (done) { + deleteBlockProperties(block2); + + blocksVerify.processBlock(block2, false, function (err, result) { + if (err) { + expect(err).to.equal(['Transaction is already confirmed:', transactionsBlock1[0].id].join(' ')); + done(); + } + }, true); + }); + }); + + describe('processBlock() for valid block {broadcast: false, saveBlock: true}', function () { + + it('should generate block 2 with valid generator slot', function (done) { + var secret = 'flip relief play educate address plastic doctor fix must frown oppose segment'; + + block2 = createBlock(blocks, blockLogic, secret, 33772862, transactionsBlock2, block1); + expect(block2.version).to.equal(0); + expect(block2.timestamp).to.equal(33772862); + expect(block2.numberOfTransactions).to.equal(1); + expect(block2.reward).to.equal(0); + expect(block2.totalFee).to.equal(10000000); + expect(block2.totalAmount).to.equal(100000000); + expect(block2.payloadLength).to.equal(117); + expect(block2.transactions).to.deep.equal(transactionsBlock2); + expect(block2.previousBlock).to.equal(block1.id); + done(); + }); + + it('should be ok when process block 2', function (done) { + blocks.lastBlock.set(block1); + + blocksVerify.processBlock(block2, false, function (err, result) { + if (err) { + return done(err); + } + expect(result).to.be.undefined; + var onMessage = modulesLoader.scope.bus.getMessages(); + expect(onMessage[0]).to.equal('transactionsSaved'); + expect(onMessage[1][0].id).to.equal(block2.transactions[0].id); + modulesLoader.scope.bus.clearMessages(); + done(); + }, true); + }); + + it('should fail when process block 2 again (checkExists)', function (done) { + blocks.lastBlock.set(block1); + + blocksVerify.processBlock(block2, false, function (err, result) { + expect(err).to.equal(['Block', block2.id, 'already exists'].join(' ')); + done(); + }, true); + }); + }); + + // Sends a block to network, don't save it locally. + describe('processBlock() for valid block {broadcast: true, saveBlock: false}', function () { + + it('should generate account', function (done) { + accounts.setAccountAndGet(userAccount.account, function (err, newaccount) { + if (err) { + return done(err); + } + expect(newaccount.address).to.equal(userAccount.account.address); + done(); + }); + }); + + it('should generate block 3', function (done) { + var secret = 'flavor type stone episode capable usage save sniff notable liar gas someone'; + + block3 = createBlock(blocks, blockLogic, secret, 33942637, [], block2); + expect(block3.version).to.equal(0); + done(); + }); + + it('should be ok when broadcast block 3', function (done) { + blocksVerify.processBlock(block3, true, function (err, result) { + if (err) { + return done(err); + } + expect(result).to.be.undefined; + var onMessage = modulesLoader.scope.bus.getMessages(); + expect(onMessage[0]).to.equal('newBlock'); + onMessage[1] = bson.deserialize(onMessage[1]); + expect(onMessage[1].version).to.be.undefined; + expect(onMessage[1].numberOfTransactions).to.be.undefined; + expect(onMessage[1].totalAmount).to.be.undefined; + expect(onMessage[1].totalFee).to.be.undefined; + expect(onMessage[1].payloadLength).to.be.undefined; + expect(onMessage[1].reward).to.be.undefined; + expect(onMessage[1].transactions).to.be.undefined; + expect(onMessage[1].id).to.be.undefined; + expect(onMessage[2]).to.be.true; + expect(onMessage[3]).to.be.undefined; // transactionsSaved + modulesLoader.scope.bus.clearMessages(); + done(); + }, false); + }); + + it('should be ok when broadcast block 3 again (checkExists)', function (done) { + blocks.lastBlock.set(block2); + + blocksVerify.processBlock(block3, true, function (err, result) { + if (err) { + return done(err); + } + expect(result).to.be.undefined; + var onMessage = modulesLoader.scope.bus.getMessages(); + expect(onMessage[0]).to.equal('newBlock'); + onMessage[1] = bson.deserialize(onMessage[1]); + expect(onMessage[1].version).to.be.undefined; + expect(onMessage[1].numberOfTransactions).to.be.undefined; + expect(onMessage[1].totalAmount).to.be.undefined; + expect(onMessage[1].totalFee).to.be.undefined; + expect(onMessage[1].payloadLength).to.be.undefined; + expect(onMessage[1].reward).to.be.undefined; + expect(onMessage[1].transactions).to.be.undefined; + expect(onMessage[1].id).to.be.undefined; + expect(onMessage[2]).to.be.true; + expect(onMessage[3]).to.be.undefined; // transactionsSaved + modulesLoader.scope.bus.clearMessages(); + done(); + }, false); + }); + }); + + // Receives a block from network, don't save it locally. + describe('processBlock() for valid block {broadcast: false, saveBlock: false}', function () { + + it('should be ok when receive block 3', function (done) { + blocks.lastBlock.set(block2); + deleteBlockProperties(block3); + + blocksVerify.processBlock(block3, false, function (err, result) { + if (err) { + return done(err); + } + expect(result).to.be.undefined; + var onMessage = modulesLoader.scope.bus.getMessages(); + expect(onMessage).to.be.an('array').that.is.empty; + done(); + }, false); + }); + + it('should be ok when receive block 3 again (checkExists)', function (done) { + blocks.lastBlock.set(block2); + deleteBlockProperties(block3); + + blocksVerify.processBlock(block3, false, function (err, result) { + expect(result).to.be.undefined; + var onMessage = modulesLoader.scope.bus.getMessages(); + expect(onMessage).to.be.an('array').that.is.empty; + done(); + }, false); + }); + }); });