From 2fa8e27010f66dd9e3e8abaddc04958586c8065f Mon Sep 17 00:00:00 2001 From: Davide Gessa Date: Wed, 7 Dec 2016 19:31:08 +0100 Subject: [PATCH 001/123] Delegate stats for a given period --- modules/blocks.js | 24 ++++++++++++++++++++++++ modules/delegates.js | 21 ++++++++++++++------- schema/delegates.js | 6 ++++++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/modules/blocks.js b/modules/blocks.js index 7c5a9881e19..9f605f2a65c 100644 --- a/modules/blocks.js +++ b/modules/blocks.js @@ -1529,5 +1529,29 @@ shared.getStatus = function (req, cb) { }); }; + +shared.aggregateBlocksReward = function (req, cb) { + if (!__private.loaded) { + return setImmediate(cb, 'Blockchain is loading'); + } + + library.schema.validate(req.body, schema.aggregateBlocksReward, function (err) { + if (err) { + return setImmediate(cb, err[0].message); + } + + /*SELECT + sum ("b_totalFee") as fees, + sum ("b_reward") as reward, + count (*) as count + FROM + public.blocks_list + WHERE + "b_generatorPublicKey" != '' and "b_timestamp" > 9999 + */ + return setImmediate(cb, null, {fees: data.fees, reward: data.rewards, count: data.count}); + }); +}; + // Export module.exports = Blocks; diff --git a/modules/delegates.js b/modules/delegates.js index 6bc8ef2df41..5853c455590 100644 --- a/modules/delegates.js +++ b/modules/delegates.js @@ -755,13 +755,20 @@ shared.getForgedByAccount = function (req, cb) { return setImmediate(cb, err[0].message); } - modules.accounts.getAccount({publicKey: req.body.generatorPublicKey}, ['fees', 'rewards'], function (err, account) { - if (err || !account) { - return setImmediate(cb, err || 'Account not found'); - } - var forged = bignum(account.fees).plus(bignum(account.rewards)).toString(); - return setImmediate(cb, null, {fees: account.fees, rewards: account.rewards, forged: forged}); - }); + if (req.body.start && req.body.end) { + modules.blocks.aggregateBlocksReward({generatorPublicKey: req.body.generatorPublicKey, start: req.body.start, end: req.body.end}, function (err, reward) { + var forged = bignum(reward.fees).plus(bignum(reward.rewards)).toString(); + return setImmediate(cb, null, {fees: reward.fees, rewards: reward.rewards, forged: forged, count: reward.count}); + }); + } else { + modules.accounts.getAccount({publicKey: req.body.generatorPublicKey}, ['fees', 'rewards'], function (err, account) { + if (err || !account) { + return setImmediate(cb, err || 'Account not found'); + } + var forged = bignum(account.fees).plus(bignum(account.rewards)).toString(); + return setImmediate(cb, null, {fees: account.fees, rewards: account.rewards, forged: forged}); + }); + } }); }; diff --git a/schema/delegates.js b/schema/delegates.js index 1b6b681f0c9..3c0da4a4818 100644 --- a/schema/delegates.js +++ b/schema/delegates.js @@ -114,6 +114,12 @@ module.exports = { generatorPublicKey: { type: 'string', format: 'publicKey' + }, + start: { + type: 'string' + }, + end: { + type: 'string' } }, required: ['generatorPublicKey'] From 8913a7dd79290509cca263ce16eaae48c4464722 Mon Sep 17 00:00:00 2001 From: Davide Gessa Date: Thu, 8 Dec 2016 11:34:53 +0100 Subject: [PATCH 002/123] Aggregation method for block reward --- modules/blocks.js | 51 +++++++++++++++++++++++++-------------------- schema/delegates.js | 4 ++-- sql/blocks.js | 9 ++++++++ 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/modules/blocks.js b/modules/blocks.js index 9f605f2a65c..132e5f12285 100644 --- a/modules/blocks.js +++ b/modules/blocks.js @@ -1397,6 +1397,34 @@ Blocks.prototype.cleanup = function (cb) { } }; + + +Blocks.prototype.aggregateBlocksReward = function (filter, cb) { + var params = {}, where = []; + + where.push('"b_generatorPublicKey"::bytea = ${generatorPublicKey}'); + params.generatorPublicKey = filter.generatorPublicKey; + + where.push('"b_timestamp" >= ${start}'); + params.start = filter.start; + + where.push('"b_timestamp" <= ${end}'); + params.end = filter.end; + + library.db.query(sql.aggregateBlocksReward({ + where: where, + }), params).then(function (rows) { + var data = rows[0]; + data = { fees: data.fees || '0', rewards: data.rewards || '0', count: data.count || '0' }; + + return setImmediate(cb, null, data); + }).catch(function (err) { + library.logger.error(err.stack); + return setImmediate(cb, 'Blocks#list error'); + }); +}; + + // Shared shared.getBlock = function (req, cb) { if (!__private.loaded) { @@ -1530,28 +1558,5 @@ shared.getStatus = function (req, cb) { }; -shared.aggregateBlocksReward = function (req, cb) { - if (!__private.loaded) { - return setImmediate(cb, 'Blockchain is loading'); - } - - library.schema.validate(req.body, schema.aggregateBlocksReward, function (err) { - if (err) { - return setImmediate(cb, err[0].message); - } - - /*SELECT - sum ("b_totalFee") as fees, - sum ("b_reward") as reward, - count (*) as count - FROM - public.blocks_list - WHERE - "b_generatorPublicKey" != '' and "b_timestamp" > 9999 - */ - return setImmediate(cb, null, {fees: data.fees, reward: data.rewards, count: data.count}); - }); -}; - // Export module.exports = Blocks; diff --git a/schema/delegates.js b/schema/delegates.js index 3c0da4a4818..c83e8e7c126 100644 --- a/schema/delegates.js +++ b/schema/delegates.js @@ -116,10 +116,10 @@ module.exports = { format: 'publicKey' }, start: { - type: 'string' + type: 'integer' }, end: { - type: 'string' + type: 'integer' } }, required: ['generatorPublicKey'] diff --git a/sql/blocks.js b/sql/blocks.js index f755195b7d8..b0194e23189 100644 --- a/sql/blocks.js +++ b/sql/blocks.js @@ -24,6 +24,15 @@ var BlocksSql = { ].filter(Boolean).join(' '); }, + + aggregateBlocksReward: function (params) { + return [ + 'SELECT sum ("b_totalFee") as fees, sum ("b_reward") as reward, count (*) as count', + 'FROM blocks_list', + (params.where.length ? 'WHERE ' + params.where.join(' AND ') : '') + ].filter(Boolean).join(' '); + }, + list: function (params) { return [ 'SELECT * FROM blocks_list', From ae43ac625b80a8b2fe234004a34fb9386ef02e77 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Thu, 8 Dec 2016 11:33:48 +0100 Subject: [PATCH 003/123] Loading transactions / signatures only at launch. Separating sync timer into its own interval. --- modules/loader.js | 55 ++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/modules/loader.js b/modules/loader.js index 0edfdf64f26..1cbacb79172 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -21,6 +21,8 @@ __private.genesisBlock = null; __private.total = 0; __private.blocksToSync = 0; __private.syncIntervalId = null; +__private.syncInterval = 10000; +__private.retries = 5; // Constructor function Loader (cb, scope) { @@ -80,6 +82,27 @@ __private.syncTrigger = function (turnOn) { } }; +__private.syncTimer = function () { + setImmediate(function nextSync () { + var lastReceipt = modules.blocks.lastReceipt(); + + if (__private.loaded && !self.syncing() && (!lastReceipt || lastReceipt.stale)) { + library.sequence.add(function (cb) { + async.retry(__private.retries, __private.sync, cb); + }, function (err) { + if (err) { + library.logger.log('Sync timer', err); + __private.initalize(); + } + + return setTimeout(nextSync, __private.syncInterval); + }); + } else { + return setTimeout(nextSync, __private.syncInterval); + } + }); +} + __private.loadSignatures = function (cb) { async.waterfall([ function (waterCb) { @@ -654,32 +677,13 @@ Loader.prototype.sandboxApi = function (call, args, cb) { // Events Loader.prototype.onPeersReady = function () { - var retries = 5; - - setImmediate(function nextSeries () { + setImmediate(function load () { async.series({ - sync: function (seriesCb) { - var lastReceipt = modules.blocks.lastReceipt(); - - if (__private.loaded && !self.syncing() && (!lastReceipt || lastReceipt.stale)) { - library.sequence.add(function (cb) { - async.retry(retries, __private.sync, cb); - }, function (err) { - if (err) { - library.logger.log('Sync timer', err); - } - - return setImmediate(seriesCb); - }); - } else { - return setImmediate(seriesCb); - } - }, loadTransactions: function (seriesCb) { if (__private.loaded) { - async.retry(retries, __private.loadTransactions, function (err) { + async.retry(__private.retries, __private.loadTransactions, function (err) { if (err) { - library.logger.log('Unconfirmed transactions timer', err); + library.logger.log('Unconfirmed transactions loader', err); } return setImmediate(seriesCb); @@ -690,9 +694,9 @@ Loader.prototype.onPeersReady = function () { }, loadSignatures: function (seriesCb) { if (__private.loaded) { - async.retry(retries, __private.loadSignatures, function (err) { + async.retry(__private.retries, __private.loadSignatures, function (err) { if (err) { - library.logger.log('Signatures timer', err); + library.logger.log('Signatures loader', err); } return setImmediate(seriesCb); @@ -705,7 +709,8 @@ Loader.prototype.onPeersReady = function () { if (err) { __private.initalize(); } - return setTimeout(nextSeries, 10000); + + return __private.syncTimer(); }); }); }; From 465dc0a1bc109e6f542ba8621e256a3a376379a4 Mon Sep 17 00:00:00 2001 From: Davide Gessa Date: Thu, 8 Dec 2016 11:56:26 +0100 Subject: [PATCH 004/123] Block reward aggregate --- modules/blocks.js | 5 +++-- sql/blocks.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/blocks.js b/modules/blocks.js index 132e5f12285..31683581307 100644 --- a/modules/blocks.js +++ b/modules/blocks.js @@ -1406,15 +1406,16 @@ Blocks.prototype.aggregateBlocksReward = function (filter, cb) { params.generatorPublicKey = filter.generatorPublicKey; where.push('"b_timestamp" >= ${start}'); - params.start = filter.start; + params.start = filter.start - constants.epochTime.getTime () / 1000; where.push('"b_timestamp" <= ${end}'); - params.end = filter.end; + params.end = filter.end - constants.epochTime.getTime () / 1000; library.db.query(sql.aggregateBlocksReward({ where: where, }), params).then(function (rows) { var data = rows[0]; + console.log (data); data = { fees: data.fees || '0', rewards: data.rewards || '0', count: data.count || '0' }; return setImmediate(cb, null, data); diff --git a/sql/blocks.js b/sql/blocks.js index b0194e23189..10a0b89f08a 100644 --- a/sql/blocks.js +++ b/sql/blocks.js @@ -27,7 +27,7 @@ var BlocksSql = { aggregateBlocksReward: function (params) { return [ - 'SELECT sum ("b_totalFee") as fees, sum ("b_reward") as reward, count (*) as count', + 'SELECT sum ("b_totalFee") as fees, sum ("b_reward") as rewards, count (*) as count', 'FROM blocks_list', (params.where.length ? 'WHERE ' + params.where.join(' AND ') : '') ].filter(Boolean).join(' '); From 4bd16243b9f542634bcd8d6af0ea6258a9df4c05 Mon Sep 17 00:00:00 2001 From: Davide Gessa Date: Thu, 8 Dec 2016 15:47:02 +0100 Subject: [PATCH 005/123] Review fixtures --- modules/blocks.js | 5 +---- sql/blocks.js | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/modules/blocks.js b/modules/blocks.js index 31683581307..7ee8477f371 100644 --- a/modules/blocks.js +++ b/modules/blocks.js @@ -1415,17 +1415,15 @@ Blocks.prototype.aggregateBlocksReward = function (filter, cb) { where: where, }), params).then(function (rows) { var data = rows[0]; - console.log (data); data = { fees: data.fees || '0', rewards: data.rewards || '0', count: data.count || '0' }; return setImmediate(cb, null, data); }).catch(function (err) { library.logger.error(err.stack); - return setImmediate(cb, 'Blocks#list error'); + return setImmediate(cb, 'Blocks#aggregateBlocksReward error'); }); }; - // Shared shared.getBlock = function (req, cb) { if (!__private.loaded) { @@ -1558,6 +1556,5 @@ shared.getStatus = function (req, cb) { }); }; - // Export module.exports = Blocks; diff --git a/sql/blocks.js b/sql/blocks.js index 10a0b89f08a..b11ec294350 100644 --- a/sql/blocks.js +++ b/sql/blocks.js @@ -24,10 +24,9 @@ var BlocksSql = { ].filter(Boolean).join(' '); }, - aggregateBlocksReward: function (params) { return [ - 'SELECT sum ("b_totalFee") as fees, sum ("b_reward") as rewards, count (*) as count', + 'SELECT sum ("b_totalFee") as fees, sum ("b_reward") as rewards, count (1) as count', 'FROM blocks_list', (params.where.length ? 'WHERE ' + params.where.join(' AND ') : '') ].filter(Boolean).join(' '); From f08f5a5d88348076b6532ca0567b409ef1572d7e Mon Sep 17 00:00:00 2001 From: Davide Gessa Date: Thu, 8 Dec 2016 15:48:50 +0100 Subject: [PATCH 006/123] Removing new lines --- modules/blocks.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/blocks.js b/modules/blocks.js index 7ee8477f371..9e8794bece1 100644 --- a/modules/blocks.js +++ b/modules/blocks.js @@ -1397,8 +1397,6 @@ Blocks.prototype.cleanup = function (cb) { } }; - - Blocks.prototype.aggregateBlocksReward = function (filter, cb) { var params = {}, where = []; @@ -1416,7 +1414,6 @@ Blocks.prototype.aggregateBlocksReward = function (filter, cb) { }), params).then(function (rows) { var data = rows[0]; data = { fees: data.fees || '0', rewards: data.rewards || '0', count: data.count || '0' }; - return setImmediate(cb, null, data); }).catch(function (err) { library.logger.error(err.stack); From e9ea789dd8c4bcb71fc24ecc9511bfaa5deddb74 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Thu, 8 Dec 2016 12:33:05 +0100 Subject: [PATCH 007/123] Using express API to retrieve remote IP addresses. http://expressjs.com/en/api.html#req.ip --- modules/delegates.js | 4 ++-- modules/transport.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/delegates.js b/modules/delegates.js index 6bc8ef2df41..ecfa32d935a 100644 --- a/modules/delegates.js +++ b/modules/delegates.js @@ -91,7 +91,7 @@ __private.attachApi = function () { return res.json({success: false, error: err[0].message}); } - var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; + var ip = req.ip; if (!checkIpInList(library.config.forging.access.whiteList, ip)) { return res.json({success: false, error: 'Access denied'}); @@ -130,7 +130,7 @@ __private.attachApi = function () { return res.json({success: false, error: err[0].message}); } - var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; + var ip = req.ip; if (!checkIpInList(library.config.forging.access.whiteList, ip)) { return res.json({success: false, error: 'Access denied'}); diff --git a/modules/transport.js b/modules/transport.js index ccc645d7a09..c88d7cf3648 100644 --- a/modules/transport.js +++ b/modules/transport.js @@ -47,7 +47,7 @@ __private.attachApi = function () { router.use(function (req, res, next) { req.peer = modules.peers.accept( { - ip: req.headers['x-forwarded-for'] || req.connection.remoteAddress, + ip: req.ip, port: req.headers.port } ); From 3c23ce35ebe089ad8faa8a49b1e48b32b5552bd3 Mon Sep 17 00:00:00 2001 From: Mq Date: Fri, 9 Dec 2016 03:54:36 +0100 Subject: [PATCH 008/123] Fixed incorrect currentSlot calculation in getNextForgers Also added `currentBlockSlot` property to getNextForgers endpoint. --- modules/delegates.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/delegates.js b/modules/delegates.js index ecfa32d935a..13fbc536ed2 100644 --- a/modules/delegates.js +++ b/modules/delegates.js @@ -619,7 +619,8 @@ shared.getNextForgers = function (req, cb) { return setImmediate(cb, err); } - var currentSlot = slots.getSlotNumber(currentBlock.timestamp); + var currentBlockSlot = slots.getSlotNumber(currentBlock.timestamp); + var currentSlot = slots.getSlotNumber(); var nextForgers = []; for (var i = 1; i <= slots.delegates && i <= limit; i++) { @@ -627,7 +628,7 @@ shared.getNextForgers = function (req, cb) { nextForgers.push (activeDelegates[(currentSlot + i) % slots.delegates]); } } - return setImmediate(cb, null, {currentBlock: currentBlock.height, currentSlot: currentSlot, delegates: nextForgers}); + return setImmediate(cb, null, {currentBlock: currentBlock.height, currentBlockSlot: currentBlockSlot, currentSlot: currentSlot, delegates: nextForgers}); }); }; From 02b6b4d69480e6c6a574141e220bcf7056487987 Mon Sep 17 00:00:00 2001 From: Mq Date: Fri, 9 Dec 2016 04:35:34 +0100 Subject: [PATCH 009/123] Describing new property for getNextForgers --- test/api/delegates.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/api/delegates.js b/test/api/delegates.js index 3e648dc27c1..72b8bcc2d10 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -1006,6 +1006,7 @@ describe('GET /api/delegates/getNextForgers', function () { node.get('/api/delegates/getNextForgers', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('currentBlock').that.is.a('number'); + node.expect(res.body).to.have.property('currentBlockSlot').that.is.a('number'); node.expect(res.body).to.have.property('currentSlot').that.is.a('number'); node.expect(res.body).to.have.property('delegates').that.is.an('array'); node.expect(res.body.delegates).to.have.lengthOf(10); @@ -1017,6 +1018,7 @@ describe('GET /api/delegates/getNextForgers', function () { node.get('/api/delegates/getNextForgers?' + 'limit=1', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('currentBlock').that.is.a('number'); + node.expect(res.body).to.have.property('currentBlockSlot').that.is.a('number'); node.expect(res.body).to.have.property('currentSlot').that.is.a('number'); node.expect(res.body).to.have.property('delegates').that.is.an('array'); node.expect(res.body.delegates).to.have.lengthOf(1); @@ -1028,6 +1030,7 @@ describe('GET /api/delegates/getNextForgers', function () { node.get('/api/delegates/getNextForgers?' + 'limit=101', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('currentBlock').that.is.a('number'); + node.expect(res.body).to.have.property('currentBlockSlot').that.is.a('number'); node.expect(res.body).to.have.property('currentSlot').that.is.a('number'); node.expect(res.body).to.have.property('delegates').that.is.an('array'); node.expect(res.body.delegates).to.have.lengthOf(101); From 6392cceaff1294e3e9d2bc5b51a20d9586ba54f8 Mon Sep 17 00:00:00 2001 From: Mq Date: Tue, 13 Dec 2016 14:18:04 +0100 Subject: [PATCH 010/123] Added exception for invalid multi-sig creation tx Fix https://github.com/LiskHQ/lisk/issues/351 issue. --- helpers/exceptions.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/helpers/exceptions.js b/helpers/exceptions.js index f598b30414b..b84a1aa98b4 100644 --- a/helpers/exceptions.js +++ b/helpers/exceptions.js @@ -22,9 +22,11 @@ module.exports = { signatures: [ '5676385569187187158', // 868797 '5384302058030309746', // 869890 - '9352922026980330230', // 925165 + '9352922026980330230' // 925165 + ], + multisignatures: [ + '14122550998639658526' // 1189962 ], - multisignatures: [], votes: [ '5524930565698900323', // 20407 '11613486949732674475', // 123300 From a35a09fa14433a8c60ae57f906b21fadedf86f1a Mon Sep 17 00:00:00 2001 From: Mq Date: Thu, 15 Dec 2016 22:04:51 +0100 Subject: [PATCH 011/123] Add possibility to get account via publicKey Closes https://github.com/LiskHQ/lisk/issues/355 --- modules/accounts.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/accounts.js b/modules/accounts.js index 2d6832a5d0b..0bc235a9661 100644 --- a/modules/accounts.js +++ b/modules/accounts.js @@ -473,7 +473,18 @@ shared.getAccount = function (req, cb) { return setImmediate(cb, err[0].message); } - self.getAccount({ address: req.body.address }, function (err, account) { + if (!req.body.address && !req.body.publicKey) { + return setImmediate(cb, 'Missing required property: address or publicKey'); + } + + // self.getAccount can accept publicKey as argument, but we also compare here + // if account publicKey match address (when both are supplied) + let address = req.body.publicKey ? self.generateAddressByPublicKey(req.body.publicKey) : req.body.address; + if (req.body.address && req.body.publicKey && address !== req.body.address) { + return setImmediate(cb, 'Account publicKey do not match address'); + } + + self.getAccount({ address: address }, function (err, account) { if (err) { return setImmediate(cb, err); } From b81b5a7fe3587289b0a6eda0ad55f357cacf6eba Mon Sep 17 00:00:00 2001 From: Mq Date: Thu, 15 Dec 2016 22:21:18 +0100 Subject: [PATCH 012/123] Changed schema validation rule for getAccount API endpoint --- schema/accounts.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/schema/accounts.js b/schema/accounts.js index 44f6db70c71..ec5ab44adb3 100644 --- a/schema/accounts.js +++ b/schema/accounts.js @@ -93,9 +93,12 @@ module.exports = { format: 'address', minLength: 1, maxLength: 22 + }, + publicKey: { + type: 'string', + format: 'publicKey' } - }, - required: ['address'] + } }, top: { id: 'accounts.top', From 22a1dc8d0eaeb5978218bd996dbf63cbb5005cdb Mon Sep 17 00:00:00 2001 From: Mq Date: Thu, 15 Dec 2016 23:30:07 +0100 Subject: [PATCH 013/123] Changed expectations for /api/accounts endpoint --- test/api/accounts.js | 138 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 130 insertions(+), 8 deletions(-) diff --git a/test/api/accounts.js b/test/api/accounts.js index 4a5b9b4b9c9..692e3b21b97 100644 --- a/test/api/accounts.js +++ b/test/api/accounts.js @@ -232,14 +232,31 @@ describe('POST /api/accounts/generatePublicKey', function () { }); }); -describe('GET /accounts?address=', function () { +describe('GET /accounts', function () { - function getAccounts (address, done) { - node.get('/api/accounts?address=' + address, done); + function getAccounts (params, done) { + node.get('/api/accounts?' + params, done); } it('using known address should be ok', function (done) { - getAccounts(node.gAccount.address, function (err, res) { + getAccounts('address=' + node.gAccount.address, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('account').that.is.an('object'); + node.expect(res.body.account).to.have.property('address').to.equal(node.gAccount.address); + node.expect(res.body.account).to.have.property('unconfirmedBalance').that.is.a('string'); + node.expect(res.body.account).to.have.property('balance').that.is.a('string'); + node.expect(res.body.account).to.have.property('publicKey').to.equal(node.gAccount.publicKey); + node.expect(res.body.account).to.have.property('unconfirmedSignature').to.equal(0); + node.expect(res.body.account).to.have.property('secondSignature').to.equal(0); + node.expect(res.body.account).to.have.property('secondPublicKey').to.equal(null); + node.expect(res.body.account).to.have.property('multisignatures').to.a('array'); + node.expect(res.body.account).to.have.property('u_multisignatures').to.a('array'); + done(); + }); + }); + + it('using known address and empty publicKey should be ok', function (done) { + getAccounts('address=' + node.gAccount.address + '&publicKey=', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('account').that.is.an('object'); node.expect(res.body.account).to.have.property('address').to.equal(node.gAccount.address); @@ -256,7 +273,7 @@ describe('GET /accounts?address=', function () { }); it('using known lowercase address should be ok', function (done) { - getAccounts(node.gAccount.address.toLowerCase(), function (err, res) { + getAccounts('address=' + node.gAccount.address.toLowerCase(), function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('account').that.is.an('object'); node.expect(res.body.account).to.have.property('address').to.equal(node.gAccount.address); @@ -273,7 +290,7 @@ describe('GET /accounts?address=', function () { }); it('using unknown address should fail', function (done) { - getAccounts(account.address, function (err, res) { + getAccounts('address=' + account.address, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.eql('Account not found'); done(); @@ -281,7 +298,7 @@ describe('GET /accounts?address=', function () { }); it('using invalid address should fail', function (done) { - getAccounts('thisIsNOTALiskAddress', function (err, res) { + getAccounts('address=' + 'thisIsNOTALiskAddress', function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); node.expect(res.body.error).to.contain('Object didn\'t pass validation for format address: thisIsNOTALiskAddress'); @@ -290,11 +307,116 @@ describe('GET /accounts?address=', function () { }); it('using empty address should fail', function (done) { - getAccounts('', function (err, res) { + getAccounts('address=', function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error'); + node.expect(res.body.error).to.contain('String is too short (0 chars), minimum 1'); + done(); + }); + }); + + it('using known publicKey should be ok', function (done) { + getAccounts('publicKey=' + node.gAccount.publicKey, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('account').that.is.an('object'); + node.expect(res.body.account).to.have.property('address').to.equal(node.gAccount.address); + node.expect(res.body.account).to.have.property('unconfirmedBalance').that.is.a('string'); + node.expect(res.body.account).to.have.property('balance').that.is.a('string'); + node.expect(res.body.account).to.have.property('publicKey').to.equal(node.gAccount.publicKey); + node.expect(res.body.account).to.have.property('unconfirmedSignature').to.equal(0); + node.expect(res.body.account).to.have.property('secondSignature').to.equal(0); + node.expect(res.body.account).to.have.property('secondPublicKey').to.equal(null); + node.expect(res.body.account).to.have.property('multisignatures').to.a('array'); + node.expect(res.body.account).to.have.property('u_multisignatures').to.a('array'); + done(); + }); + }); + + it('using known publicKey and empty address should be ok', function (done) { + getAccounts('publicKey=' + node.gAccount.publicKey + '&address=', function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('account').that.is.an('object'); + node.expect(res.body.account).to.have.property('address').to.equal(node.gAccount.address); + node.expect(res.body.account).to.have.property('unconfirmedBalance').that.is.a('string'); + node.expect(res.body.account).to.have.property('balance').that.is.a('string'); + node.expect(res.body.account).to.have.property('publicKey').to.equal(node.gAccount.publicKey); + node.expect(res.body.account).to.have.property('unconfirmedSignature').to.equal(0); + node.expect(res.body.account).to.have.property('secondSignature').to.equal(0); + node.expect(res.body.account).to.have.property('secondPublicKey').to.equal(null); + node.expect(res.body.account).to.have.property('multisignatures').to.a('array'); + node.expect(res.body.account).to.have.property('u_multisignatures').to.a('array'); + done(); + }); + }); + + it('using unknown publicKey should fail', function (done) { + getAccounts('publicKey=' + account.publicKey, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error').to.eql('Account not found'); + done(); + }); + }); + + it('using invalid publicKey should fail', function (done) { + getAccounts('publicKey=' + 'thisIsNOTALiskAccountPublicKey', function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error'); + node.expect(res.body.error).to.contain('Object didn\'t pass validation for format publicKey: thisIsNOTALiskAccountPublicKey'); + done(); + }); + }); + + it('using invalid publicKey (integer) should fail', function (done) { + getAccounts('publicKey=' + '123', function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error'); + node.expect(res.body.error).to.contain('Expected type string but found type integer'); + done(); + }); + }); + + it('using empty publicKey should fail', function (done) { + getAccounts('publicKey=', function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error'); + node.expect(res.body.error).to.contain('Missing required property: address or publicKey'); + done(); + }); + }); + + it('using empty publicKey and address should fail', function (done) { + getAccounts('publicKey=&address=', function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); node.expect(res.body.error).to.contain('String is too short (0 chars), minimum 1'); done(); }); }); + + it('using known address and matching publicKey should be ok', function (done) { + getAccounts('address=' + node.gAccount.address + '&publicKey=' + node.gAccount.publicKey, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('account').that.is.an('object'); + node.expect(res.body.account).to.have.property('address').to.equal(node.gAccount.address); + node.expect(res.body.account).to.have.property('unconfirmedBalance').that.is.a('string'); + node.expect(res.body.account).to.have.property('balance').that.is.a('string'); + node.expect(res.body.account).to.have.property('publicKey').to.equal(node.gAccount.publicKey); + node.expect(res.body.account).to.have.property('unconfirmedSignature').to.equal(0); + node.expect(res.body.account).to.have.property('secondSignature').to.equal(0); + node.expect(res.body.account).to.have.property('secondPublicKey').to.equal(null); + node.expect(res.body.account).to.have.property('multisignatures').to.a('array'); + node.expect(res.body.account).to.have.property('u_multisignatures').to.a('array'); + done(); + }); + }); + + it('using known address and not matching publicKey should fail', function (done) { + getAccounts('address=' + node.gAccount.address + '&publicKey=' + account.publicKey, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error'); + node.expect(res.body.error).to.contain('Account publicKey do not match address'); + done(); + }); + }); + }); From aa0509255a7f858bd26280563eeaf28643c8c3f4 Mon Sep 17 00:00:00 2001 From: Mq Date: Fri, 16 Dec 2016 22:21:24 +0100 Subject: [PATCH 014/123] Improve consistency on fork 1 and 5 recovery Closes https://github.com/LiskHQ/lisk/issues/358 --- modules/blocks.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/blocks.js b/modules/blocks.js index 7c5a9881e19..70c357056bb 100644 --- a/modules/blocks.js +++ b/modules/blocks.js @@ -1331,16 +1331,17 @@ Blocks.prototype.onReceiveBlock = function (block) { // Fork: Consecutive height but different previous block id. modules.delegates.fork(block, 1); - // If newly received block is older than last one - we have wrong parent and should rewind. - if (block.timestamp < __private.lastBlock.timestamp) { + // We should keep the oldest one or if both have same age - keep one with lower id + if (block.timestamp > __private.lastBlock.timestamp || (block.timestamp === __private.lastBlock.timestamp && block.id > __private.lastBlock.id)) { + library.logger.info('Last block stands'); + return setImmediate(cb); + } else { + // In other cases - we have wrong parent and should rewind. library.logger.info('Last block and parent loses'); async.series([ self.deleteLastBlock, self.deleteLastBlock ], cb); - } else { - library.logger.info('Last block stands'); - return setImmediate(cb); } } else if (block.previousBlock === __private.lastBlock.previousBlock && block.height === __private.lastBlock.height && block.id !== __private.lastBlock.id) { // Fork: Same height and previous block id, but different block id. @@ -1351,10 +1352,12 @@ Blocks.prototype.onReceiveBlock = function (block) { library.logger.warn('Delegate forging on multiple nodes', block.generatorPublicKey); } - // Two competiting blocks on same height - we should always keep the oldest one. - if (block.timestamp < __private.lastBlock.timestamp) { + // Two competiting blocks on same height, we should keep the oldest one or if both have same age - keep one with lower id + if (block.timestamp > __private.lastBlock.timestamp || (block.timestamp === __private.lastBlock.timestamp && block.id > __private.lastBlock.id)) { + library.logger.info('Last block stands'); + return setImmediate(cb); + } else { library.logger.info('Last block loses'); - async.series([ function (seriesCb) { self.deleteLastBlock(seriesCb); @@ -1363,9 +1366,6 @@ Blocks.prototype.onReceiveBlock = function (block) { return __private.receiveBlock(block, seriesCb); } ], cb); - } else { - library.logger.info('Last block stands'); - return setImmediate(cb); } } else { return setImmediate(cb); From d9d2b6f92073cd8fac17ebfb5d99499d77a3a8f8 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Fri, 16 Dec 2016 15:28:58 +0100 Subject: [PATCH 015/123] Setting up travis based continuous integration. --- .travis.yml | 15 +++++++++++++++ Gruntfile.js | 15 +++++++++++++++ README.md | 1 + package.json | 3 ++- 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000000..43ce311ee53 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +dist: 'trusty' +language: node_js +node_js: + - '0.12' +services: + - postgresql +addons: + postgresql: '9.6' +before_script: + - createdb lisk_test + - psql -d lisk_test -c "alter user "$USER" with password 'password';" + - wget https://downloads.lisk.io/lisk-node/lisk-node-Linux-x86_64.tar.gz + - tar -zxvf lisk-node-Linux-x86_64.tar.gz + - cp test/config.json test/genesisBlock.json . + - node app.js &> /dev/null & diff --git a/Gruntfile.js b/Gruntfile.js index e0c1392c815..4d275fc0efc 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -113,6 +113,19 @@ module.exports = function (grunt) { 'test/api/**/*.js', 'test/unit/**/*.js' ] + }, + + mochaTest: { + test: { + options: { + reporter: 'spec', + quiet: false, + clearRequireCache: false, + noFail: false, + timeout: '250s' + }, + src: ['test'] + } } }); @@ -123,7 +136,9 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-exec'); grunt.loadNpmTasks('grunt-contrib-compress'); grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-mocha-test'); grunt.registerTask('default', ['release']); grunt.registerTask('release', ['exec:folder', 'obfuscator', 'exec:package', 'exec:build', 'compress']); + grunt.registerTask('travis', 'mochaTest', 'jshint'); }; diff --git a/README.md b/README.md index 52a5060bd24..92e66fb2493 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ Lisk is a next generation crypto-currency and decentralized application platform, written entirely in JavaScript. For more information please refer to our website: https://lisk.io/. [![Join the chat at https://gitter.im/LiskHQ/lisk](https://badges.gitter.im/LiskHQ/lisk.svg)](https://gitter.im/LiskHQ/lisk?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Build Status](https://travis-ci.org/LiskHQ/lisk.svg?branch=development)](https://travis-ci.org/LiskHQ/lisk) ## Installation diff --git a/package.json b/package.json index 5bf84cb583c..61da9ae8668 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "start": "node app.js", - "test": "./node_modules/.bin/mocha", + "test": "./node_modules/.bin/grunt travis --verbose", "cov": "./node_modules/.bin/mocha --require blanket -R html-cov test/helpers test/logic > tmp/coverage.html" }, "author": "Boris Povod , Pavel Nekrasov , Oliver Beddows ", @@ -62,6 +62,7 @@ "grunt-contrib-jshint": "=1.0.0", "grunt-exec": "=1.0.1", "grunt-jsdox": "=0.1.7", + "grunt-mocha-test": "=0.13.2", "grunt-obfuscator": "=0.1.0", "jsdox": "=0.4.10", "jshint": "=2.9.3", From 847f6b40b52d914b133a489fa774f3fae3c8108d Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Mon, 19 Dec 2016 12:33:19 +0100 Subject: [PATCH 016/123] Fixing linting errors. --- modules/loader.js | 2 +- modules/transactions.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/loader.js b/modules/loader.js index 1cbacb79172..3f94fea704b 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -101,7 +101,7 @@ __private.syncTimer = function () { return setTimeout(nextSync, __private.syncInterval); } }); -} +}; __private.loadSignatures = function (cb) { async.waterfall([ diff --git a/modules/transactions.js b/modules/transactions.js index 1f77df9361e..35df8203f0a 100644 --- a/modules/transactions.js +++ b/modules/transactions.js @@ -190,7 +190,7 @@ __private.getById = function (id, cb) { __private.getVotesById = function (transaction, cb) { library.db.query(sql.getVotesById, {id: transaction.id}).then(function (rows) { if (!rows.length) { - return setImmediate(cb, 'Transaction not found: ' + id); + return setImmediate(cb, 'Transaction not found: ' + transaction.id); } var votes = rows[0].votes.split(','); @@ -198,9 +198,9 @@ __private.getVotesById = function (transaction, cb) { var deleted = []; for (var i = 0; i < votes.length; i++) { - if (votes[i].substring(0, 1) == "+") { + if (votes[i].substring(0, 1) === '+') { added.push (votes[i].substring(1)); - } else if (votes[i].substring(0, 1) == "-") { + } else if (votes[i].substring(0, 1) === '-') { deleted.push (votes[i].substring(1)); } } @@ -406,7 +406,7 @@ shared.getTransaction = function (req, cb) { return setImmediate(cb, 'Transaction not found'); } - if (transaction.type == 3) { + if (transaction.type === 3) { __private.getVotesById(transaction, function (err, transaction) { return setImmediate(cb, null, {transaction: transaction}); }); From 7f48782717c128018fd9798bcc27641d5d6becd1 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Mon, 19 Dec 2016 13:22:22 +0100 Subject: [PATCH 017/123] Fixing grunt.registerTask arguments. Changing task execution order. --- Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 4d275fc0efc..4127414686e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -140,5 +140,5 @@ module.exports = function (grunt) { grunt.registerTask('default', ['release']); grunt.registerTask('release', ['exec:folder', 'obfuscator', 'exec:package', 'exec:build', 'compress']); - grunt.registerTask('travis', 'mochaTest', 'jshint'); + grunt.registerTask('travis', ['jshint', 'mochaTest']); }; From 36671af69868a669e34ac02dfcda6d50847bf0a9 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Mon, 19 Dec 2016 13:34:21 +0100 Subject: [PATCH 018/123] Peforming npm install on lisk-js. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 43ce311ee53..95f7ab6a171 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,5 +11,6 @@ before_script: - psql -d lisk_test -c "alter user "$USER" with password 'password';" - wget https://downloads.lisk.io/lisk-node/lisk-node-Linux-x86_64.tar.gz - tar -zxvf lisk-node-Linux-x86_64.tar.gz + - cd test/lisk-js/; npm install; cd ../.. - cp test/config.json test/genesisBlock.json . - node app.js &> /dev/null & From 9be35847433587d602b9a3cf9b9d905e46b0c359 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Mon, 19 Dec 2016 16:29:50 +0100 Subject: [PATCH 019/123] Update exceptions.js --- helpers/exceptions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/exceptions.js b/helpers/exceptions.js index b84a1aa98b4..9ed1844059d 100644 --- a/helpers/exceptions.js +++ b/helpers/exceptions.js @@ -22,7 +22,7 @@ module.exports = { signatures: [ '5676385569187187158', // 868797 '5384302058030309746', // 869890 - '9352922026980330230' // 925165 + '9352922026980330230' // 925165 ], multisignatures: [ '14122550998639658526' // 1189962 From e2d2098f688563fae84c524659bf522a79fc2064 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Mon, 19 Dec 2016 14:58:17 +0100 Subject: [PATCH 020/123] Standarising code. --- sql/blocks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/blocks.js b/sql/blocks.js index b11ec294350..94cfbcf59d0 100644 --- a/sql/blocks.js +++ b/sql/blocks.js @@ -26,8 +26,8 @@ var BlocksSql = { aggregateBlocksReward: function (params) { return [ - 'SELECT sum ("b_totalFee") as fees, sum ("b_reward") as rewards, count (1) as count', - 'FROM blocks_list', + 'SELECT SUM("b_totalFee") AS fees, SUM("b_reward") AS rewards, COUNT(1) AS count', + 'FROM blocks_list', (params.where.length ? 'WHERE ' + params.where.join(' AND ') : '') ].filter(Boolean).join(' '); }, From 90cc4ac64ed008ae98747fabd6bd284961bd573d Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Mon, 19 Dec 2016 16:16:31 +0100 Subject: [PATCH 021/123] Describing GET /api/delegates/forging/getForgedByAccount. --- test/api/delegates.js | 97 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/test/api/delegates.js b/test/api/delegates.js index 3e648dc27c1..20351c08e29 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -1000,6 +1000,103 @@ describe('GET /api/delegates/forging/status', function () { }); }); +describe('GET /api/delegates/forging/getForgedByAccount', function () { + + var validParams; + + beforeEach(function () { + validParams = { + generatorPublicKey: '9d3058175acab969f41ad9b86f7a2926c74258670fe56b37c429c01fca9f2f0f', + start: 0, + end: 0 + }; + }); + + function buildParams () { + return [ + 'generatorPublicKey=' + validParams.generatorPublicKey, + validParams.start ? 'start=' + validParams.start : '', + validParams.end ? 'end=' + validParams.end : '', + ].filter(Boolean).join('&'); + } + + it('using no params should fail', function (done) { + node.get('/api/delegates/forging/getForgedByAccount', function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error').to.eql('Missing required property: generatorPublicKey'); + done(); + }); + }); + + it('using valid params should be ok', function (done) { + node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('fees').that.is.a('string').and.eql('0'); + node.expect(res.body).to.have.property('rewards').that.is.a('string').and.eql('0'); + node.expect(res.body).to.have.property('forged').that.is.a('string').and.eql('0'); + done(); + }); + }); + + it('using unknown generatorPublicKey should fail', function (done) { + validParams.generatorPublicKey = node.randomAccount().publicKey; + + node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error').to.eql('Account not found'); + done(); + }); + }); + + it('using invalid generatorPublicKey should fail', function (done) { + validParams.generatorPublicKey = 'invalidPublicKey'; + + node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error').to.eql('Object didn\'t pass validation for format publicKey: invalidPublicKey'); + done(); + }); + }); + + it('using no start should be ok', function (done) { + delete validParams.start; + + node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + done(); + }); + }); + + it('using no end should be ok', function (done) { + delete validParams.end; + + node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + done(); + }); + }); + + it('using string start should fail', function (done) { + validParams.start = 'one'; + + node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error').to.eql('Expected type integer but found type string'); + done(); + }); + }); + + it('using string end should fail', function (done) { + validParams.end = 'two'; + + node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error').to.eql('Expected type integer but found type string'); + done(); + }); + }); +}); + describe('GET /api/delegates/getNextForgers', function () { it('using no params should be ok', function (done) { From 90e0eabe4aa0dbb88877831f23af91615df9cdf3 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Mon, 19 Dec 2016 17:55:42 +0100 Subject: [PATCH 022/123] Fixing linting error; Replacing let keyword. --- modules/accounts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/accounts.js b/modules/accounts.js index 0bc235a9661..ee55b78361d 100644 --- a/modules/accounts.js +++ b/modules/accounts.js @@ -477,9 +477,9 @@ shared.getAccount = function (req, cb) { return setImmediate(cb, 'Missing required property: address or publicKey'); } - // self.getAccount can accept publicKey as argument, but we also compare here + // self.getAccount can accept publicKey as argument, but we also compare here // if account publicKey match address (when both are supplied) - let address = req.body.publicKey ? self.generateAddressByPublicKey(req.body.publicKey) : req.body.address; + var address = req.body.publicKey ? self.generateAddressByPublicKey(req.body.publicKey) : req.body.address; if (req.body.address && req.body.publicKey && address !== req.body.address) { return setImmediate(cb, 'Account publicKey do not match address'); } From c6f0c58a8851d55f62275ec17b1d0a33e9ced58d Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Mon, 19 Dec 2016 18:16:01 +0100 Subject: [PATCH 023/123] Changing expectation. Using known publicKey and empty address. --- test/api/accounts.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/test/api/accounts.js b/test/api/accounts.js index 692e3b21b97..494c3477aa6 100644 --- a/test/api/accounts.js +++ b/test/api/accounts.js @@ -332,19 +332,10 @@ describe('GET /accounts', function () { }); }); - it('using known publicKey and empty address should be ok', function (done) { + it('using known publicKey and empty address should fail', function (done) { getAccounts('publicKey=' + node.gAccount.publicKey + '&address=', function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('account').that.is.an('object'); - node.expect(res.body.account).to.have.property('address').to.equal(node.gAccount.address); - node.expect(res.body.account).to.have.property('unconfirmedBalance').that.is.a('string'); - node.expect(res.body.account).to.have.property('balance').that.is.a('string'); - node.expect(res.body.account).to.have.property('publicKey').to.equal(node.gAccount.publicKey); - node.expect(res.body.account).to.have.property('unconfirmedSignature').to.equal(0); - node.expect(res.body.account).to.have.property('secondSignature').to.equal(0); - node.expect(res.body.account).to.have.property('secondPublicKey').to.equal(null); - node.expect(res.body.account).to.have.property('multisignatures').to.a('array'); - node.expect(res.body.account).to.have.property('u_multisignatures').to.a('array'); + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error').to.eql('String is too short (0 chars), minimum 1'); done(); }); }); From daba9bc09a47fa5bb509201e20f0360a1ed5aa16 Mon Sep 17 00:00:00 2001 From: Mq Date: Mon, 19 Dec 2016 18:27:49 +0100 Subject: [PATCH 024/123] Redesign /api/delegates/forging/status endpoint - endpoint is now private and use same whitelist as `/api/delegates/forging/enable` and `/api/delegates/forging/disable` - endpoint without parameters return `status` and array of delegates public keys that currently forging on node - whitelist checking is performed first --- modules/delegates.js | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/modules/delegates.js b/modules/delegates.js index bcfbfddde60..7a7573af7bc 100644 --- a/modules/delegates.js +++ b/modules/delegates.js @@ -86,17 +86,15 @@ __private.attachApi = function () { } router.post('/forging/enable', function (req, res) { + if (!checkIpInList(library.config.forging.access.whiteList, req.ip)) { + return res.json({success: false, error: 'Access denied'}); + } + library.schema.validate(req.body, schema.enableForging, function (err) { if (err) { return res.json({success: false, error: err[0].message}); } - var ip = req.ip; - - if (!checkIpInList(library.config.forging.access.whiteList, ip)) { - return res.json({success: false, error: 'Access denied'}); - } - var keypair = library.ed.makeKeypair(crypto.createHash('sha256').update(req.body.secret, 'utf8').digest()); if (req.body.publicKey) { @@ -125,17 +123,15 @@ __private.attachApi = function () { }); router.post('/forging/disable', function (req, res) { + if (!checkIpInList(library.config.forging.access.whiteList, req.ip)) { + return res.json({success: false, error: 'Access denied'}); + } + library.schema.validate(req.body, schema.disableForging, function (err) { if (err) { return res.json({success: false, error: err[0].message}); } - var ip = req.ip; - - if (!checkIpInList(library.config.forging.access.whiteList, ip)) { - return res.json({success: false, error: 'Access denied'}); - } - var keypair = library.ed.makeKeypair(crypto.createHash('sha256').update(req.body.secret, 'utf8').digest()); if (req.body.publicKey) { @@ -164,12 +160,21 @@ __private.attachApi = function () { }); router.get('/forging/status', function (req, res) { + if (!checkIpInList(library.config.forging.access.whiteList, req.ip)) { + return res.json({success: false, error: 'Access denied'}); + } + library.schema.validate(req.query, schema.forgingStatus, function (err) { if (err) { return res.json({success: false, error: err[0].message}); } - return res.json({success: true, enabled: !!__private.keypairs[req.query.publicKey]}); + if (req.query.publicKey) { + return res.json({success: true, enabled: !!__private.keypairs[req.query.publicKey]}); + } else { + var delegates_cnt = _.keys(__private.keypairs).length; + return res.json({success: true, enabled: (delegates_cnt > 0 ? true : false), delegates: _.keys(__private.keypairs) }); + } }); }); From 0fd47f97c075e210fd6e117ccf9ce2ea07cd45fd Mon Sep 17 00:00:00 2001 From: Mq Date: Mon, 19 Dec 2016 18:29:36 +0100 Subject: [PATCH 025/123] Property publicKey is no longer required one --- schema/delegates.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/schema/delegates.js b/schema/delegates.js index c83e8e7c126..038bacd9282 100644 --- a/schema/delegates.js +++ b/schema/delegates.js @@ -43,8 +43,7 @@ module.exports = { type: 'string', format: 'publicKey' } - }, - required: ['publicKey'] + } }, getDelegate: { id: 'delegates.getDelegate', From 9a6de2d1e67ce3f232d410d042b63c6896f0912f Mon Sep 17 00:00:00 2001 From: Mq Date: Mon, 19 Dec 2016 18:31:36 +0100 Subject: [PATCH 026/123] Changed expectations for /api/delegates/forging/status --- test/api/delegates.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/api/delegates.js b/test/api/delegates.js index 3ef419af95c..3147872b892 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -959,10 +959,11 @@ describe('GET /api/delegates/search', function () { }); describe('GET /api/delegates/forging/status', function () { - it('using no params should fail', function (done) { + it('using no params should be ok', function (done) { node.get('/api/delegates/forging/status', function (err, res) { - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('error').to.eql('Missing required property: publicKey'); + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('enabled').to.be.true; + node.expect(res.body).to.have.property('delegates').that.is.an('array'); done(); }); }); @@ -978,7 +979,8 @@ describe('GET /api/delegates/forging/status', function () { it('using empty publicKey should be ok', function (done) { node.get('/api/delegates/forging/status?publicKey=', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('enabled').to.be.false; + node.expect(res.body).to.have.property('enabled').to.be.true; + node.expect(res.body).to.have.property('delegates').that.is.an('array'); done(); }); }); From 7ec118e5da2d86c9cc265da5225fb66c82a19ea6 Mon Sep 17 00:00:00 2001 From: Davide Gessa Date: Thu, 22 Dec 2016 14:05:55 +0100 Subject: [PATCH 027/123] Peers count API --- modules/peers.js | 68 ++++++++++++++++++++++++++++++++++++++++++++++- sql/peers.js | 7 +++++ test/api/peers.js | 13 +++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/modules/peers.js b/modules/peers.js index 7027200e81c..81d0df42c65 100644 --- a/modules/peers.js +++ b/modules/peers.js @@ -46,7 +46,8 @@ __private.attachApi = function () { router.map(shared, { 'get /': 'getPeers', 'get /version': 'version', - 'get /get': 'getPeer' + 'get /get': 'getPeer', + 'get /count': 'count' }); router.use(function (req, res) { @@ -151,6 +152,51 @@ __private.count = function (cb) { }); }; +__private.countByFilter = function (filter, cb) { + var where = []; + var params = {}; + + if (filter.port) { + where.push('"port" = ${port}'); + params.port = filter.port; + } + + if (filter.state >= 0) { + where.push('"state" = ${state}'); + params.state = filter.state; + } + + if (filter.os) { + where.push('"os" = ${os}'); + params.os = filter.os; + } + + if (filter.version) { + where.push('"version" = ${version}'); + params.version = filter.version; + } + + if (filter.broadhash) { + where.push('"broadhash" = ${broadhash}'); + params.broadhash = filter.broadhash; + } + + if (filter.height) { + where.push('"height" = ${height}'); + params.height = filter.height; + } + + library.db.query(sql.countByFilter({ + where: where + }), params).then(function (rows) { + var res = rows.length && rows[0].count; + return setImmediate(cb, null, res); + }).catch(function (err) { + library.logger.error(err.stack); + return setImmediate(cb, 'Peers#count error'); + }); +}; + __private.banManager = function (cb) { library.db.query(sql.banManager, { now: Date.now() }).then(function (res) { return setImmediate(cb, null, res); @@ -434,6 +480,26 @@ Peers.prototype.onPeersReady = function () { }; // Shared +shared.count = function (req, cb) { + async.series({ + connected: function (cb) { + __private.countByFilter({state: 2}, cb); + }, + disconnected: function (cb) { + __private.countByFilter({state: 1}, cb); + }, + banned: function (cb) { + __private.countByFilter({state: 0}, cb); + } + }, function (err, res) { + if (err) { + return setImmediate(cb, 'Failed to get peer count'); + } + + return setImmediate(cb, null, res); + }); +}; + shared.getPeers = function (req, cb) { library.schema.validate(req.body, schema.getPeers, function (err) { if (err) { diff --git a/sql/peers.js b/sql/peers.js index d707cf83831..f7fdbd3773a 100644 --- a/sql/peers.js +++ b/sql/peers.js @@ -7,6 +7,13 @@ var PeersSql = { banManager: 'UPDATE peers SET "state" = 1, "clock" = null WHERE ("state" = 0 AND "clock" - ${now} < 0)', + countByFilter: function (params) { + return [ + 'SELECT COUNT(*)::int FROM peers', + (params.where.length ? 'WHERE ' + params.where.join(' AND ') : '') + ].filter(Boolean).join(' '); + }, + getByFilter: function (params) { return [ 'SELECT "ip", "port", "state", "os", "version", ENCODE("broadhash", \'hex\') AS "broadhash", "height" FROM peers', diff --git a/test/api/peers.js b/test/api/peers.js index 7109e25ae64..4a25c8f1df7 100644 --- a/test/api/peers.js +++ b/test/api/peers.js @@ -14,6 +14,19 @@ describe('GET /api/peers/version', function () { }); }); +describe('GET /api/peers/count', function () { + + it('check for a valid response', function (done) { + node.get('/api/peers/count', function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('connected').that.is.a('number'); + node.expect(res.body).to.have.property('disconnected').that.is.a('number'); + node.expect(res.body).to.have.property('banned').that.is.a('number'); + done (); + }); + }); +}); + describe('GET /api/peers', function () { it('using invalid ip should fail', function (done) { From b570ec2dff807d3f887381e06514d3b21b51bcef Mon Sep 17 00:00:00 2001 From: TheGoldenEye Date: Sat, 31 Dec 2016 15:56:18 +0100 Subject: [PATCH 028/123] pass the debug status to the app --- modules/dapps.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/dapps.js b/modules/dapps.js index a6df7a3171e..7b04917a9c7 100644 --- a/modules/dapps.js +++ b/modules/dapps.js @@ -1069,7 +1069,13 @@ __private.createSandbox = function (dapp, params, cb) { return setImmediate(cb, err); } - var sandbox = new Sandbox(path.join(dappPath, 'index.js'), dapp.transactionId, params, __private.apiHandler, true); + var withDebug = false; + process.execArgv.forEach( function(item, index) { + if (item.indexOf('--debug') >= 0) + withDebug = true; + }); + + var sandbox = new Sandbox(path.join(dappPath, 'index.js'), dapp.transactionId, params, __private.apiHandler, withDebug); __private.sandboxes[dapp.transactionId] = sandbox; sandbox.on('exit', function () { From 27842739535b2e3254b8721ab537d5dc91d58ac6 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Mon, 2 Jan 2017 17:04:28 +0100 Subject: [PATCH 029/123] Specifying exact node_js version. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 95f7ab6a171..c97a25cab25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ dist: 'trusty' language: node_js node_js: - - '0.12' + - '0.12.17' services: - postgresql addons: From 0fe481e722d3b50ce6de333516bb7a03ee84f295 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Tue, 3 Jan 2017 13:39:57 +0100 Subject: [PATCH 030/123] Changing example description --- test/api/peers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api/peers.js b/test/api/peers.js index 4a25c8f1df7..d0dd7283109 100644 --- a/test/api/peers.js +++ b/test/api/peers.js @@ -16,7 +16,7 @@ describe('GET /api/peers/version', function () { describe('GET /api/peers/count', function () { - it('check for a valid response', function (done) { + it('should be ok', function (done) { node.get('/api/peers/count', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('connected').that.is.a('number'); From c7c0564463d2cd69081da399f650c1dcadbbfe9b Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Tue, 3 Jan 2017 14:32:21 +0100 Subject: [PATCH 031/123] Add API call to get transactions count Allows to see how many confirmed, unconfirmed, multisignature and queued transactions exists in particular moment via API call. closes #341 --- modules/transactions.js | 15 +++++++++++++++ sql/transactions.js | 2 ++ test/api/transactions.js | 14 ++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/modules/transactions.js b/modules/transactions.js index 35df8203f0a..57030ca41d5 100644 --- a/modules/transactions.js +++ b/modules/transactions.js @@ -49,6 +49,7 @@ __private.attachApi = function () { router.map(shared, { 'get /': 'getTransactions', 'get /get': 'getTransaction', + 'get /count': 'getTransactionsCount', 'get /queued/get': 'getQueuedTransaction', 'get /queued': 'getQueuedTransactions', 'get /multisignatures/get': 'getMultisignatureTransaction', @@ -417,6 +418,20 @@ shared.getTransaction = function (req, cb) { }); }; +shared.getTransactionsCount = function (req, cb) { + library.db.query(sql.count).then(function (transactionsCount) { + return setImmediate(cb, null, { + confirmed: transactionsCount[0].count, + multisignature: __private.transactionPool.multisignature.transactions.length, + unconfirmed: __private.transactionPool.unconfirmed.transactions.length, + queued: __private.transactionPool.queued.transactions.length + }); + + }, function (err) { + return setImmediate(cb, 'Unable to count transactions'); + }); +}; + shared.getQueuedTransaction = function (req, cb) { return __private.getPooledTransaction('getQueuedTransaction', req, cb); }; diff --git a/sql/transactions.js b/sql/transactions.js index cb2d2cf7d2c..2df17bd7ba3 100644 --- a/sql/transactions.js +++ b/sql/transactions.js @@ -15,6 +15,8 @@ var TransactionsSql = { 'height' ], + count: 'SELECT COUNT("id")::int AS "count" FROM trs', + countById: 'SELECT COUNT("id")::int AS "count" FROM trs WHERE "id" = ${id}', countList: function (params) { diff --git a/test/api/transactions.js b/test/api/transactions.js index b91b738d2fe..f7ad1336660 100644 --- a/test/api/transactions.js +++ b/test/api/transactions.js @@ -247,6 +247,20 @@ describe('GET /api/transactions/get?id=', function () { }); }); +describe('GET /api/transactions/count', function () { + + it('should be ok', function (done) { + node.get('/api/transactions/count', function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('confirmed').that.is.an('number'); + node.expect(res.body).to.have.property('queued').that.is.an('number'); + node.expect(res.body).to.have.property('multisignature').that.is.an('number'); + node.expect(res.body).to.have.property('unconfirmed').that.is.an('number'); + done(); + }); + }); +}); + describe('GET /api/transactions/queued/get?id=', function () { it('using unknown id should be ok', function (done) { From 01e6541110d61eb6d957776d2a803da29b24d46c Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Wed, 4 Jan 2017 16:44:02 +0100 Subject: [PATCH 032/123] Add latest commit info reachable through API After call to /api/peers/version return build info if lisk was installed from binaries or last commit if installed using source. closes #340 --- helpers/git.js | 13 +++++++++++++ modules/peers.js | 10 +++++++++- test/api/peers.js | 4 ++-- 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 helpers/git.js diff --git a/helpers/git.js b/helpers/git.js new file mode 100644 index 00000000000..a1de77888c7 --- /dev/null +++ b/helpers/git.js @@ -0,0 +1,13 @@ +'use strict'; +var childProcess = require('child_process'); + +/** + @throws Will throw an error if the git is not installed or not a git repository. + */ +function getLastCommit() { + return childProcess.execSync('git rev-parse HEAD').toString().trim(); +} + +module.exports = { + getLastCommit: getLastCommit +}; \ No newline at end of file diff --git a/modules/peers.js b/modules/peers.js index 81d0df42c65..bb6730ebcf9 100644 --- a/modules/peers.js +++ b/modules/peers.js @@ -6,6 +6,7 @@ var constants = require('../helpers/constants.js'); var extend = require('extend'); var fs = require('fs'); var ip = require('ip'); +var git = require('../helpers/git.js'); var OrderBy = require('../helpers/orderBy.js'); var path = require('path'); var Peer = require('../logic/peer.js'); @@ -544,7 +545,14 @@ shared.getPeer = function (req, cb) { }; shared.version = function (req, cb) { - return setImmediate(cb, null, {version: library.config.version, build: library.build}); + try { + var response = library.build ? {build: library} : {commit: git.getLastCommit()}; + response.version = library.config.version; + return setImmediate(cb, null, response); + } + catch (err) { + return setImmediate(cb, 'Git is not installed or not a git repository'); + } }; // Export diff --git a/test/api/peers.js b/test/api/peers.js index d0dd7283109..a4af2bfca26 100644 --- a/test/api/peers.js +++ b/test/api/peers.js @@ -4,10 +4,10 @@ var node = require('./../node.js'); describe('GET /api/peers/version', function () { - it('should be ok', function (done) { + it('should be working with sources', function (done) { node.get('/api/peers/version', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('build').to.be.a('string'); + node.expect(res.body).to.have.property('commit').to.be.a('string'); node.expect(res.body).to.have.property('version').to.be.a('string'); done(); }); From 983cdb864a9bedefb4a1c0253d179d8fd65303bd Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Thu, 5 Jan 2017 10:41:00 +0100 Subject: [PATCH 033/123] Add both commit and build info through API Fix case when no response is returned when lisk was installed neither from source nor binaries. fixes #371 --- helpers/git.js | 10 ++++++---- modules/peers.js | 13 +++++-------- test/api/peers.js | 3 ++- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/helpers/git.js b/helpers/git.js index a1de77888c7..1f79e5fc521 100644 --- a/helpers/git.js +++ b/helpers/git.js @@ -1,11 +1,13 @@ 'use strict'; var childProcess = require('child_process'); -/** - @throws Will throw an error if the git is not installed or not a git repository. - */ function getLastCommit() { - return childProcess.execSync('git rev-parse HEAD').toString().trim(); + try { + return childProcess.execSync('git rev-parse HEAD').toString().trim(); + } + catch (err) { + return ''; + } } module.exports = { diff --git a/modules/peers.js b/modules/peers.js index bb6730ebcf9..b93ef7e2e8a 100644 --- a/modules/peers.js +++ b/modules/peers.js @@ -545,14 +545,11 @@ shared.getPeer = function (req, cb) { }; shared.version = function (req, cb) { - try { - var response = library.build ? {build: library} : {commit: git.getLastCommit()}; - response.version = library.config.version; - return setImmediate(cb, null, response); - } - catch (err) { - return setImmediate(cb, 'Git is not installed or not a git repository'); - } + return setImmediate(cb, null, { + build: library.build, + commit: git.getLastCommit(), + version: library.config.version + }); }; // Export diff --git a/test/api/peers.js b/test/api/peers.js index a4af2bfca26..d3be8102d39 100644 --- a/test/api/peers.js +++ b/test/api/peers.js @@ -4,9 +4,10 @@ var node = require('./../node.js'); describe('GET /api/peers/version', function () { - it('should be working with sources', function (done) { + it('should be ok', function (done) { node.get('/api/peers/version', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('build').to.be.a('string'); node.expect(res.body).to.have.property('commit').to.be.a('string'); node.expect(res.body).to.have.property('version').to.be.a('string'); done(); From 5b9ae96251b09083bbae2591412b55716c7d0ad8 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Thu, 5 Jan 2017 10:07:39 +0100 Subject: [PATCH 034/123] Display null values as last while queries with ORDER_BY Add NULLS LAST option to sql queries with ORDER_BY closes #356 --- sql/blocks.js | 8 ++++---- sql/dapps.js | 6 +++--- sql/delegates.js | 2 +- sql/loader.js | 2 +- sql/peers.js | 2 +- sql/system.js | 2 +- sql/transactions.js | 2 +- sql/transport.js | 2 +- test/api/peers.js | 26 ++++++++++++++++++++++++++ 9 files changed, 39 insertions(+), 13 deletions(-) diff --git a/sql/blocks.js b/sql/blocks.js index 94cfbcf59d0..dc9f9a9ac03 100644 --- a/sql/blocks.js +++ b/sql/blocks.js @@ -36,14 +36,14 @@ var BlocksSql = { return [ 'SELECT * FROM blocks_list', (params.where.length ? 'WHERE ' + params.where.join(' AND ') : ''), - (params.sortField ? 'ORDER BY ' + [params.sortField, params.sortMethod].join(' ') : ''), + (params.sortField ? 'ORDER BY ' + [params.sortField, params.sortMethod].join(' ') + ' NULLS LAST' : ''), 'LIMIT ${limit} OFFSET ${offset}' ].filter(Boolean).join(' '); }, getById: 'SELECT * FROM blocks_list WHERE "b_id" = ${id}', - getIdSequence: 'SELECT (ARRAY_AGG("id" ORDER BY "height" ASC))[1] AS "id", MIN("height") AS "height", CAST("height" / ${delegates} AS INTEGER) + (CASE WHEN "height" % ${activeDelegates} > 0 THEN 1 ELSE 0 END) AS "round" FROM blocks WHERE "height" <= ${height} GROUP BY "round" ORDER BY "height" DESC LIMIT ${limit}', + getIdSequence: 'SELECT (ARRAY_AGG("id" ORDER BY "height" ASC NULLS LAST))[1] AS "id", MIN("height") AS "height", CAST("height" / ${delegates} AS INTEGER) + (CASE WHEN "height" % ${activeDelegates} > 0 THEN 1 ELSE 0 END) AS "round" FROM blocks WHERE "height" <= ${height} GROUP BY "round" ORDER BY "height" DESC NULLS LAST LIMIT ${limit}', getCommonBlock: function (params) { return [ @@ -75,9 +75,9 @@ var BlocksSql = { ].filter(Boolean).join(' '); }, - loadBlocksOffset: 'SELECT * FROM full_blocks_list WHERE "b_height" >= ${offset} AND "b_height" < ${limit} ORDER BY "b_height", "t_rowId"', + loadBlocksOffset: 'SELECT * FROM full_blocks_list WHERE "b_height" >= ${offset} AND "b_height" < ${limit} ORDER BY "b_height", "t_rowId" NULLS LAST', - loadLastBlock: 'SELECT * FROM full_blocks_list WHERE "b_height" = (SELECT MAX("height") FROM blocks) ORDER BY "b_height", "t_rowId"', + loadLastBlock: 'SELECT * FROM full_blocks_list WHERE "b_height" = (SELECT MAX("height") FROM blocks) ORDER BY "b_height", "t_rowId" NULLS LAST', getBlockId: 'SELECT "id" FROM blocks WHERE "id" = ${id}', diff --git a/sql/dapps.js b/sql/dapps.js index 5788551dfcd..9e91e7e8ebe 100644 --- a/sql/dapps.js +++ b/sql/dapps.js @@ -27,7 +27,7 @@ var DappsSql = { return [ 'SELECT "name", "description", "tags", "link", "type", "category", "icon", "transactionId" FROM dapps', (params.where.length ? 'WHERE ' + params.where.join(' OR ') : ''), - (params.sortField ? 'ORDER BY ' + [params.sortField, params.sortMethod].join(' ') : ''), + (params.sortField ? 'ORDER BY ' + [params.sortField, params.sortMethod].join(' ') + ' NULLS LAST' : ''), 'LIMIT ${limit} OFFSET ${offset}' ].filter(Boolean).join(' '); }, @@ -36,7 +36,7 @@ var DappsSql = { getCommonBlock: 'SELECT b."height" AS "height", t."id" AS "id", t."senderId" AS "senderId", t."amount" AS "amount" FROM trs t INNER JOIN blocks b ON t."blockId" = b."id" AND t."id" = ${id} AND t."type" = ${type} INNER JOIN intransfer dt ON dt."transactionId" = t."id" AND dt."dappid" = ${dappid}', - getWithdrawalLastTransaction: 'SELECT ot."outTransactionId" FROM trs t INNER JOIN blocks b ON t."blockId" = b."id" AND t."type" = ${type} INNER JOIN outtransfer ot ON ot."transactionId" = t."id" AND ot."dappId" = ${dappid} ORDER BY b."height" DESC LIMIT 1', + getWithdrawalLastTransaction: 'SELECT ot."outTransactionId" FROM trs t INNER JOIN blocks b ON t."blockId" = b."id" AND t."type" = ${type} INNER JOIN outtransfer ot ON ot."transactionId" = t."id" AND ot."dappId" = ${dappid} ORDER BY b."height" DESC LIMIT 1 NULLS LAST', getBalanceTransactions: function (params) { return [ @@ -44,7 +44,7 @@ var DappsSql = { 'INNER JOIN blocks b ON t."blockId" = b."id" AND t."type" = ${type}', 'INNER JOIN intransfer dt ON dt."transactionId" = t."id" AND dt."dappId" = ${dappid}', (params.lastId ? 'WHERE b."height" > (SELECT "height" FROM blocks ib INNER JOIN trs it ON ib."id" = it."blockId" AND it."id" = ${lastId})' : ''), - 'ORDER BY b."height"' + 'ORDER BY b."height" NULLS LAST' ].filter(Boolean).join(' '); } }; diff --git a/sql/delegates.js b/sql/delegates.js index ad2a17f490b..425a8d9245b 100644 --- a/sql/delegates.js +++ b/sql/delegates.js @@ -19,7 +19,7 @@ var DelegatesSql = { 'SELECT m."username", m."address", ENCODE(m."publicKey", \'hex\') AS "publicKey", m."vote", m."producedblocks", m."missedblocks"', 'FROM mem_accounts m', 'WHERE m."isDelegate" = 1 AND m."username" LIKE ${q}', - 'ORDER BY ' + [params.sortField, params.sortMethod].join(' '), + 'ORDER BY ' + [params.sortField, params.sortMethod].join(' ') + ' NULLS LAST', 'LIMIT ${limit}' ].join(' '); diff --git a/sql/loader.js b/sql/loader.js index b777856c60f..74e829899c5 100644 --- a/sql/loader.js +++ b/sql/loader.js @@ -5,7 +5,7 @@ var LoaderSql = { getGenesisBlock: 'SELECT "id", "payloadHash", "blockSignature" FROM blocks WHERE "height" = 1', - countMemAccounts: 'SELECT COUNT(*)::int FROM mem_accounts WHERE "blockId" = (SELECT "id" FROM "blocks" ORDER BY "height" DESC LIMIT 1)', + countMemAccounts: 'SELECT COUNT(*)::int FROM mem_accounts WHERE "blockId" = (SELECT "id" FROM "blocks" ORDER BY "height" DESC NULLS LAST LIMIT 1)', getMemRounds: 'SELECT "round" FROM mem_round GROUP BY "round"', diff --git a/sql/peers.js b/sql/peers.js index f7fdbd3773a..0107a9179ee 100644 --- a/sql/peers.js +++ b/sql/peers.js @@ -18,7 +18,7 @@ var PeersSql = { return [ 'SELECT "ip", "port", "state", "os", "version", ENCODE("broadhash", \'hex\') AS "broadhash", "height" FROM peers', (params.where.length ? 'WHERE ' + params.where.join(' AND ') : ''), - (params.sortField ? 'ORDER BY ' + [params.sortField, params.sortMethod].join(' ') : 'ORDER BY RANDOM()'), + (params.sortField ? 'ORDER BY ' + [params.sortField, params.sortMethod].join(' ') + ' NULLS LAST' : 'ORDER BY RANDOM()'), 'LIMIT ${limit} OFFSET ${offset}' ].filter(Boolean).join(' '); }, diff --git a/sql/system.js b/sql/system.js index eef397194cf..43119f7a6b7 100644 --- a/sql/system.js +++ b/sql/system.js @@ -1,7 +1,7 @@ 'use strict'; var SystemSql = { - getBroadhash: 'SELECT "id" FROM blocks ORDER BY "height" DESC LIMIT ${limit}' + getBroadhash: 'SELECT "id" FROM blocks ORDER BY "height" DESC NULLS LAST LIMIT ${limit}' }; module.exports = SystemSql; diff --git a/sql/transactions.js b/sql/transactions.js index 2df17bd7ba3..e07f52a31b8 100644 --- a/sql/transactions.js +++ b/sql/transactions.js @@ -36,7 +36,7 @@ var TransactionsSql = { (params.where.length || params.owner ? 'WHERE' : ''), (params.where.length ? '(' + params.where.join(' OR ') + ')' : ''), (params.where.length && params.owner ? ' AND ' + params.owner : params.owner), - (params.sortField ? 'ORDER BY ' + [params.sortField, params.sortMethod].join(' ') : ''), + (params.sortField ? 'ORDER BY ' + [params.sortField, params.sortMethod].join(' ') + ' NULLS LAST' : ''), 'LIMIT ${limit} OFFSET ${offset}' ].filter(Boolean).join(' '); }, diff --git a/sql/transport.js b/sql/transport.js index 0ec2542cd7f..d481ad813ea 100644 --- a/sql/transport.js +++ b/sql/transport.js @@ -1,7 +1,7 @@ 'use strict'; var TransportSql = { - getCommonBlock: 'SELECT MAX("height") AS "height", "id", "previousBlock", "timestamp" FROM blocks WHERE "id" IN ($1:csv) GROUP BY "id" ORDER BY "height" DESC' + getCommonBlock: 'SELECT MAX("height") AS "height", "id", "previousBlock", "timestamp" FROM blocks WHERE "id" IN ($1:csv) GROUP BY "id" ORDER BY "height" DESC NULLS LAST' }; module.exports = TransportSql; diff --git a/test/api/peers.js b/test/api/peers.js index d0dd7283109..5e8c8b4369f 100644 --- a/test/api/peers.js +++ b/test/api/peers.js @@ -348,6 +348,32 @@ describe('GET /api/peers', function () { }); }); + it('using orderBy == "height:desc" should not place NULLs first', function (done) { + var orderBy = 'height:desc'; + var params = 'orderBy=' + orderBy; + + node.get('/api/peers?' + params, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('peers').that.is.an('array'); + + var dividedIndices = res.body.peers.reduce(function (memo, peer, index) { + memo[peer.height === null ? 'nullIndices' : 'notNullIndices'].push(index); + return memo; + }, {notNullIndices: [], nullIndices: []}); + + if (dividedIndices.nullIndices.length && dividedIndices.notNullIndices.length) { + var ascOrder = function (a, b) { return a - b; }; + dividedIndices.notNullIndices.sort(ascOrder); + dividedIndices.nullIndices.sort(ascOrder); + + node.expect(dividedIndices.notNullIndices[dividedIndices.notNullIndices.length - 1]) + .to.be.at.most(dividedIndices.nullIndices[0]); + } + + done(); + }); + }); + it('using string limit should fail', function (done) { var limit = 'one'; var params = 'limit=' + limit; From 35c845d889b6e2bf414dcb7397315a75d86df00a Mon Sep 17 00:00:00 2001 From: Isabella Dell Date: Thu, 5 Jan 2017 09:01:40 -0600 Subject: [PATCH 035/123] Update README.md --- README.md | 93 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 92e66fb2493..591ca0653ce 100644 --- a/README.md +++ b/README.md @@ -5,48 +5,50 @@ Lisk is a next generation crypto-currency and decentralized application platform [![Join the chat at https://gitter.im/LiskHQ/lisk](https://badges.gitter.im/LiskHQ/lisk.svg)](https://gitter.im/LiskHQ/lisk?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/LiskHQ/lisk.svg?branch=development)](https://travis-ci.org/LiskHQ/lisk) -## Installation +**NOTE:** The following information is applicable to: **Ubuntu 14.04 (LTS) - x86_64**. -**NOTE:** The following is applicable to: **Ubuntu 14.04 (LTS) - x86_64**. +## Prerequisites -Install essentials: +- Nodejs v0.12.17 or higher () -- Nodejs serves as the underlying engine for code execution. -``` -sudo apt-get update -sudo apt-get install -y autoconf automake build-essential curl git libtool python -``` + ``` + curl -sL https://deb.nodesource.com/setup_0.12 | sudo -E bash - + sudo apt-get install -y nodejs + ``` -Install PostgreSQL (version 9.5.2): +- Git () -- Used for cloning and updating Lisk -``` -curl -sL "https://downloads.lisk.io/scripts/setup_postgresql.Linux" | bash - -sudo -u postgres createuser --createdb $USER -createdb lisk_test -sudo -u postgres psql -d lisk_test -c "alter user "$USER" with password 'password';" -``` + `sudo apt-get install -y git` -Install Node.js (version 0.12.x) + npm: +- Bower () -- Bower helps to install required JavaScript dependencies. -``` -curl -sL https://deb.nodesource.com/setup_0.12 | sudo -E bash - -sudo apt-get install -y nodejs -``` + `sudo npm install -g bower` -Install grunt-cli (globally): +- Grunt.js () -- Grunt is used to compile the frontend code and serves other functions. -``` -sudo npm install grunt-cli -g -``` + `sudo npm install -g grunt` -Install bower (globally): -``` -sudo npm install bower -g -``` +- Tool chain components -- Used for compiling dependencies + + `sudo apt-get install -y python build-essential curl automake autoconf libtool` + +- Install PostgreSQL (version 9.6.1): + + ``` + curl -sL "https://downloads.lisk.io/scripts/setup_postgresql.Linux" | bash - + sudo -u postgres createuser --createdb $USER + createdb lisk_test + sudo -u postgres psql -d lisk_test -c "alter user "$USER" with password 'password';" + ``` + +## Installation Steps -Install node modules: +Clone the Lisk repository using Git and initialize the modules. ``` +git clone https://github.com/LiskHQ/lisk.git +cd lisk npm install ``` @@ -75,23 +77,35 @@ bower install grunt release ``` -## Launch +## Managing Lisk -To launch Lisk: +To test that Lisk is built and configured correctly, run the following command: -``` -node app.js -``` +`node app.js` + +In a browser navigate to: . If Lisk is running on a remote system, switch `localhost` for the external IP Address of the machine. + +Once the process is verified as running correctly, `CTRL+C` and start the process with `forever`. This will fork the process into the background and automatically recover the process if it fails. + +`forever start app.js` + +After the process is started its runtime status and log location can be found by issuing this statement: + +`forever list` + +To stop Lisk after it has been started with `forever`, issue the following command: + +`forever stop app.js` **NOTE:** The **port**, **address** and **config-path** can be overridden by providing the relevant command switch: ``` -node app.js -p [port] -a [address] -c [config-path] +forever start app.js -p [port] -a [address] -c [config-path] ``` ## Tests -Before running any tests, please ensure Lisk is configured to run on the same testnet as used by the test-suite. +Before running any tests, please ensure Lisk is configured to run on the same testnet that is used by the test-suite. Replace **config.json** and **genesisBlock.json** with the corresponding files under the **test** directory: @@ -99,13 +113,20 @@ Replace **config.json** and **genesisBlock.json** with the corresponding files u cp test/config.json test/genesisBlock.json . ``` +**NOTE:** If the node was started with a different genesis block previous, trauncate the database before running tests. + +``` +dropdb lisk_test +createdb lisk_test +``` + **NOTE:** The master passphrase for this genesis block is as follows: ``` wagon stock borrow episode laundry kitten salute link globe zero feed marble ``` -Launch lisk (runs on port 4000): +Launch Lisk (runs on port 4000): ``` node app.js From 831737a619e8b393a36c5b416665ca2ce38a014b Mon Sep 17 00:00:00 2001 From: Isabella Dell Date: Thu, 5 Jan 2017 09:06:43 -0600 Subject: [PATCH 036/123] Update README.md --- README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 591ca0653ce..5d152234539 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,15 @@ Lisk is a next generation crypto-currency and decentralized application platform curl -sL https://deb.nodesource.com/setup_0.12 | sudo -E bash - sudo apt-get install -y nodejs ``` + +- Install PostgreSQL (version 9.6.1): + + ``` + curl -sL "https://downloads.lisk.io/scripts/setup_postgresql.Linux" | bash - + sudo -u postgres createuser --createdb $USER + createdb lisk_test + sudo -u postgres psql -d lisk_test -c "alter user "$USER" with password 'password';" + ``` - Git () -- Used for cloning and updating Lisk @@ -28,19 +37,13 @@ Lisk is a next generation crypto-currency and decentralized application platform `sudo npm install -g grunt` - - Tool chain components -- Used for compiling dependencies `sudo apt-get install -y python build-essential curl automake autoconf libtool` + +- Forever () -- Forever manages the node process for Lisk (Optional) -- Install PostgreSQL (version 9.6.1): - - ``` - curl -sL "https://downloads.lisk.io/scripts/setup_postgresql.Linux" | bash - - sudo -u postgres createuser --createdb $USER - createdb lisk_test - sudo -u postgres psql -d lisk_test -c "alter user "$USER" with password 'password';" - ``` + `sudo npm install -g forever` ## Installation Steps From 1d8365b2d0d972a50813cab94fb3048abb2af63d Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Thu, 5 Jan 2017 16:37:01 +0100 Subject: [PATCH 037/123] Remove NULLS LAST option in SQL queries when unnecessary fixes #356 --- sql/blocks.js | 8 ++++---- sql/dapps.js | 6 +++--- sql/loader.js | 2 +- sql/system.js | 2 +- sql/transport.js | 2 +- test/api/peers.js | 5 +---- 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/sql/blocks.js b/sql/blocks.js index dc9f9a9ac03..94cfbcf59d0 100644 --- a/sql/blocks.js +++ b/sql/blocks.js @@ -36,14 +36,14 @@ var BlocksSql = { return [ 'SELECT * FROM blocks_list', (params.where.length ? 'WHERE ' + params.where.join(' AND ') : ''), - (params.sortField ? 'ORDER BY ' + [params.sortField, params.sortMethod].join(' ') + ' NULLS LAST' : ''), + (params.sortField ? 'ORDER BY ' + [params.sortField, params.sortMethod].join(' ') : ''), 'LIMIT ${limit} OFFSET ${offset}' ].filter(Boolean).join(' '); }, getById: 'SELECT * FROM blocks_list WHERE "b_id" = ${id}', - getIdSequence: 'SELECT (ARRAY_AGG("id" ORDER BY "height" ASC NULLS LAST))[1] AS "id", MIN("height") AS "height", CAST("height" / ${delegates} AS INTEGER) + (CASE WHEN "height" % ${activeDelegates} > 0 THEN 1 ELSE 0 END) AS "round" FROM blocks WHERE "height" <= ${height} GROUP BY "round" ORDER BY "height" DESC NULLS LAST LIMIT ${limit}', + getIdSequence: 'SELECT (ARRAY_AGG("id" ORDER BY "height" ASC))[1] AS "id", MIN("height") AS "height", CAST("height" / ${delegates} AS INTEGER) + (CASE WHEN "height" % ${activeDelegates} > 0 THEN 1 ELSE 0 END) AS "round" FROM blocks WHERE "height" <= ${height} GROUP BY "round" ORDER BY "height" DESC LIMIT ${limit}', getCommonBlock: function (params) { return [ @@ -75,9 +75,9 @@ var BlocksSql = { ].filter(Boolean).join(' '); }, - loadBlocksOffset: 'SELECT * FROM full_blocks_list WHERE "b_height" >= ${offset} AND "b_height" < ${limit} ORDER BY "b_height", "t_rowId" NULLS LAST', + loadBlocksOffset: 'SELECT * FROM full_blocks_list WHERE "b_height" >= ${offset} AND "b_height" < ${limit} ORDER BY "b_height", "t_rowId"', - loadLastBlock: 'SELECT * FROM full_blocks_list WHERE "b_height" = (SELECT MAX("height") FROM blocks) ORDER BY "b_height", "t_rowId" NULLS LAST', + loadLastBlock: 'SELECT * FROM full_blocks_list WHERE "b_height" = (SELECT MAX("height") FROM blocks) ORDER BY "b_height", "t_rowId"', getBlockId: 'SELECT "id" FROM blocks WHERE "id" = ${id}', diff --git a/sql/dapps.js b/sql/dapps.js index 9e91e7e8ebe..5788551dfcd 100644 --- a/sql/dapps.js +++ b/sql/dapps.js @@ -27,7 +27,7 @@ var DappsSql = { return [ 'SELECT "name", "description", "tags", "link", "type", "category", "icon", "transactionId" FROM dapps', (params.where.length ? 'WHERE ' + params.where.join(' OR ') : ''), - (params.sortField ? 'ORDER BY ' + [params.sortField, params.sortMethod].join(' ') + ' NULLS LAST' : ''), + (params.sortField ? 'ORDER BY ' + [params.sortField, params.sortMethod].join(' ') : ''), 'LIMIT ${limit} OFFSET ${offset}' ].filter(Boolean).join(' '); }, @@ -36,7 +36,7 @@ var DappsSql = { getCommonBlock: 'SELECT b."height" AS "height", t."id" AS "id", t."senderId" AS "senderId", t."amount" AS "amount" FROM trs t INNER JOIN blocks b ON t."blockId" = b."id" AND t."id" = ${id} AND t."type" = ${type} INNER JOIN intransfer dt ON dt."transactionId" = t."id" AND dt."dappid" = ${dappid}', - getWithdrawalLastTransaction: 'SELECT ot."outTransactionId" FROM trs t INNER JOIN blocks b ON t."blockId" = b."id" AND t."type" = ${type} INNER JOIN outtransfer ot ON ot."transactionId" = t."id" AND ot."dappId" = ${dappid} ORDER BY b."height" DESC LIMIT 1 NULLS LAST', + getWithdrawalLastTransaction: 'SELECT ot."outTransactionId" FROM trs t INNER JOIN blocks b ON t."blockId" = b."id" AND t."type" = ${type} INNER JOIN outtransfer ot ON ot."transactionId" = t."id" AND ot."dappId" = ${dappid} ORDER BY b."height" DESC LIMIT 1', getBalanceTransactions: function (params) { return [ @@ -44,7 +44,7 @@ var DappsSql = { 'INNER JOIN blocks b ON t."blockId" = b."id" AND t."type" = ${type}', 'INNER JOIN intransfer dt ON dt."transactionId" = t."id" AND dt."dappId" = ${dappid}', (params.lastId ? 'WHERE b."height" > (SELECT "height" FROM blocks ib INNER JOIN trs it ON ib."id" = it."blockId" AND it."id" = ${lastId})' : ''), - 'ORDER BY b."height" NULLS LAST' + 'ORDER BY b."height"' ].filter(Boolean).join(' '); } }; diff --git a/sql/loader.js b/sql/loader.js index 74e829899c5..b777856c60f 100644 --- a/sql/loader.js +++ b/sql/loader.js @@ -5,7 +5,7 @@ var LoaderSql = { getGenesisBlock: 'SELECT "id", "payloadHash", "blockSignature" FROM blocks WHERE "height" = 1', - countMemAccounts: 'SELECT COUNT(*)::int FROM mem_accounts WHERE "blockId" = (SELECT "id" FROM "blocks" ORDER BY "height" DESC NULLS LAST LIMIT 1)', + countMemAccounts: 'SELECT COUNT(*)::int FROM mem_accounts WHERE "blockId" = (SELECT "id" FROM "blocks" ORDER BY "height" DESC LIMIT 1)', getMemRounds: 'SELECT "round" FROM mem_round GROUP BY "round"', diff --git a/sql/system.js b/sql/system.js index 43119f7a6b7..eef397194cf 100644 --- a/sql/system.js +++ b/sql/system.js @@ -1,7 +1,7 @@ 'use strict'; var SystemSql = { - getBroadhash: 'SELECT "id" FROM blocks ORDER BY "height" DESC NULLS LAST LIMIT ${limit}' + getBroadhash: 'SELECT "id" FROM blocks ORDER BY "height" DESC LIMIT ${limit}' }; module.exports = SystemSql; diff --git a/sql/transport.js b/sql/transport.js index d481ad813ea..0ec2542cd7f 100644 --- a/sql/transport.js +++ b/sql/transport.js @@ -1,7 +1,7 @@ 'use strict'; var TransportSql = { - getCommonBlock: 'SELECT MAX("height") AS "height", "id", "previousBlock", "timestamp" FROM blocks WHERE "id" IN ($1:csv) GROUP BY "id" ORDER BY "height" DESC NULLS LAST' + getCommonBlock: 'SELECT MAX("height") AS "height", "id", "previousBlock", "timestamp" FROM blocks WHERE "id" IN ($1:csv) GROUP BY "id" ORDER BY "height" DESC' }; module.exports = TransportSql; diff --git a/test/api/peers.js b/test/api/peers.js index 5e8c8b4369f..88eb4605934 100644 --- a/test/api/peers.js +++ b/test/api/peers.js @@ -349,10 +349,7 @@ describe('GET /api/peers', function () { }); it('using orderBy == "height:desc" should not place NULLs first', function (done) { - var orderBy = 'height:desc'; - var params = 'orderBy=' + orderBy; - - node.get('/api/peers?' + params, function (err, res) { + node.get('/api/peers?orderBy=height:desc', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('peers').that.is.an('array'); From b5cbd58f2cdce74ada30bc2e360c2827ded532a4 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Sun, 8 Jan 2017 20:04:22 +0100 Subject: [PATCH 038/123] Closes #381. Verifying each vote in trs.asset.votes. Checking vote type, format and length. --- logic/vote.js | 32 ++++++++++++++++++++++- test/api/peer.transactions.votes.js | 40 +++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/logic/vote.js b/logic/vote.js index f72cf0c44e9..571a31dac19 100644 --- a/logic/vote.js +++ b/logic/vote.js @@ -51,7 +51,37 @@ Vote.prototype.verify = function (trs, sender, cb) { return setImmediate(cb, 'Voting limit exceeded. Maximum is 33 votes per transaction'); } - self.checkConfirmedDelegates(trs, cb); + async.eachSeries(trs.asset.votes, function (vote, eachSeriesCb) { + self.verifyVote(vote, function (err) { + if (err) { + return setImmediate(eachSeriesCb, ['Invalid vote at index', trs.asset.votes.indexOf(vote), '-', err].join(' ')); + } else { + return setImmediate(eachSeriesCb); + } + }); + }, function (err) { + if (err) { + return setImmediate(cb, err); + } else { + return self.checkConfirmedDelegates(trs, cb); + } + }); +}; + +Vote.prototype.verifyVote = function (vote, cb) { + if (typeof vote !== 'string') { + return setImmediate(cb, 'Invalid vote type'); + } + + if (!/[-+]{1}[0-9a-z]{64}/.test(vote)) { + return setImmediate(cb, 'Invalid vote format'); + } + + if (vote.length !== 65) { + return setImmediate(cb, 'Invalid vote length'); + } + + return setImmediate(cb); }; Vote.prototype.checkConfirmedDelegates = function (trs, cb) { diff --git a/test/api/peer.transactions.votes.js b/test/api/peer.transactions.votes.js index b5b2565933b..a2898b60ed4 100644 --- a/test/api/peer.transactions.votes.js +++ b/test/api/peer.transactions.votes.js @@ -144,6 +144,46 @@ describe('POST /peer/transactions', function () { }); }); + it('using transaction.asset.votes containing invalid vote type', function (done) { + var transaction = node.lisk.vote.createVote(account.password, [0]); + + postVote(transaction, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('message').to.equal('Invalid vote at index 0 - Invalid vote type'); + done(); + }); + }); + + it('using transaction.asset.votes containing invalid vote format', function (done) { + var transaction = node.lisk.vote.createVote(account.password, ['@' + delegate]); + + postVote(transaction, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('message').to.equal('Invalid vote at index 0 - Invalid vote format'); + done(); + }); + }); + + it('using transaction.asset.votes containing invalid vote length', function (done) { + var transaction = node.lisk.vote.createVote(account.password, ['+' + delegate + 'z']); + + postVote(transaction, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('message').to.equal('Invalid vote at index 0 - Invalid vote length'); + done(); + }); + }); + + it('using transaction.asset.votes containing manipulated vote', function (done) { + var transaction = node.lisk.vote.createVote(account.password, ['+8a6d629685b18e17e5f534065bad4984a8aa6b499c5783c3e65f61779e6da06czz']); + + postVote(transaction, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('message').to.equal('Invalid vote at index 0 - Invalid vote length'); + done(); + }); + }); + it('voting twice for a delegate should fail', function (done) { async.series([ function (seriesCb) { From a58288f73b639e13d12560569bd88f4e7a1e6b05 Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 10 Jan 2017 00:16:06 +0100 Subject: [PATCH 039/123] Fixed fees calculations in getForgedByAccount endpoint --- modules/blocks.js | 21 ++++++++++----------- modules/delegates.js | 2 +- sql/blocks.js | 6 +----- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/modules/blocks.js b/modules/blocks.js index 793505306d2..f1cbcc0bac1 100644 --- a/modules/blocks.js +++ b/modules/blocks.js @@ -1398,20 +1398,19 @@ Blocks.prototype.cleanup = function (cb) { }; Blocks.prototype.aggregateBlocksReward = function (filter, cb) { - var params = {}, where = []; + var params = {}; - where.push('"b_generatorPublicKey"::bytea = ${generatorPublicKey}'); params.generatorPublicKey = filter.generatorPublicKey; + + if (filter.start) { + params.start = filter.start - constants.epochTime.getTime () / 1000; + } + + if (filter.end !== undefined) { + params.end = filter.end - constants.epochTime.getTime () / 1000; + } - where.push('"b_timestamp" >= ${start}'); - params.start = filter.start - constants.epochTime.getTime () / 1000; - - where.push('"b_timestamp" <= ${end}'); - params.end = filter.end - constants.epochTime.getTime () / 1000; - - library.db.query(sql.aggregateBlocksReward({ - where: where, - }), params).then(function (rows) { + library.db.query(sql.aggregateBlocksReward(params), params).then(function (rows) { var data = rows[0]; data = { fees: data.fees || '0', rewards: data.rewards || '0', count: data.count || '0' }; return setImmediate(cb, null, data); diff --git a/modules/delegates.js b/modules/delegates.js index 7a7573af7bc..4bc79f0045b 100644 --- a/modules/delegates.js +++ b/modules/delegates.js @@ -761,7 +761,7 @@ shared.getForgedByAccount = function (req, cb) { return setImmediate(cb, err[0].message); } - if (req.body.start && req.body.end) { + if (req.body.start !== undefined || req.body.end !== undefined) { modules.blocks.aggregateBlocksReward({generatorPublicKey: req.body.generatorPublicKey, start: req.body.start, end: req.body.end}, function (err, reward) { var forged = bignum(reward.fees).plus(bignum(reward.rewards)).toString(); return setImmediate(cb, null, {fees: reward.fees, rewards: reward.rewards, forged: forged, count: reward.count}); diff --git a/sql/blocks.js b/sql/blocks.js index 94cfbcf59d0..d5608c53063 100644 --- a/sql/blocks.js +++ b/sql/blocks.js @@ -25,11 +25,7 @@ var BlocksSql = { }, aggregateBlocksReward: function (params) { - return [ - 'SELECT SUM("b_totalFee") AS fees, SUM("b_reward") AS rewards, COUNT(1) AS count', - 'FROM blocks_list', - (params.where.length ? 'WHERE ' + params.where.join(' AND ') : '') - ].filter(Boolean).join(' '); + return 'WITH borders as (SELECT (SELECT (CAST(b."height" / 101 AS INTEGER) + (CASE WHEN b."height" % 101 > 0 THEN 1 ELSE 0 END)) FROM blocks b ORDER BY b.height DESC LIMIT 1) as current, (SELECT (CAST(b."height" / 101 AS INTEGER) + (CASE WHEN b."height" % 101 > 0 THEN 1 ELSE 0 END)) FROM blocks b ' + (params.start !== undefined ? ' WHERE b.height >= ${start}' : '') + ' ORDER BY b.height ASC LIMIT 1) as min, (SELECT (CAST(b."height" / 101 AS INTEGER) + (CASE WHEN b."height" % 101 > 0 THEN 1 ELSE 0 END)) FROM blocks b ' + (params.end !== undefined ? ' WHERE b.height <= ${end}' : '') + ' ORDER BY b.height DESC LIMIT 1) as max), r as (SELECT DISTINCT (CAST(b."height" / 101 AS INTEGER) + (CASE WHEN b."height" % 101 > 0 THEN 1 ELSE 0 END)) AS round FROM blocks b WHERE b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\')), re as (SELECT r.round as round, ((r.round-1)*101)+1 as min, r.round*101 as max FROM r WHERE r.round >= (SELECT min FROM borders) AND round <= (SELECT max FROM borders)), sum_min as (SELECT sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b."reward" ELSE 0 END) AS "rewards", sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS "blocks" FROM blocks b WHERE b.height BETWEEN (SELECT min FROM re ORDER BY round ASC LIMIT 1) AND (SELECT max FROM re ORDER BY round ASC LIMIT 1) ' + (params.start !== undefined ? 'AND b.height >= ${start}' : '') + '), sum_max as (SELECT sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b."reward" ELSE 0 END) AS "rewards", sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS "blocks" FROM blocks b WHERE b.height BETWEEN (SELECT min FROM re ORDER BY round DESC LIMIT 1) AND (SELECT max FROM re ORDER BY round DESC LIMIT 1)' + (params.end !== undefined ? 'AND b.height <= ${end}' : '') + '), rs as (SELECT re.*, SUM(b."totalFee") AS "fees", sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b."reward" ELSE 0 END) AS "rewards", sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS "blocks" FROM re, blocks b WHERE b.height BETWEEN re.min AND re.max GROUP BY re.round, re.min, re.max), rsc as (SELECT (CASE WHEN round = borders.current THEN 0 ELSE fees END), round, (CASE WHEN round = borders.min THEN (SELECT blocks FROM sum_min) ELSE (CASE WHEN round = borders.max THEN (SELECT blocks FROM sum_max) ELSE blocks END) END) as blocks, (CASE WHEN round = borders.min THEN (SELECT rewards FROM sum_min) ELSE (CASE WHEN round = borders.max THEN (SELECT rewards FROM sum_max) ELSE rewards END) END) as rewards, (SELECT 1 FROM blocks b WHERE b.height = rs.max AND b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') LIMIT 1) AS last FROM rs, borders) SELECT sum (rsc.blocks) as "count", sum(floor(rsc.fees/101)*rsc.blocks + (CASE WHEN rsc.last = 1 THEN (rsc.fees-floor(rsc.fees/101)*101) ELSE 0 END)) as "fees", sum(rsc.rewards) as "rewards" FROM rsc'; }, list: function (params) { From 47ecf23b8719e10d93a6fb9a8dcc7e6f6ded8155 Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 10 Jan 2017 01:03:10 +0100 Subject: [PATCH 040/123] Make sql query more readable --- sql/blocks.js | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/sql/blocks.js b/sql/blocks.js index d5608c53063..0b5391cac0c 100644 --- a/sql/blocks.js +++ b/sql/blocks.js @@ -25,7 +25,49 @@ var BlocksSql = { }, aggregateBlocksReward: function (params) { - return 'WITH borders as (SELECT (SELECT (CAST(b."height" / 101 AS INTEGER) + (CASE WHEN b."height" % 101 > 0 THEN 1 ELSE 0 END)) FROM blocks b ORDER BY b.height DESC LIMIT 1) as current, (SELECT (CAST(b."height" / 101 AS INTEGER) + (CASE WHEN b."height" % 101 > 0 THEN 1 ELSE 0 END)) FROM blocks b ' + (params.start !== undefined ? ' WHERE b.height >= ${start}' : '') + ' ORDER BY b.height ASC LIMIT 1) as min, (SELECT (CAST(b."height" / 101 AS INTEGER) + (CASE WHEN b."height" % 101 > 0 THEN 1 ELSE 0 END)) FROM blocks b ' + (params.end !== undefined ? ' WHERE b.height <= ${end}' : '') + ' ORDER BY b.height DESC LIMIT 1) as max), r as (SELECT DISTINCT (CAST(b."height" / 101 AS INTEGER) + (CASE WHEN b."height" % 101 > 0 THEN 1 ELSE 0 END)) AS round FROM blocks b WHERE b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\')), re as (SELECT r.round as round, ((r.round-1)*101)+1 as min, r.round*101 as max FROM r WHERE r.round >= (SELECT min FROM borders) AND round <= (SELECT max FROM borders)), sum_min as (SELECT sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b."reward" ELSE 0 END) AS "rewards", sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS "blocks" FROM blocks b WHERE b.height BETWEEN (SELECT min FROM re ORDER BY round ASC LIMIT 1) AND (SELECT max FROM re ORDER BY round ASC LIMIT 1) ' + (params.start !== undefined ? 'AND b.height >= ${start}' : '') + '), sum_max as (SELECT sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b."reward" ELSE 0 END) AS "rewards", sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS "blocks" FROM blocks b WHERE b.height BETWEEN (SELECT min FROM re ORDER BY round DESC LIMIT 1) AND (SELECT max FROM re ORDER BY round DESC LIMIT 1)' + (params.end !== undefined ? 'AND b.height <= ${end}' : '') + '), rs as (SELECT re.*, SUM(b."totalFee") AS "fees", sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b."reward" ELSE 0 END) AS "rewards", sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS "blocks" FROM re, blocks b WHERE b.height BETWEEN re.min AND re.max GROUP BY re.round, re.min, re.max), rsc as (SELECT (CASE WHEN round = borders.current THEN 0 ELSE fees END), round, (CASE WHEN round = borders.min THEN (SELECT blocks FROM sum_min) ELSE (CASE WHEN round = borders.max THEN (SELECT blocks FROM sum_max) ELSE blocks END) END) as blocks, (CASE WHEN round = borders.min THEN (SELECT rewards FROM sum_min) ELSE (CASE WHEN round = borders.max THEN (SELECT rewards FROM sum_max) ELSE rewards END) END) as rewards, (SELECT 1 FROM blocks b WHERE b.height = rs.max AND b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') LIMIT 1) AS last FROM rs, borders) SELECT sum (rsc.blocks) as "count", sum(floor(rsc.fees/101)*rsc.blocks + (CASE WHEN rsc.last = 1 THEN (rsc.fees-floor(rsc.fees/101)*101) ELSE 0 END)) as "fees", sum(rsc.rewards) as "rewards" FROM rsc'; + return [ + 'WITH borders as (SELECT', + '(SELECT (CAST(b.height / 101 AS INTEGER) + (CASE WHEN b.height % 101 > 0 THEN 1 ELSE 0 END)) FROM blocks b ORDER BY b.height DESC LIMIT 1) as current,', + '(SELECT (CAST(b.height / 101 AS INTEGER) + (CASE WHEN b.height % 101 > 0 THEN 1 ELSE 0 END)) FROM blocks b', + (params.start !== undefined ? ' WHERE b.height >= ${start}' : ''), + 'ORDER BY b.height ASC LIMIT 1) as min,', + '(SELECT (CAST(b.height / 101 AS INTEGER) + (CASE WHEN b.height % 101 > 0 THEN 1 ELSE 0 END)) FROM blocks b', + (params.end !== undefined ? ' WHERE b.height <= ${end}' : ''), + 'ORDER BY b.height DESC LIMIT 1) as max', + '),', + 'r as (SELECT DISTINCT ', + '(CAST(b.height / 101 AS INTEGER) + (CASE WHEN b.height % 101 > 0 THEN 1 ELSE 0 END)) AS round', + 'FROM blocks b WHERE b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\')),', + 're as (SELECT r.round as round, ((r.round-1)*101)+1 as min, r.round*101 as max', + 'FROM r WHERE r.round >= (SELECT min FROM borders) AND round <= (SELECT max FROM borders)),', + 'sum_min as (SELECT', + 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b.reward ELSE 0 END) AS rewards,', + 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS blocks', + 'FROM blocks b WHERE b.height BETWEEN (SELECT min FROM re ORDER BY round ASC LIMIT 1) AND (SELECT max FROM re ORDER BY round ASC LIMIT 1)', + (params.start !== undefined ? 'AND b.height >= ${start}' : ''), + '),', + 'sum_max as (SELECT', + 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b.reward ELSE 0 END) AS rewards,', + 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS blocks', + 'FROM blocks b WHERE b.height BETWEEN (SELECT min FROM re ORDER BY round DESC LIMIT 1) AND (SELECT max FROM re ORDER BY round DESC LIMIT 1)', + (params.end !== undefined ? 'AND b.height <= ${end}' : ''), + '),', + 'rs as (SELECT re.*, SUM(b."totalFee") AS fees,', + 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b.reward ELSE 0 END) AS rewards,', + 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS blocks', + 'FROM re, blocks b WHERE b.height BETWEEN re.min AND re.max GROUP BY re.round, re.min, re.max),', + 'rsc as (SELECT', + '(CASE WHEN round = borders.current THEN 0 ELSE fees END), round,', + '(CASE WHEN round = borders.min THEN (SELECT blocks FROM sum_min) ELSE (CASE WHEN round = borders.max THEN (SELECT blocks FROM sum_max) ELSE blocks END) END) as blocks,', + '(CASE WHEN round = borders.min THEN (SELECT rewards FROM sum_min) ELSE (CASE WHEN round = borders.max THEN (SELECT rewards FROM sum_max) ELSE rewards END) END) as rewards,', + '(SELECT 1 FROM blocks b WHERE b.height = rs.max AND b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') LIMIT 1) AS last', + 'FROM rs, borders)', + 'SELECT', + 'sum (rsc.blocks) as count,', + 'sum(floor(rsc.fees/101)*rsc.blocks + (CASE WHEN rsc.last = 1 THEN (rsc.fees-floor(rsc.fees/101)*101) ELSE 0 END)) as fees,', + 'sum(rsc.rewards) as rewards', + 'FROM rsc' + ].filter(Boolean).join(' '); }, list: function (params) { From ddffa9770caeafe1f914a58ea896e9851e794228 Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 10 Jan 2017 01:48:25 +0100 Subject: [PATCH 041/123] Add check for delegate exists in aggregateBlocksReward --- modules/blocks.js | 3 +++ modules/delegates.js | 3 +++ sql/blocks.js | 6 +++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/blocks.js b/modules/blocks.js index f1cbcc0bac1..0ccce9d6b7b 100644 --- a/modules/blocks.js +++ b/modules/blocks.js @@ -1412,6 +1412,9 @@ Blocks.prototype.aggregateBlocksReward = function (filter, cb) { library.db.query(sql.aggregateBlocksReward(params), params).then(function (rows) { var data = rows[0]; + if (data.delegate === null) { + return setImmediate(cb, 'Account not exists or is not a delegate'); + } data = { fees: data.fees || '0', rewards: data.rewards || '0', count: data.count || '0' }; return setImmediate(cb, null, data); }).catch(function (err) { diff --git a/modules/delegates.js b/modules/delegates.js index 4bc79f0045b..5f0aff78580 100644 --- a/modules/delegates.js +++ b/modules/delegates.js @@ -763,6 +763,9 @@ shared.getForgedByAccount = function (req, cb) { if (req.body.start !== undefined || req.body.end !== undefined) { modules.blocks.aggregateBlocksReward({generatorPublicKey: req.body.generatorPublicKey, start: req.body.start, end: req.body.end}, function (err, reward) { + if (err) { + return setImmediate(cb, err || 'Error calculating block rewards'); + } var forged = bignum(reward.fees).plus(bignum(reward.rewards)).toString(); return setImmediate(cb, null, {fees: reward.fees, rewards: reward.rewards, forged: forged, count: reward.count}); }); diff --git a/sql/blocks.js b/sql/blocks.js index 0b5391cac0c..e46c10fca87 100644 --- a/sql/blocks.js +++ b/sql/blocks.js @@ -26,7 +26,10 @@ var BlocksSql = { aggregateBlocksReward: function (params) { return [ - 'WITH borders as (SELECT', + 'WITH', + 'delegate as (SELECT', + '1 FROM mem_accounts m WHERE m."isDelegate" = 1 AND m."publicKey" = DECODE (${generatorPublicKey}, \'hex\') LIMIT 1),', + 'borders as (SELECT', '(SELECT (CAST(b.height / 101 AS INTEGER) + (CASE WHEN b.height % 101 > 0 THEN 1 ELSE 0 END)) FROM blocks b ORDER BY b.height DESC LIMIT 1) as current,', '(SELECT (CAST(b.height / 101 AS INTEGER) + (CASE WHEN b.height % 101 > 0 THEN 1 ELSE 0 END)) FROM blocks b', (params.start !== undefined ? ' WHERE b.height >= ${start}' : ''), @@ -63,6 +66,7 @@ var BlocksSql = { '(SELECT 1 FROM blocks b WHERE b.height = rs.max AND b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') LIMIT 1) AS last', 'FROM rs, borders)', 'SELECT', + '(SELECT * FROM delegate) as delegate,', 'sum (rsc.blocks) as count,', 'sum(floor(rsc.fees/101)*rsc.blocks + (CASE WHEN rsc.last = 1 THEN (rsc.fees-floor(rsc.fees/101)*101) ELSE 0 END)) as fees,', 'sum(rsc.rewards) as rewards', From 0817002a3ca93130760b475c75ce77548afcceb7 Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 10 Jan 2017 02:45:32 +0100 Subject: [PATCH 042/123] Changing expectations for getForgedByAccount --- test/api/delegates.js | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/test/api/delegates.js b/test/api/delegates.js index 3147872b892..c1bfdd49642 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -1031,17 +1031,33 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { }); it('using valid params should be ok', function (done) { + delete validParams.start; + delete validParams.end; + + node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('fees').that.is.a('string'); + node.expect(res.body).to.have.property('rewards').that.is.a('string'); + node.expect(res.body).to.have.property('forged').that.is.a('string'); + done(); + }); + }); + + it('using valid params with borders should be ok', function (done) { node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('fees').that.is.a('string').and.eql('0'); - node.expect(res.body).to.have.property('rewards').that.is.a('string').and.eql('0'); - node.expect(res.body).to.have.property('forged').that.is.a('string').and.eql('0'); + node.expect(res.body).to.have.property('fees').that.is.a('string'); + node.expect(res.body).to.have.property('rewards').that.is.a('string'); + node.expect(res.body).to.have.property('forged').that.is.a('string'); + node.expect(res.body).to.have.property('count').that.is.a('number'); done(); }); }); it('using unknown generatorPublicKey should fail', function (done) { validParams.generatorPublicKey = node.randomAccount().publicKey; + delete validParams.start; + delete validParams.end; node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; @@ -1050,6 +1066,16 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { }); }); + it('using unknown generatorPublicKey with borders should fail', function (done) { + validParams.generatorPublicKey = node.randomAccount().publicKey; + + node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error').to.eql('Account not exists or is not a delegate'); + done(); + }); + }); + it('using invalid generatorPublicKey should fail', function (done) { validParams.generatorPublicKey = 'invalidPublicKey'; @@ -1065,6 +1091,10 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('fees').that.is.a('string'); + node.expect(res.body).to.have.property('rewards').that.is.a('string'); + node.expect(res.body).to.have.property('forged').that.is.a('string'); + node.expect(res.body).to.have.property('count').that.is.a('number'); done(); }); }); @@ -1074,6 +1104,10 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('fees').that.is.a('string'); + node.expect(res.body).to.have.property('rewards').that.is.a('string'); + node.expect(res.body).to.have.property('forged').that.is.a('string'); + node.expect(res.body).to.have.property('count').that.is.a('number'); done(); }); }); From 65e3cdb0281e11cee945a0a3629252436b563175 Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 10 Jan 2017 03:15:54 +0100 Subject: [PATCH 043/123] Direct adjust of active delegates, changing expectations for getForgedByAccount --- modules/blocks.js | 1 + sql/blocks.js | 12 ++++++------ test/api/delegates.js | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/modules/blocks.js b/modules/blocks.js index 0ccce9d6b7b..01a81a35ea5 100644 --- a/modules/blocks.js +++ b/modules/blocks.js @@ -1401,6 +1401,7 @@ Blocks.prototype.aggregateBlocksReward = function (filter, cb) { var params = {}; params.generatorPublicKey = filter.generatorPublicKey; + params.delegates = constants.activeDelegates; if (filter.start) { params.start = filter.start - constants.epochTime.getTime () / 1000; diff --git a/sql/blocks.js b/sql/blocks.js index e46c10fca87..bb0b027c922 100644 --- a/sql/blocks.js +++ b/sql/blocks.js @@ -30,18 +30,18 @@ var BlocksSql = { 'delegate as (SELECT', '1 FROM mem_accounts m WHERE m."isDelegate" = 1 AND m."publicKey" = DECODE (${generatorPublicKey}, \'hex\') LIMIT 1),', 'borders as (SELECT', - '(SELECT (CAST(b.height / 101 AS INTEGER) + (CASE WHEN b.height % 101 > 0 THEN 1 ELSE 0 END)) FROM blocks b ORDER BY b.height DESC LIMIT 1) as current,', - '(SELECT (CAST(b.height / 101 AS INTEGER) + (CASE WHEN b.height % 101 > 0 THEN 1 ELSE 0 END)) FROM blocks b', + '(SELECT (CAST(b.height / ${delegates} AS INTEGER) + (CASE WHEN b.height % ${delegates} > 0 THEN 1 ELSE 0 END)) FROM blocks b ORDER BY b.height DESC LIMIT 1) as current,', + '(SELECT (CAST(b.height / ${delegates} AS INTEGER) + (CASE WHEN b.height % ${delegates} > 0 THEN 1 ELSE 0 END)) FROM blocks b', (params.start !== undefined ? ' WHERE b.height >= ${start}' : ''), 'ORDER BY b.height ASC LIMIT 1) as min,', - '(SELECT (CAST(b.height / 101 AS INTEGER) + (CASE WHEN b.height % 101 > 0 THEN 1 ELSE 0 END)) FROM blocks b', + '(SELECT (CAST(b.height / ${delegates} AS INTEGER) + (CASE WHEN b.height % ${delegates} > 0 THEN 1 ELSE 0 END)) FROM blocks b', (params.end !== undefined ? ' WHERE b.height <= ${end}' : ''), 'ORDER BY b.height DESC LIMIT 1) as max', '),', 'r as (SELECT DISTINCT ', - '(CAST(b.height / 101 AS INTEGER) + (CASE WHEN b.height % 101 > 0 THEN 1 ELSE 0 END)) AS round', + '(CAST(b.height / ${delegates} AS INTEGER) + (CASE WHEN b.height % ${delegates} > 0 THEN 1 ELSE 0 END)) AS round', 'FROM blocks b WHERE b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\')),', - 're as (SELECT r.round as round, ((r.round-1)*101)+1 as min, r.round*101 as max', + 're as (SELECT r.round as round, ((r.round-1)*${delegates})+1 as min, r.round*${delegates} as max', 'FROM r WHERE r.round >= (SELECT min FROM borders) AND round <= (SELECT max FROM borders)),', 'sum_min as (SELECT', 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b.reward ELSE 0 END) AS rewards,', @@ -68,7 +68,7 @@ var BlocksSql = { 'SELECT', '(SELECT * FROM delegate) as delegate,', 'sum (rsc.blocks) as count,', - 'sum(floor(rsc.fees/101)*rsc.blocks + (CASE WHEN rsc.last = 1 THEN (rsc.fees-floor(rsc.fees/101)*101) ELSE 0 END)) as fees,', + 'sum(floor(rsc.fees/${delegates})*rsc.blocks + (CASE WHEN rsc.last = 1 THEN (rsc.fees-floor(rsc.fees/${delegates})*${delegates}) ELSE 0 END)) as fees,', 'sum(rsc.rewards) as rewards', 'FROM rsc' ].filter(Boolean).join(' '); diff --git a/test/api/delegates.js b/test/api/delegates.js index c1bfdd49642..2657622199d 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -1017,8 +1017,8 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { function buildParams () { return [ 'generatorPublicKey=' + validParams.generatorPublicKey, - validParams.start ? 'start=' + validParams.start : '', - validParams.end ? 'end=' + validParams.end : '', + validParams.start !== undefined ? 'start=' + validParams.start : '', + validParams.end !== undefined ? 'end=' + validParams.end : '', ].filter(Boolean).join('&'); } From 539b4baa241be3e3bf5ba90383d10f4c4c666e5c Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 10 Jan 2017 03:35:23 +0100 Subject: [PATCH 044/123] Changing expectations for getForgedByAccount --- test/api/delegates.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/api/delegates.js b/test/api/delegates.js index 2657622199d..1a915ff32d5 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -1046,10 +1046,10 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { it('using valid params with borders should be ok', function (done) { node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('fees').that.is.a('string'); - node.expect(res.body).to.have.property('rewards').that.is.a('string'); - node.expect(res.body).to.have.property('forged').that.is.a('string'); - node.expect(res.body).to.have.property('count').that.is.a('number'); + node.expect(res.body).to.have.property('fees').that.is.a('string').and.eql('0');; + node.expect(res.body).to.have.property('rewards').that.is.a('string').and.eql('0');; + node.expect(res.body).to.have.property('forged').that.is.a('string').and.eql('0');; + node.expect(res.body).to.have.property('count').that.is.a('string').and.eql('0');; done(); }); }); From 0e6064d19a74388301345870e215dd4c388d749c Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 10 Jan 2017 03:40:40 +0100 Subject: [PATCH 045/123] Changing expectations for getForgedByAccount --- test/api/delegates.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/api/delegates.js b/test/api/delegates.js index 1a915ff32d5..9058855ea62 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -1046,10 +1046,10 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { it('using valid params with borders should be ok', function (done) { node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('fees').that.is.a('string').and.eql('0');; - node.expect(res.body).to.have.property('rewards').that.is.a('string').and.eql('0');; - node.expect(res.body).to.have.property('forged').that.is.a('string').and.eql('0');; - node.expect(res.body).to.have.property('count').that.is.a('string').and.eql('0');; + node.expect(res.body).to.have.property('fees').that.is.a('string').and.eql('0'); + node.expect(res.body).to.have.property('rewards').that.is.a('string').and.eql('0'); + node.expect(res.body).to.have.property('forged').that.is.a('string').and.eql('0'); + node.expect(res.body).to.have.property('count').that.is.a('string').and.eql('0'); done(); }); }); @@ -1091,10 +1091,10 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('fees').that.is.a('string'); - node.expect(res.body).to.have.property('rewards').that.is.a('string'); - node.expect(res.body).to.have.property('forged').that.is.a('string'); - node.expect(res.body).to.have.property('count').that.is.a('number'); + node.expect(res.body).to.have.property('fees').that.is.a('string').and.eql('0'); + node.expect(res.body).to.have.property('rewards').that.is.a('string').and.eql('0'); + node.expect(res.body).to.have.property('forged').that.is.a('string').and.eql('0'); + node.expect(res.body).to.have.property('count').that.is.a('string').and.eql('0'); done(); }); }); @@ -1104,10 +1104,10 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('fees').that.is.a('string'); - node.expect(res.body).to.have.property('rewards').that.is.a('string'); - node.expect(res.body).to.have.property('forged').that.is.a('string'); - node.expect(res.body).to.have.property('count').that.is.a('number'); + node.expect(res.body).to.have.property('fees').that.is.a('string').and.eql('0'); + node.expect(res.body).to.have.property('rewards').that.is.a('string').and.eql('0'); + node.expect(res.body).to.have.property('forged').that.is.a('string').and.eql('0'); + node.expect(res.body).to.have.property('count').that.is.a('string').and.eql('0'); done(); }); }); From 9a38a92248019adeecc9eef92b26309384a07d1a Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 10 Jan 2017 04:05:43 +0100 Subject: [PATCH 046/123] Changing expectations for getForgedByAccount --- test/api/delegates.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/api/delegates.js b/test/api/delegates.js index 9058855ea62..b5cb14f0a9d 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -1104,10 +1104,10 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('fees').that.is.a('string').and.eql('0'); - node.expect(res.body).to.have.property('rewards').that.is.a('string').and.eql('0'); - node.expect(res.body).to.have.property('forged').that.is.a('string').and.eql('0'); - node.expect(res.body).to.have.property('count').that.is.a('string').and.eql('0'); + node.expect(res.body).to.have.property('fees').that.is.a('string'); + node.expect(res.body).to.have.property('rewards').that.is.a('string'); + node.expect(res.body).to.have.property('forged').that.is.a('string'); + node.expect(res.body).to.have.property('count').that.is.a('string'); done(); }); }); From ab3c92f22d9d91e21717af11e8b138cdf4042257 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Tue, 10 Jan 2017 18:40:23 +0100 Subject: [PATCH 047/123] Check if account is a multisign Handle situation when sender has no multisignatures field. closes #383 --- modules/multisignatures.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/multisignatures.js b/modules/multisignatures.js index 0a658323fc6..47ae212d2ae 100644 --- a/modules/multisignatures.js +++ b/modules/multisignatures.js @@ -357,7 +357,7 @@ shared.sign = function (req, cb) { if (!scope.transaction.requesterPublicKey) { permissionDenied = ( - (scope.sender.multisignatures.indexOf(scope.keypair.publicKey.toString('hex')) === -1) + (!scope.sender.multisignatures || scope.sender.multisignatures.indexOf(scope.keypair.publicKey.toString('hex')) === -1) ); } else { permissionDenied = ( From c6022f364fa1d0c5679ebc50ebe89e4903595fb5 Mon Sep 17 00:00:00 2001 From: 4miners Date: Wed, 11 Jan 2017 01:21:28 +0100 Subject: [PATCH 048/123] Search based on timestamp instead of height --- modules/blocks.js | 4 ++-- modules/delegates.js | 2 +- sql/blocks.js | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/blocks.js b/modules/blocks.js index 01a81a35ea5..487127e644e 100644 --- a/modules/blocks.js +++ b/modules/blocks.js @@ -1403,8 +1403,8 @@ Blocks.prototype.aggregateBlocksReward = function (filter, cb) { params.generatorPublicKey = filter.generatorPublicKey; params.delegates = constants.activeDelegates; - if (filter.start) { - params.start = filter.start - constants.epochTime.getTime () / 1000; + if (filter.start !== undefined) { + params.start = filter.start - constants.epochTime.getTime () / 1000; } if (filter.end !== undefined) { diff --git a/modules/delegates.js b/modules/delegates.js index 5f0aff78580..bbb1c7f14ba 100644 --- a/modules/delegates.js +++ b/modules/delegates.js @@ -764,7 +764,7 @@ shared.getForgedByAccount = function (req, cb) { if (req.body.start !== undefined || req.body.end !== undefined) { modules.blocks.aggregateBlocksReward({generatorPublicKey: req.body.generatorPublicKey, start: req.body.start, end: req.body.end}, function (err, reward) { if (err) { - return setImmediate(cb, err || 'Error calculating block rewards'); + return setImmediate(cb, err); } var forged = bignum(reward.fees).plus(bignum(reward.rewards)).toString(); return setImmediate(cb, null, {fees: reward.fees, rewards: reward.rewards, forged: forged, count: reward.count}); diff --git a/sql/blocks.js b/sql/blocks.js index bb0b027c922..7672a8901b8 100644 --- a/sql/blocks.js +++ b/sql/blocks.js @@ -32,10 +32,10 @@ var BlocksSql = { 'borders as (SELECT', '(SELECT (CAST(b.height / ${delegates} AS INTEGER) + (CASE WHEN b.height % ${delegates} > 0 THEN 1 ELSE 0 END)) FROM blocks b ORDER BY b.height DESC LIMIT 1) as current,', '(SELECT (CAST(b.height / ${delegates} AS INTEGER) + (CASE WHEN b.height % ${delegates} > 0 THEN 1 ELSE 0 END)) FROM blocks b', - (params.start !== undefined ? ' WHERE b.height >= ${start}' : ''), + (params.start !== undefined ? ' WHERE b.timestamp >= ${start}' : ''), 'ORDER BY b.height ASC LIMIT 1) as min,', '(SELECT (CAST(b.height / ${delegates} AS INTEGER) + (CASE WHEN b.height % ${delegates} > 0 THEN 1 ELSE 0 END)) FROM blocks b', - (params.end !== undefined ? ' WHERE b.height <= ${end}' : ''), + (params.end !== undefined ? ' WHERE b.timestamp <= ${end}' : ''), 'ORDER BY b.height DESC LIMIT 1) as max', '),', 'r as (SELECT DISTINCT ', @@ -47,13 +47,13 @@ var BlocksSql = { 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b.reward ELSE 0 END) AS rewards,', 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS blocks', 'FROM blocks b WHERE b.height BETWEEN (SELECT min FROM re ORDER BY round ASC LIMIT 1) AND (SELECT max FROM re ORDER BY round ASC LIMIT 1)', - (params.start !== undefined ? 'AND b.height >= ${start}' : ''), + (params.start !== undefined ? 'AND b.timestamp >= ${start}' : ''), '),', 'sum_max as (SELECT', 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b.reward ELSE 0 END) AS rewards,', 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS blocks', 'FROM blocks b WHERE b.height BETWEEN (SELECT min FROM re ORDER BY round DESC LIMIT 1) AND (SELECT max FROM re ORDER BY round DESC LIMIT 1)', - (params.end !== undefined ? 'AND b.height <= ${end}' : ''), + (params.end !== undefined ? 'AND b.timestamp <= ${end}' : ''), '),', 'rs as (SELECT re.*, SUM(b."totalFee") AS fees,', 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b.reward ELSE 0 END) AS rewards,', From d53b305e61b16cd5a073f48cac2b6163ecb02c89 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Wed, 11 Jan 2017 15:14:09 +0100 Subject: [PATCH 049/123] provide test for signing non multisig transaction --- test/api/multisignatures.js | 70 ++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/test/api/multisignatures.js b/test/api/multisignatures.js index f1906c56332..dd5d4b5acf9 100644 --- a/test/api/multisignatures.js +++ b/test/api/multisignatures.js @@ -61,7 +61,10 @@ function confirmTransaction (transactionId, passphrases, done) { secret: passphrase, transactionId: transactionId }, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + var possibleErr = err || res.body.err; + if (err || !res.body.success) { + return untilCb(err || res.body.error); + } node.expect(res.body).to.have.property('transactionId').to.equal(transactionId); count++; return untilCb(); @@ -160,10 +163,10 @@ describe('PUT /api/multisignatures', function () { delete validParams.keysgroup; node.put('/api/multisignatures', validParams, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('error'); - done(); - }); + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error'); + done(); + }); }); it('using string keysgroup should fail', function (done) { @@ -547,3 +550,60 @@ describe('POST /api/multisignatures/sign (transaction)', function () { }); }); }); + + +describe('POST /api/multisignatures/sign (not a multisign)', function () { + + var transactionId; + + before(function (done) { + node.put('/api/transactions/', { + secret: node.gAccount.password , + amount: 1, + recipientId: accounts[0].address + }, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('transactionId').that.is.not.empty; + transactionId = res.body.transactionId; + done(); + }); + }); + + it('should be impossible to sign the transaction from not multisignatured account', function (done) { + node.onNewBlock(function (err) { + node.get('/api/transactions/get?id=' + transactionId, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('transaction'); + node.expect(res.body.transaction).to.have.property('id').to.equal(transactionId); + confirmTransaction(transactionId, [multisigAccount.password], function (err, res) { + node.expect(err).not.to.be.empty; + done(); + }); + }); + }); + }); + + it('should have no pending multisignatures', function (done) { + node.get('/api/multisignatures/pending?publicKey=' + accounts[0].publicKey, function (err, res) { + node.expect(res.body).to.have.property('success'); + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('transactions').that.is.an('array'); + node.expect(res.body.transactions.length).to.equal(0); + done(); + }); + }); + + after(function (done) { + //send the money back + node.put('/api/transactions/', { + secret: accounts[0].password, + amount: 1, + recipientId: node.gAccount.address + }, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + done(); + }); + }); + + +}); From 625d003cf5de3ab89bd662463bd99284db636fe2 Mon Sep 17 00:00:00 2001 From: TheGoldenEye Date: Wed, 11 Jan 2017 21:26:14 +0100 Subject: [PATCH 050/123] ignore versionChar, check only version this.minVersion can contain range specifier (^ ~ *) which cannot compared directly --- modules/system.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/system.js b/modules/system.js index b94247ac51b..43e22039d36 100644 --- a/modules/system.js +++ b/modules/system.js @@ -78,11 +78,8 @@ System.prototype.versionCompatible = function (version) { version = version.replace(rcRegExp, ''); } - if (this.minVersionChar && versionChar) { - return (version + versionChar) === (this.minVersion + this.minVersionChar); - } else { - return semver.satisfies(version, this.minVersion); - } + // ignore versionChar, check only version + return semver.satisfies(version, this.minVersion); }; System.prototype.getBroadhash = function (cb) { From c75d6a8f4117031d34591190122870ce4756086a Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Thu, 12 Jan 2017 13:11:38 +0100 Subject: [PATCH 051/123] Remove unused variable in multisignatures test --- test/api/multisignatures.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/api/multisignatures.js b/test/api/multisignatures.js index dd5d4b5acf9..d4a5f6a20dc 100644 --- a/test/api/multisignatures.js +++ b/test/api/multisignatures.js @@ -61,7 +61,6 @@ function confirmTransaction (transactionId, passphrases, done) { secret: passphrase, transactionId: transactionId }, function (err, res) { - var possibleErr = err || res.body.err; if (err || !res.body.success) { return untilCb(err || res.body.error); } From 36f0e723fde256cd2646e8e5e5c486d5fa506f07 Mon Sep 17 00:00:00 2001 From: TheGoldenEye Date: Tue, 17 Jan 2017 21:06:18 +0100 Subject: [PATCH 052/123] check versionChar, if no range specifier is used --- modules/system.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/system.js b/modules/system.js index 43e22039d36..f9b740b9cc8 100644 --- a/modules/system.js +++ b/modules/system.js @@ -78,6 +78,12 @@ System.prototype.versionCompatible = function (version) { version = version.replace(rcRegExp, ''); } + // if no range specifier is used for minVersion, check the complete version string (inclusive versionChar) + var rangeRegExp = /[\^\~\*]/; + if (this.minVersionChar && versionChar && !rangeRegExp.test(this.minVersion)) { + return (version + versionChar) === (this.minVersion + this.minVersionChar); + } + // ignore versionChar, check only version return semver.satisfies(version, this.minVersion); }; From dde59f58d040b006e934e5a4e44b2dc21c5ca4f8 Mon Sep 17 00:00:00 2001 From: TheGoldenEye Date: Tue, 17 Jan 2017 21:26:38 +0100 Subject: [PATCH 053/123] no need to escape '~' --- modules/system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/system.js b/modules/system.js index f9b740b9cc8..f63c35f901a 100644 --- a/modules/system.js +++ b/modules/system.js @@ -79,7 +79,7 @@ System.prototype.versionCompatible = function (version) { } // if no range specifier is used for minVersion, check the complete version string (inclusive versionChar) - var rangeRegExp = /[\^\~\*]/; + var rangeRegExp = /[\^~\*]/; if (this.minVersionChar && versionChar && !rangeRegExp.test(this.minVersion)) { return (version + versionChar) === (this.minVersion + this.minVersionChar); } From a1be525efce8abaa027c3f29f9ee66fb950775ab Mon Sep 17 00:00:00 2001 From: TheGoldenEye Date: Tue, 17 Jan 2017 22:07:16 +0100 Subject: [PATCH 054/123] Column identifiers (fields) must be enclosed in double quotes remove unused private.DOUBLE_DOUBLE_QUOTES definition --- modules/sql.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/sql.js b/modules/sql.js index 859d1adb5df..de39e134d27 100644 --- a/modules/sql.js +++ b/modules/sql.js @@ -10,9 +10,10 @@ var sandboxHelper = require('../helpers/sandbox.js'); var modules, library, self, __private = {}, shared = {}; __private.loaded = false; -__private.DOUBLE_DOUBLE_QUOTES = /''/g; __private.SINGLE_QUOTES = /'/g; __private.SINGLE_QUOTES_DOUBLED = '\'\''; +__private.DOUBLE_QUOTES = /"/g; +__private.DOUBLE_QUOTES_DOUBLED = '""'; // Constructor function Sql (cb, scope) { @@ -47,6 +48,10 @@ __private.escape = function (what) { throw 'Unsupported data ' + typeof what; }; +__private.escape2 = function (str) { + return '"' + str.replace(__private.DOUBLE_QUOTES, __private.DOUBLE_QUOTES_DOUBLED) + '"'; +}; + __private.pass = function (obj, dappid) { for (var property in obj) { if (typeof obj[property] === 'object') { @@ -100,6 +105,7 @@ __private.query = function (action, config, cb) { try { sql = jsonSql.build(extend({}, config, defaultConfig)); + library.logger.trace("sql.query: ", sql); } catch (e) { return done(e); } @@ -120,7 +126,7 @@ __private.query = function (action, config, cb) { return batchPack.length === 0; }, function (cb) { var fields = Object.keys(config.fields).map(function (field) { - return __private.escape(config.fields[field]); + return __private.escape2(config.fields[field]); // add double quotes to field identifiers }); sql = 'INSERT INTO ' + 'dapp_' + config.dappid + '_' + config.table + ' (' + fields.join(',') + ') '; var rows = []; From 11bd4fb6d7329c8cb7e4421fcf9ca454d184ab04 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Tue, 17 Jan 2017 16:01:10 +0100 Subject: [PATCH 055/123] fix BackwardTick promise so it doesn't throws undefined error closes #390 --- modules/rounds.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/rounds.js b/modules/rounds.js index 98380bf7bcc..49747f08a99 100644 --- a/modules/rounds.js +++ b/modules/rounds.js @@ -106,7 +106,9 @@ Rounds.prototype.backwardTick = function (block, previousBlock, done) { delete __private.unFeesByRound[round]; delete __private.unRewardsByRound[round]; delete __private.unDelegatesByRound[round]; - }).then(promised.markBlockId); + }).then(function () { + promised.markBlockId(); + }); } else { return promised.markBlockId(); } From 8e1d2938c98c313a18629591619468d3ad1e4209 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Tue, 10 Jan 2017 20:49:30 +0100 Subject: [PATCH 056/123] Prevent from node sync with himself Secure that list of peers won't be extended with node's public address and port. closes #336 --- modules/peers.js | 5 +++-- test/api/peer.js | 23 ++++++++++++++++++++++- test/api/peers.js | 2 +- test/node.js | 4 ++-- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/modules/peers.js b/modules/peers.js index b93ef7e2e8a..814fefacace 100644 --- a/modules/peers.js +++ b/modules/peers.js @@ -291,8 +291,9 @@ Peers.prototype.accept = function (peer) { Peers.prototype.acceptable = function (peers) { return _.chain(peers).filter(function (peer) { - // Removing peers with private ip address - return !ip.isPrivate(peer.ip); + // Removing peers with private or host's ip address + return !ip.isPrivate(peer.ip) && + [ip.address('public'), library.config.port].join(':') !== [peer.ip, peer.port].join(':'); }).uniqWith(function (a, b) { // Removing non-unique peers return (a.ip + a.port) === (b.ip + b.port); diff --git a/test/api/peer.js b/test/api/peer.js index 1791d29fea0..9678a817313 100644 --- a/test/api/peer.js +++ b/test/api/peer.js @@ -1,11 +1,16 @@ 'use strict'; /*jslint mocha:true, expr:true */ var node = require('./../node.js'); +var ip = require('ip'); describe('GET /peer/list', function () { before(function (done) { - node.addPeers(2, done); + node.addPeers(2, '0.0.0.0', done); + }); + + before(function (done) { + node.addPeers(1, ip.address('public'), done); }); it('using incorrect nethash in headers should fail', function (done) { @@ -34,6 +39,7 @@ describe('GET /peer/list', function () { it('using valid headers should be ok', function (done) { node.get('/peer/list') + .end(function (err, res) { node.debug('> Response:'.grey, JSON.stringify(res.body)); node.expect(res.body).to.have.property('success').to.be.ok; @@ -50,6 +56,20 @@ describe('GET /peer/list', function () { done(); }); }); + + it('should not accept itself as a peer', function (done) { + node.get('/peer/list') + .end(function (err, res) { + node.debug('> Response:'.grey, JSON.stringify(res.body)); + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('peers').that.is.an('array'); + res.body.peers.forEach(function (peer) { + node.expect(peer).to.have.property('ip').that.is.a('string'); + node.expect(peer.ip).not.to.equal(ip.address('public')); + }); + done(); + }); + }); }); describe('GET /peer/height', function () { @@ -89,3 +109,4 @@ describe('GET /peer/height', function () { }); }); }); + diff --git a/test/api/peers.js b/test/api/peers.js index d3be8102d39..16e40e2f85c 100644 --- a/test/api/peers.js +++ b/test/api/peers.js @@ -455,7 +455,7 @@ describe('GET /api/peers/get', function () { var validParams; before(function (done) { - node.addPeers(1, function (err, headers) { + node.addPeers(1, '0.0.0.0', function (err, headers) { validParams = headers; done(); }); diff --git a/test/node.js b/test/node.js index bd6ef163ce8..3ef081003b1 100644 --- a/test/node.js +++ b/test/node.js @@ -186,7 +186,7 @@ node.waitForNewBlock = function (height, blocksToWait, cb) { }; // Adds peers to local node -node.addPeers = function (numOfPeers, cb) { +node.addPeers = function (numOfPeers, ip, cb) { var operatingSystems = ['win32','win64','ubuntu','debian', 'centos']; var port = 4000; var os, version; @@ -205,7 +205,7 @@ node.addPeers = function (numOfPeers, cb) { height: 1, nethash: node.config.nethash, os: os, - ip: '0.0.0.0', + ip: ip, port: port, version: version } From cee207be51751afe7ee18ec322544c5034bc4a83 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Wed, 18 Jan 2017 17:44:09 +0100 Subject: [PATCH 057/123] correct multisignatures signing test message, check if multisignatures field is array --- modules/multisignatures.js | 2 +- test/api/multisignatures.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/multisignatures.js b/modules/multisignatures.js index 47ae212d2ae..c7636f1b98a 100644 --- a/modules/multisignatures.js +++ b/modules/multisignatures.js @@ -357,7 +357,7 @@ shared.sign = function (req, cb) { if (!scope.transaction.requesterPublicKey) { permissionDenied = ( - (!scope.sender.multisignatures || scope.sender.multisignatures.indexOf(scope.keypair.publicKey.toString('hex')) === -1) + (!Array.isArray(scope.sender.multisignatures) || scope.sender.multisignatures.indexOf(scope.keypair.publicKey.toString('hex')) === -1) ); } else { permissionDenied = ( diff --git a/test/api/multisignatures.js b/test/api/multisignatures.js index d4a5f6a20dc..f0a5e5495f9 100644 --- a/test/api/multisignatures.js +++ b/test/api/multisignatures.js @@ -551,7 +551,7 @@ describe('POST /api/multisignatures/sign (transaction)', function () { }); -describe('POST /api/multisignatures/sign (not a multisign)', function () { +describe('POST /api/multisignatures/sign (regular account)', function () { var transactionId; @@ -568,7 +568,7 @@ describe('POST /api/multisignatures/sign (not a multisign)', function () { }); }); - it('should be impossible to sign the transaction from not multisignatured account', function (done) { + it('should be impossible to sign the transaction', function (done) { node.onNewBlock(function (err) { node.get('/api/transactions/get?id=' + transactionId, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; From 93ebbe5f21e8ef7eab40f1e1e16027ae4c171eb3 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Wed, 18 Jan 2017 18:46:42 +0100 Subject: [PATCH 058/123] add tests if NULLs are last when querying delegates and transactions --- test/api/delegates.js | 23 +++++++++++++++++++++++ test/api/transactions.js | 25 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/test/api/delegates.js b/test/api/delegates.js index 3147872b892..aaa84ca6e9f 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -629,6 +629,29 @@ describe('GET /api/delegates', function () { done(); }); }); + + it('using orderBy == "missedblocks:asc" should not place NULLs first', function (done) { + node.get('/api/delegates?orderBy=missedblocks:asc', function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('delegates').that.is.an('array'); + + var dividedIndices = res.body.peers.reduce(function (memo, peer, index) { + memo[peer.missedblocks === null ? 'nullIndices' : 'notNullIndices'].push(index); + return memo; + }, {notNullIndices: [], nullIndices: []}); + + if (dividedIndices.nullIndices.length && dividedIndices.notNullIndices.length) { + var ascOrder = function (a, b) { return a - b; }; + dividedIndices.notNullIndices.sort(ascOrder); + dividedIndices.nullIndices.sort(ascOrder); + + node.expect(dividedIndices.notNullIndices[dividedIndices.notNullIndices.length - 1]) + .to.be.at.most(dividedIndices.nullIndices[0]); + } + + done(); + }); + }); }); describe('GET /api/delegates/count', function () { diff --git a/test/api/transactions.js b/test/api/transactions.js index f7ad1336660..489d4d58c50 100644 --- a/test/api/transactions.js +++ b/test/api/transactions.js @@ -215,8 +215,33 @@ describe('GET /api/transactions', function () { done(); }); }); + + it('using orderBy == "recipientId" should not place NULLs first', function (done) { + node.get('/api/transactions?orderBy=recipientId', function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('transactions').that.is.an('array'); + + var dividedIndices = res.body.peers.reduce(function (memo, peer, index) { + memo[peer.recipientId === null ? 'nullIndices' : 'notNullIndices'].push(index); + return memo; + }, {notNullIndices: [], nullIndices: []}); + + if (dividedIndices.nullIndices.length && dividedIndices.notNullIndices.length) { + var ascOrder = function (a, b) { return a - b; }; + dividedIndices.notNullIndices.sort(ascOrder); + dividedIndices.nullIndices.sort(ascOrder); + + node.expect(dividedIndices.notNullIndices[dividedIndices.notNullIndices.length - 1]) + .to.be.at.most(dividedIndices.nullIndices[0]); + } + + done(); + }); + }); }); + + describe('GET /api/transactions/get?id=', function () { it('using valid id should be ok', function (done) { From e350bdf3ea8e072f28c119a8a6e29275ca5f74ec Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Wed, 18 Jan 2017 18:46:42 +0100 Subject: [PATCH 059/123] add tests if NULLs are last when querying delegates and transactions --- test/api/delegates.js | 25 +++++++++++++++++++++++++ test/api/transactions.js | 25 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/test/api/delegates.js b/test/api/delegates.js index 3147872b892..788d0d35509 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -629,6 +629,31 @@ describe('GET /api/delegates', function () { done(); }); }); + + it('using orderBy with random sort field should not place NULLs first', function (done) { + var delegatesSortFields = ['approval', 'productivity', 'rate', 'vote']; + var randomSortField = delegatesSortFields[Math.floor(Math.random() * delegatesSortFields.length)]; + node.get('/api/delegates?orderBy=' + randomSortField, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('delegates').that.is.an('array'); + + var dividedIndices = res.body.delegates.reduce(function (memo, peer, index) { + memo[peer[randomSortField] === null ? 'nullIndices' : 'notNullIndices'].push(index); + return memo; + }, {notNullIndices: [], nullIndices: []}); + + if (dividedIndices.nullIndices.length && dividedIndices.notNullIndices.length) { + var ascOrder = function (a, b) { return a - b; }; + dividedIndices.notNullIndices.sort(ascOrder); + dividedIndices.nullIndices.sort(ascOrder); + + node.expect(dividedIndices.notNullIndices[dividedIndices.notNullIndices.length - 1]) + .to.be.at.most(dividedIndices.nullIndices[0]); + } + + done(); + }); + }); }); describe('GET /api/delegates/count', function () { diff --git a/test/api/transactions.js b/test/api/transactions.js index f7ad1336660..874c4823ba3 100644 --- a/test/api/transactions.js +++ b/test/api/transactions.js @@ -215,8 +215,33 @@ describe('GET /api/transactions', function () { done(); }); }); + + it('using orderBy == "recipientId" should not place NULLs first', function (done) { + node.get('/api/transactions?orderBy=recipientId', function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('transactions').that.is.an('array'); + + var dividedIndices = res.body.transactions.reduce(function (memo, peer, index) { + memo[peer.recipientId === null ? 'nullIndices' : 'notNullIndices'].push(index); + return memo; + }, {notNullIndices: [], nullIndices: []}); + + if (dividedIndices.nullIndices.length && dividedIndices.notNullIndices.length) { + var ascOrder = function (a, b) { return a - b; }; + dividedIndices.notNullIndices.sort(ascOrder); + dividedIndices.nullIndices.sort(ascOrder); + + node.expect(dividedIndices.notNullIndices[dividedIndices.notNullIndices.length - 1]) + .to.be.at.most(dividedIndices.nullIndices[0]); + } + + done(); + }); + }); }); + + describe('GET /api/transactions/get?id=', function () { it('using valid id should be ok', function (done) { From 91b2343fea17930cc8fe054e25c59457d12ce013 Mon Sep 17 00:00:00 2001 From: Isabella Dell Date: Thu, 19 Jan 2017 09:12:56 -0600 Subject: [PATCH 060/123] Add Copyright for 2017 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5d152234539..fd6082896b5 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ npm test -- test/lib/transactions.js The MIT License (MIT) -Copyright (c) 2016 Lisk +Copyright (c) 2016-2017 Lisk Copyright (c) 2014-2015 Crypti Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: From 147d75499ee3c9c6ddc8ba647f07e94ea99bea0b Mon Sep 17 00:00:00 2001 From: Isabella Dell Date: Thu, 19 Jan 2017 10:03:10 -0600 Subject: [PATCH 061/123] Switch Node to install with nvm --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index fd6082896b5..14f6fa71cc1 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,21 @@ Lisk is a next generation crypto-currency and decentralized application platform **NOTE:** The following information is applicable to: **Ubuntu 14.04 (LTS) - x86_64**. -## Prerequisites +## Prerequisites - In order -- Nodejs v0.12.17 or higher () -- Nodejs serves as the underlying engine for code execution. +- Tool chain components -- Used for compiling dependencies + + `sudo apt-get install -y python build-essential curl automake autoconf libtool` + +- Git () -- Used for cloning and updating Lisk + + `sudo apt-get install -y git` + +- Nodejs v0.12.17 () -- Nodejs serves as the underlying engine for code execution. ``` - curl -sL https://deb.nodesource.com/setup_0.12 | sudo -E bash - - sudo apt-get install -y nodejs + curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.0/install.sh | bash + nvm install v0.12.17 ``` - Install PostgreSQL (version 9.6.1): @@ -24,11 +32,7 @@ Lisk is a next generation crypto-currency and decentralized application platform createdb lisk_test sudo -u postgres psql -d lisk_test -c "alter user "$USER" with password 'password';" ``` - -- Git () -- Used for cloning and updating Lisk - - `sudo apt-get install -y git` - + - Bower () -- Bower helps to install required JavaScript dependencies. `sudo npm install -g bower` @@ -36,10 +40,6 @@ Lisk is a next generation crypto-currency and decentralized application platform - Grunt.js () -- Grunt is used to compile the frontend code and serves other functions. `sudo npm install -g grunt` - -- Tool chain components -- Used for compiling dependencies - - `sudo apt-get install -y python build-essential curl automake autoconf libtool` - Forever () -- Forever manages the node process for Lisk (Optional) From 203ccc7aeb0760f3d3cda5275d250c5de38f9cad Mon Sep 17 00:00:00 2001 From: Isabella Dell Date: Thu, 19 Jan 2017 10:09:29 -0600 Subject: [PATCH 062/123] Remove sudo from npm modules. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 14f6fa71cc1..ff16e6f7a51 100644 --- a/README.md +++ b/README.md @@ -35,15 +35,15 @@ Lisk is a next generation crypto-currency and decentralized application platform - Bower () -- Bower helps to install required JavaScript dependencies. - `sudo npm install -g bower` + `npm install -g bower` - Grunt.js () -- Grunt is used to compile the frontend code and serves other functions. - `sudo npm install -g grunt` + `npm install -g grunt` - Forever () -- Forever manages the node process for Lisk (Optional) - `sudo npm install -g forever` + `npm install -g forever` ## Installation Steps From 17d09c61931cc0332686d55d773de55f2d02226a Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Mon, 16 Jan 2017 16:11:06 +0100 Subject: [PATCH 063/123] add unique_address constraint for peers table Referred to postgres docs it is better to have constraints while resolving conflicts during upserts. closes #361 Signed-off-by: Maciej Baj --- .../20170113181857_addConstraintsToPeers.sql | 11 +++++++++++ sql/peers.js | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 sql/migrations/20170113181857_addConstraintsToPeers.sql diff --git a/sql/migrations/20170113181857_addConstraintsToPeers.sql b/sql/migrations/20170113181857_addConstraintsToPeers.sql new file mode 100644 index 00000000000..5da53181cac --- /dev/null +++ b/sql/migrations/20170113181857_addConstraintsToPeers.sql @@ -0,0 +1,11 @@ +/* Add constraints to improve upserts + * + */ + +BEGIN; + +ALTER TABLE peers + ADD CONSTRAINT unique_address +UNIQUE (ip, port); + +COMMIT; \ No newline at end of file diff --git a/sql/peers.js b/sql/peers.js index f7fdbd3773a..13c1c8de2df 100644 --- a/sql/peers.js +++ b/sql/peers.js @@ -41,7 +41,7 @@ var PeersSql = { addDapp: 'INSERT INTO peers_dapp ("peerId", "dappid") VALUES (${peerId}, ${dappId}) ON CONFLICT DO NOTHING', - upsert: 'INSERT INTO peers AS p ("ip", "port", "state", "os", "version", "broadhash", "height") VALUES (${ip}, ${port}, ${state}, ${os}, ${version}, ${broadhash}, ${height}) ON CONFLICT ("ip", "port") DO UPDATE SET ("ip", "port", "state", "os", "version", "broadhash", "height") = (${ip}, ${port}, (CASE WHEN p."state" = 0 THEN p."state" ELSE ${state} END), ${os}, ${version}, (CASE WHEN ${broadhash} IS NULL THEN p."broadhash" ELSE ${broadhash} END), (CASE WHEN ${height} IS NULL THEN p."height" ELSE ${height} END))' + upsert: 'INSERT INTO peers AS p ("ip", "port", "state", "os", "version", "broadhash", "height") VALUES (${ip}, ${port}, ${state}, ${os}, ${version}, ${broadhash}, ${height}) ON CONFLICT ON CONSTRAINT unique_address DO UPDATE SET ("ip", "port", "state", "os", "version", "broadhash", "height") = (${ip}, ${port}, (CASE WHEN p."state" = 0 THEN p."state" ELSE ${state} END), ${os}, ${version}, (CASE WHEN ${broadhash} IS NULL THEN p."broadhash" ELSE ${broadhash} END), (CASE WHEN ${height} IS NULL THEN p."height" ELSE ${height} END))' }; module.exports = PeersSql; From 756b4c803031b5286fb9c05aeb759dab55ef93a9 Mon Sep 17 00:00:00 2001 From: Isabella Dell Date: Thu, 19 Jan 2017 10:24:32 -0600 Subject: [PATCH 064/123] Add createdb for mainnet This has 0 impact and saves some headache. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ff16e6f7a51..6dbb6a2139d 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,9 @@ Lisk is a next generation crypto-currency and decentralized application platform curl -sL "https://downloads.lisk.io/scripts/setup_postgresql.Linux" | bash - sudo -u postgres createuser --createdb $USER createdb lisk_test + createdb lisk_main sudo -u postgres psql -d lisk_test -c "alter user "$USER" with password 'password';" + sudo -u postgres psql -d lisk_main -c "alter user "$USER" with password 'password';" ``` - Bower () -- Bower helps to install required JavaScript dependencies. From a56779abf0ef363d238f4bb7ed6d6e69fab9ad8c Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Thu, 19 Jan 2017 17:36:54 +0100 Subject: [PATCH 065/123] add missing return statement in promise in BackwardTick function --- modules/rounds.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rounds.js b/modules/rounds.js index 49747f08a99..0eab898c720 100644 --- a/modules/rounds.js +++ b/modules/rounds.js @@ -107,7 +107,7 @@ Rounds.prototype.backwardTick = function (block, previousBlock, done) { delete __private.unRewardsByRound[round]; delete __private.unDelegatesByRound[round]; }).then(function () { - promised.markBlockId(); + return promised.markBlockId(); }); } else { return promised.markBlockId(); From 231d32f3610058c3e298bf634f65456f8976792e Mon Sep 17 00:00:00 2001 From: TheGoldenEye Date: Thu, 19 Jan 2017 21:42:46 +0100 Subject: [PATCH 066/123] replace double quote by single quote travis ci check failed modules/sql.js 108 | library.logger.trace("sql.query: ", sql); ^ [W109] Strings must use singlequote. --- modules/sql.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sql.js b/modules/sql.js index de39e134d27..a5bfd078716 100644 --- a/modules/sql.js +++ b/modules/sql.js @@ -105,7 +105,7 @@ __private.query = function (action, config, cb) { try { sql = jsonSql.build(extend({}, config, defaultConfig)); - library.logger.trace("sql.query: ", sql); + library.logger.trace('sql.query:', sql); } catch (e) { return done(e); } From 6b74bab3a99523b1d07c76b77717ef4807f1b0c5 Mon Sep 17 00:00:00 2001 From: 4miners Date: Fri, 20 Jan 2017 07:19:36 +0100 Subject: [PATCH 067/123] Improve ban handlig, added additional logs --- modules/blocks.js | 6 +----- modules/loader.js | 3 +-- modules/transport.js | 39 ++++++++++++++++++++++----------------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/modules/blocks.js b/modules/blocks.js index 793505306d2..10a67589ae7 100644 --- a/modules/blocks.js +++ b/modules/blocks.js @@ -879,11 +879,7 @@ Blocks.prototype.loadBlocksFromPeer = function (peer, cb) { } else { var id = (block ? block.id : 'null'); - library.logger.debug(['Block', id].join(' '), err.toString()); - if (block) { library.logger.debug('Block', block); } - - library.logger.warn(['Block', id, 'is not valid, ban 10 min'].join(' '), peer.string); - modules.peers.state(peer.ip, peer.port, 0, 600); + library.logger.debug ('Block processing failed [blocks]', { id: id, err: err.toString (), block: block}) } return seriesCb(err); }, true); diff --git a/modules/loader.js b/modules/loader.js index 3f94fea704b..9f894efc5d1 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -189,8 +189,7 @@ __private.loadTransactions = function (cb) { try { transaction = library.logic.transaction.objectNormalize(transaction); } catch (e) { - library.logger.debug(['Transaction', id].join(' '), e.toString()); - if (transaction) { library.logger.debug('Transaction', transaction); } + library.logger.debug ('Transaction normalization failed [loader]', {id: id, 'err': e.toString (), tx: transaction}) library.logger.warn(['Transaction', id, 'is not valid, ban 10 min'].join(' '), peer.string); modules.peers.state(peer.ip, peer.port, 0, 600); diff --git a/modules/transport.js b/modules/transport.js index c88d7cf3648..5bca6ceab02 100644 --- a/modules/transport.js +++ b/modules/transport.js @@ -96,8 +96,14 @@ __private.attachApi = function () { router.get('/blocks/common', function (req, res, next) { req.sanitize(req.query, schema.commonBlock, function (err, report, query) { - if (err) { return next(err); } - if (!report.isValid) { return res.json({success: false, error: report.issues}); } + if (err) { + library.logger.debug ('Common block request validation failed', { err: err.toString (), req: req.query }); + return next(err); + } + if (!report.isValid) { + library.logger.debug ('Common block request validation failed', { err: report, req: req.query }); + return res.json({success: false, error: report.issues}); + } var escapedIds = query.ids // Remove quotes @@ -110,7 +116,7 @@ __private.attachApi = function () { }); if (!escapedIds.length) { - library.logger.warn('Invalid common block request, ban 10 min', req.peer.string); + library.logger.debug ('Common block request validation failed', { err: 'ESCAPE', req: req.query }); // Ban peer for 10 minutes __private.banPeer({peer: req.peer, code: 'ECOMMON', req: req, clock: 600}); @@ -155,13 +161,10 @@ __private.attachApi = function () { try { block = library.logic.block.objectNormalize(block); } catch (e) { - library.logger.debug(['Block', id].join(' '), e.toString()); - if (block) { library.logger.debug('Block', block); } + library.logger.debug ('Block normalization failed [transport]', {'err': e.toString (), block: block }); - if (req.peer) { - // Ban peer for 10 minutes - __private.banPeer({peer: req.peer, code: 'EBLOCK', req: req, clock: 600}); - } + // Ban peer for 10 minutes + __private.banPeer({peer: req.peer, code: 'EBLOCK', req: req, clock: 600}); return res.status(200).json({success: false, error: e.toString()}); } @@ -341,7 +344,12 @@ __private.hashsum = function (obj) { }; __private.banPeer = function (options) { + if (!options.peer || !options.peer.ip || !options.peer.port) { + library.logger.trace ('Peer ban skipped', { options: options }); + return false; + } library.logger.warn([options.code, ['Ban', options.peer.string, (options.clock / 60), 'minutes'].join(' '), options.req.method, options.req.url].join(' ')); + library.logger.trace ('Peer banned', { options: options }); modules.peers.state(options.peer.ip, options.peer.port, 0, options.clock); }; @@ -436,13 +444,10 @@ __private.receiveTransaction = function (transaction, req, cb) { try { transaction = library.logic.transaction.objectNormalize(transaction); } catch (e) { - library.logger.debug(['Transaction', id].join(' '), e.toString()); - if (transaction) { library.logger.debug('Transaction', transaction); } + library.logger.debug ('Transaction normalization failed [transport]', {id: id, 'err': e.toString (), 'tx': transaction }); - if (req.peer) { - // Ban peer for 10 minutes - __private.banPeer({peer: req.peer, code: 'ETRANSACTION', req: req, clock: 600}); - } + // Ban peer for 10 minutes + __private.banPeer({peer: req.peer, code: 'ETRANSACTION', req: req, clock: 600}); return setImmediate(cb, 'Invalid transaction body'); } @@ -572,8 +577,8 @@ Transport.prototype.getFromPeer = function (peer, options, cb) { // Remove peer __private.removePeer({peer: peer, code: err.code, req: req}); } else { - // Ban peer for 10 minutes - __private.banPeer({peer: peer, code: err.code, req: req, clock: 600}); + // Ban peer for 1 minute + __private.banPeer({peer: peer, code: err.code, req: req, clock: 60}); } } From b11af9723737e6e654e1bdd7ce2c3e0b8edda949 Mon Sep 17 00:00:00 2001 From: 4miners Date: Fri, 20 Jan 2017 07:35:59 +0100 Subject: [PATCH 068/123] Fix jshint errors --- modules/blocks.js | 2 +- modules/loader.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/blocks.js b/modules/blocks.js index 10a67589ae7..590603bcb38 100644 --- a/modules/blocks.js +++ b/modules/blocks.js @@ -879,7 +879,7 @@ Blocks.prototype.loadBlocksFromPeer = function (peer, cb) { } else { var id = (block ? block.id : 'null'); - library.logger.debug ('Block processing failed [blocks]', { id: id, err: err.toString (), block: block}) + library.logger.debug ('Block processing failed [blocks]', { id: id, err: err.toString (), block: block}); } return seriesCb(err); }, true); diff --git a/modules/loader.js b/modules/loader.js index 9f894efc5d1..0002cfc29cc 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -189,7 +189,7 @@ __private.loadTransactions = function (cb) { try { transaction = library.logic.transaction.objectNormalize(transaction); } catch (e) { - library.logger.debug ('Transaction normalization failed [loader]', {id: id, 'err': e.toString (), tx: transaction}) + library.logger.debug ('Transaction normalization failed [loader]', {id: id, 'err': e.toString (), tx: transaction}); library.logger.warn(['Transaction', id, 'is not valid, ban 10 min'].join(' '), peer.string); modules.peers.state(peer.ip, peer.port, 0, 600); From 35db8e1482085403cc515753635f70278f9a3c8f Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Fri, 20 Jan 2017 13:18:57 +0100 Subject: [PATCH 069/123] Hinting file. Adding missing braces. --- modules/dapps.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/dapps.js b/modules/dapps.js index 7b04917a9c7..39edc300360 100644 --- a/modules/dapps.js +++ b/modules/dapps.js @@ -1071,8 +1071,9 @@ __private.createSandbox = function (dapp, params, cb) { var withDebug = false; process.execArgv.forEach( function(item, index) { - if (item.indexOf('--debug') >= 0) + if (item.indexOf('--debug') >= 0) { withDebug = true; + } }); var sandbox = new Sandbox(path.join(dappPath, 'index.js'), dapp.transactionId, params, __private.apiHandler, withDebug); From 1514e914798dff1ecb1b2752bb7dd7d1aeeb9fd3 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Fri, 20 Jan 2017 13:58:58 +0100 Subject: [PATCH 070/123] set up unit tests environment Write example unit test for peers upsert, mock modules with sinon, provide db access to test modules --- package.json | 1 + test/common/globalBefore.js | 20 +++++++++++ test/common/initModule.js | 66 +++++++++++++++++++++++++++++++++++++ test/index.js | 1 + test/unit/modules/peers.js | 61 ++++++++++++++++++++++++++++++++++ 5 files changed, 149 insertions(+) create mode 100644 test/common/globalBefore.js create mode 100644 test/common/initModule.js create mode 100644 test/unit/modules/peers.js diff --git a/package.json b/package.json index 61da9ae8668..9828bc2eed3 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "jshint": "=2.9.3", "mocha": "=3.1.0", "moment": "=2.15.1", + "sinon": "^1.17.7", "supertest": "=2.0.0" }, "config": { diff --git a/test/common/globalBefore.js b/test/common/globalBefore.js new file mode 100644 index 00000000000..cbe9a798ef0 --- /dev/null +++ b/test/common/globalBefore.js @@ -0,0 +1,20 @@ +'use strict'; + +/** + * @param {string} table + * @param {Logger} logger + * @param {Object} db + * @param {Function} cb + */ +function clearDatabaseTable(db, logger, table, cb) { + db.query('DELETE FROM ' + table).then(function (result) { + cb(null, result); + }).catch(function (err) { + logger.err('unable to delete data from table: ' + table); + throw err; + }); +} + +module.exports = { + clearDatabaseTable: clearDatabaseTable +}; \ No newline at end of file diff --git a/test/common/initModule.js b/test/common/initModule.js new file mode 100644 index 00000000000..bfef223e4c4 --- /dev/null +++ b/test/common/initModule.js @@ -0,0 +1,66 @@ +'use strict'; + +var express = require('express'); +var merge = require('lodash/merge'); + +var path = require('path'); +var dirname = path.join(__dirname, '..', '..'); +var config = require(path.join(dirname, '/config.json')); +var database = require(path.join(dirname, '/helpers', 'database.js')); +var genesisblock = require(path.join(dirname, '/genesisBlock.json')); +var Logger = require(dirname + '/logger.js'); + +var modulesLoader = new function() { + + this.db = null; + this.logger = new Logger({ echo: null, errorLevel: config.fileLogLevel, filename: config.logFileName }); + /** + * @param {Function} Module + * @param {Object} scope + * @param {Function} cb + */ + this.init = function (Module, scope, cb) { + new Module(function (err, module) { + return cb(err, module); + }, merge({}, scope, { + network: { + app: express() + }, + genesisblock: genesisblock + })); + }; + + /** + * @param {Function} Module + * @param {Function} cb + */ + this.initWithDb = function(Module, cb) { + this.getDbConnection(function (err, db) { + if (err) { + return cb(err); + } + this.init(Module, {db: db}, cb); + }.bind(this)); + }; + + /** + * @param {Function} cb + */ + this.getDbConnection = function (cb) { + if (this.db) { + return cb(null, this.db); + } + database.connect(config.db, this.logger, function (err, db) { + if (err) { + return cb(err); + } + this.db = db; + cb(null, this.db); + }.bind(this)); + }; + +}; + +module.exports = { + modulesLoader: modulesLoader +}; \ No newline at end of file diff --git a/test/index.js b/test/index.js index 86fe0fcaa5f..66a8b8e5d0e 100644 --- a/test/index.js +++ b/test/index.js @@ -1,5 +1,6 @@ require('./unit/helpers/request-limiter.js'); require('./unit/logic/blockReward.js'); +require('./unit/modules/peers.js'); require('./api/accounts.js'); require('./api/blocks.js'); diff --git a/test/unit/modules/peers.js b/test/unit/modules/peers.js new file mode 100644 index 00000000000..4bcfabe6486 --- /dev/null +++ b/test/unit/modules/peers.js @@ -0,0 +1,61 @@ +'use strict'; /*jslint mocha:true, expr:true */ + +var chai = require('chai'); +var express = require('express'); +var sinon = require('sinon'); +var node = require('../../node.js'); + +var clearDatabaseTable = require('../../common/globalBefore').clearDatabaseTable; +var modulesLoader = require('../../common/initModule').modulesLoader; +var Peer = require('../../../logic/peer'); +var Peers = require('../../../modules/peers'); +var PeerSweeper = require('../../../logic/peerSweeper'); + +var randomPeer = { + 'broadhash': '198f2b61a8eb95fbeed58b8216780b68f697f26b849acf00c8c93bb9b24f783d', + 'dappid': null, + 'height': 1, + 'ip': '40.40.40.40', + 'os': 'unknown', + 'port': 4000, + 'state': 2, + 'version': '0.1.2' +}; + +describe('peers', function () { + + var peers; + + before(function (done) { + modulesLoader.initWithDb(Peers, function (err, peersModule) { + if (err) { + return done(err); + } + peers = peersModule; + done(); + }); + }); + + describe('update', function () { + + before(function (done) { + sinon.stub(PeerSweeper.prototype, 'push').returns(true); + done(); + }); + + it('should call PeerSweeper push with proper parameters', function (done) { + node.expect(peers.update(randomPeer)).to.be.ok; + sinon.assert.calledWith(PeerSweeper.prototype.push, 'upsert', new Peer(randomPeer).object()); + done(); + }); + }); + + after(function (done) { + modulesLoader.getDbConnection(function (err, db) { + if (err) { + return done(err); + } + clearDatabaseTable(db, modulesLoader.logger, 'peers', done); + }); + }); +}); From dea14086b54f6442dd2b1efeac1ff4ac372e08c6 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Fri, 20 Jan 2017 16:03:26 +0100 Subject: [PATCH 071/123] add tests to check if NULLs are last while using all of sort fields in delegates, peers and transactions --- modules/transactions.js | 4 +++- test/api/delegates.js | 37 +++++++++++++++++++----------------- test/api/peers.js | 41 ++++++++++++++++++++++------------------ test/api/transactions.js | 37 ++++++++++++++++++++---------------- 4 files changed, 67 insertions(+), 52 deletions(-) diff --git a/modules/transactions.js b/modules/transactions.js index 57030ca41d5..938de0b63b9 100644 --- a/modules/transactions.js +++ b/modules/transactions.js @@ -126,8 +126,10 @@ __private.list = function (filter, cb) { filter.orderBy, { sortFields: sql.sortFields, fieldPrefix: function (sortField) { - if (['height', 'blockId', 'confirmations'].indexOf(sortField) > -1) { + if (['height'].indexOf(sortField) > -1) { return 'b_' + sortField; + } else if (['confirmations'].indexOf(sortField) > -1) { + return sortField; } else { return 't_' + sortField; } diff --git a/test/api/delegates.js b/test/api/delegates.js index 788d0d35509..555abad0e47 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -1,5 +1,6 @@ 'use strict'; /*jslint mocha:true, expr:true */ +var async = require('async'); var node = require('./../node.js'); function openAccount (params, done) { @@ -630,27 +631,29 @@ describe('GET /api/delegates', function () { }); }); - it('using orderBy with random sort field should not place NULLs first', function (done) { + it('using orderBy with any of sort fields should not place NULLs first', function (done) { var delegatesSortFields = ['approval', 'productivity', 'rate', 'vote']; - var randomSortField = delegatesSortFields[Math.floor(Math.random() * delegatesSortFields.length)]; - node.get('/api/delegates?orderBy=' + randomSortField, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('delegates').that.is.an('array'); + async.each(delegatesSortFields, function (sortField, cb) { + node.get('/api/delegates?orderBy=' + sortField, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('delegates').that.is.an('array'); - var dividedIndices = res.body.delegates.reduce(function (memo, peer, index) { - memo[peer[randomSortField] === null ? 'nullIndices' : 'notNullIndices'].push(index); - return memo; - }, {notNullIndices: [], nullIndices: []}); + var dividedIndices = res.body.delegates.reduce(function (memo, peer, index) { + memo[peer[sortField] === null ? 'nullIndices' : 'notNullIndices'].push(index); + return memo; + }, {notNullIndices: [], nullIndices: []}); - if (dividedIndices.nullIndices.length && dividedIndices.notNullIndices.length) { - var ascOrder = function (a, b) { return a - b; }; - dividedIndices.notNullIndices.sort(ascOrder); - dividedIndices.nullIndices.sort(ascOrder); - - node.expect(dividedIndices.notNullIndices[dividedIndices.notNullIndices.length - 1]) - .to.be.at.most(dividedIndices.nullIndices[0]); - } + if (dividedIndices.nullIndices.length && dividedIndices.notNullIndices.length) { + var ascOrder = function (a, b) { return a - b; }; + dividedIndices.notNullIndices.sort(ascOrder); + dividedIndices.nullIndices.sort(ascOrder); + node.expect(dividedIndices.notNullIndices[dividedIndices.notNullIndices.length - 1]) + .to.be.at.most(dividedIndices.nullIndices[0]); + } + cb(); + }); + }, function () { done(); }); }); diff --git a/test/api/peers.js b/test/api/peers.js index 88eb4605934..6cfc1341a82 100644 --- a/test/api/peers.js +++ b/test/api/peers.js @@ -1,6 +1,8 @@ 'use strict'; /*jslint mocha:true, expr:true */ +var async = require('async'); var node = require('./../node.js'); +var peersSortFields = require('../../sql/peers').sortFields; describe('GET /api/peers/version', function () { @@ -348,27 +350,30 @@ describe('GET /api/peers', function () { }); }); - it('using orderBy == "height:desc" should not place NULLs first', function (done) { - node.get('/api/peers?orderBy=height:desc', function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('peers').that.is.an('array'); + it('using orderBy with any of sort fields should not place NULLs first', function (done) { + async.each(peersSortFields, function (sortField, cb) { + node.get('/api/peers?orderBy=' + sortField, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('peers').that.is.an('array'); - var dividedIndices = res.body.peers.reduce(function (memo, peer, index) { - memo[peer.height === null ? 'nullIndices' : 'notNullIndices'].push(index); - return memo; - }, {notNullIndices: [], nullIndices: []}); + var dividedIndices = res.body.peers.reduce(function (memo, peer, index) { + memo[peer[sortField] === null ? 'nullIndices' : 'notNullIndices'].push(index); + return memo; + }, {notNullIndices: [], nullIndices: []}); - if (dividedIndices.nullIndices.length && dividedIndices.notNullIndices.length) { - var ascOrder = function (a, b) { return a - b; }; - dividedIndices.notNullIndices.sort(ascOrder); - dividedIndices.nullIndices.sort(ascOrder); + if (dividedIndices.nullIndices.length && dividedIndices.notNullIndices.length) { + var ascOrder = function (a, b) { return a - b; }; + dividedIndices.notNullIndices.sort(ascOrder); + dividedIndices.nullIndices.sort(ascOrder); - node.expect(dividedIndices.notNullIndices[dividedIndices.notNullIndices.length - 1]) - .to.be.at.most(dividedIndices.nullIndices[0]); - } - - done(); - }); + node.expect(dividedIndices.notNullIndices[dividedIndices.notNullIndices.length - 1]) + .to.be.at.most(dividedIndices.nullIndices[0]); + } + cb(); + }); + }, function () { + done(); + }); }); it('using string limit should fail', function (done) { diff --git a/test/api/transactions.js b/test/api/transactions.js index 874c4823ba3..b643a94bdb0 100644 --- a/test/api/transactions.js +++ b/test/api/transactions.js @@ -1,6 +1,8 @@ 'use strict'; /*jslint mocha:true, expr:true */ +var async = require('async'); var node = require('./../node.js'); +var transactionSortFields = require('../../sql/transactions').sortFields; var account = node.randomTxAccount(); var account2 = node.randomTxAccount(); @@ -216,25 +218,28 @@ describe('GET /api/transactions', function () { }); }); - it('using orderBy == "recipientId" should not place NULLs first', function (done) { - node.get('/api/transactions?orderBy=recipientId', function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactions').that.is.an('array'); + it('using orderBy with any of sort fields should not place NULLs first', function (done) { + async.each(transactionSortFields, function (sortField, cb) { + node.get('/api/transactions?orderBy=' + sortField, function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('transactions').that.is.an('array'); - var dividedIndices = res.body.transactions.reduce(function (memo, peer, index) { - memo[peer.recipientId === null ? 'nullIndices' : 'notNullIndices'].push(index); - return memo; - }, {notNullIndices: [], nullIndices: []}); + var dividedIndices = res.body.transactions.reduce(function (memo, peer, index) { + memo[peer[sortField] === null ? 'nullIndices' : 'notNullIndices'].push(index); + return memo; + }, {notNullIndices: [], nullIndices: []}); - if (dividedIndices.nullIndices.length && dividedIndices.notNullIndices.length) { - var ascOrder = function (a, b) { return a - b; }; - dividedIndices.notNullIndices.sort(ascOrder); - dividedIndices.nullIndices.sort(ascOrder); - - node.expect(dividedIndices.notNullIndices[dividedIndices.notNullIndices.length - 1]) - .to.be.at.most(dividedIndices.nullIndices[0]); - } + if (dividedIndices.nullIndices.length && dividedIndices.notNullIndices.length) { + var ascOrder = function (a, b) { return a - b; }; + dividedIndices.notNullIndices.sort(ascOrder); + dividedIndices.nullIndices.sort(ascOrder); + node.expect(dividedIndices.notNullIndices[dividedIndices.notNullIndices.length - 1]) + .to.be.at.most(dividedIndices.nullIndices[0]); + } + cb(); + }); + }, function () { done(); }); }); From 20be89e6099c0720ad5f9aa2e64ecd13563316a8 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Fri, 20 Jan 2017 16:11:10 +0100 Subject: [PATCH 072/123] fix path accessing in helpers/database --- helpers/database.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/helpers/database.js b/helpers/database.js index 6e9f7d9bcbb..5bdcd047a04 100644 --- a/helpers/database.js +++ b/helpers/database.js @@ -4,6 +4,7 @@ var async = require('async'); var bignum = require('./bignum'); var fs = require('fs'); var path = require('path'); +var dirname = path.join(__dirname, '..'); // var isWin = /^win/.test(process.platform); // var isMac = /^darwin/.test(process.platform); @@ -102,7 +103,7 @@ function Migrator (pgp, db) { }; this.applyRuntimeQueryFile = function (waterCb) { - var sql = new pgp.QueryFile(path.join('sql', 'runtime.sql'), {minify: true}); + var sql = new pgp.QueryFile(path.join(dirname, 'sql', 'runtime.sql'), {minify: true}); db.query(sql).then(function () { return waterCb(); @@ -123,7 +124,7 @@ module.exports.connect = function (config, logger, cb) { monitor.attach(pgOptions, config.logEvents); monitor.setTheme('matrix'); - monitor.log = function(msg, info){ + monitor.log = function (msg, info){ logger.log(info.event, info.text); info.display = false; }; From a9544cf4519ba41f2c8c3d07e590e6ebe3993dba Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Fri, 20 Jan 2017 17:35:26 +0100 Subject: [PATCH 073/123] use async library from test node module --- test/api/delegates.js | 3 +-- test/api/peers.js | 3 +-- test/api/transactions.js | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/test/api/delegates.js b/test/api/delegates.js index 555abad0e47..a3c1f0d3acc 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -1,6 +1,5 @@ 'use strict'; /*jslint mocha:true, expr:true */ -var async = require('async'); var node = require('./../node.js'); function openAccount (params, done) { @@ -633,7 +632,7 @@ describe('GET /api/delegates', function () { it('using orderBy with any of sort fields should not place NULLs first', function (done) { var delegatesSortFields = ['approval', 'productivity', 'rate', 'vote']; - async.each(delegatesSortFields, function (sortField, cb) { + node.async.each(delegatesSortFields, function (sortField, cb) { node.get('/api/delegates?orderBy=' + sortField, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('delegates').that.is.an('array'); diff --git a/test/api/peers.js b/test/api/peers.js index 6cfc1341a82..c3c71c329a9 100644 --- a/test/api/peers.js +++ b/test/api/peers.js @@ -1,6 +1,5 @@ 'use strict'; /*jslint mocha:true, expr:true */ -var async = require('async'); var node = require('./../node.js'); var peersSortFields = require('../../sql/peers').sortFields; @@ -351,7 +350,7 @@ describe('GET /api/peers', function () { }); it('using orderBy with any of sort fields should not place NULLs first', function (done) { - async.each(peersSortFields, function (sortField, cb) { + node.async.each(peersSortFields, function (sortField, cb) { node.get('/api/peers?orderBy=' + sortField, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('peers').that.is.an('array'); diff --git a/test/api/transactions.js b/test/api/transactions.js index b643a94bdb0..4d94b8baf8b 100644 --- a/test/api/transactions.js +++ b/test/api/transactions.js @@ -1,6 +1,5 @@ 'use strict'; /*jslint mocha:true, expr:true */ -var async = require('async'); var node = require('./../node.js'); var transactionSortFields = require('../../sql/transactions').sortFields; @@ -219,7 +218,7 @@ describe('GET /api/transactions', function () { }); it('using orderBy with any of sort fields should not place NULLs first', function (done) { - async.each(transactionSortFields, function (sortField, cb) { + node.async.each(transactionSortFields, function (sortField, cb) { node.get('/api/transactions?orderBy=' + sortField, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('transactions').that.is.an('array'); From 4bf8237d02600bdf13e0ca3f22110c5db2a92397 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Mon, 23 Jan 2017 11:38:51 +0100 Subject: [PATCH 074/123] add unique_address constraint based on peers_unique index --- sql/migrations/20170113181857_addConstraintsToPeers.sql | 6 +++--- sql/peers.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sql/migrations/20170113181857_addConstraintsToPeers.sql b/sql/migrations/20170113181857_addConstraintsToPeers.sql index 5da53181cac..349d695b6ee 100644 --- a/sql/migrations/20170113181857_addConstraintsToPeers.sql +++ b/sql/migrations/20170113181857_addConstraintsToPeers.sql @@ -4,8 +4,8 @@ BEGIN; -ALTER TABLE peers - ADD CONSTRAINT unique_address -UNIQUE (ip, port); +ALTER TABLE "peers" + ADD CONSTRAINT "address_unique" UNIQUE + USING INDEX "peers_unique"; COMMIT; \ No newline at end of file diff --git a/sql/peers.js b/sql/peers.js index 13c1c8de2df..aaded48965a 100644 --- a/sql/peers.js +++ b/sql/peers.js @@ -41,7 +41,7 @@ var PeersSql = { addDapp: 'INSERT INTO peers_dapp ("peerId", "dappid") VALUES (${peerId}, ${dappId}) ON CONFLICT DO NOTHING', - upsert: 'INSERT INTO peers AS p ("ip", "port", "state", "os", "version", "broadhash", "height") VALUES (${ip}, ${port}, ${state}, ${os}, ${version}, ${broadhash}, ${height}) ON CONFLICT ON CONSTRAINT unique_address DO UPDATE SET ("ip", "port", "state", "os", "version", "broadhash", "height") = (${ip}, ${port}, (CASE WHEN p."state" = 0 THEN p."state" ELSE ${state} END), ${os}, ${version}, (CASE WHEN ${broadhash} IS NULL THEN p."broadhash" ELSE ${broadhash} END), (CASE WHEN ${height} IS NULL THEN p."height" ELSE ${height} END))' + upsert: 'INSERT INTO peers AS p ("ip", "port", "state", "os", "version", "broadhash", "height") VALUES (${ip}, ${port}, ${state}, ${os}, ${version}, ${broadhash}, ${height}) ON CONFLICT ON CONSTRAINT address_unique DO UPDATE SET ("ip", "port", "state", "os", "version", "broadhash", "height") = (${ip}, ${port}, (CASE WHEN p."state" = 0 THEN p."state" ELSE ${state} END), ${os}, ${version}, (CASE WHEN ${broadhash} IS NULL THEN p."broadhash" ELSE ${broadhash} END), (CASE WHEN ${height} IS NULL THEN p."height" ELSE ${height} END))' }; module.exports = PeersSql; From 20d54e525266a4a460723d8ba0f25da5fd96cc7d Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Mon, 23 Jan 2017 18:30:34 +0100 Subject: [PATCH 075/123] remove after sending money back after multisig test ends --- test/api/multisignatures.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/test/api/multisignatures.js b/test/api/multisignatures.js index f0a5e5495f9..97db46d2696 100644 --- a/test/api/multisignatures.js +++ b/test/api/multisignatures.js @@ -591,18 +591,4 @@ describe('POST /api/multisignatures/sign (regular account)', function () { done(); }); }); - - after(function (done) { - //send the money back - node.put('/api/transactions/', { - secret: accounts[0].password, - amount: 1, - recipientId: node.gAccount.address - }, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - done(); - }); - }); - - }); From 24cac3db3367108a3827fc9c7b48239cb6558f08 Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 24 Jan 2017 03:34:07 +0100 Subject: [PATCH 076/123] Make sql query more readable, changed error message --- modules/blocks.js | 2 +- sql/blocks.js | 46 +++++++++++++++++++++++----------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/modules/blocks.js b/modules/blocks.js index 487127e644e..48ccab91b20 100644 --- a/modules/blocks.js +++ b/modules/blocks.js @@ -1414,7 +1414,7 @@ Blocks.prototype.aggregateBlocksReward = function (filter, cb) { library.db.query(sql.aggregateBlocksReward(params), params).then(function (rows) { var data = rows[0]; if (data.delegate === null) { - return setImmediate(cb, 'Account not exists or is not a delegate'); + return setImmediate(cb, 'Account not found or is not a delegate'); } data = { fees: data.fees || '0', rewards: data.rewards || '0', count: data.count || '0' }; return setImmediate(cb, null, data); diff --git a/sql/blocks.js b/sql/blocks.js index 7672a8901b8..1b0199aaa92 100644 --- a/sql/blocks.js +++ b/sql/blocks.js @@ -27,49 +27,49 @@ var BlocksSql = { aggregateBlocksReward: function (params) { return [ 'WITH', - 'delegate as (SELECT', + 'delegate AS (SELECT', '1 FROM mem_accounts m WHERE m."isDelegate" = 1 AND m."publicKey" = DECODE (${generatorPublicKey}, \'hex\') LIMIT 1),', - 'borders as (SELECT', - '(SELECT (CAST(b.height / ${delegates} AS INTEGER) + (CASE WHEN b.height % ${delegates} > 0 THEN 1 ELSE 0 END)) FROM blocks b ORDER BY b.height DESC LIMIT 1) as current,', + 'borders AS (SELECT', + '(SELECT (CAST(b.height / ${delegates} AS INTEGER) + (CASE WHEN b.height % ${delegates} > 0 THEN 1 ELSE 0 END)) FROM blocks b ORDER BY b.height DESC LIMIT 1) AS current,', '(SELECT (CAST(b.height / ${delegates} AS INTEGER) + (CASE WHEN b.height % ${delegates} > 0 THEN 1 ELSE 0 END)) FROM blocks b', (params.start !== undefined ? ' WHERE b.timestamp >= ${start}' : ''), - 'ORDER BY b.height ASC LIMIT 1) as min,', + 'ORDER BY b.height ASC LIMIT 1) AS min,', '(SELECT (CAST(b.height / ${delegates} AS INTEGER) + (CASE WHEN b.height % ${delegates} > 0 THEN 1 ELSE 0 END)) FROM blocks b', (params.end !== undefined ? ' WHERE b.timestamp <= ${end}' : ''), - 'ORDER BY b.height DESC LIMIT 1) as max', + 'ORDER BY b.height DESC LIMIT 1) AS max', '),', - 'r as (SELECT DISTINCT ', + 'r AS (SELECT DISTINCT ', '(CAST(b.height / ${delegates} AS INTEGER) + (CASE WHEN b.height % ${delegates} > 0 THEN 1 ELSE 0 END)) AS round', 'FROM blocks b WHERE b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\')),', - 're as (SELECT r.round as round, ((r.round-1)*${delegates})+1 as min, r.round*${delegates} as max', + 're AS (SELECT r.round AS round, ((r.round-1)*${delegates})+1 AS min, r.round*${delegates} AS max', 'FROM r WHERE r.round >= (SELECT min FROM borders) AND round <= (SELECT max FROM borders)),', - 'sum_min as (SELECT', - 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b.reward ELSE 0 END) AS rewards,', - 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS blocks', + 'sum_min AS (SELECT', + 'SUM(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b.reward ELSE 0 END) AS rewards,', + 'SUM(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS blocks', 'FROM blocks b WHERE b.height BETWEEN (SELECT min FROM re ORDER BY round ASC LIMIT 1) AND (SELECT max FROM re ORDER BY round ASC LIMIT 1)', (params.start !== undefined ? 'AND b.timestamp >= ${start}' : ''), '),', - 'sum_max as (SELECT', - 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b.reward ELSE 0 END) AS rewards,', - 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS blocks', + 'sum_max AS (SELECT', + 'SUM(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b.reward ELSE 0 END) AS rewards,', + 'SUM(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS blocks', 'FROM blocks b WHERE b.height BETWEEN (SELECT min FROM re ORDER BY round DESC LIMIT 1) AND (SELECT max FROM re ORDER BY round DESC LIMIT 1)', (params.end !== undefined ? 'AND b.timestamp <= ${end}' : ''), '),', - 'rs as (SELECT re.*, SUM(b."totalFee") AS fees,', - 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b.reward ELSE 0 END) AS rewards,', - 'sum(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS blocks', + 'rs AS (SELECT re.*, SUM(b."totalFee") AS fees,', + 'SUM(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN b.reward ELSE 0 END) AS rewards,', + 'SUM(CASE WHEN b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') THEN 1 ELSE 0 END) AS blocks', 'FROM re, blocks b WHERE b.height BETWEEN re.min AND re.max GROUP BY re.round, re.min, re.max),', - 'rsc as (SELECT', + 'rsc AS (SELECT', '(CASE WHEN round = borders.current THEN 0 ELSE fees END), round,', - '(CASE WHEN round = borders.min THEN (SELECT blocks FROM sum_min) ELSE (CASE WHEN round = borders.max THEN (SELECT blocks FROM sum_max) ELSE blocks END) END) as blocks,', - '(CASE WHEN round = borders.min THEN (SELECT rewards FROM sum_min) ELSE (CASE WHEN round = borders.max THEN (SELECT rewards FROM sum_max) ELSE rewards END) END) as rewards,', + '(CASE WHEN round = borders.min THEN (SELECT blocks FROM sum_min) ELSE (CASE WHEN round = borders.max THEN (SELECT blocks FROM sum_max) ELSE blocks END) END) AS blocks,', + '(CASE WHEN round = borders.min THEN (SELECT rewards FROM sum_min) ELSE (CASE WHEN round = borders.max THEN (SELECT rewards FROM sum_max) ELSE rewards END) END) AS rewards,', '(SELECT 1 FROM blocks b WHERE b.height = rs.max AND b."generatorPublicKey" = DECODE (${generatorPublicKey}, \'hex\') LIMIT 1) AS last', 'FROM rs, borders)', 'SELECT', - '(SELECT * FROM delegate) as delegate,', - 'sum (rsc.blocks) as count,', - 'sum(floor(rsc.fees/${delegates})*rsc.blocks + (CASE WHEN rsc.last = 1 THEN (rsc.fees-floor(rsc.fees/${delegates})*${delegates}) ELSE 0 END)) as fees,', - 'sum(rsc.rewards) as rewards', + '(SELECT * FROM delegate) AS delegate,', + 'SUM(rsc.blocks) AS count,', + 'SUM(floor(rsc.fees/${delegates})*rsc.blocks + (CASE WHEN rsc.last = 1 THEN (rsc.fees-floor(rsc.fees/${delegates})*${delegates}) ELSE 0 END)) AS fees,', + 'SUM(rsc.rewards) AS rewards', 'FROM rsc' ].filter(Boolean).join(' '); }, From 0f8cc6f28a0721eafc4a9a807f1331b4db42f0f7 Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 24 Jan 2017 12:49:44 +0100 Subject: [PATCH 077/123] Changing expectations for getForgedByAccount --- test/api/delegates.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api/delegates.js b/test/api/delegates.js index b5cb14f0a9d..1517463d952 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -1071,7 +1071,7 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('error').to.eql('Account not exists or is not a delegate'); + node.expect(res.body).to.have.property('error').to.eql('Account not found or is not a delegate'); done(); }); }); From 0dca3d2208df9167fcd51f1b6c7dd99cdab17769 Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 24 Jan 2017 12:51:55 +0100 Subject: [PATCH 078/123] Improved performance of summedRound SQL query --- sql/rounds.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/rounds.js b/sql/rounds.js index 886e0f17aac..2fff43a4a64 100644 --- a/sql/rounds.js +++ b/sql/rounds.js @@ -19,7 +19,7 @@ var RoundsSql = { updateBlockId: 'UPDATE mem_accounts SET "blockId" = ${newId} WHERE "blockId" = ${oldId};', - summedRound: 'SELECT SUM(b."totalFee")::bigint AS "fees", ARRAY_AGG(b."reward") AS "rewards", ARRAY_AGG(ENCODE(b."generatorPublicKey", \'hex\')) AS "delegates" FROM blocks b WHERE (SELECT (CAST(b."height" / ${activeDelegates} AS INTEGER) + (CASE WHEN b."height" % ${activeDelegates} > 0 THEN 1 ELSE 0 END))) = ${round}' + summedRound: 'WITH round_blocks as (SELECT ((${round}-1)*${activeDelegates})+1 as min, ${round}*${activeDelegates} as max) SELECT SUM(b."totalFee")::bigint AS "fees", ARRAY_AGG(b."reward") AS "rewards", ARRAY_AGG(ENCODE(b."generatorPublicKey", \'hex\')) AS "delegates" FROM blocks b WHERE b.height BETWEEN (SELECT min FROM round_blocks) AND (SELECT max FROM round_blocks)' }; module.exports = RoundsSql; From 003123190b291b5830e4df1d5c6ef0244200cc3e Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 24 Jan 2017 13:02:43 +0100 Subject: [PATCH 079/123] Ignoring Sublime SFTP config file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d55ff456982..d0501c27e5b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ release ssl/ stacktrace* tmp +sftp-config.json From 9029be45bde3d752dcd33e6584a5f5ace8ff6c52 Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 24 Jan 2017 13:08:00 +0100 Subject: [PATCH 080/123] Redesign of /api/transactions endpoint --- logic/transaction.js | 1 + modules/transactions.js | 175 +++++++++++++----- schema/transactions.js | 66 ++++++- .../20170124071600_recreateTrsListView.sql | 33 ++++ sql/transactions.js | 12 +- 5 files changed, 229 insertions(+), 58 deletions(-) create mode 100644 sql/migrations/20170124071600_recreateTrsListView.sql diff --git a/logic/transaction.js b/logic/transaction.js index 6ed371685ef..95a39e63a7c 100644 --- a/logic/transaction.js +++ b/logic/transaction.js @@ -826,6 +826,7 @@ Transaction.prototype.dbRead = function (raw) { requesterPublicKey: raw.t_requesterPublicKey, senderId: raw.t_senderId, recipientId: raw.t_recipientId, + recipientPublicKey: raw.m_recipientPublicKey || null, amount: parseInt(raw.t_amount), fee: parseInt(raw.t_fee), signature: raw.t_signature, diff --git a/modules/transactions.js b/modules/transactions.js index 57030ca41d5..9aa034849cd 100644 --- a/modules/transactions.js +++ b/modules/transactions.js @@ -1,23 +1,23 @@ 'use strict'; -var async = require('async'); -var ByteBuffer = require('bytebuffer'); -var constants = require('../helpers/constants.js'); -var crypto = require('crypto'); -var extend = require('extend'); -var genesisblock = null; -var OrderBy = require('../helpers/orderBy.js'); -var Router = require('../helpers/router.js'); -var sandboxHelper = require('../helpers/sandbox.js'); -var schema = require('../schema/transactions.js'); -var slots = require('../helpers/slots.js'); -var sql = require('../sql/transactions.js'); -var TransactionPool = require('../logic/transactionPool.js'); -var transactionTypes = require('../helpers/transactionTypes.js'); -var Transfer = require('../logic/transfer.js'); +var _ = require('lodash'), + async = require('async'), + ByteBuffer = require('bytebuffer'), + constants = require('../helpers/constants.js'), + crypto = require('crypto'), + extend = require('extend'), + OrderBy = require('../helpers/orderBy.js'), + Router = require('../helpers/router.js'), + sandboxHelper = require('../helpers/sandbox.js'), + schema = require('../schema/transactions.js'), + slots = require('../helpers/slots.js'), + sql = require('../sql/transactions.js'), + TransactionPool = require('../logic/transactionPool.js'), + transactionTypes = require('../helpers/transactionTypes.js'), + Transfer = require('../logic/transfer.js'); // Private fields -var modules, library, self, __private = {}, shared = {}; +var modules, library, self, __private = {}, shared = {}, genesisblock = null; __private.assetTypes = {}; @@ -73,39 +73,89 @@ __private.attachApi = function () { __private.list = function (filter, cb) { var sortFields = sql.sortFields; - var params = {}, where = [], owner = ''; + var params = {}, + where = [], + err, + owner = '', + allowedFieldsMap = { + blockId: '"t_blockId" = ${blockId}', + senderPublicKey: '"t_senderPublicKey" = DECODE (${senderPublicKey}, \'hex\')', + recipientPublicKey: '"m_recipientPublicKey" = DECODE (${recipientPublicKey}, \'hex\')', + senderId: '"t_senderId" = ${senderId}', + recipientId: '"t_recipientId" = ${recipientId}', + fromHeight: '"b_height" >= ${fromHeight}', + toHeight: '"b_height" <= ${toHeight}', + fromTimestamp: '"t_timestamp" >= ${fromTimestamp}', + toTimestamp: '"t_timestamp" <= ${toTimestamp}', + senderIds: '"t_senderId" IN (${senderIds:csv})', + recipientIds: '"t_recipientId" IN (${recipientIds:csv})', + senderPublicKeys: 'ENCODE ("t_senderPublicKey", \'hex\') IN (${senderPublicKeys:csv})', + recipientPublicKeys: 'ENCODE ("m_recipientPublicKey", \'hex\') IN (${recipientPublicKeys:csv})', + minAmount: '"t_amount" >= ${minAmount}', + maxAmount: '"t_amount" <= ${minAmount}', + type: '"t_type" = ${type}', + minConfirmations: 'confirmations >= ${minConfirmations}', + limit: null, + offset: null, + orderBy: null, + // FIXME: Backward compatibility, should be removed after transitional period + ownerAddress: null, + ownerPublicKey: null + }; + + // Generate list of fields with conditions + var isFirst = true; + _.each (filter, function (value, key) { + var field = String (key).split (':'); + if (field.length === 1) { + // Only field identifier, so using default 'OR' condition + field.unshift ('OR'); + } else if (field.length === 2) { + // Condition supplied, checking if correct one + if (_.includes (['or', 'and'], field[0].toLowerCase ())) { + field[0] = field[0].toUpperCase (); + } else { + err = 'Incorrect condition [' + field[0] + '] for field: ' + field[1]; + return false; + } + } else { + // Invalid parameter 'x:y:z' + err = 'Invalid parameter supplied: ' + key; + return false; + } - if (filter.blockId) { - where.push('"t_blockId" = ${blockId}'); - params.blockId = filter.blockId; - } + if (!value) { + err = 'Value for parameter [' + field[1] + '] cannot be empty'; + return false; + } - if (filter.senderPublicKey) { - where.push('"t_senderPublicKey"::bytea = ${senderPublicKey}'); - params.senderPublicKey = filter.senderPublicKey; - } + if (!_.includes (_.keys (allowedFieldsMap), field[1])) { + err = 'Parameter is not supported: ' + field[1]; + return false; + } - if (filter.senderId) { - where.push('"t_senderId" = ${senderId}'); - params.senderId = filter.senderId; - } + if (allowedFieldsMap[field[1]]) { + if (_.includes (['fromTimestamp', 'toTimestamp'], field[1])) { + value = value - constants.epochTime.getTime () / 1000; + } + where.push ((!isFirst ? (field[0] + ' ') : '') + allowedFieldsMap[field[1]]); + params[field[1]] = value; + isFirst = false; + } + }); - if (filter.recipientId) { - where.push('"t_recipientId" = ${recipientId}'); - params.recipientId = filter.recipientId; + // Checking for errors + if (err) { + return setImmediate (cb, err); } + // FIXME: Backward compatibility, should be removed after transitional period if (filter.ownerAddress && filter.ownerPublicKey) { - owner = '("t_senderPublicKey"::bytea = ${ownerPublicKey} OR "t_recipientId" = ${ownerAddress})'; + owner = '("t_senderPublicKey" = DECODE (${ownerPublicKey}, \'hex\') OR "t_recipientId" = ${ownerAddress})'; params.ownerPublicKey = filter.ownerPublicKey; params.ownerAddress = filter.ownerAddress; } - if (filter.type >= 0) { - where.push('"t_type" = ${type}'); - params.type = filter.type; - } - if (!filter.limit) { params.limit = 100; } else { @@ -118,8 +168,8 @@ __private.list = function (filter, cb) { params.offset = Math.abs(filter.offset); } - if (params.limit > 100) { - return setImmediate(cb, 'Invalid limit. Maximum is 100'); + if (params.limit > 1000) { + return setImmediate(cb, 'Invalid limit, maximum is 1000'); } var orderBy = OrderBy( @@ -381,18 +431,41 @@ Transactions.prototype.onPeersReady = function () { // Shared shared.getTransactions = function (req, cb) { - library.schema.validate(req.body, schema.getTransactions, function (err) { - if (err) { - return setImmediate(cb, err[0].message); - } - - __private.list(req.body, function (err, data) { - if (err) { - return setImmediate(cb, 'Failed to get transactions: ' + err); - } + async.waterfall([ + function (waterCb) { + var params = {}, + pattern = /(and|or){1}:/i; + + // Filter out 'and:'/'or:' from params to perform schema validation + _.each (req.body, function (value, key) { + var param = String (key).replace (pattern, ''); + // Dealing with array-like parameters (csv comma separated) + if (_.includes (['senderIds', 'recipintIds', 'senderPublicKeys', 'recipientPublicKeys'], param)) { + value = String (value).split (','); + req.body[key] = value; + } + params[param] = value; + }); - return setImmediate(cb, null, {transactions: data.transactions, count: data.count}); - }); + library.schema.validate (params, schema.getTransactions, function (err) { + if (err) { + return setImmediate (waterCb, err[0].message); + } else { + return setImmediate (waterCb, null); + } + }); + }, + function (waterCb) { + __private.list (req.body, function (err, data) { + if (err) { + return setImmediate (waterCb, 'Failed to get transactions: ' + err); + } else { + return setImmediate (waterCb, null, {transactions: data.transactions, count: data.count}); + } + }); + } + ], function (err, res) { + return setImmediate (cb, err, res); }); }; diff --git a/schema/transactions.js b/schema/transactions.js index 514d7501fa1..cf314f963fb 100644 --- a/schema/transactions.js +++ b/schema/transactions.js @@ -54,13 +54,77 @@ module.exports = { minimum: 0, maximum: constants.fixedPoint }, + senderPublicKeys: { + type: 'array', + minLength: 1, + 'items': { + type: 'string', + format: 'publicKey' + } + }, + recipientPublicKeys: { + type: 'array', + minLength: 1, + 'items': { + type: 'string', + format: 'publicKey' + } + }, + senderIds: { + type: 'array', + minLength: 1, + 'items': { + type: 'string', + format: 'address', + minLength: 1, + maxLength: 22 + } + }, + recipientIds: { + type: 'array', + minLength: 1, + 'items': { + type: 'string', + format: 'address', + minLength: 1, + maxLength: 22 + } + }, + fromHeight: { + type: 'integer', + minimum: 0 + }, + toHeight: { + type: 'integer', + minimum: 0 + }, + fromTimestamp: { + type: 'integer', + minimum: 0 + }, + toTimestamp: { + type: 'integer', + minimum: 0 + }, + minAmount: { + type: 'integer', + minimum: 0 + }, + maxAmount: { + type: 'integer', + minimum: 0 + }, + minConfirmations: { + type: 'integer', + minimum: 0 + }, orderBy: { type: 'string' }, limit: { type: 'integer', minimum: 1, - maximum: 100 + maximum: 1000 }, offset: { type: 'integer', diff --git a/sql/migrations/20170124071600_recreateTrsListView.sql b/sql/migrations/20170124071600_recreateTrsListView.sql new file mode 100644 index 00000000000..ea79756cca0 --- /dev/null +++ b/sql/migrations/20170124071600_recreateTrsListView.sql @@ -0,0 +1,33 @@ +/* + * Add 'm_recipientPublicKey' column to 'trs_list' view + * Change 't_senderPublicKey' data type from 'string' to 'bytea' + */ + +BEGIN; + +DROP VIEW IF EXISTS trs_list; + +CREATE VIEW trs_list AS + +SELECT t."id" AS "t_id", + b."height" AS "b_height", + t."blockId" AS "t_blockId", + t."type" AS "t_type", + t."timestamp" AS "t_timestamp", + t."senderPublicKey" AS "t_senderPublicKey", + m."publicKey" AS "m_recipientPublicKey", + t."senderId" AS "t_senderId", + t."recipientId" AS "t_recipientId", + t."amount" AS "t_amount", + t."fee" AS "t_fee", + ENCODE(t."signature", 'hex') AS "t_signature", + ENCODE(t."signSignature", 'hex') AS "t_SignSignature", + t."signatures" AS "t_signatures", + (SELECT MAX("height") + 1 FROM blocks) - b."height" AS "confirmations" + +FROM trs t + +INNER JOIN blocks b ON t."blockId" = b."id" +INNER JOIN mem_accounts m ON t."recipientId" = m."address"; + +COMMIT; diff --git a/sql/transactions.js b/sql/transactions.js index 2df17bd7ba3..43c7e2651c8 100644 --- a/sql/transactions.js +++ b/sql/transactions.js @@ -21,20 +21,20 @@ var TransactionsSql = { countList: function (params) { return [ - 'SELECT COUNT("t_id") FROM trs_list', - 'INNER JOIN blocks b ON "t_blockId" = b."id"', + 'SELECT COUNT(1) FROM trs_list', (params.where.length || params.owner ? 'WHERE' : ''), - (params.where.length ? '(' + params.where.join(' OR ') + ')' : ''), + (params.where.length ? '(' + params.where.join(' ') + ')' : ''), + // FIXME: Backward compatibility, should be removed after transitional period (params.where.length && params.owner ? ' AND ' + params.owner : params.owner) ].filter(Boolean).join(' '); }, list: function (params) { - // Need to fix 'or' or 'and' in query return [ - 'SELECT * FROM trs_list', + 'SELECT *, ENCODE ("t_senderPublicKey", \'hex\') AS "t_senderPublicKey", ENCODE ("m_recipientPublicKey", \'hex\') AS "m_recipientPublicKey" FROM trs_list', (params.where.length || params.owner ? 'WHERE' : ''), - (params.where.length ? '(' + params.where.join(' OR ') + ')' : ''), + (params.where.length ? '(' + params.where.join(' ') + ')' : ''), + // FIXME: Backward compatibility, should be removed after transitional period (params.where.length && params.owner ? ' AND ' + params.owner : params.owner), (params.sortField ? 'ORDER BY ' + [params.sortField, params.sortMethod].join(' ') : ''), 'LIMIT ${limit} OFFSET ${offset}' From a3c4870e50138eb3cea63c11de530724d06b6fbe Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 24 Jan 2017 15:03:21 +0100 Subject: [PATCH 081/123] Added test coverage for api/transactions endpoint --- test/api/transactions.js | 148 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 2 deletions(-) diff --git a/test/api/transactions.js b/test/api/transactions.js index f7ad1336660..a9be42457c9 100644 --- a/test/api/transactions.js +++ b/test/api/transactions.js @@ -94,6 +94,150 @@ describe('GET /api/transactions', function () { }); }); + it('using valid parameters with and/or should be ok', function (done) { + var limit = 10; + var offset = 0; + var orderBy = 'amount:asc'; + + var params = [ + 'and:blockId=' + '1', + 'or:senderId=' + node.gAccount.address, + 'or:recipientId=' + account.address, + 'limit=' + limit, + 'offset=' + offset, + 'orderBy=' + orderBy + ]; + + node.get('/api/transactions?' + params.join('&'), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('transactions').that.is.an('array'); + node.expect(res.body.transactions).to.have.length.within(transactionList.length, limit); + for (var i = 0; i < res.body.transactions.length; i++) { + if (res.body.transactions[i + 1]) { + node.expect(res.body.transactions[i].amount).to.be.at.most(res.body.transactions[i + 1].amount); + } + } + done(); + }); + }); + + it('using valid parameters with/without and/or should be ok', function (done) { + var limit = 10; + var offset = 0; + var orderBy = 'amount:asc'; + + var params = [ + 'and:blockId=' + '1', + 'or:senderId=' + node.gAccount.address, + 'or:recipientId=' + account.address, + 'fromHeight=' + 1, + 'toHeight=' + 666, + 'and:fromTimestamp=' + 0, + 'and:minAmount=' + 0, + 'limit=' + limit, + 'offset=' + offset, + 'orderBy=' + orderBy + ]; + + node.get('/api/transactions?' + params.join('&'), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('transactions').that.is.an('array'); + node.expect(res.body.transactions).to.have.length.within(transactionList.length, limit); + for (var i = 0; i < res.body.transactions.length; i++) { + if (res.body.transactions[i + 1]) { + node.expect(res.body.transactions[i].amount).to.be.at.most(res.body.transactions[i + 1].amount); + } + } + done(); + }); + }); + + it('using valid array-like parameters should be ok', function (done) { + var limit = 10; + var offset = 0; + var orderBy = 'amount:asc'; + + var params = [ + 'blockId=' + '1', + 'or:senderIds=' + node.gAccount.address + ',' + account.address, + 'or:recipientIds=' + account.address + ',' + account2.address, + 'or:senderPublicKeys=' + node.gAccount.publicKey, + 'or:recipientPublicKeys=' + node.gAccount.publicKey + ',' + account.publicKey, + 'limit=' + limit, + 'offset=' + offset, + 'orderBy=' + orderBy + ]; + + node.get('/api/transactions?' + params.join('&'), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res.body).to.have.property('transactions').that.is.an('array'); + node.expect(res.body.transactions).to.have.length.within(transactionList.length, limit); + for (var i = 0; i < res.body.transactions.length; i++) { + if (res.body.transactions[i + 1]) { + node.expect(res.body.transactions[i].amount).to.be.at.most(res.body.transactions[i + 1].amount); + } + } + done(); + }); + }); + + it('using one invalid field name with and/or should fail', function (done) { + var limit = 10; + var offset = 0; + var orderBy = 'amount:asc'; + + var params = [ + 'and:blockId=' + '1', + 'or:senderId=' + node.gAccount.address, + 'or:whatever=' + account.address, + 'limit=' + limit, + 'offset=' + offset, + 'orderBy=' + orderBy + ]; + + node.get('/api/transactions?' + params.join('&'), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error'); + done(); + }); + }); + + it('using invalid condition should fail', function (done) { + var params = [ + 'whatever:senderId=' + node.gAccount.address + ]; + + node.get('/api/transactions?' + params.join('&'), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error'); + done(); + }); + }); + + it('using invalid field name (x:y:z) should fail', function (done) { + var params = [ + 'or:whatever:senderId=' + node.gAccount.address + ]; + + node.get('/api/transactions?' + params.join('&'), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error'); + done(); + }); + }); + + it('using empty parameter should fail', function (done) { + var params = [ + 'and:publicKey=' + ]; + + node.get('/api/transactions?' + params.join('&'), function (err, res) { + node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res.body).to.have.property('error'); + done(); + }); + }); + it('using type should be ok', function (done) { var type = node.txTypes.SEND; var params = 'type=' + type; @@ -123,8 +267,8 @@ describe('GET /api/transactions', function () { }); }); - it('using limit > 100 should fail', function (done) { - var limit = 101; + it('using limit > 1000 should fail', function (done) { + var limit = 1001; var params = 'limit=' + limit; node.get('/api/transactions?' + params, function (err, res) { From 0560483f62695a0de98c94dc701967f01644a486 Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 24 Jan 2017 15:04:02 +0100 Subject: [PATCH 082/123] Convert bytea to string in getById query --- sql/transactions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/transactions.js b/sql/transactions.js index 43c7e2651c8..3a3252c9427 100644 --- a/sql/transactions.js +++ b/sql/transactions.js @@ -41,7 +41,7 @@ var TransactionsSql = { ].filter(Boolean).join(' '); }, - getById: 'SELECT * FROM trs_list WHERE "t_id" = ${id}', + getById: 'SELECT *, ENCODE ("t_senderPublicKey", \'hex\') AS "t_senderPublicKey", ENCODE ("m_recipientPublicKey", \'hex\') AS "m_recipientPublicKey" FROM trs_list WHERE "t_id" = ${id}', getVotesById: 'SELECT * FROM votes WHERE "transactionId" = ${id}' }; From f90ecd4cc88cf6d579cac3f9f95c3baf58d07cf2 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Tue, 24 Jan 2017 16:07:44 +0100 Subject: [PATCH 083/123] Removing extra new lines --- test/api/transactions.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/api/transactions.js b/test/api/transactions.js index 4d94b8baf8b..3190f9ed178 100644 --- a/test/api/transactions.js +++ b/test/api/transactions.js @@ -244,8 +244,6 @@ describe('GET /api/transactions', function () { }); }); - - describe('GET /api/transactions/get?id=', function () { it('using valid id should be ok', function (done) { From acf919fa3e26bc8e2a47cd27f06752692c4c810e Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Tue, 24 Jan 2017 16:38:17 +0100 Subject: [PATCH 084/123] Removing extra line --- test/api/multisignatures.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/api/multisignatures.js b/test/api/multisignatures.js index 97db46d2696..f533a9ca081 100644 --- a/test/api/multisignatures.js +++ b/test/api/multisignatures.js @@ -550,7 +550,6 @@ describe('POST /api/multisignatures/sign (transaction)', function () { }); }); - describe('POST /api/multisignatures/sign (regular account)', function () { var transactionId; From 85e15ca2ad435a42f8f0371928b485f12de3fc0e Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 24 Jan 2017 17:16:37 +0100 Subject: [PATCH 085/123] Reverse checks order, replaced INNER JOIN with LEFT JOIN --- modules/transactions.js | 8 ++++---- sql/migrations/20170124071600_recreateTrsListView.sql | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/transactions.js b/modules/transactions.js index 9aa034849cd..2c65b6e30f7 100644 --- a/modules/transactions.js +++ b/modules/transactions.js @@ -124,13 +124,13 @@ __private.list = function (filter, cb) { return false; } - if (!value) { - err = 'Value for parameter [' + field[1] + '] cannot be empty'; + if (!_.includes (_.keys (allowedFieldsMap), field[1])) { + err = 'Parameter is not supported: ' + field[1]; return false; } - if (!_.includes (_.keys (allowedFieldsMap), field[1])) { - err = 'Parameter is not supported: ' + field[1]; + if (!value && allowedFieldsMap[field[1]]) { + err = 'Value for parameter [' + field[1] + '] cannot be empty'; return false; } diff --git a/sql/migrations/20170124071600_recreateTrsListView.sql b/sql/migrations/20170124071600_recreateTrsListView.sql index ea79756cca0..165ca5b3810 100644 --- a/sql/migrations/20170124071600_recreateTrsListView.sql +++ b/sql/migrations/20170124071600_recreateTrsListView.sql @@ -28,6 +28,6 @@ SELECT t."id" AS "t_id", FROM trs t INNER JOIN blocks b ON t."blockId" = b."id" -INNER JOIN mem_accounts m ON t."recipientId" = m."address"; +LEFT JOIN mem_accounts m ON t."recipientId" = m."address"; COMMIT; From 59f10c60a1f97aefcbf7dd4da9cf37b27a12fb70 Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 24 Jan 2017 18:44:48 +0100 Subject: [PATCH 086/123] Improved checks and schema validation --- app.js | 5 +++++ modules/transactions.js | 5 +++-- schema/transactions.js | 8 ++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app.js b/app.js index 31fdb24f2cb..c188da3a911 100644 --- a/app.js +++ b/app.js @@ -246,6 +246,11 @@ d.run(function () { return value; } + // Ignore conditional fields for transactions list + if (/^.+?:(blockId|recipientId|senderId)$/.test(name)) { + return value; + } + /*jslint eqeq: true*/ if (isNaN(value) || parseInt(value) != value || isNaN(parseInt(value, radix))) { return value; diff --git a/modules/transactions.js b/modules/transactions.js index 2c65b6e30f7..5ca51ebeec8 100644 --- a/modules/transactions.js +++ b/modules/transactions.js @@ -129,7 +129,8 @@ __private.list = function (filter, cb) { return false; } - if (!value && allowedFieldsMap[field[1]]) { + // Checking for empty parameters, 0 is allowed for few + if (!value && !(value === 0 && _.includes (['fromTimestamp', 'minAmount', 'minConfirmations', 'type', 'offset'], field[1]))) { err = 'Value for parameter [' + field[1] + '] cannot be empty'; return false; } @@ -440,7 +441,7 @@ shared.getTransactions = function (req, cb) { _.each (req.body, function (value, key) { var param = String (key).replace (pattern, ''); // Dealing with array-like parameters (csv comma separated) - if (_.includes (['senderIds', 'recipintIds', 'senderPublicKeys', 'recipientPublicKeys'], param)) { + if (_.includes (['senderIds', 'recipientIds', 'senderPublicKeys', 'recipientPublicKeys'], param)) { value = String (value).split (','); req.body[key] = value; } diff --git a/schema/transactions.js b/schema/transactions.js index cf314f963fb..2e9d59925c5 100644 --- a/schema/transactions.js +++ b/schema/transactions.js @@ -92,11 +92,11 @@ module.exports = { }, fromHeight: { type: 'integer', - minimum: 0 + minimum: 1 }, toHeight: { type: 'integer', - minimum: 0 + minimum: 1 }, fromTimestamp: { type: 'integer', @@ -104,7 +104,7 @@ module.exports = { }, toTimestamp: { type: 'integer', - minimum: 0 + minimum: 1 }, minAmount: { type: 'integer', @@ -112,7 +112,7 @@ module.exports = { }, maxAmount: { type: 'integer', - minimum: 0 + minimum: 1 }, minConfirmations: { type: 'integer', From 019918acd5fc8ed7e08182870e3b1755eebf298e Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 24 Jan 2017 19:53:25 +0100 Subject: [PATCH 087/123] Removed circular statement --- modules/transport.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/transport.js b/modules/transport.js index 5bca6ceab02..ffedd5b950b 100644 --- a/modules/transport.js +++ b/modules/transport.js @@ -349,7 +349,6 @@ __private.banPeer = function (options) { return false; } library.logger.warn([options.code, ['Ban', options.peer.string, (options.clock / 60), 'minutes'].join(' '), options.req.method, options.req.url].join(' ')); - library.logger.trace ('Peer banned', { options: options }); modules.peers.state(options.peer.ip, options.peer.port, 0, options.clock); }; From 4bece90fd8b144f7938997e70ed702a5d42ca927 Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 24 Jan 2017 21:00:36 +0100 Subject: [PATCH 088/123] Optimized transactions/list SQL query --- sql/transactions.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sql/transactions.js b/sql/transactions.js index 3a3252c9427..435d5702d44 100644 --- a/sql/transactions.js +++ b/sql/transactions.js @@ -31,7 +31,10 @@ var TransactionsSql = { list: function (params) { return [ - 'SELECT *, ENCODE ("t_senderPublicKey", \'hex\') AS "t_senderPublicKey", ENCODE ("m_recipientPublicKey", \'hex\') AS "m_recipientPublicKey" FROM trs_list', + 'SELECT "t_id", "b_height", "t_blockId", "t_type", "t_timestamp", "t_senderId", "t_recipientId",', + '"t_amount", "t_fee", "t_signature", "t_SignSignature", "t_signatures", "confirmations",', + 'ENCODE ("t_senderPublicKey", \'hex\') AS "t_senderPublicKey", ENCODE ("m_recipientPublicKey", \'hex\') AS "m_recipientPublicKey"', + 'FROM trs_list', (params.where.length || params.owner ? 'WHERE' : ''), (params.where.length ? '(' + params.where.join(' ') + ')' : ''), // FIXME: Backward compatibility, should be removed after transitional period From ec174d4485e261cfe271592c31ac328101f9ca9e Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Fri, 13 Jan 2017 16:07:45 +0100 Subject: [PATCH 089/123] add parameter to sync with specified nodes Provide option to sync with specified nodes and choose only them as peers. closes #296 --- app.js | 27 ++++++---- config.json | 3 ++ helpers/parametersReader.js | 16 ++++++ modules/loader.js | 100 ++++++++++++++++++++++-------------- test/config.json | 3 ++ 5 files changed, 101 insertions(+), 48 deletions(-) create mode 100644 helpers/parametersReader.js diff --git a/app.js b/app.js index 31fdb24f2cb..b095836529d 100644 --- a/app.js +++ b/app.js @@ -2,6 +2,7 @@ var async = require('async'); var checkIpInList = require('./helpers/checkIpInList.js'); +var parametersReader = require('./helpers/parametersReader.js'); var extend = require('extend'); var fs = require('fs'); var genesisblock = require('./genesisBlock.json'); @@ -29,7 +30,8 @@ program .option('-c, --config ', 'config file path') .option('-p, --port ', 'listening port number') .option('-a, --address ', 'listening host name or ip') - .option('-x, --peers [peers...]', 'peers list') + .option('-x, --peers [peers...]', 'peers list to seed from') + .option('-y, --sync [peers...]', 'peers list to sync with') .option('-l, --log ', 'log level') .option('-s, --snapshot ', 'verify snapshot') .parse(process.argv); @@ -45,17 +47,22 @@ if (program.address) { } if (program.peers) { - if (typeof program.peers === 'string') { - appConfig.peers.list = program.peers.split(',').map(function (peer) { - peer = peer.split(':'); + appConfig.peers.list = parametersReader.convertToAddressList(program.peers, appConfig.port); +} + +if (program.sync) { + appConfig.syncPeers = { + list: parametersReader.convertToAddressList(program.sync, appConfig.port).map(function (syncPeer) { return { - ip: peer.shift(), - port: peer.shift() || appConfig.port + ip: syncPeer.ip, + port: syncPeer.port, + version: appConfig.version, + state: 2, + broadhash: appConfig.nethash, + height: 1 }; - }); - } else { - appConfig.peers.list = []; - } + }) + }; } if (program.log) { diff --git a/config.json b/config.json index 78b0d7b664a..c8820a737a7 100644 --- a/config.json +++ b/config.json @@ -88,6 +88,9 @@ "timeout": 5000 } }, + "syncPeers": { + "list": [] + }, "broadcasts": { "broadcastInterval": 5000, "broadcastLimit": 20, diff --git a/helpers/parametersReader.js b/helpers/parametersReader.js new file mode 100644 index 00000000000..f8f85f2d011 --- /dev/null +++ b/helpers/parametersReader.js @@ -0,0 +1,16 @@ +'use strict'; + +var parametersReader = {}; + +parametersReader.convertToAddressList = function (addresses, optPort) { + return typeof addresses !== 'string' ? [] : + addresses.split(',').map(function (address) { + address = address.split(':'); + return { + ip: address.shift(), + port: address.shift() || optPort + }; + }); +}; + +module.exports = parametersReader; diff --git a/modules/loader.js b/modules/loader.js index 3f94fea704b..8d7771ebde0 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -622,49 +622,73 @@ Loader.prototype.getNetwork = function (cb) { if (__private.network.height > 0 && Math.abs(__private.network.height - modules.blocks.getLastBlock().height) === 1) { return setImmediate(cb, null, __private.network); } - async.waterfall([ - function (waterCb) { - modules.transport.getFromRandomPeer({ - api: '/list', - method: 'GET' - }, function (err, res) { - if (err) { - return setImmediate(waterCb, err); - } else { - return setImmediate(waterCb, null, res); - } - }); - }, - function (res, waterCb) { - library.schema.validate(res.body, schema.getNetwork.peers, function (err) { - var peers = modules.peers.acceptable(res.body.peers); + return library.config.syncPeers.list.length ? setNetwork(library.config.syncPeers.list) : findDecentralizedPeers(); + + function findDecentralizedPeers () { + async.waterfall([ + function (waterCb) { + modules.transport.getFromRandomPeer({ + api: '/list', + method: 'GET' + }, function (err, res) { + if (err) { + return setImmediate(waterCb, err); + } else { + return setImmediate(waterCb, null, res); + } + }); + }, + function (res, waterCb) { + library.schema.validate(res.body, schema.getNetwork.peers, function (err) { + var peers = modules.peers.acceptable(res.body.peers); - if (err) { - return setImmediate(waterCb, err); - } else { - library.logger.log(['Received', peers.length, 'peers from'].join(' '), res.peer.string); - return setImmediate(waterCb, null, peers); - } - }); - }, - function (peers, waterCb) { - async.map(peers, __private.getPeer, function (err, peers) { - return setImmediate(waterCb, err, peers); + if (err) { + return setImmediate(waterCb, err); + } else { + library.logger.log(['Received', peers.length, 'peers from'].join(' '), res.peer.string); + return setImmediate(waterCb, null, peers); + } + }); + }, + function (peers, waterCb) { + async.map(peers, __private.getPeer, function (err, peers) { + return setImmediate(waterCb, err, peers); + }); + } + ], function (err, heights) { + if (err) { + return setImmediate(cb, err); + } + + __private.network = __private.findGoodPeers(heights); + + if (!__private.network.peers.length) { + return setImmediate(cb, 'Failed to find enough good peers'); + } else { + return setImmediate(cb, null, __private.network); + } + }); + } + + function setNetwork (requestedPeers) { + return async.map(requestedPeers, __private.getPeer, function (err, peers) { + var syncedPeers = peers.filter(function (peer) { + return peer.height; + }).map(function (syncedPeer) { + return syncedPeer.peer; }); - } - ], function (err, heights) { - if (err) { - return setImmediate(cb, err); - } - __private.network = __private.findGoodPeers(heights); + if (!syncedPeers.length) { + throw new Error('Failed to synchronize with all requested peers'); + } - if (!__private.network.peers.length) { - return setImmediate(cb, 'Failed to find enough good peers'); - } else { + __private.network = { + peers: syncedPeers, + height: Math.max(syncedPeers.map(function (p) { return p.height; })) + }; return setImmediate(cb, null, __private.network); - } - }); + }); + } }; Loader.prototype.syncing = function () { diff --git a/test/config.json b/test/config.json index bf3503796da..648c72bec68 100644 --- a/test/config.json +++ b/test/config.json @@ -47,6 +47,9 @@ "timeout": 5000 } }, + "syncPeers": { + "list": [] + }, "broadcasts": { "broadcastInterval": 5000, "broadcastLimit": 20, From 1d7c2ae6a809f52948e1073b1c3d79adcf42e4e0 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Tue, 10 Jan 2017 20:49:30 +0100 Subject: [PATCH 090/123] Prevent from node sync with himself Secure that list of peers won't be extended with node's public address and port. closes #336 --- modules/peers.js | 5 +++-- package.json | 2 +- test/api/peer.js | 2 -- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/peers.js b/modules/peers.js index 814fefacace..f804c0acb92 100644 --- a/modules/peers.js +++ b/modules/peers.js @@ -292,8 +292,9 @@ Peers.prototype.accept = function (peer) { Peers.prototype.acceptable = function (peers) { return _.chain(peers).filter(function (peer) { // Removing peers with private or host's ip address - return !ip.isPrivate(peer.ip) && - [ip.address('public'), library.config.port].join(':') !== [peer.ip, peer.port].join(':'); + return !(ip.isPrivate(peer.ip) || ip.address('public', 'ipv4', true).some(function (address) { + return [address, library.config.port].join(':') === [peer.ip, peer.port].join(':'); + })); }).uniqWith(function (a, b) { // Removing non-unique peers return (a.ip + a.port) === (b.ip + b.port); diff --git a/package.json b/package.json index 61da9ae8668..44bc13d4cfe 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "express-query-int": "=1.0.1", "express-rate-limit": "=2.5.0", "extend": "=3.0.0", - "ip": "=1.1.3", + "ip": "https://github.com/LiskHQ/node-ip/tarball/ec401f16915024450bf9b843eeb4c5366ef5235c", "json-schema": "=0.2.3", "json-sql": "LiskHQ/json-sql#27e1ed1", "lisk-sandbox": "LiskHQ/lisk-sandbox#162da38", diff --git a/test/api/peer.js b/test/api/peer.js index 9678a817313..7719fdf4b0d 100644 --- a/test/api/peer.js +++ b/test/api/peer.js @@ -39,7 +39,6 @@ describe('GET /peer/list', function () { it('using valid headers should be ok', function (done) { node.get('/peer/list') - .end(function (err, res) { node.debug('> Response:'.grey, JSON.stringify(res.body)); node.expect(res.body).to.have.property('success').to.be.ok; @@ -109,4 +108,3 @@ describe('GET /peer/height', function () { }); }); }); - From ce5a0bbf7b2d73e5a2f50de7ad51b473562ff758 Mon Sep 17 00:00:00 2001 From: 4miners Date: Thu, 26 Jan 2017 23:08:39 +0100 Subject: [PATCH 091/123] Fix coding standards --- modules/blocks.js | 2 +- modules/loader.js | 2 +- modules/transport.js | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/blocks.js b/modules/blocks.js index 590603bcb38..f170e44f451 100644 --- a/modules/blocks.js +++ b/modules/blocks.js @@ -879,7 +879,7 @@ Blocks.prototype.loadBlocksFromPeer = function (peer, cb) { } else { var id = (block ? block.id : 'null'); - library.logger.debug ('Block processing failed [blocks]', { id: id, err: err.toString (), block: block}); + library.logger.debug('Block processing failed', {id: id, err: err.toString(), module: 'blocks', block: block}); } return seriesCb(err); }, true); diff --git a/modules/loader.js b/modules/loader.js index 0002cfc29cc..d42f1575bdd 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -189,7 +189,7 @@ __private.loadTransactions = function (cb) { try { transaction = library.logic.transaction.objectNormalize(transaction); } catch (e) { - library.logger.debug ('Transaction normalization failed [loader]', {id: id, 'err': e.toString (), tx: transaction}); + library.logger.debug('Transaction normalization failed', {id: id, err: e.toString(), module: 'loader', tx: transaction}); library.logger.warn(['Transaction', id, 'is not valid, ban 10 min'].join(' '), peer.string); modules.peers.state(peer.ip, peer.port, 0, 600); diff --git a/modules/transport.js b/modules/transport.js index ffedd5b950b..24be3c4c552 100644 --- a/modules/transport.js +++ b/modules/transport.js @@ -97,11 +97,11 @@ __private.attachApi = function () { router.get('/blocks/common', function (req, res, next) { req.sanitize(req.query, schema.commonBlock, function (err, report, query) { if (err) { - library.logger.debug ('Common block request validation failed', { err: err.toString (), req: req.query }); + library.logger.debug('Common block request validation failed', {err: err.toString(), req: req.query}); return next(err); } if (!report.isValid) { - library.logger.debug ('Common block request validation failed', { err: report, req: req.query }); + library.logger.debug('Common block request validation failed', {err: report, req: req.query}); return res.json({success: false, error: report.issues}); } @@ -116,7 +116,7 @@ __private.attachApi = function () { }); if (!escapedIds.length) { - library.logger.debug ('Common block request validation failed', { err: 'ESCAPE', req: req.query }); + library.logger.debug('Common block request validation failed', {err: 'ESCAPE', req: req.query}); // Ban peer for 10 minutes __private.banPeer({peer: req.peer, code: 'ECOMMON', req: req, clock: 600}); @@ -161,7 +161,7 @@ __private.attachApi = function () { try { block = library.logic.block.objectNormalize(block); } catch (e) { - library.logger.debug ('Block normalization failed [transport]', {'err': e.toString (), block: block }); + library.logger.debug('Block normalization failed', {err: e.toString(), module: 'transport', block: block }); // Ban peer for 10 minutes __private.banPeer({peer: req.peer, code: 'EBLOCK', req: req, clock: 600}); @@ -345,7 +345,7 @@ __private.hashsum = function (obj) { __private.banPeer = function (options) { if (!options.peer || !options.peer.ip || !options.peer.port) { - library.logger.trace ('Peer ban skipped', { options: options }); + library.logger.trace('Peer ban skipped', {options: options}); return false; } library.logger.warn([options.code, ['Ban', options.peer.string, (options.clock / 60), 'minutes'].join(' '), options.req.method, options.req.url].join(' ')); @@ -443,7 +443,7 @@ __private.receiveTransaction = function (transaction, req, cb) { try { transaction = library.logic.transaction.objectNormalize(transaction); } catch (e) { - library.logger.debug ('Transaction normalization failed [transport]', {id: id, 'err': e.toString (), 'tx': transaction }); + library.logger.debug('Transaction normalization failed', {id: id, err: e.toString(), module: 'transport', tx: transaction}); // Ban peer for 10 minutes __private.banPeer({peer: req.peer, code: 'ETRANSACTION', req: req, clock: 600}); From befd54aa84c51e58ecb9c6c133925a4e3fe5d6f4 Mon Sep 17 00:00:00 2001 From: 4miners Date: Fri, 27 Jan 2017 00:03:49 +0100 Subject: [PATCH 092/123] Fix coding standards --- modules/transactions.js | 170 ++++++++++++++++++++-------------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/modules/transactions.js b/modules/transactions.js index 5ca51ebeec8..d2562969040 100644 --- a/modules/transactions.js +++ b/modules/transactions.js @@ -1,23 +1,28 @@ 'use strict'; -var _ = require('lodash'), - async = require('async'), - ByteBuffer = require('bytebuffer'), - constants = require('../helpers/constants.js'), - crypto = require('crypto'), - extend = require('extend'), - OrderBy = require('../helpers/orderBy.js'), - Router = require('../helpers/router.js'), - sandboxHelper = require('../helpers/sandbox.js'), - schema = require('../schema/transactions.js'), - slots = require('../helpers/slots.js'), - sql = require('../sql/transactions.js'), - TransactionPool = require('../logic/transactionPool.js'), - transactionTypes = require('../helpers/transactionTypes.js'), - Transfer = require('../logic/transfer.js'); +var _ = require('lodash'); +var async = require('async'); +var ByteBuffer = require('bytebuffer'); +var constants = require('../helpers/constants.js'); +var crypto = require('crypto'); +var extend = require('extend'); +var OrderBy = require('../helpers/orderBy.js'); +var Router = require('../helpers/router.js'); +var sandboxHelper = require('../helpers/sandbox.js'); +var schema = require('../schema/transactions.js'); +var slots = require('../helpers/slots.js'); +var sql = require('../sql/transactions.js'); +var TransactionPool = require('../logic/transactionPool.js'); +var transactionTypes = require('../helpers/transactionTypes.js'); +var Transfer = require('../logic/transfer.js'); // Private fields -var modules, library, self, __private = {}, shared = {}, genesisblock = null; +var __private = {}; +var shared = {}; +var genesisblock = null; +var modules; +var library; +var self; __private.assetTypes = {}; @@ -72,82 +77,77 @@ __private.attachApi = function () { }; __private.list = function (filter, cb) { - var sortFields = sql.sortFields; - var params = {}, - where = [], - err, - owner = '', - allowedFieldsMap = { - blockId: '"t_blockId" = ${blockId}', - senderPublicKey: '"t_senderPublicKey" = DECODE (${senderPublicKey}, \'hex\')', - recipientPublicKey: '"m_recipientPublicKey" = DECODE (${recipientPublicKey}, \'hex\')', - senderId: '"t_senderId" = ${senderId}', - recipientId: '"t_recipientId" = ${recipientId}', - fromHeight: '"b_height" >= ${fromHeight}', - toHeight: '"b_height" <= ${toHeight}', - fromTimestamp: '"t_timestamp" >= ${fromTimestamp}', - toTimestamp: '"t_timestamp" <= ${toTimestamp}', - senderIds: '"t_senderId" IN (${senderIds:csv})', - recipientIds: '"t_recipientId" IN (${recipientIds:csv})', - senderPublicKeys: 'ENCODE ("t_senderPublicKey", \'hex\') IN (${senderPublicKeys:csv})', - recipientPublicKeys: 'ENCODE ("m_recipientPublicKey", \'hex\') IN (${recipientPublicKeys:csv})', - minAmount: '"t_amount" >= ${minAmount}', - maxAmount: '"t_amount" <= ${minAmount}', - type: '"t_type" = ${type}', - minConfirmations: 'confirmations >= ${minConfirmations}', - limit: null, - offset: null, - orderBy: null, - // FIXME: Backward compatibility, should be removed after transitional period - ownerAddress: null, - ownerPublicKey: null - }; - - // Generate list of fields with conditions - var isFirst = true; - _.each (filter, function (value, key) { - var field = String (key).split (':'); + var params = {}; + var where = []; + var allowedFieldsMap = { + blockId: '"t_blockId" = ${blockId}', + senderPublicKey: '"t_senderPublicKey" = DECODE (${senderPublicKey}, \'hex\')', + recipientPublicKey: '"m_recipientPublicKey" = DECODE (${recipientPublicKey}, \'hex\')', + senderId: '"t_senderId" = ${senderId}', + recipientId: '"t_recipientId" = ${recipientId}', + fromHeight: '"b_height" >= ${fromHeight}', + toHeight: '"b_height" <= ${toHeight}', + fromTimestamp: '"t_timestamp" >= ${fromTimestamp}', + toTimestamp: '"t_timestamp" <= ${toTimestamp}', + senderIds: '"t_senderId" IN (${senderIds:csv})', + recipientIds: '"t_recipientId" IN (${recipientIds:csv})', + senderPublicKeys: 'ENCODE ("t_senderPublicKey", \'hex\') IN (${senderPublicKeys:csv})', + recipientPublicKeys: 'ENCODE ("m_recipientPublicKey", \'hex\') IN (${recipientPublicKeys:csv})', + minAmount: '"t_amount" >= ${minAmount}', + maxAmount: '"t_amount" <= ${minAmount}', + type: '"t_type" = ${type}', + minConfirmations: 'confirmations >= ${minConfirmations}', + limit: null, + offset: null, + orderBy: null, + // FIXME: Backward compatibility, should be removed after transitional period + ownerAddress: null, + ownerPublicKey: null + }; + var owner = ''; + var isFirstWhere = true; + + var processParams = function (value, key) { + var field = String(key).split(':'); if (field.length === 1) { // Only field identifier, so using default 'OR' condition - field.unshift ('OR'); + field.unshift('OR'); } else if (field.length === 2) { // Condition supplied, checking if correct one - if (_.includes (['or', 'and'], field[0].toLowerCase ())) { - field[0] = field[0].toUpperCase (); + if (_.includes(['or', 'and'], field[0].toLowerCase())) { + field[0] = field[0].toUpperCase(); } else { - err = 'Incorrect condition [' + field[0] + '] for field: ' + field[1]; - return false; + throw new Error('Incorrect condition [' + field[0] + '] for field: ' + field[1]); } } else { // Invalid parameter 'x:y:z' - err = 'Invalid parameter supplied: ' + key; - return false; + throw new Error('Invalid parameter supplied: ' + key); } - if (!_.includes (_.keys (allowedFieldsMap), field[1])) { - err = 'Parameter is not supported: ' + field[1]; - return false; + if (!_.includes(_.keys(allowedFieldsMap), field[1])) { + throw new Error('Parameter is not supported: ' + field[1]); } // Checking for empty parameters, 0 is allowed for few - if (!value && !(value === 0 && _.includes (['fromTimestamp', 'minAmount', 'minConfirmations', 'type', 'offset'], field[1]))) { - err = 'Value for parameter [' + field[1] + '] cannot be empty'; - return false; + if (!value && !(value === 0 && _.includes(['fromTimestamp', 'minAmount', 'minConfirmations', 'type', 'offset'], field[1]))) { + throw new Error('Value for parameter [' + field[1] + '] cannot be empty'); } if (allowedFieldsMap[field[1]]) { - if (_.includes (['fromTimestamp', 'toTimestamp'], field[1])) { - value = value - constants.epochTime.getTime () / 1000; + if (_.includes(['fromTimestamp', 'toTimestamp'], field[1])) { + value = value - constants.epochTime.getTime() / 1000; } - where.push ((!isFirst ? (field[0] + ' ') : '') + allowedFieldsMap[field[1]]); + where.push((!isFirstWhere ? (field[0] + ' ') : '') + allowedFieldsMap[field[1]]); params[field[1]] = value; - isFirst = false; + isFirstWhere = false; } - }); + }; - // Checking for errors - if (err) { - return setImmediate (cb, err); + // Generate list of fields with conditions + try { + _.each(filter, processParams); + } catch (err) { + return setImmediate(cb, err.message); } // FIXME: Backward compatibility, should be removed after transitional period @@ -434,39 +434,39 @@ Transactions.prototype.onPeersReady = function () { shared.getTransactions = function (req, cb) { async.waterfall([ function (waterCb) { - var params = {}, - pattern = /(and|or){1}:/i; + var params = {}; + var pattern = /(and|or){1}:/i; // Filter out 'and:'/'or:' from params to perform schema validation - _.each (req.body, function (value, key) { - var param = String (key).replace (pattern, ''); + _.each(req.body, function (value, key) { + var param = String(key).replace(pattern, ''); // Dealing with array-like parameters (csv comma separated) - if (_.includes (['senderIds', 'recipientIds', 'senderPublicKeys', 'recipientPublicKeys'], param)) { - value = String (value).split (','); + if (_.includes(['senderIds', 'recipientIds', 'senderPublicKeys', 'recipientPublicKeys'], param)) { + value = String(value).split(','); req.body[key] = value; } params[param] = value; }); - library.schema.validate (params, schema.getTransactions, function (err) { + library.schema.validate(params, schema.getTransactions, function (err) { if (err) { - return setImmediate (waterCb, err[0].message); + return setImmediate(waterCb, err[0].message); } else { - return setImmediate (waterCb, null); + return setImmediate(waterCb, null); } }); }, function (waterCb) { - __private.list (req.body, function (err, data) { + __private.list(req.body, function (err, data) { if (err) { - return setImmediate (waterCb, 'Failed to get transactions: ' + err); + return setImmediate(waterCb, 'Failed to get transactions: ' + err); } else { - return setImmediate (waterCb, null, {transactions: data.transactions, count: data.count}); + return setImmediate(waterCb, null, {transactions: data.transactions, count: data.count}); } }); } ], function (err, res) { - return setImmediate (cb, err, res); + return setImmediate(cb, err, res); }); }; From 3707a386e76ce2d9409e1c5ade338cb0160ca1a3 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Fri, 27 Jan 2017 11:36:16 +0100 Subject: [PATCH 093/123] Hinting file Fixing indent and comment formatting. --- modules/sql.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/sql.js b/modules/sql.js index a5bfd078716..7c853c4cc69 100644 --- a/modules/sql.js +++ b/modules/sql.js @@ -49,7 +49,7 @@ __private.escape = function (what) { }; __private.escape2 = function (str) { - return '"' + str.replace(__private.DOUBLE_QUOTES, __private.DOUBLE_QUOTES_DOUBLED) + '"'; + return '"' + str.replace(__private.DOUBLE_QUOTES, __private.DOUBLE_QUOTES_DOUBLED) + '"'; }; __private.pass = function (obj, dappid) { @@ -126,7 +126,7 @@ __private.query = function (action, config, cb) { return batchPack.length === 0; }, function (cb) { var fields = Object.keys(config.fields).map(function (field) { - return __private.escape2(config.fields[field]); // add double quotes to field identifiers + return __private.escape2(config.fields[field]); // Add double quotes to field identifiers }); sql = 'INSERT INTO ' + 'dapp_' + config.dappid + '_' + config.table + ' (' + fields.join(',') + ') '; var rows = []; From a6189c6d3ce24092a6ad14dc33cdaba0693a914e Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Fri, 27 Jan 2017 11:36:42 +0100 Subject: [PATCH 094/123] fix sinon version, syntax changes --- package.json | 2 +- test/common/globalBefore.js | 2 +- test/common/initModule.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 9828bc2eed3..81582e55192 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "jshint": "=2.9.3", "mocha": "=3.1.0", "moment": "=2.15.1", - "sinon": "^1.17.7", + "sinon": "=1.17.7", "supertest": "=2.0.0" }, "config": { diff --git a/test/common/globalBefore.js b/test/common/globalBefore.js index cbe9a798ef0..68c1bedb052 100644 --- a/test/common/globalBefore.js +++ b/test/common/globalBefore.js @@ -6,7 +6,7 @@ * @param {Object} db * @param {Function} cb */ -function clearDatabaseTable(db, logger, table, cb) { +function clearDatabaseTable (db, logger, table, cb) { db.query('DELETE FROM ' + table).then(function (result) { cb(null, result); }).catch(function (err) { diff --git a/test/common/initModule.js b/test/common/initModule.js index bfef223e4c4..61ec117d5c4 100644 --- a/test/common/initModule.js +++ b/test/common/initModule.js @@ -34,7 +34,7 @@ var modulesLoader = new function() { * @param {Function} Module * @param {Function} cb */ - this.initWithDb = function(Module, cb) { + this.initWithDb = function (Module, cb) { this.getDbConnection(function (err, db) { if (err) { return cb(err); From 4c36e6a9068bb2588b24d6ad87ab3ae49f663994 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Fri, 27 Jan 2017 12:44:01 +0100 Subject: [PATCH 095/123] Changing error message --- test/common/globalBefore.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/common/globalBefore.js b/test/common/globalBefore.js index 68c1bedb052..e11336ae5f7 100644 --- a/test/common/globalBefore.js +++ b/test/common/globalBefore.js @@ -10,11 +10,11 @@ function clearDatabaseTable (db, logger, table, cb) { db.query('DELETE FROM ' + table).then(function (result) { cb(null, result); }).catch(function (err) { - logger.err('unable to delete data from table: ' + table); + logger.err('Failed to clear database table: ' + table); throw err; }); } module.exports = { clearDatabaseTable: clearDatabaseTable -}; \ No newline at end of file +}; From d56acf92fa7441c52cc4064c7b0c6becd2ef9e79 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Mon, 30 Jan 2017 18:21:21 +0100 Subject: [PATCH 096/123] pararellize Travis jobs Create a Travis jobs matrix consisting of seperate tests. closes #407 --- .travis.yml | 42 ++++++++++++++++++++++-- Gruntfile.js | 26 +++++++-------- package.json | 20 ++--------- test/api/accounts.js | 4 +++ test/api/blocks.js | 4 +++ test/api/dapps.js | 4 +++ test/api/delegates.js | 4 +++ test/api/loader.js | 4 +++ test/api/multisignatures.js | 4 +++ test/api/peer.blocks.js | 4 +++ test/api/peer.dapp.js | 4 +++ test/api/peer.js | 4 +++ test/api/peer.signatures.js | 4 +++ test/api/peer.transactions.collision.js | 6 +++- test/api/peer.transactions.delegates.js | 4 +++ test/api/peer.transactions.main.js | 4 +++ test/api/peer.transactions.signatures.js | 4 +++ test/api/peer.transactions.stress.js | 4 +++ test/api/peer.transactions.votes.js | 4 +++ test/api/peers.js | 4 +++ test/api/signatures.js | 4 +++ test/api/transactions.js | 4 +++ test/common/globalBefore.js | 34 ++++++++++++++++++- test/mocha.opts | 6 +++- 24 files changed, 169 insertions(+), 37 deletions(-) diff --git a/.travis.yml b/.travis.yml index c97a25cab25..5d325b40989 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,52 @@ dist: 'trusty' language: node_js node_js: - - '0.12.17' + - '6.9.4' +cache: + yarn: true + directories: + - test/lisk-js services: - postgresql addons: postgresql: '9.6' +install: + - curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - + - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list + - sudo apt-get update && sudo apt-get install yarn + - yarn before_script: - createdb lisk_test - psql -d lisk_test -c "alter user "$USER" with password 'password';" - wget https://downloads.lisk.io/lisk-node/lisk-node-Linux-x86_64.tar.gz - tar -zxvf lisk-node-Linux-x86_64.tar.gz - - cd test/lisk-js/; npm install; cd ../.. + - cd test/lisk-js/; yarn; cd ../.. - cp test/config.json test/genesisBlock.json . - - node app.js &> /dev/null & + - node app.js &> .app.log & +env: + matrix: + - TEST=test/api/peer.transactions.stress.js + - TEST=test/api/peer.transactions.votes.js + - TEST=test/api/delegates.js + - TEST=test/api/accounts.js + - TEST=test/api/blocks.js + - TEST=test/api/dapps.js + - TEST=test/api/loader.js + - TEST=test/api/multisignatures.js + - TEST=test/api/peer.js + - TEST=test/api/peer.dapp.js + - TEST=test/api/peer.blocks.js + - TEST=test/api/peer.signatures.js + - TEST=test/api/peer.transactions.collision.js + - TEST=test/api/peer.transactions.delegates.js + - TEST=test/api/peer.transactions.main.js + - TEST=test/api/peer.transactions.signatures.js + - TEST=test/api/peers.js + - TEST=test/api/signatures.js + - TEST=test/api/transactions.js + + - TEST=test/unit/helpers + - TEST=test/unit/logic +script: 'npm run travis' +after_failure: + - cat .app.log diff --git a/Gruntfile.js b/Gruntfile.js index 4127414686e..47d935b0abb 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -21,6 +21,8 @@ module.exports = function (grunt) { var release_dir = __dirname + '/release/', version_dir = release_dir + config.version; + var maxBufferSize = require('buffer').kMaxLength - 1; + grunt.initConfig({ obfuscator: { files: files, @@ -68,6 +70,14 @@ module.exports = function (grunt) { }, build: { command: 'cd ' + version_dir + '/ && touch build && echo "v' + today + '" > build' + }, + coverage: { + command: 'node_modules/.bin/istanbul cover --dir test/.coverage ./node_modules/.bin/mocha', + maxBuffer: maxBufferSize + }, + coverageSingle: { + command: 'node_modules/.bin/istanbul cover --dir test/.$TEST_coverage ./node_modules/.bin/mocha $TEST', + maxBuffer: maxBufferSize } }, @@ -113,19 +123,6 @@ module.exports = function (grunt) { 'test/api/**/*.js', 'test/unit/**/*.js' ] - }, - - mochaTest: { - test: { - options: { - reporter: 'spec', - quiet: false, - clearRequireCache: false, - noFail: false, - timeout: '250s' - }, - src: ['test'] - } } }); @@ -140,5 +137,6 @@ module.exports = function (grunt) { grunt.registerTask('default', ['release']); grunt.registerTask('release', ['exec:folder', 'obfuscator', 'exec:package', 'exec:build', 'compress']); - grunt.registerTask('travis', ['jshint', 'mochaTest']); + grunt.registerTask('travis', ['jshint', 'exec:coverageSingle']); + grunt.registerTask('test', ['jshint', 'exec:coverage']); }; diff --git a/package.json b/package.json index 3660a6312c5..08ddd517bc2 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "private": true, "scripts": { "start": "node app.js", - "test": "./node_modules/.bin/grunt travis --verbose", - "cov": "./node_modules/.bin/mocha --require blanket -R html-cov test/helpers test/logic > tmp/coverage.html" + "test": "./node_modules/.bin/grunt test --verbose", + "travis": "./node_modules/.bin/grunt travis --verbose" }, "author": "Boris Povod , Pavel Nekrasov , Oliver Beddows ", "dependencies": { @@ -48,7 +48,6 @@ }, "devDependencies": { "bitcore-mnemonic": "=1.1.1", - "blanket": "=1.2.3", "browserify-bignum": "=1.3.0-2", "buffer": "=4.9.1", "chai": "=3.5.0", @@ -62,26 +61,13 @@ "grunt-contrib-jshint": "=1.0.0", "grunt-exec": "=1.0.1", "grunt-jsdox": "=0.1.7", - "grunt-mocha-test": "=0.13.2", "grunt-obfuscator": "=0.1.0", + "istanbul": "=0.4.5", "jsdox": "=0.4.10", "jshint": "=2.9.3", "mocha": "=3.1.0", "moment": "=2.15.1", "sinon": "=1.17.7", "supertest": "=2.0.0" - }, - "config": { - "blanket": { - "pattern": [ - "helpers", - "logic" - ], - "data-cover-never": [ - "node_modules", - "test", - "tmp" - ] - } } } diff --git a/test/api/accounts.js b/test/api/accounts.js index 494c3477aa6..b1fcf14a715 100644 --- a/test/api/accounts.js +++ b/test/api/accounts.js @@ -4,6 +4,10 @@ var node = require('./../node.js'); var account = node.randomAccount(); +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('POST /api/accounts/open', function () { function openAccount (params, done) { diff --git a/test/api/blocks.js b/test/api/blocks.js index 881c47b9f26..4e679bbf255 100644 --- a/test/api/blocks.js +++ b/test/api/blocks.js @@ -12,6 +12,10 @@ var block = { var testBlocksUnder101 = false; +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('GET /api/blocks/getBroadhash', function () { it('should be ok', function (done) { diff --git a/test/api/dapps.js b/test/api/dapps.js index ddabecd91ff..5729c88ad9a 100644 --- a/test/api/dapps.js +++ b/test/api/dapps.js @@ -28,6 +28,10 @@ function putTransaction (params, done) { }); } +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + before(function (done) { // Send to LISK to account 1 address setTimeout(function () { diff --git a/test/api/delegates.js b/test/api/delegates.js index 0cb6a23c5f7..91779d91d92 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -26,6 +26,10 @@ function putDelegates (params, done) { }); } +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('PUT /api/accounts/delegates without funds', function () { var account; diff --git a/test/api/loader.js b/test/api/loader.js index 66abf143dbb..d645146dde3 100644 --- a/test/api/loader.js +++ b/test/api/loader.js @@ -2,6 +2,10 @@ var node = require('./../node.js'); +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('GET /api/loader/status/ping', function () { it('should be ok', function (done) { diff --git a/test/api/multisignatures.js b/test/api/multisignatures.js index f1906c56332..b6322c5cf51 100644 --- a/test/api/multisignatures.js +++ b/test/api/multisignatures.js @@ -97,6 +97,10 @@ before(function (done) { }); }); +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + before(function (done) { sendLISK(multisigAccount, null, done); }); diff --git a/test/api/peer.blocks.js b/test/api/peer.blocks.js index 5e6ed9c1796..ecb4a18afb0 100644 --- a/test/api/peer.blocks.js +++ b/test/api/peer.blocks.js @@ -4,6 +4,10 @@ var node = require('./../node.js'); var genesisblock = require('../../genesisBlock.json'); +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('GET /peer/blocks', function () { it('using valid headers should be ok', function (done) { diff --git a/test/api/peer.dapp.js b/test/api/peer.dapp.js index e2078a07d47..0ab40a302fc 100644 --- a/test/api/peer.dapp.js +++ b/test/api/peer.dapp.js @@ -2,6 +2,10 @@ var node = require('./../node.js'); +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('POST /peer/dapp/message', function () { }); diff --git a/test/api/peer.js b/test/api/peer.js index 7719fdf4b0d..0e9f6c6234b 100644 --- a/test/api/peer.js +++ b/test/api/peer.js @@ -3,6 +3,10 @@ var node = require('./../node.js'); var ip = require('ip'); +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('GET /peer/list', function () { before(function (done) { diff --git a/test/api/peer.signatures.js b/test/api/peer.signatures.js index 7e1b72c05e4..1e05d245a37 100644 --- a/test/api/peer.signatures.js +++ b/test/api/peer.signatures.js @@ -27,6 +27,10 @@ function postSignature (transaction, signature, done) { }, done); } +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('GET /peer/signatures', function () { it('using incorrect nethash in headers should fail', function (done) { diff --git a/test/api/peer.transactions.collision.js b/test/api/peer.transactions.collision.js index 77591376d68..b5fad66a4af 100644 --- a/test/api/peer.transactions.collision.js +++ b/test/api/peer.transactions.collision.js @@ -9,6 +9,10 @@ function postTransaction (transaction, done) { }, done); } +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('POST /peer/transactions', function () { describe('when two passphrases collide into the same address', function () { @@ -38,7 +42,7 @@ describe('POST /peer/transactions', function () { postTransaction(transaction, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('message').to.equal('Invalid sender public key: b26dd40ba33e4785e49ddc4f106c0493ed00695817235c778f487aea5866400a expected: ce33db918b059a6e99c402963b42cf51c695068007ef01d8c383bb8a41270263'); + node.expect(res.body).to.have.property('message').to.equal('Failed to verify signature'); done(); }); }); diff --git a/test/api/peer.transactions.delegates.js b/test/api/peer.transactions.delegates.js index 917d3cd9692..e8b6fc0e1dd 100644 --- a/test/api/peer.transactions.delegates.js +++ b/test/api/peer.transactions.delegates.js @@ -25,6 +25,10 @@ function sendLISK (params, done) { }); } +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('POST /peer/transactions', function () { describe('registering a delegate', function () { diff --git a/test/api/peer.transactions.main.js b/test/api/peer.transactions.main.js index 8eb8b98067c..105ab1ffe92 100644 --- a/test/api/peer.transactions.main.js +++ b/test/api/peer.transactions.main.js @@ -15,6 +15,10 @@ function getAddress (address, done) { node.get('/api/accounts?address=' + address, done); } +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('GET /peer/transactions', function () { it('using incorrect nethash in headers should fail', function (done) { diff --git a/test/api/peer.transactions.signatures.js b/test/api/peer.transactions.signatures.js index fb19c4d8c46..e7628ea72a5 100644 --- a/test/api/peer.transactions.signatures.js +++ b/test/api/peer.transactions.signatures.js @@ -26,6 +26,10 @@ function sendLISK (params, done) { }); } +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('POST /peer/transactions', function () { describe('enabling second signature', function () { diff --git a/test/api/peer.transactions.stress.js b/test/api/peer.transactions.stress.js index 22837df2eda..ee38827f063 100644 --- a/test/api/peer.transactions.stress.js +++ b/test/api/peer.transactions.stress.js @@ -14,6 +14,10 @@ function postTransactions (transactions, done) { }, done); } +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('POST /peer/transactions', function () { describe('sending 1000 bundled transfers to random addresses', function () { diff --git a/test/api/peer.transactions.votes.js b/test/api/peer.transactions.votes.js index a2898b60ed4..5a0829f11b1 100644 --- a/test/api/peer.transactions.votes.js +++ b/test/api/peer.transactions.votes.js @@ -81,6 +81,10 @@ function registerDelegate (account, done) { }); } +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('POST /peer/transactions', function () { before(function (done) { diff --git a/test/api/peers.js b/test/api/peers.js index df7d5d65898..38b56c9177a 100644 --- a/test/api/peers.js +++ b/test/api/peers.js @@ -3,6 +3,10 @@ var node = require('./../node.js'); var peersSortFields = require('../../sql/peers').sortFields; +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + describe('GET /api/peers/version', function () { it('should be ok', function (done) { diff --git a/test/api/signatures.js b/test/api/signatures.js index 287d273603e..204d281ea9d 100644 --- a/test/api/signatures.js +++ b/test/api/signatures.js @@ -32,6 +32,10 @@ function sendLISK (account, done) { }); } +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + before(function (done) { setTimeout(function () { sendLISK(account, done); diff --git a/test/api/transactions.js b/test/api/transactions.js index c4a11002f19..e9484196d95 100644 --- a/test/api/transactions.js +++ b/test/api/transactions.js @@ -44,6 +44,10 @@ function sendLISK (account, done) { }); } +before(function (done) { + require('./../common/globalBefore').waitUntilBlockchainReady(done); +}); + before(function (done) { setTimeout(function () { sendLISK(account, done); diff --git a/test/common/globalBefore.js b/test/common/globalBefore.js index e11336ae5f7..706a6281c09 100644 --- a/test/common/globalBefore.js +++ b/test/common/globalBefore.js @@ -1,5 +1,7 @@ 'use strict'; +var node = require('./../node.js'); + /** * @param {string} table * @param {Logger} logger @@ -15,6 +17,36 @@ function clearDatabaseTable (db, logger, table, cb) { }); } +/** + * @param {Function} cb + * @param {Number} [retries=10] retries + * @param {Number} [timeout=200] timeout + */ +function waitUntilBlockchainReady (cb, retries, timeout) { + if (!retries) { + retries = 10; + } + if (!timeout) { + timeout = 200; + } + (function fetchBlockchainStatus () { + node.get('/api/loader/status', function (err, res) { + node.expect(err).to.not.exist; + retries -= 1; + if (!res.body.success && res.body.error === 'Blockchain is loading' && retries >= 0) { + return setTimeout(function () { + fetchBlockchainStatus(); + }, timeout); + } + else if (res.body.success && res.body.loaded) { + return cb(); + } + return cb('Failed to load blockchain'); + }); + })(); +} + module.exports = { - clearDatabaseTable: clearDatabaseTable + clearDatabaseTable: clearDatabaseTable, + waitUntilBlockchainReady: waitUntilBlockchainReady }; diff --git a/test/mocha.opts b/test/mocha.opts index 05ea6d8ec12..4aa4037ca41 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1 +1,5 @@ ---timeout 250s +--timeout 500s +--reporter spec +--quiet false +--clearRequireCache false +--noFail false \ No newline at end of file From 9f6ea0d1d4d5d911e370b54b33c82d56f7866f6a Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Wed, 1 Feb 2017 16:16:19 +0100 Subject: [PATCH 097/123] move waiting for ready blockchain hook to node.js --- test/api/accounts.js | 4 ---- test/api/blocks.js | 4 ---- test/api/dapps.js | 4 ---- test/api/delegates.js | 4 ---- test/api/loader.js | 4 ---- test/api/multisignatures.js | 4 ---- test/api/peer.blocks.js | 4 ---- test/api/peer.dapp.js | 4 ---- test/api/peer.js | 4 ---- test/api/peer.signatures.js | 4 ---- test/api/peer.transactions.collision.js | 4 ---- test/api/peer.transactions.delegates.js | 4 ---- test/api/peer.transactions.main.js | 4 ---- test/api/peer.transactions.signatures.js | 4 ---- test/api/peer.transactions.stress.js | 4 ---- test/api/peer.transactions.votes.js | 4 ---- test/api/peers.js | 4 ---- test/api/signatures.js | 4 ---- test/api/transactions.js | 4 ---- test/node.js | 6 +++++- 20 files changed, 5 insertions(+), 77 deletions(-) diff --git a/test/api/accounts.js b/test/api/accounts.js index b1fcf14a715..494c3477aa6 100644 --- a/test/api/accounts.js +++ b/test/api/accounts.js @@ -4,10 +4,6 @@ var node = require('./../node.js'); var account = node.randomAccount(); -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('POST /api/accounts/open', function () { function openAccount (params, done) { diff --git a/test/api/blocks.js b/test/api/blocks.js index 4e679bbf255..881c47b9f26 100644 --- a/test/api/blocks.js +++ b/test/api/blocks.js @@ -12,10 +12,6 @@ var block = { var testBlocksUnder101 = false; -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('GET /api/blocks/getBroadhash', function () { it('should be ok', function (done) { diff --git a/test/api/dapps.js b/test/api/dapps.js index 5729c88ad9a..ddabecd91ff 100644 --- a/test/api/dapps.js +++ b/test/api/dapps.js @@ -28,10 +28,6 @@ function putTransaction (params, done) { }); } -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - before(function (done) { // Send to LISK to account 1 address setTimeout(function () { diff --git a/test/api/delegates.js b/test/api/delegates.js index 91779d91d92..0cb6a23c5f7 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -26,10 +26,6 @@ function putDelegates (params, done) { }); } -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('PUT /api/accounts/delegates without funds', function () { var account; diff --git a/test/api/loader.js b/test/api/loader.js index d645146dde3..66abf143dbb 100644 --- a/test/api/loader.js +++ b/test/api/loader.js @@ -2,10 +2,6 @@ var node = require('./../node.js'); -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('GET /api/loader/status/ping', function () { it('should be ok', function (done) { diff --git a/test/api/multisignatures.js b/test/api/multisignatures.js index b6322c5cf51..f1906c56332 100644 --- a/test/api/multisignatures.js +++ b/test/api/multisignatures.js @@ -97,10 +97,6 @@ before(function (done) { }); }); -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - before(function (done) { sendLISK(multisigAccount, null, done); }); diff --git a/test/api/peer.blocks.js b/test/api/peer.blocks.js index ecb4a18afb0..5e6ed9c1796 100644 --- a/test/api/peer.blocks.js +++ b/test/api/peer.blocks.js @@ -4,10 +4,6 @@ var node = require('./../node.js'); var genesisblock = require('../../genesisBlock.json'); -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('GET /peer/blocks', function () { it('using valid headers should be ok', function (done) { diff --git a/test/api/peer.dapp.js b/test/api/peer.dapp.js index 0ab40a302fc..e2078a07d47 100644 --- a/test/api/peer.dapp.js +++ b/test/api/peer.dapp.js @@ -2,10 +2,6 @@ var node = require('./../node.js'); -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('POST /peer/dapp/message', function () { }); diff --git a/test/api/peer.js b/test/api/peer.js index 0e9f6c6234b..7719fdf4b0d 100644 --- a/test/api/peer.js +++ b/test/api/peer.js @@ -3,10 +3,6 @@ var node = require('./../node.js'); var ip = require('ip'); -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('GET /peer/list', function () { before(function (done) { diff --git a/test/api/peer.signatures.js b/test/api/peer.signatures.js index 1e05d245a37..7e1b72c05e4 100644 --- a/test/api/peer.signatures.js +++ b/test/api/peer.signatures.js @@ -27,10 +27,6 @@ function postSignature (transaction, signature, done) { }, done); } -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('GET /peer/signatures', function () { it('using incorrect nethash in headers should fail', function (done) { diff --git a/test/api/peer.transactions.collision.js b/test/api/peer.transactions.collision.js index b5fad66a4af..6bd3d1e8d62 100644 --- a/test/api/peer.transactions.collision.js +++ b/test/api/peer.transactions.collision.js @@ -9,10 +9,6 @@ function postTransaction (transaction, done) { }, done); } -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('POST /peer/transactions', function () { describe('when two passphrases collide into the same address', function () { diff --git a/test/api/peer.transactions.delegates.js b/test/api/peer.transactions.delegates.js index e8b6fc0e1dd..917d3cd9692 100644 --- a/test/api/peer.transactions.delegates.js +++ b/test/api/peer.transactions.delegates.js @@ -25,10 +25,6 @@ function sendLISK (params, done) { }); } -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('POST /peer/transactions', function () { describe('registering a delegate', function () { diff --git a/test/api/peer.transactions.main.js b/test/api/peer.transactions.main.js index 105ab1ffe92..8eb8b98067c 100644 --- a/test/api/peer.transactions.main.js +++ b/test/api/peer.transactions.main.js @@ -15,10 +15,6 @@ function getAddress (address, done) { node.get('/api/accounts?address=' + address, done); } -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('GET /peer/transactions', function () { it('using incorrect nethash in headers should fail', function (done) { diff --git a/test/api/peer.transactions.signatures.js b/test/api/peer.transactions.signatures.js index e7628ea72a5..fb19c4d8c46 100644 --- a/test/api/peer.transactions.signatures.js +++ b/test/api/peer.transactions.signatures.js @@ -26,10 +26,6 @@ function sendLISK (params, done) { }); } -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('POST /peer/transactions', function () { describe('enabling second signature', function () { diff --git a/test/api/peer.transactions.stress.js b/test/api/peer.transactions.stress.js index ee38827f063..22837df2eda 100644 --- a/test/api/peer.transactions.stress.js +++ b/test/api/peer.transactions.stress.js @@ -14,10 +14,6 @@ function postTransactions (transactions, done) { }, done); } -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('POST /peer/transactions', function () { describe('sending 1000 bundled transfers to random addresses', function () { diff --git a/test/api/peer.transactions.votes.js b/test/api/peer.transactions.votes.js index 5a0829f11b1..a2898b60ed4 100644 --- a/test/api/peer.transactions.votes.js +++ b/test/api/peer.transactions.votes.js @@ -81,10 +81,6 @@ function registerDelegate (account, done) { }); } -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('POST /peer/transactions', function () { before(function (done) { diff --git a/test/api/peers.js b/test/api/peers.js index 38b56c9177a..df7d5d65898 100644 --- a/test/api/peers.js +++ b/test/api/peers.js @@ -3,10 +3,6 @@ var node = require('./../node.js'); var peersSortFields = require('../../sql/peers').sortFields; -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - describe('GET /api/peers/version', function () { it('should be ok', function (done) { diff --git a/test/api/signatures.js b/test/api/signatures.js index 204d281ea9d..287d273603e 100644 --- a/test/api/signatures.js +++ b/test/api/signatures.js @@ -32,10 +32,6 @@ function sendLISK (account, done) { }); } -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - before(function (done) { setTimeout(function () { sendLISK(account, done); diff --git a/test/api/transactions.js b/test/api/transactions.js index e9484196d95..c4a11002f19 100644 --- a/test/api/transactions.js +++ b/test/api/transactions.js @@ -44,10 +44,6 @@ function sendLISK (account, done) { }); } -before(function (done) { - require('./../common/globalBefore').waitUntilBlockchainReady(done); -}); - before(function (done) { setTimeout(function () { sendLISK(account, done); diff --git a/test/node.js b/test/node.js index 3ef081003b1..f0a8d135e68 100644 --- a/test/node.js +++ b/test/node.js @@ -1,4 +1,4 @@ -'use strict'; +'use strict'; /*jslint mocha:true, expr:true */ // Root object var node = {}; @@ -361,5 +361,9 @@ node.put = function (path, params, done) { return abstractRequest({ verb: 'PUT', path: path, params: params }, done); }; +before(function (done) { + require('./common/globalBefore').waitUntilBlockchainReady(done); +}); + // Exports module.exports = node; From ae98a3db360a0cdbdb95eb63a7b7cc7eabccd601 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Thu, 2 Feb 2017 16:49:19 +0100 Subject: [PATCH 098/123] fix paths on builded bundles --- helpers/database.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/database.js b/helpers/database.js index 5bdcd047a04..e7b5eb8ceaf 100644 --- a/helpers/database.js +++ b/helpers/database.js @@ -4,7 +4,6 @@ var async = require('async'); var bignum = require('./bignum'); var fs = require('fs'); var path = require('path'); -var dirname = path.join(__dirname, '..'); // var isWin = /^win/.test(process.platform); // var isMac = /^darwin/.test(process.platform); @@ -103,6 +102,7 @@ function Migrator (pgp, db) { }; this.applyRuntimeQueryFile = function (waterCb) { + var dirname = require('path').basename(__dirname) === 'helpers' ? path.join(__dirname, '..') : __dirname; var sql = new pgp.QueryFile(path.join(dirname, 'sql', 'runtime.sql'), {minify: true}); db.query(sql).then(function () { From e72a901b04eadd301b67dd3fc198f62a12fc9655 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Fri, 3 Feb 2017 11:41:39 +0100 Subject: [PATCH 099/123] Removing unnessasary require --- helpers/database.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/database.js b/helpers/database.js index e7b5eb8ceaf..ec7726738d5 100644 --- a/helpers/database.js +++ b/helpers/database.js @@ -102,7 +102,7 @@ function Migrator (pgp, db) { }; this.applyRuntimeQueryFile = function (waterCb) { - var dirname = require('path').basename(__dirname) === 'helpers' ? path.join(__dirname, '..') : __dirname; + var dirname = path.basename(__dirname) === 'helpers' ? path.join(__dirname, '..') : __dirname; var sql = new pgp.QueryFile(path.join(dirname, 'sql', 'runtime.sql'), {minify: true}); db.query(sql).then(function () { From 8096a3f2d83930d502e1fb1f5fd0d5309127b6ae Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Fri, 3 Feb 2017 12:21:50 +0100 Subject: [PATCH 100/123] fix gruntfile deleted dependencies --- Gruntfile.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 47d935b0abb..e504babab1e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -133,7 +133,6 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-exec'); grunt.loadNpmTasks('grunt-contrib-compress'); grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-mocha-test'); grunt.registerTask('default', ['release']); grunt.registerTask('release', ['exec:folder', 'obfuscator', 'exec:package', 'exec:build', 'compress']); From 7fe37c3c8c0ace9cc2255a77ac53cfa76d696b49 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Fri, 3 Feb 2017 15:34:56 +0100 Subject: [PATCH 101/123] not crash node after none seed peers is found --- modules/loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/loader.js b/modules/loader.js index d72526bf6eb..81fbb14a046 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -678,7 +678,7 @@ Loader.prototype.getNetwork = function (cb) { }); if (!syncedPeers.length) { - throw new Error('Failed to synchronize with all requested peers'); + return setImmediate(cb, 'Failed to synchronize with requested peers'); } __private.network = { From 1ddf47a6309a346b35c285e537edd950d7f2266c Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Fri, 3 Feb 2017 16:00:31 +0100 Subject: [PATCH 102/123] increase the level of syncTimer to from log to error --- modules/loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/loader.js b/modules/loader.js index 81fbb14a046..b50a39379c0 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -91,7 +91,7 @@ __private.syncTimer = function () { async.retry(__private.retries, __private.sync, cb); }, function (err) { if (err) { - library.logger.log('Sync timer', err); + library.logger.error('Sync timer', err); __private.initalize(); } From bd29b90f84e7caf6b0daae79302dad69c4a5fdf3 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Fri, 3 Feb 2017 16:33:53 +0100 Subject: [PATCH 103/123] Updating lisk-ui submodule --- public | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public b/public index 3c340a808f4..e10ffb7d01b 160000 --- a/public +++ b/public @@ -1 +1 @@ -Subproject commit 3c340a808f4900c70a8d3368f3d2520dddefb7de +Subproject commit e10ffb7d01bcf6cb5a916866c70763a97f60ec95 From a674031d57673c0fa4959abff5a7ab7521eff229 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Mon, 6 Feb 2017 15:22:45 +0100 Subject: [PATCH 104/123] Changing minVersion operator from ~ to >= --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index c8820a737a7..dac8d156561 100644 --- a/config.json +++ b/config.json @@ -2,7 +2,7 @@ "port": 8000, "address": "0.0.0.0", "version": "0.5.0", - "minVersion": "~0.5.0", + "minVersion": ">=0.5.0", "fileLogLevel": "info", "logFileName": "logs/lisk.log", "consoleLogLevel": "info", From 64907c785632a5908d45fd48e873607eed342355 Mon Sep 17 00:00:00 2001 From: 4miners Date: Sun, 5 Feb 2017 05:58:33 +0100 Subject: [PATCH 105/123] Retrieve git commit info only once on app start --- app.js | 33 ++++++++++++++++++++++++++++++++- helpers/git.js | 24 +++++++++++++++++------- modules/peers.js | 19 +++++++++++++++++-- 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/app.js b/app.js index 6b6f7cbb718..f64a1321119 100644 --- a/app.js +++ b/app.js @@ -2,13 +2,14 @@ var async = require('async'); var checkIpInList = require('./helpers/checkIpInList.js'); -var parametersReader = require('./helpers/parametersReader.js'); var extend = require('extend'); var fs = require('fs'); var genesisblock = require('./genesisBlock.json'); +var git = require('./helpers/git.js'); var https = require('https'); var Logger = require('./logger.js'); var packageJson = require('./package.json'); +var parametersReader = require('./helpers/parametersReader.js'); var path = require('path'); var program = require('commander'); var Sequence = require('./helpers/sequence.js'); @@ -18,6 +19,15 @@ var z_schema = require('./helpers/z_schema.js'); process.stdin.resume(); var versionBuild = fs.readFileSync(path.join(__dirname, 'build'), 'utf8'); +/** + * Hash of last git commit + * + * @private + * @property lastCommit + * @type {String} + * @default '' + */ +var lastCommit = ''; if (typeof gc !== 'undefined') { setInterval(function () { @@ -101,6 +111,13 @@ var config = { var logger = new Logger({ echo: appConfig.consoleLogLevel, errorLevel: appConfig.fileLogLevel, filename: appConfig.logFileName }); +// Trying to get last git commit +try { + lastCommit = git.getLastCommit(); +} catch (err) { + logger.debug('Cannot get last git commit', err.message); +} + var d = require('domain').create(); d.on('error', function (err) { @@ -147,6 +164,20 @@ d.run(function () { build: function (cb) { cb(null, versionBuild); }, + /** + * Returns hash of last git commit + * + * @property lastCommit + * @type {Function} + * @async + * @param {Function} cb Callback function + * @return {Function} cb Callback function from params + * @return {Object} cb[0] Always return `null` here + * @return {String} cb[1] Hash of last git commit + */ + lastCommit: function (cb) { + cb(null, lastCommit); + }, genesisblock: function (cb) { cb(null, { diff --git a/helpers/git.js b/helpers/git.js index 1f79e5fc521..37048ad4469 100644 --- a/helpers/git.js +++ b/helpers/git.js @@ -1,13 +1,23 @@ 'use strict'; var childProcess = require('child_process'); -function getLastCommit() { - try { - return childProcess.execSync('git rev-parse HEAD').toString().trim(); - } - catch (err) { - return ''; - } +/** + * Return hash of last git commit if available + * + * @method getLastCommit + * @public + * @return {String} Hash of last git commit + * @throws {Error} Throws error if cannot get last git commit + */ +function getLastCommit () { + var spawn = childProcess.spawnSync('git', ['rev-parse', 'HEAD']); + var err = spawn.stderr.toString().trim(); + + if (err) { + throw new Error(err); + } else { + return spawn.stdout.toString().trim(); + } } module.exports = { diff --git a/modules/peers.js b/modules/peers.js index f804c0acb92..e8f9ad88a6d 100644 --- a/modules/peers.js +++ b/modules/peers.js @@ -546,10 +546,25 @@ shared.getPeer = function (req, cb) { }); }; +/** + * Returns information about version + * + * @public + * @async + * @method version + * @param {Object} req HTTP request object + * @param {Function} cb Callback function + * @return {Function} cb Callback function from params (through setImmediate) + * @return {Object} cb[0] Always return `null` here + * @return {Object} cb[1] Anonymous object with version info + * @return {String} cb[1].build Build information (if available, otherwise '') + * @return {String} cb[1].commit Hash of last git commit (if available, otherwise '') + * @return {String} cb[1].version Lisk version from config file + */ shared.version = function (req, cb) { return setImmediate(cb, null, { - build: library.build, - commit: git.getLastCommit(), + build: library.build, + commit: library.lastCommit, version: library.config.version }); }; From 8e65d8d4613147fd5927cbc3f3ff65598108c5b7 Mon Sep 17 00:00:00 2001 From: 4miners Date: Sun, 5 Feb 2017 17:15:37 +0100 Subject: [PATCH 106/123] Improved documentation --- app.js | 4 ++-- helpers/git.js | 6 ++++++ modules/peers.js | 10 +++++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app.js b/app.js index f64a1321119..05513ac8ee1 100644 --- a/app.js +++ b/app.js @@ -172,8 +172,8 @@ d.run(function () { * @async * @param {Function} cb Callback function * @return {Function} cb Callback function from params - * @return {Object} cb[0] Always return `null` here - * @return {String} cb[1] Hash of last git commit + * @return {Object} cb.err Always return `null` here + * @return {String} cb.lastCommit Hash of last git commit */ lastCommit: function (cb) { cb(null, lastCommit); diff --git a/helpers/git.js b/helpers/git.js index 37048ad4469..68beecae576 100644 --- a/helpers/git.js +++ b/helpers/git.js @@ -1,4 +1,10 @@ 'use strict'; +/** +* Helper module for parsing git commit information +* +* @class git.js +*/ + var childProcess = require('child_process'); /** diff --git a/modules/peers.js b/modules/peers.js index e8f9ad88a6d..f9fed9b9af7 100644 --- a/modules/peers.js +++ b/modules/peers.js @@ -555,11 +555,11 @@ shared.getPeer = function (req, cb) { * @param {Object} req HTTP request object * @param {Function} cb Callback function * @return {Function} cb Callback function from params (through setImmediate) - * @return {Object} cb[0] Always return `null` here - * @return {Object} cb[1] Anonymous object with version info - * @return {String} cb[1].build Build information (if available, otherwise '') - * @return {String} cb[1].commit Hash of last git commit (if available, otherwise '') - * @return {String} cb[1].version Lisk version from config file + * @return {Object} cb.err Always return `null` here + * @return {Object} cb.obj Anonymous object with version info + * @return {String} cb.obj.build Build information (if available, otherwise '') + * @return {String} cb.obj.commit Hash of last git commit (if available, otherwise '') + * @return {String} cb.obj.version Lisk version from config file */ shared.version = function (req, cb) { return setImmediate(cb, null, { From 2bd2415b9ef193e3778e5a67d7012f635ec68ed9 Mon Sep 17 00:00:00 2001 From: 4miners Date: Mon, 6 Feb 2017 08:31:18 +0100 Subject: [PATCH 107/123] Fix unhandled promise rejection --- modules/transport.js | 61 +++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/modules/transport.js b/modules/transport.js index 24be3c4c552..bd28c17c3aa 100644 --- a/modules/transport.js +++ b/modules/transport.js @@ -529,48 +529,45 @@ Transport.prototype.getFromPeer = function (peer, options, cb) { req.body = options.data; } - var request = popsicle.request(req); + popsicle.request(req) + .use(popsicle.plugins.parse(['json'], false)) + .then(function (res) { + if (res.status !== 200) { + // Remove peer + __private.removePeer({peer: peer, code: 'ERESPONSE ' + res.status, req: req}); - request.use(popsicle.plugins.parse(['json'], false)); + return setImmediate(cb, ['Received bad response code', res.status, req.method, req.url].join(' ')); + } else { + var headers = peer.extend(res.headers); - request.then(function (res) { - if (res.status !== 200) { - // Remove peer - __private.removePeer({peer: peer, code: 'ERESPONSE ' + res.status, req: req}); + var report = library.schema.validate(headers, schema.headers); + if (!report) { + // Remove peer + __private.removePeer({peer: peer, code: 'EHEADERS', req: req}); - return setImmediate(cb, ['Received bad response code', res.status, req.method, req.url].join(' ')); - } else { - var headers = peer.extend(res.headers); + return setImmediate(cb, ['Invalid response headers', JSON.stringify(headers), req.method, req.url].join(' ')); + } - var report = library.schema.validate(headers, schema.headers); - if (!report) { - // Remove peer - __private.removePeer({peer: peer, code: 'EHEADERS', req: req}); + if (!modules.system.networkCompatible(headers.nethash)) { + // Remove peer + __private.removePeer({peer: peer, code: 'ENETHASH', req: req}); - return setImmediate(cb, ['Invalid response headers', JSON.stringify(headers), req.method, req.url].join(' ')); - } + return setImmediate(cb, ['Peer is not on the same network', headers.nethash, req.method, req.url].join(' ')); + } - if (!modules.system.networkCompatible(headers.nethash)) { - // Remove peer - __private.removePeer({peer: peer, code: 'ENETHASH', req: req}); + if (!modules.system.versionCompatible(headers.version)) { + // Remove peer + __private.removePeer({peer: peer, code: 'EVERSION:' + headers.version, req: req}); - return setImmediate(cb, ['Peer is not on the same network', headers.nethash, req.method, req.url].join(' ')); - } + return setImmediate(cb, ['Peer is using incompatible version', headers.version, req.method, req.url].join(' ')); + } - if (!modules.system.versionCompatible(headers.version)) { - // Remove peer - __private.removePeer({peer: peer, code: 'EVERSION:' + headers.version, req: req}); + modules.peers.update(peer); - return setImmediate(cb, ['Peer is using incompatible version', headers.version, req.method, req.url].join(' ')); + return setImmediate(cb, null, {body: res.body, peer: peer}); } - - modules.peers.update(peer); - - return setImmediate(cb, null, {body: res.body, peer: peer}); - } - }); - - request.catch(function (err) { + }) + .catch(function (err) { if (peer) { if (err.code === 'EUNAVAILABLE') { // Remove peer From e06ed3c6d7a656fea1001ad61f1f5321a33d9d18 Mon Sep 17 00:00:00 2001 From: 4miners Date: Mon, 6 Feb 2017 18:18:32 +0100 Subject: [PATCH 108/123] Remove unused reference --- modules/peers.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/peers.js b/modules/peers.js index f9fed9b9af7..a2c79cf4ed0 100644 --- a/modules/peers.js +++ b/modules/peers.js @@ -6,7 +6,6 @@ var constants = require('../helpers/constants.js'); var extend = require('extend'); var fs = require('fs'); var ip = require('ip'); -var git = require('../helpers/git.js'); var OrderBy = require('../helpers/orderBy.js'); var path = require('path'); var Peer = require('../logic/peer.js'); From a9934f234d102f667685c83dffc4522ff0710223 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Tue, 7 Feb 2017 11:27:37 +0100 Subject: [PATCH 109/123] Updating lisk-ui submodule --- public | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public b/public index e10ffb7d01b..5c62f31297c 160000 --- a/public +++ b/public @@ -1 +1 @@ -Subproject commit e10ffb7d01bcf6cb5a916866c70763a97f60ec95 +Subproject commit 5c62f31297c82276cc77a651057bbbaa3101883b From dc8e6b0ded6ff09bda6e3b461503d51ba6e8e892 Mon Sep 17 00:00:00 2001 From: 4miners Date: Tue, 7 Feb 2017 14:23:12 +0100 Subject: [PATCH 110/123] Start sync when peers ready, improve logging --- modules/loader.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/loader.js b/modules/loader.js index b50a39379c0..5bdd6a496b5 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -68,11 +68,14 @@ __private.attachApi = function () { __private.syncTrigger = function (turnOn) { if (turnOn === false && __private.syncIntervalId) { + library.logger.trace('Clearing sync interval'); clearTimeout(__private.syncIntervalId); __private.syncIntervalId = null; } if (turnOn === true && !__private.syncIntervalId) { + library.logger.trace('Setting sync interval'); setImmediate(function nextSyncTrigger () { + library.logger.trace('Sync trigger'); library.network.io.sockets.emit('loader/sync', { blocks: __private.blocksToSync, height: modules.blocks.getLastBlock().height @@ -83,8 +86,10 @@ __private.syncTrigger = function (turnOn) { }; __private.syncTimer = function () { + library.logger.trace('Setting sync timer'); setImmediate(function nextSync () { var lastReceipt = modules.blocks.lastReceipt(); + library.logger.trace('Sync timer trigger', {loaded: __private.loaded, syncing: self.syncing(), last_receipt: lastReceipt}); if (__private.loaded && !self.syncing() && (!lastReceipt || lastReceipt.stale)) { library.sequence.add(function (cb) { @@ -700,6 +705,10 @@ Loader.prototype.sandboxApi = function (call, args, cb) { // Events Loader.prototype.onPeersReady = function () { + library.logger.trace('Peers ready', {module: 'loader'}); + // Enforce sync early + __private.syncTimer(); + setImmediate(function load () { async.series({ loadTransactions: function (seriesCb) { @@ -729,6 +738,8 @@ Loader.prototype.onPeersReady = function () { } } }, function (err) { + library.logger.trace('Transactions and signatures pulled'); + if (err) { __private.initalize(); } From dcd3475a192a1e7884d1b89a8aee91b6e4bbe597 Mon Sep 17 00:00:00 2001 From: 4miners Date: Fri, 10 Feb 2017 13:53:48 +0100 Subject: [PATCH 111/123] Improve peers data acquisition, logging, tuned peer sweeper --- logic/peerSweeper.js | 6 ++--- modules/loader.js | 5 +++++ modules/peers.js | 52 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/logic/peerSweeper.js b/logic/peerSweeper.js index bd35c18fa78..96e7207964b 100644 --- a/logic/peerSweeper.js +++ b/logic/peerSweeper.js @@ -5,7 +5,7 @@ var pgp = require('pg-promise'); // Constructor function PeerSweeper (scope) { this.peers = []; - this.limit = 100; + this.limit = 500; this.scope = scope; var self = this; @@ -13,10 +13,10 @@ function PeerSweeper (scope) { setImmediate(function nextSweep () { if (self.peers.length) { self.sweep(self.peers.splice(0, self.limit), function () { - return setTimeout(nextSweep, 1000); + return setTimeout(nextSweep, 500); }); } else { - return setTimeout(nextSweep, 1000); + return setTimeout(nextSweep, 500); } }); } diff --git a/modules/loader.js b/modules/loader.js index 5bdd6a496b5..e8edb25ea08 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -629,8 +629,10 @@ Loader.prototype.getNetwork = function (cb) { return library.config.syncPeers.list.length ? setNetwork(library.config.syncPeers.list) : findDecentralizedPeers(); function findDecentralizedPeers () { + library.logger.trace('Finding decentralized peers'); async.waterfall([ function (waterCb) { + library.logger.trace('Get peers from random peer'); modules.transport.getFromRandomPeer({ api: '/list', method: 'GET' @@ -643,6 +645,7 @@ Loader.prototype.getNetwork = function (cb) { }); }, function (res, waterCb) { + library.logger.trace('Validate peers list'); library.schema.validate(res.body, schema.getNetwork.peers, function (err) { var peers = modules.peers.acceptable(res.body.peers); @@ -655,11 +658,13 @@ Loader.prototype.getNetwork = function (cb) { }); }, function (peers, waterCb) { + library.logger.trace('Asking peers for height', {count: (peers ? peers.length : null)}); async.map(peers, __private.getPeer, function (err, peers) { return setImmediate(waterCb, err, peers); }); } ], function (err, heights) { + library.logger.trace('Found heights', {count: (heights ? heights.length : null)}); if (err) { return setImmediate(cb, err); } diff --git a/modules/peers.js b/modules/peers.js index a2c79cf4ed0..bc0483a231b 100644 --- a/modules/peers.js +++ b/modules/peers.js @@ -298,7 +298,7 @@ Peers.prototype.acceptable = function (peers) { // Removing non-unique peers return (a.ip + a.port) === (b.ip + b.port); // Slicing peers up to maxPeers - }).slice(0, constants.maxPeers).value(); + }).value(); }; Peers.prototype.list = function (options, cb) { @@ -408,6 +408,20 @@ Peers.prototype.update = function (peer) { return __private.sweeper.push('upsert', self.accept(peer).object()); }; +Peers.prototype.pingPeer = function (peer, cb) { + library.logger.trace('Ping peer: ' + peer.ip + ':' + peer.port); + modules.transport.getFromPeer(peer, { + api: '/height', + method: 'GET' + }, function (err, res) { + if (err) { + return setImmediate(cb, 'Failed to ping peer: ' + peer.ip + ':' + peer.port); + } else { + return setImmediate(cb); + } + }); +}; + Peers.prototype.sandboxApi = function (call, args, cb) { sandboxHelper.callMethod(shared, call, args, cb); }; @@ -426,7 +440,7 @@ Peers.prototype.onBlockchainReady = function () { port: peer.port, version: modules.system.getVersion(), state: 2, - broadhash: modules.system.getBroadhash(), + broadhash: null, height: 1 }); @@ -457,9 +471,11 @@ Peers.prototype.onBlockchainReady = function () { }; Peers.prototype.onPeersReady = function () { + library.logger.trace('onPeersReady'); setImmediate(function nextSeries () { async.series({ updatePeersList: function (seriesCb) { + library.logger.trace('onPeersReady->updatePeersList'); __private.updatePeersList(function (err) { if (err) { library.logger.error('Peers timer', err); @@ -468,15 +484,45 @@ Peers.prototype.onPeersReady = function () { }); }, nextBanManager: function (seriesCb) { + library.logger.trace('onPeersReady->nextBanManager'); __private.banManager(function (err) { if (err) { library.logger.error('Ban manager timer', err); } return setImmediate(seriesCb); }); + }, + updatePeers: function (seriesCb) { + library.logger.trace('onPeersReady->updatePeers'); + self.list ({limit: 500}, function (err, peers, consensus) { + if (err) { + library.logger.error('Get peers failed', err); + return setImmediate(seriesCb, err); + } + + library.logger.trace('onPeersReady->updatePeers', {count: (peers ? peers.length : null)}); + var updated = 0; + async.each(peers, function (peer, eachCb) { + library.logger.debug('Updating peer', {peer: peer}); + // If peer is not banned and not been updated during last 3 sec - ping + if (peer && peer.state > 0) { + self.pingPeer (peer, function (err, res) { + if (!err) { + ++updated; + } + setImmediate(eachCb); + }); + } else { + setImmediate(eachCb); + } + }, function () { + library.logger.trace('onPeersReady->updatePeers', {updated: updated, total: peers.length}); + setImmediate(seriesCb); + }); + }); } }, function (err) { - return setTimeout(nextSeries, 60000); + return setTimeout(nextSeries, 5000); }); }); }; From dbc73e4c31448c1dfcf26de625e7d7b3365aef68 Mon Sep 17 00:00:00 2001 From: 4miners Date: Fri, 10 Feb 2017 15:18:03 +0100 Subject: [PATCH 112/123] Better logs messages, returning callbacks from functions --- modules/loader.js | 4 ++-- modules/peers.js | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/loader.js b/modules/loader.js index e8edb25ea08..36a5db3011e 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -632,7 +632,7 @@ Loader.prototype.getNetwork = function (cb) { library.logger.trace('Finding decentralized peers'); async.waterfall([ function (waterCb) { - library.logger.trace('Get peers from random peer'); + library.logger.trace('Getting peers from random peer'); modules.transport.getFromRandomPeer({ api: '/list', method: 'GET' @@ -645,7 +645,7 @@ Loader.prototype.getNetwork = function (cb) { }); }, function (res, waterCb) { - library.logger.trace('Validate peers list'); + library.logger.trace('Validating peers list'); library.schema.validate(res.body, schema.getNetwork.peers, function (err) { var peers = modules.peers.acceptable(res.body.peers); diff --git a/modules/peers.js b/modules/peers.js index bc0483a231b..b6a4232b4b8 100644 --- a/modules/peers.js +++ b/modules/peers.js @@ -409,7 +409,7 @@ Peers.prototype.update = function (peer) { }; Peers.prototype.pingPeer = function (peer, cb) { - library.logger.trace('Ping peer: ' + peer.ip + ':' + peer.port); + library.logger.trace('Pinging peer:: ' + peer.ip + ':' + peer.port); modules.transport.getFromPeer(peer, { api: '/height', method: 'GET' @@ -494,9 +494,9 @@ Peers.prototype.onPeersReady = function () { }, updatePeers: function (seriesCb) { library.logger.trace('onPeersReady->updatePeers'); - self.list ({limit: 500}, function (err, peers, consensus) { + self.list({limit: 500}, function (err, peers, consensus) { if (err) { - library.logger.error('Get peers failed', err); + library.logger.error('Peers listing failed', err); return setImmediate(seriesCb, err); } @@ -504,20 +504,20 @@ Peers.prototype.onPeersReady = function () { var updated = 0; async.each(peers, function (peer, eachCb) { library.logger.debug('Updating peer', {peer: peer}); - // If peer is not banned and not been updated during last 3 sec - ping + // Pinging only not banned peers if (peer && peer.state > 0) { - self.pingPeer (peer, function (err, res) { + self.pingPeer(peer, function (err, res) { if (!err) { ++updated; } - setImmediate(eachCb); + return setImmediate(eachCb); }); } else { - setImmediate(eachCb); + return setImmediate(eachCb); } }, function () { library.logger.trace('onPeersReady->updatePeers', {updated: updated, total: peers.length}); - setImmediate(seriesCb); + return setImmediate(seriesCb); }); }); } From dcb796b7d666c54dfd675b51c4245522d9c6bc77 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Fri, 10 Feb 2017 15:31:51 +0100 Subject: [PATCH 113/123] Removing extra colon --- modules/peers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/peers.js b/modules/peers.js index b6a4232b4b8..2567b4f37ae 100644 --- a/modules/peers.js +++ b/modules/peers.js @@ -409,7 +409,7 @@ Peers.prototype.update = function (peer) { }; Peers.prototype.pingPeer = function (peer, cb) { - library.logger.trace('Pinging peer:: ' + peer.ip + ':' + peer.port); + library.logger.trace('Pinging peer: ' + peer.ip + ':' + peer.port); modules.transport.getFromPeer(peer, { api: '/height', method: 'GET' From 58400dd2ae027f74eecb0c52c026b3a0fb6cac8b Mon Sep 17 00:00:00 2001 From: 4miners Date: Fri, 10 Feb 2017 15:56:38 +0100 Subject: [PATCH 114/123] Removed unnecessary log --- modules/peers.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/peers.js b/modules/peers.js index 2567b4f37ae..53f6eeb49a7 100644 --- a/modules/peers.js +++ b/modules/peers.js @@ -503,7 +503,6 @@ Peers.prototype.onPeersReady = function () { library.logger.trace('onPeersReady->updatePeers', {count: (peers ? peers.length : null)}); var updated = 0; async.each(peers, function (peer, eachCb) { - library.logger.debug('Updating peer', {peer: peer}); // Pinging only not banned peers if (peer && peer.state > 0) { self.pingPeer(peer, function (err, res) { From f226c9fa777824c46f85c9b5894ca872e1c6c58c Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Fri, 10 Feb 2017 16:52:49 +0100 Subject: [PATCH 115/123] Changing comment --- modules/peers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/peers.js b/modules/peers.js index 53f6eeb49a7..9ef2f1ffeda 100644 --- a/modules/peers.js +++ b/modules/peers.js @@ -503,7 +503,7 @@ Peers.prototype.onPeersReady = function () { library.logger.trace('onPeersReady->updatePeers', {count: (peers ? peers.length : null)}); var updated = 0; async.each(peers, function (peer, eachCb) { - // Pinging only not banned peers + // Pinging only unbanned peers if (peer && peer.state > 0) { self.pingPeer(peer, function (err, res) { if (!err) { From c1398ced2ff989ad6b87b27ada83429a3482dc27 Mon Sep 17 00:00:00 2001 From: Maciej Baj Date: Fri, 10 Feb 2017 19:03:49 +0100 Subject: [PATCH 116/123] update sync peer height, allow sync with peers through config.json --- app.js | 7 +++++-- modules/loader.js | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 05513ac8ee1..593dede5393 100644 --- a/app.js +++ b/app.js @@ -60,9 +60,12 @@ if (program.peers) { appConfig.peers.list = parametersReader.convertToAddressList(program.peers, appConfig.port); } -if (program.sync) { +if (program.sync || appConfig.syncPeers.list.length) { + var syncPeersList = appConfig.syncPeers.list.length ? + appConfig.syncPeers.list : parametersReader.convertToAddressList(program.sync, appConfig.port); + appConfig.syncPeers = { - list: parametersReader.convertToAddressList(program.sync, appConfig.port).map(function (syncPeer) { + list: syncPeersList.map(function (syncPeer) { return { ip: syncPeer.ip, port: syncPeer.port, diff --git a/modules/loader.js b/modules/loader.js index 36a5db3011e..ab01bb28d3d 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -593,11 +593,12 @@ __private.getPeer = function (peer, cb) { if (err) { return setImmediate(seriesCb, 'Failed to get height from peer: ' + peer.string); } else { + peer = res.peer; return setImmediate(seriesCb); } }); }, - validateHeight: function (seriesCb) { + validateHeight: function (seriesCb, res) { var heightIsValid = library.schema.validate(peer, schema.getNetwork.height); if (heightIsValid) { From 148915ed8bcc4bc67dde0593db582675dea24ecf Mon Sep 17 00:00:00 2001 From: macio Date: Mon, 13 Feb 2017 11:39:22 +0100 Subject: [PATCH 117/123] remove unnecessary validateHeight argument --- modules/loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/loader.js b/modules/loader.js index ab01bb28d3d..7c521ac1b5e 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -598,7 +598,7 @@ __private.getPeer = function (peer, cb) { } }); }, - validateHeight: function (seriesCb, res) { + validateHeight: function (seriesCb) { var heightIsValid = library.schema.validate(peer, schema.getNetwork.height); if (heightIsValid) { From cb9f89ed92f83414f0079093883ee6209dcc310d Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Mon, 13 Feb 2017 16:18:15 +0100 Subject: [PATCH 118/123] Removing line wrap --- app.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app.js b/app.js index 593dede5393..c1bb4120fa6 100644 --- a/app.js +++ b/app.js @@ -61,8 +61,7 @@ if (program.peers) { } if (program.sync || appConfig.syncPeers.list.length) { - var syncPeersList = appConfig.syncPeers.list.length ? - appConfig.syncPeers.list : parametersReader.convertToAddressList(program.sync, appConfig.port); + var syncPeersList = appConfig.syncPeers.list.length ? appConfig.syncPeers.list : parametersReader.convertToAddressList(program.sync, appConfig.port); appConfig.syncPeers = { list: syncPeersList.map(function (syncPeer) { From b5cd5970a9ee5b49d1a0892f96441d7515d23ed3 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Thu, 16 Feb 2017 11:21:04 +0100 Subject: [PATCH 119/123] Revert "Merge pull request #437 from MaciejBaj/434-sync-peers-fix" This reverts commit 1ea2f7c22c20443d899d746e802d57b8db3e7b31, reversing changes made to 79faddf92f8ececee3a5262fa45e00f483de1b1b. --- app.js | 6 ++---- modules/loader.js | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index c1bb4120fa6..05513ac8ee1 100644 --- a/app.js +++ b/app.js @@ -60,11 +60,9 @@ if (program.peers) { appConfig.peers.list = parametersReader.convertToAddressList(program.peers, appConfig.port); } -if (program.sync || appConfig.syncPeers.list.length) { - var syncPeersList = appConfig.syncPeers.list.length ? appConfig.syncPeers.list : parametersReader.convertToAddressList(program.sync, appConfig.port); - +if (program.sync) { appConfig.syncPeers = { - list: syncPeersList.map(function (syncPeer) { + list: parametersReader.convertToAddressList(program.sync, appConfig.port).map(function (syncPeer) { return { ip: syncPeer.ip, port: syncPeer.port, diff --git a/modules/loader.js b/modules/loader.js index 7c521ac1b5e..36a5db3011e 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -593,7 +593,6 @@ __private.getPeer = function (peer, cb) { if (err) { return setImmediate(seriesCb, 'Failed to get height from peer: ' + peer.string); } else { - peer = res.peer; return setImmediate(seriesCb); } }); From 2522d47d6043ea8b68d0b11b7c38fbea80a205c0 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Thu, 16 Feb 2017 11:26:17 +0100 Subject: [PATCH 120/123] Revert "Merge pull request #389 from MaciejBaj/peers-sync" This reverts commit df05dc7d622a2645ee3743c9741a6f77b062b93c, reversing changes made to d9fa1bd8513d51dd6ca394e0b0bb7111855d35b0. --- app.js | 26 ++++----- config.json | 3 -- helpers/parametersReader.js | 16 ------ modules/loader.js | 105 +++++++++++++----------------------- test/config.json | 3 -- 5 files changed, 48 insertions(+), 105 deletions(-) delete mode 100644 helpers/parametersReader.js diff --git a/app.js b/app.js index 05513ac8ee1..78c85ea968b 100644 --- a/app.js +++ b/app.js @@ -40,8 +40,7 @@ program .option('-c, --config ', 'config file path') .option('-p, --port ', 'listening port number') .option('-a, --address ', 'listening host name or ip') - .option('-x, --peers [peers...]', 'peers list to seed from') - .option('-y, --sync [peers...]', 'peers list to sync with') + .option('-x, --peers [peers...]', 'peers list') .option('-l, --log ', 'log level') .option('-s, --snapshot ', 'verify snapshot') .parse(process.argv); @@ -57,22 +56,17 @@ if (program.address) { } if (program.peers) { - appConfig.peers.list = parametersReader.convertToAddressList(program.peers, appConfig.port); -} - -if (program.sync) { - appConfig.syncPeers = { - list: parametersReader.convertToAddressList(program.sync, appConfig.port).map(function (syncPeer) { + if (typeof program.peers === 'string') { + appConfig.peers.list = program.peers.split(',').map(function (peer) { + peer = peer.split(':'); return { - ip: syncPeer.ip, - port: syncPeer.port, - version: appConfig.version, - state: 2, - broadhash: appConfig.nethash, - height: 1 + ip: peer.shift(), + port: peer.shift() || appConfig.port }; - }) - }; + }); + } else { + appConfig.peers.list = []; + } } if (program.log) { diff --git a/config.json b/config.json index dac8d156561..8a0da2de4fa 100644 --- a/config.json +++ b/config.json @@ -88,9 +88,6 @@ "timeout": 5000 } }, - "syncPeers": { - "list": [] - }, "broadcasts": { "broadcastInterval": 5000, "broadcastLimit": 20, diff --git a/helpers/parametersReader.js b/helpers/parametersReader.js deleted file mode 100644 index f8f85f2d011..00000000000 --- a/helpers/parametersReader.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -var parametersReader = {}; - -parametersReader.convertToAddressList = function (addresses, optPort) { - return typeof addresses !== 'string' ? [] : - addresses.split(',').map(function (address) { - address = address.split(':'); - return { - ip: address.shift(), - port: address.shift() || optPort - }; - }); -}; - -module.exports = parametersReader; diff --git a/modules/loader.js b/modules/loader.js index 36a5db3011e..efe9d80a437 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -626,78 +626,49 @@ Loader.prototype.getNetwork = function (cb) { if (__private.network.height > 0 && Math.abs(__private.network.height - modules.blocks.getLastBlock().height) === 1) { return setImmediate(cb, null, __private.network); } - return library.config.syncPeers.list.length ? setNetwork(library.config.syncPeers.list) : findDecentralizedPeers(); - - function findDecentralizedPeers () { - library.logger.trace('Finding decentralized peers'); - async.waterfall([ - function (waterCb) { - library.logger.trace('Getting peers from random peer'); - modules.transport.getFromRandomPeer({ - api: '/list', - method: 'GET' - }, function (err, res) { - if (err) { - return setImmediate(waterCb, err); - } else { - return setImmediate(waterCb, null, res); - } - }); - }, - function (res, waterCb) { - library.logger.trace('Validating peers list'); - library.schema.validate(res.body, schema.getNetwork.peers, function (err) { - var peers = modules.peers.acceptable(res.body.peers); - - if (err) { - return setImmediate(waterCb, err); - } else { - library.logger.log(['Received', peers.length, 'peers from'].join(' '), res.peer.string); - return setImmediate(waterCb, null, peers); - } - }); - }, - function (peers, waterCb) { - library.logger.trace('Asking peers for height', {count: (peers ? peers.length : null)}); - async.map(peers, __private.getPeer, function (err, peers) { - return setImmediate(waterCb, err, peers); - }); - } - ], function (err, heights) { - library.logger.trace('Found heights', {count: (heights ? heights.length : null)}); - if (err) { - return setImmediate(cb, err); - } - - __private.network = __private.findGoodPeers(heights); - - if (!__private.network.peers.length) { - return setImmediate(cb, 'Failed to find enough good peers'); - } else { - return setImmediate(cb, null, __private.network); - } - }); - } + async.waterfall([ + function (waterCb) { + modules.transport.getFromRandomPeer({ + api: '/list', + method: 'GET' + }, function (err, res) { + if (err) { + return setImmediate(waterCb, err); + } else { + return setImmediate(waterCb, null, res); + } + }); + }, + function (res, waterCb) { + library.schema.validate(res.body, schema.getNetwork.peers, function (err) { + var peers = modules.peers.acceptable(res.body.peers); - function setNetwork (requestedPeers) { - return async.map(requestedPeers, __private.getPeer, function (err, peers) { - var syncedPeers = peers.filter(function (peer) { - return peer.height; - }).map(function (syncedPeer) { - return syncedPeer.peer; + if (err) { + return setImmediate(waterCb, err); + } else { + library.logger.log(['Received', peers.length, 'peers from'].join(' '), res.peer.string); + return setImmediate(waterCb, null, peers); + } }); + }, + function (peers, waterCb) { + async.map(peers, __private.getPeer, function (err, peers) { + return setImmediate(waterCb, err, peers); + }); + } + ], function (err, heights) { + if (err) { + return setImmediate(cb, err); + } - if (!syncedPeers.length) { - return setImmediate(cb, 'Failed to synchronize with requested peers'); - } + __private.network = __private.findGoodPeers(heights); - __private.network = { - peers: syncedPeers, - height: Math.max(syncedPeers.map(function (p) { return p.height; })) - }; + if (!__private.network.peers.length) { + return setImmediate(cb, 'Failed to find enough good peers'); + } else { return setImmediate(cb, null, __private.network); - }); - } + } + }); }; Loader.prototype.syncing = function () { diff --git a/test/config.json b/test/config.json index 648c72bec68..bf3503796da 100644 --- a/test/config.json +++ b/test/config.json @@ -47,9 +47,6 @@ "timeout": 5000 } }, - "syncPeers": { - "list": [] - }, "broadcasts": { "broadcastInterval": 5000, "broadcastLimit": 20, From 949aff6532df4d9ba0048230e0f843336a8d94b3 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Thu, 16 Feb 2017 12:06:06 +0100 Subject: [PATCH 121/123] Removing unused requirement --- app.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app.js b/app.js index 78c85ea968b..aed027b5759 100644 --- a/app.js +++ b/app.js @@ -9,7 +9,6 @@ var git = require('./helpers/git.js'); var https = require('https'); var Logger = require('./logger.js'); var packageJson = require('./package.json'); -var parametersReader = require('./helpers/parametersReader.js'); var path = require('path'); var program = require('commander'); var Sequence = require('./helpers/sequence.js'); From 2672b2dccd78d2ed7b518c9b8452723cf0dff83c Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Tue, 21 Feb 2017 15:49:27 +0100 Subject: [PATCH 122/123] Updating lisk-ui submodule --- public | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public b/public index 5c62f31297c..11d52333e27 160000 --- a/public +++ b/public @@ -1 +1 @@ -Subproject commit 5c62f31297c82276cc77a651057bbbaa3101883b +Subproject commit 11d52333e276b7b1f2f2fae37d18622f762b63b1 From 8da902eefdb760b3562f822fe8d6e2d194e7fe83 Mon Sep 17 00:00:00 2001 From: Oliver Beddows Date: Tue, 21 Feb 2017 15:51:58 +0100 Subject: [PATCH 123/123] Bumping version --- config.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index 8a0da2de4fa..976784f6465 100644 --- a/config.json +++ b/config.json @@ -1,7 +1,7 @@ { "port": 8000, "address": "0.0.0.0", - "version": "0.5.0", + "version": "0.6.0", "minVersion": ">=0.5.0", "fileLogLevel": "info", "logFileName": "logs/lisk.log", diff --git a/package.json b/package.json index 08ddd517bc2..0c7270f87e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lisk", - "version": "0.5.0", + "version": "0.6.0", "private": true, "scripts": { "start": "node app.js",