1. 程式人生 > >balance transfer程式碼解析及api深度追蹤(五)初始化鏈碼

balance transfer程式碼解析及api深度追蹤(五)初始化鏈碼

一程式碼解析 var path = require(‘path’); var fs = require(‘fs’); var util = require(‘util’); var hfc = require(‘fabric-client’); var Peer = require(‘fabric-client/lib/Peer.js’); var EventHub = require(‘fabric-client/lib/EventHub.js’); var helper = require(’./helper.js’); var logger = helper.getLogger(‘instantiate-chaincode’); var ORGS = hfc.getConfigSetting(‘network-config’); var tx_id = null; var eh = null;

var instantiateChaincode = function(channelName, chaincodeName, chaincodeVersion, functionName, args, username, org) { logger.debug(’\n============ Instantiate chaincode on organization ’ + org + ’ ============\n’); var channel = helper.getChannelForOrg(org); var client = helper.getClientForOrg(org); // 1 獲取組織管理員 return helper.getOrgAdmin(org).then((user) => { // read the config block from the orderer for the channel // and initialize the verify MSPs based on the participating // organizations // 2 初始化 channel 的msp 會從order裡獲取配置 return channel.initialize();(1) }, (err) => { logger.error('Failed to enroll user ‘’ + username + ‘’. ’ + err); throw new Error('Failed to enroll user ‘’ + username + ‘’. ’ + err); }).then((success) => { tx_id = client.newTransactionID(); // send proposal to endorser

//3.1封裝提案請求 var request = { chaincodeId: chaincodeName, chaincodeVersion: chaincodeVersion, args: args, txId: tx_id };

    if (functionName)
        request.fcn = functionName;
    // 3.2  提交初始化chaincode提案給背書節點  
    return channel.sendInstantiateProposal(request)(4);
}, (err) => {
    logger.error('Failed to initialize the channel');
    throw new Error('Failed to initialize the channel');
}).then((results) => {
    var proposalResponses = results[0];
    var proposal = results[1];
    var all_good = true;
    //4 驗證所有的背書響應是否ok
    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", endorsement signature: %s',
            proposalResponses[0].response.status, proposalResponses[0].response.message,
            proposalResponses[0].response.payload, proposalResponses[0].endorsement
            .signature));
        //4.0 封裝交易請求
        var request = {
            proposalResponses: proposalResponses,
            proposal: proposal
        };
        // set the transaction listener and set a timeout of 30sec
        // if the transaction did not get committed within the timeout period,
        // fail the test
        var deployId = tx_id.getTransactionID();
        // 4.1 設定監聽事件連結
        eh = client.newEventHub();
        let data = fs.readFileSync(path.join(__dirname, ORGS[org].peers['peer1'][
            'tls_cacerts'
        ]));
        eh.setPeerAddr(ORGS[org].peers['peer1']['events'], {
            pem: Buffer.from(data).toString(),
            'ssl-target-name-override': ORGS[org].peers['peer1']['server-hostname']
        });
        eh.connect();
        //4.2  監聽事件並設定超時, 生成promise
        let txPromise = new Promise((resolve, reject) => {
            let handle = setTimeout(() => {
                eh.disconnect();
                reject();
            }, 30000);

            eh.registerTxEvent(deployId, (tx, code) => {
                logger.info(
                    'The chaincode instantiate transaction has been committed on peer ' +
                    eh._ep._endpoint.addr);
                clearTimeout(handle);
                eh.unregisterTxEvent(deployId);
                eh.disconnect();

                if (code !== 'VALID') {
                    logger.error('The chaincode instantiate transaction was invalid, code = ' + code);
                    reject();
                } else {
                    logger.info('The chaincode instantiate transaction was valid.');
                    resolve();
                }
            });
        });
        // 5 背書響應ok  傳送交易
        var sendPromise = channel.sendTransaction(request);
        //6  整合promise  並執行
        return Promise.all([sendPromise].concat([txPromise])).then((results) => {
            logger.debug('Event promise all complete and testing complete');
            //7  這個是第五步執行的結果
            return results[0]; // the first returned value is from the 'sendPromise' which is from the 'sendTransaction()' call
        }).catch((err) => {
            logger.error(
                util.format('Failed to send instantiate transaction and get notifications within the timeout period. %s', err)
            );
            return 'Failed to send instantiate transaction and get notifications within the timeout period.';
        });
    } else {
        logger.error(
            'Failed to send instantiate Proposal or receive valid response. Response null or status is not 200. exiting...'
        );
        return 'Failed to send instantiate Proposal or receive valid response. Response null or status is not 200. exiting...';
    }
}, (err) => {
    logger.error('Failed to send instantiate proposal due to error: ' + err.stack ?
        err.stack : err);
    return 'Failed to send instantiate proposal due to error: ' + err.stack ?
        err.stack : err;
}).then((response) => {
    // 8   處理第六步返回的結果
    if (response.status === 'SUCCESS') {
        logger.info('Successfully sent transaction to the orderer.');
        return 'Chaincode Instantiation is SUCCESS';
    } else {
        logger.error('Failed to order the transaction. Error code: ' + response.status);
        return 'Failed to order the transaction. Error code: ' + response.status;
    }
}, (err) => {
    logger.error('Failed to send instantiate due to error: ' + err.stack ? err
        .stack : err);
    return 'Failed to send instantiate due to error: ' + err.stack ? err.stack :
        err;
});

}; exports.instantiateChaincode = instantiateChaincode; 二 api深度追蹤 (1) * Initializes the channel object with the Membership Service Providers (MSPs). The channel’s * MSPs are critical in providing applications the ability to validate certificates and verify * signatures in messages received from the fabric backend. For instance, after calling * [sendTransactionProposal()]{@link Channel#sendTransactionProposal}, the application can * verify the signatures in the proposal response’s endorsements to ensure they have not been * tampered with. * * This method retrieves the configuration from the orderer if no “config” parameter is passed in. * Optionally a configuration may be passed in to initialize this channel without making the call * to the orderer. * * @param {byte[]} config - Optional. An encoded (a.k.a un-decoded) byte array of the protobuf “ConfigUpdate” * @return {Promise} A Promise that will resolve when the action is complete *

	initialize(config_update) {
		if (config_update) {
			this.loadConfigUpdate(config_update);
			return Promise.resolve(true);
		}

		var self = this;
		return this.getChannelConfig()(2)
			.then(
				function (config_envelope) {
					logger.debug('initialize - got config envelope from getChannelConfig :: %j', config_envelope);
					var config_items = self.loadConfigEnvelope(config_envelope);
					return Promise.resolve(config_items);
				}
			)
			.catch(
				function (error) {
					logger.error('initialize - system error ::' + error.stack ? error.stack : error);
					return Promise.reject(new Error(error));
				}
			);
	}

(2) * Asks the peer for the current (latest) configuration block for this channel. * @param {string | Peer} target - Optional. The peer to be used to make the * request. * @returns {Promise} A Promise for a {@link ConfigEnvelope} object containing the configuration items. */

	getChannelConfig(target) {
		let method = 'getChannelConfig';
		logger.debug('%s - start for channel %s', method, this._name);
		var targets = this._getTargetForQuery(target);
		var signer = this._clientContext._getSigningIdentity(true);
		var tx_id = new TransactionID(signer, true);
		var request = {
			targets: targets,
			chaincodeId: Constants.CSCC,
			txId: tx_id,
			signer: signer,
			fcn: 'GetConfigBlock',
			args: [this._name]
		};
		return this.sendTransactionProposal(request)(3)
			.then(
				function (results) {
					var responses = results[0];
					// var proposal = results[1];
					logger.debug('%s - results received', method);
					if (responses && Array.isArray(responses)) {
						let response = responses[0];
						if (response instanceof Error) {
							return Promise.reject(response);
						}
						else if (response.response && response.response.payload) {
							let block = _commonProto.Block.decode(response.response.payload);
							let envelope = _commonProto.Envelope.decode(block.data.data[0]);
							let payload = _commonProto.Payload.decode(envelope.payload);
							let config_envelope = _configtxProto.ConfigEnvelope.decode(payload.data);
							return Promise.resolve(config_envelope);
						}
						else {
							logger.error('%s - unknown response ::%s', method, response);
							return Promise.reject(new Error(response));
						}
					}
					return Promise.reject(new Error('Payload results are missing from the get channel config'));
				}
			).catch(
				function (err) {
					logger.error('%s - Failed getting channel config. Error: %s', method, err.stack ? err.stack : err);
					return Promise.reject(err);
				}
			);
	}

(3) * Sends a transaction proposal to one or more endorsing peers. * * After a chaincode gets [installed]{@link Client#installChaincode} and * [instantiated]{@link Channel#instantiateChaincode}, it’s ready to take endorsement * proposals and participating in transaction processing. A chaincode transaction * starts with a proposal that gets sent to the endorsing peers, which executes * the target chaincode and decides whether the proposal should be endorsed (if it * executes successfully) or not (if the chaincode returns an error). * * @param {ChaincodeInvokeRequest} request * @param {Number} timeout - A number indicating milliseconds to wait on the * response before rejecting the promise with a * timeout error. This overrides the default timeout * of the Peer instance and the global timeout in the config settings. * @returns {Promise} A Promise for the {@link ProposalResponseObject} */

	sendTransactionProposal(request, timeout) {
		logger.debug('sendTransactionProposal - start');

		if (!request) {
			throw new Error('Missing request object for this transaction proposal');
		}
		request.targets = this._getTargets(request.targets, Constants.NetworkConfig.ENDORSING_PEER_ROLE);

		return Channel.sendTransactionProposal(request, this._name, this._clientContext, timeout);
	}

	/*
	 * Internal static method to allow transaction proposals to be called without
	 * creating a new channel
	 */
	static sendTransactionProposal(request, channelId, clientContext, timeout) {
		// Verify that a Peer has been added
		var errorMsg = clientUtils.checkProposalRequest(request);

		if (errorMsg) {
			// do nothing so we skip the rest of the checks
		} else if (!request.args) {
			// args is not optional because we need for transaction to execute
			errorMsg = 'Missing "args" in Transaction proposal request';
		} else if (!request.targets || request.targets.length < 1) {
			errorMsg = 'Missing peer objects in Transaction proposal';
		}

		if (errorMsg) {
			logger.error('sendTransactionProposal error ' + errorMsg);
			throw new Error(errorMsg);
		}

		var args = [];
		args.push(Buffer.from(request.fcn ? request.fcn : 'invoke', 'utf8'));
		logger.debug('sendTransactionProposal - adding function arg:%s', request.fcn ? request.fcn : 'invoke');

		for (let i = 0; i < request.args.length; i++) {
			logger.debug('sendTransactionProposal - adding arg:%s', request.args[i]);
			args.push(Buffer.from(request.args[i], 'utf8'));
		}
		//special case to support the bytes argument of the query by hash
		if (request.argbytes) {
			logger.debug('sendTransactionProposal - adding the argument :: argbytes');
			args.push(request.argbytes);
		}
		else {
			logger.debug('sendTransactionProposal - not adding the argument :: argbytes');
		}
		let invokeSpec = {
			type: _ccProto.ChaincodeSpec.Type.GOLANG,
			chaincode_id: {
				name: request.chaincodeId
			},
			input: {
				args: args
			}
		};

		var proposal, header;
		var signer = null;
		if (request.signer) {
			signer = request.signer;
		} else {
			signer = clientContext._getSigningIdentity(request.txId.isAdmin());
		}
		var channelHeader = clientUtils.buildChannelHeader(
			_commonProto.HeaderType.ENDORSER_TRANSACTION,
			channelId,
			request.txId.getTransactionID(),
			null,
			request.chaincodeId,
			clientUtils.buildCurrentTimestamp(),
			request.targets[0].getClientCertHash()
		);
		header = clientUtils.buildHeader(signer, channelHeader, request.txId.getNonce());
		proposal = clientUtils.buildProposal(invokeSpec, header, request.transientMap);
		let signed_proposal = clientUtils.signProposal(signer, proposal);

		return clientUtils.sendPeersProposal(request.targets, signed_proposal, timeout)
			.then(
				function (responses) {
					return Promise.resolve([responses, proposal]);
				}
			).catch(
				function (err) {
					logger.error('Failed Proposal. Error: %s', err.stack ? err.stack : err);
					return Promise.reject(err);
				}
			);
	}

(4) * Sends a chaincode instantiate proposal to one or more endorsing peers. * * A chaincode must be instantiated on a channel-by-channel basis before it can * be used. The chaincode must first be installed on the endorsing peers where * this chaincode is expected to run, by calling [client.installChaincode()]{@link Client#installChaincode}. * * Instantiating a chaincode is a full transaction operation, meaning it must be * first endorsed as a proposal, then the endorsements are sent to the orderer * to be processed for ordering and validation. When the transaction finally gets * committed to the channel’s ledger on the peers, the chaincode is then considered * activated and the peers are ready to take requests to process transactions. * * @param {ChaincodeInstantiateUpgradeRequest} request * @param {Number} timeout - A number indicating milliseconds to wait on the * response before rejecting the promise with a * timeout error. This overrides the default timeout * of the Peer instance and the global timeout in the config settings. * @returns {Promise} A Promise for the {@link ProposalResponseObject} */

	sendInstantiateProposal(request, timeout) {
		return this._sendChaincodeProposal(request, 'deploy', timeout);
	}

	/**
	 * Sends a chaincode upgrade proposal to one or more endorsing peers.
	 *
	 * Upgrading a chaincode involves steps similar to instantiating a chaincode.
	 * The new chaincode must first be installed on the endorsing peers where
	 * this chaincode is expected to run.
	 * <br><br>
	 * Similar to instantiating a chaincode, upgrading chaincodes is also a full transaction
	 * operation.
	 *
	 * @param {ChaincodeInstantiateUpgradeRequest} request
	 * @param {Number} timeout - A number indicating milliseconds to wait on the
	 *                              response before rejecting the promise with a
	 *                              timeout error. This overrides the default timeout
	 *                              of the Peer instance and the global timeout in the config settings.
	 * @returns {Promise} A Promise for the {@link ProposalResponseObject}
	 */
	sendUpgradeProposal(request, timeout) {
		return this._sendChaincodeProposal(request, 'upgrade', timeout);
	}

	/*
	 * Internal method to handle both chaincode calls
	 */
	_sendChaincodeProposal(request, command, timeout) {
		let errorMsg = null;

		//validate the incoming request
		if (!errorMsg) errorMsg = clientUtils.checkProposalRequest(request);
		if (!errorMsg) errorMsg = clientUtils.checkInstallRequest(request);
		if (errorMsg) {
			logger.error('sendChainCodeProposal error ' + errorMsg);
			return Promise.reject(new Error(errorMsg));
		}
		const peers = this._getTargets(request.targets, Constants.NetworkConfig.ENDORSING_PEER_ROLE);

		// args is optional because some chaincode may not need any input parameters during initialization
		if (!request.args) {
			request.args = [];
		}

		// step 1: construct a ChaincodeSpec
		const args = [];
		args.push(Buffer.from(request.fcn ? request.fcn : 'init', 'utf8'));

		for (let arg of request.args)
			args.push(Buffer.from(arg, 'utf8'));

		const ccSpec = {
			type: clientUtils.translateCCType(request.chaincodeType),
			chaincode_id: {
				name: request.chaincodeId,
				version: request.chaincodeVersion
			},
			input: {
				args: args
			}
		};

		// step 2: construct the ChaincodeDeploymentSpec
		const chaincodeDeploymentSpec = new _ccProto.ChaincodeDeploymentSpec();
		chaincodeDeploymentSpec.setChaincodeSpec(ccSpec);

		const signer = this._clientContext._getSigningIdentity(request.txId.isAdmin());
		const lcccSpec_args = [
			Buffer.from(command),
			Buffer.from(this._name),
			chaincodeDeploymentSpec.toBuffer()
		];
		if (request['endorsement-policy']) {
			lcccSpec_args[3] = this._buildEndorsementPolicy(request['endorsement-policy']);
		}

		const lcccSpec = {
			// type: _ccProto.ChaincodeSpec.Type.GOLANG,
			type: clientUtils.translateCCType(request.chaincodeType),
			chaincode_id: { name: Constants.LSCC },
			input: { args: lcccSpec_args }
		};

		const channelHeader = clientUtils.buildChannelHeader(
			_commonProto.HeaderType.ENDORSER_TRANSACTION,
			this._name,
			request.txId.getTransactionID(),
			null,
			Constants.LSCC,
			clientUtils.buildCurrentTimestamp(),
			peers[0].getClientCertHash()
		);
		const header = clientUtils.buildHeader(signer, channelHeader, request.txId.getNonce());
		const proposal = clientUtils.buildProposal(lcccSpec, header, request.transientMap);
		const signed_proposal = clientUtils.signProposal(signer, proposal);

		return clientUtils.sendPeersProposal(peers, signed_proposal, timeout)
			.then(
				function (responses) {
					return [responses, proposal];
				}
			);
	}