diff --git a/framework/src/modules/chain/chain.js b/framework/src/modules/chain/chain.js index 127917fad19..6daf9adeb43 100644 --- a/framework/src/modules/chain/chain.js +++ b/framework/src/modules/chain/chain.js @@ -233,8 +233,6 @@ module.exports = class Chain { action.params[0], action.params[1] ), - storageRead: async action => - this.scope.logic.block.storageRead(action.params[0]), getLastConsensus: async () => this.scope.modules.peers.getLastConsensus(), loaderLoaded: async () => this.scope.modules.loader.loaded(), loaderSyncing: async () => this.scope.modules.loader.syncing(), diff --git a/framework/src/modules/chain/index.js b/framework/src/modules/chain/index.js index d547391ebcb..9959662a523 100644 --- a/framework/src/modules/chain/index.js +++ b/framework/src/modules/chain/index.js @@ -65,7 +65,6 @@ module.exports = class ChainModule extends BaseModule { getPeersCountByFilter: async action => this.chain.actions.getPeersCountByFilter(action), postSignature: async action => this.chain.actions.postSignature(action), - storageRead: async action => this.chain.actions.storageRead(action), getLastConsensus: async () => this.chain.actions.getLastConsensus(), loaderLoaded: async () => this.chain.actions.loaderLoaded(), loaderSyncing: async () => this.chain.actions.loaderSyncing(), diff --git a/framework/src/modules/chain/logic/block.js b/framework/src/modules/chain/logic/block.js index 3166fa8e2e9..9f67ffdb592 100644 --- a/framework/src/modules/chain/logic/block.js +++ b/framework/src/modules/chain/logic/block.js @@ -517,11 +517,10 @@ Block.prototype.dbRead = function(raw) { }; /** - * Creates block object based on raw data. + * Creates block object based on raw database block data. * - * @param {Object} raw + * @param {Object} raw Raw database data block object * @returns {null|block} Block object - * @todo Add description for the params */ Block.prototype.storageRead = function(raw) { if (!raw.id) { diff --git a/framework/src/modules/http_api/controllers/blocks.js b/framework/src/modules/http_api/controllers/blocks.js index d92a78f1bde..fea553bffc7 100644 --- a/framework/src/modules/http_api/controllers/blocks.js +++ b/framework/src/modules/http_api/controllers/blocks.js @@ -15,8 +15,10 @@ 'use strict'; const _ = require('lodash'); +const crypto = require('crypto'); const apiCodes = require('../helpers/api_codes.js'); const ApiError = require('../helpers/api_error.js'); +const Bignum = require('../helpers/bignum.js'); let library; let sortFields; @@ -108,6 +110,66 @@ BlocksController.getBlocks = function(context, next) { }); }; +/** + * Gets address by public. + * + * @private + * @param {publicKey} publicKey Public key + * @returns {address} address + * @todo Replace by Lisk Elements once integrated. + */ +function getAddressByPublicKey(publicKey) { + const publicKeyHash = crypto + .createHash('sha256') + .update(publicKey, 'hex') + .digest(); + const temp = Buffer.alloc(8); + + for (let i = 0; i < 8; i++) { + temp[i] = publicKeyHash[7 - i]; + } + + const address = `${Bignum.fromBuffer(temp).toString()}L`; + return address; +} + +/** + * Parse raw block data from database into expected API response type for blocks + * + * @param {Object} raw Raw block data from database + * @return {block} Block formatted according to API specification + */ +function parseBlockFromDatabase(raw) { + if (!raw.id) { + return null; + } + + const block = { + id: raw.id, + version: parseInt(raw.version), + timestamp: parseInt(raw.timestamp), + height: parseInt(raw.height), + previousBlock: raw.previousBlockId, + numberOfTransactions: parseInt(raw.numberOfTransactions), + totalAmount: new Bignum(raw.totalAmount).toString(), + totalFee: new Bignum(raw.totalFee).toString(), + reward: new Bignum(raw.reward).toString(), + payloadLength: parseInt(raw.payloadLength), + payloadHash: raw.payloadHash, + generatorPublicKey: raw.generatorPublicKey, + generatorId: getAddressByPublicKey(raw.generatorPublicKey), + blockSignature: raw.blockSignature, + confirmations: parseInt(raw.confirmations), + totalForged: new Bignum(raw.totalFee).plus(raw.reward).toString(), + }; + + if (raw.transactions) { + block.transactions = raw.transactions; + } + + return block; +} + /** * Get filtered list of blocks (without transactions - BasicBlock). * @@ -180,13 +242,7 @@ function _list(params, cb) { library.storage.entities.Block.get(filters, options) // FIXME: Can have poor performance because it performs SHA256 hash calculation for each block .then(async rows => - setImmediate( - cb, - null, - await Promise.all( - rows.map(row => library.channel.invoke('chain:storageRead', [row])) - ) - ) + setImmediate(cb, null, rows.map(parseBlockFromDatabase)) ) .catch(err => { library.logger.error(err.stack); diff --git a/framework/test/mocha/unit/modules/http_api/controllers/blocks.js b/framework/test/mocha/unit/modules/http_api/controllers/blocks.js index 30b95ffc94a..d6b05ddae91 100644 --- a/framework/test/mocha/unit/modules/http_api/controllers/blocks.js +++ b/framework/test/mocha/unit/modules/http_api/controllers/blocks.js @@ -22,12 +22,34 @@ const BlocksController = rewire( describe('blocks/api', () => { let _list; + let parseBlockFromDatabase; let library; let loggerSpy; let storageStub; let channelStub; + let rawBlock; beforeEach(done => { + rawBlock = { + id: '8999789716699660339', + payloadHash: + 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', + generatorPublicKey: + '68680ca0bcd4676489976837edeac305c34f652e970386013ef26e67589a2516', + blockSignature: + '51356c69d94762ef355a95a960002aabc80d331da136cc07082bf98856e57ea9d13d2c74dd923c412221b36f4c3e276b2d83d9019521ddf003d0b39698d4ae0a', + height: 1860, + totalFee: '0', + reward: '0', + payloadLength: 0, + previousBlockId: '14888522644597494628', + numberOfTransactions: 0, + totalAmount: '0', + timestamp: 87670360, + version: '1', + confirmations: 37, + }; + storageStub = { entities: { Block: { @@ -52,6 +74,7 @@ describe('blocks/api', () => { }); library = BlocksController.__get__('library'); _list = BlocksController.__get__('_list'); + parseBlockFromDatabase = BlocksController.__get__('parseBlockFromDatabase'); done(); }); @@ -69,6 +92,35 @@ describe('blocks/api', () => { }); }); + describe('parseBlockFromDatabase', () => { + let formattedBlock; + + beforeEach(async () => { + formattedBlock = parseBlockFromDatabase(rawBlock); + }); + + it('should return a block properly formatted when transactions is undefined', async () => { + const rawBlockWithTransactions = _.cloneDeep(rawBlock); + formattedBlock = parseBlockFromDatabase(rawBlockWithTransactions); + expect(formattedBlock).deep.equal(formattedBlock); + expect(formattedBlock).to.not.have.property('transactions'); + }); + + it('should return a block properly formatted when transactions is not undefined', async () => { + const rawBlockWithTransactions = _.cloneDeep(rawBlock); + rawBlockWithTransactions.transactions = []; + formattedBlock = parseBlockFromDatabase(rawBlockWithTransactions); + expect(formattedBlock).to.have.property('transactions'); + }); + + it('should return null when no id is present for raw data', async () => { + const rawBlockWithoutID = _.cloneDeep(rawBlock); + delete rawBlockWithoutID.id; + formattedBlock = parseBlockFromDatabase(rawBlockWithoutID); + expect(formattedBlock).to.be.null; + }); + }); + describe('_list', () => { describe('list', () => { afterEach(done => {