diff --git a/.eslintrc.json b/.eslintrc.json index 48d1a8c56a3..003a67658a7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,7 +6,7 @@ "rules": { "semi": "error", "no-eq-null": "off", - "indent": ["error", "tab", { "flatTernaryExpressions": true }], + "indent": ["error", "tab", { "flatTernaryExpressions": true, "SwitchCase": 1 }], "eqeqeq": "off", "curly": "error", "no-undef": "error", diff --git a/.gitignore b/.gitignore index 043e9b36320..f5e6751804d 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ tmp sftp-config.json *.swp *.swo +test/integration/logs/ +test/integration/configs/ +!test/integration/configs/config.non-forge.json diff --git a/Gruntfile.js b/Gruntfile.js index 17e6e852b8c..8157088ec4e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -6,6 +6,7 @@ var util = require('util'); module.exports = function (grunt) { var files = [ 'logger.js', + 'workersController.js', 'api/**/*.js', 'helpers/**/*.js', 'modules/**/*.js', @@ -41,6 +42,7 @@ module.exports = function (grunt) { util.format('mkdir -p %s/logs', version_dir), util.format('mkdir -p %s/pids', version_dir), util.format('cp %s/app.js %s', release_dir, version_dir), + util.format('cp %s/workersController.js %s', release_dir, version_dir), util.format('cp %s/config.json %s', __dirname, version_dir), util.format('cp %s/package.json %s', __dirname, version_dir), util.format('cp %s/genesisBlock.json %s', __dirname, version_dir), @@ -70,10 +72,26 @@ module.exports = function (grunt) { maxBuffer: maxBufferSize }, + coverageUnit: { + command: 'node_modules/.bin/istanbul cover --dir test/.coverage-unit ./node_modules/.bin/_mocha test/unit/index.js', + maxBuffer: maxBufferSize + }, + + testFunctional: { + command: './node_modules/.bin/mocha test/api/index.js', + maxBuffer: maxBufferSize + }, + fetchCoverage: { command: 'rm -rf ./test/.coverage-func.zip; curl -o ./test/.coverage-func.zip $HOST/coverage/download', maxBuffer: maxBufferSize }, + + createBundles: { + command: 'npm run create-bundles', + maxBuffer: maxBufferSize + }, + coverageReport: { command: 'rm -f ./test/.coverage-unit/lcov.info; ./node_modules/.bin/istanbul report --root ./test/.coverage-unit/ --dir ./test/.coverage-unit' } @@ -118,11 +136,13 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-eslint'); grunt.registerTask('default', ['release']); - grunt.registerTask('release', ['exec:folder', 'obfuscator', 'exec:package', 'exec:build', 'compress']); + grunt.registerTask('release', ['exec:folder', 'obfuscator', 'exec:createBundles', 'exec:package', 'exec:build', 'compress']); grunt.registerTask('jenkins', ['exec:coverageSingle']); grunt.registerTask('coverageReport', ['exec:coverageReport']); grunt.registerTask('eslint-nofix', ['eslint']); grunt.registerTask('test', ['eslint', 'exec:coverage']); + grunt.registerTask('test-unit', ['eslint', 'exec:coverageUnit']); + grunt.registerTask('test-functional', ['eslint', 'exec:testFunctional']); grunt.registerTask('eslint-fix', 'Run eslint and fix formatting', function () { grunt.config.set('eslint.options.fix', true); diff --git a/Jenkinsfile b/Jenkinsfile index a250bdbe2a3..15cb097a980 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -206,10 +206,10 @@ lock(resource: "Lisk-Core-Nodes", inversePrecedence: true) { ''' } }, //End node-01 tests - "Functional Peer - Peer" : { + "Functional Peer - Peers" : { node('node-02'){ sh ''' - export TEST=test/api/peer.js TEST_TYPE='FUNC' + export TEST=test/api/peers.js TEST_TYPE='FUNC' cd "$(echo $WORKSPACE | cut -f 1 -d '@')" npm run jenkins ''' @@ -218,7 +218,7 @@ lock(resource: "Lisk-Core-Nodes", inversePrecedence: true) { "Functional Peer - Blocks" : { node('node-02'){ sh ''' - export TEST=test/api/peer.blocks.js TEST_TYPE='FUNC' + export TEST=test/api/peer/peer.blocks.ws.js TEST_TYPE='FUNC' cd "$(echo $WORKSPACE | cut -f 1 -d '@')" npm run jenkins ''' @@ -227,7 +227,7 @@ lock(resource: "Lisk-Core-Nodes", inversePrecedence: true) { "Functional Peer - Signatures" : { node('node-02'){ sh ''' - export TEST=test/api/peer.signatures.js TEST_TYPE='FUNC' + export TEST=test/api/peer/peer.signatures.ws.js TEST_TYPE='FUNC' cd "$(echo $WORKSPACE | cut -f 1 -d '@')" npm run jenkins ''' @@ -236,7 +236,7 @@ lock(resource: "Lisk-Core-Nodes", inversePrecedence: true) { "Functional Peer - Transactions Collision" : { node('node-02'){ sh ''' - export TEST=test/api/peer.transactions.collision.js TEST_TYPE='FUNC' + export TEST=test/api/peer/peer.transactions.collision.ws.js TEST_TYPE='FUNC' cd "$(echo $WORKSPACE | cut -f 1 -d '@')" npm run jenkins ''' @@ -245,7 +245,7 @@ lock(resource: "Lisk-Core-Nodes", inversePrecedence: true) { "Functional Peer - Transactions Delegates" : { node('node-02'){ sh ''' - export TEST=test/api/peer.transactions.delegates.js TEST_TYPE='FUNC' + export TEST=test/api/peer/peer.transactions.delegates.ws.js TEST_TYPE='FUNC' cd "$(echo $WORKSPACE | cut -f 1 -d '@')" npm run jenkins ''' @@ -254,7 +254,7 @@ lock(resource: "Lisk-Core-Nodes", inversePrecedence: true) { "Functional Peer - Transactions Main" : { node('node-02'){ sh ''' - export TEST=test/api/peer.transactions.main.js TEST_TYPE='FUNC' + export TEST=test/api/peer/peer.transactions.main.ws.js TEST_TYPE='FUNC' cd "$(echo $WORKSPACE | cut -f 1 -d '@')" npm run jenkins ''' @@ -263,16 +263,25 @@ lock(resource: "Lisk-Core-Nodes", inversePrecedence: true) { "Functional Peer - Transaction Signatures" : { node('node-02'){ sh ''' - export TEST=test/api/peer.transactions.signatures.js TEST_TYPE='FUNC' + export TEST=test/api/peer/peer.transactions.signatures.ws.js TEST_TYPE='FUNC' cd "$(echo $WORKSPACE | cut -f 1 -d '@')" npm run jenkins ''' } }, - "Functional Peer - Peers" : { + "Functional Peer - Transport" : { node('node-02'){ sh ''' - export TEST=test/api/peers.js TEST_TYPE='FUNC' + export TEST=test/api/peer/transport.js TEST_TYPE='FUNC' + cd "$(echo $WORKSPACE | cut -f 1 -d '@')" + npm run jenkins + ''' + } + }, + "Functional Peer - Peers websockets" : { + node('node-02'){ + sh ''' + export TEST=test/api/peer/peer.ws.js TEST_TYPE='FUNC' cd "$(echo $WORKSPACE | cut -f 1 -d '@')" npm run jenkins ''' @@ -281,7 +290,7 @@ lock(resource: "Lisk-Core-Nodes", inversePrecedence: true) { "Functional Peer - Votes" : { node('node-02'){ sh ''' - export TEST=test/api/peer.transactions.votes.js TEST_TYPE='FUNC' + export TEST=test/api/peer/peer.transactions.votes.ws.js TEST_TYPE='FUNC' cd "$(echo $WORKSPACE | cut -f 1 -d '@')" npm run jenkins ''' @@ -338,7 +347,7 @@ lock(resource: "Lisk-Core-Nodes", inversePrecedence: true) { "Functional Stress - Transactions" : { node('node-04'){ sh ''' - export TEST=test/api/peer.transactions.stress.js TEST_TYPE='FUNC' + export TEST=test/api/peer/peer.transactions.stress.ws.js TEST_TYPE='FUNC' cd "$(echo $WORKSPACE | cut -f 1 -d '@')" npm run jenkins ''' diff --git a/api/http/transport.js b/api/http/transport.js index b02e06b14ea..e69de29bb2d 100644 --- a/api/http/transport.js +++ b/api/http/transport.js @@ -1,111 +0,0 @@ -'use strict'; - -var Router = require('../../helpers/router'); -var httpApi = require('../../helpers/httpApi'); -var schema = require('../../schema/transport'); - -/** - * Binds api with modules and creates common url. - * - End point: `/peer` - * - Private API: - * - get /blocks/common - * - get /blocks - * - get /list - * - get /height - * - get /ping - * - get /signatures - * - get /transactions - * - post /blocks - * - post /signatures - * - post /transactions - * @memberof module:transport - * @requires helpers/Router - * @requires helpers/httpApi - * @constructor - * @param {Object} transportModule - Module transport instance. - * @param {scope} app - Network app. - * @param {function} logger - */ -// Constructor -function TransportHttpApi (transportModule, app, logger, cache) { - - var router = new Router(); - - router.use(httpApi.middleware.attachResponseHeaders.bind(null, transportModule.headers)); - router.use(httpApi.middleware.blockchainReady.bind(null, transportModule.isLoaded)); - - router.use(handshakeMiddleware); - - router.get('/blocks/common', getCommonBlocksMiddleware); - router.get('/blocks', httpApi.middleware.sanitize('query', schema.blocks, transportModule.internal.blocks)); - - router.map(transportModule.internal, { - 'get /list': 'list', - 'get /height': 'height', - 'get /ping': 'ping', - 'get /signatures': 'getSignatures', - 'get /transactions': 'getTransactions' - - }); - - // Custom parameters internal functions - router.post('/blocks', function (req, res) { - transportModule.internal.postBlock(req.body.block, req.peer, req.method + ' ' + req.url, httpApi.respond.bind(null, res)); - }); - - router.post('/signatures', function (req, res) { - transportModule.internal.postSignatures({signatures: req.body.signatures, signature: req.body.signature}, httpApi.respond.bind(null, res)); - }); - - router.post('/transactions', function (req, res) { - transportModule.internal.postTransactions({ - transactions: req.body.transactions, - transaction: req.body.transaction - }, req.peer, req.method + ' ' + req.url, httpApi.respond.bind(null, res)); - }); - - router.use(httpApi.middleware.notFound); - - app.use('/peer', router); - - function handshakeMiddleware (req, res, next) { - transportModule.internal.handshake(req.ip, req.headers.port, req.headers, validateHeaders, function (err, peer) { - if (err) { - return res.status(500).send(err); - } - - req.peer = peer; - - return next(); - }); - - function validateHeaders (headers, cb) { - return req.sanitize(headers, schema.headers, function (err, report, sanitized) { - if (err) { - return cb(err.toString()); - } else if (!report.isValid) { - return cb(report.issues); - } - - return cb(); - }); - } - } - - function getCommonBlocksMiddleware (req, res, next) { - req.sanitize(req.query, schema.commonBlock, function (err, report, query) { - if (err) { - logger.debug('Common block request validation failed', {err: err.toString(), req: req.query}); - return next(err); - } - if (!report.isValid) { - logger.debug('Common block request validation failed', {err: report, req: req.query}); - return res.json({success: false, error: report.issues}); - } - - return transportModule.internal.blocksCommon(query.ids, req.peer, req.method + ' ' + req.url, httpApi.respond.bind(null, res)); - }); - } -} - -module.exports = TransportHttpApi; diff --git a/api/ws/rpc/wsRPC.js b/api/ws/rpc/wsRPC.js new file mode 100644 index 00000000000..da2975b647a --- /dev/null +++ b/api/ws/rpc/wsRPC.js @@ -0,0 +1,188 @@ +'use strict'; + +var _ = require('lodash'); +var MasterWAMPServer = require('wamp-socket-cluster/MasterWAMPServer'); +var scClient = require('socketcluster-client'); +var WAMPClient = require('wamp-socket-cluster/WAMPClient'); +var System = require('../../../modules/system'); +var PromiseDefer = require('../../../helpers/promiseDefer'); + +var wsServer = null; + +var wsRPC = { + + clientsConnectionsMap: {}, + scClient: scClient, + wampClient: new WAMPClient(), + + /** + * @param {MasterWAMPServer} __wsServer + */ + setServer: function (__wsServer) { + wsServer = __wsServer; + }, + + /** + * @throws {Error} thrown if wsServer haven't been initialized before + * @returns {MasterWAMPServer} wsServer + */ + getServer: function () { + if (!wsServer) { + throw new Error('WS server haven\'t been initialized!'); + } + return wsServer; + }, + /** + * @param {string} ip + * @param {number} port + * @returns {ClientRPCStub} {[string]: function} map where keys are all procedures registered + */ + getClientRPCStub: function (ip, port) { + if (!ip || !port) { + throw new Error('RPC client needs ip and port to establish WS connection with: ' + ip + ':' + port); + } + + var address = ip + ':' + port; + var connectionState = this.clientsConnectionsMap[address]; + + //first time init || previously rejected + if (!connectionState || connectionState.status === ConnectionState.STATUS.DISCONNECTED) { + connectionState = new ConnectionState(ip, port); + this.clientsConnectionsMap[address] = connectionState; + } + return connectionState.stub; + } +}; + +ConnectionState.STATUS = { + NEW: 1, + PENDING: 2, + ESTABLISHED: 3, + DISCONNECTED: 4 +}; + +function ConnectionState (ip, port) { + this.ip = ip; + this.port = +port; + this.status = ConnectionState.STATUS.NEW; + this.socketDefer = PromiseDefer(); + this.stub = new ClientRPCStub(this); +} + +ConnectionState.prototype.reconnect = function () { + this.status = ConnectionState.STATUS.PENDING; + this.socketDefer = PromiseDefer(); +}; + +ConnectionState.prototype.reject = function (reason) { + this.status = ConnectionState.STATUS.DISCONNECTED; + this.socketDefer.reject(reason); +}; + +ConnectionState.prototype.resolve = function (socket) { + this.status = ConnectionState.STATUS.ESTABLISHED; + this.socketDefer.resolve(socket); +}; + +/** + * The stub of all RPC methods registered on WS server + * Example: + * methodA registered on WS server can be called by a client by simply: + * sampleClientStub.methodA(exampleArg, cb); + * + * @typedef {Object} clientStub + * @property {function} procedure - procedure that will be called with argument and callback + */ + + +/** + * @param {ConnectionState} connectionState + * @returns {clientStub} + */ +var ClientRPCStub = function (connectionState) { + try { + var wsServer = wsRPC.getServer(); + } catch (wsServerNotInitializedException) { + return {}; + } + + return _.reduce(Object.assign({}, wsServer.endpoints.rpc, wsServer.endpoints.event), + function (availableCalls, procedureHandler, procedureName) { + availableCalls[procedureName] = this.sendAfterSocketReadyCb(connectionState)(procedureName); + return availableCalls; + }.bind(this), {}); +}; + + +/** + * @param {ConnectionState} connectionState + */ +ClientRPCStub.prototype.initializeNewConnection = function (connectionState) { + + var options = { + hostname: connectionState.ip, + port: connectionState.port, + protocol: 'http', + autoReconnect: true, + query: System.getHeaders() + }; + + var clientSocket = wsRPC.scClient.connect(options); + wsRPC.wampClient.upgradeToWAMP(clientSocket); + + clientSocket.on('connect', function () { + return connectionState.resolve(clientSocket); + }); + + clientSocket.on('error', function () { + clientSocket.disconnect(); + }); + + clientSocket.on('connectAbort', function (err, data) { + connectionState.reject('Connection rejected by failed handshake procedure'); + }); + + clientSocket.on('disconnect', function () { + connectionState.reject('Connection disconnected'); + }); +}; + +/** + * @param {ConnectionState} connectionState + * @returns {function} function to be called with procedure, to be then called with optional argument and/or callback + */ +ClientRPCStub.prototype.sendAfterSocketReadyCb = function (connectionState) { + return function (procedureName) { + /** + * @param {object} data [data={}] argument passed to procedure + * @param {function} cb [cb=function(){}] cb + */ + return function (data, cb) { + cb = _.isFunction(cb) ? cb : _.isFunction(data) ? data : function () {}; + data = (data && !_.isFunction(data)) ? data : {}; + + if (connectionState.status === ConnectionState.STATUS.NEW || connectionState.status === ConnectionState.STATUS.DISCONNECTED) { + connectionState.reconnect(); + ClientRPCStub.prototype.initializeNewConnection(connectionState); + } + + connectionState.socketDefer.promise.then(function (socket) { + return socket.wampSend(procedureName, data) + .then(function (res) { + return setImmediate(cb, null, res); + }) + .catch(function (err) { + return setImmediate(cb, err); + }); + }).catch(function (err) { + return setImmediate(cb, err); + }); + }; + }; +}; + +module.exports = { + wsRPC: wsRPC, + ConnectionState: ConnectionState, + ClientRPCStub: ClientRPCStub +}; diff --git a/api/ws/transport.js b/api/ws/transport.js new file mode 100644 index 00000000000..5d1a2e06c9b --- /dev/null +++ b/api/ws/transport.js @@ -0,0 +1,24 @@ +'use strict'; + +var wsApi = require('../../helpers/wsApi'); +var wsRPC = require('./rpc/wsRPC').wsRPC; + +function TransportWSApi (transportModule, app, logger) { + + wsRPC.getServer().registerRPCEndpoints({ + acceptPeer: transportModule.internal.acceptPeer, + removePeer: transportModule.internal.removePeer, + blocksCommon: transportModule.internal.blocksCommon, + blocks: transportModule.internal.blocks, + list: transportModule.internal.list, + height: transportModule.internal.height, + getTransactions: transportModule.internal.getTransactions, + getSignatures: transportModule.internal.getSignatures, + status: transportModule.internal.status, + postBlock: transportModule.internal.postBlock, + postSignatures: transportModule.internal.postSignatures, + postTransactions: transportModule.internal.postTransactions + }); +} + +module.exports = TransportWSApi; diff --git a/app.js b/app.js index 9ed7aea2014..8e0d46e34f4 100644 --- a/app.js +++ b/app.js @@ -24,20 +24,22 @@ */ var async = require('async'); -var checkIpInList = require('./helpers/checkIpInList.js'); var extend = require('extend'); var fs = require('fs'); +var https = require('https'); +var path = require('path'); +var SocketCluster = require('socketcluster').SocketCluster; +var util = require('util'); 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 path = require('path'); -var program = require('commander'); +var workersController = require('./workersController'); +var wsRPC = require('./api/ws/rpc/wsRPC').wsRPC; + +var AppConfig = require('./helpers/config.js'); +var git = require('./helpers/git.js'); var httpApi = require('./helpers/httpApi.js'); var Sequence = require('./helpers/sequence.js'); -var util = require('util'); var z_schema = require('./helpers/z_schema.js'); process.stdin.resume(); @@ -55,57 +57,11 @@ if (typeof gc !== 'undefined') { }, 60000); } -program - .version(packageJson.version) - .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('-l, --log ', 'log level') - .option('-s, --snapshot ', 'verify snapshot') - .parse(process.argv); - /** * @property {object} - The default list of configuration options. Can be updated by CLI. * @default 'config.json' */ -var appConfig = require('./helpers/config.js')(program.config); - -if (program.port) { - appConfig.port = program.port; -} - -if (program.address) { - appConfig.address = program.address; -} - -if (program.peers) { - if (typeof program.peers === 'string') { - appConfig.peers.list = program.peers.split(',').map(function (peer) { - peer = peer.split(':'); - return { - ip: peer.shift(), - port: peer.shift() || appConfig.port - }; - }); - } else { - appConfig.peers.list = []; - } -} - -if (program.log) { - appConfig.consoleLogLevel = program.log; -} - -if (program.snapshot) { - appConfig.loading.snapshot = Math.abs( - Math.floor(program.snapshot) - ); -} - -if (process.env.NODE_ENV === 'test') { - appConfig.coverage = true; -} +var appConfig = AppConfig(require('./package.json')); // Define top endpoint availability process.env.TOP = appConfig.topAccounts; @@ -148,7 +104,7 @@ var config = { peers: { http: './api/http/peers.js' }, signatures: { http: './api/http/signatures.js' }, transactions: { http: './api/http/transactions.js' }, - transport: { http: './api/http/transport.js' } + transport: { ws: './api/ws/transport.js' } } }; @@ -281,6 +237,58 @@ d.run(function () { }); }], + webSocket: ['config', 'connect', 'logger', 'network', function (scope, cb) { + var webSocketConfig = { + workers: scope.config.wsWorkers, + port: scope.config.port, + wsEngine: 'uws', + appName: 'lisk', + workerController: workersController.path, + perMessageDeflate: false, + secretKey: 'liskSecretKey', + pingInterval: 5000, + // How many milliseconds to wait without receiving a ping + // before closing the socket + pingTimeout: 60000, + // Maximum amount of milliseconds to wait before force-killing + // a process after it was passed a 'SIGTERM' or 'SIGUSR2' signal + processTermTimeout: 10000, + logLevel: 0 + }; + + if (scope.config.ssl.enabled) { + extend(webSocketConfig, { + protocol: 'https', + // This is the same as the object provided to Node.js's https server + protocolOptions: { + key: fs.readFileSync(scope.config.ssl.options.key), + cert: fs.readFileSync(scope.config.ssl.options.cert), + ciphers: 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:' + 'ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:' + '!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA' + } + }); + } + + var childProcessOptions = { + version: scope.config.version, + minVersion: scope.config.minVersion, + nethash: scope.config.nethash, + port: scope.config.port, + nonce: scope.config.nonce + }; + + var socketCluster = new SocketCluster(webSocketConfig); + + var MasterWAMPServer = require('wamp-socket-cluster/MasterWAMPServer'); + + scope.network.app.rpc = wsRPC.setServer(new MasterWAMPServer(socketCluster, childProcessOptions)); + + socketCluster.on('ready', function (err, result) { + scope.logger.info('Socket Cluster ready for incoming connections'); + cb(); + }); + + }], + dbSequence: ['logger', function (scope, cb) { var sequence = new Sequence({ onWarning: function (current, limit) { @@ -323,7 +331,7 @@ d.run(function () { var queryParser = require('express-query-int'); var randomString = require('randomstring'); - scope.nonce = randomString.generate(16); + scope.config.nonce = randomString.generate(16); scope.network.app.use(require('express-domain-middleware')); scope.network.app.use(bodyParser.raw({limit: '2mb'})); scope.network.app.use(bodyParser.urlencoded({extended: true, limit: '2mb', parameterLimit: 5000})); @@ -474,7 +482,7 @@ d.run(function () { * at leats will contain the required elements. * @param {nodeStyleCallback} cb - Callback function with resulted load. */ - modules: ['network', 'connect', 'config', 'logger', 'bus', 'sequence', 'dbSequence', 'balancesSequence', 'db', 'logic', 'cache', function (scope, cb) { + modules: ['network', 'connect', 'webSocket', 'config', 'logger', 'bus', 'sequence', 'dbSequence', 'balancesSequence', 'db', 'logic', 'cache', function (scope, cb) { var tasks = {}; @@ -508,7 +516,7 @@ d.run(function () { * at leats will contain the required elements. * @param {function} cb - Callback function. */ - api: ['modules', 'logger', 'network', function (scope, cb) { + api: ['modules', 'logger', 'network', 'webSocket', function (scope, cb) { Object.keys(config.api).forEach(function (moduleName) { Object.keys(config.api[moduleName]).forEach(function (protocol) { var apiEndpointPath = config.api[moduleName][protocol]; @@ -516,7 +524,7 @@ d.run(function () { var ApiEndpoint = require(apiEndpointPath); new ApiEndpoint(scope.modules[moduleName], scope.network.app, scope.logger, scope.modules.cache); } catch (e) { - scope.logger.error('Unable to load API endpoint for ' + moduleName + ' of ' + protocol, e); + scope.logger.error('Unable to load API endpoint for ' + moduleName + ' of ' + protocol, e.message); } }); }); @@ -541,8 +549,8 @@ d.run(function () { * @param {nodeStyleCallback} cb - Callback function with `scope.network`. */ listen: ['ready', function (scope, cb) { - scope.network.server.listen(scope.config.port, scope.config.address, function (err) { - scope.logger.info('Lisk started: ' + scope.config.address + ':' + scope.config.port); + scope.network.server.listen(scope.config.httpPort, scope.config.address, function (err) { + scope.logger.info('Lisk started: ' + scope.config.address + ':' + scope.config.httpPort); if (!err) { if (scope.config.ssl.enabled) { diff --git a/config.json b/config.json index cc65da49294..da6e7860599 100644 --- a/config.json +++ b/config.json @@ -1,136 +1,138 @@ { - "port": 8000, - "address": "0.0.0.0", - "version": "0.9.2", - "minVersion": ">=0.5.0", - "fileLogLevel": "info", - "logFileName": "logs/lisk.log", - "consoleLogLevel": "none", - "trustProxy": false, - "topAccounts": false, - "cacheEnabled": false, - "db": { - "host": "localhost", - "port": 5432, - "database": "lisk_main", - "user": "", - "password": "password", - "poolSize": 95, - "poolIdleTimeout": 30000, - "reapIntervalMillis": 1000, - "logEvents": [ - "error" - ] + "port": 8001, + "httpPort": 8000, + "address": "0.0.0.0", + "version": "0.9.1", + "minVersion": ">=0.5.0", + "fileLogLevel": "info", + "logFileName": "logs/lisk.log", + "consoleLogLevel": "none", + "trustProxy": false, + "topAccounts": false, + "cacheEnabled": false, + "wsWorkers": 1, + "db": { + "host": "localhost", + "port": 5432, + "database": "lisk_main", + "user": "", + "password": "password", + "poolSize": 95, + "poolIdleTimeout": 30000, + "reapIntervalMillis": 1000, + "logEvents": [ + "error" + ] + }, + "redis": { + "host": "127.0.0.1", + "port": 6380, + "db": 0, + "password": null + }, + "api": { + "enabled": true, + "access": { + "public": false, + "whiteList": ["127.0.0.1"] }, - "redis": { - "host": "127.0.0.1", - "port": 6380, - "db": 0, - "password": null + "options": { + "limits": { + "max": 0, + "delayMs": 0, + "delayAfter": 0, + "windowMs": 60000 + } + } + }, + "peers": { + "enabled": true, + "list": [ + { + "ip": "83.136.254.92", + "port": 8000 + }, + { + "ip": "83.136.249.76", + "port": 8000 + }, + { + "ip": "94.237.28.66", + "port": 8000 + }, + { + "ip": "94.237.24.199", + "port": 8000 + }, + { + "ip": "209.50.49.23", + "port": 8000 + }, + { + "ip": "209.50.49.40", + "port": 8000 + }, + { + "ip": "94.237.64.70", + "port": 8000 + }, + { + "ip": "94.237.64.73", + "port": 8000 + }, + { + "ip": "94.237.40.140", + "port": 8000 + }, + { + "ip": "94.237.40.141", + "port": 8000 + } + ], + "access": { + "blackList": [] }, - "api": { - "enabled": true, - "access": { - "public": false, - "whiteList": ["127.0.0.1"] - }, - "options": { - "limits": { - "max": 0, - "delayMs": 0, - "delayAfter": 0, - "windowMs": 60000 - } - } - }, - "peers": { - "enabled": true, - "list": [ - { - "ip": "83.136.254.92", - "port": 8000 - }, - { - "ip": "83.136.249.76", - "port": 8000 - }, - { - "ip": "94.237.28.66", - "port": 8000 - }, - { - "ip": "94.237.24.199", - "port": 8000 - }, - { - "ip": "209.50.49.23", - "port": 8000 - }, - { - "ip": "209.50.49.40", - "port": 8000 - }, - { - "ip": "94.237.64.70", - "port": 8000 - }, - { - "ip": "94.237.64.73", - "port": 8000 - }, - { - "ip": "94.237.40.140", - "port": 8000 - }, - { - "ip": "94.237.40.141", - "port": 8000 - } - ], - "access": { - "blackList": [] - }, - "options": { - "limits": { - "max": 0, - "delayMs": 0, - "delayAfter": 0, - "windowMs": 60000 - }, - "timeout": 5000 - } - }, - "broadcasts": { - "broadcastInterval": 5000, - "broadcastLimit": 20, - "parallelLimit": 20, - "releaseLimit": 25, - "relayLimit": 2 - }, - "transactions": { - "maxTxsPerQueue": 1000 - }, - "forging": { - "force": false, - "secret": [], - "access": { - "whiteList": [ - "127.0.0.1" - ] - } - }, - "loading": { - "verifyOnLoading": false, - "loadPerIteration": 5000 - }, - "ssl": { - "enabled": false, - "options": { - "port": 443, - "address": "0.0.0.0", - "key": "./ssl/lisk.key", - "cert": "./ssl/lisk.crt" - } - }, - "nethash": "ed14889723f24ecc54871d058d98ce91ff2f973192075c0155ba2b7b70ad2511" + "options": { + "limits": { + "max": 0, + "delayMs": 0, + "delayAfter": 0, + "windowMs": 60000 + }, + "timeout": 5000 + } + }, + "broadcasts": { + "broadcastInterval": 5000, + "broadcastLimit": 20, + "parallelLimit": 20, + "releaseLimit": 25, + "relayLimit": 2 + }, + "transactions": { + "maxTxsPerQueue": 1000 + }, + "forging": { + "force": false, + "secret": [], + "access": { + "whiteList": [ + "127.0.0.1" + ] + } + }, + "loading": { + "verifyOnLoading": false, + "loadPerIteration": 5000 + }, + "ssl": { + "enabled": false, + "options": { + "port": 443, + "address": "0.0.0.0", + "key": "./ssl/lisk.key", + "cert": "./ssl/lisk.crt" + } + }, + "nethash": "ed14889723f24ecc54871d058d98ce91ff2f973192075c0155ba2b7b70ad2511" } diff --git a/helpers/config.js b/helpers/config.js index c7efa6534a8..11cd54a797d 100644 --- a/helpers/config.js +++ b/helpers/config.js @@ -2,6 +2,7 @@ var fs = require('fs'); var path = require('path'); +var program = require('commander'); var z_schema = require('./z_schema.js'); var configSchema = require('../schema/config.js'); var constants = require('../helpers/constants.js'); @@ -10,35 +11,94 @@ var constants = require('../helpers/constants.js'); * Loads config.json file * @memberof module:helpers * @implements {validateForce} - * @param {string} configPath + * @param {Object} packageJson * @returns {Object} configData */ -function Config (configPath) { - var configData = fs.readFileSync(path.resolve(process.cwd(), (configPath || 'config.json')), 'utf8'); +function Config (packageJson) { - if (!configData.length) { + program + .version(packageJson.version) + .option('-c, --config ', 'config file path') + .option('-p, --port ', 'listening port number') + .option('-h, --http-port ', 'listening HTTP port number') + .option('-d, --database ', 'database name') + .option('-a, --address ', 'listening host name or ip') + .option('-x, --peers [peers...]', 'peers list') + .option('-l, --log ', 'log level') + .option('-s, --snapshot ', 'verify snapshot') + .parse(process.argv); + + var configPath = program.config; + var appConfig = fs.readFileSync(path.resolve(process.cwd(), (configPath || 'config.json')), 'utf8'); + + if (!appConfig.length) { console.log('Failed to read config file'); process.exit(1); } else { - configData = JSON.parse(configData); + appConfig = JSON.parse(appConfig); } var validator = new z_schema(); - var valid = validator.validate(configData, configSchema.config); + var valid = validator.validate(appConfig, configSchema.config); if (!valid) { console.log('Failed to validate config data', validator.getLastErrors()); process.exit(1); } else { - validateForce(configData); - return configData; + validateForce(appConfig); + + if (program.port) { + appConfig.port = program.port; + } + + if (program.httpPort) { + appConfig.httpPort = program.httpPort; + } + + if (program.address) { + appConfig.address = program.address; + } + + if (program.database) { + appConfig.db.database = program.database; + } + + if (program.peers) { + if (typeof program.peers === 'string') { + appConfig.peers.list = program.peers.split(',').map(function (peer) { + peer = peer.split(':'); + return { + ip: peer.shift(), + port: peer.shift() || appConfig.port + }; + }); + } else { + appConfig.peers.list = []; + } + } + + if (program.log) { + appConfig.consoleLogLevel = program.log; + } + + if (program.snapshot) { + appConfig.loading.snapshot = Math.abs( + Math.floor(program.snapshot) + ); + } + + if (process.env.NODE_ENV === 'test') { + appConfig.coverage = true; + } + + return appConfig; } } /** * Validates nethash value from constants and sets forging force to false if any. * @private - * @param {Object} configData + * @param {Object} configData */ function validateForce (configData) { if (configData.forging.force) { diff --git a/helpers/constants.js b/helpers/constants.js index d79dc65172c..2678ed45c96 100644 --- a/helpers/constants.js +++ b/helpers/constants.js @@ -40,7 +40,7 @@ * @property {number} totalAmount * @property {number} unconfirmedTransactionTimeOut - 1080 blocks */ -module.exports = { +var constants = { activeDelegates: 101, addressLength: 208, blockHeaderLength: 248, @@ -93,3 +93,5 @@ module.exports = { totalAmount: 10000000000000000, unconfirmedTransactionTimeOut: 10800 // 1080 blocks }; + +module.exports = constants; diff --git a/helpers/httpApi.js b/helpers/httpApi.js index 40976a4ddc0..ce1854b9c99 100644 --- a/helpers/httpApi.js +++ b/helpers/httpApi.js @@ -37,6 +37,7 @@ var middleware = { errorLogger: function (logger, err, req, res, next) { if (!err) { return next(); } logger.error('API error ' + req.url, err.message); + console.trace(err); res.status(500).send({success: false, error: 'API error: ' + err.message}); }, diff --git a/helpers/json-schema/validator.js b/helpers/json-schema/validator.js index 6677f610c79..859678b936b 100644 --- a/helpers/json-schema/validator.js +++ b/helpers/json-schema/validator.js @@ -27,16 +27,16 @@ JsonSchema.validate = Validator.validate; JsonSchema.addRule('type', { validate : function (accept, value) { switch (accept) { - case 'array': - return Array.isArray(value); - case 'object': - return typeof value === 'object' && value !== null; - case 'null': - return value === null; - case 'integer': - return typeof value === 'number'; - default: - return typeof value === accept; + case 'array': + return Array.isArray(value); + case 'object': + return typeof value === 'object' && value !== null; + case 'null': + return value === null; + case 'integer': + return typeof value === 'number'; + default: + return typeof value === accept; } } }); diff --git a/helpers/milestoneBlocks.js b/helpers/milestoneBlocks.js deleted file mode 100644 index 8eb077ad7d1..00000000000 --- a/helpers/milestoneBlocks.js +++ /dev/null @@ -1,4 +0,0 @@ -'use strict'; - -module.exports = { -}; diff --git a/helpers/peersManager.js b/helpers/peersManager.js new file mode 100644 index 00000000000..d0cc857ab14 --- /dev/null +++ b/helpers/peersManager.js @@ -0,0 +1,75 @@ +'use strict'; + +function PeersManager () { + + this.peers = {}; + this.addressToNonceMap = {}; + this.nonceToAddressMap = {}; +} + +PeersManager.prototype.add = function (peer) { + // 1. do not add peers without address + // 2. prevent changing address by the peer with same nonce + if (!peer.string || this.nonceToAddressMap[peer.nonce] && peer.string !== this.nonceToAddressMap[peer.nonce]) { + return false; + } + if (this.peers[peer.string]) { + return this.update(peer); + } + this.peers[peer.string] = peer; + this.addressToNonceMap[peer.string] = peer.nonce; + if (peer.nonce) { + this.nonceToAddressMap[peer.nonce] = peer.string; + } + return true; +}; + +PeersManager.prototype.remove = function (peer) { + this.nonceToAddressMap[peer.nonce] = null; + delete this.nonceToAddressMap[peer.nonce]; + + this.addressToNonceMap[peer.string] = null; + delete this.addressToNonceMap[peer.string]; + + this.peers[peer.string] = null; + delete this.peers[peer.string]; +}; + +PeersManager.prototype.update = function (peer) { + var oldNonce = this.addressToNonceMap[peer.string]; + var oldAddress = this.nonceToAddressMap[oldNonce]; + if (oldNonce) { + this.nonceToAddressMap[oldNonce] = null; + delete this.nonceToAddressMap[oldNonce]; + } + if (oldAddress) { + this.addressToNonceMap[oldAddress] = null; + delete this.addressToNonceMap[oldAddress]; + + this.peers[oldAddress] = null; + delete this.peers[oldAddress]; + } + this.add(peer); +}; + +PeersManager.prototype.getAll = function () { + return this.peers; +}; + +PeersManager.prototype.getByAddress = function (address) { + return this.peers[address]; +}; + +PeersManager.prototype.getByNonce = function (nonce) { + return this.peers[this.nonceToAddressMap[nonce]]; +}; + +PeersManager.prototype.getNonce = function (address) { + return this.addressToNonceMap[address]; +}; + +PeersManager.prototype.getAddress = function (nonce) { + return this.nonceToAddressMap[nonce]; +}; + +module.exports = new PeersManager(); diff --git a/helpers/promiseDefer.js b/helpers/promiseDefer.js new file mode 100644 index 00000000000..e1b836f26f9 --- /dev/null +++ b/helpers/promiseDefer.js @@ -0,0 +1,19 @@ +'use strict'; + +var Promise = require('bluebird'); + +function PromiseDefer () { + var resolve, reject; + var promise = new Promise(function (__resolve, __reject) { + resolve = __resolve; + reject = __reject; + }); + + return { + resolve: resolve, + reject: reject, + promise: promise + }; +} + +module.exports = PromiseDefer; diff --git a/helpers/wsApi.js b/helpers/wsApi.js new file mode 100644 index 00000000000..67356baf438 --- /dev/null +++ b/helpers/wsApi.js @@ -0,0 +1,84 @@ +'use strict'; + +var _ = require('lodash'); +var url = require('url'); +var checkIpInList = require('./checkIpInList'); +var Z_schema = require('../helpers/z_schema.js'); +var schema = require('../schema/transport.js'); +var Peer = require('../logic/peer.js'); +var constants = require('./constants'); + +var z_schema = new Z_schema(); + +var middleware = { + + Handshake: function (system) { + return function (headers, cb) { + headers = headers || {}; + var peer = new Peer(headers); + headers.state = Peer.STATE.CONNECTED; + + z_schema.validate(headers, schema.headers, function (error) { + headers = peer.applyHeaders(headers); + + if (error) { + return setImmediate(cb, { + success: false, + error: error, + code: 'EHEADERS' + }, peer); + } + + if (!system.nonceCompatible(headers.nonce)) { + return setImmediate(cb, { + success: false, + message: 'Request is made by itself', + expected: 'different than ' + system.getNonce(), + received: headers.nonce, + code: 'ENONCE' + }, peer); + } + + if (!system.networkCompatible(headers.nethash)) { + return setImmediate(cb, { + success: false, + message: 'Request is made on the wrong network', + expected: system.getNethash(), + received: headers.nethash, + code: 'ENETHASH' + }, peer); + } + + if (!system.versionCompatible(headers.version)) { + return setImmediate(cb, { + success: false, + message: 'Request is made from incompatible version', + expected: system.getMinVersion(), + received: headers.version, + peer: peer, + code: 'EVERSION' + }, peer); + } + + return setImmediate(cb, null, peer); + }); + }; + } +}; + +var extractHeaders = function (request) { + var headers = _.get(url.parse(request.url, true), 'query', {}); + if (!headers) { + throw new Error('No headers specified'); + } + + headers.ip = request.remoteAddress.split(':').pop(); + headers.port = parseInt(headers.port); + + return headers; +}; + +module.exports = { + middleware: middleware, + extractHeaders: extractHeaders +}; diff --git a/logger.js b/logger.js index ca122a23f1a..f6f921991d9 100644 --- a/logger.js +++ b/logger.js @@ -3,6 +3,9 @@ var strftime = require('strftime').utc(); var fs = require('fs'); var util = require('util'); +var child_process = require('child_process'); +var path = require('path'); + require('colors'); module.exports = function (config) { @@ -30,10 +33,11 @@ module.exports = function (config) { fatal: 'FTL' }; - config.filename = config.filename || __dirname + '/logs.log'; + config.filename = __dirname + '/' + (config.filename || 'logs.log'); config.errorLevel = config.errorLevel || 'log'; + child_process.execSync('mkdir -p ' + path.dirname(config.filename)); var log_file = fs.createWriteStream(config.filename, {flags: 'a'}); exports.setLevel = function (errorLevel) { diff --git a/logic/account.js b/logic/account.js index 9c80f154097..210785b13f4 100644 --- a/logic/account.js +++ b/logic/account.js @@ -669,119 +669,119 @@ Account.prototype.merge = function (address, diff, cb) { if (diff[value] !== undefined) { var trueValue = diff[value]; switch (self.conv[value]) { - case String: - update[value] = trueValue; - break; - case Number: - if (isNaN(trueValue) || trueValue === Infinity) { - console.log(diff); - return setImmediate(cb, 'Encountered unsane number: ' + trueValue); - } else if (Math.abs(trueValue) === trueValue && trueValue !== 0) { - update.$inc = update.$inc || {}; - update.$inc[value] = Math.floor(trueValue); - if (value === 'balance') { - round.push({ - query: 'INSERT INTO mem_round ("address", "amount", "delegate", "blockId", "round") SELECT ${address}, (${amount})::bigint, "dependentId", ${blockId}, ${round} FROM mem_accounts2delegates WHERE "accountId" = ${address};', - values: { - address: address, - amount: trueValue, - blockId: diff.blockId, - round: diff.round - } - }); - } - } else if (trueValue < 0) { - update.$dec = update.$dec || {}; - update.$dec[value] = Math.floor(Math.abs(trueValue)); - // If decrementing u_balance on account - if (update.$dec.u_balance) { + case String: + update[value] = trueValue; + break; + case Number: + if (isNaN(trueValue) || trueValue === Infinity) { + console.log(diff); + return setImmediate(cb, 'Encountered unsane number: ' + trueValue); + } else if (Math.abs(trueValue) === trueValue && trueValue !== 0) { + update.$inc = update.$inc || {}; + update.$inc[value] = Math.floor(trueValue); + if (value === 'balance') { + round.push({ + query: 'INSERT INTO mem_round ("address", "amount", "delegate", "blockId", "round") SELECT ${address}, (${amount})::bigint, "dependentId", ${blockId}, ${round} FROM mem_accounts2delegates WHERE "accountId" = ${address};', + values: { + address: address, + amount: trueValue, + blockId: diff.blockId, + round: diff.round + } + }); + } + } else if (trueValue < 0) { + update.$dec = update.$dec || {}; + update.$dec[value] = Math.floor(Math.abs(trueValue)); + // If decrementing u_balance on account + if (update.$dec.u_balance) { // Remove virginity and ensure marked columns become immutable - update.virgin = 0; - } - if (value === 'balance') { - round.push({ - query: 'INSERT INTO mem_round ("address", "amount", "delegate", "blockId", "round") SELECT ${address}, (${amount})::bigint, "dependentId", ${blockId}, ${round} FROM mem_accounts2delegates WHERE "accountId" = ${address};', - values: { - address: address, - amount: trueValue, - blockId: diff.blockId, - round: diff.round - } - }); - } - } - break; - case Array: - if (Object.prototype.toString.call(trueValue[0]) === '[object Object]') { - for (i = 0; i < trueValue.length; i++) { - val = trueValue[i]; - if (val.action === '-') { - delete val.action; - remove_object[value] = remove_object[value] || []; - remove_object[value].push(val); - } else if (val.action === '+') { - delete val.action; - insert_object[value] = insert_object[value] || []; - insert_object[value].push(val); - } else { - delete val.action; - insert_object[value] = insert_object[value] || []; - insert_object[value].push(val); + update.virgin = 0; + } + if (value === 'balance') { + round.push({ + query: 'INSERT INTO mem_round ("address", "amount", "delegate", "blockId", "round") SELECT ${address}, (${amount})::bigint, "dependentId", ${blockId}, ${round} FROM mem_accounts2delegates WHERE "accountId" = ${address};', + values: { + address: address, + amount: trueValue, + blockId: diff.blockId, + round: diff.round + } + }); } } - } else { - for (i = 0; i < trueValue.length; i++) { - var math = trueValue[i][0]; - val = null; - if (math === '-') { - val = trueValue[i].slice(1); - remove[value] = remove[value] || []; - remove[value].push(val); - if (value === 'delegates') { - round.push({ - query: 'INSERT INTO mem_round ("address", "amount", "delegate", "blockId", "round") SELECT ${address}, (-balance)::bigint, ${delegate}, ${blockId}, ${round} FROM mem_accounts WHERE address = ${address};', - values: { - address: address, - delegate: val, - blockId: diff.blockId, - round: diff.round - } - }); - } - } else if (math === '+') { - val = trueValue[i].slice(1); - insert[value] = insert[value] || []; - insert[value].push(val); - if (value === 'delegates') { - round.push({ - query: 'INSERT INTO mem_round ("address", "amount", "delegate", "blockId", "round") SELECT ${address}, (balance)::bigint, ${delegate}, ${blockId}, ${round} FROM mem_accounts WHERE address = ${address};', - values: { - address: address, - delegate: val, - blockId: diff.blockId, - round: diff.round - } - }); - } - } else { + break; + case Array: + if (Object.prototype.toString.call(trueValue[0]) === '[object Object]') { + for (i = 0; i < trueValue.length; i++) { val = trueValue[i]; - insert[value] = insert[value] || []; - insert[value].push(val); - if (value === 'delegates') { - round.push({ - query: 'INSERT INTO mem_round ("address", "amount", "delegate", "blockId", "round") SELECT ${address}, (balance)::bigint, ${delegate}, ${blockId}, ${round} FROM mem_accounts WHERE address = ${address};', - values: { - address: address, - delegate: val, - blockId: diff.blockId, - round: diff.round - } - }); + if (val.action === '-') { + delete val.action; + remove_object[value] = remove_object[value] || []; + remove_object[value].push(val); + } else if (val.action === '+') { + delete val.action; + insert_object[value] = insert_object[value] || []; + insert_object[value].push(val); + } else { + delete val.action; + insert_object[value] = insert_object[value] || []; + insert_object[value].push(val); + } + } + } else { + for (i = 0; i < trueValue.length; i++) { + var math = trueValue[i][0]; + val = null; + if (math === '-') { + val = trueValue[i].slice(1); + remove[value] = remove[value] || []; + remove[value].push(val); + if (value === 'delegates') { + round.push({ + query: 'INSERT INTO mem_round ("address", "amount", "delegate", "blockId", "round") SELECT ${address}, (-balance)::bigint, ${delegate}, ${blockId}, ${round} FROM mem_accounts WHERE address = ${address};', + values: { + address: address, + delegate: val, + blockId: diff.blockId, + round: diff.round + } + }); + } + } else if (math === '+') { + val = trueValue[i].slice(1); + insert[value] = insert[value] || []; + insert[value].push(val); + if (value === 'delegates') { + round.push({ + query: 'INSERT INTO mem_round ("address", "amount", "delegate", "blockId", "round") SELECT ${address}, (balance)::bigint, ${delegate}, ${blockId}, ${round} FROM mem_accounts WHERE address = ${address};', + values: { + address: address, + delegate: val, + blockId: diff.blockId, + round: diff.round + } + }); + } + } else { + val = trueValue[i]; + insert[value] = insert[value] || []; + insert[value].push(val); + if (value === 'delegates') { + round.push({ + query: 'INSERT INTO mem_round ("address", "amount", "delegate", "blockId", "round") SELECT ${address}, (balance)::bigint, ${delegate}, ${blockId}, ${round} FROM mem_accounts WHERE address = ${address};', + values: { + address: address, + delegate: val, + blockId: diff.blockId, + round: diff.round + } + }); + } } } } - } - break; + break; } } }); diff --git a/logic/broadcaster.js b/logic/broadcaster.js index a66ba9ed869..fbf42a9f599 100644 --- a/logic/broadcaster.js +++ b/logic/broadcaster.js @@ -52,15 +52,13 @@ function Broadcaster (broadcasts, force, peers, transaction, logger) { // Broadcast routes self.routes = [{ - path: '/transactions', + path: 'postTransactions', collection: 'transactions', - object: 'transaction', - method: 'POST' + object: 'transaction' }, { - path: '/signatures', + path: 'postSignatures', collection: 'signatures', - object: 'signature', - method: 'POST' + object: 'signature' }]; // Broadcaster timer @@ -131,7 +129,7 @@ Broadcaster.prototype.enqueue = function (params, options) { }; /** - * Gets peers and for each peer create it and broadcast. + * Gets peers and for each peer create it and broadcast. * @implements {getPeers} * @implements {library.logic.peers.create} * @param {Object} params @@ -140,6 +138,7 @@ Broadcaster.prototype.enqueue = function (params, options) { * @return {setImmediateCallback} err | peers */ Broadcaster.prototype.broadcast = function (params, options, cb) { + options.data.peer = library.logic.peers.me(); params.limit = params.limit || self.config.peerLimit; params.broadhash = params.broadhash || null; @@ -151,17 +150,13 @@ Broadcaster.prototype.broadcast = function (params, options, cb) { return setImmediate(waterCb, null, params.peers); } }, - function getFromPeer (peers, waterCb) { + function sendToPeer (peers, waterCb) { library.logger.debug('Begin broadcast', options); - - if (params.limit === self.config.peerLimit) { + if (params.limit === self.config.peerLimit) { peers = peers.slice(0, self.config.broadcastLimit); } - async.eachLimit(peers, self.config.parallelLimit, function (peer, eachLimitCb) { - peer = library.logic.peers.create(peer); - - modules.transport.getFromPeer(peer, options, function (err) { + peer.rpc[options.api](options.data, function (err, result) { if (err) { library.logger.debug('Failed to broadcast to peer: ' + peer.string, err); } @@ -271,7 +266,7 @@ __private.squashQueue = function (broadcasts) { }).filter(Boolean); squashed.push({ - options: { api: route.path, data: data, method: route.method }, + options: { api: route.path, data: data }, immediate: false }); } diff --git a/logic/peer.js b/logic/peer.js index 81f5b248a0b..b365593d004 100644 --- a/logic/peer.js +++ b/logic/peer.js @@ -2,6 +2,7 @@ var _ = require('lodash'); var ip = require('ip'); +var wsRPC = require('../api/ws/rpc/wsRPC').wsRPC; /** * Creates a peer. @@ -14,6 +15,15 @@ var ip = require('ip'); */ // Constructor function Peer (peer) { + + Object.defineProperties(this, { + rpc: { + get: function () { + return wsRPC.getClientRPCStub(this.ip, this.port); + }.bind(this) + } + }); + return this.accept(peer || {}); } @@ -42,12 +52,14 @@ Peer.prototype.properties = [ 'height', 'clock', 'updated', - 'nonce' + 'nonce', + 'httpPort' ]; Peer.prototype.immutable = [ 'ip', 'port', + 'httpPort', 'string' ]; @@ -179,6 +191,7 @@ Peer.prototype.object = function () { } }); + delete copy.rpc; return copy; }; diff --git a/logic/peers.js b/logic/peers.js index 3f964d13be3..c9d6b574fb2 100644 --- a/logic/peers.js +++ b/logic/peers.js @@ -4,12 +4,15 @@ var _ = require('lodash'); var async = require('async'); var Peer = require('../logic/peer.js'); var schema = require('../schema/peers.js'); +var System = require('../modules/system.js'); +var peersManager = require('../helpers/peersManager.js'); // Private fields var __private = {}; var self; var library; var modules; + /** * Initializes library. * @memberof module:peers @@ -25,10 +28,19 @@ function Peers (logger, cb) { logger: logger }; self = this; - __private.peers = {}; + __private.me = null; + + this.peersManager = peersManager; + return setImmediate(cb, null, this); } +Peers.prototype.me = function () { + var me = _.extend(System.getHeaders(), {state: Peer.STATE.CONNECTED}); + delete me.ip; + return me; +}; + /** * Returns a peer instance. * @param {peer} peer @@ -49,7 +61,7 @@ Peers.prototype.create = function (peer) { */ Peers.prototype.exists = function (peer) { peer = self.create(peer); - return !!__private.peers[peer.string]; + return !!self.peersManager.getByAddress(peer.string); }; /** @@ -59,10 +71,10 @@ Peers.prototype.exists = function (peer) { */ Peers.prototype.get = function (peer) { if (typeof peer === 'string') { - return __private.peers[peer]; + return self.peersManager.getByAddress(peer); } else { peer = self.create(peer); - return __private.peers[peer.string]; + return self.peersManager.getByAddress(peer.string); } }; @@ -77,8 +89,10 @@ Peers.prototype.upsert = function (peer, insertOnly) { var insert = function (peer) { if (!_.isEmpty(modules.peers.acceptable([peer]))) { peer.updated = Date.now(); - __private.peers[peer.string] = peer; - library.logger.debug('Inserted new peer', peer.string); + if (self.peersManager.add(peer)) { + return library.logger.debug('Inserted new peer', peer.string); + } + library.logger.debug('Cannot insert peer (nonce exists / empty address field)', peer.string); } else { library.logger.debug('Rejecting unacceptable peer', peer.string); } @@ -90,12 +104,12 @@ Peers.prototype.upsert = function (peer, insertOnly) { var diff = {}; _.each(peer, function (value, key) { - if (key !== 'updated' && __private.peers[peer.string][key] !== value) { + if (key !== 'updated' && self.peersManager.getByAddress(peer.string)[key] !== value) { diff[key] = value; } }); - __private.peers[peer.string].update(peer); + self.peersManager.getByAddress(peer.string).update(peer); if (Object.keys(diff).length) { library.logger.debug('Updated peer ' + peer.string, diff); @@ -105,12 +119,12 @@ Peers.prototype.upsert = function (peer, insertOnly) { }; peer = self.create(peer); - + peer.string = peer.string || self.peersManager.getAddress(peer.nonce); + if (!peer.string) { - library.logger.warn('Upsert invalid peer rejected', {peer: peer}); + console.trace('Upsert invalid peer rejected', {peer: peer}); return false; } - // Performing insert or update if (self.exists(peer)) { // Skip update if insert-only is forced @@ -131,7 +145,7 @@ Peers.prototype.upsert = function (peer, insertOnly) { _.each(__private.peers, function (peer, index) { ++cnt_total; - if (peer.state === 2) { + if (peer.state === Peer.STATE.CONNECTED) { ++cnt_active; } if (!peer.height) { @@ -157,9 +171,8 @@ Peers.prototype.remove = function (peer) { // Remove peer if exists if (self.exists(peer)) { library.logger.info('Removed peer', peer.string); - library.logger.debug('Removed peer', {peer: __private.peers[peer.string]}); - __private.peers[peer.string] = null; // Possible memory leak prevention - delete __private.peers[peer.string]; + library.logger.debug('Removed peer', {peer: peer}); + self.peersManager.remove(peer); return true; } else { library.logger.debug('Failed to remove peer', {err: 'AREMOVED', peer: peer}); @@ -174,9 +187,9 @@ Peers.prototype.remove = function (peer) { */ Peers.prototype.list = function (normalize) { if (normalize) { - return Object.keys(__private.peers).map(function (key) { return __private.peers[key].object(); }); + return Object.keys(self.peersManager.addressToNonceMap).map(function (key) { return self.peersManager.getByAddress(key).object(); }); } else { - return Object.keys(__private.peers).map(function (key) { return __private.peers[key]; }); + return Object.keys(self.peersManager.addressToNonceMap).map(function (key) { return self.peersManager.getByAddress(key); }); } }; diff --git a/logic/transaction.js b/logic/transaction.js index 5e0e32e92d5..27dd148e61d 100644 --- a/logic/transaction.js +++ b/logic/transaction.js @@ -1058,6 +1058,9 @@ Transaction.prototype.schema = { * @throws {string} error message */ Transaction.prototype.objectNormalize = function (trs) { + if (_.isEmpty(trs)) { + throw 'Empty trs passed'; + } if (!__private.types[trs.type]) { throw 'Unknown transaction type ' + trs.type; } diff --git a/logic/vote.js b/logic/vote.js index 2edf208381f..a52f00643ea 100644 --- a/logic/vote.js +++ b/logic/vote.js @@ -358,8 +358,6 @@ Vote.prototype.objectNormalize = function (trs) { * @return {null|votes} votes object */ Vote.prototype.dbRead = function (raw) { - // console.log(raw.v_votes); - if (!raw.v_votes) { return null; } else { diff --git a/modules/blocks/process.js b/modules/blocks/process.js index f3a386490f5..c22a533cf7c 100644 --- a/modules/blocks/process.js +++ b/modules/blocks/process.js @@ -70,47 +70,44 @@ Process.prototype.getCommonBlock = function (peer, height, cb) { }, function (res, waterCb) { var ids = res.ids; - // Perform request to supplied remote peer - modules.transport.getFromPeer(peer, { - api: '/blocks/common?ids=' + ids, - method: 'GET' - }, function (err, res) { - if (err || res.body.error) { - return setImmediate(waterCb, err || res.body.error.toString()); - } else if (!res.body.common) { + peer = library.logic.peers.create(peer); + peer.rpc.blocksCommon({ids: ids}, function (err, res) { + if (err) { + return setImmediate(waterCb, err); + } else if (!res.common) { // FIXME: Need better checking here, is base on 'common' property enough? comparisionFailed = true; return setImmediate(waterCb, ['Chain comparison failed with peer:', peer.string, 'using ids:', ids].join(' ')); } else { - return setImmediate(waterCb, null, res); + return setImmediate(waterCb, null, res.common); } }); }, - function (res, waterCb) { + function (common, waterCb) { // Validate remote peer response via schema - library.schema.validate(res.body.common, schema.getCommonBlock, function (err) { + library.schema.validate(common, schema.getCommonBlock, function (err) { if (err) { return setImmediate(waterCb, err[0].message); } else { - return setImmediate(waterCb, null, res); + return setImmediate(waterCb, null, common); } }); }, - function (res, waterCb) { + function (common, waterCb) { // Check that block with ID, previousBlock and height exists in database - library.db.query(sql.getCommonBlock(res.body.common.previousBlock), { - id: res.body.common.id, - previousBlock: res.body.common.previousBlock, - height: res.body.common.height + library.db.query(sql.getCommonBlock(common.previousBlock), { + id: common.id, + previousBlock: common.previousBlock, + height: common.height }).then(function (rows) { if (!rows.length || !rows[0].count) { // Block doesn't exists - comparison failed comparisionFailed = true; - return setImmediate(waterCb, ['Chain comparison failed with peer:', peer.string, 'using block:', JSON.stringify(res.body.common)].join(' ')); + return setImmediate(waterCb, ['Chain comparison failed with peer:', peer.string, 'using block:', JSON.stringify(common)].join(' ')); } else { // Block exists - it's common between our node and remote peer - return setImmediate(waterCb, null, res.body.common); + return setImmediate(waterCb, null, common); } }).catch(function (err) { // SQL error occurred @@ -150,7 +147,7 @@ Process.prototype.loadBlocksOffset = function (limit, offset, verify, cb) { var params = { limit: newLimit, offset: offset || 0 }; library.logger.debug('Loading blocks offset', {limit: limit, offset: offset, verify: verify}); - // Execute in sequence via dbSequence + // Execute in sequence via dbSequence] library.dbSequence.add(function (cb) { // Loads full blocks from database // FIXME: Weird logic in that SQL query, also ordering used can be performance bottleneck - to rewrite @@ -209,29 +206,22 @@ Process.prototype.loadBlocksOffset = function (limit, offset, verify, cb) { * @return {Object} cb.lastValidBlock Normalized new last block */ Process.prototype.loadBlocksFromPeer = function (peer, cb) { - // Set current last block as last valid block var lastValidBlock = modules.blocks.lastBlock.get(); - // Normalize peer peer = library.logic.peers.create(peer); library.logger.info('Loading blocks from: ' + peer.string); function getFromPeer (seriesCb) { - // Ask remote peer for blocks - modules.transport.getFromPeer(peer, { - method: 'GET', - api: '/blocks?lastBlockId=' + lastValidBlock.id - }, function (err, res) { - err = err || res.body.error; + peer.rpc.blocks({lastBlockId: lastValidBlock.id, peer: library.logic.peers.me()}, function (err, res) { + err = err || res.error; if (err) { return setImmediate(seriesCb, err); } else { - return setImmediate(seriesCb, null, res.body.blocks); + return setImmediate(seriesCb, null, res.blocks); } }); } - // Validate remote peer response via schema function validateBlocks (blocks, seriesCb) { var report = library.schema.validate(blocks, schema.loadBlocksFromPeer); @@ -241,7 +231,6 @@ Process.prototype.loadBlocksFromPeer = function (peer, cb) { return setImmediate(seriesCb, null, blocks); } } - // Process all received blocks function processBlocks (blocks, seriesCb) { // Skip if ther is no blocks @@ -261,7 +250,6 @@ Process.prototype.loadBlocksFromPeer = function (peer, cb) { return setImmediate(seriesCb, err); }); } - // Process single block function processBlock (block, seriesCb) { // Start block processing - broadcast: false, saveBlock: true diff --git a/modules/delegates.js b/modules/delegates.js index 5aba1ff9004..992bfaa33d1 100644 --- a/modules/delegates.js +++ b/modules/delegates.js @@ -166,8 +166,9 @@ __private.forge = function (cb) { return modules.transport.getPeers({limit: constants.maxPeers}, seriesCb); }, checkBroadhash: function (seriesCb) { - if (modules.transport.poorConsensus()) { - return setImmediate(seriesCb, ['Inadequate broadhash consensus', modules.transport.consensus(), '%'].join(' ')); + var consensus = modules.peers.getConsensus(); + if (modules.transport.poorConsensus(consensus)) { + return setImmediate(seriesCb, ['Inadequate broadhash consensus', consensus, '%'].join(' ')); } else { return setImmediate(seriesCb); } @@ -513,13 +514,14 @@ Delegates.prototype.validateBlockSlot = function (block, cb) { */ Delegates.prototype.onBind = function (scope) { modules = { - loader: scope.loader, - rounds: scope.rounds, accounts: scope.accounts, blocks: scope.blocks, - transport: scope.transport, - transactions: scope.transactions, delegates: scope.delegates, + loader: scope.loader, + peers: scope.peers, + rounds: scope.rounds, + transactions: scope.transactions, + transport: scope.transport }; __private.assetTypes[transactionTypes.DELEGATE].bind( diff --git a/modules/loader.js b/modules/loader.js index 728b63d3492..fc310f8d9fd 100644 --- a/modules/loader.js +++ b/modules/loader.js @@ -143,7 +143,6 @@ __private.syncTimer = function () { * Processes each signature from peer. * @private * @implements {Loader.getNetwork} - * @implements {modules.transport.getFromPeer} * @implements {library.schema.validate} * @implements {library.sequence.add} * @implements {async.eachSeries} @@ -165,16 +164,13 @@ __private.loadSignatures = function (cb) { }, function (peer, waterCb) { library.logger.log('Loading signatures from: ' + peer.string); - - modules.transport.getFromPeer(peer, { - api: '/signatures', - method: 'GET' - }, function (err, res) { + peer = library.logic.peers.create(peer); + peer.rpc.getSignatures(function (err, res) { if (err) { return setImmediate(waterCb, err); } else { - library.schema.validate(res.body, schema.loadSignatures, function (err) { - return setImmediate(waterCb, err, res.body.signatures); + library.schema.validate(res, schema.loadSignatures, function (err) { + return setImmediate(waterCb, err, res.signatures); }); } }); @@ -204,7 +200,6 @@ __private.loadSignatures = function (cb) { * Calls processUnconfirmedTransaction for each transaction. * @private * @implements {Loader.getNetwork} - * @implements {modules.transport.getFromPeer} * @implements {library.schema.validate} * @implements {async.eachSeries} * @implements {library.logic.transaction.objectNormalize} @@ -229,20 +224,15 @@ __private.loadTransactions = function (cb) { }, function (peer, waterCb) { library.logger.log('Loading transactions from: ' + peer.string); - - modules.transport.getFromPeer(peer, { - api: '/transactions', - method: 'GET' - }, function (err, res) { + peer.rpc.getTransactions(function (err, res) { if (err) { return setImmediate(waterCb, err); } - - library.schema.validate(res.body, schema.loadTransactions, function (err) { + library.schema.validate(res, schema.loadTransactions, function (err) { if (err) { return setImmediate(waterCb, err[0].message); } else { - return setImmediate(waterCb, null, peer, res.body.transactions); + return setImmediate(waterCb, null, peer, res.transactions); } }); }); @@ -526,6 +516,7 @@ __private.loadBlocksFromNetwork = function (cb) { var errorCount = 0; var loaded = false; + self.getNetwork(function (err, network) { if (err) { return setImmediate(cb, err); @@ -656,26 +647,26 @@ __private.sync = function (cb) { * @private * @implements {modules.blocks.lastBlock.get} * @implements {library.logic.peers.create} - * @param {number} heights + * @param {array} peers * @return {Object} {height number, peers array} */ -__private.findGoodPeers = function (heights) { +Loader.prototype.findGoodPeers = function (peers) { var lastBlockHeight = modules.blocks.lastBlock.get().height; - library.logger.trace('Good peers - received', {count: heights.length}); + library.logger.trace('Good peers - received', {count: peers.length}); - heights = heights.filter(function (item) { + peers = peers.filter(function (item) { // Removing unreachable peers or heights below last block height return item != null && item.height >= lastBlockHeight; }); - library.logger.trace('Good peers - filtered', {count: heights.length}); + library.logger.trace('Good peers - filtered', {count: peers.length}); // No peers found - if (heights.length === 0) { + if (peers.length === 0) { return {height: 0, peers: []}; } else { // Ordering the peers with descending height - heights = heights.sort(function (a,b) { + peers = peers.sort(function (a,b) { return b.height - a.height; }); @@ -687,8 +678,8 @@ __private.findGoodPeers = function (heights) { var aggregation = 2; // Histogram calculation, together with histogram maximum - for (var i in heights) { - var val = parseInt(heights[i].height / aggregation) * aggregation; + for (var i in peers) { + var val = parseInt(peers[i].height / aggregation) * aggregation; histogram[val] = (histogram[val] ? histogram[val] : 0) + 1; if (histogram[val] > max) { @@ -698,7 +689,7 @@ __private.findGoodPeers = function (heights) { } // Performing histogram cut of peers too far from histogram maximum - var peers = heights.filter(function (item) { + peers = peers.filter(function (item) { return item && Math.abs(height - item.height) < aggregation + 1; }).map(function (item) { return library.logic.peers.create(item); @@ -716,7 +707,7 @@ __private.findGoodPeers = function (heights) { // Rationale: // - We pick 100 random peers from a random peer (could be unreachable). // - Then for each of them we grab the height of their blockchain. -// - With this list we try to get a peer with sensibly good blockchain height (see __private.findGoodPeers for actual strategy). +// - With this list we try to get a peer with sensibly good blockchain height (see Loader.prototype.findGoodPeers for actual strategy). /** * Gets good peers. * @implements {modules.blocks.lastBlock.get} @@ -729,13 +720,12 @@ Loader.prototype.getNetwork = function (cb) { if (__private.network.height > 0 && Math.abs(__private.network.height - modules.blocks.lastBlock.get().height) === 1) { return setImmediate(cb, null, __private.network); } - modules.peers.list({}, function (err, peers) { if (err) { return setImmediate(cb, err); } - __private.network = __private.findGoodPeers(peers); + __private.network = self.findGoodPeers(peers); if (!__private.network.peers.length) { return setImmediate(cb, 'Failed to find enough good peers'); @@ -882,7 +872,7 @@ Loader.prototype.shared = { blocks: __private.blocksToSync, height: modules.blocks.lastBlock.get().height, broadhash: modules.system.getBroadhash(), - consensus: modules.transport.consensus() + consensus: modules.peers.getConsensus() }); } }; diff --git a/modules/peers.js b/modules/peers.js index 3cc944fa325..1210430bfe5 100644 --- a/modules/peers.js +++ b/modules/peers.js @@ -15,7 +15,7 @@ var sql = require('../sql/peers.js'); var util = require('util'); // Private fields -var modules, library, self, __private = {}, shared = {}; +var modules, library, self, __private = {}; /** * Initializes library with scope content. @@ -42,7 +42,10 @@ function Peers (cb, scope) { config: { peers: scope.config.peers, version: scope.config.version, - }, + forging: { + force: scope.config.forging.force + } + } }; self = this; @@ -67,8 +70,8 @@ __private.countByFilter = function (filter, cb) { * Gets randomly ordered list of peers by filter. * @private * @param {Object} filter - * @param {function} cb - Callback function. - * @returns {setImmediateCallback} peers + * @param {function} [cb=undefined] cb - Callback function (synchronous function if not passed. + * @returns {setImmediateCallback|[Peer]} peers */ __private.getByFilter = function (filter, cb) { var allowedFields = ['ip', 'port', 'state', 'os', 'version', 'broadhash', 'height', 'nonce']; @@ -107,7 +110,7 @@ __private.getByFilter = function (filter, cb) { }; // Apply filters (by AND) - var peers = library.logic.peers.list(true); + var peers = library.logic.peers.list(); peers = peers.filter(function (peer) { // var peer = __private.peers[index]; @@ -142,9 +145,40 @@ __private.getByFilter = function (filter, cb) { peers = peers.slice(offset); } + if (!cb) { + return peers; + } return setImmediate(cb, null, peers); }; +__private.getMatched = function (test, peers) { + peers = peers || library.logic.peers.list(); + + var key = Object.keys(test)[0]; + var value = test[key]; + + return peers.filter(function (peer) { + return peer[key] === value; + }); +}; + +__private.updatePeerStatus = function (err, status, peer) { + + if (err) { + peer.applyHeaders({state: Peer.STATE.DISCONNECTED}); + return false; + } else { + peer.applyHeaders({ + height: status.height, + broadhash: status.broadhash, + nonce: status.nonce, + state: Peer.STATE.CONNECTED //connected + }); + } + + library.logic.peers.upsert(peer); +}; + /** * Pings to every member of peers list. * @private @@ -156,10 +190,15 @@ __private.insertSeeds = function (cb) { library.logger.trace('Peers->insertSeeds'); async.each(library.config.peers.list, function (peer, eachCb) { peer = library.logic.peers.create(peer); - library.logger.trace('Processing seed peer: ' + peer.string); - self.ping(peer, function (err) { - ++updated; - return setImmediate(eachCb); + library.logger.debug('Processing seed peer: ' + peer.string); + peer.rpc.status(function (err, status) { + __private.updatePeerStatus(err, status, peer); + if (!err) { + updated += 1; + } else { + library.logger.trace('Ping peer failed: ' + peer.string, err); + } + return setImmediate(eachCb, err); }); }, function (err) { library.logger.trace('Peers->insertSeeds - Peers discovered', {updated: updated, total: library.config.peers.list.length}); @@ -182,20 +221,24 @@ __private.dbLoad = function (cb) { library.logger.info('Imported peers from database', {count: rows.length}); async.each (rows, function (peer, eachCb) { peer = library.logic.peers.create(peer); - if (library.logic.peers.exists(peer)) { peer = library.logic.peers.get(peer); if (peer && peer.state > 0 && Date.now() - peer.updated > 3000) { - self.ping(peer, function (err) { - ++updated; - return setImmediate(eachCb); - }); - } else { - return setImmediate(eachCb); + return updatePeer(peer, eachCb); } - } else { - self.ping(peer, function (err) { - ++updated; + return setImmediate(eachCb); + } + + return updatePeer(peer, eachCb); + + function updatePeer (peer, cb) { + peer.rpc.status(function (err, status) { + __private.updatePeerStatus(err, status, peer); + if (!err) { + updated += 1; + } else { + library.logger.trace('Ping peer from db failed: ' + peer.string, err); + } return setImmediate(eachCb); }); } @@ -256,6 +299,27 @@ __private.dbSave = function (cb) { }); }; +Peers.prototype.getConsensus = function (matched, active) { + + if (library.config.forging.force) { + return undefined; + } + + active = active || __private.getByFilter({state: Peer.STATE.CONNECTED}); + matched = matched || __private.getMatched({broadhash: modules.system.getBroadhash()}, active); + + active = active.slice(0, constants.maxPeers); + matched = matched.slice(0, constants.maxPeers); + + var consensus = Math.round(matched.length / active.length * 100 * 1e2) / 100; + + if (isNaN(consensus)) { + return 0; + } + + return consensus; +}; + // Public methods /** @@ -265,49 +329,27 @@ __private.dbSave = function (cb) { * @todo rename this function to activePeer or similar */ Peers.prototype.update = function (peer) { - peer.state = Peer.STATE.CONNECTED; return library.logic.peers.upsert(peer); }; /** * Removes peer from peers list if it is not a peer from config file list. * @implements logic.peers.remove - * @param {string} pip - Peer ip - * @param {number} port - * @return {function} Calls peers.remove + * @param {Peer} peer + * @return {boolean} Calls peers.remove */ -Peers.prototype.remove = function (pip, port) { - var frozenPeer = _.find(library.config.peers.list, function (peer) { - return peer.ip === pip && peer.port === port; +Peers.prototype.remove = function (peer) { + var frozenPeer = _.find(library.config.peers.list, function (__peer) { + return peer.ip === __peer.ip && peer.port === __peer.port; }); if (frozenPeer) { // FIXME: Keeping peer frozen is bad idea at all - library.logger.debug('Cannot remove frozen peer', pip + ':' + port); - } else { - return library.logic.peers.remove ({ip: pip, port: port}); + library.logger.debug('Cannot remove frozen peer', peer.ip + ':' + peer.port); + peer.state = Peer.STATE.DISCONNECTED; + library.logic.peers.upsert(peer); + return false; } -}; - -/** - * Pings peer. - * @implements transport.getFromPeer - * @param {peer} peer - List of arguments. - * @param {function} cb - Callback function. - * @returns {setImmediateCallback} cb | error when ping peer fails - */ -Peers.prototype.ping = function (peer, cb) { - library.logger.trace('Pinging peer: ' + peer.string); - modules.transport.getFromPeer(peer, { - api: '/height', - method: 'GET' - }, function (err, res) { - if (err) { - library.logger.trace('Ping peer failed: ' + peer.string, err); - return setImmediate(cb, err); - } else { - return setImmediate(cb); - } - }); + return library.logic.peers.remove(peer); }; /** @@ -318,17 +360,22 @@ Peers.prototype.ping = function (peer, cb) { Peers.prototype.discover = function (cb) { library.logger.trace('Peers->discover'); function getFromRandomPeer (waterCb) { - modules.transport.getFromRandomPeer({ - api: '/list', - method: 'GET' - }, function (err, res) { - return setImmediate(waterCb, err, res); + self.list({limit: 1, allowedStates: [Peer.STATE.DISCONNECTED, Peer.STATE.CONNECTED]}, function (err, peers) { + var randomPeer = peers.length ? library.logic.peers.create(peers[0]) : null; + if (!err && randomPeer) { + randomPeer.rpc.status(function (err, status) { + __private.updatePeerStatus(err, status, randomPeer); + randomPeer.rpc.list(waterCb); + }); + } else { + return setImmediate(waterCb, err || 'No acceptable peers found'); + } }); } - function validatePeersList (res, waterCb) { - library.schema.validate(res.body, schema.discover.peers, function (err) { - return setImmediate(waterCb, err, res.body.peers); + function validatePeersList (result, waterCb) { + library.schema.validate(result, schema.discover.peers, function (err) { + return setImmediate(waterCb, err, result.peers); }); } @@ -342,7 +389,6 @@ Peers.prototype.discover = function (cb) { var updated = 0; async.each(peers, function (peer, eachCb) { peer = library.logic.peers.create(peer); - library.schema.validate(peer, schema.discover.peer, function (err) { if (err) { library.logger.warn(['Rejecting invalid peer:', peer.string].join(' '), {err: err}); @@ -383,11 +429,11 @@ Peers.prototype.acceptable = function (peers) { return (a.ip + a.port) === (b.ip + b.port); }) .filter(function (peer) { - // Removing peers with private address or nonce equal to self + // Removing peers with private address or nonce equal to itself if ((process.env['NODE_ENV'] || '').toUpperCase() === 'TEST') { - return peer.nonce !== modules.system.getNonce() && (peer.os !== 'lisk-js-api'); + return peer.nonce !== modules.system.getNonce(); } - return !ip.isPrivate(peer.ip) && peer.nonce !== modules.system.getNonce() && (peer.os !== 'lisk-js-api'); + return !ip.isPrivate(peer.ip) && peer.nonce !== modules.system.getNonce(); }).value(); }; @@ -407,14 +453,14 @@ Peers.prototype.list = function (options, cb) { function randomList (options, peers, cb) { // Get full peers list (random) - __private.getByFilter ({}, function (err, peersList) { + __private.getByFilter({}, function (err, peersList) { var accepted, found, matched, picked; found = peersList.length; // Apply filters peersList = peersList.filter(function (peer) { if (options.broadhash) { - // Skip banned and disconnected peers (state 0 and 1) + // Skip banned and disconnected peers by default return options.allowedStates.indexOf(peer.state) !== -1 && ( // Matched broadhash when attempt 0 options.attempt === 0 ? (peer.broadhash === options.broadhash) : @@ -422,7 +468,7 @@ Peers.prototype.list = function (options, cb) { options.attempt === 1 ? (peer.broadhash !== options.broadhash) : false ); } else { - // Skip banned and disconnected peers (state 0 and 1) + // Skip banned and disconnected peers by default return options.allowedStates.indexOf(peer.state) !== -1; } }); @@ -430,7 +476,7 @@ Peers.prototype.list = function (options, cb) { // Apply limit peersList = peersList.slice(0, options.limit); picked = peersList.length; - accepted = self.acceptable(peers.concat(peersList)); + accepted = peers.concat(peersList); library.logger.debug('Listing peers', {attempt: options.attempts[options.attempt], found: found, matched: matched, picked: picked, accepted: accepted.length}); return setImmediate(cb, null, accepted); }); @@ -470,7 +516,7 @@ Peers.prototype.list = function (options, cb) { Peers.prototype.onBind = function (scope) { modules = { transport: scope.transport, - system: scope.system, + system: scope.system }; }; @@ -528,8 +574,13 @@ Peers.prototype.onPeersReady = function () { // If peer is not banned and not been updated during last 3 sec - ping if (peer && peer.state > 0 && (!peer.updated || Date.now() - peer.updated > 3000)) { library.logger.trace('Updating peer', peer); - self.ping(peer, function (err) { - ++updated; + peer.rpc.status(function (err, status) { + __private.updatePeerStatus(err, status, peer); + if (!err) { + updated += 1; + } else { + library.logger.trace('Every 10sec peers check ping peer failed ' + peer.string, err); + } return setImmediate(eachCb); }); } else { @@ -545,7 +596,7 @@ Peers.prototype.onPeersReady = function () { }); } // Loop in 10sec intervals (5sec + 5sec connect timeout from pingPeer) - jobsQueue.register('peersDiscoveryAndUpdate', peersDiscoveryAndUpdate, 5000); + jobsQueue.register('peersDiscoveryAndUpdate', peersDiscoveryAndUpdate, 60000); }; /** @@ -618,7 +669,6 @@ Peers.prototype.shared = { if (err) { return setImmediate(cb, err[0].message); } - __private.getByFilter({ ip: req.body.ip, port: req.body.port diff --git a/logs/.gitkeep b/modules/sql.js similarity index 100% rename from logs/.gitkeep rename to modules/sql.js diff --git a/modules/system.js b/modules/system.js index 4c2952fa800..0bd87f01cf8 100644 --- a/modules/system.js +++ b/modules/system.js @@ -3,6 +3,8 @@ var async = require('async'); var crypto = require('crypto'); var os = require('os'); +var _ = require('lodash'); +var constants = require('../helpers/constants.js'); var semver = require('semver'); var sql = require('../sql/system.js'); @@ -32,24 +34,26 @@ function System (cb, scope) { library = { logger: scope.logger, db: scope.db, - nonce: scope.nonce, config: { version: scope.config.version, port: scope.config.port, nethash: scope.config.nethash, minVersion: scope.config.minVersion, - }, + nonce: scope.config.nonce + } }; + self = this; __private.os = os.platform() + os.release(); __private.version = library.config.version; __private.port = library.config.port; + __private.httpPort = library.config.httpPort; __private.height = 1; __private.nethash = library.config.nethash; __private.broadhash = library.config.nethash; __private.minVersion = library.config.minVersion; - __private.nonce = library.nonce; + __private.nonce = library.config.nonce; if (rcRegExp.test(__private.minVersion)) { this.minVersion = __private.minVersion.replace(rcRegExp, ''); @@ -62,6 +66,23 @@ function System (cb, scope) { } // Public methods + +/** + * Sets the entire __private variable + * @param {object} headers + */ +System.setHeaders = function (headers) { + __private = headers; +}; + +/** + * Returns all headers from __private variable + * @returns {*} __private + */ +System.getHeaders = function () { + return __private; +}; + /** * Returns private variables object content. * @return {Object} @@ -104,7 +125,7 @@ System.prototype.getHeight = function () { /** * Gets private variable `nethash` - * @return {hash} + * @return {string} hash */ System.prototype.getNethash = function () { return __private.nethash; @@ -112,18 +133,37 @@ System.prototype.getNethash = function () { /** * Gets private variable `nonce` - * @return {nonce} + * @return {string} nonce */ System.prototype.getNonce = function () { return __private.nonce; }; + /** - * Gets private variable `nethash` and compares with input param. - * @param {hash} - * @return {boolean} True if input param is equal to private value. + * Invokes cb with broadhash + * @param {function} cb + * @callback broadhashCallback + * @param {Error} err + * @param {string} broadhash */ -System.prototype.networkCompatible = function (nethash) { - return __private.nethash === nethash; +System.prototype.getBroadhash = function (cb) { + if (typeof cb !== 'function') { + return __private.broadhash; + } + + library.db.query(sql.getBroadhash, { limit: 5 }).then(function (rows) { + if (rows.length <= 1) { + return setImmediate(cb, null, __private.nethash); + } else { + var seed = rows.map(function (row) { return row.id; }).join(''); + var hash = crypto.createHash('sha256').update(seed, 'utf8').digest(); + + return setImmediate(cb, null, hash.toString('hex')); + } + }).catch(function (err) { + library.logger.error(err.stack); + return setImmediate(cb, err); + }); }; /** @@ -134,6 +174,15 @@ System.prototype.getMinVersion = function () { return __private.minVersion; }; +/** + * Checks nethash (network) compatibility. + * @param {string} nethash + * @returns {boolean} + */ +System.prototype.networkCompatible = function (nethash) { + return __private.nethash === nethash; +}; + /** * Checks version compatibility from input param against private values. * @implements {semver} @@ -159,30 +208,12 @@ System.prototype.versionCompatible = function (version) { }; /** - * Gets private nethash or creates a new one, based on input param and data. - * @implements {library.db.query} - * @implements {crypto.createHash} - * @param {*} cb - * @return {hash|setImmediateCallback} err | private nethash or new hash. + * Checks nonce (unique app id) compatibility- compatible when different than given. + * @param nonce + * @returns {boolean} */ -System.prototype.getBroadhash = function (cb) { - if (typeof cb !== 'function') { - return __private.broadhash; - } - - library.db.query(sql.getBroadhash, { limit: 5 }).then(function (rows) { - if (rows.length <= 1) { - return setImmediate(cb, null, __private.nethash); - } else { - var seed = rows.map(function (row) { return row.id; }).join(''); - var hash = crypto.createHash('sha256').update(seed, 'utf8').digest(); - - return setImmediate(cb, null, hash.toString('hex')); - } - }).catch(function (err) { - library.logger.error(err.stack); - return setImmediate(cb, err); - }); +System.prototype.nonceCompatible = function (nonce) { + return nonce && __private.nonce !== nonce; }; /** diff --git a/modules/transport.js b/modules/transport.js index 83d0f521be9..2ef7a643569 100644 --- a/modules/transport.js +++ b/modules/transport.js @@ -1,9 +1,7 @@ 'use strict'; -var _ = require('lodash'); var async = require('async'); var Broadcaster = require('../logic/broadcaster.js'); -var Peer = require('../logic/peer.js'); var bignum = require('../helpers/bignum.js'); var constants = require('../helpers/constants.js'); var crypto = require('crypto'); @@ -12,6 +10,9 @@ var ip = require('ip'); var popsicle = require('popsicle'); var schema = require('../schema/transport.js'); var sql = require('../sql/transport.js'); +var zlib = require('zlib'); +var Peer = require('../logic/peer'); +var System = require('../modules/system'); // Private fields var modules, library, self, __private = {}, shared = {}; @@ -92,8 +93,13 @@ __private.hashsum = function (obj) { * @param {string} extraMessage */ __private.removePeer = function (options, extraMessage) { - library.logger.debug([options.code, 'Removing peer', options.peer.string, extraMessage].join(' ')); - modules.peers.remove(options.peer.ip, options.peer.port); + if (!options.peer) { + library.logger.debug('Cannot remove empty peer'); + return false; + } + + library.logger.debug([options.code, 'Removing peer', options.peer.ip + ':' + options.peer.port, extraMessage].join(' ')); + return modules.peers.remove(options.peer); }; /** @@ -206,7 +212,7 @@ __private.receiveTransactions = function (query, peer, extraLogMessage, cb) { }; /** - * Normalizes transaction and bans peer if it fails. + * Normalizes transaction and remove peer if it fails. * Calls balancesSequence.add to receive transaction and * processUnconfirmedTransaction to confirm it. * @private @@ -234,7 +240,7 @@ __private.receiveTransaction = function (transaction, peer, extraLogMessage, cb) } library.balancesSequence.add(function (cb) { - library.logger.debug('Received transaction ' + transaction.id + ' from peer ' + peer.string); + library.logger.debug('Received transaction ' + transaction.id + ' from peer ' + library.logic.peers.peersManager.getAddress(peer.nonce)); modules.transactions.processUnconfirmedTransaction(transaction, true, function (err) { if (err) { library.logger.debug(['Transaction', id].join(' '), err.toString()); @@ -262,24 +268,19 @@ Transport.prototype.headers = function (headers) { return __private.headers; }; -/** - * Gets consensus - * @return {number} broadcaster consensus - */ -Transport.prototype.consensus = function () { - return __private.broadcaster.consensus; -}; /** * Returns true if broadcaster consensus is less than minBroadhashConsensus. * Returns false if consensus is undefined. + * @param {number} [modules.peers.getConsensus()] * @return {boolean} */ -Transport.prototype.poorConsensus = function () { - if (__private.broadcaster.consensus === undefined) { +Transport.prototype.poorConsensus = function (consensus) { + var consensus = consensus || modules.peers.getConsensus(); + if (consensus === undefined) { return false; } else { - return (__private.broadcaster.consensus < constants.minBroadhashConsensus); + return (consensus < constants.minBroadhashConsensus); } }; @@ -294,138 +295,25 @@ Transport.prototype.getPeers = function (params, cb) { return __private.broadcaster.getPeers(params, cb); }; -/** - * Calls peers.list based on config options to get peers, calls getFromPeer - * with first peer from list. - * @implements {modules.peers.list} - * @implements {getFromPeer} - * @param {Object} config - * @param {function|Object} options - * @param {function} cb - * @return {setImmediateCallback|getFromPeer} error | calls getFromPeer - */ -Transport.prototype.getFromRandomPeer = function (config, options, cb) { - if (typeof options === 'function') { - cb = options; - options = config; - config = {}; - } - config.limit = 1; - config.allowedStates = [Peer.STATE.DISCONNECTED, Peer.STATE.CONNECTED]; - modules.peers.list(config, function (err, peers) { - if (!err && peers.length) { - return self.getFromPeer(peers[0], options, cb); - } else { - return setImmediate(cb, err || 'No acceptable peers found'); - } - }); -}; - -/** - * Requests information from peer(ip, port, url) and validates response: - * - status 200 - * - headers valid schema - * - same network (response headers nethash) - * - compatible version (response headers version) - * Removes peer if error, updates peer otherwise - * @requires {popsicle} - * @implements {library.logic.peers.create} - * @implements {library.schema.validate} - * @implements {modules.system.networkCompatible} - * @implements {modules.system.versionCompatible} - * @implements {modules.peers.update} - * @implements {__private.removePeer} - * @param {peer} peer - * @param {Object} options - * @param {function} cb - * @return {setImmediateCallback|Object} error message | {body, peer} - * @todo implements secure http request with https - */ -Transport.prototype.getFromPeer = function (peer, options, cb) { - var url; - - if (options.api) { - url = '/peer' + options.api; - } else { - url = options.url; - } - - peer = library.logic.peers.create(peer); - - var req = { - url: 'http://' + peer.ip + ':' + peer.port + url, - method: options.method, - headers: __private.headers, - timeout: library.config.peers.options.timeout - }; - - if (options.data) { - req.body = options.data; - } - - 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.method + ' ' + req.url); - - return setImmediate(cb, ['Received bad response code', res.status, req.method, req.url].join(' ')); - } else { - var headers = peer.applyHeaders(res.headers); - - var report = library.schema.validate(headers, schema.headers); - if (!report) { - // Remove peer - __private.removePeer({peer: peer, code: 'EHEADERS'}, req.method + ' ' + req.url); - - return setImmediate(cb, ['Invalid response headers', JSON.stringify(headers), req.method, req.url].join(' ')); - } - - if (!modules.system.networkCompatible(headers.nethash)) { - // Remove peer - __private.removePeer({peer: peer, code: 'ENETHASH'}, req.method + ' ' + req.url); - - return setImmediate(cb, ['Peer is not on the same network', headers.nethash, req.method, req.url].join(' ')); - } - - if (!modules.system.versionCompatible(headers.version)) { - // Remove peer - __private.removePeer({peer: peer, code: 'EVERSION:' + headers.version}, req.method + ' ' + req.url); - - return setImmediate(cb, ['Peer is using incompatible version', headers.version, req.method, req.url].join(' ')); - } - - modules.peers.update(peer); - - return setImmediate(cb, null, {body: res.body, peer: peer}); - } - }).catch(function (err) { - if (peer) { - __private.removePeer({peer: peer, code: err.code}, req.method + ' ' + req.url); - } - - return setImmediate(cb, [err.code, 'Request failed', req.method, req.url].join(' ')); - }); -}; - // Events /** * Bounds scope to private broadcaster amd initialize headers. - * @implements {modules.system.headers} + * @implements {System.getHeaders} * @implements {broadcaster.bind} * @param {modules} scope - Loaded modules. */ Transport.prototype.onBind = function (scope) { modules = { blocks: scope.blocks, - peers: scope.peers, + dapps: scope.dapps, + loader: scope.loader, multisignatures: scope.multisignatures, - transactions: scope.transactions, + peers: scope.peers, system: scope.system, + transactions: scope.transactions }; - __private.headers = modules.system.headers(); + __private.headers = System.getHeaders(); __private.broadcaster.bind( scope.peers, scope.transport, @@ -451,7 +339,7 @@ Transport.prototype.onBlockchainReady = function () { */ Transport.prototype.onSignature = function (signature, broadcast) { if (broadcast && !__private.broadcaster.maxRelays(signature)) { - __private.broadcaster.enqueue({}, {api: '/signatures', data: {signature: signature}, method: 'POST'}); + __private.broadcaster.enqueue({}, {api: 'postSignatures', data: {signature: signature}}); library.network.io.sockets.emit('signature/change', signature); } }; @@ -467,7 +355,7 @@ Transport.prototype.onSignature = function (signature, broadcast) { */ Transport.prototype.onUnconfirmedTransaction = function (transaction, broadcast) { if (broadcast && !__private.broadcaster.maxRelays(transaction)) { - __private.broadcaster.enqueue({}, {api: '/transactions', data: {transaction: transaction}, method: 'POST'}); + __private.broadcaster.enqueue({}, {api: 'postTransactions', data: {transaction: transaction}}); library.network.io.sockets.emit('transactions/change', transaction); } }; @@ -484,14 +372,31 @@ Transport.prototype.onUnconfirmedTransaction = function (transaction, broadcast) */ Transport.prototype.onNewBlock = function (block, broadcast) { if (broadcast) { - var broadhash = modules.system.getBroadhash(); - modules.system.update(function () { - if (!__private.broadcaster.maxRelays(block)) { - __private.broadcaster.broadcast({limit: constants.maxPeers, broadhash: broadhash}, {api: '/blocks', data: {block: block}, method: 'POST', immediate: true}); + if (!__private.broadcaster.maxRelays(block) && !modules.loader.syncing()) { + modules.peers.list({}, function (err, peers) { + async.each(peers.filter(function (peer) { return peer.state === Peer.STATE.CONNECTED; }), function (peer, cb) { + peer.rpc.acceptPeer(library.logic.peers.me(), function (err) { + if (err) { + library.logger.debug('Failed to update peer after new block applied', peer.string); + cb({errorMsg: err, peer: peer}); + __private.removePeer({peer: peer, code: 'ECOMMUNICATION'}); + } else { + library.logger.debug('Peer notified correctly after update', peer.string); + cb(); + } + }); + }, function (err) { + if (err) { + library.logger.debug('Broadcasting block aborted - cannot update info at peer: ', err.peer.ip + ':' + err.peer.port); + } else { + __private.broadcaster.broadcast({limit: constants.maxPeers, broadhash: modules.system.getBroadhash()}, {api: 'postBlock', data: {block: block}, immediate: true}); + } + }); + }); } - library.network.io.sockets.emit('blocks/change', block); }); + library.network.io.sockets.emit('blocks/change', block); } }; @@ -519,30 +424,40 @@ Transport.prototype.isLoaded = function () { * @see {@link http://apidocjs.com/} */ Transport.prototype.internal = { - blocksCommon: function (ids, peer, extraLogMessage, cb) { - var escapedIds = ids + blocksCommon: function (query, cb) { + query = query || {}; + return library.schema.validate(query, schema.commonBlock, function (err, valid) { + if (err) { + err = err[0].message + ': ' + err[0].path; + library.logger.debug('Common block request validation failed', {err: err.toString(), req: query}); + return setImmediate(cb, err); + } + + var escapedIds = query.ids // Remove quotes - .replace(/['"]+/g, '') - // Separate by comma into an array - .split(',') - // Reject any non-numeric values - .filter(function (id) { - return /^[0-9]+$/.test(id); - }); + .replace(/['"]+/g, '') + // Separate by comma into an array + .split(',') + // Reject any non-numeric values + .filter(function (id) { + return /^[0-9]+$/.test(id); + }); - if (!escapedIds.length) { - library.logger.debug('Common block request validation failed', {err: 'ESCAPE', req: ids}); + if (!escapedIds.length) { + library.logger.debug('Common block request validation failed', {err: 'ESCAPE', req: query.ids}); - __private.removePeer({peer: peer, code: 'ECOMMON'}, extraLogMessage); + __private.removePeer({peer: query.peer, code: 'ECOMMON'}); - return setImmediate(cb, 'Invalid block id sequence'); - } + return setImmediate(cb, 'Invalid block id sequence'); + } + + library.db.query(sql.getCommonBlock, escapedIds).then(function (rows) { + return setImmediate(cb, null, { success: true, common: rows[0] || null }); + }).catch(function (err) { + library.logger.error(err.stack); + return setImmediate(cb, 'Failed to get common block'); + }); - library.db.query(sql.getCommonBlock, escapedIds).then(function (rows) { - return setImmediate(cb, null, { success: true, common: rows[0] || null }); - }).catch(function (err) { - library.logger.error(err.stack); - return setImmediate(cb, 'Failed to get common block'); }); }, @@ -551,6 +466,7 @@ Transport.prototype.internal = { // According to maxium payload of 58150 bytes per block with every transaction being a vote // Discounting maxium compression setting used in middleware // Maximum transport payload = 2000000 bytes + query = query || {}; modules.blocks.utils.loadBlocksData({ limit: 34, // 1977100 bytes lastId: query.lastBlockId @@ -563,15 +479,16 @@ Transport.prototype.internal = { }); }, - postBlock: function (block, peer, extraLogMessage, cb) { + postBlock: function (query, cb) { + query = query || {}; try { - block = library.logic.block.objectNormalize(block); + var block = library.logic.block.objectNormalize(query.block); } catch (e) { - library.logger.debug('Block normalization failed', {err: e.toString(), module: 'transport', block: block }); + library.logger.debug('Block normalization failed', {err: e.toString(), module: 'transport', block: query.block }); - __private.removePeer({peer: peer, code: 'EBLOCK'}, extraLogMessage); + __private.removePeer({peer: query.peer, code: 'EBLOCK'}); - return setImmediate(cb, null, {success: false, error: e.toString()}); + return setImmediate(cb, e.toString()); } library.bus.message('receiveBlock', block); @@ -580,18 +497,19 @@ Transport.prototype.internal = { }, list: function (req, cb) { - modules.peers.list({limit: constants.maxPeers}, function (err, peers) { + req = req || {}; + modules.peers.list(Object.assign({}, {limit: constants.maxPeers}, req.query), function (err, peers) { peers = (!err ? peers : []); return setImmediate(cb, null, {success: !err, peers: peers}); }); }, height: function (req, cb) { - return setImmediate(cb, null, {success: true, height: modules.blocks.lastBlock.get().height}); + return setImmediate(cb, null, {success: true, height: modules.system.getHeight()}); }, - ping: function (req, cb) { - return setImmediate(cb, null, {success: true}); + status: function (req, cb) { + return setImmediate(cb, null, {success: true, height: modules.system.getHeight(), broadhash: modules.system.getBroadhash(), nonce: modules.system.getNonce()}); }, postSignatures: function (query, cb) { @@ -625,22 +543,20 @@ Transport.prototype.internal = { signatures: trs.signatures }); } - return setImmediate(__cb); }, function () { return setImmediate(cb, null, {success: true, signatures: signatures}); }); }, - getTransactions: function (req, cb) { + getTransactions: function (query, cb) { var transactions = modules.transactions.getMergedTransactionList(true, constants.maxSharedTxs); - return setImmediate(cb, null, {success: true, transactions: transactions}); }, - postTransactions: function (query, peer, extraLogMessage, cb) { + postTransactions: function (query, cb) { if (query.transactions) { - __private.receiveTransactions(query, peer, extraLogMessage, function (err) { + __private.receiveTransactions(query, query.peer, query.extraLogMessage, function (err) { if (err) { return setImmediate(cb, null, {success: false, message: err}); } else { @@ -648,7 +564,7 @@ Transport.prototype.internal = { } }); } else { - __private.receiveTransaction(query.transaction, peer, extraLogMessage, function (err, id) { + __private.receiveTransaction(query.transaction, query.peer, query.extraLogMessage, function (err, id) { if (err) { return setImmediate(cb, null, {success: false, message: err}); } else { @@ -658,55 +574,23 @@ Transport.prototype.internal = { } }, - handshake: function (ip, port, headers, validateHeaders, cb) { - var peer = library.logic.peers.create( - { - ip: ip, - port: port - } - ); - - var headers = peer.applyHeaders(headers); - - validateHeaders(headers, function (error, extraMessage) { - if (error) { - // Remove peer - __private.removePeer({peer: peer, code: 'EHEADERS'}, extraMessage); - - return setImmediate(cb, {success: false, error: error}); - } - - if (!modules.system.networkCompatible(headers.nethash)) { - // Remove peer - __private.removePeer({peer: peer, code: 'ENETHASH'}, extraMessage); - - return setImmediate(cb, { - success: false, - message: 'Request is made on the wrong network', - expected: modules.system.getNethash(), - received: headers.nethash - }); - } - - if (!modules.system.versionCompatible(headers.version)) { - // Remove peer - __private.removePeer({ - peer: peer, - code: 'EVERSION:' + headers.version - }, extraMessage); - - return setImmediate(cb, { - success: false, - message: 'Request is made from incompatible version', - expected: modules.system.getMinVersion(), - received: headers.version - }); - } - - modules.peers.update(peer); + /** + * @param {Peer} peer + * @param {function} cb + */ + removePeer: function (peer, cb) { + return setImmediate(cb, __private.removePeer({peer: peer, code: 0}, '') ? null : 'Failed to remove peer'); + }, - return setImmediate(cb, null, peer); - }); + /** + * @param {Peer} peer + * @param {function} cb + */ + acceptPeer: function (peer, cb) { + if (['height', 'nonce', 'broadhash'].some(function (header) { return peer[header] === undefined; })) { + return setImmediate(cb, 'No headers information'); + } + return setImmediate(cb, modules.peers.update(peer) ? null : 'Failed to accept peer'); } }; diff --git a/package.json b/package.json index 8c8a1c4307c..928354e2fcf 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,16 @@ "private": true, "scripts": { "start": "node app.js", - "test": "./node_modules/.bin/grunt test --verbose", "jenkins": "./node_modules/.bin/grunt jenkins --verbose", "eslint": "./node_modules/.bin/grunt eslint-nofix --verbose", "fetchCoverage": "./node_modules/.bin/grunt exec:fetchCoverage --verbose", "coverageReport": "./node_modules/.bin/grunt coverageReport --verbose", + "test": "./node_modules/.bin/grunt test --verbose", + "test-unit": "./node_modules/.bin/grunt test-unit --verbose", + "test-integration": "export TEST=test/integration/peers.integration.js && export NODE_ENV=TEST && ./node_modules/.bin/grunt jenkins", + "test-functional": "grunt test-functional", "jsdoc": "jsdoc -c docs/conf.json --verbose --pedantic", + "create-bundles": "webpack", "server-docs": "npm run jsdoc && http-server docs/jsdoc/" }, "author": "Lisk Foundation , lightcurve GmbH ", @@ -17,6 +21,7 @@ "dependencies": { "async": "=2.4.1", "bignumber.js": "=4.0.2", + "bluebird": "=3.5.0", "body-parser": "=1.17.2", "bytebuffer": "=5.0.1", "change-case": "=3.0.1", @@ -45,11 +50,14 @@ "rimraf": "=2.6.1", "semver": "=5.3.0", "socket.io": "=2.0.3", + "socketcluster": "=5.14.2", + "socketcluster-client": "=5.3.0", "sodium": "LiskHQ/node-sodium#716de00", "strftime": "=0.10.0", "valid-url": "=1.0.9", "validator": "=7.0.0", "validator.js": "=2.0.3", + "wamp-socket-cluster": "LiskHQ/wamp-socket-cluster#47b53a59f72441686314296f619b67cc8e855e21", "z-schema": "=3.18.2" }, "devDependencies": { @@ -74,7 +82,10 @@ "lisk-js": "=0.4.3", "mocha": "=3.4.2", "moment": "=2.18.1", + "pm2": "=2.4.6", "sinon": "=2.3.4", - "supertest": "=3.0.0" + "supertest": "=3.0.0", + "webpack": "=2.6.1", + "webpack-node-externals": "=1.6.0" } } diff --git a/schema/transport.js b/schema/transport.js index 4bb25c904ed..23422d8ce1d 100644 --- a/schema/transport.js +++ b/schema/transport.js @@ -7,6 +7,10 @@ module.exports = { id: 'transport.headers', type: 'object', properties: { + ip: { + type: 'string', + format: 'ip' + }, port: { type: 'integer', minimum: 1, @@ -42,7 +46,7 @@ module.exports = { max: 16 } }, - required: ['port', 'version', 'nethash'] + required: ['ip', 'port', 'version', 'nethash'] }, commonBlock: { id: 'transport.commonBlock', diff --git a/test/api/accounts.js b/test/api/accounts.js index fa04369d7f6..ad85652a875 100644 --- a/test/api/accounts.js +++ b/test/api/accounts.js @@ -1,13 +1,14 @@ 'use strict'; -var node = require('./../node.js'); +var node = require('../node.js'); +var http = require('../common/httpCommunication.js'); var account = node.randomAccount(); describe('POST /api/accounts/open', function () { function openAccount (params, done) { - node.post('/api/accounts/open', params, done); + http.post('/api/accounts/open', params, done); } it('using known passphrase should be ok', function (done) { @@ -97,7 +98,7 @@ describe('POST /api/accounts/open', function () { describe('GET /api/accounts/getBalance?address=', function () { function getBalance (address, done) { - node.get('/api/accounts/getBalance?address=' + address, done); + http.get('/api/accounts/getBalance?address=' + address, done); } it('using known address should be ok', function (done) { @@ -139,7 +140,7 @@ describe('GET /api/accounts/getBalance?address=', function () { describe('GET /api/accounts/getPublicKey?address=', function () { function getPublicKey (address, done) { - node.get('/api/accounts/getPublicKey?address=' + address, done); + http.get('/api/accounts/getPublicKey?address=' + address, done); } it('using known address should be ok', function (done) { @@ -179,7 +180,7 @@ describe('GET /api/accounts/getPublicKey?address=', function () { describe('POST /api/accounts/generatePublicKey', function () { function generatePublicKey (params, done) { - node.post('/api/accounts/generatePublicKey', params, done); + http.post('/api/accounts/generatePublicKey', params, done); } it('using known passphrase should be ok', function (done) { @@ -235,7 +236,7 @@ describe('POST /api/accounts/generatePublicKey', function () { describe('GET /accounts', function () { function getAccounts (params, done) { - node.get('/api/accounts?' + params, done); + http.get('/api/accounts?' + params, done); } it('using known address should be ok', function (done) { diff --git a/test/api/blocks.js b/test/api/blocks.js index 9183ac00020..fa5389aa410 100644 --- a/test/api/blocks.js +++ b/test/api/blocks.js @@ -1,7 +1,8 @@ 'use strict'; -var node = require('./../node.js'); -var modulesLoader = require('./../common/initModule.js').modulesLoader; +var node = require('../node.js'); +var http = require('../common/httpCommunication.js'); +var modulesLoader = require('../common/initModule').modulesLoader; var block = { blockHeight: 0, @@ -16,7 +17,7 @@ var testBlocksUnder101 = false; describe('GET /api/blocks/getBroadhash', function () { it('should be ok', function (done) { - node.get('/api/blocks/getBroadhash', function (err, res) { + http.get('/api/blocks/getBroadhash', function (err, res) { node.expect(res.body).to.have.property('broadhash').to.be.a('string'); done(); }); @@ -26,7 +27,7 @@ describe('GET /api/blocks/getBroadhash', function () { describe('GET /api/blocks/getEpoch', function () { it('should be ok', function (done) { - node.get('/api/blocks/getEpoch', function (err, res) { + http.get('/api/blocks/getEpoch', function (err, res) { node.expect(res.body).to.have.property('epoch').to.be.a('string'); done(); }); @@ -36,7 +37,7 @@ describe('GET /api/blocks/getEpoch', function () { describe('GET /api/blocks/getHeight', function () { it('should be ok', function (done) { - node.get('/api/blocks/getHeight', function (err, res) { + http.get('/api/blocks/getHeight', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; if (res.body.success && res.body.height != null) { node.expect(res.body).to.have.property('height').to.be.above(0); @@ -54,7 +55,7 @@ describe('GET /api/blocks/getHeight', function () { describe('GET /api/blocks/getFee', function () { it('should be ok', function (done) { - node.get('/api/blocks/getFee', function (err, res) { + http.get('/api/blocks/getFee', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('fee'); node.expect(res.body.fee).to.equal(node.fees.transactionFee); @@ -66,7 +67,7 @@ describe('GET /api/blocks/getFee', function () { describe('GET /api/blocks/getfees', function () { it('should be ok', function (done) { - node.get('/api/blocks/getFees', function (err, res) { + http.get('/api/blocks/getFees', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('fees'); node.expect(res.body.fees.send).to.equal(node.fees.transactionFee); @@ -83,7 +84,7 @@ describe('GET /api/blocks/getfees', function () { describe('GET /api/blocks/getNethash', function () { it('should be ok', function (done) { - node.get('/api/blocks/getNethash', function (err, res) { + http.get('/api/blocks/getNethash', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('nethash').to.be.a('string'); node.expect(res.body.nethash).to.equal(node.config.nethash); @@ -95,7 +96,7 @@ describe('GET /api/blocks/getNethash', function () { describe('GET /api/blocks/getMilestone', function () { it('should be ok', function (done) { - node.get('/api/blocks/getMilestone', function (err, res) { + http.get('/api/blocks/getMilestone', function (err, res) { node.expect(res.body).to.have.property('milestone').to.be.a('number'); done(); }); @@ -105,7 +106,7 @@ describe('GET /api/blocks/getMilestone', function () { describe('GET /api/blocks/getReward', function () { it('should be ok', function (done) { - node.get('/api/blocks/getReward', function (err, res) { + http.get('/api/blocks/getReward', function (err, res) { node.expect(res.body).to.have.property('reward').to.be.a('number'); done(); }); @@ -115,7 +116,7 @@ describe('GET /api/blocks/getReward', function () { describe('GET /api/blocks/getSupply', function () { it('should be ok', function (done) { - node.get('/api/blocks/getSupply', function (err, res) { + http.get('/api/blocks/getSupply', function (err, res) { node.expect(res.body).to.have.property('supply').to.be.a('number'); done(); }); @@ -125,7 +126,7 @@ describe('GET /api/blocks/getSupply', function () { describe('GET /api/blocks/getStatus', function () { it('should be ok', function (done) { - node.get('/api/blocks/getStatus', function (err, res) { + http.get('/api/blocks/getStatus', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('broadhash').to.be.a('string'); node.expect(res.body).to.have.property('epoch').to.be.a('string'); @@ -174,7 +175,7 @@ describe('GET /blocks (cache)', function () { var url, params; url = '/api/blocks?'; params = 'height=' + block.blockHeight; - node.get(url + params, function (err, res) { + http.get(url + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('blocks').that.is.an('array'); node.expect(res.body).to.have.property('count').to.equal(1); @@ -191,7 +192,7 @@ describe('GET /blocks (cache)', function () { var url, params; url = '/api/blocks?'; params = 'height=' + -1000; - node.get(url + params, function (err, res) { + http.get(url + params, function (err, res) { node.expect(res.body).to.have.property('success').to.not.be.ok; cache.getJsonForKey(url + params, function (err, res) { node.expect(err).to.not.exist; @@ -205,7 +206,7 @@ describe('GET /blocks (cache)', function () { var url, params; url = '/api/blocks?'; params = 'height=' + block.blockHeight; - node.get(url + params, function (err, res) { + http.get(url + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('blocks').that.is.an('array'); node.expect(res.body).to.have.property('count').to.equal(1); @@ -229,7 +230,7 @@ describe('GET /blocks (cache)', function () { describe('GET /blocks', function () { function getBlocks (params, done) { - node.get('/api/blocks?' + params, done); + http.get('/api/blocks?' + params, done); } it('using height should be ok', function (done) { @@ -374,7 +375,7 @@ describe('GET /blocks', function () { describe('GET /api/blocks/get?id=', function () { function getBlocks (id, done) { - node.get('/api/blocks/get?id=' + id, done); + http.get('/api/blocks/get?id=' + id, done); } it('using genesisblock id should be ok', function (done) { diff --git a/test/api/dapps.js b/test/api/dapps.js index 92143202e68..f0aafc23da0 100644 --- a/test/api/dapps.js +++ b/test/api/dapps.js @@ -1,13 +1,13 @@ 'use strict'; var node = require('./../node.js'); +var http = require('../common/httpCommunication.js'); +var ws = require('../common/wsCommunication.js'); var clearDatabaseTable = require('../common/globalBefore').clearDatabaseTable; var modulesLoader = require('../common/initModule').modulesLoader; function postTransaction (transaction, done) { - node.post('/peer/transactions', { - transaction: transaction - }, done); + ws.call('postTransactions', { transaction: transaction }, done, true); } before(function (done) { @@ -26,7 +26,6 @@ before(function (done) { node.async.eachSeries([node.guestbookDapp, node.blockDataDapp], function (dapp, eachSeriesCb) { var transaction = node.lisk.dapp.createDapp(node.gAccount.password, null, dapp); dapp.transactionId = transaction.id; - postTransaction(transaction, eachSeriesCb); }, done); }); @@ -38,7 +37,7 @@ before(function (done) { describe('GET /api/dapps/get?id=', function () { function getDapp (id, done) { - node.get('/api/dapps/get?id=' + id, done); + http.get('/api/dapps/get?id=' + id, done); } it('using no id should fail', function (done) { @@ -90,7 +89,7 @@ describe('GET /api/dapps/get?id=', function () { describe('GET /api/dapps/categories', function () { it('should be ok', function (done) { - node.get('/api/dapps/categories', function (err, res) { + http.get('/api/dapps/categories', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('categories').that.is.an('object'); for (var i in node.dappCategories) { @@ -104,7 +103,7 @@ describe('GET /api/dapps/categories', function () { describe('GET /api/dapps/?type=', function () { function getDapps (params, done) { - node.get('/api/dapps?type=' + params, done); + http.get('/api/dapps?type=' + params, done); } it('using no type should fail', function (done) { @@ -163,7 +162,7 @@ describe('GET /api/dapps/?type=', function () { describe('GET /api/dapps/?name=', function () { function getDapps (params, done) { - node.get('/api/dapps?name=' + params, done); + http.get('/api/dapps?name=' + params, done); } it('using name with length < 1 should fail', function (done) { @@ -222,7 +221,7 @@ describe('GET /api/dapps/?name=', function () { describe('GET /api/dapps/?category=', function () { function getDapps (params, done) { - node.get('/api/dapps?category=' + params, done); + http.get('/api/dapps?category=' + params, done); } it('using numeric category should fail', function (done) { @@ -271,7 +270,7 @@ describe('GET /api/dapps/?category=', function () { describe('GET /api/dapps/?link=', function () { function getDapps (params, done) { - node.get('/api/dapps?link=' + params, done); + http.get('/api/dapps?link=' + params, done); } it('using numeric link should fail', function (done) { @@ -329,7 +328,7 @@ describe('GET /api/dapps/?link=', function () { describe('GET /api/dapps/?limit=', function () { function getDapps (params, done) { - node.get('/api/dapps?limit=' + params, done); + http.get('/api/dapps?limit=' + params, done); } it('using limit == 0 should fail', function (done) { @@ -378,7 +377,7 @@ describe('GET /api/dapps/?limit=', function () { describe('GET /api/dapps/?limit=1&offset=', function () { function getDapps (params, done) { - node.get('/api/dapps?limit=1&offset=' + params, done); + http.get('/api/dapps?limit=1&offset=' + params, done); } it('using offset < 0 should fail', function (done) { @@ -415,7 +414,7 @@ describe('GET /api/dapps/?limit=1&offset=', function () { describe('GET /api/dapps/?orderBy=', function () { function getDapps (params, done) { - node.get('/api/dapps?orderBy=' + params, done); + http.get('/api/dapps?orderBy=' + params, done); } it('using orderBy == "category:asc" should be ok', function (done) { diff --git a/test/api/delegates.js b/test/api/delegates.js index 96d67471e33..a63eb7650c8 100644 --- a/test/api/delegates.js +++ b/test/api/delegates.js @@ -1,29 +1,30 @@ 'use strict'; -var node = require('./../node.js'); +var node = require('../node.js'); +var http = require('../common/httpCommunication.js'); var modulesLoader = require('./../common/initModule.js').modulesLoader; var genesisDelegates = require('../genesisDelegates.json'); function openAccount (params, done) { - node.post('/api/accounts/open', params, function (err, res) { + http.post('/api/accounts/open', params, function (err, res) { done(err, res); }); } function sendLISK (params, done) { - node.put('/api/transactions/', params, function (err, res) { + http.put('/api/transactions/', params, function (err, res) { done(err, res); }); } function putAccountsDelegates (params, done) { - node.put('/api/accounts/delegates', params, function (err, res) { + http.put('/api/accounts/delegates', params, function (err, res) { done(err, res); }); } function putDelegates (params, done) { - node.put('/api/delegates', params, function (err, res) { + http.put('/api/delegates', params, function (err, res) { done(err, res); }); } @@ -409,7 +410,7 @@ describe('GET /api/delegates (cache)', function () { var url; url = '/api/delegates'; - node.get(url, function (err, res) { + http.get(url, 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 response = res.body; @@ -427,7 +428,7 @@ describe('GET /api/delegates (cache)', function () { orderBy = 'unknown:asc'; params = 'orderBy=' + orderBy; - node.get(url+ params, function (err, res) { + http.get(url+ params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Invalid sort field'); cache.getJsonForKey(url + params, function (err, res) { @@ -442,7 +443,7 @@ describe('GET /api/delegates (cache)', function () { var url; url = '/api/delegates'; - node.get(url, function (err, res) { + http.get(url, 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 response = res.body; @@ -463,9 +464,8 @@ describe('GET /api/delegates (cache)', function () { }); describe('GET /api/delegates', function () { - it('using no params should be ok', function (done) { - node.get('/api/delegates', function (err, res) { + http.get('/api/delegates', 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'); node.expect(res.body.delegates).to.have.lengthOf(101); @@ -483,7 +483,7 @@ describe('GET /api/delegates', function () { var orderBy = 'unknown:asc'; var params = 'orderBy=' + orderBy; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Invalid sort field'); done(); @@ -494,7 +494,7 @@ describe('GET /api/delegates', function () { var orderBy = 'approval:asc'; var params = 'orderBy=' + orderBy; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, 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'); node.expect(res.body.delegates).to.have.lengthOf(101); @@ -506,7 +506,7 @@ describe('GET /api/delegates', function () { var orderBy = 'productivity:asc'; var params = 'orderBy=' + orderBy; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, 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'); node.expect(res.body.delegates).to.have.lengthOf(101); @@ -518,7 +518,7 @@ describe('GET /api/delegates', function () { var orderBy = 'rank:asc'; var params = 'orderBy=' + orderBy; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, 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'); node.expect(res.body.delegates).to.have.lengthOf(101); @@ -535,7 +535,7 @@ describe('GET /api/delegates', function () { var orderBy = 'rank:desc'; var params = 'orderBy=' + orderBy; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, 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'); node.expect(res.body.delegates).to.have.lengthOf(101); @@ -552,7 +552,7 @@ describe('GET /api/delegates', function () { var orderBy = 'vote:asc'; var params = 'orderBy=' + orderBy; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, 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'); node.expect(res.body.delegates).to.have.lengthOf(101); @@ -569,7 +569,7 @@ describe('GET /api/delegates', function () { var orderBy = 'vote:desc'; var params = 'orderBy=' + orderBy; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, 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'); node.expect(res.body.delegates).to.have.lengthOf(101); @@ -586,7 +586,7 @@ describe('GET /api/delegates', function () { var orderBy = 'username:asc'; var params = 'orderBy=' + orderBy; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, 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'); node.expect(res.body.delegates).to.have.lengthOf(101); @@ -598,7 +598,7 @@ describe('GET /api/delegates', function () { var orderBy = 'address:asc'; var params = 'orderBy=' + orderBy; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, 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'); node.expect(res.body.delegates).to.have.lengthOf(101); @@ -610,7 +610,7 @@ describe('GET /api/delegates', function () { var orderBy = 'publicKey:asc'; var params = 'orderBy=' + orderBy; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, 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'); node.expect(res.body.delegates).to.have.lengthOf(101); @@ -622,7 +622,7 @@ describe('GET /api/delegates', function () { var limit = 'one'; var params = 'limit=' + limit; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Expected type integer but found type string'); done(); @@ -633,7 +633,7 @@ describe('GET /api/delegates', function () { var limit = -1; var params = 'limit=' + limit; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Value -1 is less than minimum 1'); done(); @@ -644,7 +644,7 @@ describe('GET /api/delegates', function () { var limit = 0; var params = 'limit=' + limit; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Value 0 is less than minimum 1'); done(); @@ -655,7 +655,7 @@ describe('GET /api/delegates', function () { var limit = 1; var params = 'limit=' + limit; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, 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'); node.expect(res.body.delegates).to.have.lengthOf(1); @@ -667,7 +667,7 @@ describe('GET /api/delegates', function () { var limit = 101; var params = 'limit=' + limit; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, 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'); node.expect(res.body.delegates).to.have.lengthOf(101); @@ -679,7 +679,7 @@ describe('GET /api/delegates', function () { var limit = 102; var params = 'limit=' + limit; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Value 102 is greater than maximum 101'); done(); @@ -690,7 +690,7 @@ describe('GET /api/delegates', function () { var limit = 'one'; var params = 'offset=' + limit; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Expected type integer but found type string'); done(); @@ -701,7 +701,7 @@ describe('GET /api/delegates', function () { var offset = 1; var params = 'offset=' + offset; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, 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'); node.expect(res.body.delegates).to.have.lengthOf(101); @@ -713,7 +713,7 @@ describe('GET /api/delegates', function () { var offset = -1; var params = 'offset=' + offset; - node.get('/api/delegates?' + params, function (err, res) { + http.get('/api/delegates?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Value -1 is less than minimum 0'); done(); @@ -723,7 +723,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', 'rank', 'vote']; node.async.each(delegatesSortFields, function (sortField, cb) { - node.get('/api/delegates?orderBy=' + sortField, function (err, res) { + http.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'); @@ -751,7 +751,7 @@ describe('GET /api/delegates', function () { describe('GET /api/delegates/count', function () { it('should be ok', function (done) { - node.get('/api/delegates/count', function (err, res) { + http.get('/api/delegates/count', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('count').to.be.at.least(101); done(); @@ -778,7 +778,7 @@ describe('GET /api/delegates/voters', function () { }); before(function (done) { - node.put('/api/accounts/delegates', { + http.put('/api/accounts/delegates', { secret: account.password, delegates: ['+' + node.eAccount.publicKey] }, function (err, res) { @@ -792,7 +792,7 @@ describe('GET /api/delegates/voters', function () { it('using no publicKey should be ok', function (done) { var params = 'publicKey='; - node.get('/api/delegates/voters?' + params, function (err, res) { + http.get('/api/delegates/voters?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('accounts').that.is.an('array').that.is.empty; done(); @@ -802,7 +802,7 @@ describe('GET /api/delegates/voters', function () { it('using invalid publicKey should fail', function (done) { var params = 'publicKey=' + 'notAPublicKey'; - node.get('/api/delegates/voters?' + params, function (err, res) { + http.get('/api/delegates/voters?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -813,7 +813,7 @@ describe('GET /api/delegates/voters', function () { var params = 'publicKey=' + node.eAccount.publicKey; node.onNewBlock(function (err) { - node.get('/api/delegates/voters?' + params, function (err, res) { + http.get('/api/delegates/voters?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('accounts').that.is.an('array'); var flag = 0; @@ -832,7 +832,7 @@ describe('GET /api/delegates/voters', function () { describe('GET /api/delegates/search', function () { it('using no criteria should fail', function (done) { - node.get('/api/delegates/search', function (err, res) { + http.get('/api/delegates/search', function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -842,7 +842,7 @@ describe('GET /api/delegates/search', function () { it('using blank criteria should fail', function (done) { var q = ''; - node.get('/api/delegates/search?q=' + q, function (err, res) { + http.get('/api/delegates/search?q=' + q, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -852,7 +852,7 @@ describe('GET /api/delegates/search', function () { it('using wildcard criteria should be ok', function (done) { var q = '%'; // 1 character - node.get('/api/delegates/search?q=' + q, function (err, res) { + http.get('/api/delegates/search?q=' + q, 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'); done(); @@ -862,7 +862,7 @@ describe('GET /api/delegates/search', function () { it('using criteria with length == 1 should be ok', function (done) { var q = 'g'; // 1 character - node.get('/api/delegates/search?q=' + q, function (err, res) { + http.get('/api/delegates/search?q=' + q, 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'); done(); @@ -872,7 +872,7 @@ describe('GET /api/delegates/search', function () { it('using criteria with length == 20 should be ok', function (done) { var q = 'genesis_123456789012'; // 20 characters - node.get('/api/delegates/search?q=' + q, function (err, res) { + http.get('/api/delegates/search?q=' + q, 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'); done(); @@ -882,7 +882,7 @@ describe('GET /api/delegates/search', function () { it('using criteria with length > 20 should fail', function (done) { var q = 'genesis_1234567890123'; // 21 characters - node.get('/api/delegates/search?q=' + q, function (err, res) { + http.get('/api/delegates/search?q=' + q, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -892,7 +892,7 @@ describe('GET /api/delegates/search', function () { it('using critera == "genesis_1" should return 13 delegates', function (done) { var q = 'genesis_1'; - node.get('/api/delegates/search?q=' + q, function (err, res) { + http.get('/api/delegates/search?q=' + q, 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'); node.expect(res.body.delegates).to.have.length(13); @@ -903,7 +903,7 @@ describe('GET /api/delegates/search', function () { it('using critera == "genesis_10" should return 3 delegates', function (done) { var q = 'genesis_10'; - node.get('/api/delegates/search?q=' + q, function (err, res) { + http.get('/api/delegates/search?q=' + q, 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'); node.expect(res.body.delegates).to.have.length(3); @@ -914,7 +914,7 @@ describe('GET /api/delegates/search', function () { it('using critera == "genesis_101" should return 1 delegate', function (done) { var q = 'genesis_101'; - node.get('/api/delegates/search?q=' + q, function (err, res) { + http.get('/api/delegates/search?q=' + q, 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'); node.expect(res.body.delegates).to.have.length(1); @@ -925,7 +925,7 @@ describe('GET /api/delegates/search', function () { it('using critera == "genesis_101" should have all properties', function (done) { var q = 'genesis_101'; - node.get('/api/delegates/search?q=' + q, function (err, res) { + http.get('/api/delegates/search?q=' + q, 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'); node.expect(res.body.delegates).to.have.length(1); @@ -947,7 +947,7 @@ describe('GET /api/delegates/search', function () { it('using no limit should be ok', function (done) { var q = 'genesis_'; - node.get('/api/delegates/search?q=' + q, function (err, res) { + http.get('/api/delegates/search?q=' + q, 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'); node.expect(res.body.delegates).to.have.length(101); @@ -959,7 +959,7 @@ describe('GET /api/delegates/search', function () { var q = 'genesis_'; var limit = 'one'; - node.get('/api/delegates/search?q=' + q + '&limit=' + limit, function (err, res) { + http.get('/api/delegates/search?q=' + q + '&limit=' + limit, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -970,7 +970,7 @@ describe('GET /api/delegates/search', function () { var q = 'genesis_'; var limit = -100; - node.get('/api/delegates/search?q=' + q + '&limit=' + limit, function (err, res) { + http.get('/api/delegates/search?q=' + q + '&limit=' + limit, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -981,7 +981,7 @@ describe('GET /api/delegates/search', function () { var q = 'genesis_'; var limit = -1; - node.get('/api/delegates/search?q=' + q + '&limit=' + limit, function (err, res) { + http.get('/api/delegates/search?q=' + q + '&limit=' + limit, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -992,7 +992,7 @@ describe('GET /api/delegates/search', function () { var q = 'genesis_'; var limit = 0; - node.get('/api/delegates/search?q=' + q + '&limit=' + limit, function (err, res) { + http.get('/api/delegates/search?q=' + q + '&limit=' + limit, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -1003,7 +1003,7 @@ describe('GET /api/delegates/search', function () { var q = 'genesis_'; var limit = 1; - node.get('/api/delegates/search?q=' + q + '&limit=' + limit, function (err, res) { + http.get('/api/delegates/search?q=' + q + '&limit=' + limit, 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'); node.expect(res.body.delegates).to.have.length(1); @@ -1015,7 +1015,7 @@ describe('GET /api/delegates/search', function () { var q = 'genesis_'; var limit = 1000; - node.get('/api/delegates/search?q=' + q + '&limit=' + limit, function (err, res) { + http.get('/api/delegates/search?q=' + q + '&limit=' + limit, 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'); node.expect(res.body.delegates).to.have.length(101); @@ -1027,7 +1027,7 @@ describe('GET /api/delegates/search', function () { var q = 'genesis_'; var limit = 1001; - node.get('/api/delegates/search?q=' + q + '&limit=' + limit, function (err, res) { + http.get('/api/delegates/search?q=' + q + '&limit=' + limit, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -1037,7 +1037,7 @@ describe('GET /api/delegates/search', function () { it('using orderBy == "unknown:asc" should fail', function (done) { var q = 'genesis_'; - node.get('/api/delegates/search?q=' + q + '&orderBy=unknown:asc', function (err, res) { + http.get('/api/delegates/search?q=' + q + '&orderBy=unknown:asc', function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -1047,7 +1047,7 @@ describe('GET /api/delegates/search', function () { it('using no orderBy should be ordered by ascending username', function (done) { var q = 'genesis_'; - node.get('/api/delegates/search?q=' + q, function (err, res) { + http.get('/api/delegates/search?q=' + q, 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'); node.expect(res.body.delegates).to.have.length(101); @@ -1062,7 +1062,7 @@ describe('GET /api/delegates/search', function () { it('using orderBy == "username:asc" should be ordered by ascending username', function (done) { var q = 'genesis_'; - node.get('/api/delegates/search?q=' + q + '&orderBy=username:asc', function (err, res) { + http.get('/api/delegates/search?q=' + q + '&orderBy=username: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'); node.expect(res.body.delegates).to.have.length(101); @@ -1077,7 +1077,7 @@ describe('GET /api/delegates/search', function () { it('using orderBy == "username:desc" should be ordered by descending username', function (done) { var q = 'genesis_'; - node.get('/api/delegates/search?q=' + q + '&orderBy=username:desc', function (err, res) { + http.get('/api/delegates/search?q=' + q + '&orderBy=username:desc', 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'); node.expect(res.body.delegates).to.have.length(101); @@ -1092,7 +1092,7 @@ describe('GET /api/delegates/search', function () { describe('GET /api/delegates/forging/status', function () { it('using no params should be ok', function (done) { - node.get('/api/delegates/forging/status', function (err, res) { + http.get('/api/delegates/forging/status', function (err, res) { 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'); @@ -1101,7 +1101,7 @@ describe('GET /api/delegates/forging/status', function () { }); it('using invalid publicKey should fail', function (done) { - node.get('/api/delegates/forging/status?publicKey=' + 'invalidPublicKey', function (err, res) { + http.get('/api/delegates/forging/status?publicKey=' + 'invalidPublicKey', 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(); @@ -1109,7 +1109,7 @@ 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) { + http.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.true; node.expect(res.body).to.have.property('delegates').that.is.an('array'); @@ -1118,7 +1118,7 @@ describe('GET /api/delegates/forging/status', function () { }); it('using disabled publicKey should be ok', function (done) { - node.get('/api/delegates/forging/status?publicKey=' + 'c094ebee7ec0c50ebee32918655e089f6e1a604b83bcaa760293c61e0f18ab6f', function (err, res) { + http.get('/api/delegates/forging/status?publicKey=' + 'c094ebee7ec0c50ebee32918655e089f6e1a604b83bcaa760293c61e0f18ab6f', 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; done(); @@ -1126,7 +1126,7 @@ describe('GET /api/delegates/forging/status', function () { }); it('using enabled publicKey should be ok', function (done) { - node.get('/api/delegates/forging/status?publicKey=' + '9d3058175acab969f41ad9b86f7a2926c74258670fe56b37c429c01fca9f2f0f', function (err, res) { + http.get('/api/delegates/forging/status?publicKey=' + '9d3058175acab969f41ad9b86f7a2926c74258670fe56b37c429c01fca9f2f0f', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('enabled').to.be.true; done(); @@ -1138,11 +1138,11 @@ describe('POST /api/delegates/forging/disable', function () { var testDelegate = genesisDelegates.delegates[0]; before(function (done) { - node.get('/api/delegates/forging/status?publicKey=' + testDelegate.publicKey, function (err, res) { + http.get('/api/delegates/forging/status?publicKey=' + testDelegate.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.a('boolean'); if (!res.body.enabled) { - node.post('/api/delegates/forging/enable', { + http.post('/api/delegates/forging/enable', { publicKey: testDelegate.publicKey, secret: testDelegate.secret }, function (err, res) { @@ -1156,7 +1156,7 @@ describe('POST /api/delegates/forging/disable', function () { }); it('using no params should fail', function (done) { - node.post('/api/delegates/forging/disable', {}, function (err, res) { + http.post('/api/delegates/forging/disable', {}, function (err, res) { node.expect(res.body).to.have.property('success').not.to.be.ok; node.expect(res.body).to.have.property('error').to.be.a('string').and.to.contain('Missing required property: secret'); done(); @@ -1164,7 +1164,7 @@ describe('POST /api/delegates/forging/disable', function () { }); it('using invalid secret should fail', function (done) { - node.post('/api/delegates/forging/disable', { + http.post('/api/delegates/forging/disable', { publicKey: testDelegate.publicKey, secret: 'invalid secret' }, function (err, res) { @@ -1175,7 +1175,7 @@ describe('POST /api/delegates/forging/disable', function () { }); it('using valid params should be ok', function (done) { - node.post('/api/delegates/forging/disable', { + http.post('/api/delegates/forging/disable', { publicKey: testDelegate.publicKey, secret: testDelegate.secret }, function (err, res) { @@ -1190,11 +1190,11 @@ describe('POST /api/delegates/forging/enable', function () { var testDelegate = genesisDelegates.delegates[0]; before(function (done) { - node.get('/api/delegates/forging/status?publicKey=' + testDelegate.publicKey, function (err, res) { + http.get('/api/delegates/forging/status?publicKey=' + testDelegate.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.a('boolean'); if (res.body.enabled) { - node.post('/api/delegates/forging/disable', { + http.post('/api/delegates/forging/disable', { publicKey: testDelegate.publicKey, secret: testDelegate.secret }, function (err, res) { @@ -1208,7 +1208,7 @@ describe('POST /api/delegates/forging/enable', function () { }); it('using no params should fail', function (done) { - node.post('/api/delegates/forging/enable', {}, function (err, res) { + http.post('/api/delegates/forging/enable', {}, function (err, res) { node.expect(res.body).to.have.property('success').not.to.be.ok; node.expect(res.body).to.have.property('error').to.be.a('string').and.to.contain('Missing required property: secret'); done(); @@ -1216,7 +1216,7 @@ describe('POST /api/delegates/forging/enable', function () { }); it('using invalid secret should fail', function (done) { - node.post('/api/delegates/forging/enable', { + http.post('/api/delegates/forging/enable', { publicKey: testDelegate.publicKey, secret: 'invalid secret' }, function (err, res) { @@ -1227,7 +1227,7 @@ describe('POST /api/delegates/forging/enable', function () { }); it('using valid params should be ok', function (done) { - node.post('/api/delegates/forging/enable', { + http.post('/api/delegates/forging/enable', { publicKey: testDelegate.publicKey, secret: testDelegate.secret }, function (err, res) { @@ -1259,7 +1259,7 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { } it('using no params should fail', function (done) { - node.get('/api/delegates/forging/getForgedByAccount', function (err, res) { + http.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(); @@ -1270,7 +1270,7 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { delete validParams.start; delete validParams.end; - node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + http.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'); @@ -1280,7 +1280,7 @@ 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) { + http.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'); @@ -1295,7 +1295,7 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { delete validParams.start; delete validParams.end; - node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + http.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(); @@ -1305,7 +1305,7 @@ 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) { + http.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 or is not a delegate'); done(); @@ -1315,7 +1315,7 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { it('using invalid generatorPublicKey should fail', function (done) { validParams.generatorPublicKey = 'invalidPublicKey'; - node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + http.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(); @@ -1325,7 +1325,7 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { it('using no start should be ok', function (done) { delete validParams.start; - node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + http.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'); @@ -1338,7 +1338,7 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { it('using no end should be ok', function (done) { delete validParams.end; - node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + http.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'); @@ -1351,7 +1351,7 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { it('using string start should fail', function (done) { validParams.start = 'one'; - node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + http.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(); @@ -1361,7 +1361,7 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { it('using string end should fail', function (done) { validParams.end = 'two'; - node.get('/api/delegates/forging/getForgedByAccount?' + buildParams(), function (err, res) { + http.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(); @@ -1372,7 +1372,7 @@ describe('GET /api/delegates/forging/getForgedByAccount', function () { describe('GET /api/delegates/getNextForgers', function () { it('using no params should be ok', function (done) { - node.get('/api/delegates/getNextForgers', function (err, res) { + http.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'); @@ -1384,7 +1384,7 @@ describe('GET /api/delegates/getNextForgers', function () { }); it('using limit === 1 should be ok', function (done) { - node.get('/api/delegates/getNextForgers?' + 'limit=1', function (err, res) { + http.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'); @@ -1396,7 +1396,7 @@ describe('GET /api/delegates/getNextForgers', function () { }); it('using limit === 101 should be ok', function (done) { - node.get('/api/delegates/getNextForgers?' + 'limit=101', function (err, res) { + http.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'); diff --git a/test/api/index.js b/test/api/index.js new file mode 100644 index 00000000000..1d76d9454eb --- /dev/null +++ b/test/api/index.js @@ -0,0 +1,15 @@ +require('./accounts'); +require('./blocks'); +require('./dapps'); +require('./delegates'); +require('./loader'); +require('./multisignatures'); +require('./signatures'); +require('./transactions'); +require('./peer/transport'); +require('./peer/peer.ws'); +require('./peer/peer.transactions.main.ws'); +require('./peer/peer.transactions.collision.ws'); +require('./peer/peer.transactions.delegates.ws'); +require('./peer/peer.transactions.signatures.ws'); +require('./peer/peer.transactions.votes.ws'); diff --git a/test/api/loader.js b/test/api/loader.js index c77ba5732ce..d4718e1fbd0 100644 --- a/test/api/loader.js +++ b/test/api/loader.js @@ -1,6 +1,7 @@ 'use strict'; -var node = require('./../node.js'); +var node = require('../node.js'); +var http = require('../common/httpCommunication.js'); describe('GET /api/loader/status/ping', function () { @@ -9,7 +10,7 @@ describe('GET /api/loader/status/ping', function () { }); it('should be ok', function (done) { - node.get('/api/loader/status/ping', function (err, res) { + http.get('/api/loader/status/ping', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -19,7 +20,7 @@ describe('GET /api/loader/status/ping', function () { describe('GET /api/loader/status/sync', function () { it('should be ok', function (done) { - node.get('/api/loader/status/sync', function (err, res) { + http.get('/api/loader/status/sync', function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('syncing').to.a('boolean'); node.expect(res.body).to.have.property('blocks').to.be.a('number'); diff --git a/test/api/multisignatures.js b/test/api/multisignatures.js index 0f00d89a2e9..5c5f9df3cdd 100644 --- a/test/api/multisignatures.js +++ b/test/api/multisignatures.js @@ -1,7 +1,8 @@ 'use strict'; var async = require('async'); -var node = require('./../node.js'); +var node = require('../node.js'); +var http = require('../common/httpCommunication.js'); var totalMembers = node.randomNumber(2, 16); var requiredSignatures = node.randomNumber(2, totalMembers + 1); @@ -22,7 +23,7 @@ var multiSigTx = { function sendLISK (account, i, done) { var randomLISK = node.randomLISK(); - node.put('/api/transactions/', { + http.put('/api/transactions/', { secret: node.gAccount.password, amount: randomLISK, recipientId: account.address @@ -36,7 +37,7 @@ function sendLISK (account, i, done) { } function sendLISKFromMultisigAccount (amount, recipient, done) { - node.put('/api/transactions/', { + http.put('/api/transactions/', { secret: multisigAccount.password, amount: amount, recipientId: recipient @@ -57,7 +58,7 @@ function confirmTransaction (transactionId, passphrases, done) { function (untilCb) { var passphrase = passphrases[count]; - node.post('/api/multisignatures/sign', { + http.post('/api/multisignatures/sign', { secret: passphrase, transactionId: transactionId }, function (err, res) { @@ -131,7 +132,7 @@ describe('PUT /api/multisignatures', function () { it('using random passphase should fail', function (done) { validParams.secret = node.randomPassword(); - node.put('/api/multisignatures', validParams, function (err, res) { + http.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').to.match(/Account does not have enough LSK: [0-9]+L balance: 0/); done(); @@ -141,7 +142,7 @@ describe('PUT /api/multisignatures', function () { it('using owner\'s public key in keysgroup should fail', function (done) { validParams.secret = accounts[accounts.length - 1].password; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -151,7 +152,7 @@ describe('PUT /api/multisignatures', function () { it('using empty keysgroup should fail', function (done) { validParams.keysgroup = []; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -161,7 +162,7 @@ describe('PUT /api/multisignatures', function () { it('using no keysgroup should fail', function (done) { delete validParams.keysgroup; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -171,7 +172,7 @@ describe('PUT /api/multisignatures', function () { it('using string keysgroup should fail', function (done) { validParams.keysgroup = 'invalid'; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -181,7 +182,7 @@ describe('PUT /api/multisignatures', function () { it('using no passphase should fail', function (done) { delete validParams.secret; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -191,7 +192,7 @@ describe('PUT /api/multisignatures', function () { it('using invalid passphrase should fail', function (done) { validParams.secret = multisigAccount.password + 'inv4lid'; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -201,7 +202,7 @@ describe('PUT /api/multisignatures', function () { it('using no lifetime', function (done) { delete validParams.lifetime; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -211,7 +212,7 @@ describe('PUT /api/multisignatures', function () { it('using string lifetime should fail', function (done) { validParams.lifetime = 'invalid'; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -221,7 +222,7 @@ describe('PUT /api/multisignatures', function () { it('using lifetime greater than maximum allowed should fail', function (done) { validParams.lifetime = 73; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -231,7 +232,7 @@ describe('PUT /api/multisignatures', function () { it('using lifetime == 0 should fail', function (done) { validParams.lifetime = 0; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -241,7 +242,7 @@ describe('PUT /api/multisignatures', function () { it('using negative lifetime should fail', function (done) { validParams.lifetime = -1; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -251,7 +252,7 @@ describe('PUT /api/multisignatures', function () { it('using no min should fail', function (done) { delete validParams.min; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -261,7 +262,7 @@ describe('PUT /api/multisignatures', function () { it('using string min should fail', function (done) { validParams.min = 'invalid'; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -271,7 +272,7 @@ describe('PUT /api/multisignatures', function () { it('using min greater than the total members should fail', function (done) { validParams.min = totalMembers + 5; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -281,7 +282,7 @@ describe('PUT /api/multisignatures', function () { it('using min == 0 should fail', function (done) { validParams.min = 0; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -291,7 +292,7 @@ describe('PUT /api/multisignatures', function () { it('using negative min should fail', function (done) { validParams.min = -1; - node.put('/api/multisignatures', validParams, function (err, res) { + http.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(); @@ -299,7 +300,7 @@ describe('PUT /api/multisignatures', function () { }); it('using valid params should be ok', function (done) { - node.put('/api/multisignatures', validParams, function (err, res) { + http.put('/api/multisignatures', validParams, 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; multiSigTx.txId = res.body.transactionId; @@ -320,7 +321,7 @@ describe('GET /api/multisignatures/pending', function () { it('using invalid public key should fail', function (done) { var publicKey = 1234; - node.get('/api/multisignatures/pending?publicKey=' + publicKey, function (err, res) { + http.get('/api/multisignatures/pending?publicKey=' + publicKey, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -328,7 +329,7 @@ describe('GET /api/multisignatures/pending', function () { }); it('using no public key should be ok', function (done) { - node.get('/api/multisignatures/pending?publicKey=', function (err, res) { + http.get('/api/multisignatures/pending?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'); @@ -338,7 +339,7 @@ describe('GET /api/multisignatures/pending', function () { }); it('using valid public key should be ok', function (done) { - node.get('/api/multisignatures/pending?publicKey=' + multisigAccount.publicKey, function (err, res) { + http.get('/api/multisignatures/pending?publicKey=' + multisigAccount.publicKey, 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.length).to.be.at.least(1); @@ -381,7 +382,7 @@ describe('PUT /api/transactions', function () { it('when group transaction is pending should be ok', function (done) { sendLISKFromMultisigAccount(100000000, node.gAccount.address, function (err, transactionId) { node.onNewBlock(function (err) { - node.get('/api/transactions/get?id=' + transactionId, function (err, res) { + http.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); @@ -411,7 +412,7 @@ describe('POST /api/multisignatures/sign (group)', function () { it('using random passphrase should fail', function (done) { validParams.secret = node.randomPassword(); - node.post('/api/multisignatures/sign', validParams, function (err, res) { + http.post('/api/multisignatures/sign', validParams, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; done(); }); @@ -420,7 +421,7 @@ describe('POST /api/multisignatures/sign (group)', function () { it('using null passphrase should fail', function (done) { validParams.secret = null; - node.post('/api/multisignatures/sign', validParams, function (err, res) { + http.post('/api/multisignatures/sign', validParams, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; done(); }); @@ -429,7 +430,7 @@ describe('POST /api/multisignatures/sign (group)', function () { it('using undefined passphrase should fail', function (done) { validParams.secret = undefined; - node.post('/api/multisignatures/sign', validParams, function (err, res) { + http.post('/api/multisignatures/sign', validParams, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; done(); }); @@ -438,7 +439,7 @@ describe('POST /api/multisignatures/sign (group)', function () { it('using one less than total signatures should not confirm transaction', function (done) { confirmTransaction(multiSigTx.txId, passphrases.slice(0, (passphrases.length - 1)), function () { node.onNewBlock(function (err) { - node.get('/api/transactions/get?id=' + multiSigTx.txId, function (err, res) { + http.get('/api/transactions/get?id=' + multiSigTx.txId, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; done(); }); @@ -447,7 +448,7 @@ describe('POST /api/multisignatures/sign (group)', function () { }); it('using same signature again should fail', function (done) { - node.post('/api/multisignatures/sign', validParams, function (err, res) { + http.post('/api/multisignatures/sign', validParams, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Transaction already signed'); done(); @@ -455,9 +456,9 @@ describe('POST /api/multisignatures/sign (group)', function () { }); it('using same signature again should not confirm transaction', function (done) { - node.post('/api/multisignatures/sign', validParams, function (err, res) { + http.post('/api/multisignatures/sign', validParams, function (err, res) { node.onNewBlock(function (err) { - node.get('/api/transactions/get?id=' + multiSigTx.txId, function (err, res) { + http.get('/api/transactions/get?id=' + multiSigTx.txId, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; done(); }); @@ -468,7 +469,7 @@ describe('POST /api/multisignatures/sign (group)', function () { it('using one more signature should confirm transaction', function (done) { confirmTransaction(multiSigTx.txId, passphrases.slice(-1), function () { node.onNewBlock(function (err) { - node.get('/api/transactions/get?id=' + multiSigTx.txId, function (err, res) { + http.get('/api/transactions/get?id=' + multiSigTx.txId, 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(multiSigTx.txId); @@ -507,7 +508,7 @@ describe('POST /api/multisignatures/sign (transaction)', function () { it('using one less than minimum signatures should not confirm transaction', function (done) { confirmTransaction(multiSigTx.txId, passphrases.slice(0, (multiSigTx.min - 1)), function () { node.onNewBlock(function (err) { - node.get('/api/transactions/get?id=' + multiSigTx.txId, function (err, res) { + http.get('/api/transactions/get?id=' + multiSigTx.txId, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; done(); }); @@ -516,7 +517,7 @@ describe('POST /api/multisignatures/sign (transaction)', function () { }); it('using same signature again should fail', function (done) { - node.post('/api/multisignatures/sign', validParams, function (err, res) { + http.post('/api/multisignatures/sign', validParams, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Transaction already signed'); done(); @@ -524,9 +525,9 @@ describe('POST /api/multisignatures/sign (transaction)', function () { }); it('using same signature again should not confirm transaction', function (done) { - node.post('/api/multisignatures/sign', validParams, function (err, res) { + http.post('/api/multisignatures/sign', validParams, function (err, res) { node.onNewBlock(function (err) { - node.get('/api/transactions/get?id=' + multiSigTx.txId, function (err, res) { + http.get('/api/transactions/get?id=' + multiSigTx.txId, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; done(); }); @@ -537,7 +538,7 @@ describe('POST /api/multisignatures/sign (transaction)', function () { it('using one more signature should confirm transaction', function (done) { confirmTransaction(multiSigTx.txId, passphrases.slice(-1), function () { node.onNewBlock(function (err) { - node.get('/api/transactions/get?id=' + multiSigTx.txId, function (err, res) { + http.get('/api/transactions/get?id=' + multiSigTx.txId, 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(multiSigTx.txId); @@ -553,7 +554,7 @@ describe('POST /api/multisignatures/sign (regular account)', function () { var transactionId; before(function (done) { - node.put('/api/transactions/', { + http.put('/api/transactions/', { secret: node.gAccount.password , amount: 1, recipientId: accounts[0].address @@ -567,7 +568,7 @@ describe('POST /api/multisignatures/sign (regular account)', function () { it('should be impossible to sign the transaction', function (done) { node.onNewBlock(function (err) { - node.get('/api/transactions/get?id=' + transactionId, function (err, res) { + http.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); @@ -580,7 +581,7 @@ describe('POST /api/multisignatures/sign (regular account)', function () { }); it('should have no pending multisignatures', function (done) { - node.get('/api/multisignatures/pending?publicKey=' + accounts[0].publicKey, function (err, res) { + http.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'); diff --git a/test/api/peer.blocks.js b/test/api/peer.blocks.js deleted file mode 100644 index 59dd530dc8c..00000000000 --- a/test/api/peer.blocks.js +++ /dev/null @@ -1,222 +0,0 @@ -'use strict'; - -var node = require('./../node.js'); - -var genesisblock = require('../../genesisBlock.json'); - -describe('GET /peer/blocks', function () { - - it('using valid headers should be ok', function (done) { - node.get('/peer/blocks') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('blocks').that.is.an('array'); - res.body.blocks.forEach(function (block) { - node.expect(block).to.have.property('b_id').that.is.a('string'); - node.expect(block).to.have.property('b_version').that.is.a('number'); - node.expect(block).to.have.property('b_timestamp').that.is.a('number'); - node.expect(block).to.have.property('b_height').that.is.a('number'); - node.expect(block).to.have.property('b_previousBlock'); - node.expect(block).to.have.property('b_numberOfTransactions').that.is.a('number'); - node.expect(block).to.have.property('b_totalAmount').that.is.a('string'); - node.expect(block).to.have.property('b_totalFee').that.is.a('string'); - node.expect(block).to.have.property('b_reward').that.is.a('string'); - node.expect(block).to.have.property('b_payloadLength').that.is.a('number'); - node.expect(block).to.have.property('b_payloadHash').that.is.a('string'); - node.expect(block).to.have.property('b_generatorPublicKey').that.is.a('string'); - node.expect(block).to.have.property('b_blockSignature').that.is.a('string'); - node.expect(block).to.have.property('t_id'); - node.expect(block).to.have.property('t_rowId'); - node.expect(block).to.have.property('t_type'); - node.expect(block).to.have.property('t_timestamp'); - node.expect(block).to.have.property('t_senderPublicKey'); - node.expect(block).to.have.property('t_senderId'); - node.expect(block).to.have.property('t_recipientId'); - node.expect(block).to.have.property('t_amount'); - node.expect(block).to.have.property('t_fee'); - node.expect(block).to.have.property('t_signature'); - node.expect(block).to.have.property('t_signSignature'); - node.expect(block).to.have.property('s_publicKey'); - node.expect(block).to.have.property('d_username'); - node.expect(block).to.have.property('v_votes'); - node.expect(block).to.have.property('m_min'); - node.expect(block).to.have.property('m_lifetime'); - node.expect(block).to.have.property('m_keysgroup'); - node.expect(block).to.have.property('dapp_name'); - node.expect(block).to.have.property('dapp_description'); - node.expect(block).to.have.property('dapp_tags'); - node.expect(block).to.have.property('dapp_type'); - node.expect(block).to.have.property('dapp_link'); - node.expect(block).to.have.property('dapp_category'); - node.expect(block).to.have.property('dapp_icon'); - node.expect(block).to.have.property('in_dappId'); - node.expect(block).to.have.property('ot_dappId'); - node.expect(block).to.have.property('ot_outTransactionId'); - node.expect(block).to.have.property('t_requesterPublicKey'); - node.expect(block).to.have.property('t_signatures'); - }); - done(); - }); - }); -}); - -describe('GET /peer/blocks/common', function () { - - it('using incorrect nethash in headers should fail', function (done) { - node.get('/peer/blocks/common') - .set('nethash', 'incorrect') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body.expected).to.equal(node.config.nethash); - done(); - }); - }); - - it('using no params should fail', function (done) { - node.get('/peer/blocks/common') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('error').to.equal('Missing required property: ids: #/'); - done(); - }); - }); - - it('using ids == "";"";"" should fail', function (done) { - node.get('/peer/blocks/common?ids="";"";""') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('error').to.equal('Invalid block id sequence'); - done(); - }); - }); - - it('using ids == \'\',\'\',\'\' should fail', function (done) { - node.get('/peer/blocks/common?ids=\'\',\'\',\'\'') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('error').to.equal('Invalid block id sequence'); - done(); - }); - }); - - it('using ids == "","","" should fail', function (done) { - node.get('/peer/blocks/common?ids="","",""') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('error').to.equal('Invalid block id sequence'); - done(); - }); - }); - - it('using ids == one,two,three should fail', function (done) { - node.get('/peer/blocks/common?ids=one,two,three') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('error').to.equal('Invalid block id sequence'); - done(); - }); - }); - - it('using ids == "1","2","3" should be ok and return null common block', function (done) { - node.get('/peer/blocks/common?ids="1","2","3"') - .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('common').to.be.null; - done(); - }); - }); - - it('using ids == \'1\',\'2\',\'3\' should be ok and return null common block', function (done) { - node.get('/peer/blocks/common?ids=\'1\',\'2\',\'3\'') - .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('common').to.be.null; - done(); - }); - }); - - it('using ids == 1,2,3 should be ok and return null common block', function (done) { - node.get('/peer/blocks/common?ids=1,2,3') - .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('common').to.be.null; - done(); - }); - }); - - it('using ids which include genesisblock.id should be ok', function (done) { - node.get('/peer/blocks/common?ids=' + [genesisblock.id.toString(),'2','3'].join(',')) - .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('common').to.be.an('object'); - node.expect(res.body.common).to.have.property('height').that.is.a('number'); - node.expect(res.body.common).to.have.property('id').that.is.a('string'); - node.expect(res.body.common).to.have.property('previousBlock').that.is.null; - node.expect(res.body.common).to.have.property('timestamp').that.is.equal(0); - done(); - }); - }); -}); - -describe('POST /peer/blocks', function () { - - it('using incorrect nethash in headers should fail', function (done) { - node.post('/peer/blocks', { dummy: 'dummy' }) - .set('nethash', 'incorrect') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body.expected).to.equal(node.config.nethash); - done(); - }); - }); - - it('using no block should fail', function (done) { - node.post('/peer/blocks') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('error').to.contain('Failed to validate block schema'); - done(); - }); - }); - - it('using invalid block schema should fail', function (done) { - var blockSignature = genesisblock.blockSignature; - genesisblock.blockSignature = null; - - node.post('/peer/blocks', { block: genesisblock }) - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('error').to.contain('Failed to validate block schema'); - genesisblock.blockSignature = blockSignature; - done(); - }); - }); - - it('using valid block schema should be ok', function (done) { - genesisblock.transactions.forEach(function (transaction) { - if (transaction.asset && transaction.asset.delegate) { - transaction.asset.delegate.publicKey = transaction.senderPublicKey; - } - }); - node.post('/peer/blocks', { block: genesisblock }) - .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('blockId').to.equal('6524861224470851795'); - done(); - }); - }); -}); diff --git a/test/api/peer.js b/test/api/peer.js deleted file mode 100644 index d09afddabd4..00000000000 --- a/test/api/peer.js +++ /dev/null @@ -1,110 +0,0 @@ -'use strict'; - -var node = require('./../node.js'); -var ip = require('ip'); - -describe('GET /peer/list', function () { - - before(function (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) { - node.get('/peer/list') - .set('nethash', 'incorrect') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body.expected).to.equal(node.config.nethash); - done(); - }); - }); - - it('using incompatible version in headers should fail', function (done) { - node.get('/peer/list') - .set('version', '0.1.0a') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('message').to.eql('Request is made from incompatible version'); - node.expect(res.body).to.have.property('expected').to.eql('0.0.0a'); - node.expect(res.body).to.have.property('received').to.eql('0.1.0a'); - done(); - }); - }); - - 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; - 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).to.have.property('port').that.is.a('number'); - node.expect(peer).to.have.property('state').that.is.a('number'); - node.expect(peer).to.have.property('os'); - node.expect(peer).to.have.property('version'); - node.expect(peer).to.have.property('broadhash'); - node.expect(peer).to.have.property('height'); - }); - 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 () { - - it('using incorrect nethash in headers should fail', function (done) { - node.get('/peer/height') - .set('nethash', 'incorrect') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body.expected).to.equal(node.config.nethash); - done(); - }); - }); - - it('using incompatible version in headers should fail', function (done) { - node.get('/peer/height') - .set('version', '0.1.0a') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('message').to.eql('Request is made from incompatible version'); - node.expect(res.body).to.have.property('expected').to.eql('0.0.0a'); - node.expect(res.body).to.have.property('received').to.eql('0.1.0a'); - done(); - }); - }); - - it('using valid headers should be ok', function (done) { - node.get('/peer/height') - .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.be.an('object').that.has.property('height'); - node.expect(res.body.height).to.be.a('number').to.be.above(1); - done(); - }); - }); -}); diff --git a/test/api/peer/peer.blocks.ws.js b/test/api/peer/peer.blocks.ws.js new file mode 100644 index 00000000000..a2d2ca8512d --- /dev/null +++ b/test/api/peer/peer.blocks.ws.js @@ -0,0 +1,182 @@ +'use strict'; + +var node = require('../../node.js'); +var ws = require('../../common/wsCommunication'); + +var genesisblock = require('../../genesisBlock.json'); + +describe('blocks', function () { + + it('using valid headers should be ok', function (done) { + ws.call('blocks', null, function (err, res) { + node.debug('> Error / Response:'.grey, JSON.stringify(err), JSON.stringify(res)); + node.expect(res).to.have.property('blocks').that.is.an('array'); + res.blocks.forEach(function (block) { + node.expect(block).to.have.property('b_id').that.is.a('string'); + node.expect(block).to.have.property('b_version').that.is.a('number'); + node.expect(block).to.have.property('b_timestamp').that.is.a('number'); + node.expect(block).to.have.property('b_height').that.is.a('number'); + node.expect(block).to.have.property('b_previousBlock'); + node.expect(block).to.have.property('b_numberOfTransactions').that.is.a('number'); + node.expect(block).to.have.property('b_totalAmount').that.is.a('string'); + node.expect(block).to.have.property('b_totalFee').that.is.a('string'); + node.expect(block).to.have.property('b_reward').that.is.a('string'); + node.expect(block).to.have.property('b_payloadLength').that.is.a('number'); + node.expect(block).to.have.property('b_payloadHash').that.is.a('string'); + node.expect(block).to.have.property('b_generatorPublicKey').that.is.a('string'); + node.expect(block).to.have.property('b_blockSignature').that.is.a('string'); + node.expect(block).to.have.property('t_id'); + node.expect(block).to.have.property('t_rowId'); + node.expect(block).to.have.property('t_type'); + node.expect(block).to.have.property('t_timestamp'); + node.expect(block).to.have.property('t_senderPublicKey'); + node.expect(block).to.have.property('t_senderId'); + node.expect(block).to.have.property('t_recipientId'); + node.expect(block).to.have.property('t_amount'); + node.expect(block).to.have.property('t_fee'); + node.expect(block).to.have.property('t_signature'); + node.expect(block).to.have.property('t_signSignature'); + node.expect(block).to.have.property('s_publicKey'); + node.expect(block).to.have.property('d_username'); + node.expect(block).to.have.property('v_votes'); + node.expect(block).to.have.property('m_min'); + node.expect(block).to.have.property('m_lifetime'); + node.expect(block).to.have.property('m_keysgroup'); + node.expect(block).to.have.property('dapp_name'); + node.expect(block).to.have.property('dapp_description'); + node.expect(block).to.have.property('dapp_tags'); + node.expect(block).to.have.property('dapp_type'); + node.expect(block).to.have.property('dapp_link'); + node.expect(block).to.have.property('dapp_category'); + node.expect(block).to.have.property('dapp_icon'); + node.expect(block).to.have.property('in_dappId'); + node.expect(block).to.have.property('ot_dappId'); + node.expect(block).to.have.property('ot_outTransactionId'); + node.expect(block).to.have.property('t_requesterPublicKey'); + node.expect(block).to.have.property('t_signatures'); + }); + done(); + }); + }); +}); + +describe('blocksCommon', function () { + + it('using no params should fail', function (done) { + ws.call('blocksCommon', function (err, res) { + node.debug('> Error / Response:'.grey, JSON.stringify(err), JSON.stringify(res)); + node.expect(res).to.be.undefined; + node.expect(err).to.equal('Missing required property: ids: #/'); + done(); + }); + }); + + it('using ids == "";"";"" should fail', function (done) { + ws.call('blocksCommon', {ids: '"";"";""'}, function (err, res) { + node.debug('> Error / Response:'.grey, JSON.stringify(err), JSON.stringify(res)); + node.expect(err).to.equal('Invalid block id sequence'); + done(); + }); + }); + + it('using ids == \'\',\'\',\'\' should fail', function (done) { + ws.call('blocksCommon', {ids: '\'\',\'\',\'\''}, function (err, res) { + node.debug('> Error / Response:'.grey, JSON.stringify(err), JSON.stringify(res)); + + node.expect(err).to.equal('Invalid block id sequence'); + done(); + }); + }); + + it('using ids == "","","" should fail', function (done) { + ws.call('blocksCommon', {ids: '"","",""'}, function (err, res) { + node.debug('> Error / Response:'.grey, JSON.stringify(err), JSON.stringify(res)); + node.expect(err).to.equal('Invalid block id sequence'); + done(); + }); + }); + + it('using ids == one,two,three should fail', function (done) { + ws.call('blocksCommon', {ids: 'one,two,three'}, function (err, res) { + node.debug('> Error / Response:'.grey, JSON.stringify(err), JSON.stringify(res)); + node.expect(err).to.equal('Invalid block id sequence'); + done(); + }); + }); + + it('using ids == "1","2","3" should be ok and return null common block', function (done) { + ws.call('blocksCommon', {ids: '"1","2","3"'}, function (err, res) { + node.debug('> Error / Response:'.grey, JSON.stringify(err), JSON.stringify(res)); + + node.expect(res).to.have.property('common').to.be.null; + done(); + }); + }); + + it('using ids == \'1\',\'2\',\'3\' should be ok and return null common block', function (done) { + ws.call('blocksCommon', {ids: '\'1\',\'2\',\'3\''}, function (err, res) { + node.debug('> Error / Response:'.grey, JSON.stringify(err), JSON.stringify(res)); + + node.expect(res).to.have.property('common').to.be.null; + done(); + }); + }); + + it('using ids == 1,2,3 should be ok and return null common block', function (done) { + ws.call('blocksCommon', {ids: '1,2,3'}, function (err, res) { + node.debug('> Error / Response:'.grey, JSON.stringify(err), JSON.stringify(res)); + + node.expect(res).to.have.property('common').to.be.null; + done(); + }); + }); + + it('using ids which include genesisblock.id should be ok', function (done) { + ws.call('blocksCommon', {ids: [genesisblock.id.toString(),'2','3'].join(',')}, function (err, res) { + node.debug('> Error / Response:'.grey, JSON.stringify(err), JSON.stringify(res)); + + node.expect(res).to.have.property('common').to.be.an('object'); + node.expect(res.common).to.have.property('height').that.is.a('number'); + node.expect(res.common).to.have.property('id').that.is.a('string'); + node.expect(res.common).to.have.property('previousBlock').that.is.null; + node.expect(res.common).to.have.property('timestamp').that.is.equal(0); + done(); + }); + }); +}); + +describe('postBlock', function () { + + it('using no block should fail', function (done) { + ws.call('postBlock', function (err, res) { + node.debug('> Error / Response:'.grey, JSON.stringify(err), JSON.stringify(res)); + node.expect(err).to.contain('Failed to validate block schema'); + done(); + }); + }); + + it('using invalid block schema should fail', function (done) { + var blockSignature = genesisblock.blockSignature; + genesisblock.blockSignature = null; + + ws.call('postBlock', { block: genesisblock }, function (err, res) { + node.debug('> Error / Response:'.grey, JSON.stringify(err), JSON.stringify(res)); + node.expect(err).to.contain('Failed to validate block schema'); + genesisblock.blockSignature = blockSignature; + done(); + }); + }); + + it('using valid block schema should be ok', function (done) { + genesisblock.transactions.forEach(function (transaction) { + if (transaction.asset && transaction.asset.delegate) { + transaction.asset.delegate.publicKey = transaction.senderPublicKey; + } + }); + ws.call('postBlock', { block: genesisblock }, function (err, res) { + node.debug('> Error / Response:'.grey, JSON.stringify(err), JSON.stringify(res)); + node.expect(res).to.have.property('blockId').to.equal('6524861224470851795'); + done(); + }); + }); +}); diff --git a/test/api/peer.signatures.js b/test/api/peer/peer.signatures.ws.js similarity index 54% rename from test/api/peer.signatures.js rename to test/api/peer/peer.signatures.ws.js index 7a2ba941481..340d1db7f43 100644 --- a/test/api/peer.signatures.js +++ b/test/api/peer/peer.signatures.ws.js @@ -1,25 +1,27 @@ 'use strict'; -var node = require('./../node.js'); +var node = require('../../node.js'); +var http = require('../../common/httpCommunication.js'); +var ws = require('../../common/wsCommunication.js'); var owner = node.randomAccount(); var coSigner1 = node.randomAccount(); var coSigner2 = node.randomAccount(); function postTransaction (transaction, done) { - node.post('/peer/transactions', { + ws.call('postTransactions', { transaction: transaction - }, done); + }, done, true); } function postTransactions (transactions, done) { - node.post('/peer/transactions', { + ws.call('postTransactions', { transactions: transactions - }, done); + }, done, true); } function postSignature (transaction, signature, done) { - node.post('/peer/signatures', { + ws.call('postSignatures', { signature: { transaction: transaction.id, signature: signature @@ -27,47 +29,21 @@ function postSignature (transaction, signature, done) { }, done); } -describe('GET /peer/signatures', function () { - - it('using incorrect nethash in headers should fail', function (done) { - node.get('/peer/signatures') - .set('nethash', 'incorrect') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body.expected).to.equal(node.config.nethash); - done(); - }); - }); - - it('using incompatible version in headers should fail', function (done) { - node.get('/peer/signatures') - .set('version', '0.1.0a') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('message').to.eql('Request is made from incompatible version'); - node.expect(res.body).to.have.property('expected').to.eql('0.0.0a'); - node.expect(res.body).to.have.property('received').to.eql('0.1.0a'); - done(); - }); - }); +describe('getSignatures', function () { it('using valid headers should be ok', function (done) { - node.get('/peer/signatures') - .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('signatures').that.is.an('array'); - done(); - }); + ws.call('getSignatures', function (err, res) { + node.debug('> Response:'.grey, JSON.stringify(res)); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('signatures').that.is.an('array'); + done(); + }); }); }); -describe('POST /peer/signatures', function () { +describe('postSignatures', function () { var validParams; - var transaction = node.lisk.transaction.createTransaction('1L', 1, node.gAccount.password); beforeEach(function (done) { @@ -80,56 +56,28 @@ describe('POST /peer/signatures', function () { done(); }); - it('using incorrect nethash in headers should fail', function (done) { - node.post('/peer/signatures') - .set('nethash', 'incorrect') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body.expected).to.equal(node.config.nethash); - done(); - }); - }); - - it('using incompatible version in headers should fail', function (done) { - node.post('/peer/signatures') - .set('version', '0.1.0a') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('message').to.eql('Request is made from incompatible version'); - node.expect(res.body).to.have.property('expected').to.eql('0.0.0a'); - node.expect(res.body).to.have.property('received').to.eql('0.1.0a'); - done(); - }); - }); - it('using invalid signature schema should fail', function (done) { delete validParams.signature.transaction; - node.post('/peer/signatures', validParams) - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('message').to.equal('Invalid signature body'); - done(); - }); + ws.call('postSignatures', validParams, function (err, res) { + node.debug('> Response:'.grey, JSON.stringify(res)); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Invalid signature body'); + done(); + }); }); it('using unprocessable signature should fail', function (done) { validParams.signature.transaction = '1'; - node.post('/peer/signatures', validParams) - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('message').to.equal('Error processing signature: Transaction not found'); - done(); - }); + ws.call('postSignatures', validParams, function (err, res) { + node.debug('> Response:'.grey, JSON.stringify(res)); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Error processing signature: Transaction not found'); + done(); + }); }); - it('using processable signature should be ok'); - describe('creating a new multisignature account', function () { var transaction; @@ -145,7 +93,7 @@ describe('POST /peer/signatures', function () { eachSeriesCb(); }, function (err) { postTransactions(transactions, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; node.onNewBlock(function (err) { done(); }); @@ -162,7 +110,7 @@ describe('POST /peer/signatures', function () { transaction = node.lisk.multisignature.createMultisignature(owner.password, null, keysgroup, lifetime, min); postTransactions([transaction], function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; node.onNewBlock(function (err) { done(); }); @@ -173,8 +121,8 @@ describe('POST /peer/signatures', function () { var signature = node.lisk.multisignature.signTransaction(transaction, owner.password); postSignature(transaction, signature, 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('Error processing signature: Failed to verify signature'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Error processing signature: Failed to verify signature'); done(); }); }); @@ -183,7 +131,7 @@ describe('POST /peer/signatures', function () { var signature = node.lisk.multisignature.signTransaction(transaction, coSigner1.password); postSignature(transaction, signature, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; done(); }); }); @@ -191,7 +139,7 @@ describe('POST /peer/signatures', function () { it('using processable signature for coSigner1 should not confirm the transaction', function (done) { node.onNewBlock(function (err) { node.onNewBlock(function (err) { - node.get('/api/transactions/get?id=' + transaction.id, function (err, res) { + http.get('/api/transactions/get?id=' + transaction.id, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; done(); }); @@ -203,14 +151,14 @@ describe('POST /peer/signatures', function () { var signature = node.lisk.multisignature.signTransaction(transaction, coSigner2.password); postSignature(transaction, signature, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; done(); }); }); it('using processable signature for coSigner2 should confirm the transaction', function (done) { node.onNewBlock(function (err) { - node.get('/api/transactions/get?id=' + transaction.id, function (err, res) { + http.get('/api/transactions/get?id=' + transaction.id, 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(transaction.id); @@ -233,7 +181,7 @@ describe('POST /peer/signatures', function () { transaction = node.lisk.multisignature.createTransaction('1L', 1, owner.password); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; node.onNewBlock(function (err) { done(); }); @@ -244,7 +192,7 @@ describe('POST /peer/signatures', function () { var signature = node.lisk.multisignature.signTransaction(transaction, coSigner1.password); postSignature(transaction, signature, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; done(); }); }); @@ -252,7 +200,7 @@ describe('POST /peer/signatures', function () { it('using processable signature for coSigner1 should not confirm the transaction', function (done) { node.onNewBlock(function (err) { node.onNewBlock(function (err) { - node.get('/api/transactions/get?id=' + transaction.id, function (err, res) { + http.get('/api/transactions/get?id=' + transaction.id, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; done(); }); @@ -264,14 +212,14 @@ describe('POST /peer/signatures', function () { var signature = node.lisk.multisignature.signTransaction(transaction, coSigner2.password); postSignature(transaction, signature, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; done(); }); }); it('using processable signature for coSigner2 should confirm the transaction', function (done) { node.onNewBlock(function (err) { - node.get('/api/transactions/get?id=' + transaction.id, function (err, res) { + http.get('/api/transactions/get?id=' + transaction.id, 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(transaction.id); @@ -281,9 +229,4 @@ describe('POST /peer/signatures', function () { }); }); - describe('using multiple signatures', function () { - it('with unprocessable signature should fail'); - - it('with processable signature should be ok'); - }); }); diff --git a/test/api/peer.transactions.collision.js b/test/api/peer/peer.transactions.collision.ws.js similarity index 71% rename from test/api/peer.transactions.collision.js rename to test/api/peer/peer.transactions.collision.ws.js index 92cc1bab192..cd8e041a9ae 100644 --- a/test/api/peer.transactions.collision.js +++ b/test/api/peer/peer.transactions.collision.ws.js @@ -1,18 +1,19 @@ 'use strict'; var crypto = require('crypto'); -var node = require('./../node.js'); +var node = require('../../node.js'); +var ws = require('../../common/wsCommunication.js'); -var modulesLoader = require('../common/initModule').modulesLoader; -var Account = require('../../logic/account'); +var modulesLoader = require('../../common/initModule').modulesLoader; +var Account = require('../../../logic/account'); function postTransaction (transaction, done) { - node.post('/peer/transactions', { + ws.call('postTransactions', { transaction: transaction - }, done); + }, done, true); } -describe('POST /peer/transactions', function () { +describe('postTransaction', function () { describe('when two passphrases collide into the same address', function () { @@ -40,7 +41,7 @@ describe('POST /peer/transactions', function () { // Send funds to collision account var transaction = node.lisk.transaction.createTransaction(collision.address, 220000000, node.gAccount.password); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; node.onNewBlock(done); }); }); @@ -53,8 +54,8 @@ describe('POST /peer/transactions', function () { transaction.id = node.lisk.crypto.getId(transaction); 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('Failed to verify signature'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Failed to verify signature'); done(); }); }); @@ -65,8 +66,8 @@ describe('POST /peer/transactions', function () { transaction.id = node.lisk.crypto.getId(transaction); 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('Failed to verify signature'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Failed to verify signature'); done(); }); }); @@ -82,7 +83,7 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.transaction.createTransaction(node.gAccount.address, 100000000, collision.passphrases[0]); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; done(); }); }); @@ -91,8 +92,8 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.transaction.createTransaction(node.gAccount.address, 100000000, collision.passphrases[1]); 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).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Invalid sender public key: b26dd40ba33e4785e49ddc4f106c0493ed00695817235c778f487aea5866400a expected: ce33db918b059a6e99c402963b42cf51c695068007ef01d8c383bb8a41270263'); done(); }); }); diff --git a/test/api/peer.transactions.delegates.js b/test/api/peer/peer.transactions.delegates.ws.js similarity index 74% rename from test/api/peer.transactions.delegates.js rename to test/api/peer/peer.transactions.delegates.ws.js index 72f939864be..e4ccd8ab748 100644 --- a/test/api/peer.transactions.delegates.js +++ b/test/api/peer/peer.transactions.delegates.ws.js @@ -1,38 +1,37 @@ 'use strict'; var crypto = require('crypto'); -var node = require('./../node.js'); +var node = require('../../node.js'); +var ws = require('../../common/wsCommunication.js'); var account = node.randomAccount(); var account2 = node.randomAccount(); function postTransaction (transaction, done) { - node.post('/peer/transactions', { + ws.call('postTransactions', { transaction: transaction - }, function (err, res) { - done(err, res); - }); + }, done, true); } function sendLISK (params, done) { var transaction = node.lisk.transaction.createTransaction(params.recipientId, params.amount, params.secret); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; node.onNewBlock(function (err) { done(err, res); }); }); } -describe('POST /peer/transactions', function () { +describe('postTransactions', function () { describe('registering a delegate', function () { it('using undefined transaction', function (done) { postTransaction(undefined, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('message').to.contain('Invalid transaction body'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.contain('Invalid transaction body'); done(); }); }); @@ -44,8 +43,8 @@ describe('POST /peer/transactions', function () { delete transaction.asset; 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.contain('Invalid transaction body'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.contain('Invalid transaction body'); done(); }); }); @@ -57,8 +56,8 @@ describe('POST /peer/transactions', function () { transaction.fee = node.fees.delegateRegistrationFee; 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.match(/Account does not have enough LSK: [0-9]+L balance: 0/); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.match(/Account does not have enough LSK: [0-9]+L balance: 0/); done(); }); }); @@ -79,7 +78,7 @@ describe('POST /peer/transactions', function () { transaction.fee = node.fees.delegateRegistrationFee; postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('success').to.be.not.ok; done(); }); }); @@ -89,7 +88,7 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.delegate.createDelegate(account.password, account.username); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('success').to.be.not.ok; done(); }); }); @@ -99,7 +98,7 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.delegate.createDelegate(account2.password, account.username.toUpperCase()); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('success').to.be.not.ok; done(); }); }); @@ -110,8 +109,8 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.delegate.createDelegate(account.password, account.username); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactionId').to.equal(transaction.id); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').to.equal(transaction.id); done(); }); }); @@ -135,11 +134,11 @@ describe('POST /peer/transactions', function () { var transaction2 = node.lisk.delegate.createDelegate(account2.password, account2.username); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; node.onNewBlock(function () { postTransaction(transaction2, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('success').to.be.not.ok; done(); }); }); diff --git a/test/api/peer.transactions.main.js b/test/api/peer/peer.transactions.main.ws.js similarity index 55% rename from test/api/peer.transactions.main.js rename to test/api/peer/peer.transactions.main.ws.js index fdbeb088d80..71aadf2c5dc 100644 --- a/test/api/peer.transactions.main.js +++ b/test/api/peer/peer.transactions.main.ws.js @@ -1,89 +1,60 @@ 'use strict'; var crypto = require('crypto'); -var node = require('./../node.js'); +var node = require('../../node.js'); +var http = require('../../common/httpCommunication.js'); +var ws = require('../../common/wsCommunication.js'); var genesisblock = require('../../genesisBlock.json'); function postTransaction (transaction, done) { - node.post('/peer/transactions', { + ws.call('postTransactions', { transaction: transaction - }, done); + }, done, true); } function getAddress (address, done) { - node.get('/api/accounts?address=' + address, done); + http.get('/api/accounts?address=' + address, done); } -describe('GET /peer/transactions', function () { - - it('using incorrect nethash in headers should fail', function (done) { - node.get('/peer/transactions') - .set('nethash', 'incorrect') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body.expected).to.equal(node.config.nethash); - done(); - }); - }); - - it('using incompatible version in headers should fail', function (done) { - node.get('/peer/transactions') - .set('version', '0.1.0a') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('message').to.eql('Request is made from incompatible version'); - node.expect(res.body).to.have.property('expected').to.eql('0.0.0a'); - node.expect(res.body).to.have.property('received').to.eql('0.1.0a'); - done(); - }); - }); +describe('getTransactions', function () { it('using valid headers should be ok', function (done) { - node.get('/peer/transactions') - .end(function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactions').to.be.an('array'); - done(); - }); + ws.call('getTransactions', function (err, res) { + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactions').to.be.an('array'); + done(); + }); }); -}); -describe('POST /peer/transactions', function () { + before(function (done) { + var randomAccount = node.randomAccount(); + var transaction = node.lisk.transaction.createTransaction(randomAccount.address, 1, node.gAccount.password); - it('using incorrect nethash in headers should fail', function (done) { - node.post('/peer/transactions') - .set('nethash', 'incorrect') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body.expected).to.equal(node.config.nethash); - done(); - }); + postTransaction(transaction, function (err, res) { + node.expect(res).to.have.property('success').to.be.ok; + done(err); + }); }); - it('using incompatible version in headers should fail', function (done) { - node.post('/peer/transactions') - .set('version', '0.1.0a') - .end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('message').to.eql('Request is made from incompatible version'); - node.expect(res.body).to.have.property('expected').to.eql('0.0.0a'); - node.expect(res.body).to.have.property('received').to.eql('0.1.0a'); - done(); - }); + it('should return non empty transaction list after post', function (done) { + ws.call('getTransactions', function (err, res) { + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactions').to.be.an('array').and.to.be.not.empty; + done(); + }); }); +}); + +describe('postTransactions', function () { it('using valid headers should be ok', function (done) { var account = node.randomAccount(); var transaction = node.lisk.transaction.createTransaction(account.address, 1, node.gAccount.password); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactionId').to.equal(transaction.id); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').to.equal(transaction.id); done(); }); }); @@ -93,12 +64,12 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.transaction.createTransaction(account.address, 1, node.gAccount.password); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactionId').to.equal(transaction.id); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').to.equal(transaction.id); 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.match(/Transaction is already processed: [0-9]+/); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.match(/Transaction is already processed: [0-9]+/); done(); }); }); @@ -109,13 +80,13 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.transaction.createTransaction(account.address, 1, node.gAccount.password); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactionId').to.equal(transaction.id); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').to.equal(transaction.id); node.onNewBlock(function (err) { 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.match(/Transaction is already confirmed: [0-9]+/); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.match(/Transaction is already confirmed: [0-9]+/); done(); }); }); @@ -129,12 +100,12 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.transaction.createTransaction(address, 100000000, node.gAccount.password); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; node.onNewBlock(function (err) { var transaction2 = node.lisk.transaction.createTransaction(address.toLowerCase(), 100000000, node.gAccount.password); postTransaction(transaction2, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; node.onNewBlock(function (err) { getAddress(address, function (err, res) { @@ -153,8 +124,8 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.transaction.createTransaction(undefined, 1, node.gAccount.password); 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.eql('Missing recipient'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.eql('Missing recipient'); done(); }); }); @@ -163,8 +134,8 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.transaction.createTransaction('0123456789001234567890L', 1, node.gAccount.password); 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.contain('Invalid transaction body'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.contain('Invalid transaction body'); done(); }); }); @@ -173,8 +144,8 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.transaction.createTransaction('1L', -1, node.gAccount.password); 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'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message'); done(); }); }); @@ -185,8 +156,8 @@ describe('POST /peer/transactions', function () { transaction.id = node.lisk.crypto.getId(transaction); 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'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message'); done(); }); }); @@ -195,8 +166,8 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.transaction.createTransaction('1L', 1, 'randomstring'); 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.match(/Account does not have enough LSK: [0-9]+L balance: 0/); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.match(/Account does not have enough LSK: [0-9]+L balance: 0/); done(); }); }); @@ -206,8 +177,8 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.transaction.createTransaction(account.address, 1, node.gAccount.password); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactionId').to.equal(transaction.id); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').to.equal(transaction.id); node.onNewBlock(function () { var count = 1; @@ -215,8 +186,8 @@ describe('POST /peer/transactions', function () { node.async.doUntil(function (next) { postTransaction(transaction2, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('message').to.match(/Account does not have enough LSK: [0-9]+L balance: 1e-8/); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.match(/Account does not have enough LSK: [0-9]+L balance: 1e-8/); count++; return next(); }); @@ -235,8 +206,8 @@ describe('POST /peer/transactions', function () { transaction.id = node.lisk.crypto.getId(transaction); 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'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message'); done(); }); }); @@ -246,8 +217,8 @@ describe('POST /peer/transactions', function () { transaction.senderPublicKey = node.randomPassword(); 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'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message'); done(); }); }); @@ -257,8 +228,8 @@ describe('POST /peer/transactions', function () { transaction.signature = node.randomPassword(); 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'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message'); done(); }); }); @@ -268,8 +239,8 @@ describe('POST /peer/transactions', function () { transaction.blockId = genesisblock.id; 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'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message'); done(); }); }); @@ -279,8 +250,8 @@ describe('POST /peer/transactions', function () { node.gAccount.password); 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'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message'); done(); }); }); @@ -289,8 +260,8 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.transaction.createTransaction('12L', 1.3, node.gAccount.password); 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'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message'); done(); }); }); @@ -312,16 +283,11 @@ describe('POST /peer/transactions', function () { it('should fail', function (done) { postTransaction(signedTransactionFromGenesis, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('message').equals('Invalid sender. Can not send from genesis account'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').equals('Invalid sender. Can not send from genesis account'); done(); }); }); }); - describe('using multiple transactions', function () { - it('with invalid transaction should fail'); - - it('with valid transaction should be ok'); - }); }); diff --git a/test/api/peer.transactions.signatures.js b/test/api/peer/peer.transactions.signatures.ws.js similarity index 66% rename from test/api/peer.transactions.signatures.js rename to test/api/peer/peer.transactions.signatures.ws.js index e2c74afd2da..ad1f386bf82 100644 --- a/test/api/peer.transactions.signatures.js +++ b/test/api/peer/peer.transactions.signatures.ws.js @@ -1,39 +1,36 @@ 'use strict'; var crypto = require('crypto'); -var node = require('./../node.js'); +var node = require('../../node.js'); +var ws = require('../../common/wsCommunication.js'); var account = node.randomAccount(); -var account2 = node.randomAccount(); -var account3 = node.randomAccount(); function postTransaction (transaction, done) { - node.post('/peer/transactions', { + ws.call('postTransactions', { transaction: transaction - }, function (err, res) { - done(err, res); - }); + }, done, true); } function sendLISK (params, done) { var transaction = node.lisk.transaction.createTransaction(params.recipientId, params.amount, params.secret); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; node.onNewBlock(function (err) { done(err, res); }); }); } -describe('POST /peer/transactions', function () { +describe('postTransaction', function () { describe('enabling second signature', function () { it('using undefined transaction', function (done) { postTransaction(undefined, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('message').to.contain('Invalid transaction body'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.contain('Invalid transaction body'); done(); }); }); @@ -44,8 +41,8 @@ describe('POST /peer/transactions', function () { delete transaction.asset; 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.contain('Invalid transaction body'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.contain('Invalid transaction body'); done(); }); }); @@ -56,8 +53,8 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.signature.createSignature(node.randomPassword(), node.randomPassword()); 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.match(/Account does not have enough LSK: [0-9]+L balance: 0/); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.match(/Account does not have enough LSK: [0-9]+L balance: 0/); done(); }); }); @@ -78,8 +75,8 @@ describe('POST /peer/transactions', function () { transaction.fee = node.fees.secondPasswordFee; postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactionId').to.equal(transaction.id); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').to.equal(transaction.id); done(); }); }); @@ -98,7 +95,7 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.transaction.createTransaction('1L', 1, node.gAccount.password, account.secondPassword); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('success').to.be.not.ok; done(); }); }); @@ -107,8 +104,8 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.transaction.createTransaction('1L', 1, account.password, ''); 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('Missing sender second signature'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Missing sender second signature'); done(); }); }); @@ -119,8 +116,8 @@ describe('POST /peer/transactions', function () { transaction.id = node.lisk.crypto.getId(transaction); 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('Failed to verify second signature'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Failed to verify second signature'); done(); }); }); @@ -129,8 +126,8 @@ describe('POST /peer/transactions', function () { var transaction = node.lisk.transaction.createTransaction('1L', 1, account.password, account.secondPassword); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactionId').to.equal(transaction.id); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').to.equal(transaction.id); done(); }); }); diff --git a/test/api/peer.transactions.stress.js b/test/api/peer/peer.transactions.stress.ws.js similarity index 80% rename from test/api/peer.transactions.stress.js rename to test/api/peer/peer.transactions.stress.ws.js index 43eab29e1bf..60dd6408ef3 100644 --- a/test/api/peer.transactions.stress.js +++ b/test/api/peer/peer.transactions.stress.ws.js @@ -1,20 +1,22 @@ 'use strict'; -var node = require('./../node.js'); +var node = require('../../node.js'); +var http = require('../../common/httpCommunication.js'); +var ws = require('../../common/wsCommunication.js'); function postTransaction (transaction, done) { - node.post('/peer/transactions', { + ws.call('postTransactions', { transaction: transaction - }, done); + }, done, true); } function postTransactions (transactions, done) { - node.post('/peer/transactions', { + ws.call('postTransactions', { transactions: transactions - }, done); + }, done, true); } -describe('POST /peer/transactions', function () { +describe('postTransactions', function () { describe('sending 1000 bundled transfers to random addresses', function () { @@ -39,7 +41,7 @@ describe('POST /peer/transactions', function () { } postTransactions(bundled, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; next(); }); }, function () { @@ -53,7 +55,7 @@ describe('POST /peer/transactions', function () { var blocksToWait = Math.ceil(maximum / node.constants.maxTxsPerBlock); node.waitForBlocks(blocksToWait, function (err) { node.async.eachSeries(transactions, function (transaction, eachSeriesCb) { - node.get('/api/transactions/get?id=' + transaction.id, function (err, res) { + http.get('/api/transactions/get?id=' + transaction.id, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('transaction').that.is.an('object'); return setImmediate(eachSeriesCb); @@ -78,8 +80,8 @@ describe('POST /peer/transactions', function () { ); postTransaction(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactionId').to.equal(transaction.id); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').to.equal(transaction.id); transactions.push(transaction); count++; next(); @@ -95,7 +97,7 @@ describe('POST /peer/transactions', function () { var blocksToWait = Math.ceil(maximum / node.constants.maxTxsPerBlock); node.waitForBlocks(blocksToWait, function (err) { node.async.eachSeries(transactions, function (transaction, eachSeriesCb) { - node.get('/api/transactions/get?id=' + transaction.id, function (err, res) { + http.get('/api/transactions/get?id=' + transaction.id, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('transaction').that.is.an('object'); return setImmediate(eachSeriesCb); diff --git a/test/api/peer.transactions.votes.js b/test/api/peer/peer.transactions.votes.ws.js similarity index 72% rename from test/api/peer.transactions.votes.js rename to test/api/peer/peer.transactions.votes.ws.js index 163529f2348..1a091c90797 100644 --- a/test/api/peer.transactions.votes.js +++ b/test/api/peer/peer.transactions.votes.ws.js @@ -1,6 +1,9 @@ 'use strict'; -var node = require('./../node.js'); +var async = require('async'); +var node = require('../../node.js'); +var http = require('../../common/httpCommunication.js'); +var ws = require('../../common/wsCommunication.js'); var account = node.randomAccount(); @@ -9,7 +12,7 @@ var delegates = []; var votedDelegates = []; function getDelegates (done) { - node.get('/api/delegates', function (err, res) { + http.get('/api/delegates', 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'); return done(err, res); @@ -17,7 +20,7 @@ function getDelegates (done) { } function getVotes (address, done) { - node.get('/api/accounts/delegates/?address=' + address, function (err, res) { + http.get('/api/accounts/delegates/?address=' + address, 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'); return done(err, res); @@ -43,13 +46,11 @@ function postVotes (params, done) { } function postVote (transaction, done) { - node.post('/peer/transactions', { transaction: transaction }, function (err, res) { - return done(err, res); - }); + ws.call('postTransactions', { transaction: transaction }, done, true); } function sendLISK (params, done) { - node.put('/api/transactions', params, function (err, res) { + http.put('/api/transactions', params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.onNewBlock(function (err) { return done(err, res); @@ -61,15 +62,15 @@ function registerDelegate (account, done) { account.username = node.randomDelegateName().toLowerCase(); var transaction = node.lisk.delegate.createDelegate(account.password, account.username); - node.post('/peer/transactions', { transaction: transaction }, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + ws.call('postTransactions', { transaction: transaction }, function (err, res) { + node.expect(res).to.have.property('success').to.be.ok; node.onNewBlock(function (err) { return done(err, res); }); - }); + }, true); } -describe('POST /peer/transactions', function () { +describe('postTransactions', function () { before(function (done) { sendLISK({ @@ -107,15 +108,15 @@ describe('POST /peer/transactions', function () { passphrase: account.password, action: '-', voteCb: function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; } }, done); }); it('using undefined transaction', function (done) { postVote(undefined, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.not.ok; - node.expect(res.body).to.have.property('message').to.contain('Invalid transaction body'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Invalid transaction body - Empty trs passed'); done(); }); }); @@ -126,8 +127,8 @@ describe('POST /peer/transactions', function () { delete transaction.asset; 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.contain('Invalid transaction body'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.contain('Invalid transaction body'); done(); }); }); @@ -136,8 +137,8 @@ describe('POST /peer/transactions', function () { 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'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Invalid vote at index 0 - Invalid vote type'); done(); }); }); @@ -146,8 +147,8 @@ describe('POST /peer/transactions', function () { 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'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Invalid vote at index 0 - Invalid vote format'); done(); }); }); @@ -156,8 +157,8 @@ describe('POST /peer/transactions', function () { 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'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Invalid vote at index 0 - Invalid vote length'); done(); }); }); @@ -166,8 +167,8 @@ describe('POST /peer/transactions', function () { 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'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Invalid vote at index 0 - Invalid vote length'); done(); }); }); @@ -177,7 +178,7 @@ describe('POST /peer/transactions', function () { function (seriesCb) { var transaction = node.lisk.vote.createVote(account.password, ['+' + delegate]); postVote(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; return seriesCb(); }); }, @@ -187,7 +188,7 @@ describe('POST /peer/transactions', function () { function (seriesCb) { var transaction2 = node.lisk.vote.createVote(account.password, ['+' + delegate]); postVote(transaction2, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; return seriesCb(); }); }, @@ -197,7 +198,7 @@ describe('POST /peer/transactions', function () { function (seriesCb) { var transaction2 = node.lisk.vote.createVote(account.password, ['+' + delegate]); postVote(transaction2, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('success').to.be.not.ok; return seriesCb(); }); }, @@ -215,8 +216,8 @@ describe('POST /peer/transactions', function () { it('removing votes from a delegate should be ok', 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.ok; - node.expect(res.body).to.have.property('transactionId').to.equal(transaction.id); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').to.equal(transaction.id); node.onNewBlock(function (err) { return done(err); }); @@ -229,8 +230,8 @@ describe('POST /peer/transactions', function () { })); postVote(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactionId').to.equal(transaction.id); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').to.equal(transaction.id); node.onNewBlock(function (err) { return done(err); }); @@ -243,8 +244,8 @@ describe('POST /peer/transactions', function () { })); postVote(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactionId').to.equal(transaction.id); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').to.equal(transaction.id); node.onNewBlock(function (err) { return done(err); }); @@ -257,8 +258,8 @@ describe('POST /peer/transactions', function () { })); 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('Voting limit exceeded. Maximum is 33 votes per transaction'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Voting limit exceeded. Maximum is 33 votes per transaction'); node.onNewBlock(function (err) { return done(err); }); @@ -271,8 +272,8 @@ describe('POST /peer/transactions', function () { passphrase: account.password, action: '+', voteCb: function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactionId').that.is.a('string'); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').that.is.a('string'); } }, done); }); @@ -283,8 +284,8 @@ describe('POST /peer/transactions', function () { })); 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('Voting limit exceeded. Maximum is 33 votes per transaction'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Voting limit exceeded. Maximum is 33 votes per transaction'); node.onNewBlock(function (err) { return done(err); }); @@ -297,8 +298,8 @@ describe('POST /peer/transactions', function () { passphrase: account.password, action: '-', voteCb: function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactionId').that.is.a('string'); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').that.is.a('string'); } }, done); }); @@ -332,8 +333,8 @@ describe('POST /peer/transactions after registering a new delegate', function () var transaction = node.lisk.vote.createVote(account.password, ['+' + account.publicKey]); postVote(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactionId').to.equal(transaction.id); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').to.equal(transaction.id); node.onNewBlock(function (err) { return done(err); }); @@ -341,7 +342,7 @@ describe('POST /peer/transactions after registering a new delegate', function () }); it('exceeding maximum of 101 votes should fail', function (done) { - node.async.series([ + async.series([ function (seriesCb) { getVotes(account.address, function (err, res) { node.expect(res.body).to.have.property('delegates').that.has.lengthOf(1); @@ -357,7 +358,7 @@ describe('POST /peer/transactions after registering a new delegate', function () passphrase: account.password, action: '+', voteCb: function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('success').to.be.ok; } }, seriesCb); }, @@ -370,8 +371,8 @@ describe('POST /peer/transactions after registering a new delegate', function () })); 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('Maximum number of 101 votes exceeded (1 too many)'); + node.expect(res).to.have.property('success').to.be.not.ok; + node.expect(res).to.have.property('message').to.equal('Maximum number of 101 votes exceeded (1 too many)'); seriesCb(); }); }, @@ -390,8 +391,8 @@ describe('POST /peer/transactions after registering a new delegate', function () var transaction = node.lisk.vote.createVote(account.password, ['-' + account.publicKey]); postVote(transaction, function (err, res) { - node.expect(res.body).to.have.property('success').to.be.ok; - node.expect(res.body).to.have.property('transactionId').to.equal(transaction.id); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('transactionId').to.equal(transaction.id); node.onNewBlock(function (err) { return done(err); }); diff --git a/test/api/peer/peer.ws.js b/test/api/peer/peer.ws.js new file mode 100644 index 00000000000..3a29f4e48b6 --- /dev/null +++ b/test/api/peer/peer.ws.js @@ -0,0 +1,46 @@ +'use strict'; + +var node = require('../../node.js'); +var ws = require('../../common/wsCommunication'); + +var ip = require('ip'); + + +//because of acceptable function (non accepting of private addresses) this will fail if running against +//lisk instance without shell variable NODE_ENV set to TEST +describe('list', function () { + + before(function (done) { + ws.addPeers(2, '0.0.0.0', done); + }); + + before(function (done) { + ws.addPeers(1, ip.address('public'), done); + }); + + + it('should return non empty peers list', function (done) { + ws.call('list', null, function (err, res) { + node.debug('> Response:'.grey, JSON.stringify(res)); + node.expect(err).to.be.null; + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('peers').to.be.an('array').and.not.empty; + done(); + }); + }); + +}); + +describe('height', function () { + + it('should receive height', function (done) { + ws.call('height', null, function (err, res) { + node.debug('> Response:'.grey, JSON.stringify(res)); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.be.an('object').that.has.property('height'); + node.expect(res.height).to.be.a('number').to.be.at.least(1); + done(); + }); + }); + +}); diff --git a/test/api/peer/transport.js b/test/api/peer/transport.js new file mode 100644 index 00000000000..3840798966c --- /dev/null +++ b/test/api/peer/transport.js @@ -0,0 +1,217 @@ +'use strict'; + +var WAMPClient = require('wamp-socket-cluster/WAMPClient'); + +var node = require('../../node'); +var ws = require('../../common/wsCommunication'); +var PromiseDefer = require('../../../helpers/promiseDefer'); + + +describe('handshake', function () { + + var socketDefer = null; + + beforeEach(function () { + socketDefer = PromiseDefer(); + }); + + it('should not connect without headers', function (done) { + + ws.connect('127.0.0.1', 5000, socketDefer, null); + + socketDefer.promise + .then(function (socket) { + return done('Should not be here'); + }).catch(function () { + return done(); + }); + }); + + it('using incorrect nethash in headers should fail', function (done) { + socketDefer = PromiseDefer(); + + var headers = node.generatePeerHeaders('127.0.0.1', 5002); + headers['nethash'] = 'incorrect'; + + ws.connect('127.0.0.1', 5000, socketDefer, headers); + + socketDefer.promise + .then(function (socket) { + return done('Should not be here'); + }).catch(function (err) { + return done(); + }); + }); + + it('using incorrect version in headers should fail', function (done) { + socketDefer = PromiseDefer(); + + var headers = node.generatePeerHeaders('127.0.0.1', 5002); + headers['version'] = '0.1.0a'; + + ws.connect('127.0.0.1', 5000, socketDefer, headers); + + socketDefer.promise + .then(function (socket) { + return done('Should not be here'); + }).catch(function (err) { + return done(); + }); + }); + + it('should not accept itself as a peer', function (done) { + socketDefer = PromiseDefer(); + + var headers = node.generatePeerHeaders('127.0.0.1', 5000); + headers['version'] = '0.1.0a'; + + ws.connect('127.0.0.1', 5000, socketDefer, headers); + + socketDefer.promise + .then(function (socket) { + return done('Should not be here'); + }).catch(function (err) { + return done(); + }); + }); + + it('should connect with valid options', function (done) { + + var socketDefer = PromiseDefer(); + + ws.connect('127.0.0.1', 5000, socketDefer, node.generatePeerHeaders('127.0.0.1', 5002)); + + socketDefer.promise + .then(function (socket) { + socket.disconnect(); + return done(); + }).catch(function (err) { + return done(err); + }); + }); + + it('should list connected peer properly', function (done) { + + var socketDefer = PromiseDefer(); + + ws.connect('127.0.0.1', 5000, socketDefer, node.generatePeerHeaders('127.0.0.1', 5002)); + + socketDefer.promise + .then(function (socket) { + socket.wampSend('list').then(function (res) { + node.debug('> Response:'.grey, JSON.stringify(res)); + node.expect(res).to.have.property('success').to.be.ok; + node.expect(res).to.have.property('peers').that.is.an('array').and.not.empty; + res.peers.forEach(function (peer) { + node.expect(peer).to.have.property('ip').that.is.a('string'); + node.expect(peer).to.have.property('port').that.is.a('number'); + node.expect(peer).to.have.property('state').that.is.a('number'); + node.expect(peer).to.have.property('os'); + node.expect(peer).to.have.property('version'); + node.expect(peer).to.have.property('broadhash'); + node.expect(peer).to.have.property('height'); + }); + }).catch(function (err) { + done(err); + }); + return done(); + }).catch(function (err) { + return done(err); + }); + }); + + +}); + +describe('RPC', function () { + + var clientSocket; + + before(function (done) { + var socketDefer = PromiseDefer(); + ws.connect('127.0.0.1', 5000, socketDefer); + socketDefer.promise + .then(function (socket) { + clientSocket = socket; + return done(); + }).catch(function (err) { + return done(err); + }); + }); + + describe('height', function () { + + it('should return height', function (done) { + clientSocket.wampSend('height') + .then(function (result) { + node.expect(result).to.have.property('success').to.be.ok; + node.expect(result).to.have.property('height').to.be.a('number'); + done(); + }).catch(function (err) { + done(err); + }); + }); + }); + + describe('status', function () { + + it('should return height and broadhash', function (done) { + clientSocket.wampSend('status') + .then(function (result) { + node.expect(result).to.have.property('success').to.be.ok; + node.expect(result).to.have.property('height').to.be.a('number'); + node.expect(result).to.have.property('broadhash').to.be.a('string'); + done(); + }).catch(function (err) { + done(err); + }); + }); + }); + + describe('list', function () { + + it('should return list of peers', function (done) { + clientSocket.wampSend('list') + .then(function (result) { + node.expect(result).to.have.property('success').to.be.ok; + node.expect(result).to.have.property('peers').to.be.an('array'); + done(); + }).catch(function (err) { + done(err); + }); + }); + + it('should should work ok with asking for a list multiple times', function (done) { + + var count = 0; + for (var i = 0; i < 100; i += 1) { + clientSocket.wampSend('list') + .then(function (result) { + node.expect(result).to.have.property('success').to.be.ok; + node.expect(result).to.have.property('peers').to.be.an('array'); + count += 1; + if (count === 99) { + return done(); + } + }).catch(function (err) { + done(err); + }); + } + + + }); + }); + + describe('blocks', function () { + + it('should return height and broadhash', function (done) { + clientSocket.wampSend('blocks') + .then(function (result) { + node.expect(result).to.have.property('blocks').to.be.an('array'); + done(); + }).catch(function (err) { + done(err); + }); + }); + }); +}); diff --git a/test/api/peers.js b/test/api/peers.js index 700c0849eb3..36985bb93d2 100644 --- a/test/api/peers.js +++ b/test/api/peers.js @@ -1,12 +1,18 @@ 'use strict'; -var node = require('./../node.js'); +var node = require('../node.js'); +var http = require('../common/httpCommunication.js'); +var ws = require('../common/wsCommunication.js'); var peersSortFields = require('../../sql/peers').sortFields; +before(function (done) { + ws.addPeer('127.0.0.1', 5000, done); +}); + describe('GET /api/peers/version', function () { it('should be ok', function (done) { - node.get('/api/peers/version', function (err, res) { + http.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'); @@ -19,7 +25,7 @@ describe('GET /api/peers/version', function () { describe('GET /api/peers/count', function () { it('should be ok', function (done) { - node.get('/api/peers/count', function (err, res) { + http.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'); @@ -35,7 +41,7 @@ describe('GET /api/peers', function () { var ip = 'invalid'; var params = 'ip=' + ip; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Object didn\'t pass validation for format ip: invalid'); done(); @@ -46,7 +52,7 @@ describe('GET /api/peers', function () { var ip = '0.0.0.0'; var params = 'ip=' + ip; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -56,7 +62,7 @@ describe('GET /api/peers', function () { var port = 0; var params = 'port=' + port; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Value 0 is less than minimum 1'); done(); @@ -67,7 +73,7 @@ describe('GET /api/peers', function () { var port = 65535; var params = 'port=' + port; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -77,7 +83,7 @@ describe('GET /api/peers', function () { var port = 65536; var params = 'port=' + port; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Value 65536 is greater than maximum 65535'); done(); @@ -88,12 +94,12 @@ describe('GET /api/peers', function () { var state = 0; var params = 'state=' + state; - node.get('/api/peers?' + params, function (err, res) { + http.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'); if (res.body.peers.length > 0) { for (var i = 0; i < res.body.peers.length; i++) { - node.expect(res.body.peers[i].state).to.equal(parseInt(state)); + node.expect(res.body.peers[i].state).to.equal(parseInt(state)); } } done(); @@ -104,12 +110,12 @@ describe('GET /api/peers', function () { var state = 1; var params = 'state=' + state; - node.get('/api/peers?' + params, function (err, res) { + http.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'); if (res.body.peers.length > 0) { for (var i = 0; i < res.body.peers.length; i++) { - node.expect(res.body.peers[i].state).to.equal(parseInt(state)); + node.expect(res.body.peers[i].state).to.equal(parseInt(state)); } } done(); @@ -120,12 +126,12 @@ describe('GET /api/peers', function () { var state = 2; var params = 'state=' + state; - node.get('/api/peers?' + params, function (err, res) { + http.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'); if (res.body.peers.length > 0) { for (var i = 0; i < res.body.peers.length; i++) { - node.expect(res.body.peers[i].state).to.equal(parseInt(state)); + node.expect(res.body.peers[i].state).to.equal(parseInt(state)); } } done(); @@ -136,7 +142,7 @@ describe('GET /api/peers', function () { var state = 3; var params = 'state=' + state; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Value 3 is greater than maximum 2'); done(); @@ -147,7 +153,7 @@ describe('GET /api/peers', function () { var os = 'b'; var params = 'os=' + os; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -157,7 +163,7 @@ describe('GET /api/peers', function () { var os = 'battle-claw-lunch-confirm-correct-limb-siege-erode-child-libert'; var params = 'os=' + os; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -167,7 +173,7 @@ describe('GET /api/peers', function () { var os = 'battle-claw-lunch-confirm-correct-limb-siege-erode-child-liberty-'; var params = 'os=' + os; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('String is too long (65 chars), maximum 64'); done(); @@ -178,7 +184,7 @@ describe('GET /api/peers', function () { var os = 'freebsd10'; var params = 'os=' + os; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -188,7 +194,7 @@ describe('GET /api/peers', function () { var os = 'freebsd10.3'; var params = 'os=' + os; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -198,7 +204,7 @@ describe('GET /api/peers', function () { var os = 'freebsd10.3-'; var params = 'os=' + os; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -208,7 +214,7 @@ describe('GET /api/peers', function () { var os = 'freebsd10.3_'; var params = 'os=' + os; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -218,7 +224,7 @@ describe('GET /api/peers', function () { var os = 'freebsd10.3_RELEASE'; var params = 'os=' + os; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -228,7 +234,7 @@ describe('GET /api/peers', function () { var os = 'freebsd10.3_RELEASE-p7'; var params = 'os=' + os; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -238,7 +244,7 @@ describe('GET /api/peers', function () { var os = 'freebsd10.3_RELEASE-p7-@'; var params = 'os=' + os; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Object didn\'t pass validation for format os: freebsd10.3_RELEASE-p7-@'); done(); @@ -249,7 +255,7 @@ describe('GET /api/peers', function () { var version = '999.999.999'; var params = 'version=' + version; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -259,7 +265,7 @@ describe('GET /api/peers', function () { var version = '9999.999.999'; var params = 'version=' + version; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Object didn\'t pass validation for format version: 9999.999.999'); done(); @@ -270,7 +276,7 @@ describe('GET /api/peers', function () { var version = '999.9999.999'; var params = 'version=' + version; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Object didn\'t pass validation for format version: 999.9999.999'); done(); @@ -281,7 +287,7 @@ describe('GET /api/peers', function () { var version = '999.999.9999'; var params = 'version=' + version; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Object didn\'t pass validation for format version: 999.999.9999'); done(); @@ -292,7 +298,7 @@ describe('GET /api/peers', function () { var version = '999.999.999a'; var params = 'version=' + version; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -302,7 +308,7 @@ describe('GET /api/peers', function () { var version = '999.999.999ab'; var params = 'version=' + version; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Object didn\'t pass validation for format version: 999.999.999ab'); done(); @@ -313,7 +319,7 @@ describe('GET /api/peers', function () { var broadhash = 'invalid'; var params = 'broadhash=' + broadhash; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Object didn\'t pass validation for format hex: invalid'); done(); @@ -324,7 +330,7 @@ describe('GET /api/peers', function () { var broadhash = node.config.nethash; var params = 'broadhash=' + broadhash; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -334,7 +340,7 @@ describe('GET /api/peers', function () { var orderBy = 'state:desc'; var params = 'orderBy=' + orderBy; - node.get('/api/peers?' + params, function (err, res) { + http.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'); @@ -351,36 +357,36 @@ describe('GET /api/peers', function () { }); it('using orderBy with any of sort fields should not place NULLs first', function (done) { - 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'); - - 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); - - node.expect(dividedIndices.notNullIndices[dividedIndices.notNullIndices.length - 1]) - .to.be.at.most(dividedIndices.nullIndices[0]); - } - cb(); - }); - }, function () { - done(); - }); + node.async.each(peersSortFields, function (sortField, cb) { + http.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[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]); + } + cb(); + }); + }, function () { + done(); + }); }); it('using string limit should fail', function (done) { var limit = 'one'; var params = 'limit=' + limit; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Expected type integer but found type string'); done(); @@ -391,7 +397,7 @@ describe('GET /api/peers', function () { var limit = -1; var params = 'limit=' + limit; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Value -1 is less than minimum 1'); done(); @@ -402,7 +408,7 @@ describe('GET /api/peers', function () { var limit = 0; var params = 'limit=' + limit; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Value 0 is less than minimum 1'); done(); @@ -413,7 +419,7 @@ describe('GET /api/peers', function () { var limit = 1; var params = 'limit=' + limit; - node.get('/api/peers?' + params, function (err, res) { + http.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'); node.expect(res.body.peers.length).to.be.at.most(limit); @@ -425,7 +431,7 @@ describe('GET /api/peers', function () { var limit = 100; var params = 'limit=' + limit; - node.get('/api/peers?' + params, function (err, res) { + http.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'); node.expect(res.body.peers.length).to.be.at.most(limit); @@ -437,7 +443,7 @@ describe('GET /api/peers', function () { var limit = 101; var params = 'limit=' + limit; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Value 101 is greater than maximum 100'); done(); @@ -448,7 +454,7 @@ describe('GET /api/peers', function () { var offset = 'one'; var params = 'offset=' + offset; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Expected type integer but found type string'); done(); @@ -459,7 +465,7 @@ describe('GET /api/peers', function () { var offset = -1; var params = 'offset=' + offset; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Value -1 is less than minimum 0'); done(); @@ -470,7 +476,7 @@ describe('GET /api/peers', function () { var offset = 1; var params = 'offset=' + offset; - node.get('/api/peers?' + params, function (err, res) { + http.get('/api/peers?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; done(); }); @@ -479,17 +485,17 @@ describe('GET /api/peers', function () { describe('GET /api/peers/get', function () { - var validParams, frozenPeerPort = 9999; + var validParams; before(function (done) { - node.addPeers(1, '127.0.0.1', function (err, headers) { + http.addPeers(1, '0.0.0.0', function (err, headers) { validParams = headers; done(); }); }); it('using known ip address with no port should fail', function (done) { - node.get('/api/peers/get?ip=127.0.0.1', function (err, res) { + http.get('/api/peers/get?ip=127.0.0.1', function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Missing required property: port'); done(); @@ -497,15 +503,15 @@ describe('GET /api/peers/get', function () { }); it('using valid port with no ip address should fail', function (done) { - node.get('/api/peers/get?port=' + validParams.port, function (err, res) { + http.get('/api/peers/get?port=' + validParams.port, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Missing required property: ip'); done(); }); }); - it('using ip address and port of frozen peer should be ok', function (done) { - node.get('/api/peers/get?ip=127.0.0.1&port=' + frozenPeerPort, function (err, res) { + it('using known ip address and port should be ok', function (done) { + http.get('/api/peers/get?ip=127.0.0.1&port=' + validParams.port, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('peer').to.be.an('object'); done(); @@ -513,7 +519,7 @@ describe('GET /api/peers/get', function () { }); it('using unknown ip address and port should fail', function (done) { - node.get('/api/peers/get?ip=0.0.0.0&port=' + validParams.port, function (err, res) { + http.get('/api/peers/get?ip=0.0.0.0&port=' + validParams.port, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('Peer not found'); done(); @@ -524,7 +530,7 @@ describe('GET /api/peers/get', function () { describe('GET /api/peers/unknown', function () { it('should not to do anything', function (done) { - node.get('/api/peers/unknown', function (err, res) { + http.get('/api/peers/unknown', function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error').to.equal('API endpoint not found'); done(); diff --git a/test/api/signatures.js b/test/api/signatures.js index e677db00fc9..1d602635a09 100644 --- a/test/api/signatures.js +++ b/test/api/signatures.js @@ -1,21 +1,22 @@ 'use strict'; -var node = require('./../node.js'); +var node = require('../node.js'); +var http = require('../common/httpCommunication.js'); var account = node.randomTxAccount(); var account2 = node.randomTxAccount(); var account3 = node.randomTxAccount(); function putSignature (params, done) { - node.put('/api/signatures', params, done); + http.put('/api/signatures', params, done); } function putTransaction (params, done) { - node.put('/api/transactions', params, done); + http.put('/api/transactions', params, done); } function putDelegate (params, done) { - node.put('/api/delegates', params, done); + http.put('/api/delegates', params, done); } function sendLISK (account, done) { diff --git a/test/api/transactions.js b/test/api/transactions.js index b0892ec3dfe..36887161f22 100644 --- a/test/api/transactions.js +++ b/test/api/transactions.js @@ -1,8 +1,9 @@ 'use strict'; -var node = require('./../node.js'); -var modulesLoader = require('./../common/initModule.js').modulesLoader; +var node = require('../node.js'); +var http = require('../common/httpCommunication.js'); var transactionSortFields = require('../../sql/transactions').sortFields; +var modulesLoader = require('../common/initModule').modulesLoader; var transactionTypes = require('../../helpers/transactionTypes.js'); var genesisblock = require('../genesisBlock.json'); @@ -14,13 +15,13 @@ var transactionList = []; var offsetTimestamp = 0; function openAccount (params, done) { - node.post('/api/accounts/open', params, function (err, res) { + http.post('/api/accounts/open', params, function (err, res) { done(err, res); }); } function putTransaction (params, done) { - node.put('/api/transactions', params, done); + http.put('/api/transactions', params, done); } function sendLISK (account, amount, done) { @@ -117,7 +118,7 @@ describe('GET /api/transactions (cache)', function () { 'recipientId=' + account.address, ]; - node.get(url + params.join('&'), function (err, res) { + http.get(url + 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'); var response = res.body; @@ -136,7 +137,7 @@ describe('GET /api/transactions (cache)', function () { 'whatever:senderId=' + node.gAccount.address ]; - node.get(url + params.join('&'), function (err, res) { + http.get(url + 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'); cache.getJsonForKey(url + params, function (err, res) { @@ -168,7 +169,7 @@ describe('GET /api/transactions', function () { 'orderBy=' + orderBy ]; - node.get('/api/transactions?' + params.join('&'), function (err, res) { + http.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); @@ -195,7 +196,7 @@ describe('GET /api/transactions', function () { 'orderBy=' + orderBy ]; - node.get('/api/transactions?' + params.join('&'), function (err, res) { + http.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); @@ -223,7 +224,7 @@ describe('GET /api/transactions', function () { 'orderBy=' + orderBy ]; - node.get('/api/transactions?' + params.join('&'), function (err, res) { + http.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(2, limit); @@ -256,7 +257,7 @@ describe('GET /api/transactions', function () { 'orderBy=' + orderBy ]; - node.get('/api/transactions?' + params.join('&'), function (err, res) { + http.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); @@ -285,7 +286,7 @@ describe('GET /api/transactions', function () { 'orderBy=' + orderBy ]; - node.get('/api/transactions?' + params.join('&'), function (err, res) { + http.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); @@ -312,7 +313,7 @@ describe('GET /api/transactions', function () { 'orderBy=' + orderBy ]; - node.get('/api/transactions?' + params.join('&'), function (err, res) { + http.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(); @@ -324,7 +325,7 @@ describe('GET /api/transactions', function () { 'whatever:senderId=' + node.gAccount.address ]; - node.get('/api/transactions?' + params.join('&'), function (err, res) { + http.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(); @@ -336,7 +337,7 @@ describe('GET /api/transactions', function () { 'or:whatever:senderId=' + node.gAccount.address ]; - node.get('/api/transactions?' + params.join('&'), function (err, res) { + http.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(); @@ -348,7 +349,7 @@ describe('GET /api/transactions', function () { 'and:publicKey=' ]; - node.get('/api/transactions?' + params.join('&'), function (err, res) { + http.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(); @@ -359,7 +360,7 @@ describe('GET /api/transactions', function () { var type = node.txTypes.SEND; var params = 'type=' + type; - node.get('/api/transactions?' + params, function (err, res) { + http.get('/api/transactions?' + params, 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'); for (var i = 0; i < res.body.transactions.length; i++) { @@ -372,7 +373,7 @@ describe('GET /api/transactions', function () { }); it('using no params should be ok', function (done) { - node.get('/api/transactions', function (err, res) { + http.get('/api/transactions', 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'); for (var i = 0; i < res.body.transactions.length; i++) { @@ -387,7 +388,7 @@ describe('GET /api/transactions', function () { it('using too small fromUnixTime should fail', function (done) { var params = 'fromUnixTime=1464109199'; - node.get('/api/transactions?' + params, function (err, res) { + http.get('/api/transactions?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -397,7 +398,7 @@ describe('GET /api/transactions', function () { it('using too small toUnixTime should fail', function (done) { var params = 'toUnixTime=1464109200'; - node.get('/api/transactions?' + params, function (err, res) { + http.get('/api/transactions?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -408,7 +409,7 @@ describe('GET /api/transactions', function () { var limit = 1001; var params = 'limit=' + limit; - node.get('/api/transactions?' + params, function (err, res) { + http.get('/api/transactions?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -419,7 +420,7 @@ describe('GET /api/transactions', function () { var orderBy = 'timestamp:asc'; var params = 'orderBy=' + orderBy; - node.get('/api/transactions?' + params, function (err, res) { + http.get('/api/transactions?' + params, 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'); @@ -442,7 +443,7 @@ describe('GET /api/transactions', function () { var offset = 1; var params = 'offset=' + offset; - node.get('/api/transactions?' + params, function (err, res) { + http.get('/api/transactions?' + params, 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'); if (res.body.transactions.length > 0) { @@ -456,11 +457,11 @@ describe('GET /api/transactions', function () { var offset = 'one'; var params = 'offset=' + offset; - node.get('/api/transactions?' + params, function (err, res) { + http.get('/api/transactions?' + params, 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 completely invalid fields should fail', function (done) { @@ -473,7 +474,7 @@ describe('GET /api/transactions', function () { 'orderBy=invalid' ]; - node.get('/api/transactions?' + params.join('&'), function (err, res) { + http.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(); @@ -490,7 +491,7 @@ describe('GET /api/transactions', function () { 'orderBy=blockId:asc' ]; - node.get('/api/transactions?' + params.join('&'), function (err, res) { + http.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(); @@ -499,7 +500,7 @@ describe('GET /api/transactions', function () { it('using orderBy with any of sort fields should not place NULLs first', function (done) { node.async.each(transactionSortFields, function (sortField, cb) { - node.get('/api/transactions?orderBy=' + sortField, function (err, res) { + http.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'); @@ -530,7 +531,7 @@ describe('GET /api/transactions/get?id=', function () { var transactionInCheck = transactionList[0]; var params = 'id=' + transactionInCheck.txId; - node.get('/api/transactions/get?' + params, function (err, res) { + http.get('/api/transactions/get?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.ok; node.expect(res.body).to.have.property('transaction').that.is.an('object'); node.expect(res.body.transaction.id).to.equal(transactionInCheck.txId); @@ -546,7 +547,7 @@ describe('GET /api/transactions/get?id=', function () { it('using invalid id should fail', function (done) { var params = 'id=invalid'; - node.get('/api/transactions/get?' + params, function (err, res) { + http.get('/api/transactions/get?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.not.ok; node.expect(res.body).to.have.property('error'); done(); @@ -559,7 +560,7 @@ describe('GET /api/transactions/get?id=', function () { }); var params = 'id=' + transactionInCheck.id; - node.get('/api/transactions/get?' + params, function (err, res) { + http.get('/api/transactions/get?' + params, function (err, res) { node.expect(res.body.transaction.type).to.equal(transactionTypes.VOTE); node.expect(res.body.transaction.type).to.equal(transactionInCheck.type); @@ -579,7 +580,7 @@ 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) { + http.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'); @@ -595,7 +596,7 @@ describe('GET /api/transactions/queued/get?id=', function () { it('using unknown id should be ok', function (done) { var params = 'id=' + '1234'; - node.get('/api/transactions/queued/get?' + params, function (err, res) { + http.get('/api/transactions/queued/get?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.false; node.expect(res.body).to.have.property('error').that.is.equal('Transaction not found'); done(); @@ -606,7 +607,7 @@ describe('GET /api/transactions/queued/get?id=', function () { describe('GET /api/transactions/queued', function () { it('should be ok', function (done) { - node.get('/api/transactions/queued', function (err, res) { + http.get('/api/transactions/queued', 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).to.have.property('count').that.is.an('number'); @@ -620,7 +621,7 @@ describe('GET /api/transactions/multisignatures/get?id=', function () { it('using unknown id should be ok', function (done) { var params = 'id=' + '1234'; - node.get('/api/transactions/multisignatures/get?' + params, function (err, res) { + http.get('/api/transactions/multisignatures/get?' + params, function (err, res) { node.expect(res.body).to.have.property('success').to.be.false; node.expect(res.body).to.have.property('error').that.is.equal('Transaction not found'); done(); @@ -631,7 +632,7 @@ describe('GET /api/transactions/multisignatures/get?id=', function () { describe('GET /api/transactions/multisignatures', function () { it('should be ok', function (done) { - node.get('/api/transactions/multisignatures', function (err, res) { + http.get('/api/transactions/multisignatures', 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).to.have.property('count').that.is.an('number'); @@ -645,7 +646,7 @@ describe('GET /api/transactions/unconfirmed/get?id=', function () { it('using valid id should be ok', function (done) { var params = 'id=' + transactionList[transactionList.length - 1].txId; - node.get('/api/transactions/unconfirmed/get?' + params, function (err, res) { + http.get('/api/transactions/unconfirmed/get?' + params, function (err, res) { node.expect(res.body).to.have.property('success'); if (res.body.success && res.body.transaction != null) { node.expect(res.body).to.have.property('transaction').that.is.an('object'); @@ -661,7 +662,7 @@ describe('GET /api/transactions/unconfirmed/get?id=', function () { describe('GET /api/transactions/unconfirmed', function () { it('should be ok', function (done) { - node.get('/api/transactions/unconfirmed', function (err, res) { + http.get('/api/transactions/unconfirmed', 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).to.have.property('count').that.is.an('number'); diff --git a/test/common/globalBefore.js b/test/common/globalBefore.js index a882e93dd60..dd4862da356 100644 --- a/test/common/globalBefore.js +++ b/test/common/globalBefore.js @@ -1,6 +1,8 @@ 'use strict'; -var node = require('./../node.js'); +var popsicle = require('popsicle'); +var config = require('../../config.json'); + /** * @param {string} table @@ -21,28 +23,43 @@ function clearDatabaseTable (db, logger, table, cb) { * @param {Function} cb * @param {Number} [retries=10] retries * @param {Number} [timeout=200] timeout + * @param {String} [baseUrl='http://localhost:5000'] timeout */ -function waitUntilBlockchainReady (cb, retries, timeout) { +function waitUntilBlockchainReady (cb, retries, timeout, baseUrl) { if (!retries) { retries = 10; } if (!timeout) { timeout = 1000; } + + baseUrl = baseUrl || 'http://' + config.address + ':' + config.httpPort; (function fetchBlockchainStatus () { - node.get('/api/loader/status', function (err, res) { - node.expect(err).to.not.exist; - retries -= 1; - if (!res.body.loaded && retries >= 0) { - return setTimeout(function () { - fetchBlockchainStatus(); - }, timeout); - } - else if (res.body.success && res.body.loaded) { - return cb(); - } - return cb('Failed to load blockchain'); - }); + popsicle.get(baseUrl + '/api/loader/status') + .then(function (res) { + retries -= 1; + res = JSON.parse(res.body); + if (!res.loaded && retries >= 0) { + return setTimeout(function () { + fetchBlockchainStatus(); + }, timeout); + } + else if (res.success && res.loaded) { + return cb(); + } + return cb('Failed to load blockchain'); + }) + .catch(function (err) { + retries -= 1; + if (retries >= 0) { + return setTimeout(function () { + fetchBlockchainStatus(); + }, timeout); + } else { + return cb('Server is not responding'); + } + + }); })(); } diff --git a/test/common/httpCommunication.js b/test/common/httpCommunication.js new file mode 100644 index 00000000000..b744f11f35c --- /dev/null +++ b/test/common/httpCommunication.js @@ -0,0 +1,85 @@ +'use strict'; + +var node = require('../node'); + +var httpCommunication = { + abstractRequest: function (options, done) { + var request = node.api[options.verb.toLowerCase()](options.path); + + request.set('Accept', 'application/json'); + request.expect('Content-Type', /json/); + request.expect(200); + + if (options.params) { + request.send(options.params); + } + + var verb = options.verb.toUpperCase(); + node.debug(['> Path:'.grey, verb, options.path].join(' ')); + if (verb === 'POST' || verb === 'PUT') { + node.debug(['> Data:'.grey, JSON.stringify(options.params)].join(' ')); + } + + if (done) { + request.end(function (err, res) { + node.debug('> Response:'.grey, JSON.stringify(res ? res.body : err)); + done(err, res); + }); + } else { + return request; + } + }, + + // Get the given path + get: function (path, done) { + return this.abstractRequest({verb: 'GET', path: path, params: null}, done); + }, + + // Post to the given path + post: function (path, params, done) { + return this.abstractRequest({verb: 'POST', path: path, params: params}, done); + }, + + // Put to the given path + put: function (path, params, done) { + return this.abstractRequest({verb: 'PUT', path: path, params: params}, done); + }, + + // Adds peers to local node + addPeers: function (numOfPeers, ip, cb) { + var i = 0; + + node.async.whilst(function () { + return i < numOfPeers; + }, function (next) { + + var request = node.popsicle.get({ + url: node.baseUrl + '/peer/height', + headers: node.generatePeerHeaders(ip, 5000) + }); + + request.use(node.popsicle.plugins.parse(['json'])); + + request.then(function (res) { + if (res.status !== 200) { + return next(['Received bad response code', res.status, res.url].join(' ')); + } else { + i++; + next(); + } + }); + + request.catch(function (err) { + return next(err); + }); + + }, function (err) { + // Wait for peer to be swept to db + setTimeout(function () { + return cb(err, node.generatePeerHeaders(ip, 5000)); + }, 3000); + }); + } +}; + +module.exports = httpCommunication; diff --git a/test/common/initModule.js b/test/common/initModule.js index 0060f699416..a40d9a07161 100644 --- a/test/common/initModule.js +++ b/test/common/initModule.js @@ -6,7 +6,7 @@ var randomString = require('randomstring'); var _ = require('lodash'); -var async = require('../node').async; +var async = require('async'); var dirname = path.join(__dirname, '..', '..'); var config = require(path.join(dirname, '/config.json')); var Sequence = require(path.join(dirname, '/helpers', 'sequence.js')); @@ -17,6 +17,7 @@ var z_schema = require('../../helpers/z_schema.js'); var cacheHelper = require('../../helpers/cache.js'); var Cache = require('../../modules/cache.js'); var ed = require('../../helpers/ed'); +var jobsQueue = require('../../helpers/jobsQueue'); var Transaction = require('../../logic/transaction.js'); var Account = require('../../logic/account.js'); @@ -24,6 +25,7 @@ var modulesLoader = new function () { this.db = null; this.logger = new Logger({ echo: null, errorLevel: config.fileLogLevel, filename: config.logFileName }); + config.nonce = randomString.generate(16); this.scope = { config: config, genesisblock: { block: genesisblock }, @@ -40,8 +42,7 @@ var modulesLoader = new function () { onWarning: function (current, limit) { this.logger.warn('Balance queue', current); } - }), - nonce: randomString.generate(16) + }) }; /** @@ -53,35 +54,35 @@ var modulesLoader = new function () { */ this.initLogic = function (Logic, scope, cb) { switch (Logic.name) { - case 'Account': - new Logic(scope.db, scope.schema, scope.logger, cb); - break; - case 'Transaction': - async.series({ - account: function (cb) { - new Account(scope.db, scope.schema, scope.logger, cb); - } - }, function (err, result) { - new Logic(scope.db, scope.ed, scope.schema, scope.genesisblock, result.account, scope.logger, cb); - }); - break; - case 'Block': - async.waterfall([ - function (waterCb) { - return new Account(scope.db, scope.schema, scope.logger, waterCb); - }, - function (account, waterCb) { - return new Transaction(scope.db, scope.ed, scope.schema, scope.genesisblock, account, scope.logger, waterCb); - } - ], function (err, transaction) { - new Logic(scope.ed, scope.schema, transaction, cb); - }); - break; - case 'Peers': - new Logic(scope.logger, cb); - break; - default: - console.log('no Logic case initLogic'); + case 'Account': + new Logic(scope.db, scope.schema, scope.logger, cb); + break; + case 'Transaction': + async.series({ + account: function (cb) { + new Account(scope.db, scope.schema, scope.logger, cb); + } + }, function (err, result) { + new Logic(scope.db, scope.ed, scope.schema, scope.genesisblock, result.account, scope.logger, cb); + }); + break; + case 'Block': + async.waterfall([ + function (waterCb) { + return new Account(scope.db, scope.schema, scope.logger, waterCb); + }, + function (account, waterCb) { + return new Transaction(scope.db, scope.ed, scope.schema, scope.genesisblock, account, scope.logger, waterCb); + } + ], function (err, transaction) { + new Logic(scope.ed, scope.schema, transaction, cb); + }); + break; + case 'Peers': + new Logic(scope.logger, cb); + break; + default: + console.log('no Logic case initLogic'); } }; @@ -144,6 +145,10 @@ var modulesLoader = new function () { ], cb); }; + this.clear = function () { + jobsQueue.jobs = {}; + }; + /** * Initializes all created Modules in directory * @@ -151,6 +156,7 @@ var modulesLoader = new function () { * @param {object} [scope={}] scope */ this.initAllModules = function (cb, scope) { + this.clear(); this.initModules([ {accounts: require('../../modules/accounts')}, {blocks: require('../../modules/blocks')}, diff --git a/test/common/objectStubs.js b/test/common/objectStubs.js index d6cebc4d650..15d2811ff04 100644 --- a/test/common/objectStubs.js +++ b/test/common/objectStubs.js @@ -7,9 +7,12 @@ var randomPeer = { 'height': 1, 'ip': '40.40.40.40', 'os': 'unknown', - 'port': 4000, + 'port': 5000, + 'httpPort': 4000, 'state': 2, - 'version': '0.0.0' + 'version': '0.0.0', + 'nonce': 'randomnonce', + rpc: {} }; module.exports = { diff --git a/test/common/wsCommunication.js b/test/common/wsCommunication.js new file mode 100644 index 00000000000..e1819dbea54 --- /dev/null +++ b/test/common/wsCommunication.js @@ -0,0 +1,98 @@ +'use strict'; + +var _ = require('lodash'); +var Promise = require('bluebird'); +var PromiseDefer = require('../../helpers/promiseDefer'); +var ClientRPCStub = require('../../api/ws/rpc/wsRPC').ClientRPCStub; +var ConnectionState = require('../../api/ws/rpc/wsRPC').ConnectionState; +var System = require('../../modules/system'); +var scClient = require('socketcluster-client'); +var WAMPClient = require('wamp-socket-cluster/WAMPClient'); +var wampClient = new WAMPClient(); + +var node = require('../node'); + +var wsCommunication = { + + defaultConnectionState: null, + + connect: function (ip, port, socketDefer, headers) { + + var wsOptions = { + protocol: 'http', + hostname: ip || '127.0.0.1', + port: +port || 9999, + autoReconnect: true, + query: headers !== undefined ? headers : node.generatePeerHeaders(ip, port) + }; + + console.log(wsOptions); + + var socket = scClient.connect(wsOptions); + + wampClient.upgradeToWAMP(socket); + + socket.on('connectAbort', function (err) { + return socketDefer.reject(err); + }); + + socket.on('connect', function () { + return socketDefer.resolve(socket); + }); + + socket.on('error', function (err) { + console.log(err); + }); + }, + + // Get the given path + call: function (procedure, data, done, includePeer) { + if (!this.defaultConnectionState) { + this.defaultConnectionState = new ConnectionState('127.0.0.1', 5000); + this.defaultSocketPeerHeaders = node.generatePeerHeaders('127.0.0.1', 9999); + System.setHeaders(this.defaultSocketPeerHeaders); + this.caller = ClientRPCStub.prototype.sendAfterSocketReadyCb(this.defaultConnectionState); + } + if (includePeer && typeof data === 'object') { + data.peer = _.assign({ + ip: '127.0.0.1', + port: 9999 + }, this.defaultSocketPeerHeaders); + } + + return this.caller(procedure)(data, done); + }, + + // Adds peers to local node + addPeers: function (numOfPeers, ip, cb) { + + var peersConnectionsDefers = Array.apply(null, new Array(numOfPeers)).map(function () { + var socketDefer = PromiseDefer(); + this.connect(ip, 5000, socketDefer, node.generatePeerHeaders(ip, node.randomizeSelection(1000) + 4001)); + return socketDefer; + }.bind(this)); + + Promise.all(peersConnectionsDefers.map(function (peerDefer) { return peerDefer.promise; })) + .then(function (results) { + return cb(null, results); + }) + .catch(function (err) { + return cb(err); + }); + }, + + // Adds peer to local node + addPeer: function (ip, port, cb) { + + var socketDefer = PromiseDefer(); + this.connect(ip, port, socketDefer); + + socketDefer.promise.then(function () { + return cb(); + }).catch(function (err) { + return cb(err); + }); + } +}; + +module.exports = wsCommunication; diff --git a/test/config.json b/test/config.json index 31876367869..f6ff39adc12 100644 --- a/test/config.json +++ b/test/config.json @@ -1,5 +1,6 @@ { - "port": 4000, + "port": 5000, + "httpPort": 4000, "address": "0.0.0.0", "version": "0.0.0a", "minVersion": "0.0.0a", @@ -9,6 +10,7 @@ "trustProxy": false, "topAccounts": false, "cacheEnabled": true, + "wsWorkers": 1, "db": { "host": "localhost", "port": 5432, @@ -47,7 +49,7 @@ "enabled": true, "list": [{ "ip": "127.0.0.1", - "port": 9999 + "port": "5000" }], "access": { "blackList": [] diff --git a/test/index.js b/test/index.js index 2087c394fcd..995cffeb465 100644 --- a/test/index.js +++ b/test/index.js @@ -1,21 +1,2 @@ -require('./unit/helpers/request-limiter.js'); -require('./unit/logic/blockReward.js'); -require('./unit/sql/blockRewards.js'); -require('./unit/modules/peers.js'); -require('./unit/modules/blocks.js'); - -require('./api/accounts'); -require('./api/blocks'); -require('./api/dapps'); -require('./api/delegates'); -require('./api/loader'); -require('./api/multisignatures'); -require('./api/peer'); -require('./api/peer.transactions.main'); -require('./api/peer.transactions.collision'); -require('./api/peer.transactions.delegates'); -require('./api/peer.transactions.signatures'); -require('./api/peer.transactions.votes'); -require('./api/peers'); -require('./api/signatures'); -require('./api/transactions'); +require('./api'); +require('./unit'); \ No newline at end of file diff --git a/test/integration/configs/config.non-forge.json b/test/integration/configs/config.non-forge.json new file mode 100644 index 00000000000..3bb0a3f5912 --- /dev/null +++ b/test/integration/configs/config.non-forge.json @@ -0,0 +1,93 @@ +{ + "port": 5000, + "httpPort": 4000, + "address": "0.0.0.0", + "version": "0.0.0a", + "minVersion": "0.0.0a", + "fileLogLevel": "info", + "logFileName": "logs/lisk.log", + "consoleLogLevel": "debug", + "trustProxy": true, + "topAccounts": false, + "wsWorkers": 1, + "db": { + "host": "localhost", + "port": 5432, + "database": "lisk_local", + "user": "", + "password": "password", + "poolSize": 95, + "poolIdleTimeout": 30000, + "reapIntervalMillis": 1000, + "logEvents": [ + "error" + ] + }, + "api": { + "enabled": true, + "access": { + "public": true, + "whiteList": [] + }, + "options": { + "limits": { + "max": 0, + "delayMs": 0, + "delayAfter": 0, + "windowMs": 60000 + } + } + }, + "peers": { + "enabled": true, + "list": [{ + "ip": "127.0.0.1", + "port": 5000 + }], + "access": { + "blackList": [] + }, + "options": { + "limits": { + "max": 0, + "delayMs": 0, + "delayAfter": 0, + "windowMs": 60000 + }, + "timeout": 5000 + } + }, + "broadcasts": { + "broadcastInterval": 5000, + "broadcastLimit": 20, + "parallelLimit": 20, + "releaseLimit": 25, + "relayLimit": 2 + }, + "transactions": { + "maxTxsPerQueue": 1000 + }, + "forging": { + "force": false, + "secret": [], + "access": { + "whiteList": [ + "127.0.0.1" + ] + } + }, + "loading": { + "verifyOnLoading": false, + "loadPerIteration": 5000 + }, + "ssl": { + "enabled": false, + "options": { + "port": 443, + "address": "0.0.0.0", + "key": "./ssl/lisk.key", + "cert": "./ssl/lisk.crt" + } + }, + "nethash": "198f2b61a8eb95fbeed58b8216780b68f697f26b849acf00c8c93bb9b24f783d" +} \ No newline at end of file diff --git a/test/integration/peers.integration.js b/test/integration/peers.integration.js new file mode 100644 index 00000000000..8f57988f817 --- /dev/null +++ b/test/integration/peers.integration.js @@ -0,0 +1,395 @@ +'use strict'; + +var _ = require('lodash'); +var fs = require('fs'); +var Promise = require('bluebird'); + +var chai = require('chai'); +var expect = require('chai').expect; +var popsicle = require('popsicle'); +var scClient = require('socketcluster-client'); +var WAMPClient = require('wamp-socket-cluster/WAMPClient'); +var child_process = require('child_process'); +var waitUntilBlockchainReady = require('../common/globalBefore').waitUntilBlockchainReady; + +var baseConfig = require('../../test/config.json'); + +var SYNC_MODE = { + RANDOM: 0, + ALL_TO_FIRST: 1, + ALL_TO_GROUP: 2 +}; + +var SYNC_MODE_DEFAULT_ARGS = { + RANDOM: { + PROBABILITY: 0.5 //range 0 - 1 + }, + ALL_TO_GROUP: { + INDICES: [] + } +}; + +var testNodeConfigs = generateNodesConfig(10, SYNC_MODE.ALL_TO_FIRST, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + +function generateNodePeers (numOfPeers, syncMode, syncModeArgs) { + syncModeArgs = syncModeArgs || SYNC_MODE_DEFAULT_ARGS; + switch (syncMode) { + case SYNC_MODE.RANDOM: + var peersList = []; + + if (typeof syncModeArgs.PROBABILITY !== 'number') { + throw new Error('Probability parameter not specified to random sync mode'); + } + var isPickedWithProbability = function (n) { + return !!n && Math.random() <= n; + }; + + return Array.apply(null, new Array(numOfPeers)).forEach(function (val, index) { + if (isPickedWithProbability(syncModeArgs.PROBABILITY)) { + peersList.push({ + ip: '127.0.0.1', + port: 5000 + index + }); + } + }); + break; + + case SYNC_MODE.ALL_TO_FIRST: + return [{ + ip: '127.0.0.1', + port: 5001 + }]; + break; + + case SYNC_MODE.ALL_TO_GROUP: + throw new Error('To implement'); + break; + } +} + +function generateNodesConfig (numOfPeers, syncMode, forgingNodesIndices) { + var secretsInChunk = Math.ceil(baseConfig.forging.secret.length / forgingNodesIndices.length); + var secretsChunks = Array.apply(null, new Array(forgingNodesIndices.length)).map(function (val, index) { + return baseConfig.forging.secret.slice(index * secretsInChunk, (index + 1) * secretsInChunk); + }); + + return Array.apply(null, new Array(numOfPeers)).map(function (val, index) { + var isForging = forgingNodesIndices.indexOf(index) !== -1; + return { + ip: '127.0.0.1', + port: 5000 + index, + database: 'lisk_local_' + index, + peers: { + list: generateNodePeers(numOfPeers, syncMode) + }, + forging: isForging, + secrets: isForging ? secretsChunks[index] : [] + }; + }); +} + +function generatePM2NodesConfig (testNodeConfigs) { + + var pm2Config = { + apps: [] + }; + + function insertNewNode (index, nodeConfig) { + + function peersAsString (peersList) { + return peersList.reduce(function (acc, peer) { + acc += peer.ip + ':' + peer.port + ','; + return acc; + }, '').slice(0, -1); + } + + var nodePM2Config = { + 'exec_mode': 'fork', + 'script': 'app.js', + 'name': 'node_' + index, + 'args': ' -p ' + nodeConfig.port + + ' -h ' + (nodeConfig.port - 1000) + + ' -x ' + peersAsString(nodeConfig.peers.list) + + ' -d ' + nodeConfig.database, + 'env': { + 'NODE_ENV': 'test' + }, + 'error_file': './test/integration/logs/lisk-test-node-' + index + '.err.log', + 'out_file': './test/integration/logs/lisk-test-node-' + index + '.out.log' + }; + + if (!nodeConfig.forging) { + nodePM2Config.args += ' -c ./test/integration/configs/config.non-forge.json'; + } else { + var currentNodeConfig = _.clone(baseConfig); + currentNodeConfig.forging.force = false; + currentNodeConfig.forging.secret = nodeConfig.secrets; + fs.writeFileSync(__dirname + '/configs/config.node-' + index + '.json', JSON.stringify(currentNodeConfig, null, 4)); + nodePM2Config.args += ' -c ./test/integration/configs/config.node-' + index + '.json'; + } + pm2Config.apps.push(nodePM2Config); + } + + testNodeConfigs.forEach(function (testNodeConfig, index) { + insertNewNode(index, testNodeConfig); + }); + + fs.writeFileSync(__dirname + '/pm2.integration.json', JSON.stringify(pm2Config, null, 4)); +} + +var monitorWSClient = { + protocol: 'http', + hostname: '127.0.0.1', + port: 'toOverwrite', + autoReconnect: true, + query: { + port: 9999, + nethash: '198f2b61a8eb95fbeed58b8216780b68f697f26b849acf00c8c93bb9b24f783d', + broadhash: '198f2b61a8eb95fbeed58b8216780b68f697f26b849acf00c8c93bb9b24f783d', + height: 1, + version: '0.0.0a', + nonce: 'ABCD' + } +}; + +function clearLogs (cb) { + child_process.exec('rm -rf test/integration/logs/*', function (err, stdout) { + return cb(err); + }); +} + +function launchTestNodes (cb) { + child_process.exec('node_modules/.bin/pm2 start test/integration/pm2.integration.json', function (err, stdout) { + return cb(err); + }); +} + +function killTestNodes (cb) { + child_process.exec('node_modules/.bin/pm2 delete all', function (err, stdout) { + console.log(stdout); + return cb(err); + }); +} + +function runFunctionalTests (cb) { + var child = child_process.spawn('npm', ['run', 'test-functional'], { + cwd: __dirname + '/../..' + }); + + child.stdout.pipe(process.stdout); + + child.on('close', function (code) { + return cb(code === 0 ? undefined : code); + }); + + child.on('error', function (err) { + return cb(err); + }); +} + +function recreateDatabases (done) { + var recreatedCnt = 0; + testNodeConfigs.forEach(function (nodeConfig) { + child_process.exec('dropdb ' + nodeConfig.database + '; createdb ' + nodeConfig.database, function (err, stdout) { + if (err) { + return done(err); + } + recreatedCnt += 1; + if (recreatedCnt === testNodeConfigs.length) { + done(); + } + }); + }); +} + +before(function () { + generatePM2NodesConfig(testNodeConfigs); +}); + +before(function (done) { + clearLogs(done); +}); + +before(function (done) { + recreateDatabases(done); +}); + +before(function (done) { + launchTestNodes(done); +}); + +before(function (done) { + + var nodesReadyCnt = 0; + var nodeReadyCb = function (err) { + if (err) { + return done(err); + } + nodesReadyCnt += 1; + if (nodesReadyCnt === testNodeConfigs.length) { + done(); + } + }; + + testNodeConfigs.forEach(function (testNodeConfig) { + waitUntilBlockchainReady(nodeReadyCb, 20, 2000, 'http://' + testNodeConfig.ip + ':' + (testNodeConfig.port - 1000)); + }); +}); + +var sockets = []; + +describe('Peers mutual connections', function () { + + before(function (done) { + var connectedTo = 0; + var wampClient = new WAMPClient(); + //ToDo: more clever way for waiting until all test node being able to receive connections + setTimeout(function () { + testNodeConfigs.forEach(function (testNodeConfig) { + monitorWSClient.port = testNodeConfig.port; + var socket = scClient.connect(monitorWSClient); + wampClient.upgradeToWAMP(socket); + socket.on('connect', function () { + sockets.push(socket); + connectedTo += 1; + if (connectedTo === testNodeConfigs.length) { + done(); + } + }); + socket.on('error', function (err) {}); + socket.on('connectAbort', function (err) { + done('Unable to establish WS connection with ' + testNodeConfig.ip + ':' + testNodeConfig.port); + }); + }, 1000); + }); + }); + + it('should return a list of peer mutually interconnected', function (done) { + Promise.all(sockets.map(function (socket) { + return socket.wampSend('list'); + })).then(function (results) { + var resultsFrom = 0; + results.forEach(function (result) { + resultsFrom += 1; + expect(result).to.have.property('success').to.be.ok; + expect(result).to.have.property('peers').to.be.a('array'); + var peerPorts = result.peers.map(function (p) { + return p.port; + }); + + var allPeerPorts = testNodeConfigs.map(function (testNodeConfig) { + return testNodeConfig.port; + }); + + expect(_.intersection(allPeerPorts, peerPorts)).to.be.an('array').and.not.to.be.empty; + if (resultsFrom === testNodeConfigs.length) { + done(); + } + }); + + }).catch(function (err) { + done(err); + }); + + }); +}); + +describe('propagation', function () { + + before(function (done) { + runFunctionalTests(done); + }); + + describe('blocks', function () { + + var nodesBlocks; + + before(function (done) { + Promise.all(testNodeConfigs.map(function (testNodeConfig) { + return popsicle.get({ + url: 'http://' + testNodeConfig.ip + ':' + (testNodeConfig.port - 1000) + '/api/blocks', + headers: { + 'Accept': 'application/json', + 'ip': '0.0.0.0', + 'port': 9999, + 'nethash': '198f2b61a8eb95fbeed58b8216780b68f697f26b849acf00c8c93bb9b24f783d', + 'version': '0.0.0a' + } + }); + })).then(function (results) { + nodesBlocks = results.map(function (res) { + return JSON.parse(res.body).blocks; + }); + expect(nodesBlocks).to.have.lengthOf(testNodeConfigs.length); + done(); + }).catch(function (err) { + done(err); + }); + }); + + it('should contain non empty blocks after running functional tests', function () { + nodesBlocks.forEach(function (blocks) { + expect(blocks).to.be.an('array').and.not.empty; + }); + }); + + it('should have all peers at the same height', function () { + var uniquePeersHeights = _(nodesBlocks).map('length').uniq().value(); + expect(uniquePeersHeights).to.have.lengthOf(1); + }); + + it('should have all blocks the same at all peers', function () { + var patternBlocks = nodesBlocks[0]; + for (var i = 0; i < patternBlocks.length; i += 1) { + for (var j = 1; j < nodesBlocks.length; j += 1) { + expect(_.isEqual(nodesBlocks[j][i], patternBlocks[i])); + } + } + }); + }); + + describe('transactions', function () { + + var nodesTransactions = []; + + before(function (done) { + Promise.all(sockets.map(function (socket) { + return socket.wampSend('blocks'); + })).then(function (results) { + nodesTransactions = results.map(function (res) { + return res.blocks; + }); + expect(nodesTransactions).to.have.lengthOf(testNodeConfigs.length); + done(); + }).catch(function (err) { + done(err); + }); + }); + + it('should contain non empty transactions after running functional tests', function () { + nodesTransactions.forEach(function (transactions) { + expect(transactions).to.be.an('array').and.not.empty; + }); + }); + + it('should have all peers having same amount of confirmed transactions', function () { + var uniquePeersTransactionsNumber = _(nodesTransactions).map('length').uniq().value(); + expect(uniquePeersTransactionsNumber).to.have.lengthOf(1); + }); + + it('should have all transactions the same at all peers', function () { + var patternTransactions = nodesTransactions[0]; + for (var i = 0; i < patternTransactions.length; i += 1) { + for (var j = 1; j < nodesTransactions.length; j += 1) { + expect(_.isEqual(nodesTransactions[j][i], patternTransactions[i])); + } + } + }); + }); + +}); + + +after(function (done) { + killTestNodes(done); +}); diff --git a/test/integration/pm2.integration.json b/test/integration/pm2.integration.json new file mode 100644 index 00000000000..d1a220ae405 --- /dev/null +++ b/test/integration/pm2.integration.json @@ -0,0 +1,114 @@ +{ + "apps": [ + { + "exec_mode": "fork", + "script": "app.js", + "name": "node_0", + "args": " -p 5000 -h 4000 -x 127.0.0.1:5001 -d lisk_local_0 -c ./test/integration/configs/config.node-0.json", + "env": { + "NODE_ENV": "test" + }, + "error_file": "./test/integration/logs/lisk-test-node-0.err.log", + "out_file": "./test/integration/logs/lisk-test-node-0.out.log" + }, + { + "exec_mode": "fork", + "script": "app.js", + "name": "node_1", + "args": " -p 5001 -h 4001 -x 127.0.0.1:5001 -d lisk_local_1 -c ./test/integration/configs/config.node-1.json", + "env": { + "NODE_ENV": "test" + }, + "error_file": "./test/integration/logs/lisk-test-node-1.err.log", + "out_file": "./test/integration/logs/lisk-test-node-1.out.log" + }, + { + "exec_mode": "fork", + "script": "app.js", + "name": "node_2", + "args": " -p 5002 -h 4002 -x 127.0.0.1:5001 -d lisk_local_2 -c ./test/integration/configs/config.node-2.json", + "env": { + "NODE_ENV": "test" + }, + "error_file": "./test/integration/logs/lisk-test-node-2.err.log", + "out_file": "./test/integration/logs/lisk-test-node-2.out.log" + }, + { + "exec_mode": "fork", + "script": "app.js", + "name": "node_3", + "args": " -p 5003 -h 4003 -x 127.0.0.1:5001 -d lisk_local_3 -c ./test/integration/configs/config.node-3.json", + "env": { + "NODE_ENV": "test" + }, + "error_file": "./test/integration/logs/lisk-test-node-3.err.log", + "out_file": "./test/integration/logs/lisk-test-node-3.out.log" + }, + { + "exec_mode": "fork", + "script": "app.js", + "name": "node_4", + "args": " -p 5004 -h 4004 -x 127.0.0.1:5001 -d lisk_local_4 -c ./test/integration/configs/config.node-4.json", + "env": { + "NODE_ENV": "test" + }, + "error_file": "./test/integration/logs/lisk-test-node-4.err.log", + "out_file": "./test/integration/logs/lisk-test-node-4.out.log" + }, + { + "exec_mode": "fork", + "script": "app.js", + "name": "node_5", + "args": " -p 5005 -h 4005 -x 127.0.0.1:5001 -d lisk_local_5 -c ./test/integration/configs/config.node-5.json", + "env": { + "NODE_ENV": "test" + }, + "error_file": "./test/integration/logs/lisk-test-node-5.err.log", + "out_file": "./test/integration/logs/lisk-test-node-5.out.log" + }, + { + "exec_mode": "fork", + "script": "app.js", + "name": "node_6", + "args": " -p 5006 -h 4006 -x 127.0.0.1:5001 -d lisk_local_6 -c ./test/integration/configs/config.node-6.json", + "env": { + "NODE_ENV": "test" + }, + "error_file": "./test/integration/logs/lisk-test-node-6.err.log", + "out_file": "./test/integration/logs/lisk-test-node-6.out.log" + }, + { + "exec_mode": "fork", + "script": "app.js", + "name": "node_7", + "args": " -p 5007 -h 4007 -x 127.0.0.1:5001 -d lisk_local_7 -c ./test/integration/configs/config.node-7.json", + "env": { + "NODE_ENV": "test" + }, + "error_file": "./test/integration/logs/lisk-test-node-7.err.log", + "out_file": "./test/integration/logs/lisk-test-node-7.out.log" + }, + { + "exec_mode": "fork", + "script": "app.js", + "name": "node_8", + "args": " -p 5008 -h 4008 -x 127.0.0.1:5001 -d lisk_local_8 -c ./test/integration/configs/config.node-8.json", + "env": { + "NODE_ENV": "test" + }, + "error_file": "./test/integration/logs/lisk-test-node-8.err.log", + "out_file": "./test/integration/logs/lisk-test-node-8.out.log" + }, + { + "exec_mode": "fork", + "script": "app.js", + "name": "node_9", + "args": " -p 5009 -h 4009 -x 127.0.0.1:5001 -d lisk_local_9 -c ./test/integration/configs/config.node-9.json", + "env": { + "NODE_ENV": "test" + }, + "error_file": "./test/integration/logs/lisk-test-node-9.err.log", + "out_file": "./test/integration/logs/lisk-test-node-9.out.log" + } + ] +} \ No newline at end of file diff --git a/test/node.js b/test/node.js index 5cb4503347c..6a29eb5b498 100644 --- a/test/node.js +++ b/test/node.js @@ -22,16 +22,18 @@ node.chai.config.includeStack = true; node.chai.use(require('chai-bignumber')(node.bignum)); node.lisk = require('lisk-js'); node.supertest = require('supertest'); +var randomString = require('randomstring'); require('colors'); // Node configuration -node.baseUrl = 'http://' + node.config.address + ':' + node.config.port; +node.baseUrl = 'http://' + node.config.address + ':' + node.config.httpPort; node.api = node.supertest(node.baseUrl); node.normalizer = 100000000; // Use this to convert LISK amount to normal value node.blockTime = 10000; // Block time in miliseconds node.blockTimePlus = 12000; // Block time + 2 seconds in miliseconds node.version = node.config.version; // Node version +node.nonce = randomString.generate(16); // Transaction fees node.fees = { @@ -223,53 +225,23 @@ node.waitForNewBlock = function (height, blocksToWait, cb) { ); }; -// Adds peers to local node -node.addPeers = function (numOfPeers, ip, cb) { +node.generatePeerHeaders = function (ip, port) { + port = port || 9999; + ip = ip || '127.0.0.1'; var operatingSystems = ['win32','win64','ubuntu','debian', 'centos']; - var port = 9999; // Frozen peer port - var os, version; - var i = 0; - - node.async.whilst(function () { - return i < numOfPeers; - }, function (next) { - os = operatingSystems[node.randomizeSelection(operatingSystems.length)]; - version = node.version; - - var request = node.popsicle.get({ - url: node.baseUrl + '/peer/height', - headers: { - broadhash: node.config.nethash, - height: 1, - nethash: node.config.nethash, - os: os, - ip: ip, - port: port, - version: version, - nonce: 'randomNonce' - } - }); - - request.use(node.popsicle.plugins.parse(['json'])); - - request.then(function (res) { - if (res.status !== 200) { - return next(['Received bad response code', res.status, res.url].join(' ')); - } else { - i++; - next(); - } - }); - - request.catch(function (err) { - return next(err); - }); - }, function (err) { - // Wait for peer to be swept to db - setTimeout(function () { - return cb(err, {os: os, version: version, port: port}); - }, 3000); - }); + var os = operatingSystems[node.randomizeSelection(operatingSystems.length)]; + var version = node.version; + + return { + broadhash: node.config.nethash, + height: 1, + nethash: node.config.nethash, + os: os, + ip: ip, + port: port, + version: version, + nonce: node.nonce + }; }; // Returns a random index for an array @@ -357,54 +329,6 @@ node.randomPassword = function () { return Math.random().toString(36).substring(7); }; -// Abstract request -function abstractRequest (options, done) { - var request = node.api[options.verb.toLowerCase()](options.path); - - request.set('Accept', 'application/json'); - request.set('version', node.version); - request.set('nethash', node.config.nethash); - request.set('ip', '0.0.0.0'); - request.set('port', node.config.port); - - request.expect('Content-Type', /json/); - request.expect(200); - - if (options.params) { - request.send(options.params); - } - - var verb = options.verb.toUpperCase(); - node.debug(['> Path:'.grey, verb, options.path].join(' ')); - if (verb === 'POST' || verb === 'PUT') { - node.debug(['> Data:'.grey, JSON.stringify(options.params)].join(' ')); - } - - if (done) { - request.end(function (err, res) { - node.debug('> Response:'.grey, JSON.stringify(res.body)); - done(err, res); - }); - } else { - return request; - } -} - -// Get the given path -node.get = function (path, done) { - return abstractRequest({ verb: 'GET', path: path, params: null }, done); -}; - -// Post to the given path -node.post = function (path, params, done) { - return abstractRequest({ verb: 'POST', path: path, params: params }, done); -}; - -// Put to the given path -node.put = function (path, params, done) { - return abstractRequest({ verb: 'PUT', path: path, params: params }, done); -}; - before(function (done) { require('./common/globalBefore').waitUntilBlockchainReady(done); }); diff --git a/test/unit/helpers/RPC.js b/test/unit/helpers/RPC.js new file mode 100644 index 00000000000..eb9f67708ef --- /dev/null +++ b/test/unit/helpers/RPC.js @@ -0,0 +1,178 @@ +'use strict'; + +var config = require('../../../config.json'); + +var chai = require('chai'); +var expect = require('chai').expect; +var express = require('express'); +var _ = require('lodash'); +var sinon = require('sinon'); + +var constants = require('../../../helpers/constants'); +var WAMPClient = require('wamp-socket-cluster/WAMPClient'); + +var wsRPC = require('../../../api/ws/rpc/wsRPC').wsRPC; +var ClientRPCStub = require('../../../api/ws/rpc/wsRPC').ClientRPCStub; +var ConnectionState = require('../../../api/ws/rpc/wsRPC').ConnectionState; + +var MasterWAMPServer = require('wamp-socket-cluster/MasterWAMPServer'); + +var socketClusterMock = { + on: sinon.spy() +}; + +describe('wsRPC', function () { + + it('should have empty clientsConnectionsMap field', function () { + expect(wsRPC).to.have.property('clientsConnectionsMap').to.be.a('object').and.to.be.empty; + }); + + it('should have wampClient field of instance WAMPClient', function () { + expect(wsRPC).to.have.property('wampClient').and.to.be.a('object'); + expect(wsRPC.wampClient.constructor.name).equal('WAMPClient'); + }); + + it('should have scClient field without connections', function () { + expect(wsRPC).to.have.property('scClient').and.to.be.a('object'); + expect(wsRPC.scClient).to.have.property('connections').to.be.a('object').and.to.be.empty; + }); + + describe('setServer', function () { + + before(function () { + wsRPC.setServer(null); + }); + + it('getter should return throw an error when setting server to null', function () { + wsRPC.setServer(null); + expect(wsRPC.getServer).to.throw('WS server haven\'t been initialized!'); + }); + + it('getter should return throw an error when setting server to 0', function () { + wsRPC.setServer(0); + expect(wsRPC.getServer).to.throw('WS server haven\'t been initialized!'); + }); + + it('getter should return throw an error when setting server to undefined', function () { + wsRPC.setServer(undefined); + expect(wsRPC.getServer).to.throw('WS server haven\'t been initialized!'); + }); + + it('should return server instance after setting it', function () { + wsRPC.setServer({name: 'my ws server'}); + var wsRPCServer = wsRPC.getServer(); + expect(wsRPCServer).to.be.an('object').eql({name: 'my ws server'}); + }); + + after(function () { + wsRPC.setServer(null); + }); + }); + + describe('getServer', function () { + + before(function () { + wsRPC.setServer(null); + }); + + it('should raise and error when wsSerer is not set', function () { + expect(wsRPC.getServer).to.throw('WS server haven\'t been initialized!'); + }); + + it('should return wsSerer set before', function () { + wsRPC.setServer({name: 'my ws server'}); + expect(wsRPC.getServer).not.to.throw; + expect(wsRPC.getServer()).to.a('object').eql({name: 'my ws server'}); + }); + + after(function () { + wsRPC.setServer(null); + }); + }); + + describe('getClientRPCStub', function () { + + var validPort = 4000, validIp = '127.0.0.1'; + + it('should throw error when no arguments specified', function () { + expect(wsRPC.getClientRPCStub).to.throw('RPC client needs ip and port to establish WS connection with: undefined:undefined'); + }); + + it('should throw error when no port specified', function (done) { + try { + wsRPC.getClientRPCStub(validIp, undefined); + } catch (er) { + expect(er.message).equal('RPC client needs ip and port to establish WS connection with: 127.0.0.1:undefined'); + return done(); + } + done('Should not be here'); + }); + + it('should throw error when no ip specified', function (done) { + try { + wsRPC.getClientRPCStub(undefined, validPort); + } catch (er) { + expect(er.message).equal('RPC client needs ip and port to establish WS connection with: undefined:4000'); + return done(); + } + done('Should not be here'); + }); + + it('should not initialize new connection just after getting RPC stub', function () { + ClientRPCStub.prototype.initializeNewConnection = sinon.spy(); + wsRPC.getClientRPCStub(validIp, validPort); + expect(ClientRPCStub.prototype.initializeNewConnection.called).not.to.be.ok; + }); + + it('should add new entry in clientsConnectionsMap after getting stub', function () { + wsRPC.getClientRPCStub(validIp, validPort); + expect(wsRPC.clientsConnectionsMap).to.have.property(validIp + ':' + validPort).to.be.an.instanceof(ConnectionState); + }); + + it('should return empty client stub when no endpoints registered', function () { + var rpcStub = wsRPC.getClientRPCStub(validIp, validPort); + expect(rpcStub).to.be.a('object').and.to.be.empty; + }); + + describe('stub', function () { + var validRPCEndpoint = { + 'rpcProcedure': function (param) { + return param; + } + }; + + var masterWAMPServer, masterWAMPServerConfig = {}; + + beforeEach(function () { + wsRPC.clientsConnectionsMap = {}; + masterWAMPServer = new MasterWAMPServer(socketClusterMock, masterWAMPServerConfig); + wsRPC.setServer(masterWAMPServer); + }); + + it('should return client stub with rpc methods registered on MasterWAMPServer', function () { + var wsServer = wsRPC.getServer(); + wsServer.reassignRPCEndpoints(validRPCEndpoint); + var rpcStub = wsRPC.getClientRPCStub(validIp, validPort); + expect(rpcStub).to.have.property('rpcProcedure').and.to.be.a('function'); + }); + + var validEventEndpoint = { + 'eventProcedure': function (param) { + return param; + } + }; + + it('should return client stub with event and rpc methods registered on MasterWAMPServer', function () { + var wsServer = wsRPC.getServer(); + wsServer.reassignRPCEndpoints(validRPCEndpoint); + wsServer.reassignEventEndpoints(validEventEndpoint); + var rpcStub = wsRPC.getClientRPCStub(validIp, validPort); + expect(rpcStub).to.have.property('eventProcedure').and.to.be.a('function'); + expect(rpcStub).to.have.property('rpcProcedure').and.to.be.a('function'); + }); + + }); + + + }); +}); diff --git a/test/unit/helpers/RoundChanges.js b/test/unit/helpers/RoundChanges.js index ad4e411b6f3..a97391d0f71 100644 --- a/test/unit/helpers/RoundChanges.js +++ b/test/unit/helpers/RoundChanges.js @@ -1,10 +1,11 @@ 'use strict'; var chai = require('chai'); +var expect = require('chai').expect; + var express = require('express'); var ip = require('ip'); var _ = require('lodash'); -var node = require('../../node.js'); var RoundChanges = require('../../../helpers/RoundChanges.js'); describe('RoundChanges', function () { @@ -24,8 +25,8 @@ describe('RoundChanges', function () { it('should accept valid scope', function () { var roundChanges = new RoundChanges(validScope); - node.expect(roundChanges.roundFees).equal(validScope.roundFees); - node.expect(_.isEqual(roundChanges.roundRewards, validScope.roundRewards)).to.be.ok; + expect(roundChanges.roundFees).equal(validScope.roundFees); + expect(_.isEqual(roundChanges.roundRewards, validScope.roundRewards)).to.be.ok; }); it('should floor fees value', function () { @@ -33,7 +34,7 @@ describe('RoundChanges', function () { var roundChanges = new RoundChanges(validScope); - node.expect(roundChanges.roundFees).equal(50); + expect(roundChanges.roundFees).equal(50); }); it('should round up fees after exceeding precision', function () { @@ -41,7 +42,7 @@ describe('RoundChanges', function () { var roundChanges = new RoundChanges(validScope); - node.expect(roundChanges.roundFees).equal(51); + expect(roundChanges.roundFees).equal(51); }); it('should accept Infinite fees as expected', function () { @@ -49,7 +50,7 @@ describe('RoundChanges', function () { var roundChanges = new RoundChanges(validScope); - node.expect(roundChanges.roundFees).equal(Infinity); + expect(roundChanges.roundFees).equal(Infinity); }); }); @@ -60,10 +61,10 @@ describe('RoundChanges', function () { var rewardsAt = 2; var res = roundChanges.at(rewardsAt); - node.expect(res.fees).equal(4); - node.expect(res.feesRemaining).equal(96); - node.expect(res.rewards).equal(validScope.roundRewards[rewardsAt]); // 100 - node.expect(res.balance).equal(104); + expect(res.fees).equal(4); + expect(res.feesRemaining).equal(96); + expect(res.rewards).equal(validScope.roundRewards[rewardsAt]); // 100 + expect(res.balance).equal(104); }); it('should calculate round changes from Infinite fees', function () { @@ -73,10 +74,10 @@ describe('RoundChanges', function () { var rewardsAt = 2; var res = roundChanges.at(rewardsAt); - node.expect(res.fees).equal(Infinity); - node.expect(res.feesRemaining).to.be.NaN; - node.expect(res.rewards).equal(validScope.roundRewards[rewardsAt]); // 100 - node.expect(res.balance).equal(Infinity); + expect(res.fees).equal(Infinity); + expect(res.feesRemaining).to.be.NaN; + expect(res.rewards).equal(validScope.roundRewards[rewardsAt]); // 100 + expect(res.balance).equal(Infinity); }); it('should calculate round changes from Number.MAX_VALUE fees', function () { @@ -87,12 +88,12 @@ describe('RoundChanges', function () { var res = roundChanges.at(rewardsAt); var expectedFees = 1779894192932990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099; // 1.7976931348623157e+308 / 101 (delegates num) - node.expect(res.fees).equal(expectedFees); - node.expect(res.rewards).equal(validScope.roundRewards[rewardsAt]); // 100 - node.expect(res.feesRemaining).equal(1); + expect(res.fees).equal(expectedFees); + expect(res.rewards).equal(validScope.roundRewards[rewardsAt]); // 100 + expect(res.feesRemaining).equal(1); var expectedBalance = 1779894192932990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990099009900990199; // 1.7976931348623157e+308 / 101 (delegates num) + 100 - node.expect(res.balance).equal(expectedBalance); + expect(res.balance).equal(expectedBalance); }); }); }); diff --git a/test/unit/helpers/wsApi.js b/test/unit/helpers/wsApi.js new file mode 100644 index 00000000000..0688dc5a28b --- /dev/null +++ b/test/unit/helpers/wsApi.js @@ -0,0 +1,127 @@ +'use strict'; + +var config = require('../../../config.json'); + +var chai = require('chai'); +var expect = require('chai').expect; +var express = require('express'); +var _ = require('lodash'); +var sinon = require('sinon'); + +var wsApi = require('../../../helpers/wsApi'); + +var System = require('../../../modules/system'); + +describe('handshake', function () { + + var system, handshake, validRequest; + + before(function (done) { + + sinon.stub(System.prototype, 'getNethash').returns(config.nethash); + sinon.stub(System.prototype, 'getMinVersion').returns(config.version); + + new System(function (err, __system) { + system = __system; + handshake = wsApi.middleware.Handshake(system); + done(err); + }, {config: { + version: config.version, + minVersion: config.minVersion, + nethash: config.nethash, + nonce: 'EXAMPLE_NONCE' + }}); + + validRequest = { + ip: '0.0.0.0', + port: 4000, + nethash: config.nethash, + version: config.version, + nonce: 'PEER_NONCE' + }; + }); + + it('should accept handshake when valid request passed', function (done) { + + handshake(validRequest, function (err, data) { + expect(err).to.be.null; + done(); + }); + }); + + it('should return error when empty undefined request', function (done) { + + handshake(undefined, function (err, data) { + expect(err).not.to.be.empty; + expect(err).to.have.property('error').that.is.an('array'); + done(); + }); + }); + + it('should return error when empty null request', function (done) { + + handshake(null, function (err, data) { + expect(err).not.to.be.empty; + expect(err).to.have.property('error').that.is.an('array'); + done(); + }); + }); + + it('should return error when 0 as request', function (done) { + + handshake(0, function (err, data) { + expect(err).not.to.be.empty; + expect(err).to.have.property('error').that.is.an('array'); + done(); + }); + }); + + it('should return error when empty request passed', function (done) { + + handshake({}, function (err, data) { + expect(err).not.to.be.empty; + expect(err).to.have.property('error').that.is.an('array'); + done(); + }); + }); + + it('should return error when wrong request without version passed', function (done) { + var malformedRequest = _.clone(validRequest); + delete malformedRequest.version; + handshake(malformedRequest, function (err, data) { + expect(err).not.to.be.empty; + expect(err).to.have.property('error').that.is.an('array'); + done(); + }); + }); + + it('should return error when wrong request without port passed', function (done) { + var malformedRequest = _.clone(validRequest); + delete malformedRequest.port; + handshake(malformedRequest, function (err, data) { + expect(err).not.to.be.empty; + expect(err).to.have.property('error').that.is.an('array'); + done(); + }); + }); + + it('should return error when wrong request without ip passed', function (done) { + var malformedRequest = _.clone(validRequest); + delete malformedRequest.ip; + handshake(malformedRequest, function (err, data) { + expect(err).not.to.be.empty; + expect(err).to.have.property('error').that.is.an('array'); + done(); + }); + }); + + it('should return error when wrong request without nethash passed', function (done) { + var malformedRequest = _.clone(validRequest); + delete malformedRequest.nethash; + handshake(malformedRequest, function (err, data) { + expect(err).not.to.be.empty; + expect(err).to.have.property('error').that.is.an('array'); + done(); + }); + }); +}); diff --git a/test/unit/index.js b/test/unit/index.js new file mode 100644 index 00000000000..30bcf961d05 --- /dev/null +++ b/test/unit/index.js @@ -0,0 +1,20 @@ +require('./helpers/RPC'); +require('./helpers/RoundChanges'); +require('./helpers/request-limiter.js'); +require('./helpers/wsApi'); + +require('./logic/blockReward.js'); +require('./logic/peer'); +require('./logic/peers'); +require('./logic/transaction'); +require('./logic/transfer'); +require('./logic/vote'); + +require('./modules/blocks.js'); +require('./modules/cache.js'); +require('./modules/loader.js'); +require('./modules/peers.js'); +require('./modules/rounds.js'); + +require('./sql/blockRewards.js'); + diff --git a/test/unit/logic/peer.js b/test/unit/logic/peer.js index ca355743ca2..624225e6256 100644 --- a/test/unit/logic/peer.js +++ b/test/unit/logic/peer.js @@ -18,6 +18,17 @@ describe('peer', function () { peer = new Peer({}); }); + describe('constructor', function () { + + it('should create Peer with all properties implemented', function () { + var __peer = new Peer({ip: '127.0.0.1', port: 4000}); + expect(__peer).to.have.property('ip').equal('127.0.0.1'); + expect(__peer).to.have.property('port').equal(4000); + expect(__peer).to.have.property('state').equal(1); + expect(__peer).to.have.property('string').equal('127.0.0.1:4000'); + }); + }); + describe('accept', function () { it('should accept valid peer', function () { @@ -124,6 +135,16 @@ describe('peer', function () { peer.state = initialState; }); + it('should change state of banned peer', function () { + var initialState = peer.state; + // Ban peer + peer.state = 0; + // Try to unban peer + peer.update({state: 2}); + expect(peer.state).to.equal(2); + peer.state = initialState; + }); + it('should update defined values', function () { var updateData = { os: 'test os', @@ -132,7 +153,7 @@ describe('peer', function () { height: 3, nonce: 'ABCD123' }; - expect(_.isEqual(_.keys(updateData), peer.headers)).to.be.ok; + expect(_.difference(_.keys(updateData), peer.headers)).to.have.lengthOf(0); peer.update(updateData); peer.headers.forEach(function (header) { expect(peer[header]).to.exist.and.equals(updateData[header]); @@ -144,6 +165,7 @@ describe('peer', function () { var updateImmutableData = { ip: randomPeer.ip, port: randomPeer.port, + httpPort: randomPeer.port, string: randomPeer.ip + ':' + randomPeer.port }; @@ -153,6 +175,22 @@ describe('peer', function () { expect(peer[header]).equals(peerBeforeUpdate[header]).and.not.equal(updateImmutableData); }); }); + + it('should not delete values which were previously set but are not updated now', function () { + var updateData = { + os: 'test os', + version: '0.0.0', + dappid: ['test dappid'], + broadhash: 'test broadhash', + height: 3, + nonce: 'ABCD123' + }; + peer.update(updateData); + var peerBeforeUpdate = _.clone(peer); + peer.update({height: peer.height += 1}); + peer.height -= 1; + expect(_.isEqual(peer, peerBeforeUpdate)).to.be.ok; + }); }); describe('object', function () { @@ -162,7 +200,9 @@ describe('peer', function () { var peerCopy = __peer.object(); _.keys(randomPeer).forEach(function (property) { if (__peer.properties.indexOf(property) !== -1) { - expect(peerCopy[property]).to.equal(randomPeer[property]); + if (typeof randomPeer[property] !== 'object') { + expect(peerCopy[property]).to.equal(randomPeer[property]); + } if (__peer.nullable.indexOf(property) !== -1 && !randomPeer[property]) { expect(peerCopy[property]).to.be.null; } @@ -177,5 +217,6 @@ describe('peer', function () { expect(peerCopy.state).to.equal(1); peer.state = initialState; }); + }); }); diff --git a/test/unit/logic/peers.js b/test/unit/logic/peers.js index 118bd7fb511..c852c8a99d3 100644 --- a/test/unit/logic/peers.js +++ b/test/unit/logic/peers.js @@ -20,15 +20,20 @@ describe('peers', function () { if (err) { return done(err); } - __modules.peers.onBind(__modules); + var peersModuleMock = { + acceptable: function (peers) { + return peers; + } + }; + modulesLoader.initLogic(Peers, modulesLoader.scope, function (err, __peers) { peers = __peers; - peers.bindModules({peers: __modules.peers}); + peers.bindModules({peers: peersModuleMock}); done(); }); - }); + }, {}); }); function removeAll () { @@ -42,7 +47,7 @@ describe('peers', function () { function arePeersEqual (peerA, peerB) { var allPeersProperties = function (peer) { return _.keys(peer).every(function (property) { - return Peer.prototype.properties.concat(['string']).indexOf(property) !== -1; + return Peer.prototype.properties.concat(['string', 'rpc']).indexOf(property) !== -1; }); }; @@ -74,46 +79,52 @@ describe('peers', function () { }); describe('list', function () { - it('should list peers as Peer instances', function () { + + beforeEach(function () { removeAll(); + }); + + it('should list peers as Peer instances', function () { peers.upsert(randomPeer); peers.list().forEach(function (peer) { expect(peer).to.be.an.instanceof(Peer); }); - removeAll(); + }); + + it('should list peers with rpc', function () { + peers.upsert(randomPeer); + peers.list().forEach(function (peer) { + expect(peer).have.property('rpc'); + }); }); it('should list peers as objects when normalized', function () { - removeAll(); peers.upsert(randomPeer); peers.list(true).forEach(function (peer) { expect(peer).to.be.an('object'); }); - removeAll(); + }); + + it('should should not contain rpc when normalized', function () { + peers.upsert(randomPeer); + peers.list(true).forEach(function (peer) { + expect(peer).not.to.have.property('rpc'); + }); }); }); describe('upsert', function () { - it('should insert new peers', function () { - removeAll(); - peers.upsert(randomPeer); - expect(peers.list().length).equal(1); + beforeEach(function () { removeAll(); }); - it('should not insert new peer with lisk-js-api os', function () { - removeAll(); - var modifiedPeer = _.clone(randomPeer); - modifiedPeer.os = 'lisk-js-api'; - peers.upsert(modifiedPeer); - expect(peers.list().length).equal(0); - removeAll(); + it('should insert new peers', function () { + peers.upsert(randomPeer); + expect(peers.list().length).equal(1); }); it('should update height of existing peer', function () { - removeAll(); - peers.upsert(randomPeer); var list = peers.list(); var inserted = list[0]; @@ -129,12 +140,9 @@ describe('peers', function () { expect(arePeersEqual(updated, modifiedPeer)).to.be.ok; expect(arePeersEqual(updated, randomPeer)).to.be.not.ok; - removeAll(); }); it('should not update height with insertOnly param', function () { - removeAll(); - peers.upsert(randomPeer); var list = peers.list(); var inserted = list[0]; @@ -149,17 +157,14 @@ describe('peers', function () { expect(list.length).equal(1); expect(arePeersEqual(updated, modifiedPeer)).to.be.not.ok; expect(arePeersEqual(updated, randomPeer)).to.be.ok; - - removeAll(); }); it('should insert peer with different ports', function () { - removeAll(); - peers.upsert(randomPeer); expect(peers.list().length).equal(1); var differentPortPeer = _.clone(randomPeer); + differentPortPeer.nonce = 'differentNonce'; differentPortPeer.port += 1; peers.upsert(differentPortPeer); var list = peers.list(); @@ -169,18 +174,16 @@ describe('peers', function () { var listPorts = _.map(list, 'port'); expect(_.isEqual(demandedPorts.sort(), listPorts.sort())).to.be.ok; - - removeAll(); }); it('should insert peer with different ips', function () { - removeAll(); - peers.upsert(randomPeer); expect(peers.list().length).equal(1); var differentIpPeer = _.clone(randomPeer); differentIpPeer.ip = '40.40.40.41'; + differentIpPeer.nonce = 'differentNonce'; + expect(differentIpPeer.ip).to.not.equal(randomPeer); peers.upsert(differentIpPeer); var list = peers.list(); @@ -190,38 +193,60 @@ describe('peers', function () { var listIps = _.map(list, 'ip'); expect(_.isEqual(demandedIps.sort(), listIps.sort())).to.be.ok; - - removeAll(); }); }); describe('exists', function () { - it('should return true if peer is on the list', function () { + beforeEach(function () { removeAll(); + }); + + it('should return false if peer is not on the list', function () { + expect(peers.exists({ + ip: '41.41.41.41', + port: '4444', + nonce: 'another_nonce' + })).not.to.be.ok; + }); + it('should return true if peer is on the list', function () { peers.upsert(randomPeer); var list = peers.list(true); expect(list.length).equal(1); expect(peers.exists(randomPeer)).to.be.ok; + }); - var differentPortPeer = _.clone(randomPeer); - differentPortPeer.port += 1; - expect(peers.exists(differentPortPeer)).to.be.not.ok; + it.skip('should return true if peer with same nonce is on the list', function () { + peers.upsert(randomPeer); + var list = peers.list(true); + expect(list.length).equal(1); + expect(peers.exists({nonce: randomPeer.nonce})).to.be.ok; }); + + it('should return true if peer with same address is on the list', function () { + peers.upsert(randomPeer); + var list = peers.list(true); + expect(list.length).equal(1); + expect(peers.exists({ip: randomPeer.ip, port: randomPeer.port})).to.be.ok; + }); + + }); describe('get', function () { - it('should return inserted peer', function () { + beforeEach(function () { removeAll(); + }); + + it('should return inserted peer', function () { peers.upsert(randomPeer); var insertedPeer = peers.get(randomPeer); expect(arePeersEqual(insertedPeer, randomPeer)).to.be.ok; }); it('should return inserted peer by address', function () { - removeAll(); peers.upsert(randomPeer); var insertedPeer = peers.get(randomPeer.ip + ':' + randomPeer.port); expect(arePeersEqual(insertedPeer, randomPeer)).to.be.ok; @@ -229,15 +254,17 @@ describe('peers', function () { }); it('should return undefined if peer is not inserted', function () { - removeAll(); expect(peers.get(randomPeer)).to.be.undefined; }); }); describe('remove', function () { - it('should remove added peer', function () { + beforeEach(function () { removeAll(); + }); + + it('should remove added peer', function () { peers.upsert(randomPeer); expect(peers.list().length).equal(1); var result = peers.remove(randomPeer); @@ -246,11 +273,266 @@ describe('peers', function () { }); it('should return false when trying to remove non inserted peer', function () { - removeAll(); var result = peers.remove(randomPeer); expect(result).to.be.not.ok; expect(peers.list().length).equal(0); }); }); + describe('peersManager', function () { + + beforeEach(function () { + removeAll(); + }); + + it('should have all fields empty at start', function () { + expect(peers.peersManager).to.have.property('peers').and.to.be.empty; + expect(peers.peersManager).to.have.property('addressToNonceMap').and.to.be.empty; + expect(peers.peersManager).to.have.property('nonceToAddressMap').and.to.be.empty; + }); + + describe('add', function () { + + var validPeer = new Peer(randomPeer); + + beforeEach(function () { + validPeer = new Peer(randomPeer); + _.each(peers.peersManager.getAll(), function (peer) { + peers.peersManager.remove(peer); + }); + }); + + + it('should insert valid peer and update fields', function () { + peers.peersManager.add(validPeer); + var expectedPeer = {}; + expectedPeer[validPeer.string] = validPeer; + expect(peers.peersManager.peers).to.eql(expectedPeer); + expect(peers.peersManager.addressToNonceMap).to.eql({'40.40.40.40:5000': 'randomnonce'}); + expect(peers.peersManager.nonceToAddressMap).to.eql({'randomnonce': '40.40.40.40:5000'}); + }); + + it('should not duplicate entries in fields', function () { + peers.peersManager.add(validPeer); + peers.peersManager.add(validPeer); + var expectedPeer = { + '40.40.40.40:5000': validPeer + }; + expect(peers.peersManager.peers).to.eql(expectedPeer); + expect(peers.peersManager.addressToNonceMap).to.eql({'40.40.40.40:5000': 'randomnonce'}); + expect(peers.peersManager.nonceToAddressMap).to.eql({'randomnonce': '40.40.40.40:5000'}); + }); + + it('should not insert peer without address', function () { + delete validPeer.string; + peers.peersManager.add(validPeer); + expect(peers.peersManager.peers).to.eql({}); + expect(peers.peersManager.addressToNonceMap).to.eql({}); + expect(peers.peersManager.nonceToAddressMap).to.eql({}); + }); + + it('should be possible to add peer without nonce but without entry in nonceToAddressMap', function () { + delete validPeer.nonce; + peers.peersManager.add(validPeer); + var expectedPeer = { + '40.40.40.40:5000': validPeer + }; + expect(peers.peersManager.peers).to.eql(expectedPeer); + expect(peers.peersManager.addressToNonceMap).to.eql({'40.40.40.40:5000': undefined}); + expect(peers.peersManager.nonceToAddressMap).to.eql({}); + }); + + it('should not be possible to add 2 peers with different addresses but same nonce', function () { + var peerB = _.clone(validPeer); + peerB.string = '50.40.40.40:4000'; + + peers.peersManager.add(validPeer); + peers.peersManager.add(peerB); + + expect(peers.peersManager.peers).to.eql({ + '40.40.40.40:5000': validPeer + }); + expect(peers.peersManager.addressToNonceMap).to.eql({'40.40.40.40:5000': 'randomnonce'}); + expect(peers.peersManager.nonceToAddressMap).to.eql({'randomnonce': '40.40.40.40:5000'}); + }); + + it('should not be possible to add multiple entries', function () { + var peerA = validPeer; + var peerB = _.clone(validPeer); + peerB.string = '50.40.40.40:4000'; + peerB.nonce = 'peerBNonce'; + + peers.peersManager.add(peerA); + peers.peersManager.add(peerB); + + var expectedPeers = { + '40.40.40.40:5000': peerA, + '50.40.40.40:4000': peerB + }; + + expect(peers.peersManager.peers).to.eql(expectedPeers); + expect(peers.peersManager.addressToNonceMap).to.eql({ + '40.40.40.40:5000': 'randomnonce', + '50.40.40.40:4000': 'peerBNonce' + }); + expect(peers.peersManager.nonceToAddressMap).to.eql({ + 'randomnonce': '40.40.40.40:5000', + 'peerBNonce': '50.40.40.40:4000' + }); + }); + }); + + describe('remove', function () { + + var validPeer = new Peer(randomPeer); + + beforeEach(function () { + validPeer = new Peer(randomPeer); + _.each(peers.peersManager.getAll(), function (peer) { + peers.peersManager.remove(peer); + }); + }); + + it('remove entry from all fields', function () { + peers.peersManager.add(validPeer); + peers.peersManager.remove(validPeer); + expect(peers.peersManager.peers).to.eql({}); + expect(peers.peersManager.addressToNonceMap).to.eql({}); + expect(peers.peersManager.nonceToAddressMap).to.eql({}); + }); + + }); + + describe('getByNonce', function () { + + var validPeer = new Peer(randomPeer); + + beforeEach(function () { + validPeer = new Peer(randomPeer); + _.each(peers.peersManager.getAll(), function (peer) { + peers.peersManager.remove(peer); + }); + }); + + it('should return added peer relying on his nonce', function () { + peers.peersManager.add(validPeer); + var receivedPeer = peers.peersManager.getByNonce(validPeer.nonce); + expect(receivedPeer).to.eql(validPeer); + }); + + it('should return undefined for not existing peers', function () { + var receivedPeer = peers.peersManager.getByNonce('notExistingNonce'); + expect(receivedPeer).to.be.undefined; + }); + + }); + + describe('getByAddress', function () { + + var validPeer = new Peer(randomPeer); + + beforeEach(function () { + validPeer = new Peer(randomPeer); + _.each(peers.peersManager.getAll(), function (peer) { + peers.peersManager.remove(peer); + }); + }); + + it('should return added peer relying on his nonce', function () { + peers.peersManager.add(validPeer); + var receivedPeer = peers.peersManager.getByAddress(validPeer.string); + expect(receivedPeer).to.eql(validPeer); + }); + + it('should return undefined for not existing peers', function () { + var receivedPeer = peers.peersManager.getByAddress('notExistingAddress'); + expect(receivedPeer).to.be.undefined; + }); + + }); + + describe('getNonce', function () { + + var validPeer = new Peer(randomPeer); + + beforeEach(function () { + validPeer = new Peer(randomPeer); + _.each(peers.peersManager.getAll(), function (peer) { + peers.peersManager.remove(peer); + }); + }); + + it('should return nonce of added peer', function () { + peers.peersManager.add(validPeer); + var receivedNonce = peers.peersManager.getNonce(validPeer.string); + expect(receivedNonce).to.eql(validPeer.nonce); + }); + + it('should return undefined no peer with given nonce was added', function () { + var receivedNonce = peers.peersManager.getNonce('notExistingAddress'); + expect(receivedNonce).to.be.undefined; + }); + + }); + + describe('getAddress', function () { + + var validPeer = new Peer(randomPeer); + + beforeEach(function () { + validPeer = new Peer(randomPeer); + _.each(peers.peersManager.getAll(), function (peer) { + peers.peersManager.remove(peer); + }); + }); + + it('should return nonce of added peer', function () { + peers.peersManager.add(validPeer); + var receivedAddress = peers.peersManager.getAddress(validPeer.nonce); + expect(receivedAddress).to.eql(validPeer.string); + }); + + it('should return undefined no peer with address nonce was added', function () { + var receivedAddress = peers.peersManager.getAddress('notExistingNonce'); + expect(receivedAddress).to.be.undefined; + }); + + }); + + describe('getAll', function () { + + var validPeer = new Peer(randomPeer); + + beforeEach(function () { + validPeer = new Peer(randomPeer); + _.each(peers.peersManager.getAll(), function (peer) { + peers.peersManager.remove(peer); + }); + }); + + it('should return empty object when no peers added', function () { + var receivedPeers = peers.peersManager.getAll(); + expect(receivedPeers).to.be.empty; + }); + + it('should return all valid peers added', function () { + var peerA = validPeer; + var peerB = _.clone(validPeer); + peerB.string = '50.40.40.40:4000'; + peerB.nonce = 'peerBNonce'; + + peers.peersManager.add(peerA); + peers.peersManager.add(peerB); + + var expectedPeers = { + '40.40.40.40:5000': peerA, + '50.40.40.40:4000': peerB + }; + + expect(peers.peersManager.getAll()).to.eql(expectedPeers); + }); + + }); + + }); }); diff --git a/test/unit/modules/accounts.js b/test/unit/modules/accounts.js index ec172af35b8..e1b7cac89f5 100644 --- a/test/unit/modules/accounts.js +++ b/test/unit/modules/accounts.js @@ -1,5 +1,6 @@ 'use strict';/*eslint*/ +var _ = require('lodash'); var node = require('./../../node.js'); var ed = require('../../../helpers/ed'); var bignum = require('../../../helpers/bignum.js'); @@ -10,7 +11,7 @@ var sinon = require('sinon'); var chai = require('chai'); var expect = require('chai').expect; var constants = require('../../../helpers/constants.js'); -var _ = require('lodash'); +var ws = require('../../common/wsCommunication.js'); var Rounds = require('../../../modules/rounds.js'); var AccountLogic = require('../../../logic/account.js'); @@ -475,20 +476,18 @@ describe('account', function () { describe('addDelegates (multisignature account)', function () { function postSignature (transaction, signature, done) { - node.post('/peer/signatures', { + ws.call('postSignatures', { signature: { transaction: transaction.id, signature: signature } - }, done); + }, done, true); } function addTransaction (transaction, done) { - node.post('/peer/transactions', { + ws.call('postTransactions', { transaction: transaction - }, function (err, res) { - done(err, res.body); - }); + }, done, transaction); } var multiAccount = node.randomAccount(); @@ -689,7 +688,7 @@ describe('account', function () { }, function (err, res) { expect(err).to.not.exist; expect(res.accounts).to.have.length(10); - expect(res.accounts.map(function (v) { return v.balance; })).to.eql(topAccountsBalance.slice(0, 10)); + expect(res.accounts.map(function (v) { return v.balance; })).to.equal(topAccountsBalance.slice(0, 10)); done(); }); }); @@ -705,6 +704,8 @@ describe('account', function () { }, function (err, res) { expect(err).to.not.exist; expect(res.accounts).to.have.length(10); + console.log(res.accounts.map(function (v) { return v.balance; })); + console.log(topAccountsBalance.slice(10, 20)); expect(res.accounts.map(function (v) { return v.balance; })).to.eql(topAccountsBalance.slice(10, 20)); done(); }); diff --git a/test/unit/modules/loader.js b/test/unit/modules/loader.js new file mode 100644 index 00000000000..b08e479952d --- /dev/null +++ b/test/unit/modules/loader.js @@ -0,0 +1,91 @@ +'use strict'; + +var chai = require('chai'); +var expect = require('chai').expect; +var _ = require('lodash'); + +var express = require('express'); +var sinon = require('sinon'); + +var modulesLoader = require('../../common/initModule').modulesLoader; + +describe('loader', function () { + + var loader, modules; + + before(function (done) { + modulesLoader.initAllModules(function (err, __modules) { + if (err) { + return done(err); + } + loader = __modules.loader; + modules = __modules; + loader.onBind(modules); + done(); + }, {}); + }); + + describe('findGoodPeers', function () { + + var MY_HEIGHT = 2; + + before(function () { + modules.blocks.lastBlock = { + get: function () { + return { + height: MY_HEIGHT + }; + } + }; + }); + + it('should return peers list sorted by height', function () { + + var peers = [ + { + ip: '1.1.1.1', + port: '4000', + height: 1 + }, + { + ip: '4.4.4.4', + port: '4000', + height: 4 + }, + { + ip: '3.3.3.3', + port: '4000', + height: 3 + }, + { + ip: '2.2.2.2', + port: '4000', + height: 2 + } + ]; + + var goodPeers = loader.findGoodPeers(peers); + expect(goodPeers).to.have.property('height').equal(MY_HEIGHT); //good peers - above my height (above and equal 2) + expect(goodPeers).to.have.property('peers').to.be.an('array').to.have.lengthOf(3); + expect(_.isEqualWith(goodPeers.peers, [ + { + ip: '4.4.4.4', + port: '4000', + height: 4 + }, + { + ip: '3.3.3.3', + port: '4000', + height: 3 + }, + { + ip: '2.2.2.2', + port: '4000', + height: 2 + } + ], function (a, b) { + return a.ip === b.ip && a.port === b.port && a.height === b.height; + })).to.be.ok; + }); + }); +}); diff --git a/test/unit/modules/peers.js b/test/unit/modules/peers.js index 7563d51508f..86e1cf1c58c 100644 --- a/test/unit/modules/peers.js +++ b/test/unit/modules/peers.js @@ -4,29 +4,41 @@ var chai = require('chai'); var expect = require('chai').expect; var express = require('express'); var sinon = require('sinon'); -var randomString = require('randomstring'); var _ = require('lodash'); +var MasterWAMPServer = require('wamp-socket-cluster/MasterWAMPServer'); var config = require('../../config.json'); -var randomPeer = require('../../common/objectStubs').randomPeer; var modulesLoader = require('../../common/initModule').modulesLoader; +var randomPeer = require('../../common/objectStubs').randomPeer; +var wsRPC = require('../../../api/ws/rpc/wsRPC').wsRPC; var currentPeers = []; describe('peers', function () { - var peers, modules; + before(function () { + process.env['NODE_ENV'] = 'TEST'; + }); - var NONCE = randomString.generate(16); + var peers, modules, NONCE; function getPeers (cb) { - peers.list({broadhash: config.nethash}, function (err, __peers) { + peers.list({}, function (err, __peers) { expect(err).to.not.exist; expect(__peers).to.be.an('array'); return cb(err, __peers); }); } + function removeAll (done) { + peers.list({}, function (err, __peers) { + __peers.forEach(function (peer) { + peers.remove(peer); + }); + done(); + }); + } + before(function (done) { modulesLoader.initAllModules(function (err, __modules) { if (err) { @@ -34,125 +46,120 @@ describe('peers', function () { } peers = __modules.peers; modules = __modules; - peers.onBind(__modules); + NONCE = __modules.system.getNonce(); + peers.onBind(modules); done(); - }, {nonce: NONCE}); + }, {}); }); beforeEach(function (done) { - getPeers(function (err, __peers) { - currentPeers = __peers; - done(); - }); + currentPeers = []; + removeAll(done); }); describe('update', function () { + beforeEach(function (done) { + removeAll(done); + currentPeers = []; + }); + it('should insert new peer', function (done) { peers.update(randomPeer); - getPeers(function (err, __peers) { - expect(currentPeers.length + 1).that.equals(__peers.length); - currentPeers = __peers; - var inserted = __peers.find(function (p) { - return p.ip + ':' + p.port === randomPeer.ip + ':' + randomPeer.port; - }); - expect(inserted).to.be.an('object'); - expect(inserted).not.to.be.empty; + expect(__peers).to.be.an('array').and.to.have.lengthOf(1); + expect(__peers[0]).to.have.property('string').equal(randomPeer.ip + ':' + randomPeer.port); done(); }); }); - it('should update existing peer', function (done) { - var toUpdate = _.clone(randomPeer); - toUpdate.height += 1; - peers.update(toUpdate); + + it('should insert new peer with only ip and port and state defined', function (done) { + + var ipAndPortPeer = { + ip: '40.40.40.43', + port: 4000, + state: 2 + }; + + peers.update(ipAndPortPeer); getPeers(function (err, __peers) { - expect(currentPeers.length).that.equals(__peers.length); - currentPeers = __peers; - var updated = __peers.find(function (p) { - return p.ip + ':' + p.port === randomPeer.ip + ':' + randomPeer.port; - }); - expect(updated).to.be.an('object'); - expect(updated).not.to.be.empty; - expect(updated.ip + ':' + updated.port).that.equals(randomPeer.ip + ':' + randomPeer.port); - expect(updated.height).that.equals(toUpdate.height); + expect(__peers).to.be.an('array').and.to.have.lengthOf(1); + expect(__peers[0]).to.have.property('string').equal(ipAndPortPeer.ip + ':' + ipAndPortPeer.port); done(); }); }); - it('should insert new peer if ip or port changed', function (done) { - var toUpdate = _.clone(randomPeer); - toUpdate.port += 1; - peers.update(toUpdate); + + it('should update existing peer', function (done) { + + peers.update(randomPeer); getPeers(function (err, __peers) { - expect(currentPeers.length + 1).that.equals(__peers.length); - currentPeers = __peers; - var inserted = __peers.find(function (p) { - return p.ip + ':' + p.port === toUpdate.ip + ':' + toUpdate.port; + expect(__peers[0]).to.have.property('height').equal(randomPeer.height); + var toUpdate = _.clone(randomPeer); + toUpdate.height += 1; + peers.update(toUpdate); + getPeers(function (err, __peers) { + expect(__peers[0]).to.have.property('height').equal(toUpdate.height); + done(); }); - expect(inserted).to.be.an('object'); - expect(inserted).not.to.be.empty; - expect(inserted.ip + ':' + inserted.port).that.equals(toUpdate.ip + ':' + toUpdate.port); + }); + + }); + + it('should not insert new peer if address changed but nonce is the same', function (done) { - toUpdate.ip = '40.40.40.41'; + peers.update(randomPeer); + + getPeers(function (err, __peers) { + expect(__peers[0]).to.have.property('string').equal(randomPeer.ip + ':' + randomPeer.port); + var toUpdate = _.clone(randomPeer); + toUpdate.port += 1; peers.update(toUpdate); getPeers(function (err, __peers) { - expect(currentPeers.length + 1).that.equals(__peers.length); - currentPeers = __peers; - var inserted = __peers.find(function (p) { - return p.ip + ':' + p.port === toUpdate.ip + ':' + toUpdate.port; - }); - expect(inserted).to.be.an('object'); - expect(inserted).not.to.be.empty; - expect(inserted.ip + ':' + inserted.port).that.equals(toUpdate.ip + ':' + toUpdate.port); + expect(__peers[0]).to.have.property('string').equal(randomPeer.ip + ':' + randomPeer.port); done(); }); }); + }); - var ipAndPortPeer = { - ip: '40.41.40.41', - port: 4000 - }; + it('should not insert new peer if address changed but nonce is the same', function (done) { - it('should insert new peer with only ip and port defined', function (done) { - peers.update(ipAndPortPeer); + peers.update(randomPeer); getPeers(function (err, __peers) { - expect(currentPeers.length + 1).that.equals(__peers.length); - currentPeers = __peers; - var inserted = __peers.find(function (p) { - return p.ip + ':' + p.port === ipAndPortPeer.ip + ':' + ipAndPortPeer.port; + expect(__peers[0]).to.have.property('string').equal(randomPeer.ip + ':' + randomPeer.port); + var toUpdate = _.clone(randomPeer); + toUpdate.port += 1; + peers.update(toUpdate); + getPeers(function (err, __peers) { + expect(__peers[0]).to.have.property('string').equal(randomPeer.ip + ':' + randomPeer.port); + done(); }); - expect(inserted).to.be.an('object'); - expect(inserted).not.to.be.empty; - expect(inserted.ip + ':' + inserted.port).that.equals(ipAndPortPeer.ip + ':' + ipAndPortPeer.port); - done(); }); }); - it('should update peer with only one property defined', function (done) { - peers.update(ipAndPortPeer); - getPeers(function (err, __peers) { - currentPeers = __peers; + it('should insert new peer if address and nonce changed', function (done) { - var almostEmptyPeer = _.clone(ipAndPortPeer); - almostEmptyPeer.height = 1; + peers.update(randomPeer); - peers.update(almostEmptyPeer); + getPeers(function (err, __peers) { + expect(__peers[0]).to.have.property('string').equal(randomPeer.ip + ':' + randomPeer.port); + var secondPeer = _.clone(randomPeer); + secondPeer.port += 1; + secondPeer.nonce = 'someDifferentNonce'; + peers.update(secondPeer); getPeers(function (err, __peers) { - expect(currentPeers.length).that.equals(__peers.length); - var inserted = __peers.find(function (p) { - return p.ip + ':' + p.port === ipAndPortPeer.ip + ':' + ipAndPortPeer.port; + expect(__peers).to.have.a.lengthOf(2); + var peersAddresses = __peers.map(function (p) { + return p.string; }); - expect(inserted).to.be.an('object'); - expect(inserted).not.to.be.empty; - expect(inserted.ip + ':' + inserted.port).that.equals(ipAndPortPeer.ip + ':' + ipAndPortPeer.port); - expect(inserted.height).that.equals(almostEmptyPeer.height); + expect(peersAddresses.indexOf(randomPeer.ip + ':' + randomPeer.port) !== -1).to.be.ok; + expect(peersAddresses.indexOf(secondPeer.ip + ':' + secondPeer.port) !== -1).to.be.ok; done(); }); }); @@ -161,24 +168,15 @@ describe('peers', function () { describe('remove', function () { - before(function (done) { + it('should remove added peer', function (done) { + peers.update(randomPeer); - done(); - }); - it('should remove added peer', function (done) { getPeers(function (err, __peers) { - currentPeers = __peers; - var peerToRemove = currentPeers.find(function (p) { - return p.ip + ':' + p.port === randomPeer.ip + ':' + randomPeer.port; - }); - expect(peerToRemove).to.be.an('object').and.not.to.be.empty; - expect(peerToRemove.state).that.equals(2); - - expect(peers.remove(peerToRemove.ip, peerToRemove.port)).to.be.ok; + expect(__peers).to.be.an('array').and.to.have.lengthOf(1); + expect(peers.remove(randomPeer)).to.be.ok; getPeers(function (err, __peers) { - expect(currentPeers.length - 1).that.equals(__peers.length); - currentPeers = __peers; + expect(__peers).to.be.an('array').and.to.have.lengthOf(0); done(); }); }); @@ -203,12 +201,6 @@ describe('peers', function () { expect(peers.acceptable([privatePeer])).that.is.an('array').and.to.be.empty; }); - it('should not accept peer with lisk-js-api os', function () { - var privatePeer = _.clone(randomPeer); - privatePeer.os = 'lisk-js-api'; - expect(peers.acceptable([privatePeer])).that.is.an('array').and.to.be.empty; - }); - it('should not accept peer with host\'s nonce', function () { var peer = _.clone(randomPeer); peer.nonce = NONCE; @@ -230,62 +222,37 @@ describe('peers', function () { }); }); - describe('ping', function () { - - it('should accept peer with public ip', function (done) { - sinon.stub(modules.transport, 'getFromPeer').callsArgWith(2, null, { - success: true, - peer: randomPeer, - body: { - success: true, height: randomPeer.height, peers: [randomPeer] - } - }); - - peers.ping(randomPeer, function (err, res) { - expect(modules.transport.getFromPeer.calledOnce).to.be.ok; - expect(modules.transport.getFromPeer.calledWith(randomPeer)).to.be.ok; - modules.transport.getFromPeer.restore(); - done(); - }); - }); - }); - - describe('onBlockchainReady', function () { - + describe('events', function () { before(function () { modules.transport.onBind(modules); - }); - it('should update peers during onBlockchainReady', function (done) { - sinon.stub(peers, 'discover').callsArgWith(0, null); - var config = require('../../config.json'); - var initialPeers = _.clone(config.peers.list); - if (initialPeers.length === 0) { - config.peers.list.push(randomPeer); - } - peers.onBlockchainReady(); - setTimeout(function () { - expect(peers.discover.calledOnce).to.be.ok; - peers.discover.restore(); - done(); - }, 100); - }); - }); + var testWampServer = new MasterWAMPServer({on: sinon.spy()}, {}); - describe('onPeersReady', function () { + var usedRPCEndpoints = { + status: function () {} + }; - before(function () { - modules.transport.onBind(modules); + sinon.stub(usedRPCEndpoints, 'status').callsArgWith(0, null, { + success: true, + broadhash: '123456789broadhash', + nethash: '123456789nethash' + }); + + testWampServer.registerRPCEndpoints(usedRPCEndpoints); + wsRPC.setServer(testWampServer); }); - it('should update peers during onBlockchainReady', function (done) { - sinon.stub(peers, 'discover').callsArgWith(0, null); - peers.onPeersReady(); - setTimeout(function () { - expect(peers.discover.calledOnce).to.be.ok; - peers.discover.restore(); - done(); - }, 100); + describe('onPeersReady', function () { + + it('should update peers during onBlockchainReady', function (done) { + sinon.stub(peers, 'discover').callsArgWith(0, null); + peers.onPeersReady(); + setTimeout(function () { + expect(peers.discover.called).to.be.ok; + peers.discover.restore(); + done(); + }, 500); + }); }); }); }); diff --git a/test/unit/modules/transactions.js b/test/unit/modules/transactions.js index 02ef06839b4..8eb75a79bce 100644 --- a/test/unit/modules/transactions.js +++ b/test/unit/modules/transactions.js @@ -8,6 +8,7 @@ var async = require('async'); var transactionTypes = require('../../../helpers/transactionTypes.js'); var constants = require('../../../helpers/constants.js'); +var ws = require('../../common/wsCommunication'); var modulesLoader = require('../../common/initModule').modulesLoader; var _ = require('lodash'); @@ -86,8 +87,15 @@ describe('transactions', function () { return transactionLogic; } + + function postTransaction (transaction, done) { + ws.call('postTransactions', { + transaction: transaction + }, done, true); + } + function postSignature (transaction, signature, done) { - node.post('/peer/signatures', { + ws.call('postSignatures', { signature: { transaction: transaction.id, signature: signature @@ -95,14 +103,6 @@ describe('transactions', function () { }, done); } - function addTransaction (transaction, done) { - node.post('/peer/transactions', { - transaction: transaction - }, function (err, res) { - done(err, res.body); - }); - } - before(function (done) { async.auto({ accountLogic: function (cb) { @@ -173,7 +173,7 @@ describe('transactions', function () { async.each([voteAccount, multiAccount1, multiAccount2, multiAccount3, delegateAccount, transferAccount, signatureAccount, inTansferAccount, dappAccount], function (account, eachCb) { var transferTrs = node.lisk.transaction.createTransaction(account.address, 100000000000, node.gAccount.password); - addTransaction(transferTrs, eachCb); + postTransaction(transferTrs, eachCb); }, function (err) { expect(err).to.not.exist; node.onNewBlock(done); @@ -196,15 +196,15 @@ describe('transactions', function () { async.auto({ [transactionTypes.SEND]: function (cb) { var transferTrs = node.lisk.transaction.createTransaction(node.gAccount.address, 112340000, transferAccount.password); - addTransaction(transferTrs, mergeResponseAndTransaction(transferTrs, cb)); + postTransaction(transferTrs, mergeResponseAndTransaction(transferTrs, cb)); }, [transactionTypes.SIGNATURE]: function (cb) { var signatureTrs = node.lisk.signature.createSignature(signatureAccount.password, signatureAccount.secondPassword); - addTransaction(signatureTrs, mergeResponseAndTransaction(signatureTrs, cb)); + postTransaction(signatureTrs, mergeResponseAndTransaction(signatureTrs, cb)); }, [transactionTypes.DELEGATE]: function (cb) { var delegateTrs = node.lisk.delegate.createDelegate(delegateAccount.password, delegateAccount.username); - addTransaction(delegateTrs, mergeResponseAndTransaction(delegateTrs, cb)); + postTransaction(delegateTrs, mergeResponseAndTransaction(delegateTrs, cb)); }, [transactionTypes.VOTE]: function (cb) { var votes = [ @@ -212,7 +212,7 @@ describe('transactions', function () { '+141b16ac8d5bd150f16b1caa08f689057ca4c4434445e56661831f4e671b7c0a' ]; var voteTrs = node.lisk.vote.createVote(voteAccount.password, votes); - addTransaction(voteTrs, mergeResponseAndTransaction(voteTrs, cb)); + postTransaction(voteTrs, mergeResponseAndTransaction(voteTrs, cb)); }, [transactionTypes.MULTI]: function (cb) { var lifetime = 1; @@ -223,7 +223,7 @@ describe('transactions', function () { ]; var multiTrs = node.lisk.multisignature.createMultisignature(multiAccount1.password, null, keysgroup, lifetime, min); - addTransaction(multiTrs, mergeResponseAndTransaction(multiTrs, function (err, res) { + postTransaction(multiTrs, mergeResponseAndTransaction(multiTrs, function (err, res) { var signature1 = node.lisk.multisignature.signTransaction(multiTrs, multiAccount2.password); var signature2 = node.lisk.multisignature.signTransaction(multiTrs, multiAccount3.password); async.each([signature1, signature2], function (signature, eachCb) { @@ -241,7 +241,7 @@ describe('transactions', function () { link: 'http://www.lisk.io/' + dappName + '.zip', }; var dappTrs = node.lisk.dapp.createDapp(dappAccount.password, null, options); - addTransaction(dappTrs, mergeResponseAndTransaction(dappTrs, function (err, res) { + postTransaction(dappTrs, mergeResponseAndTransaction(dappTrs, function (err, res) { node.onNewBlock(function (err1) { cb(err, res); }); diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000000..bcd4f613e91 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,14 @@ +'use strict'; + +var nodeExternals = require('webpack-node-externals'); + +module.exports = { + entry: './workersController.js', + output: { + path: __dirname + '/release', + filename: 'workersController.js', + libraryTarget: 'commonjs2' + }, + target: 'node', + externals: [nodeExternals()] +}; diff --git a/workersController.js b/workersController.js new file mode 100644 index 00000000000..f24b90a122e --- /dev/null +++ b/workersController.js @@ -0,0 +1,83 @@ +'use strict'; + +var async = require('async'); +var url = require('url'); + +var SlaveWAMPServer = require('wamp-socket-cluster/SlaveWAMPServer'); + +var Peer = require('./logic/peer'); +var System = require('./modules/system'); +var Handshake = require('./helpers/wsApi').middleware.Handshake; +var extractHeaders = require('./helpers/wsApi').extractHeaders; + +/** + * Function is invoked by SocketCluster + * @param {Worker} worker + */ +module.exports.run = function (worker) { + + var scServer = worker.getSCServer(); + + async.auto({ + slaveWAMPServer: function (cb) { + new SlaveWAMPServer(worker, cb); + }, + config: ['slaveWAMPServer', function (scope, cb) { + cb(null, scope.slaveWAMPServer.config); + }], + + system: ['config', function (scope, cb) { + new System(cb, {config: scope.config}); + }], + + handshake: ['system', function (scope, cb) { + var handshake = Handshake(scope.system); + + scServer.addMiddleware(scServer.MIDDLEWARE_HANDSHAKE, function (req, next) { + + try { + var headers = extractHeaders(req); + } catch (invalidHeadersException) { + return next(invalidHeadersException); + } + + handshake(headers, function (err, peer) { + scope.slaveWAMPServer.sendToMaster(err ? 'removePeer' : 'acceptPeer', peer, req.remoteAddress, function (onMasterError) { + next(err || onMasterError); + }); + }); + }); + return cb(null, handshake); + }] + }, + function (err, scope) { + scServer.on('connection', function (socket) { + scope.slaveWAMPServer.upgradeToWAMP(socket); + + socket.on('disconnect', function () { + scope.slaveWAMPServer.onSocketDisconnect(socket); + try { + var headers = extractHeaders(socket.request); + } catch (invalidHeadersException) { + //ToDO: do some unable to disconnect peer logging + return; + } + return scope.slaveWAMPServer.sendToMaster('removePeer', new Peer(headers), socket.request.remoteAddress, function (err, peer) { + if (err) { + //ToDo: Again logging here- unable to remove peer + } + }); + }.bind(this)); + + socket.on('connect', function (data) { + //ToDo: integrate this socket connection with future peer client connection - one socket will be sufficient + }); + + socket.on('error', function (err) { + //ToDo: Again logger here- log errors somewhere like err.message: 'Socket hung up' + }); + }); + }); +}; + +module.exports.path = __dirname + '/workersController.js';