From 0df2e6bc922c0cdce2f87b61599c7cc3672d05ba Mon Sep 17 00:00:00 2001 From: Bret Harrison Date: Mon, 16 Jan 2017 14:17:17 -0500 Subject: [PATCH] NodeSDK - updates for latest proto files Pulled in latest fabric proto files and clean up the new chain testing and error handling. Change-Id: I6323b66ae710ceff8c5b6db2e73aae2b2a2955d8 Signed-off-by: Bret Harrison --- hfc/lib/Chain.js | 506 +++++++++++++-------- hfc/lib/Orderer.js | 131 +++++- hfc/lib/protos/common/common.proto | 17 + hfc/lib/protos/common/configuration.proto | 20 +- hfc/lib/protos/common/msp_principal.proto | 122 +++++ hfc/lib/protos/msp/mspconfig.proto | 93 ++++ hfc/lib/protos/orderer/ab.proto | 11 +- hfc/lib/protos/orderer/configuration.proto | 13 + hfc/lib/protos/peer/chaincode.proto | 7 +- hfc/lib/protos/peer/configuration.proto | 41 ++ test/unit/new-chain.js | 24 +- 11 files changed, 734 insertions(+), 251 deletions(-) create mode 100644 hfc/lib/protos/common/msp_principal.proto create mode 100644 hfc/lib/protos/msp/mspconfig.proto create mode 100644 hfc/lib/protos/peer/configuration.proto diff --git a/hfc/lib/Chain.js b/hfc/lib/Chain.js index ce300eff32..afc6045f9d 100644 --- a/hfc/lib/Chain.js +++ b/hfc/lib/Chain.js @@ -1,5 +1,5 @@ /* - Copyright 2016 IBM All Rights Reserved. + Copyright 2016, 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. @@ -34,10 +34,12 @@ var _ccTransProto = grpc.load(__dirname + '/protos/peer/chaincode_transaction.pr var _transProto = grpc.load(__dirname + '/protos/peer/fabric_transaction.proto').protos; var _proposalProto = grpc.load(__dirname + '/protos/peer/fabric_proposal.proto').protos; var _responseProto = grpc.load(__dirname + '/protos/peer/fabric_proposal_response.proto').protos; +var _mspPrProto = grpc.load(__dirname + '/protos/common/msp_principal.proto').common; var _commonProto = grpc.load(__dirname + '/protos/common/common.proto').common; var _configurationProto = grpc.load(__dirname + '/protos/common/configuration.proto').common; -var _ordererConfigurationProto = grpc.load(__dirname + - '/protos/orderer/configuration.proto').orderer; +var _ordererConfigurationProto = grpc.load(__dirname + '/protos/orderer/configuration.proto').orderer; +var _abProto = grpc.load(__dirname + '/protos/orderer/ab.proto').orderer; + /** * The class representing a chain with which the client SDK interacts. @@ -105,6 +107,7 @@ var Chain = class { // to the desired values before initializing this chain this._initial_epoch = 0; this._initial_max_message_count = 10; + this._initial_absolute_max_bytes = 10 * 1024 * 1024; this._consensus_type = 'solo'; // user must set this value before the initializeChain() method // is called @@ -335,6 +338,29 @@ var Chain = class { this._initial_max_message_count = initial_max_message_count; } + /** + * Get the initial absolute maximum bytes that will be used when this + * chain is created. + * @return {int} initial absolute maximum bytes + */ + getInitialAbsoluteMaxBytes() { + return this._initial_absolute_max_bytes; + } + + /** + * Set the initial absolute maximum bytes that will be used when this + * chain is created. + * Default 0 + * + * @param {int} initial absolute maximum bytes + */ + setInitialAbsoluteMaxBytes(initial_absolute_max_bytes) { + if(!Number.isInteger(initial_absolute_max_bytes) || initial_absolute_max_bytes < 0) { + throw new Error('initial maximum message count must be a positive integer'); + } + this._initial_absolute_max_bytes = initial_absolute_max_bytes; + } + /** * Get the initial transaction ID that will be used when this * chain is created. @@ -384,7 +410,6 @@ var Chain = class { logger.error('initializeChain - no chain id defined'); return Promise.reject(new Error('Chain name is not defined')); } - let chain_id = this._name; // verify that we have a transactionid configured if(!this._initial_transaction_id) { @@ -392,159 +417,178 @@ var Chain = class { return Promise.reject(new Error('Initial transaction id is not defined')); } - logger.debug('initializeChain - building request'); - // build fields to use when building the configuration items - var configItemChainHeader = - buildChainHeader( - _commonProto.HeaderType.CONFIGURATION_ITEM, - 1, - chain_id, - this._initial_transaction_id, - this._initial_epoch - ); - logger.debug('initializeChain - header built'); - - var orderer_type = - _configurationProto.ConfigurationItem.ConfigurationType.Orderer; - var policy_type = - _configurationProto.ConfigurationItem.ConfigurationType.Policy; - var last_modified = '0'; - var mod_policy = 'DefaultModificationPolicy'; - - var creation_items = []; - - // build configuration items - var consensusType = new _ordererConfigurationProto.ConsensusType(); - consensusType.setType(this._consensus_type); - var consensusTypeItem = - buildSignedConfigurationItem( - configItemChainHeader, - orderer_type, - last_modified, - mod_policy, - 'ConsensusType', - consensusType.toBuffer() - ); - creation_items.push(consensusTypeItem.getConfigurationItem().toBuffer()); - - logger.debug('initializeChain - bytes for consesus item ::' - + JSON.stringify(consensusTypeItem.getConfigurationItem())); - - var batchSize = new _ordererConfigurationProto.BatchSize(); - batchSize.setMaxMessageCount(this._initial_max_message_count); - var batchSizeItem = - buildSignedConfigurationItem( - configItemChainHeader, - orderer_type, - last_modified, - mod_policy, - 'BatchSize', - batchSize.toBuffer() - ); - creation_items.push(batchSizeItem.getConfigurationItem().toBuffer()); - - // TODO how do we deal with KafkaBrokers ? - - var chainCreators = new _ordererConfigurationProto.ChainCreators(); - var chainCreatorPolicyName = 'AcceptAllPolicy'; - chainCreators.setPolicies([chainCreatorPolicyName]); - var chainCreatorsItem = - buildSignedConfigurationItem( - configItemChainHeader, - orderer_type, - last_modified, - mod_policy, - 'ChainCreators', - chainCreators.toBuffer() - ); - creation_items.push( - chainCreatorsItem.getConfigurationItem().toBuffer() - ); - - var acceptAllPolicy = buildAcceptAllPolicy(); - logger.debug('accept policy::'+JSON.stringify(acceptAllPolicy)); - var acceptAllPolicyItem = - buildSignedConfigurationItem( - configItemChainHeader, - policy_type, - last_modified, - mod_policy, - chainCreatorPolicyName, - acceptAllPolicy.toBuffer() - ); - creation_items.push( - acceptAllPolicyItem.getConfigurationItem().toBuffer() - ); + var self = this; + var chain_id = this._name; + var orderer = self.getOrderers()[0]; + var userContext = null; - var rejectAllPolicy = buildRejectAllPolicy(); - logger.debug('reject policy::'+JSON.stringify(rejectAllPolicy)); - var defaultModificationPolicyItem = - buildSignedConfigurationItem( - configItemChainHeader, - policy_type, - last_modified, - mod_policy, - 'DefaultModificationPolicy', - rejectAllPolicy.toBuffer() - ); - creation_items.push( - defaultModificationPolicyItem.getConfigurationItem().toBuffer() - ); - - // hash all the bytes of all items - var itemBytes = Buffer.concat(creation_items); - logger.debug('initializeChain - itemBytes::'+itemBytes.toString('hex')); - //var creation_items_hash = this.cryptoPrimitives.hash(itemBytes); - var hashPrimitives = require('./hash.js'); - var creation_items_hash = hashPrimitives.shake_256(itemBytes, 512); - logger.debug('initializeChain - creation_item_hash::'+creation_items_hash); - - // final item to contain hash of all others - var creationPolicy = new _ordererConfigurationProto.CreationPolicy(); - creationPolicy.setPolicy(chainCreatorPolicyName); - creationPolicy.setDigest(Buffer.from(creation_items_hash, 'hex')); - //creationPolicy.setDigest(creation_items_hash); - var createPolicyItem = - buildSignedConfigurationItem( - configItemChainHeader, - orderer_type, - last_modified, - mod_policy, - 'CreationPolicy', - creationPolicy.toBuffer() - ); - - logger.debug('initializeChain - all items built'); - - var configurationEnvelope = new _configurationProto.ConfigurationEnvelope(); - configurationEnvelope.setItems([ - createPolicyItem, - consensusTypeItem, - batchSizeItem, - chainCreatorsItem, - acceptAllPolicyItem, - defaultModificationPolicyItem - ]); - - var chainHeader = new _commonProto.ChainHeader(); - chainHeader.setChainID(chain_id); - chainHeader.setType(_commonProto.HeaderType.CONFIGURATION_TRANSACTION); - - var header = new _commonProto.Header(); - header.setChainHeader(chainHeader); + return this._clientContext.getUserContext() + .then( + function(foundUserContext) { + userContext = foundUserContext; + + logger.debug('initializeChain - building broadcast message'); + // build fields to use when building the configuration items + var configItemChainHeader = buildChainHeader( + _commonProto.HeaderType.CONFIGURATION_ITEM, + chain_id, + self._initial_transaction_id, + self._initial_epoch + ); - var payload = new _commonProto.Payload(); - payload.setHeader(header); - payload.setData(configurationEnvelope.toBuffer()); - //logger.debug('initializeChain - built payload::'+JSON.stringify(payload)); + var orderer_type = + _configurationProto.ConfigurationItem.ConfigurationType.Orderer; + var policy_type = + _configurationProto.ConfigurationItem.ConfigurationType.Policy; + var last_modified = '0'; + var mod_policy = 'DefaultModificationPolicy'; + + var creation_items = []; + + // build configuration items + var consensusType = new _ordererConfigurationProto.ConsensusType(); + consensusType.setType(self._consensus_type); + var consensusTypeItem = buildSignedConfigurationItem( + configItemChainHeader, + orderer_type, + last_modified, + mod_policy, + 'ConsensusType', + consensusType.toBuffer() + ); + creation_items.push(consensusTypeItem.getConfigurationItem().toBuffer()); + + var batchSize = new _ordererConfigurationProto.BatchSize(); + batchSize.setMaxMessageCount(self._initial_max_message_count); + batchSize.setAbsoluteMaxBytes(self._initial_absolute_max_bytes); + var batchSizeItem = buildSignedConfigurationItem( + configItemChainHeader, + orderer_type, + last_modified, + mod_policy, + 'BatchSize', + batchSize.toBuffer() + ); + creation_items.push(batchSizeItem.getConfigurationItem().toBuffer()); + + // TODO how do we deal with KafkaBrokers ? + var chainCreatorPolicyName = 'AcceptAllPolicy'; + +// var chainCreators = new _ordererConfigurationProto.ChainCreators(); +// chainCreators.setPolicies([chainCreatorPolicyName]); +// var chainCreatorsItem = buildSignedConfigurationItem( +// configItemChainHeader, +// orderer_type, +// last_modified, +// mod_policy, +// 'ChainCreators', +// chainCreators.toBuffer() +// ); +// creation_items.push(chainCreatorsItem.getConfigurationItem().toBuffer()); + + var ingressPolicy = new _ordererConfigurationProto.IngressPolicy(); + ingressPolicy.setName(chainCreatorPolicyName); + var ingressPolicyItem = buildSignedConfigurationItem( + configItemChainHeader, + orderer_type, + last_modified, + mod_policy, + 'IngressPolicy', + ingressPolicy.toBuffer() + ); + creation_items.push(ingressPolicyItem.getConfigurationItem().toBuffer()); + + var egressPolicy = new _ordererConfigurationProto.EgressPolicy(); + egressPolicy.setName(chainCreatorPolicyName); + var egressPolicyItem = buildSignedConfigurationItem( + configItemChainHeader, + orderer_type, + last_modified, + mod_policy, + 'EgressPolicy', + egressPolicy.toBuffer() + ); + creation_items.push(egressPolicyItem.getConfigurationItem().toBuffer()); + + var acceptAllPolicy = buildAcceptAllPolicy(); + logger.debug('accept policy::'+JSON.stringify(acceptAllPolicy)); + var acceptAllPolicyItem = buildSignedConfigurationItem( + configItemChainHeader, + policy_type, + last_modified, + mod_policy, + chainCreatorPolicyName, + acceptAllPolicy.toBuffer() + ); + creation_items.push( + acceptAllPolicyItem.getConfigurationItem().toBuffer() + ); - var payload_bytes = payload.toBuffer(); + var rejectAllPolicy = buildRejectAllPolicy(); + logger.debug('reject policy::'+JSON.stringify(rejectAllPolicy)); + var defaultModificationPolicyItem = buildSignedConfigurationItem( + configItemChainHeader, + policy_type, + last_modified, + mod_policy, + 'DefaultModificationPolicy', + rejectAllPolicy.toBuffer() + ); + creation_items.push(defaultModificationPolicyItem.getConfigurationItem().toBuffer()); + + logger.debug('initializeChain - all policies built'); + + // hash all the bytes of all items + var itemBytes = Buffer.concat(creation_items); + logger.debug('initializeChain - itemBytes::'+itemBytes.toString('hex')); + //var creation_items_hash = this.cryptoPrimitives.hash(itemBytes); + var hashPrimitives = require('./hash.js'); + var creation_items_hash = hashPrimitives.shake_256(itemBytes, 512); + logger.debug('initializeChain - creation_item_hash::'+creation_items_hash); + + // final item to contain hash of all others + var creationPolicy = new _ordererConfigurationProto.CreationPolicy(); + creationPolicy.setPolicy(chainCreatorPolicyName); + creationPolicy.setDigest(Buffer.from(creation_items_hash, 'hex')); + //creationPolicy.setDigest(creation_items_hash); + var createPolicyItem = buildSignedConfigurationItem( + configItemChainHeader, + orderer_type, + last_modified, + mod_policy, + 'CreationPolicy', + creationPolicy.toBuffer() + ); - logger.debug('initializeChain - about to call sendBroadcast'); - var self = this; - return this._clientContext.getUserContext() - .then( - function(userContext) { + logger.debug('initializeChain - all items built'); + + //bundle all the items + var configurationEnvelope = new _configurationProto.ConfigurationEnvelope(); + configurationEnvelope.setItems([ + createPolicyItem, + consensusTypeItem, + batchSizeItem, +// chainCreatorsItem, + ingressPolicyItem, + egressPolicyItem, + acceptAllPolicyItem, + defaultModificationPolicyItem + ]); + + // build a chain header for later to be + // used in the atomic broadcast + var newChainHeader = buildChainHeader( + _commonProto.HeaderType.CONFIGURATION_TRANSACTION, + chain_id, + self._initial_transaction_id, + self._initial_epoch + ); + var broadcastHeader = buildHeader(userContext.getIdentity(), newChainHeader, utils.getNonce()); + var payload = new _commonProto.Payload(); + payload.setHeader(broadcastHeader); + payload.setData(configurationEnvelope.toBuffer()); + var payload_bytes = payload.toBuffer(); + //logger.debug('initializeChain - Here is the envelope to broadcast :: '+ JSON.stringify(payload)); let sig = userContext.getSigningIdentity().sign(payload_bytes); let signature = Buffer.from(sig); @@ -554,9 +598,73 @@ var Chain = class { payload : payload_bytes }; - var orderer = self.getOrderers()[0]; return orderer.sendBroadcast(envelope); } + ) + .then( + function(results) { + // now build the seek info , will be used once the chain is created + // to get the genesis block back + // build start + var seekSpecifiedStart = new _abProto.SeekSpecified(); + seekSpecifiedStart.setNumber(0); + var seekStart = new _abProto.SeekPosition(); + seekStart.setSpecified(seekSpecifiedStart); + + // build stop + var seekSpecifiedStop = new _abProto.SeekSpecified(); + seekSpecifiedStop.setNumber(0); + var seekStop = new _abProto.SeekPosition(); + seekStop.setSpecified(seekSpecifiedStop); + + // seek info with all parts + var seekInfo = new _abProto.SeekInfo(); + seekInfo.setStart(seekStart); + seekInfo.setStop(seekStop); + seekInfo.setBehavior(_abProto.SeekInfo.SeekBehavior.BLOCK_UNTIL_READY); //TODO should we do this or fail ?? + //logger.debug('initializeChain - seekInfo ::' + JSON.stringify(seekInfo)); + + // build the header for use with the seekInfo payload + var seekInfoHeader = buildChainHeader( + _commonProto.HeaderType.DELIVER_SEEK_INFO, + chain_id, + self._initial_transaction_id, + self._initial_epoch + ); + + var seekHeader = buildHeader(userContext.getIdentity(), seekInfoHeader, utils.getNonce()); + var seekPayload = new _commonProto.Payload(); + seekPayload.setHeader(seekHeader); + seekPayload.setData(seekInfo.toBuffer()); + var seekPayloadBytes = seekPayload.toBuffer(); + + let sig = userContext.getSigningIdentity().sign(seekPayloadBytes); + let signature = Buffer.from(sig); + + // building manually or will get protobuf errors on send + var envelope = { + signature: signature, + payload : seekPayloadBytes + }; + + return orderer.sendDeliver(envelope); + } + ) + .then( + function(results) { + logger.debug('initializeChain - good results from seek block :: %j',results); + // verify that we have the genesis block + if(results.block) { + logger.debug('initializeChain - found block'); + } + return Promise.resolve(results); + } + ) + .catch( + function(error) { + logger.error('initializeChain - system error ::' + error.stack ? error.stack : error); + return Promise.reject(new Error(error)); + } ); } @@ -705,7 +813,14 @@ var Chain = class { return self._clientContext.getUserContext() .then( function(userContext) { - header = Chain._buildHeader(userContext.getIdentity(), request.chainId, 'lccc', request.txId, request.nonce); + var chainHeader = buildChainHeader( + _commonProto.HeaderType.ENDORSER_TRANSACTION, + request.chainId, + request.txId, + null, + 'lccc' + ); + header = buildHeader(userContext.getIdentity(), chainHeader, request.nonce); proposal = self._buildProposal(lcccSpec, header); let signed_proposal = self._signProposal(userContext.getSigningIdentity(), proposal); @@ -792,7 +907,14 @@ var Chain = class { return this._clientContext.getUserContext() .then( function(userContext) { - header = Chain._buildHeader(userContext.getIdentity(), request.chainId, request.chaincodeId, request.txId, request.nonce); + var chainHeader = buildChainHeader( + _commonProto.HeaderType.ENDORSER_TRANSACTION, + request.chainId, + request.txId, + null, + request.chaincodeId + ); + header = buildHeader(userContext.getIdentity(), chainHeader, request.nonce); proposal = self._buildProposal(invokeSpec, header); let signed_proposal = self._signProposal(userContext.getSigningIdentity(), proposal); @@ -963,37 +1085,6 @@ var Chain = class { ); } - // internal utility method to build the header - /** - * @private - */ - static _buildHeader(creator, chain_id, chaincode_id, tx_id, nonce) { - let chainHeader = new _commonProto.ChainHeader(); - chainHeader.setType(_commonProto.HeaderType.ENDORSER_TRANSACTION); - chainHeader.setTxID(tx_id.toString()); - chainHeader.setChainID(chain_id); - if(chaincode_id) { - let chaincodeID = new _ccProto.ChaincodeID(); - chaincodeID.setName(chaincode_id); - - let headerExt = new _ccProposalProto.ChaincodeHeaderExtension(); - headerExt.setChaincodeID(chaincodeID); - - chainHeader.setExtension(headerExt.toBuffer()); - } - - let signatureHeader = new _commonProto.SignatureHeader(); - - signatureHeader.setCreator(creator.serialize()); - signatureHeader.setNonce(nonce); - - let header = new _commonProto.Header(); - header.setSignatureHeader(signatureHeader); - header.setChainHeader(chainHeader); - - return header; - } - // internal utility method to build the proposal /** * @private @@ -1168,19 +1259,43 @@ function packageChaincode(chaincodePath, chaincodeId, dockerfileContents) { } //utility method to build a common chain header -function buildChainHeader(type, version, chain_id, tx_id, epoch) { +function buildChainHeader(type, chain_id, tx_id, epoch, chaincode_id) { + logger.debug('buildChainHeader - type %s chain_id %s tx_id %d epoch % chaincode_id %s', + type, chain_id, tx_id, epoch, chaincode_id); var chainHeader = new _commonProto.ChainHeader(); chainHeader.setType(type); // int32 - chainHeader.setVersion(version); // int32 + chainHeader.setVersion(1); // int32 //chainHeader.setTimeStamp(time_stamp); // google.protobuf.Timestamp chainHeader.setChainID(chain_id); //string - chainHeader.setTxID(tx_id); //string - chainHeader.setEpoch(epoch); // uint64 - //chainHeader.setExtension(extension); // bytes + chainHeader.setTxID(tx_id.toString()); //string + if(epoch) { + chainHeader.setEpoch(epoch); // uint64 + } + if(chaincode_id) { + let chaincodeID = new _ccProto.ChaincodeID(); + chaincodeID.setName(chaincode_id); + + let headerExt = new _ccProposalProto.ChaincodeHeaderExtension(); + headerExt.setChaincodeID(chaincodeID); + chainHeader.setExtension(headerExt.toBuffer()); + } return chainHeader; }; +// utility method to build the header +function buildHeader(creator, chainHeader, nonce) { + let signatureHeader = new _commonProto.SignatureHeader(); + signatureHeader.setCreator(creator.serialize()); + signatureHeader.setNonce(nonce); + + let header = new _commonProto.Header(); + header.setSignatureHeader(signatureHeader); + header.setChainHeader(chainHeader); + + return header; +} + //utility method to build a signed configuration item function buildSignedConfigurationItem( chain_header, @@ -1209,12 +1324,12 @@ function buildSignedConfigurationItem( //utility method to build an accept all policy function buildAcceptAllPolicy() { - return buildPolicyEnvelope(1); + return buildPolicyEnvelope(0); } //utility method to build a reject all policy function buildRejectAllPolicy() { - return buildPolicyEnvelope(0); + return buildPolicyEnvelope(1); } //utility method to build a policy with a signature policy envelope @@ -1228,6 +1343,9 @@ function buildPolicyEnvelope(nOf) { var signaturePolicyEnvelope = new _configurationProto.SignaturePolicyEnvelope(); signaturePolicyEnvelope.setVersion(0); signaturePolicyEnvelope.setPolicy(signaturePolicy); +// var identity = new _mspPrProto.MSPPrincipal(); +// identity.setPrincipalClassification(_mspPrProto.MSPPrincipal.Classification.ByIdentity); +// identity.setPrincipal(Buffer.from('Admin')); signaturePolicyEnvelope.setIdentities([]); var policy = new _configurationProto.Policy(); diff --git a/hfc/lib/Orderer.js b/hfc/lib/Orderer.js index 35543e3b17..cdd25175f7 100644 --- a/hfc/lib/Orderer.js +++ b/hfc/lib/Orderer.js @@ -1,5 +1,5 @@ /* - Copyright 2016 IBM All Rights Reserved. + Copyright 2016, 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. @@ -55,59 +55,56 @@ var Orderer = class extends Remote { } /** - * Send a BroadcastMessage to the orderer service. + * Send a Broadcast message to the orderer service. * - * @param {byte} envelope - Byte data to be included in the BroadcastMessage - * @see the ./proto/atomicbroadcast/ab.proto + * @param {byte} envelope - Byte data to be included in the Broadcast + * @see the ./proto/orderer/ab.proto * @returns {Promise} A Promise for a BroadcastResponse - * @see the ./proto/atomicbroadcast/ab.proto + * @see the ./proto/orderer/ab.proto */ sendBroadcast(envelope) { - logger.debug('Orderer.sendBroadcast - start'); + logger.debug('sendBroadcast - start'); if(!envelope || envelope == '') { - logger.debug('Orderer.sendBroadcast ERROR - missing envelope'); + logger.debug('sendBroadcast ERROR - missing envelope'); var err = new Error('Missing data - Nothing to broadcast'); return Promise.reject(err); } var self = this; - // Send the endorsed proposals to the peer node (orderer) via grpc - // The rpc specification on the peer side is: - // rpc Broadcast(stream BroadcastMessage) returns (stream BroadcastResponse) {} + // Send the envelope to the orderer via grpc return new Promise(function(resolve, reject) { var broadcast = self._ordererClient.broadcast(); var all_done = false; - var broadcast_timeout = setTimeout(function(){ - logger.debug('Orderer.sendBroadcast - timed out after:%s', self._request_timeout); + logger.debug('sendBroadcast - timed out after:%s', self._request_timeout); return reject(new Error('REQUEST_TIMEOUT')); }, self._request_timeout); broadcast.on('data', function (response) { - logger.debug('Orderer.sendBroadcast - on data response: %j', response); + logger.debug('sendBroadcast - on data response: %j', response); clearTimeout(broadcast_timeout); all_done = true; if(response.status) { if (response.status === 'SUCCESS') { - logger.debug('Orderer.sendBroadcast - resolve with %s', response.status); + logger.debug('sendBroadcast - resolve with %s', response.status); return resolve(response); } else { - logger.error('Orderer.sendBroadcast - reject with %s', response.status); - return reject(response); + logger.error('sendBroadcast - reject with %s', response.status); + return reject(new Error(response)); } } else { - logger.error('Orderer.sendBroadcast ERROR - reject with invalid response from the orderer'); + logger.error('sendBroadcast ERROR - reject with invalid response from the orderer'); return reject(new Error('SYSTEM_ERROR')); } }); broadcast.on('end', function (response) { - logger.debug('Orderer.sendBroadcast - on end:'); + logger.debug('sendBroadcast - on end:'); // Removing the promise reject here as on an 'error', this case // will hit before the 'error' event, and we loose the error // information coming back to the caller @@ -115,22 +112,114 @@ var Orderer = class extends Remote { }); broadcast.on('error', function (err) { + clearTimeout(broadcast_timeout); if(all_done) { return; } if(err && err.code) { if(err.code == 14) { - logger.error('Orderer.sendBroadcast - on error: %j',err.stack ? err.stack : err); + logger.error('sendBroadcast - on error: %j',err.stack ? err.stack : err); return reject(new Error('SERVICE_UNAVAILABLE')); } } - logger.debug('Orderer.sendBroadcast - on error: %j',err.stack ? err.stack : err); + logger.debug('sendBroadcast - on error: %j',err.stack ? err.stack : err); return reject(new Error(err)); }); broadcast.write(envelope); broadcast.end(); - logger.debug('Orderer.sendBroadcast - sent message'); + logger.debug('sendBroadcast - sent message'); + }); + } + + /** + * Send a Deliver message to the orderer service. + * + * @param {byte} envelope - Byte data to be included in the Deliver + * @see the ./proto/orderer/ab.proto + * @returns {Promise} A Promise for a DeliverResponse + * @see the ./proto/orderer/ab.proto + */ + sendDeliver(envelope) { + logger.debug('sendDeliver - start'); + + if(!envelope || envelope == '') { + logger.debug('sendDeliver ERROR - missing envelope'); + var err = new Error('Missing data - Nothing to deliver'); + return Promise.reject(err); + } + + var self = this; + + // Send the seek info to the orderer via grpc + return new Promise(function(resolve, reject) { + try { + var deliver = self._ordererClient.deliver(); + var all_done = false; + var deliver_timeout = setTimeout(function(){ + logger.debug('sendDeliver - timed out after:%s', self._request_timeout); + deliver.cancel(); + all_done = true; + return reject(new Error('REQUEST_TIMEOUT')); + }, self._request_timeout); + + deliver.on('data', function (response) { + logger.debug('sendDeliver - on data response: %j', response); + clearTimeout(deliver_timeout); + deliver.cancel(); + all_done = true; + + if(response.status) { + // first check if the block is there, right now the 'status' will be 'UNKNOWN' when the block comes back + if (response.block) { + logger.debug('sendDeliver - resolve with block found - return status:%s', response.status); + return resolve(response); + } else { + logger.error('sendDeliver - reject with %s', response.status); + return reject(new Error(response)); + } + } + else { + logger.error('sendDeliver ERROR - reject with invalid response from the orderer'); + return reject(new Error('SYSTEM_ERROR')); + } + }); + + deliver.on('end', function (response) { + logger.debug('sendDeliver - on end'); + // this will hit before the 'error' + // so do not send reject yet + // return reject(response); + }); + + deliver.on('error', function (err) { + logger.debug('sendDeliver - on error'); + if(all_done) { + return; + } + else { + clearTimeout(deliver_timeout); + deliver.cancel(); + all_done = true; + } + if(err && err.code) { + if(err.code == 14) { + logger.error('sendDeliver - on error code 14: %j',err.stack ? err.stack : err); + return reject(new Error('SERVICE_UNAVAILABLE')); + } + } + logger.debug('sendDeliver - on error: %j',err.stack ? err.stack : err); + return reject(new Error(err)); + }); + + deliver.write(envelope); + deliver.end(); + logger.debug('sendDeliver - sent message'); + } + catch(error) { + logger.error('sendDeliver - system error ::' + error.stack ? error.stack : error); + return reject(new Error(error)); + } }); } diff --git a/hfc/lib/protos/common/common.proto b/hfc/lib/protos/common/common.proto index 9280821e14..401ed03af2 100644 --- a/hfc/lib/protos/common/common.proto +++ b/hfc/lib/protos/common/common.proto @@ -40,6 +40,7 @@ enum HeaderType { CONFIGURATION_ITEM = 2; // Used inside of the the reconfiguration message for signing over ConfigurationItems ENDORSER_TRANSACTION = 3; // Used by the SDK to submit endorser based transactions ORDERER_TRANSACTION = 4; // Used internally by the orderer for management + DELIVER_SEEK_INFO = 5; // Used as the type for Envelope messages submitted to instruct the Deliver API to seek } // This enum enlist indexes of the block metadata array @@ -49,6 +50,22 @@ enum BlockMetadataIndex { TRANSACTIONS_FILTER = 2; // Block metadata array poistion to store serialized bit array filter of invalid transactions } +// LastConfiguration is the encoded value for the Metadata message which is encoded in the LAST_CONFIGURATION block metadata index +message LastConfiguration { + uint64 index = 1; +} + +// Metadata is a common structure to be used to encode block metadata +message Metadata { + bytes value = 1; + repeated MetadataSignature signatures = 2; +} + +message MetadataSignature { + bytes signatureHeader = 1; // An encoded SignatureHeader + bytes signature = 2; // The signature over the concatenation of the Metadata value bytes, signatureHeader, and block header +} + message Header { ChainHeader chainHeader = 1; SignatureHeader signatureHeader = 2; diff --git a/hfc/lib/protos/common/configuration.proto b/hfc/lib/protos/common/configuration.proto index edc3983b70..4e14b78879 100644 --- a/hfc/lib/protos/common/configuration.proto +++ b/hfc/lib/protos/common/configuration.proto @@ -17,6 +17,7 @@ limitations under the License. syntax = "proto3"; import "common.proto"; +import "msp_principal.proto"; option go_package = "github.com/hyperledger/fabric/protos/common"; @@ -47,10 +48,17 @@ message ConfigurationEnvelope { repeated SignedConfigurationItem Items = 1; } +// ConfigurationTemplate is used as a serialization format to share configuration templates +// The orderer supplies a configuration template to the user to use when constructing a new +// chain creation transaction, so this is used to facilitate that. +message ConfigurationTemplate { + repeated ConfigurationItem Items = 1; +} + // This message may change slightly depending on the finalization of signature schemes for transactions message SignedConfigurationItem { bytes ConfigurationItem = 1; - repeated ConfigurationSignature Signatures = 2; // Signatures over the hash of ConfigurationItem + repeated ConfigurationSignature Signatures = 2; } message ConfigurationItem { @@ -58,7 +66,7 @@ message ConfigurationItem { Policy = 0; // Implies that the Value is a marshaled Policy message, and may be referred to by Key as a ModificationPolicy Chain = 1; // Marshaled format for this type is yet to be determined Orderer = 2; // Marshaled format for this type is yet to be determined - Fabric = 3; // Marshaled format for this type is yet to be determined + Peer = 3; // Marshaled format for this type is yet to be determined } ChainHeader Header = 1; // The header which ties this configuration to a particular chain ConfigurationType Type = 2; // The type of configuration this is. @@ -69,12 +77,10 @@ message ConfigurationItem { } message ConfigurationSignature { - bytes signatureHeader = 1; - bytes signature = 2; // Signature over the concatenation of configurationItem bytes and signatureHeader bytes + bytes signatureHeader = 1; // A marshaled SignatureHeader + bytes signature = 2; // Signature over the concatenation of configurationItem bytes and signatureHeader bytes } -// - // Policy expresses a policy which the orderer can evaluate, because there has been some desire expressed to support // multiple policy engines, this is typed as a oneof for now message Policy { @@ -91,7 +97,7 @@ message Policy { message SignaturePolicyEnvelope { int32 Version = 1; SignaturePolicy Policy = 2; - repeated bytes Identities = 3; + repeated MSPPrincipal Identities = 3; } // SignaturePolicy is a recursive message structure which defines a featherweight DSL for describing diff --git a/hfc/lib/protos/common/msp_principal.proto b/hfc/lib/protos/common/msp_principal.proto new file mode 100644 index 0000000000..2372aec622 --- /dev/null +++ b/hfc/lib/protos/common/msp_principal.proto @@ -0,0 +1,122 @@ +/* +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. +*/ + + +syntax = "proto3"; + +option go_package = "github.com/hyperledger/fabric/protos/common"; + +package common; + + +// msp_principal.proto contains proto messages defining the generalized +// MSP notion of identity called an MSPPrincipal. It is used as part of +// the chain configuration, in particular as the identity parameters to +// the configuration.proto file. This does not represent the MSP +// configuration for a chain, but is understood by MSPs + +// MSPPrincipal aims to represent an MSP-centric set of identities. +// In particular, this structure allows for definition of +// - a group of identities that are member of the same MSP +// - a group of identities that are member of the same organization unit +// in the same MSP +// - a group of identities that are administering a specific MSP +// - a specific identity +// Expressing these groups is done given two fields of the fields below +// - Classification, that defines the type of classification of identities +// in an MSP this principal would be defined on; Classification can take +// three values: +// (i) ByMSPRole: that represents a classification of identities within +// MSP based on one of the two pre-defined MSP rules, "member" and "admin" +// (ii) ByOrganizationUnit: that represents a classification of identities +// within MSP based on the organization unit an identity belongs to +// (iii)ByIdentity that denotes that MSPPrincipal is mapped to a single +// identity/certificate; this would mean that the Principal bytes +// message +message MSPPrincipal { + + enum Classification { + ByMSPRole = 0; // Represents the one of the dedicated MSP roles, the + // one of a member of MSP network, and the one of an + // administrator of an MSP network + ByOrganizationUnit = 1; // Denotes a finer grained (affiliation-based) + // groupping of entities, per MSP affiliation + // E.g., this can well be represented by an MSP's + // Organization unit + ByIdentity = 2; // Denotes a principal that consists of a single + // identity + } + + // Classification describes the way that one should process + // Principal. An Classification value of "ByOrganizationUnit" reflects + // that "Principal" contains the name of an organization this MSP + // handles. A Classification value "ByIdentity" means that + // "Principal" contains a specific identity. Default value + // denotes that Principal contains one of the groups by + // default supported by all MSPs ("admin" or "member"). + Classification PrincipalClassification = 1; + + // Principal completes the policy principal definition. For the default + // principal types, Principal can be either "Admin" or "Member". + // For the ByOrganizationUnit/ByIdentity values of Classification, + // PolicyPrincipal acquires its value from an organization unit or + // identity, respectively. + bytes Principal = 2; +} + + +// OrganizationUnit governs the organization of the Principal +// field of a policy principal when a specific organization unity members +// are to be defined within a policy principal. +message OrganizationUnit { + + // MSPIdentifier represents the identifier of the MSP this organization unit + // refers to + string MSPIdentifier = 1; + + // OrganizationUnitIdentifier defines the organization unit under the + // MSP identified with MSPIdentifier + string OrganizationUnitIdentifier = 2; + +} + +// MSPRole governs the organization of the Principal +// field of an MSPPrincipal when it aims to define one of the +// two dedicated roles within an MSP: Admin and Members. +message MSPRole { + + // MSPIdentifier represents the identifier of the MSP this principal + // refers to + string MSPIdentifier = 1; + + enum MSPRoleType { + Member = 0; // Represents an MSP Member + Admin = 1; // Represents an MSP Admin + } + + // MSPRoleType defines which of the available, pre-defined MSP-roles + // an identiy should posess inside the MSP with identifier MSPidentifier + MSPRoleType Role = 2; + +} + + +// TODO: Bring msp.SerializedIdentity from fabric/msp/identities.proto here. Reason below. +// SerializedIdentity represents an serialized version of an identity; +// this consists of an MSP-identifier this identity would correspond to +// and the bytes of the actual identity. A serialized form of +// SerializedIdentity would govern "Principal" field of a PolicyPrincipal +// of classification "ByIdentity". diff --git a/hfc/lib/protos/msp/mspconfig.proto b/hfc/lib/protos/msp/mspconfig.proto new file mode 100644 index 0000000000..575da5258e --- /dev/null +++ b/hfc/lib/protos/msp/mspconfig.proto @@ -0,0 +1,93 @@ +/* +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. +*/ + +syntax = "proto3"; + +option go_package = "github.com/hyperledger/fabric/protos/msp"; + +package msp; + +// MSPConfig collects all the configuration information for +// an MSP. The Config field should be unmarshalled in a way +// that depends on the Type +message MSPConfig { + // Type holds the type of the MSP; the default one would + // be of type FABRIC implementing an X.509 based provider + int32 Type = 1; + + // Config is MSP dependent configuration info + bytes Config = 2; +} + +// FabricMSPConfig collects all the configuration information for +// a Fabric MSP. +// Here we assume a default certificate validation policy, where +// any certificate signed by any of the listed rootCA certs would +// be considered as valid under this MSP. +// This MSP may or may not come with a signing identity. If it does, +// it can also issue signing identities. If it does not, it can only +// be used to validate and verify certificates. +message FabricMSPConfig { + // Name holds the identifier of the MSP; MSP identifier + // is chosen by the application that governs this MSP. + // For example, and assuming the default implementation of MSP, + // that is X.509-based and considers a single Issuer, + // this can refer to the Subject OU field or the Issuer OU field. + string Name = 1; + + // List of root certificates associated + repeated bytes RootCerts = 2; + + // Identity denoting the administrator of this MSP + repeated bytes Admins = 3; + + // Identity revocation list + repeated bytes RevocationList = 4; + + // SigningIdentity holds information on the signing identity + // this peer is to use, and which is to be imported by the + // MSP defined before + SigningIdentityInfo SigningIdentity = 5; +} + +// SigningIdentityInfo represents the configuration information +// related to the signing identity the peer is to use for generating +// endorsements +message SigningIdentityInfo { + // PublicSigner carries the public information of the signing + // identity. For an X.509 provider this would be represented by + // an X.509 certificate + bytes PublicSigner = 1; + + // PrivateSigner denotes a reference to the private key of the + // peer's signing identity + KeyInfo PrivateSigner = 2; +} + +// KeyInfo represents a (secret) key that is either already stored +// in the bccsp/keystore or key material to be imported to the +// bccsp key-store. In later versions it may contain also a +// keystore identifier +message KeyInfo { + // Identifier of the key inside the default keystore; this for + // the case of Software BCCSP as well as the HSM BCCSP would be + // the SKI of the key + string KeyIdentifier = 1; + + // KeyMaterial (optional) for the key to be imported; this is + // properly encoded key bytes, prefixed by the type of the key + bytes KeyMaterial = 2; +} diff --git a/hfc/lib/protos/orderer/ab.proto b/hfc/lib/protos/orderer/ab.proto index 3edcf8981c..c8e57e1851 100644 --- a/hfc/lib/protos/orderer/ab.proto +++ b/hfc/lib/protos/orderer/ab.proto @@ -55,10 +55,9 @@ message SeekInfo { BLOCK_UNTIL_READY = 0; FAIL_IF_NOT_READY = 1; } - string chainID = 1; // The chain to seek within - SeekPosition start = 2; // The position to start the deliver from - SeekPosition stop = 3; // The position to stop the deliver - SeekBehavior behavior = 4; // The behavior when a missing block is encountered + SeekPosition start = 1; // The position to start the deliver from + SeekPosition stop = 2; // The position to stop the deliver + SeekBehavior behavior = 3; // The behavior when a missing block is encountered } message DeliverResponse { @@ -72,6 +71,6 @@ service AtomicBroadcast { // broadcast receives a reply of Acknowledgement for each common.Envelope in order, indicating success or type of failure rpc Broadcast(stream common.Envelope) returns (stream BroadcastResponse) {} - // deliver first requires an update containing a seek message, then a stream of block replies is received. - rpc Deliver(stream SeekInfo) returns (stream DeliverResponse) {} + // deliver first requires an Envelope of type DELIVER_SEEK_INFO with Payload data as a mashaled SeekInfo message, then a stream of block replies is received. + rpc Deliver(stream common.Envelope) returns (stream DeliverResponse) {} } diff --git a/hfc/lib/protos/orderer/configuration.proto b/hfc/lib/protos/orderer/configuration.proto index 990c70a400..ca135468ba 100644 --- a/hfc/lib/protos/orderer/configuration.proto +++ b/hfc/lib/protos/orderer/configuration.proto @@ -37,6 +37,9 @@ message BatchSize { // Simply specified as number of messages for now, in the future // we may want to allow this to be specified by size in bytes uint32 maxMessageCount = 1; + // The byte count of the serialized messages in a batch cannot + // exceed this value. + uint32 absoluteMaxBytes = 2; } message BatchTimeout { @@ -61,6 +64,16 @@ message CreationPolicy { bytes digest = 2; } +// IngressPolicy is the name of the policy which incoming Broadcast messages are filtered against +message IngressPolicy { + string name = 1; +} + +// EgressPolicy is the name of the policy which incoming Deliver messages are filtered against +message EgressPolicy { + string name = 1; +} + message ChainCreators { // A list of policies, any of which may be specified as the chain creation // policy in a chain creation request diff --git a/hfc/lib/protos/peer/chaincode.proto b/hfc/lib/protos/peer/chaincode.proto index cc66e0cfda..3f9fd7af24 100644 --- a/hfc/lib/protos/peer/chaincode.proto +++ b/hfc/lib/protos/peer/chaincode.proto @@ -69,10 +69,9 @@ message ChaincodeSpec { ChaincodeID chaincodeID = 2; ChaincodeInput ctorMsg = 3; int32 timeout = 4; - string secureContext = 5; - ConfidentialityLevel confidentialityLevel = 6; - bytes metadata = 7; - repeated string attributes = 8; + ConfidentialityLevel confidentialityLevel = 5; + bytes metadata = 6; + repeated string attributes = 7; } // Specify the deployment of a chaincode. diff --git a/hfc/lib/protos/peer/configuration.proto b/hfc/lib/protos/peer/configuration.proto new file mode 100644 index 0000000000..7dbb462a34 --- /dev/null +++ b/hfc/lib/protos/peer/configuration.proto @@ -0,0 +1,41 @@ +/* +Copyright IBM Corp. 2017 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. +*/ + +syntax = "proto3"; + +option go_package = "github.com/hyperledger/fabric/protos/peer"; + +package protos; + +// AnchorPeers simply represents list of anchor peers which is used in ConfigurationItem +message AnchorPeers { + repeated AnchorPeer anchorPees = 1; +} + +// AnchorPeer message structure which provides information about anchor peer, it includes host name, +// port number and peer certificate. +message AnchorPeer { + + // DNS host name of the anchor peer + string Host = 1; + + // The port number + int32 Port = 2; + + // SSL certificate to be used to maintain mutual TLS + // connection with anchor peer + bytes Cert = 3; +} diff --git a/test/unit/new-chain.js b/test/unit/new-chain.js index 154e69e039..c70cebe064 100644 --- a/test/unit/new-chain.js +++ b/test/unit/new-chain.js @@ -108,20 +108,15 @@ test('\n\n** TEST ** new chain - chain.initializeChain() success', function(t) { ) .then( function(response) { - if (response.status === 'SUCCESS') { + if (response && response.block) { t.pass('Successfully created chain.'); } else { - t.fail('Failed to get correct error. Error code: ' + response); + t.fail('Failed to order the chain create. Error code: ' + response.status); } t.end(); }, function(err) { - if (err.status === 'BAD_REQUEST') { - t.fail('Failed to create chain.' + err); - } - else { - t.fail('Failed to get error status. Error code: ' + err); - } + t.fail('Failed to get the genesis block back due to error: ' + err.stack ? err.stack : err); t.end(); } ) @@ -163,20 +158,11 @@ test('\n\n** TEST ** new chain - chain.initializeChain() fail due to already exi ) .then( function(response) { - if (response.status === 'SUCCESS') { - t.fail('Failed, the chain was created again.'); - } else { - t.fail('Failed to get correct error. Response code: ' + response); - } + t.fail('Failed to get correct error. Response code: ' + response); t.end(); }, function(err) { - if (err.status === 'BAD_REQUEST') { - t.pass('Received the correct error message.'); - } - else { - t.fail('Failed to get correct error. Error code: ' + err); - } + t.pass('Got back failure error. Error code: ' + err); t.end(); } )