text: /**
   * 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.
   */
  'use strict';
  var util = require('util');
  var helper = require('./helper.js');
  var logger = helper.getLogger('instantiate-chaincode');
  
  var instantiateChaincode = async function(peers, channelName, chaincodeName, chaincodeVersion, functionName, chaincodeType, args, username, org_name) {
  	logger.debug('\n\n============ Instantiate chaincode on channel ' + channelName +
  		' ============\n');
  	var error_message = null;
  
  	try {
  		// first setup the client for this org
  		var client = await helper.getClientForOrg(org_name, username);
  		logger.debug('Successfully got the fabric client for the organization "%s"', org_name);
  		var channel = client.getChannel(channelName);
  		if(!channel) {
  			let message = util.format('Channel %s was not defined in the connection profile', channelName);
  			logger.error(message);
  			throw new Error(message);
  		}
  		var tx_id = client.newTransactionID(true); // Get an admin based transactionID
  		                                       // An admin based transactionID will
  		                                       // indicate that admin identity should
  		                                       // be used to sign the proposal request.
  		// will need the transaction ID string for the event registration later
  		var deployId = tx_id.getTransactionID();
  
  		// send proposal to endorser
  		var request = {
  			targets : peers,
  			chaincodeId: chaincodeName,
  			chaincodeType: chaincodeType,
  			chaincodeVersion: chaincodeVersion,
  			args: args,
  			txId: tx_id,
  
  			// Use this to demonstrate the following policy:
  			// The policy can be fulfilled when members from both orgs signed.
  			'endorsement-policy': {
  			        identities: [
  					{ role: { name: 'member', mspId: 'Org1MSP' }},
  					{ role: { name: 'member', mspId: 'Org2MSP' }}
  			        ],
  			        policy: {
  					'2-of':[{ 'signed-by': 0 }, { 'signed-by': 1 }]
  			        }
  		        }
  		};
  
  		if (functionName)
  			request.fcn = functionName;
  
  		let results = await channel.sendInstantiateProposal(request, 60000); //instantiate takes much longer
  
  		// the returned object has both the endorsement results
  		// and the actual proposal, the proposal will be needed
  		// later when we send a transaction to the orderer
  		var proposalResponses = results[0];
  		var proposal = results[1];
  
  		// lets have a look at the responses to see if they are
  		// all good, if good they will also include signatures
  		// required to be committed
  		var all_good = true;
  		for (var i in proposalResponses) {
  			let one_good = false;
  			if (proposalResponses && proposalResponses[i].response &&
  				proposalResponses[i].response.status === 200) {
  				one_good = true;
  				logger.info('instantiate proposal was good');
  			} else {
  				logger.error('instantiate proposal was bad');
  			}
  			all_good = all_good & one_good;
  		}
  
  		if (all_good) {
  			logger.info(util.format(
  				'Successfully sent Proposal and received ProposalResponse: Status - %s, message - "%s", metadata - "%s"',
  				proposalResponses[0].response.status, proposalResponses[0].response.message,
  				proposalResponses[0].response.payload, proposalResponses[0].endorsement.signature));
  
  			// wait for the channel-based event hub to tell us that the
  			// instantiate transaction was committed on the peer
  			var promises = [];
  			let event_hubs = channel.getChannelEventHubsForOrg();
  			logger.debug('found %s eventhubs for this organization %s',event_hubs.length, org_name);
  			event_hubs.forEach((eh) => {
  				let instantiateEventPromise = new Promise((resolve, reject) => {
  					logger.debug('instantiateEventPromise - setting up event');
  					let event_timeout = setTimeout(() => {
  						let message = 'REQUEST_TIMEOUT:' + eh.getPeerAddr();
  						logger.error(message);
  						eh.disconnect();
  					}, 60000);
  					eh.registerTxEvent(deployId, (tx, code, block_num) => {
  						logger.info('The chaincode instantiate transaction has been committed on peer %s',eh.getPeerAddr());
  						logger.info('Transaction %s has status of %s in blocl %s', tx, code, block_num);
  						clearTimeout(event_timeout);
  
  						if (code !== 'VALID') {
  							let message = util.format('The chaincode instantiate transaction was invalid, code:%s',code);
  							logger.error(message);
  							reject(new Error(message));
  						} else {
  							let message = 'The chaincode instantiate transaction was valid.';
  							logger.info(message);
  							resolve(message);
  						}
  					}, (err) => {
  						clearTimeout(event_timeout);
  						logger.error(err);
  						reject(err);
  					},
  						// the default for 'unregister' is true for transaction listeners
  						// so no real need to set here, however for 'disconnect'
  						// the default is false as most event hubs are long running
  						// in this use case we are using it only once
  						{unregister: true, disconnect: true}
  					);
  					eh.connect();
  				});
  				promises.push(instantiateEventPromise);
  			});
  
  			var orderer_request = {
  				txId: tx_id, // must include the transaction id so that the outbound
  				             // transaction to the orderer will be signed by the admin
  							 // id as was the proposal above, notice that transactionID
  							 // generated above was based on the admin id not the current
  							 // user assigned to the 'client' instance.
  				proposalResponses: proposalResponses,
  				proposal: proposal
  			};
  			var sendPromise = channel.sendTransaction(orderer_request);
  			// put the send to the orderer last so that the events get registered and
  			// are ready for the orderering and committing
  			promises.push(sendPromise);
  			let results = await Promise.all(promises);
  			logger.debug(util.format('------->>> R E S P O N S E : %j', results));
  			let response = results.pop(); //  orderer results are last in the results
  			if (response.status === 'SUCCESS') {
  				logger.info('Successfully sent transaction to the orderer.');
  			} else {
  				error_message = util.format('Failed to order the transaction. Error code: %s',response.status);
  				logger.debug(error_message);
  			}
  
  			// now see what each of the event hubs reported
  			for(let i in results) {
  				let event_hub_result = results[i];
  				let event_hub = event_hubs[i];
  				logger.debug('Event results for event hub :%s',event_hub.getPeerAddr());
  				if(typeof event_hub_result === 'string') {
  					logger.debug(event_hub_result);
  				} else {
  					if(!error_message) error_message = event_hub_result.toString();
  					logger.debug(event_hub_result.toString());
  				}
  			}
  		} else {
  			error_message = util.format('Failed to send Proposal and receive all good ProposalResponse');
  			logger.debug(error_message);
  		}
  	} catch (error) {
  		logger.error('Failed to send instantiate due to error: ' + error.stack ? error.stack : error);
  		error_message = error.toString();
  	}
  
  	if (!error_message) {
  		let message = util.format(
  			'Successfully instantiate chaincode in organization %s to the channel \'%s\'',
  			org_name, channelName);
  		logger.info(message);
  		// build a response to send back to the REST caller
  		let response = {
  			success: true,
  			message: message
  		};
  		return response;
  	} else {
  		let message = util.format('Failed to instantiate. cause:%s',error_message);
  		logger.error(message);
  		throw new Error(message);
  	}
  };
  exports.instantiateChaincode = instantiateChaincode;
  