一 代码解析
var createChannel = function(channelName, channelConfigPath, username, orgName) {
logger.debug(’\n====== Creating Channel ‘’ + channelName + ‘’ ======\n’);
var client = helper.getClientForOrg(orgName);
var channel = helper.getChannelForOrg(orgName);
var envelope = fs.readFileSync(path.join(__dirname, channelConfigPath));
// 提取配置信息 extract the channel config bytes from the envelope to be signed
var channelConfig = client.extractChannelConfig(1)(envelope);
return helper.getOrgAdmin(orgName)(2).then((admin) => {
logger.debug(util.format(‘Successfully acquired admin user for the organization “%s”’, orgName));
// sign the channel config bytes as “endorsement”, this is required by
// the orderer’s channel creation policy
//3 签名channel order要求 要创建(更新)的channel 必须要admin签名
let signature = client.signChannelConfig(3)(channelConfig);
let request = {
// byte[]
config: channelConfig,
// admin 签名后的数据
signatures: [signature],
name: channelName,
// 这个channel 中的order对象
orderer: channel.getOrderers()(4)[0],
txId: client.newTransactionID() (5) // 交易id
};
//4 通过client创建channel 里面 send to orderer 通过order来广播事务
return client.createChannel(6)(request);
}, (err) => {
logger.error(‘Failed to enroll user ‘’+username+’’. Error: ’ + err);
throw new Error(‘Failed to enroll user ‘’+username+’’’ + err);
}).then((response) => {
logger.debug(’ response ::%j’, response);
//5 处理响应结果
if (response && response.status === ‘SUCCESS’) {
logger.debug(‘Successfully created the channel.’);
let response = {
success: true,
message: ‘Channel ‘’ + channelName + ‘’ created Successfully’
};
return response;
} else {
logger.error(’\n!!! Failed to create the channel ‘’ + channelName +
‘’ !!!\n\n’);
throw new Error(‘Failed to create the channel ‘’ + channelName + ‘’’);
}
}, (err) => {
logger.error('Failed to initialize the channel: ’ + err.stack ? err.stack :
err);
throw new Error('Failed to initialize the channel: ’ + err.stack ? err.stack : err);
});
};
二 api追踪
(1)
* Extracts the protobuf ‘ConfigUpdate’ object out of the ‘ConfigEnvelope’ object
* that is produced by the [configtxgen tool]{@link http://hyperledger-fabric.readthedocs.io/en/latest/configtxgen.html}.
* The returned object may then be signed using the signChannelConfig() method of this class. Once the all
* signatures have been collected, the ‘ConfigUpdate’ object and the signatures may be used
* on the [createChannel()]{@link Client#createChannel} or [updateChannel()]{@link Client#updateChannel} calls.
*
* @param {byte[]} config_envelope - The encoded bytes of the ConfigEnvelope protobuf
* @returns {byte[]} The encoded bytes of the ConfigUpdate protobuf, ready to be signed
*/
extractChannelConfig(config_envelope) {
logger.debug('extractConfigUpdate - start');
try {
var envelope = _commonProto.Envelope.decode(config_envelope);
var payload = _commonProto.Payload.decode(envelope.getPayload().toBuffer());
var configtx = _configtxProto.ConfigUpdateEnvelope.decode(payload.getData().toBuffer());
return configtx.getConfigUpdate().toBuffer();
}
catch(err) {
if(err instanceof Error) {
logger.error('Problem with extracting the config update object :: %s', err.stack ? err.stack : err);
throw err;
}
else {
logger.error('Problem with extracting the config update object :: %s',err);
throw new Error(err);
}
}
}
(2)
var getOrgAdmin = function(userOrg) {
var admin = ORGS[userOrg].admin;
var keyPath = path.join(__dirname, admin.key);
var keyPEM = Buffer.from(readAllFiles(keyPath)[0]).toString();
var certPath = path.join(__dirname, admin.cert);
var certPEM = readAllFiles(certPath)[0].toString();
var client = getClientForOrg(userOrg);
var cryptoSuite = hfc.newCryptoSuite();
if (userOrg) {
cryptoSuite.setCryptoKeyStore(hfc.newCryptoKeyStore({path: getKeyStoreForOrg(getOrgName(userOrg))}));
client.setCryptoSuite(cryptoSuite);
}
return hfc.newDefaultKeyValueStore({
path: getKeyStoreForOrg(getOrgName(userOrg))
}).then((store) => {
client.setStateStore(store);
return client.createUser({
username: ‘peer’+userOrg+‘Admin’,
mspid: getMspID(userOrg),
cryptoContent: {
privateKeyPEM: keyPEM,
signedCertPEM: certPEM
}
});
});
};
(3)
/**
* Channel configuration updates can be sent to the orderers to be processed. The
* orderer enforces the Channel creation or update policies such that the updates
* will be made only when enough signatures from participating organizations are
* discovered in the request. Typically channel creation or update requests must
* be signed by participating organizations’ ADMIN principals, although this policy
* can be customized when the consortium is defined.
*
* This method uses the client instance’s current signing identity to sign over the
* configuration bytes passed in, and returns the signature that is ready to be
* included in the configuration update protobuf message to send to the orderer.
*
* @param {byte[]} config - The Configuration Update in byte form
* @return {ConfigSignature} - The signature of the current user on the config bytes
*/
signChannelConfig(config) {
logger.debug('signChannelConfigUpdate - start');
if (typeof config === 'undefined' || config === null) {
throw new Error('Channel configuration update parameter is required.');
}
if(!(config instanceof Buffer)) {
throw new Error('Channel configuration update parameter is not in the correct form.');
}
// should try to use the admin signer if assigned
// then use the assigned user
var signer = this._getSigningIdentity(true);
// signature is across a signature header and the config update
let proto_signature_header = new _commonProto.SignatureHeader();
proto_signature_header.setCreator(signer.serialize());
proto_signature_header.setNonce(sdkUtils.getNonce());
var signature_header_bytes = proto_signature_header.toBuffer();
// get all the bytes to be signed together, then sign
let signing_bytes = Buffer.concat([signature_header_bytes, config]);
let sig = signer.sign(signing_bytes);
let signature_bytes = Buffer.from(sig);
// build the return object
let proto_config_signature = new _configtxProto.ConfigSignature();
proto_config_signature.setSignatureHeader(signature_header_bytes);
proto_config_signature.setSignature(signature_bytes);
return proto_config_signature;
}
(4)
-
Returns the orderers of this channel object.
* @returns {Orderer[]} The list of orderers in the channel object
/
getOrderers() {
return this._orderers;
}
(5)
/*
* Returns a new {@link TransactionID} object. Fabric transaction ids are constructed
* as a hash of a nonce concatenated with the signing identity’s serialized bytes. The
* TransactionID object keeps the nonce and the resulting id string bundled together
* as a coherent pair.
*
* This method requires the client instance to have been assigned a userContext.
* @param {boolean} If this transactionID should be built based on the admin credentials
* Default is a non admin TransactionID
* @returns {TransactionID} An object that contains a transaction id based on the
* client’s userContext and a randomly generated nonce value.
*/newTransactionID(admin) { if(admin) { if(typeof admin === 'boolean') { if(admin) { logger.debug('newTransactionID - getting an admin TransactionID'); } else { logger.debug('newTransactionID - getting non admin TransactionID'); } } else { throw new Error('"admin" parameter must be of type boolean'); } } else { admin = false; logger.debug('newTransactionID - no admin parameter, returning non admin TransactionID'); } return new TransactionID(this._getSigningIdentity(admin), admin); }
/**
* The class representing the transaction identifier. Provides for
* automatically creating the nonce
value when an instance of this
* object is created.
*
* @class
/
var TransactionID = class {
/*
* Builds a new tranaction Id based on a user’s certificate and an automatically
* generates a nonce value.
* @param {Identity} signer_or_userContext - An instance of {@link Identity} that provides an unique
* base for this transaction id. This also may be an instance of a {@User}.
* @param {boolean} admin - Indicates that this instance will be used for administrative transactions.
*/
constructor(signer_or_userContext, admin) {
logger.debug('const - start');
if (typeof signer_or_userContext === 'undefined' || signer_or_userContext === null) {
throw new Error('Missing userContext or signing identity parameter');
}
var signer = null;
if((User.isInstance(signer_or_userContext))) {
signer = signer_or_userContext.getSigningIdentity();
} else {
signer = signer_or_userContext;
}
this._nonce = sdkUtils.getNonce(); //nonce is in bytes
let creator_bytes = signer.serialize();//same as signatureHeader.Creator
let trans_bytes = Buffer.concat([this._nonce, creator_bytes]);
let trans_hash = hashPrimitives.sha2_256(trans_bytes);
this._transaction_id = Buffer.from(trans_hash).toString();
logger.debug('const - transaction_id %s',this._transaction_id);
this._admin = admin;
}
/**
* The transaction ID
*/
getTransactionID() {
return this._transaction_id;
}
/**
* The nonce value
*/
getNonce() {
return this._nonce;
}
/**
* indicates if this transactionID was generated for an admin
*/
isAdmin() {
if(this._admin) {
return true;
} else {
return false;
}
}
};
module.exports = TransactionID;
(6)
-
@param {ChannelRequest} request - The request object.
* @returns {Promise} Promise for a result object with status on the acceptance of the create request
* by the orderer. Note that this is not the confirmation of successful creation
* of the channel. The client application must poll the orderer to discover whether
* the channel has been created completely or not.
*/createChannel(request) { var have_envelope = false; if(request && request.envelope) { logger.debug('createChannel - have envelope'); have_envelope = true; } return this._createOrUpdateChannel(request, have_envelope); }
-
internal method to support create or update of a channel
*/_createOrUpdateChannel(request, have_envelope) { logger.debug('_createOrUpdateChannel - start'); var error_msg = null; var orderer = null; if(!request) { error_msg = 'Missing all required input request parameters for initialize channel'; } // Verify that a config envelope or config has been included in the request object else if (!request.config && !have_envelope) { error_msg = 'Missing config request parameter containing the configuration of the channel'; } else if(!request.signatures && !have_envelope) { error_msg = 'Missing signatures request parameter for the new channel'; } else if(!Array.isArray(request.signatures ) && !have_envelope) { error_msg = 'Signatures request parameter must be an array of signatures'; } else if(!request.txId && !have_envelope) { error_msg = 'Missing txId request parameter'; } // verify that we have the name of the new channel else if(!request.name) { error_msg = 'Missing name request parameter'; } if(error_msg) { logger.error('_createOrUpdateChannel error %s',error_msg); return Promise.reject(new Error(error_msg)); } try { orderer = this.getTargetOrderer(request.orderer, null, request.name); } catch (err) { return Promise.reject(err); } var self = this; var channel_id = request.name; var channel = null; // caller should have gotten a admin based TransactionID // but maybe not, so go with whatever they have decided var signer = this._getSigningIdentity(request.txId.isAdmin()); var signature = null; var payload = null; if (have_envelope) { logger.debug('_createOrUpdateChannel - have envelope'); var envelope = _commonProto.Envelope.decode(request.envelope); signature = envelope.signature; payload = envelope.payload; } else { logger.debug('_createOrUpdateChannel - have config_update'); var proto_config_Update_envelope = new _configtxProto.ConfigUpdateEnvelope(); proto_config_Update_envelope.setConfigUpdate(request.config); var signatures = _stringToSignature(request.signatures); proto_config_Update_envelope.setSignatures(signatures); var proto_channel_header = clientUtils.buildChannelHeader( _commonProto.HeaderType.CONFIG_UPDATE, request.name, request.txId.getTransactionID() ); var proto_header = clientUtils.buildHeader(signer, proto_channel_header, request.txId.getNonce()); var proto_payload = new _commonProto.Payload(); proto_payload.setHeader(proto_header); proto_payload.setData(proto_config_Update_envelope.toBuffer()); var payload_bytes = proto_payload.toBuffer(); let sig = signer.sign(payload_bytes); let signature_bytes = Buffer.from(sig); signature = signature_bytes; payload = payload_bytes; } // building manually or will get protobuf errors on send var out_envelope = { signature: signature, payload : payload }; logger.debug('_createOrUpdateChannel - about to send envelope'); return orderer.sendBroadcast(out_envelope) .then( function(results) { logger.debug('_createOrUpdateChannel - good results from broadcast :: %j',results); return Promise.resolve(results); } ) .catch( function(error) { if(error instanceof Error) { logger.debug('_createOrUpdateChannel - rejecting with %s', error); return Promise.reject(error); } else { logger.error('_createOrUpdateChannel - system error :: %s', error); return Promise.reject(new Error(error)); } } ); }