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
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];
}
);
}