diff --git a/build/tasks/eslint.js b/build/tasks/eslint.js index 74a87de4af..8e0905dc51 100644 --- a/build/tasks/eslint.js +++ b/build/tasks/eslint.js @@ -18,13 +18,14 @@ gulp.task('lint', function () { '!fabric-ca-client/node_modules/**', '!docs/**', '!coverage/**', - '!tmp/**' + '!tmp/**', ]) .pipe(eslint( { env: ['es6', 'node'], extends: 'eslint:recommended', parserOptions: { + ecmaVersion: 2017, sourceType: 'module' }, rules: { @@ -41,12 +42,13 @@ gulp.task('lint', function () { 'ignoreUrls': true, 'ignoreStrings': true, 'ignoreTemplateLiterals': true, - 'ignoreRegExpLiterals': true - } - ] - } + 'ignoreRegExpLiterals': true, + }, + ], + }, + fix: true, } )) .pipe(eslint.format()) .pipe(eslint.failAfterError()); -}); +}); \ No newline at end of file diff --git a/build/tasks/test.js b/build/tasks/test.js index 17c906086c..582d74622e 100644 --- a/build/tasks/test.js +++ b/build/tasks/test.js @@ -118,7 +118,10 @@ gulp.task('test', ['clean-up', 'lint', 'pre-test', 'docker-ready', 'ca'], functi 'test/integration/invoke.js', 'test/integration/perf/orderer.js', 'test/integration/perf/peer.js', - 'test/integration/network-config.js' + 'test/integration/network-config.js', + // channel: mychannel, chaincode: e2enodecc:v0 + 'test/integration/nodechaincode/e2e.js' + ])) .pipe(addsrc.append( 'test/unit/logger.js' // put this to the last so the debugging levels are not mixed up diff --git a/fabric-client/lib/BlockDecoder.js b/fabric-client/lib/BlockDecoder.js index 5ad04faa30..ac181e6289 100644 --- a/fabric-client/lib/BlockDecoder.js +++ b/fabric-client/lib/BlockDecoder.js @@ -825,7 +825,7 @@ function decodeConfigValue(proto_config_value) { var proto_chain_creation_policy_names = _ordererConfigurationProto.ChainCreationPolicyNames.decode(proto_config_value.value.value); var names = []; var proto_names = proto_chain_creation_policy_names.getNames(); - if(proto_names) for(var i in proto_names) { + if(proto_names) for(let i in proto_names) { names.push(proto_names[i]); //string } config_value.value.names = names; @@ -842,7 +842,7 @@ function decodeConfigValue(proto_config_value) { var orderer_addresses = _commonConfigurationProto.OrdererAddresses.decode(proto_config_value.value.value); var addresses = []; var proto_addresses = orderer_addresses.getAddresses(); - if(proto_addresses) for(var i in proto_addresses) { + if(proto_addresses) for(let i in proto_addresses) { addresses.push(proto_addresses[i]); //string } config_value.value.addresses = addresses; diff --git a/fabric-client/lib/Channel.js b/fabric-client/lib/Channel.js index 3b659e814a..0dd5b55c44 100755 --- a/fabric-client/lib/Channel.js +++ b/fabric-client/lib/Channel.js @@ -113,7 +113,7 @@ var Channel = class { //to do update logger logger.debug('Constructed Channel instance: name - %s, ' + - 'network mode: %s', + 'network mode: %s', this._name, !this._devMode); } @@ -1183,7 +1183,8 @@ var Channel = class { } let lcccSpec = { - type: _ccProto.ChaincodeSpec.Type.GOLANG, + // type: _ccProto.ChaincodeSpec.Type.GOLANG, + type: clientUtils.translateCCType(request.chaincodeType), chaincode_id: { name: Constants.LSCC }, input: { args : lcccSpec_args} }; @@ -1525,7 +1526,7 @@ var Channel = class { var proposal = results[1]; logger.debug('queryByChaincode - results received'); if(responses && Array.isArray(responses)) { - var results = []; + let results = []; for(let i = 0; i < responses.length; i++) { let response = responses[i]; if(response instanceof Error) { @@ -1569,7 +1570,7 @@ var Channel = class { * @returns {boolean} A boolean value of true when both the identity and * the signature are valid, false otherwise. */ - verifyProposalResponse(proposal_response) { + verifyProposalResponse(proposal_response) { logger.debug('verifyProposalResponse - start'); if(!proposal_response) { throw new Error('Missing proposal response'); @@ -1623,7 +1624,7 @@ var Channel = class { logger.debug('verifyProposalResponse - This endorsement has both a valid identity and valid signature'); return true; - } + } /** * Utility method to examine a set of proposals to check they contain diff --git a/fabric-client/lib/Client.js b/fabric-client/lib/Client.js index 3c784fa9b4..f65830495a 100644 --- a/fabric-client/lib/Client.js +++ b/fabric-client/lib/Client.js @@ -750,7 +750,7 @@ var Client = class extends BaseClient { * 'mycompany.com/myproject/mypackage/mychaincode', then the archive must contain a * folder 'src/mycompany.com/myproject/mypackage/mychaincode', where the * GO source code resides. - * @property {string} chaincodeType - Optional. Type of chaincode. One of 'golang', 'car' or 'java'. + * @property {string} chaincodeType - Optional. Type of chaincode. One of 'golang', 'car', 'node' or 'java'. * Default is 'golang'. Note that 'java' is not supported as of v1.0. */ @@ -841,7 +841,7 @@ var Client = class extends BaseClient { // TODO add ESCC/VSCC info here ?????? let lcccSpec = { - type: _ccProto.ChaincodeSpec.Type.GOLANG, + type: ccSpec.type, chaincode_id: { name: Constants.LSCC }, diff --git a/fabric-client/lib/Packager.js b/fabric-client/lib/Packager.js index 24997f39c1..f10211cafe 100644 --- a/fabric-client/lib/Packager.js +++ b/fabric-client/lib/Packager.js @@ -16,6 +16,7 @@ var Golang = require('./packager/Golang.js'); var Car = require('./packager/Car.js'); +var Node = require('./packager/Node.js'); var utils = require('./utils.js'); var logger = utils.getLogger('packager'); @@ -26,7 +27,7 @@ var logger = utils.getLogger('packager'); * @param {Object} chaincodePath required - String of the path to location of * the source code of the chaincode * @param {Object} chaincodeType optional - String of the type of chaincode - * ['golang', 'car', 'java'] (default 'golang') + * ['golang', 'node', 'car', 'java'] (default 'golang') * @param {boolean} devmode optional - True if using dev mode * @returns {Promise} A promise for the data as a byte array */ @@ -48,14 +49,17 @@ module.exports.package = function(chaincodePath, chaincodeType, devmode) { let handler; - switch (type) { + switch (type.toLowerCase()) { case 'car': - handler = Car.package; + handler = new Car(); + break; + case 'node': + handler = new Node(); break; default: - handler = Golang.package; + handler = new Golang(); } - return resolve(handler(chaincodePath)); + return resolve(handler.package(chaincodePath)); }); }; diff --git a/fabric-client/lib/client-utils.js b/fabric-client/lib/client-utils.js index f3f3adbbfe..65deebcd53 100644 --- a/fabric-client/lib/client-utils.js +++ b/fabric-client/lib/client-utils.js @@ -1,4 +1,4 @@ - /** +/** * Copyright 2016 IBM All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,14 +33,16 @@ var Orderer = require('./Orderer.js'); var grpc = require('grpc'); var _commonProto = grpc.load(__dirname + '/protos/common/common.proto').common; -var _proposalProto = grpc.load(__dirname + '/protos/peer/proposal.proto').protos; +var _proposalProto = grpc.load(__dirname + + '/protos/peer/proposal.proto').protos; var _ccProto = grpc.load(__dirname + '/protos/peer/chaincode.proto').protos; -var _timestampProto = grpc.load(__dirname + '/protos/google/protobuf/timestamp.proto').google.protobuf; +var _timestampProto = grpc.load(__dirname + + '/protos/google/protobuf/timestamp.proto').google.protobuf; /* * This function will build the proposal */ -module.exports.buildProposal = function(invokeSpec, header, transientMap) { +module.exports.buildProposal = function (invokeSpec, header, transientMap) { // construct the ChaincodeInvocationSpec let cciSpec = new _ccProto.ChaincodeInvocationSpec(); cciSpec.setChaincodeSpec(invokeSpec); @@ -49,7 +51,7 @@ module.exports.buildProposal = function(invokeSpec, header, transientMap) { cc_payload.setInput(cciSpec.toBuffer()); if (typeof transientMap === 'object') { - logger.debug('buildProposal - adding in transientMap %j',transientMap); + logger.debug('buildProposal - adding in transientMap %j', transientMap); cc_payload.setTransientMap(transientMap); } else { @@ -67,21 +69,21 @@ module.exports.buildProposal = function(invokeSpec, header, transientMap) { /* * This function will return one Promise when sending a proposal to many peers */ -module.exports.sendPeersProposal = function(peers, proposal, timeout) { - if(!Array.isArray(peers)) { +module.exports.sendPeersProposal = function (peers, proposal, timeout) { + if (!Array.isArray(peers)) { peers = [peers]; } // make function to return an individual promise - var fn = function(peer) { - return new Promise(function(resolve,reject) { - peer.sendProposal(proposal, timeout) - .then( - function(result) { + var fn = function (peer) { + return new Promise(function (resolve, reject) { + peer.sendProposal(proposal, timeout).then( + function (result) { resolve(result); } ).catch( - function(err) { - logger.error('sendPeersProposal - Promise is rejected: %s',err.stack ? err.stack : err); + function (err) { + logger.error('sendPeersProposal - Promise is rejected: %s', + err.stack ? err.stack : err); return reject(err); } ); @@ -91,21 +93,22 @@ module.exports.sendPeersProposal = function(peers, proposal, timeout) { // settle all the promises and return array of responses var promises = peers.map(fn); var responses = []; - return settle(promises) - .then(function (results) { + return settle(promises).then(function (results) { results.forEach(function (result) { - if (result.isFulfilled()) { - logger.debug('sendPeersProposal - Promise is fulfilled: '+result.value()); - responses.push(result.value()); - } else { - logger.debug('sendPeersProposal - Promise is rejected: '+result.reason()); - if(result.reason() instanceof Error) { - responses.push(result.reason()); - } - else { - responses.push(new Error(result.reason())); + if (result.isFulfilled()) { + logger.debug('sendPeersProposal - Promise is fulfilled: ' + + result.value()); + responses.push(result.value()); + } else { + logger.debug('sendPeersProposal - Promise is rejected: ' + + result.reason()); + if (result.reason() instanceof Error) { + responses.push(result.reason()); + } + else { + responses.push(new Error(result.reason())); + } } - } }); return responses; }); @@ -114,7 +117,7 @@ module.exports.sendPeersProposal = function(peers, proposal, timeout) { /* * This function will sign the proposal */ -module.exports.signProposal = function(signingIdentity, proposal) { +module.exports.signProposal = function (signingIdentity, proposal) { let proposal_bytes = proposal.toBuffer(); // sign the proposal let sig = signingIdentity.sign(proposal_bytes); @@ -122,8 +125,8 @@ module.exports.signProposal = function(signingIdentity, proposal) { // build manually for now let signedProposal = { - signature : signature, - proposal_bytes : proposal_bytes + signature: signature, + proposal_bytes: proposal_bytes }; return signedProposal; }; @@ -131,22 +134,24 @@ module.exports.signProposal = function(signingIdentity, proposal) { /* * This function will build a common channel header */ -module.exports.buildChannelHeader = function(type, channel_id, tx_id, epoch, chaincode_id, time_stamp) { - logger.debug('buildChannelHeader - type %s channel_id %s tx_id %d epoch % chaincode_id %s', - type, channel_id, tx_id, epoch, chaincode_id); +module.exports.buildChannelHeader = function ( + type, channel_id, tx_id, epoch, chaincode_id, time_stamp) { + logger.debug( + 'buildChannelHeader - type %s channel_id %s tx_id %d epoch % chaincode_id %s', + type, channel_id, tx_id, epoch, chaincode_id); var channelHeader = new _commonProto.ChannelHeader(); channelHeader.setType(type); // int32 channelHeader.setVersion(1); // int32 - if(!time_stamp) { + if (!time_stamp) { time_stamp = module.exports.buildCurrentTimestamp(); } channelHeader.setTimestamp(time_stamp); // google.protobuf.Timestamp channelHeader.setChannelId(channel_id); //string channelHeader.setTxId(tx_id.toString()); //string - if(epoch) { + if (epoch) { channelHeader.setEpoch(epoch); // uint64 } - if(chaincode_id) { + if (chaincode_id) { let chaincodeID = new _ccProto.ChaincodeID(); chaincodeID.setName(chaincode_id); @@ -161,7 +166,7 @@ module.exports.buildChannelHeader = function(type, channel_id, tx_id, epoch, cha /* * This function will build the common header */ -module.exports.buildHeader = function(creator, channelHeader, nonce) { +module.exports.buildHeader = function (creator, channelHeader, nonce) { let signatureHeader = new _commonProto.SignatureHeader(); signatureHeader.setCreator(creator.serialize()); signatureHeader.setNonce(nonce); @@ -173,13 +178,13 @@ module.exports.buildHeader = function(creator, channelHeader, nonce) { return header; }; -module.exports.checkProposalRequest = function(request, skip) { +module.exports.checkProposalRequest = function (request, skip) { var errorMsg = null; - if(request) { - if(!request.chaincodeId) { + if (request) { + if (!request.chaincodeId) { errorMsg = 'Missing "chaincodeId" parameter in the proposal request'; - } else if(!request.txId && !skip) { + } else if (!request.txId && !skip) { errorMsg = 'Missing "txId" parameter in the proposal request'; } } else { @@ -188,11 +193,11 @@ module.exports.checkProposalRequest = function(request, skip) { return errorMsg; }; -module.exports.checkInstallRequest = function(request) { +module.exports.checkInstallRequest = function (request) { var errorMsg = null; if (request) { - if(!request.chaincodeVersion) { + if (!request.chaincodeVersion) { errorMsg = 'Missing "chaincodeVersion" parameter in the proposal request'; } } else { @@ -201,8 +206,10 @@ module.exports.checkInstallRequest = function(request) { return errorMsg; }; -module.exports.translateCCType = function(type) { - switch (type) { +module.exports.translateCCType = function (type) { + let chaincodeType = type ? type : 'golang'; + + switch (chaincodeType.toLowerCase()) { case 'golang': default: return _ccProto.ChaincodeSpec.Type.GOLANG; @@ -210,13 +217,15 @@ module.exports.translateCCType = function(type) { return _ccProto.ChaincodeSpec.Type.CAR; case 'java': return _ccProto.ChaincodeSpec.Type.JAVA; + case 'node': + return _ccProto.ChaincodeSpec.Type.NODE; } }; /* * This function will create a timestamp from the current time */ -module.exports.buildCurrentTimestamp = function() { +module.exports.buildCurrentTimestamp = function () { var now = new Date(); var timestamp = new _timestampProto.Timestamp(); timestamp.setSeconds(now.getTime() / 1000); diff --git a/fabric-client/lib/packager/BasePackager.js b/fabric-client/lib/packager/BasePackager.js new file mode 100644 index 0000000000..064d9cca43 --- /dev/null +++ b/fabric-client/lib/packager/BasePackager.js @@ -0,0 +1,143 @@ +/* + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an 'AS IS' BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +'use strict'; + +var fs = require('fs-extra'); +var tar = require('tar-stream'); +var path = require('path'); +var zlib = require('zlib'); + +var BasePackager = class { + constructor (keep) { + if (this.constructor === BasePackager) { + // BasePackager can not be constructed. + throw new TypeError('Can not construct abstract class.'); + } + if (this.package === BasePackager.prototype.package) { + throw new TypeError('Please implement method package from child class'); + } + + this.keep = keep; + } + + /** + * All of the files in the directory of request.chaincodePath will be + * included in an archive file. + * + * @param chaincodePath + */ + package (chaincodePath) { + throw new TypeError('Please implement method package from child class'); + } + + /** + * Given an input 'filePath', recursively parse the filesystem for any files + * that fit the criteria for being valid chaincode source (ISREG + keep) + * + * @param filepath + */ + findSource (filepath) { + throw new Error('abstract function called'); + } + + /** + * Predicate function for determining whether a given path should be + * considered valid source code, based entirely on the extension. It is + * assumed that other checks for file type (e.g. ISREG) have already been + * performed. + * @param filePath + * @returns {boolean} + */ + isSource (filePath) { + return (this.keep.indexOf(path.extname(filePath)) != -1); + } + + /** + * Given an {fqp, name} tuple, generate a tar entry complete with sensible + * header and populated contents read from the filesystem. + * + * @param pack + * @param desc + * @returns {Promise} + */ + packEntry (pack, desc) { + return new Promise((resolve, reject) => { + // Use a synchronous read to reduce non-determinism + var content = fs.readFileSync(desc.fqp); + if (!content) { + reject(new Error('failed to read ' + desc.fqp)); + } else { + // Use a deterministic "zero-time" for all date fields + var zeroTime = new Date(0); + var header = { + name: desc.name, + size: content.size, + mode: 0o100644, + atime: zeroTime, + mtime: zeroTime, + ctime: zeroTime + }; + + pack.entry(header, content, (err) => { + if (err) + reject(err); + else + resolve(true); + }); + } + }); + } + + /** + * Creates an .tar.gz stream from the provided descriptor entries + * + * @param descriptors + * @param dest + * @returns {Promise} + */ + generateTarGz (descriptors, dest) { + return new Promise((resolve, reject) => { + var pack = tar.pack(); + + // Setup the pipeline to compress on the fly and resolve/reject the promise + pack.pipe(zlib.createGzip()).pipe(dest).on('finish', () => { + resolve(true); + }).on('error', (err) => { + reject(err); + }); + + // Iterate through each descriptor in the order it was provided and resolve + // the entry asynchronously. We will gather results below before + // finalizing the tarball + var tasks = []; + for (let desc of descriptors) { + var task = this.packEntry(pack, desc); + tasks.push(task); + } + + // Block here until all entries have been gathered, and then finalize the + // tarball. This should result in a flush of the entire pipeline before + // resolving the top-level promise. + Promise.all(tasks).then(() => { + pack.finalize(); + }).catch((err) => { + reject(err); + }); + }); + } + +}; + +module.exports = BasePackager; \ No newline at end of file diff --git a/fabric-client/lib/packager/Car.js b/fabric-client/lib/packager/Car.js index f14567923d..dc246c7630 100644 --- a/fabric-client/lib/packager/Car.js +++ b/fabric-client/lib/packager/Car.js @@ -18,7 +18,11 @@ var utils = require('../utils.js'); var logger = utils.getLogger('packager/Car.js'); -module.exports.package = function(path) { - logger.info('Packaging CAR file from %s', path); - return utils.readFile(path); -}; +class Car { + package (path) { + logger.info('Packaging CAR file from %s', path); + return utils.readFile(path); + }; +} + +module.exports = Car; \ No newline at end of file diff --git a/fabric-client/lib/packager/Golang.js b/fabric-client/lib/packager/Golang.js index 40fcc86897..b3bab3157b 100644 --- a/fabric-client/lib/packager/Golang.js +++ b/fabric-client/lib/packager/Golang.js @@ -14,7 +14,6 @@ 'use strict'; -var os = require('os'); var fs = require('fs-extra'); var klaw = require('klaw'); var tar = require('tar-stream'); @@ -22,6 +21,7 @@ var path = require('path'); var zlib = require('zlib'); var sbuf = require('stream-buffers'); var utils = require('../utils.js'); +var BasePackager = require('./BasePackager'); var logger = utils.getLogger('packager/Golang.js'); @@ -34,65 +34,57 @@ var keep = [ '.h' ]; -// ------------------------------------------------------------------------- -// package(path) -// ------------------------------------------------------------------------- -// All of the files in the directory of the environment variable -// GOPATH joined to the request.chaincodePath will be included -// in an archive file. -// ------------------------------------------------------------------------- -module.exports.package = function(chaincodePath) { - logger.info('packaging GOLANG from %s', chaincodePath); - - // Determine the user's $GOPATH - let goPath = process.env['GOPATH']; - - // Compose the path to the chaincode project directory - let projDir = path.join(goPath, 'src', chaincodePath); - - // We generate the tar in two phases: First grab a list of descriptors, - // and then pack them into an archive. While the two phases aren't - // strictly necessary yet, they pave the way for the future where we - // will need to assemble sources from multiple packages - - var buffer = new sbuf.WritableStreamBuffer(); - - return findSource(goPath, projDir) - .then((descriptors) => { - return generateTarGz(descriptors, buffer); - }) - .then(() => { - return buffer.getContents();; - }); -}; - -// ------------------------------------------------------------------------- -// isSource(path) -// ------------------------------------------------------------------------- -// predicate function for determining whether a given path should be -// considered valid source code, based entirely on the extension. It is -// assumed that other checks for file type (e.g. ISREG) have already been -// performed. -// ------------------------------------------------------------------------- -function isSource(filePath) { - return (keep.indexOf(path.extname(filePath)) != -1); -} +class GolangPackager extends BasePackager { + + constructor () { + super(keep); + } + + /** + * All of the files in the directory of the environment variable + * GOPATH joined to the request.chaincodePath will be included + * in an archive file. + * @param chaincodePath + * @returns {Promise.} + */ + package (chaincodePath) { + logger.info('packaging GOLANG from %s', chaincodePath); -// ------------------------------------------------------------------------- -// findSource(goPath, filePath) -// ------------------------------------------------------------------------- -// Given an input 'filePath', recursively parse the filesystem for any files -// that fit the criteria for being valid golang source (ISREG + (*.(go|c|h))) -// As a convenience, we also formulate a tar-friendly "name" for each file -// based on relative position to 'goPath'. -// ------------------------------------------------------------------------- -function findSource(goPath, filePath) { - return new Promise((resolve, reject) => { - var descriptors = []; - klaw(filePath) - .on('data', (entry) => { - - if (entry.stats.isFile() && isSource(entry.path)) { + // Determine the user's $GOPATH + let goPath = process.env['GOPATH']; + + // Compose the path to the chaincode project directory + let projDir = path.join(goPath, 'src', chaincodePath); + + // We generate the tar in two phases: First grab a list of descriptors, + // and then pack them into an archive. While the two phases aren't + // strictly necessary yet, they pave the way for the future where we + // will need to assemble sources from multiple packages + + var buffer = new sbuf.WritableStreamBuffer(); + + return this.findSource(goPath, projDir).then((descriptors) => { + return super.generateTarGz(descriptors, buffer); + }).then(() => { + return buffer.getContents(); + }); + }; + + /** + * Given an input 'filePath', recursively parse the filesystem for any files + * that fit the criteria for being valid golang source (ISREG + (*.(go|c|h))) + * As a convenience, we also formulate a tar-friendly "name" for each file + * based on relative position to 'goPath'. + * @param goPath + * @param filePath + * @returns {Promise} + */ + findSource (goPath, filePath) { + return new Promise((resolve, reject) => { + var descriptors = []; + klaw(filePath).on('data', (entry) => { + + if (entry.stats.isFile() && super.isSource(entry.path)) { var desc = { name: path.relative(goPath, entry.path).split('\\').join('/'), // for windows style paths @@ -103,84 +95,13 @@ function findSource(goPath, filePath) { descriptors.push(desc); } - }) - .on('end', () => { + }).on('end', () => { resolve(descriptors); }); - }); + }); + } } -// ------------------------------------------------------------------------- -// packEntry(pack, desc) -// ------------------------------------------------------------------------- -// Given an {fqp, name} tuple, generate a tar entry complete with sensible -// header and populated contents read from the filesystem. -// ------------------------------------------------------------------------- -function packEntry(pack, desc) { - return new Promise((resolve, reject) => { - // Use a synchronous read to reduce non-determinism - var content = fs.readFileSync(desc.fqp); - if (!content) { - reject(new Error('failed to read ' + desc.fqp)); - } else { - // Use a deterministic "zero-time" for all date fields - var zeroTime = new Date(0); - var header = {name: desc.name, - size: content.size, - mode: 0o100644, - atime: zeroTime, - mtime: zeroTime, - ctime: zeroTime - }; - - pack.entry(header, content, (err) => { - if (err) - reject(err); - else - resolve(true); - }); - } - }); -} +module.exports = GolangPackager; -// ------------------------------------------------------------------------- -// generateTarGz(descriptors, dest) -// ------------------------------------------------------------------------- -// creates an .tar.gz stream from the provided descriptor entries -// ------------------------------------------------------------------------- -function generateTarGz(descriptors, dest) { - return new Promise((resolve, reject) => { - var pack = tar.pack(); - - // Setup the pipeline to compress on the fly and resolve/reject the promise - pack - .pipe(zlib.createGzip()) - .pipe(dest) - .on('finish', () => { - resolve(true); - }) - .on('error', (err) => { - reject(err); - }); - // Iterate through each descriptor in the order it was provided and resolve - // the entry asynchronously. We will gather results below before - // finalizing the tarball - var tasks = []; - for (let desc of descriptors) { - var task = packEntry(pack, desc); - tasks.push(task); - } - - // Block here until all entries have been gathered, and then finalize the - // tarball. This should result in a flush of the entire pipeline before - // resolving the top-level promise. - Promise.all(tasks) - .then(() => { - pack.finalize(); - }) - .catch((err) => { - reject(err); - }); - }); -} diff --git a/fabric-client/lib/packager/Node.js b/fabric-client/lib/packager/Node.js new file mode 100644 index 0000000000..349ad2e9ff --- /dev/null +++ b/fabric-client/lib/packager/Node.js @@ -0,0 +1,100 @@ +/* + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an 'AS IS' BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +'use strict'; + +var klaw = require('klaw'); +var path = require('path'); +var sbuf = require('stream-buffers'); +var utils = require('../utils.js'); + +var logger = utils.getLogger('packager/Node.js'); + +var BasePackager = require('./BasePackager'); + +// A list of file extensions that should be packaged into the .tar.gz. +// Files with all other file extenstions will be excluded to minimize the size +// of the install payload. +var keep = [ + '.js', + '.json', + '.proto', + '.yaml', + '.yml', +]; + +class NodePackager extends BasePackager { + + constructor () { + super(keep); + } + + /** + * All of the files in the directory of request.chaincodePath will be + * included in an archive file. + * @param chaincodePath + * @returns {Promise.} + */ + package (chaincodePath) { + logger.info('packaging Node from %s', chaincodePath); + + // Compose the path to the chaincode project directory + let projDir = chaincodePath; + + // We generate the tar in two phases: First grab a list of descriptors, + // and then pack them into an archive. While the two phases aren't + // strictly necessary yet, they pave the way for the future where we + // will need to assemble sources from multiple packages + + var buffer = new sbuf.WritableStreamBuffer(); + + return this.findSource(projDir).then((descriptors) => { + return super.generateTarGz(descriptors, buffer); + }).then(() => { + return buffer.getContents(); + }); + } + + /** + * Given an input 'filePath', recursively parse the filesystem for any files + * that fit the criteria for being valid node chaincode source + * + * @param filePath + * @returns {Promise} + */ + findSource (filePath) { + return new Promise((resolve, reject) => { + var descriptors = []; + klaw(filePath).on('data', (entry) => { + + if (entry.stats.isFile() && super.isSource(entry.path)) { + + // TOOD: remove 'src' + var desc = { + name: 'src/' + path.relative(filePath, entry.path).split('\\').join('/'), // for windows style paths + fqp: entry.path, + }; + + logger.debug('adding entry', desc); + descriptors.push(desc); + } + + }).on('end', () => { + resolve(descriptors); + }); + }); + } +} + +module.exports = NodePackager; \ No newline at end of file diff --git a/test/fixtures/src/node_cc/example_cc/chaincode.js b/test/fixtures/src/node_cc/example_cc/chaincode.js new file mode 100644 index 0000000000..aaf438dbd4 --- /dev/null +++ b/test/fixtures/src/node_cc/example_cc/chaincode.js @@ -0,0 +1,183 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This is a node-js version of example_02.go + +const shim = require('fabric-shim'); + +// An log4js logger instance +var logger = shim.newLogger('example_cc0'); +// The logger level can also be set by environment variable 'CORE_CHAINCODE_LOGGING_SHIM' +// to CRITICAL, ERROR, WARNING, DEBUG +logger.level = 'info'; + +var Chaincode = class { + async Init(stub) { + logger.info('########### example_cc0 Init ###########'); + let ret = stub.getFunctionAndParameters(); + + let A, B; // Entities + let Aval, Bval; // Asset holdings + let args = ret.params; + + if (args.length === 4) { + A = args[0]; + B = args[2]; + + Aval = parseInt(args[1]); + if (isNaN(Aval)) { + return shim.error('Expecting integer value for asset holding'); + } + Bval = parseInt(args[3]); + if (isNaN(Bval)) { + return shim.error('Expecting integer value for asset holding'); + } + + logger.info(`Aval = ${Aval}, Bval = ${Bval}`); + + try { + // Write the state to the ledger + await stub.putState(A, Buffer.from(Aval.toString())); + await stub.putState(B, Buffer.from(Bval.toString())); + return shim.success(); + } catch (e) { + return shim.error(e); + } + } else { + return shim.error('init expects 4 args'); + } + } + + async Invoke(stub) { + logger.info('########### example_cc0 Invoke ###########'); + let ret = stub.getFunctionAndParameters(); + let fcn = ret.fcn; + let args = ret.params; + + if (fcn === 'delete') { + return this.delete(stub, args); + } + + if (fcn === 'query') { + return this.query(stub, args); + } + + if (fcn === 'move') { + return this.move(stub, args); + } + + logger.Errorf(`Unknown action, check the first argument, must be one of 'delete', 'query', or 'move'. But got: ${fcn}`); + return shim.error(`Unknown action, check the first argument, must be one of 'delete', 'query', or 'move'. But got: ${fcn}`); + } + + async move(stub, args) { + let A, B; + let Aval, Bval; + let X; + + if (args.length != 3) { + return shim.error('Incorrect number of arguments. Expecting 4, function followed by 2 names and 1 value'); + } + + A = args[0]; + B = args[1]; + + try { + let Avalbytes = await stub.getState(A); + if (!Avalbytes) { + return shim.error('Entity A not found'); + } + Aval = Avalbytes.toString(); + Aval = parseInt(Aval); + } catch (e) { + return shim.error('Failed to get state A'); + } + + try { + let Bvalbytes = await stub.getState(B); + if (!Bvalbytes) { + return shim.error('Entity B not found'); + } + Bval = Bvalbytes.toString(); + Bval = parseInt(Bval); + } catch (e) { + return shim.error('Failed to get state B'); + } + // Perform the execution + X = parseInt(args[2]); + if (isNaN(X)) { + return shim.error('Invalid transaction amount, expecting a integer value'); + } + + Aval = Aval - X; + Bval = Bval + X; + logger.info(`Aval = ${Aval}, Bval = ${Bval}`); + // Write the state back to the ledger + try { + await stub.putState(A, Buffer.from(Aval.toString())); + await stub.putState(B, Buffer.from(Bval.toString())); + return shim.success(); + } catch (e) { + return shim.error(e); + } + + } + + async delete(stub, args) { + if (args.length != 1) { + return shim.error('Incorrect number of arguments. Expecting 1'); + } + + let A = args[0]; + + try { + await stub.deleteState(A); + } catch (e) { + return shim.error('Failed to delete state'); + } + + return shim.success(); + } + + async query(stub, args) { + if (args.length != 1) { + return shim.error('Incorrect number of arguments. Expecting name of the person to query'); + } + + let A = args[0]; + let Aval; + // Get the state from the ledger + try { + let Avalbytes = await stub.getState(A); + if (!Avalbytes) { + return shim.error('Entity A not found'); + } + Aval = Avalbytes.toString(); + } catch (e) { + return shim.error('Failed to get state A'); + } + + let jsonResp = { + Name: A, + Amount: Aval + }; + logger.info('Query Response:%s\n', JSON.stringify(jsonResp)); + + return shim.success(Buffer.from(Aval.toString())); + } +}; + +shim.start(new Chaincode()); \ No newline at end of file diff --git a/test/fixtures/src/node_cc/example_cc/package.json b/test/fixtures/src/node_cc/example_cc/package.json new file mode 100644 index 0000000000..4fdc3f59ec --- /dev/null +++ b/test/fixtures/src/node_cc/example_cc/package.json @@ -0,0 +1,15 @@ +{ + "name": "example_cc", + "version": "1.0.0", + "description": "", + "main": "chaincode.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node chaincode.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "fabric-shim": "unstable" + } +} diff --git a/test/fixtures/src/node_cc/example_cc1/chaincode.js b/test/fixtures/src/node_cc/example_cc1/chaincode.js new file mode 100644 index 0000000000..d097b69988 --- /dev/null +++ b/test/fixtures/src/node_cc/example_cc1/chaincode.js @@ -0,0 +1,189 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This is a node-js version of example_02.go + +const shim = require('fabric-shim'); + +// An log4js logger instance +var logger = shim.newLogger('example_cc1'); +// The logger level can also be set by environment variable 'CORE_CHAINCODE_LOGGING_SHIM' +// to CRITICAL, ERROR, WARNING, DEBUG +logger.level = 'info'; + +var Chaincode = class { + async Init(stub) { + + logger.info('########### example_cc1 Init ###########'); + // test the transient map support with chaincode instantiation + return this.testTransient(stub); + } + + async Invoke(stub) { + logger.info('########### example_cc0 Invoke ###########'); + let ret = stub.getFunctionAndParameters(); + let fcn = ret.fcn; + let args = ret.params; + + if (fcn === 'delete') { + return this.delete(stub, args); + } + + if (fcn === 'query') { + return this.query(stub, args); + } + + if (fcn === 'move') { + return this.move(stub, args); + } + if (fcn == 'echo') { + return this.echo(stub, args); + } + if (fcn == 'testTransient') { + return this.testTransient(stub); + } + + logger.Errorf(`Unknown action, check the first argument, must be one of 'delete', 'query', or 'move'. But got: ${fcn}`); + return shim.error(`Unknown action, check the first argument, must be one of 'delete', 'query', or 'move'. But got: ${fcn}`); + } + + async move(stub, args) { + let A, B; + let Aval, Bval; + let X; + + if (args.length != 3) { + return shim.error('Incorrect number of arguments. Expecting 4, function followed by 2 names and 1 value'); + } + + A = args[0]; + B = args[1]; + + try { + let Avalbytes = await stub.getState(A); + if (!Avalbytes) { + return shim.error('Entity A not found'); + } + Aval = Avalbytes.toString(); + Aval = parseInt(Aval); + } catch (e) { + return shim.error('Failed to get state A'); + } + + try { + let Bvalbytes = await stub.getState(B); + if (!Bvalbytes) { + return shim.error('Entity B not found'); + } + Bval = Bvalbytes.toString(); + Bval = parseInt(Bval); + } catch (e) { + return shim.error('Failed to get state B'); + } + // Perform the execution + X = parseInt(args[2]); + if (isNaN(X)) { + return shim.error('Invalid transaction amount, expecting a integer value'); + } + + Aval = Aval - X; + Bval = Bval + X + 10; + logger.info(`Aval = ${Aval}, Bval = ${Bval}`); + // Write the state back to the ledger + try { + await stub.putState(A, Buffer.from(Aval.toString())); + await stub.putState(B, Buffer.from(Bval.toString())); + return shim.success(); + } catch (e) { + return shim.error(e); + } + + } + + async delete(stub, args) { + if (args.length != 1) { + return shim.error('Incorrect number of arguments. Expecting 1'); + } + + let A = args[0]; + + try { + await stub.deleteState(A); + } catch (e) { + return shim.error('Failed to delete state'); + } + + return shim.success(); + } + + async query(stub, args) { + if (args.length != 1) { + return shim.error('Incorrect number of arguments. Expecting name of the person to query'); + } + + let A = args[0]; + let Aval; + // Get the state from the ledger + try { + let Avalbytes = await stub.getState(A); + if (!Avalbytes) { + return shim.error('Entity A not found'); + } + Aval = Avalbytes.toString(); + } catch (e) { + return shim.error('Failed to get state A'); + } + + let jsonResp = { + Name: A, + Amount: Aval + }; + logger.info('Query Response:%s\n', JSON.stringify(jsonResp)); + + return shim.success(Buffer.from(Aval.toString())); + } + + async testTransient(stub) { + let tm; + + try { + tm = stub.getTransient(); + } catch (e) { + logger.error('Did not find expected transient map in the proposal'); + return shim.error(Buffer.from('{"Error":"Did not find expected transient map in the proposal}')); + } + + let v = tm.map.test; + + if (!v) { + logger.error('Did not find expected key "test" in the transient map of the proposal'); + return shim.error(Buffer.from('{"Error":"Did not find expected key "test" in the transient map of the proposal}')); + } + + return shim.success(v.value); + } + + /* + * Used to return what's in the input for testing purposes + * */ + async echo(stub, args) { + logger.info('Echo Response\n'); + + return shim.success(Buffer.from(args[0])); + } +}; + +shim.start(new Chaincode()); \ No newline at end of file diff --git a/test/fixtures/src/node_cc/example_cc1/package.json b/test/fixtures/src/node_cc/example_cc1/package.json new file mode 100644 index 0000000000..673084313a --- /dev/null +++ b/test/fixtures/src/node_cc/example_cc1/package.json @@ -0,0 +1,15 @@ +{ + "name": "example_cc1", + "version": "1.0.0", + "description": "", + "main": "chaincode.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node chaincode.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "fabric-shim": "unstable" + } +} \ No newline at end of file diff --git a/test/integration/e2e/e2eUtils.js b/test/integration/e2e/e2eUtils.js index dce1c9d320..6219bdeca8 100644 --- a/test/integration/e2e/e2eUtils.js +++ b/test/integration/e2e/e2eUtils.js @@ -32,6 +32,7 @@ var Client = require('fabric-client'); var testUtil = require('../../unit/util.js'); var e2e = testUtil.END2END; +var e2e_node = testUtil.NODE_END2END; var ORGS; var grpc = require('grpc'); @@ -46,7 +47,7 @@ function init() { } } -function installChaincode(org, chaincode_path, version, t, get_admin) { +function installChaincode(org, chaincode_path, version, language, t, get_admin) { init(); Client.setConfigSetting('request-timeout', 60000); var channel_name = Client.getConfigSetting('E2E_CONFIGTX_CHANNEL_NAME', testUtil.END2END.channel); @@ -107,11 +108,19 @@ function installChaincode(org, chaincode_path, version, t, get_admin) { t.pass('Successfully enrolled user \'admin\''); the_user = admin; + let cc_id; + if(language && language==='node'){ + cc_id = e2e_node.chaincodeId; + }else{ + cc_id = e2e.chaincodeId; + } + // send proposal to endorser var request = { targets: targets, chaincodePath: chaincode_path, - chaincodeId: e2e.chaincodeId, + chaincodeId: cc_id, + chaincodeType: language, chaincodeVersion: version }; @@ -152,7 +161,7 @@ function installChaincode(org, chaincode_path, version, t, get_admin) { module.exports.installChaincode = installChaincode; -function instantiateChaincode(userOrg, chaincode_path, version, upgrade, t){ +function instantiateChaincode(userOrg, chaincode_path, version, language, upgrade, t){ init(); var channel_name = Client.getConfigSetting('E2E_CONFIGTX_CHANNEL_NAME', testUtil.END2END.channel); @@ -198,7 +207,7 @@ function instantiateChaincode(userOrg, chaincode_path, version, upgrade, t){ ) ); - var targets = []; + targets = []; var badTransientMap = { 'test1': 'transientValue' }; // have a different key than what the chaincode example_cc1.go expects in Init() var transientMap = { 'test': 'transientValue' }; @@ -260,7 +269,7 @@ function instantiateChaincode(userOrg, chaincode_path, version, upgrade, t){ // the v1 chaincode has Init() method that expects a transient map if (upgrade) { // first test that a bad transient map would get the chaincode to return an error - let request = buildChaincodeProposal(client, the_user, chaincode_path, version, upgrade, badTransientMap); + let request = buildChaincodeProposal(client, the_user, chaincode_path, version, language, upgrade, badTransientMap); tx_id = request.txId; logger.debug(util.format( @@ -276,39 +285,39 @@ function instantiateChaincode(userOrg, chaincode_path, version, upgrade, t){ // this is the longest response delay in the test, sometimes // x86 CI times out. set the per-request timeout to a super-long value return channel.sendUpgradeProposal(request, 120000) - .then((results) => { - let proposalResponses = results[0]; + .then((results) => { + let proposalResponses = results[0]; - if (version === 'v1') { + if (version === 'v1') { // expecting both peers to return an Error due to the bad transient map - let success = false; - if (proposalResponses && proposalResponses.length > 0) { - proposalResponses.forEach((response) => { - if (response instanceof Error && + let success = false; + if (proposalResponses && proposalResponses.length > 0) { + proposalResponses.forEach((response) => { + if (response instanceof Error && response.message.indexOf('Did not find expected key "test" in the transient map of the proposal')) { - success = true; - } else { - success = false; - } - }); - } + success = true; + } else { + success = false; + } + }); + } - if (success) { + if (success) { // successfully tested the negative conditions caused by // the bad transient map, now send the good transient map - request = buildChaincodeProposal(client, the_user, chaincode_path, version, upgrade, transientMap); - tx_id = request.txId; + request = buildChaincodeProposal(client, the_user, chaincode_path, version, language, upgrade, transientMap); + tx_id = request.txId; - return channel.sendUpgradeProposal(request, 120000); - } else { - throw new Error('Failed to test for bad transient map. The chaincode should have rejected the upgrade proposal.'); + return channel.sendUpgradeProposal(request, 120000); + } else { + throw new Error('Failed to test for bad transient map. The chaincode should have rejected the upgrade proposal.'); + } + } else if (version === 'v3') { + return Promise.resolve(results); } - } else if (version === 'v3') { - return Promise.resolve(results); - } - }); + }); } else { - let request = buildChaincodeProposal(client, the_user, chaincode_path, version, upgrade, transientMap); + let request = buildChaincodeProposal(client, the_user, chaincode_path, version, language, upgrade, transientMap); tx_id = request.txId; // this is the longest response delay in the test, sometimes @@ -376,17 +385,17 @@ function instantiateChaincode(userOrg, chaincode_path, version, upgrade, t){ var sendPromise = channel.sendTransaction(request); return Promise.all([sendPromise].concat(eventPromises)) - .then((results) => { + .then((results) => { - logger.debug('Event promise all complete and testing complete'); - return results[0]; // just first results are from orderer, the rest are from the peer events + logger.debug('Event promise all complete and testing complete'); + return results[0]; // just first results are from orderer, the rest are from the peer events - }).catch((err) => { + }).catch((err) => { - t.fail('Failed to send ' + type + ' transaction and get notifications within the timeout period.'); - throw new Error('Failed to send ' + type + ' transaction and get notifications within the timeout period.'); + t.fail('Failed to send ' + type + ' transaction and get notifications within the timeout period.'); + throw new Error('Failed to send ' + type + ' transaction and get notifications within the timeout period.'); - }); + }); } else { t.fail('Failed to send ' + type + ' Proposal or receive valid response. Response null or status is not 200. exiting...'); @@ -411,19 +420,27 @@ function instantiateChaincode(userOrg, chaincode_path, version, upgrade, t){ t.fail('Failed to send ' + type + ' due to error: ' + err.stack ? err.stack : err); Promise.reject(new Error('Failed to send instantiate due to error: ' + err.stack ? err.stack : err)); }); -}; +} -function buildChaincodeProposal(client, the_user, chaincode_path, version, upgrade, transientMap) { +function buildChaincodeProposal(client, the_user, chaincode_path, version, type, upgrade, transientMap) { var tx_id = client.newTransactionID(); + let cc_id; + if(type && type==='node'){ + cc_id = e2e_node.chaincodeId; + } else { + cc_id = e2e.chaincodeId; + } + // send proposal to endorser var request = { chaincodePath: chaincode_path, - chaincodeId: e2e.chaincodeId, + chaincodeId: cc_id, chaincodeVersion: version, fcn: 'init', args: ['a', '100', 'b', '200'], txId: tx_id, + chaincodeType: type, // use this to demonstrate the following policy: // 'if signed by org1 admin, then that's the only signature required, // but if that signature is missing, then the policy can also be fulfilled @@ -457,7 +474,7 @@ function buildChaincodeProposal(client, the_user, chaincode_path, version, upgra module.exports.instantiateChaincode = instantiateChaincode; -function invokeChaincode(userOrg, version, t, useStore){ +function invokeChaincode(userOrg, version, chaincodeId, t, useStore){ init(); logger.debug('invokeChaincode begin'); @@ -511,7 +528,7 @@ function invokeChaincode(userOrg, version, t, useStore){ ) ); - var orgName = ORGS[userOrg].name; + orgName = ORGS[userOrg].name; var promise; if (useStore) { @@ -571,7 +588,7 @@ function invokeChaincode(userOrg, version, t, useStore){ // send proposal to endorser var request = { - chaincodeId : e2e.chaincodeId, + chaincodeId : chaincodeId, fcn: 'move', args: ['a', 'b','100'], txId: tx_id, @@ -673,17 +690,17 @@ function invokeChaincode(userOrg, version, t, useStore){ var sendPromise = channel.sendTransaction(request); return Promise.all([sendPromise].concat(eventPromises)) - .then((results) => { + .then((results) => { - logger.debug(' event promise all complete and testing complete'); - return results[0]; // the first returned value is from the 'sendPromise' which is from the 'sendTransaction()' call + logger.debug(' event promise all complete and testing complete'); + return results[0]; // the first returned value is from the 'sendPromise' which is from the 'sendTransaction()' call - }).catch((err) => { + }).catch((err) => { - t.fail('Failed to send transaction and get notifications within the timeout period.'); - throw new Error('Failed to send transaction and get notifications within the timeout period.'); + t.fail('Failed to send transaction and get notifications within the timeout period.'); + throw new Error('Failed to send transaction and get notifications within the timeout period.'); - }); + }); } else { t.fail('Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...'); @@ -714,11 +731,11 @@ function invokeChaincode(userOrg, version, t, useStore){ throw new Error('Failed to send transaction due to error: ' + err.stack ? err.stack : err); }); -}; +} module.exports.invokeChaincode = invokeChaincode; -function queryChaincode(org, version, value, t, transientMap) { +function queryChaincode(org, version, value, chaincodeId, t, transientMap) { init(); Client.setConfigSetting('request-timeout', 60000); @@ -764,7 +781,7 @@ function queryChaincode(org, version, value, t, transientMap) { // send query var request = { - chaincodeId : e2e.chaincodeId, + chaincodeId : chaincodeId, txId: tx_id, fcn: 'query', args: ['b'] @@ -805,7 +822,7 @@ function queryChaincode(org, version, value, t, transientMap) { t.fail('Failed to send query due to error: ' + err.stack ? err.stack : err); throw new Error('Failed, got error on query'); }); -}; +} module.exports.queryChaincode = queryChaincode; @@ -828,7 +845,6 @@ function readAllFiles(dir) { var certs = []; files.forEach((file_name) => { let file_path = path.join(dir,file_name); - console.debug(' looking at file ::'+file_path); let data = fs.readFileSync(file_path); certs.push(data); }); diff --git a/test/integration/e2e/install-chaincode-fail.js b/test/integration/e2e/install-chaincode-fail.js index 050186a0b1..57e6379c50 100644 --- a/test/integration/e2e/install-chaincode-fail.js +++ b/test/integration/e2e/install-chaincode-fail.js @@ -31,14 +31,14 @@ var testUtil = require('../../unit/util.js'); test('\n\n***** End-to-end flow: chaincode install *****\n\n', (t) => { testUtil.setupChaincodeDeploy(); - e2eUtils.installChaincode('org1', testUtil.CHAINCODE_PATH, 'v0', t, false) + e2eUtils.installChaincode('org1', testUtil.CHAINCODE_PATH, 'v0', 'golang', t, false) .then(() => { t.fail('Successfully installed chaincode in peers of organization "org1"'); - return e2eUtils.installChaincode('org2', testUtil.CHAINCODE_PATH, 'v0', t, false); + return e2eUtils.installChaincode('org2', testUtil.CHAINCODE_PATH, 'v0', 'golang', t, false); }, (err) => { t.pass('Failed to install chaincode in peers of organization "org1". ' + err.stack ? err.stack : err); logger.error('Failed to install chaincode in peers of organization "org1". '); - return e2eUtils.installChaincode('org2', testUtil.CHAINCODE_PATH, 'v0', t, false); + return e2eUtils.installChaincode('org2', testUtil.CHAINCODE_PATH, 'v0', 'golang', t, false); }).then(() => { t.fail('Successfully installed chaincode in peers of organization "org2"'); t.end(); diff --git a/test/integration/e2e/install-chaincode.js b/test/integration/e2e/install-chaincode.js index e6b83a1029..9fb67e1e0e 100644 --- a/test/integration/e2e/install-chaincode.js +++ b/test/integration/e2e/install-chaincode.js @@ -28,17 +28,19 @@ var test = _test(tape); var e2eUtils = require('./e2eUtils.js'); var testUtil = require('../../unit/util.js'); +var version = 'v0' + test('\n\n***** End-to-end flow: chaincode install *****\n\n', (t) => { testUtil.setupChaincodeDeploy(); - e2eUtils.installChaincode('org1', testUtil.CHAINCODE_PATH, 'v0', t, true) + e2eUtils.installChaincode('org1', testUtil.CHAINCODE_PATH, version, 'golang', t, true) .then(() => { t.pass('Successfully installed chaincode in peers of organization "org1"'); - return e2eUtils.installChaincode('org2', testUtil.CHAINCODE_PATH, 'v0', t, true); + return e2eUtils.installChaincode('org2', testUtil.CHAINCODE_PATH, version, 'golang', t, true); }, (err) => { t.fail('Failed to install chaincode in peers of organization "org1". ' + err.stack ? err.stack : err); logger.error('Failed to install chaincode in peers of organization "org1". '); - return e2eUtils.installChaincode('org2', testUtil.CHAINCODE_PATH, 'v0', t, true); + return e2eUtils.installChaincode('org2', testUtil.CHAINCODE_PATH, version, 'golang', t, true); }).then(() => { t.pass('Successfully installed chaincode in peers of organization "org2"'); t.end(); @@ -50,4 +52,4 @@ test('\n\n***** End-to-end flow: chaincode install *****\n\n', (t) => { t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); t.end(); }); -}); +}); \ No newline at end of file diff --git a/test/integration/e2e/instantiate-chaincode.js b/test/integration/e2e/instantiate-chaincode.js index 7cab1681ce..46e8846b32 100644 --- a/test/integration/e2e/instantiate-chaincode.js +++ b/test/integration/e2e/instantiate-chaincode.js @@ -29,7 +29,7 @@ var e2eUtils = require('./e2eUtils.js'); var testUtil = require('../../unit/util.js'); test('\n\n***** End-to-end flow: instantiate chaincode *****\n\n', (t) => { - e2eUtils.instantiateChaincode('org1', testUtil.CHAINCODE_PATH, 'v0', false, t) + e2eUtils.instantiateChaincode('org1', testUtil.CHAINCODE_PATH, 'v0', 'golang', false, t) .then((result) => { if(result){ t.pass('Successfully instantiated chaincode on the channel'); diff --git a/test/integration/e2e/invoke-transaction.js b/test/integration/e2e/invoke-transaction.js index 8b3896f7c1..89128d1513 100755 --- a/test/integration/e2e/invoke-transaction.js +++ b/test/integration/e2e/invoke-transaction.js @@ -22,27 +22,29 @@ var tape = require('tape'); var _test = require('tape-promise'); var test = _test(tape); var e2eUtils = require('./e2eUtils.js'); +var testUtils = require('../../unit/util'); +var chaincodeId = testUtils.END2END.chaincodeId; test('\n\n***** End-to-end flow: invoke transaction to move money *****\n\n', (t) => { - e2eUtils.invokeChaincode('org2', 'v0', t, false/*useStore*/) - .then((result) => { - if(result){ - t.pass('Successfully invoke transaction chaincode on channel'); - return sleep(5000); - } - else { - t.fail('Failed to invoke transaction chaincode '); + e2eUtils.invokeChaincode('org2', 'v0', chaincodeId, t, false/*useStore*/) + .then((result) => { + if(result){ + t.pass('Successfully invoke transaction chaincode on channel'); + return sleep(5000); + } + else { + t.fail('Failed to invoke transaction chaincode '); + t.end(); + } + }, (err) => { + t.fail('Failed to invoke transaction chaincode on channel. ' + err.stack ? err.stack : err); t.end(); - } - }, (err) => { - t.fail('Failed to invoke transaction chaincode on channel. ' + err.stack ? err.stack : err); - t.end(); - }).then(() => { - t.end(); - }).catch((err) => { - t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); - t.end(); - }); + }).then(() => { + t.end(); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); + t.end(); + }); }); function sleep(ms) { diff --git a/test/integration/e2e/query.js b/test/integration/e2e/query.js index 1ee45d5cee..47e137b30e 100644 --- a/test/integration/e2e/query.js +++ b/test/integration/e2e/query.js @@ -22,23 +22,25 @@ var tape = require('tape'); var _test = require('tape-promise'); var test = _test(tape); var e2eUtils = require('./e2eUtils.js'); +var testUtils = require('../../unit/util'); +var chaincodeId = testUtils.END2END.chaincodeId; test('\n\n***** End-to-end flow: query chaincode *****\n\n', (t) => { - e2eUtils.queryChaincode('org2', 'v0', '300', t) - .then((result) => { - if(result){ - t.pass('Successfully query chaincode on the channel'); + e2eUtils.queryChaincode('org2', 'v0', '300', chaincodeId, t) + .then((result) => { + if(result){ + t.pass('Successfully query chaincode on the channel'); + t.end(); + } + else { + t.fail('Failed to query chaincode '); + t.end(); + } + }, (err) => { + t.fail('Failed to query chaincode on the channel. ' + err.stack ? err.stack : err); t.end(); - } - else { - t.fail('Failed to query chaincode '); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); t.end(); - } - }, (err) => { - t.fail('Failed to query chaincode on the channel. ' + err.stack ? err.stack : err); - t.end(); - }).catch((err) => { - t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); - t.end(); - }); + }); }); diff --git a/test/integration/e2e/upgrade.js b/test/integration/e2e/upgrade.js index 12bfb79c8e..beac1a7bb9 100644 --- a/test/integration/e2e/upgrade.js +++ b/test/integration/e2e/upgrade.js @@ -23,108 +23,109 @@ var _test = require('tape-promise'); var test = _test(tape); var e2eUtils = require('./e2eUtils.js'); var testUtil = require('../../unit/util.js'); +var chaincodeId = testUtil.END2END.chaincodeId; test('\n\n***** U P G R A D E flow: chaincode install *****\n\n', (t) => { testUtil.setupChaincodeDeploy(); - e2eUtils.installChaincode('org1', testUtil.CHAINCODE_UPGRADE_PATH, 'v1', t, true) - .then(() => { - t.pass('Successfully installed chaincode in peers of organization "org1"'); - return e2eUtils.installChaincode('org2', testUtil.CHAINCODE_UPGRADE_PATH, 'v1', t, true); - }, (err) => { - t.fail('Failed to install chaincode in peers of organization "org1". ' + err.stack ? err.stack : err); - t.end(); - }).then(() => { - t.pass('Successfully installed chaincode in peers of organization "org2"'); - t.end(); - }, (err) => { - t.fail('Failed to install chaincode in peers of organization "org2". ' + err.stack ? err.stack : err); - t.end(); - }).catch((err) => { - t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); - t.end(); - }); + e2eUtils.installChaincode('org1', testUtil.CHAINCODE_UPGRADE_PATH, 'v1', 'golang', t, true) + .then(() => { + t.pass('Successfully installed chaincode in peers of organization "org1"'); + return e2eUtils.installChaincode('org2', testUtil.CHAINCODE_UPGRADE_PATH, 'v1', 'golang', t, true); + }, (err) => { + t.fail('Failed to install chaincode in peers of organization "org1". ' + err.stack ? err.stack : err); + t.end(); + }).then(() => { + t.pass('Successfully installed chaincode in peers of organization "org2"'); + t.end(); + }, (err) => { + t.fail('Failed to install chaincode in peers of organization "org2". ' + err.stack ? err.stack : err); + t.end(); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); + t.end(); + }); }); test('\n\n***** U P G R A D E flow: upgrade chaincode *****\n\n', (t) => { - e2eUtils.instantiateChaincode('org1', testUtil.CHAINCODE_UPGRADE_PATH, 'v1', true, t) - .then((result) => { - if(result){ - t.pass('Successfully upgrade chaincode on the channel'); + e2eUtils.instantiateChaincode('org1', testUtil.CHAINCODE_UPGRADE_PATH, 'v1', 'golang', true, t) + .then((result) => { + if(result){ + t.pass('Successfully upgrade chaincode on the channel'); + t.end(); + } + else { + t.fail('Failed to upgrade chaincode '); + t.end(); + } + }, (err) => { + t.fail('Failed to upgrade chaincode on the channel' + err.stack ? err.stack : err); t.end(); - } - else { - t.fail('Failed to upgrade chaincode '); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); t.end(); - } - }, (err) => { - t.fail('Failed to upgrade chaincode on the channel' + err.stack ? err.stack : err); - t.end(); - }).catch((err) => { - t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); - t.end(); - }); + }); }); test('\n\n***** U P G R A D E flow: invoke transaction to move money *****\n\n', (t) => { - e2eUtils.invokeChaincode('org2', 'v1', t) - .then((result) => { - if(result){ - t.pass('Successfully invoke transaction chaincode on the channel'); + e2eUtils.invokeChaincode('org2', 'v1', chaincodeId, t) + .then((result) => { + if(result){ + t.pass('Successfully invoke transaction chaincode on the channel'); + t.end(); + } + else { + t.fail('Failed to invoke transaction chaincode '); + t.end(); + } + }, (err) => { + t.fail('Failed to invoke transaction chaincode on the channel' + err.stack ? err.stack : err); t.end(); - } - else { - t.fail('Failed to invoke transaction chaincode '); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); t.end(); - } - }, (err) => { - t.fail('Failed to invoke transaction chaincode on the channel' + err.stack ? err.stack : err); - t.end(); - }).catch((err) => { - t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); - t.end(); - }); + }); }); test('\n\n***** U P G R A D E flow: query chaincode *****\n\n', (t) => { - e2eUtils.queryChaincode('org2', 'v1', '410', t) - .then((result) => { - if(result){ - t.pass('Successfully query chaincode on the channel'); + e2eUtils.queryChaincode('org2', 'v1', '410', chaincodeId, t) + .then((result) => { + if(result){ + t.pass('Successfully query chaincode on the channel'); + t.end(); + } + else { + t.fail('Failed to query chaincode '); + t.end(); + } + }, (err) => { + t.fail('Failed to query chaincode on the channel' + err.stack ? err.stack : err); t.end(); - } - else { - t.fail('Failed to query chaincode '); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); t.end(); - } - }, (err) => { - t.fail('Failed to query chaincode on the channel' + err.stack ? err.stack : err); - t.end(); - }).catch((err) => { - t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); - t.end(); - }); + }); }); test('\n\n***** TransientMap Support in Proposals *****\n\n', (t) => { var transient = { 'test': Buffer.from('dummyValue') // string <-> byte[] }; - e2eUtils.queryChaincode('org2', 'v1', '410', t, transient) - .then((result) => { - if(result){ - t.pass('Successfully verified transient map values'); + e2eUtils.queryChaincode('org2', 'v1', '410', chaincodeId, t, transient) + .then((result) => { + if(result){ + t.pass('Successfully verified transient map values'); + t.end(); + } + else { + t.fail('Failed to test transientMap support'); + t.end(); + } + }, (err) => { + t.fail('Failed to query chaincode on the channel' + err.stack ? err.stack : err); t.end(); - } - else { - t.fail('Failed to test transientMap support'); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); t.end(); - } - }, (err) => { - t.fail('Failed to query chaincode on the channel' + err.stack ? err.stack : err); - t.end(); - }).catch((err) => { - t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); - t.end(); - }); + }); }); diff --git a/test/integration/grpc.js b/test/integration/grpc.js index 9f84f96f28..09431dfb98 100644 --- a/test/integration/grpc.js +++ b/test/integration/grpc.js @@ -69,7 +69,7 @@ test('\n\n*** GRPC communication tests ***\n\n', (t) => { // limit the send message size to 1M utils.setConfigSetting('grpc-max-send-message-length', 1024 * 1024); - e2eUtils.installChaincode('org1', testUtil.CHAINCODE_PATH, 'v2', t, true) + e2eUtils.installChaincode('org1', testUtil.CHAINCODE_PATH, 'v2', 'golang', t, true) .then(() => { t.fail('Should have failed because the file size is too big for grpc send messages'); t.end(); @@ -83,7 +83,7 @@ test('\n\n*** GRPC communication tests ***\n\n', (t) => { // now dial the send limit up utils.setConfigSetting('grpc-max-send-message-length', 1024 * 1024 * 2); - return e2eUtils.installChaincode('org1', testUtil.CHAINCODE_PATH, 'v2', t, true); + return e2eUtils.installChaincode('org1', testUtil.CHAINCODE_PATH, 'v2', 'golang', t, true); }).then(() => { t.pass('Successfully tested setting grpc send limit'); diff --git a/test/integration/instantiate.js b/test/integration/instantiate.js index 852ca80dfb..c69ae62765 100644 --- a/test/integration/instantiate.js +++ b/test/integration/instantiate.js @@ -60,7 +60,7 @@ test('\n\n **** E R R O R T E S T I N G : instantiate call fails with non-exist }); test('\n\n***** End-to-end flow: instantiate chaincode *****\n\n', (t) => { - e2eUtils.instantiateChaincode('org1', testUtil.CHAINCODE_PATH, 'v0', false, t) + e2eUtils.instantiateChaincode('org1', testUtil.CHAINCODE_PATH, 'v0', 'golang', false, t) .then((result) => { if(result){ t.pass('Successfully instantiated chaincode on the channel'); diff --git a/test/integration/nodechaincode/e2e.js b/test/integration/nodechaincode/e2e.js new file mode 100644 index 0000000000..6eb2b36bf3 --- /dev/null +++ b/test/integration/nodechaincode/e2e.js @@ -0,0 +1,7 @@ +require('../e2e/create-channel.js'); +require('../e2e/join-channel.js'); +require('./install-chaincode.js'); +require('./instantiate-chaincode.js'); +require('./invoke.js'); +require('./query.js'); +require('./upgrade.js'); \ No newline at end of file diff --git a/test/integration/nodechaincode/install-chaincode.js b/test/integration/nodechaincode/install-chaincode.js new file mode 100644 index 0000000000..412612586f --- /dev/null +++ b/test/integration/nodechaincode/install-chaincode.js @@ -0,0 +1,52 @@ +/** + * Copyright 2017 IBM All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This is an end-to-end test that focuses on exercising all parts of the fabric APIs +// in a happy-path scenario +'use strict'; + +var utils = require('fabric-client/lib/utils.js'); +var logger = utils.getLogger('E2E install-chaincode'); + +var tape = require('tape'); +var _test = require('tape-promise'); +var test = _test(tape); + +var e2eUtils = require('../e2e/e2eUtils.js'); +var testUtil = require('../../unit/util.js'); +var version = 'v0'; + +test('\n\n***** Node-Chaincode End-to-end flow: chaincode install *****\n\n', (t) => { + e2eUtils.installChaincode('org1', testUtil.NODE_CHAINCODE_PATH, version, 'node', t, true) + .then(() => { + t.pass('Successfully installed chaincode in peers of organization "org1"'); + return e2eUtils.installChaincode('org2', testUtil.NODE_CHAINCODE_PATH, version, 'node', t, true); + }, (err) => { + t.fail('Failed to install chaincode in peers of organization "org1". ' + err.stack ? err.stack : err); + logger.error('Failed to install chaincode in peers of organization "org1". '); + return e2eUtils.installChaincode('org2', testUtil.NODE_CHAINCODE_PATH, version, 'node', t, true); + }).then(() => { + t.pass('Successfully installed chaincode in peers of organization "org2"'); + t.end(); + }, (err) => { + t.fail('Failed to install chaincode in peers of organization "org2". ' + err.stack ? err.stack : err); + logger.error('Failed to install chaincode in peers of organization "org2". '); + t.end(); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); + t.end(); + }); +}); diff --git a/test/integration/nodechaincode/instantiate-chaincode.js b/test/integration/nodechaincode/instantiate-chaincode.js new file mode 100644 index 0000000000..d258fb1495 --- /dev/null +++ b/test/integration/nodechaincode/instantiate-chaincode.js @@ -0,0 +1,60 @@ +/** + * Copyright 2017 IBM All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This is an end-to-end test that focuses on exercising all parts of the fabric APIs +// in a happy-path scenario +'use strict'; + +var utils = require('fabric-client/lib/utils.js'); +var logger = utils.getLogger('E2E instantiate-chaincode'); +logger.level = 'debug'; + +var tape = require('tape'); +var _test = require('tape-promise'); +var test = _test(tape); + +var e2eUtils = require('../e2e/e2eUtils.js'); +var testUtil = require('../../unit/util.js'); + + + +test('\n\n***** Node-Chaincode End-to-end flow: instantiate chaincode *****\n\n', (t) => { + e2eUtils.instantiateChaincode('org1', testUtil.NODE_CHAINCODE_PATH, 'v0', 'node', false, t) + .then((result) => { + if(result){ + t.pass('Successfully instantiated chaincode on the channel'); + + return sleep(5000); + } + else { + t.fail('Failed to instantiate chaincode '); + t.end(); + } + }, (err) => { + t.fail('Failed to instantiate chaincode on the channel. ' + err.stack ? err.stack : err); + t.end(); + }).then(() => { + logger.debug('Successfully slept 5s to wait for chaincode instantiate to be completed and committed in all peers'); + t.end(); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); + t.end(); + }); +}); + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/test/integration/nodechaincode/invoke.js b/test/integration/nodechaincode/invoke.js new file mode 100644 index 0000000000..7b70fe9d93 --- /dev/null +++ b/test/integration/nodechaincode/invoke.js @@ -0,0 +1,52 @@ +/** + * Copyright 2017 IBM All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This is an end-to-end test that focuses on exercising all parts of the fabric APIs +// in a happy-path scenario +'use strict'; + +var tape = require('tape'); +var _test = require('tape-promise'); +var test = _test(tape); +var e2eUtils = require('../e2e/e2eUtils.js'); +var testUtils = require('../../unit/util'); +var chaincodeId = testUtils.NODE_END2END.chaincodeId; + +test('\n\n***** Node-Chaincode End-to-end flow: invoke transaction to move money *****\n\n', (t) => { + e2eUtils.invokeChaincode('org2', 'v0', chaincodeId, t, false/*useStore*/) + .then((result) => { + if(result){ + t.pass('Successfully invoke transaction chaincode on channel'); + return sleep(5000); + } + else { + t.fail('Failed to invoke transaction chaincode '); + t.end(); + } + }, (err) => { + t.fail('Failed to invoke transaction chaincode on channel. ' + err.stack ? err.stack : err); + t.end(); + }).then(() => { + t.end(); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); + t.end(); + }); +}); + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/test/integration/nodechaincode/query.js b/test/integration/nodechaincode/query.js new file mode 100644 index 0000000000..5f3290b243 --- /dev/null +++ b/test/integration/nodechaincode/query.js @@ -0,0 +1,46 @@ +/** + * Copyright 2017 IBM All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This is an end-to-end test that focuses on exercising all parts of the fabric APIs +// in a happy-path scenario +'use strict'; + +var tape = require('tape'); +var _test = require('tape-promise'); +var test = _test(tape); +var e2eUtils = require('../e2e/e2eUtils.js'); +var testUtils = require('../../unit/util'); +var chaincodeId = testUtils.NODE_END2END.chaincodeId; + +test('\n\n***** Node-Chaincode End-to-end flow: query chaincode *****\n\n', (t) => { + e2eUtils.queryChaincode('org2', 'v0', '300', chaincodeId, t) + .then((result) => { + if(result){ + t.pass('Successfully query chaincode on the channel'); + t.end(); + } + else { + t.fail('Failed to query chaincode '); + t.end(); + } + }, (err) => { + t.fail('Failed to query chaincode on the channel. ' + err.stack ? err.stack : err); + t.end(); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); + t.end(); + }); +}); diff --git a/test/integration/nodechaincode/upgrade.js b/test/integration/nodechaincode/upgrade.js new file mode 100644 index 0000000000..5d56ccccc4 --- /dev/null +++ b/test/integration/nodechaincode/upgrade.js @@ -0,0 +1,130 @@ +/** + * Copyright 2016 IBM All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This is an end-to-end test that focuses on exercising all parts of the fabric APIs +// in a happy-path scenario +'use strict'; + +var tape = require('tape'); +var _test = require('tape-promise'); +var test = _test(tape); +var e2eUtils = require('../e2e/e2eUtils.js'); +var testUtil = require('../../unit/util.js'); +var chaincodeId = testUtil.NODE_END2END.chaincodeId; +var version = 'v1'; + +test('\n\n***** Node-Chaincode U P G R A D E flow: chaincode install *****\n\n', (t) => { + e2eUtils.installChaincode('org1', testUtil.NODE_CHAINCODE_UPGRADE_PATH, version, 'node', t, true) + .then(() => { + t.pass('Successfully installed chaincode in peers of organization "org1"'); + return e2eUtils.installChaincode('org2', testUtil.NODE_CHAINCODE_UPGRADE_PATH, version, 'node', t, true); + }, (err) => { + t.fail('Failed to install chaincode in peers of organization "org1". ' + err.stack ? err.stack : err); + t.end(); + }).then(() => { + t.pass('Successfully installed chaincode in peers of organization "org2"'); + t.end(); + }, (err) => { + t.fail('Failed to install chaincode in peers of organization "org2". ' + err.stack ? err.stack : err); + t.end(); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); + t.end(); + }); +}); + +test('\n\n***** Node-Chaincode U P G R A D E flow: upgrade chaincode *****\n\n', (t) => { + e2eUtils.instantiateChaincode('org1', testUtil.NODE_CHAINCODE_UPGRADE_PATH, version, 'node', true, t) + .then((result) => { + if (result) { + t.pass('Successfully upgrade chaincode on the channel'); + t.end(); + } + else { + t.fail('Failed to upgrade chaincode '); + t.end(); + } + }, (err) => { + t.fail('Failed to upgrade chaincode on the channel' + err.stack ? err.stack : err); + t.end(); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); + t.end(); + }); +}); + +test('\n\n***** Node-Chaincode U P G R A D E flow: invoke transaction to move money *****\n\n', (t) => { + e2eUtils.invokeChaincode('org2', 'v1', chaincodeId, t) + .then((result) => { + if(result){ + t.pass('Successfully invoke transaction chaincode on the channel'); + t.end(); + } + else { + t.fail('Failed to invoke transaction chaincode '); + t.end(); + } + }, (err) => { + t.fail('Failed to invoke transaction chaincode on the channel' + err.stack ? err.stack : err); + t.end(); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); + t.end(); + }); +}); + +test('\n\n***** Node-Chaincode U P G R A D E flow: query chaincode *****\n\n', (t) => { + e2eUtils.queryChaincode('org2', 'v1', '410', chaincodeId, t) + .then((result) => { + if(result){ + t.pass('Successfully query chaincode on the channel'); + t.end(); + } + else { + t.fail('Failed to query chaincode '); + t.end(); + } + }, (err) => { + t.fail('Failed to query chaincode on the channel' + err.stack ? err.stack : err); + t.end(); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); + t.end(); + }); +}); + +test('\n\n***** Node-Chaincode TransientMap Support in Proposals *****\n\n', (t) => { + var transient = { + 'test': Buffer.from('dummyValue') // string <-> byte[] + }; + e2eUtils.queryChaincode('org2', 'v1', '410', chaincodeId, t, transient) + .then((result) => { + if(result){ + t.pass('Successfully verified transient map values'); + t.end(); + } + else { + t.fail('Failed to test transientMap support'); + t.end(); + } + }, (err) => { + t.fail('Failed to query chaincode on the channel' + err.stack ? err.stack : err); + t.end(); + }).catch((err) => { + t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); + t.end(); + }); +}); diff --git a/test/integration/upgrade.js b/test/integration/upgrade.js index b51772ee1b..9037d89da6 100644 --- a/test/integration/upgrade.js +++ b/test/integration/upgrade.js @@ -185,18 +185,18 @@ test('\n\n **** Testing re-initializing states during upgrade ****', (t) => { let tx_id = client.newTransactionID(); let VER = 'v3'; - e2eUtils.installChaincode('org1', testUtil.CHAINCODE_UPGRADE_PATH_V2, VER, t, true) + e2eUtils.installChaincode('org1', testUtil.CHAINCODE_UPGRADE_PATH_V2, VER, 'golang', t, true) .then(() => { - return e2eUtils.installChaincode('org2', testUtil.CHAINCODE_UPGRADE_PATH_V2, VER, t, true); + return e2eUtils.installChaincode('org2', testUtil.CHAINCODE_UPGRADE_PATH_V2, VER, 'golang', t, true); }, (err) => { t.fail('Failed to install chaincode in peers of organization "org1". ' + err.stack ? err.stack : err); t.end(); }).then(() => { - return e2eUtils.instantiateChaincode('org1', testUtil.CHAINCODE_UPGRADE_PATH_V2, VER, true, t); + return e2eUtils.instantiateChaincode('org1', testUtil.CHAINCODE_UPGRADE_PATH_V2, VER, 'golang', true, t); }).then((results) => { - + let chaincodeId = testUtil.END2END.chaincodeId; logger.debug('Successfully upgraded chaincode to version v3'); - return e2eUtils.queryChaincode('org1', VER, '1000', t); + return e2eUtils.queryChaincode('org1', VER, '1000', chaincodeId, t); }).then((result) => { if(result){ diff --git a/test/unit/util.js b/test/unit/util.js index 1daec00eba..073c925924 100644 --- a/test/unit/util.js +++ b/test/unit/util.js @@ -41,6 +41,18 @@ module.exports.END2END = { chaincodeVersion: 'v0' }; +module.exports.NODE_CHAINCODE_PATH = path.resolve(__dirname, '../fixtures/src/node_cc/example_cc'); +module.exports.NODE_CHAINCODE_UPGRADE_PATH = path.resolve(__dirname, '../fixtures/src/node_cc/example_cc1'); +module.exports.NODE_CHAINCODE_UPGRADE_PATH_V2 = path.resolve(__dirname, '../fixtures/src/node_cc/example_cc2'); + + +module.exports.NODE_END2END = { + channel: 'mychannel', + chaincodeId: 'e2enodecc', + chaincodeLanguage: 'node', + chaincodeVersion: 'v0' +}; + // all temporary files and directories are created under here var tempdir = Constants.tempdir;