From c0913d297a42ca608076693c3580706524841491 Mon Sep 17 00:00:00 2001 From: Bret Harrison Date: Fri, 1 Jun 2018 09:56:26 -0400 Subject: [PATCH] FAB-8806 NodeSDK - Discovery part 2 Add discovery to the initialization of the channel. Refactor NodeSDK to allow for a plugin based endorsement. Continue with the code clean up. Change-Id: I6283c94eb1519777a8eebb2456a748588fb90294 Signed-off-by: Bret Harrison --- docs/tutorials/network-config.md | 2 +- fabric-client/config/default.json | 6 +- fabric-client/lib/Channel.js | 798 ++++++++++++------ fabric-client/lib/ChannelEventHub.js | 2 +- fabric-client/lib/Client.js | 159 ++-- fabric-client/lib/Constants.js | 4 +- fabric-client/lib/EventHub.js | 2 +- fabric-client/lib/Remote.js | 11 +- fabric-client/lib/api.js | 53 +- fabric-client/lib/client-utils.js | 4 +- .../lib/impl/DiscoveryEndorsementHandler.js | 112 +++ fabric-client/lib/impl/NetworkConfig_1_0.js | 96 ++- package-lock.json | 12 +- test/fixtures/docker-compose.yaml | 14 +- test/integration/channel-event-hub.js | 5 +- test/integration/configtxlator.js | 3 +- test/integration/create-configtx-channel.js | 3 +- test/integration/e2e/create-channel.js | 6 +- test/integration/e2e/e2eUtils.js | 22 +- test/integration/e2e/join-channel.js | 6 +- test/integration/events.js | 5 +- test/integration/get-config.js | 1 + test/integration/install.js | 1 + test/integration/instantiate.js | 1 + test/integration/invoke.js | 6 +- test/integration/only-admin.js | 57 +- test/integration/orderer-channel-tests.js | 4 +- test/integration/perf/orderer.js | 6 +- test/integration/perf/peer.js | 3 +- test/integration/query.js | 1 + test/integration/upgrade.js | 5 +- test/unit/channel-event-hub.js | 23 +- test/unit/channel.js | 197 +++-- test/unit/client.js | 29 +- test/unit/endorser-handler.js | 163 ++++ test/unit/remote.js | 97 +-- 36 files changed, 1306 insertions(+), 613 deletions(-) create mode 100644 fabric-client/lib/impl/DiscoveryEndorsementHandler.js create mode 100644 test/unit/endorser-handler.js diff --git a/docs/tutorials/network-config.md b/docs/tutorials/network-config.md index 4a784764aa..16912ed85f 100644 --- a/docs/tutorials/network-config.md +++ b/docs/tutorials/network-config.md @@ -188,7 +188,7 @@ var fabric_ca_client = client.getCertificateAuthority(); ``` Then once we have a fabric-ca-client, we will be able to register new users. We could also use the fabric-ca-client to enroll users and make a few calls to the fabric client to create a user object and then assign that user object to the fabric client, but it will be much easier to just use the convenience method of the fabric client instance. Notice how we have to use the 'admin' user object returned from the client.setUserContext() to do the register. The admin user object has the credentials needed to the register. Then notice we called the same setUserContext method as we did with the admin above, this will have the fabric client object assigned with the 'user1' user context thus providing the credentials to interact with the fabric network. Note that the setUserContext also stores the user context which contains the signed certificate from the certificate authority and newly created public and private keys of the now enrolled user. ``` -ca.fabric_ca_client({enrollmentID: 'user1', affiliation: 'org1'}, admin) +fabric_ca_client.register({enrollmentID: 'user1', affiliation: 'org1'}, admin) .then((secret) => { return client.setUserContext({username:'user1', password:secret}); }).then((user)=> { diff --git a/fabric-client/config/default.json b/fabric-client/config/default.json index b200ccf2fd..08ff952ef1 100644 --- a/fabric-client/config/default.json +++ b/fabric-client/config/default.json @@ -22,5 +22,9 @@ "network-config-schema" : { "1.0": "./impl/NetworkConfig_1_0.js" }, - "grpc-wait-for-ready-timeout": 3000 + "grpc-wait-for-ready-timeout": 3000, + "initialize-with-discovery": false, + "discovery-cache-life": 300000, + "discovery-protocol": "grpcs", + "endorsement-handler-path": "fabric-client/lib/impl/DiscoveryEndorsementHandler.js" } diff --git a/fabric-client/lib/Channel.js b/fabric-client/lib/Channel.js index 7f3b05bd61..6ccbc81cf0 100755 --- a/fabric-client/lib/Channel.js +++ b/fabric-client/lib/Channel.js @@ -6,8 +6,8 @@ 'use strict'; -const utils = require('./utils.js'); -const clientUtils = require('./client-utils.js'); +const sdk_utils = require('./utils.js'); +const client_utils = require('./client-utils.js'); const util = require('util'); const path = require('path'); const Peer = require('./Peer.js'); @@ -16,7 +16,7 @@ const Orderer = require('./Orderer.js'); const BlockDecoder = require('./BlockDecoder.js'); const TransactionID = require('./TransactionID.js'); const grpc = require('grpc'); -const logger = utils.getLogger('Channel.js'); +const logger = sdk_utils.getLogger('Channel.js'); const MSPManager = require('./msp/msp-manager.js'); const Policy = require('./Policy.js'); const Constants = require('./Constants.js'); @@ -88,7 +88,7 @@ const Channel = class { if (typeof name !== 'string') { throw new Error('Failed to create Channel. channel name should be a string'); } - const channelNameRegxChecker = utils.getConfigSetting('channel-name-regx-checker'); + const channelNameRegxChecker = sdk_utils.getConfigSetting('channel-name-regx-checker'); if (channelNameRegxChecker) { const { pattern, flags } = channelNameRegxChecker; const namePattern = new RegExp(pattern ? pattern : '', flags ? flags : ''); @@ -107,6 +107,20 @@ const Channel = class { this._kafka_brokers = []; this._clientContext = clientContext; this._msp_manager = new MSPManager(); + this._chaincodes = new Map(); + this._discovery_results = null; + this._last_discover_timestamp = null; + this._discovery_peer = null; + this._use_discovery = sdk_utils.getConfigSetting('initialize-with-discovery', true); + + // setup the endorsement handler + this._endorsement_handler = null; + const handler_path = sdk_utils.getConfigSetting('endorsement-handler-path'); + if(handler_path) { + this._endorsement_handler = require(handler_path).create(this); + this._endorsement_handler.initialize(); + } + logger.debug('Constructed Channel instance: name - %s, network mode: %s', this._name, !this._devMode); } @@ -120,6 +134,24 @@ const Channel = class { this._orderers.forEach((orderer) => {orderer.close();}); } + /** + * @typedef {Object} InitializeRequest + * @property {string | Peer} target - Optional. The target peer to be used + * to make the initialization requests for configuration information. + * Default is to use the first {@link Peer} assigned to this channel. + * @property {boolean} discover - Optional. Use the discovery service on the + * the target peer to load the configuration and network information. + * Default is true. When set to false the target peer will use + * base Peer query to load only the configuration information. + * @property {string} endorsementHandler - Optional. The path to a custom + * endorsement handler implementing {@link EndorsementHandler}. + * @property {boolean} asLocalhost - Optional. Convert discovered host addresses + * to be 'localhost'. Will be needed when running a docker composed + * fabric network on the local system. + * @property {byte[]} configUpdate - Optional. To initialize this channel with + * a serialized ConfigUpdate protobuf object. + */ + /** * 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 @@ -132,24 +164,185 @@ const Channel = class { * 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" + * @param {byte[] | InitializeRequest} request - Optional. a {@link InitializeRequest} + * or 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); + initialize(request) { + const method = 'initialize'; + logger.debug('%s - start', method); + let target_peer = this._discovery_peer; + + this._discovery_results = null; + this._last_discover_timestamp = null; + let use_discovery = this._use_discovery; + + if (request) { + if(request.configUpate) { + this.loadConfigUpdate(request.configUpate); + + return Promise.resolve(true); + } else { + if(typeof request.discover !== 'undefined'){ + if(typeof request.discover === 'boolean') { + use_discovery = request.discover; + } else { + throw new Error('Reqauest parameter "discover" must be boolean'); + } + } + if(request.target) { + target_peer = request.target; + } + if(request.endorsementHandler) { + this._endorsement_handler = require(request.endorsementHandler).create(this); + this._endorsement_handler.initialize(); + } + } } - var self = this; - return this.getChannelConfig().then((config_envelope) => { - logger.debug('initialize - got config envelope from getChannelConfig :: %j', config_envelope); - const config_items = self.loadConfigEnvelope(config_envelope); - return Promise.resolve(config_items); - }).catch((error) => { - logger.error('initialize - system error ::' + error.stack ? error.stack : error); - return Promise.reject(new Error(error)); - }); + this._last_refresh_request = Object.assign({}, request); + const self = this; + if(use_discovery) { + target_peer = this._getTargetForDiscovery(target_peer); + if(!target_peer) { + throw new Error('No target provided for discovery services'); + } + const chaincodes = {}; + const discover_request = { + target: target_peer, + config: true + }; + + return self._discover(discover_request).then((discover_results) => { + // chaincode names are in each peer + if(discover_results && discover_results.peers_by_org) { + for(let org_name in discover_results.peers_by_org) { + const org = discover_results.peers_by_org[org_name]; + for(let peer_index in org.peers) { + const peer = org.peers[peer_index]; + for(let chaincode_index in peer.chaincodes) { + const chaincode = peer.chaincodes[chaincode_index]; + chaincodes[chaincode.name] = chaincode.version; + } + } + } + } + + const discover_request = { + target: target_peer, + config: true, + chaincodes: Object.keys(chaincodes), + endpoint_names: true, + initialize_msps: true + }; + + return self._discover(discover_request); + }).then((discover_results) => { + if(discover_results) { + if(discover_results.msps) { + logger.debug('%s - build msps', method); + for(let msp_name in discover_results.msps) { + const msp = discover_results.msps[msp_name]; + const config = { + rootCerts: msp.rootCerts, + intermediateCerts: msp.intermediateCerts, + admins: msp.admins, + cryptoSuite: self._clientContext._crytoSuite, + id: msp.id, + orgs: msp.orgs, + tls_root_certs: msp.tls_root_certs, + tls_intermediate_certs: msp.tls_intermediate_certs + }; + self._msp_manager.addMSP(config); + } + } + + logger.debug('%s - build target names', method); + // add orderers names + if(discover_results.orderers){ + for(let msp_id in discover_results.orderers) { + logger.debug('%s - orderers msp:%s', method, msp_id); + const endpoints = discover_results.orderers[msp_id].endpoints; + for(const endpoint of endpoints) { + logger.debug('%s - orderer mspid:%s endpoint:%s:%s', method, msp_id, endpoint.host, endpoint.port); + endpoint.name = self._buildOrdererName( + msp_id, + endpoint.host, + endpoint.port, + discover_results.msps, + request + ); + } + } + } + // add peer names + if(discover_results.peers_by_org){ + for(let msp_id in discover_results.peers_by_org) { + logger.debug('%s - peers msp:%s', method, msp_id); + const peers = discover_results.peers_by_org[msp_id].peers; + for(let index in peers) { + const peer = peers[index]; + peer.name = self._buildPeerName( + peer.endpoint, + peer.mspid, + discover_results.msps, + request + ); + logger.debug('%s - peer:%j', method, peer); + } + } + } + //add peer names for setEndorsements + if(discover_results.endorsement_targets) { + for(let chaincode_name in discover_results.endorsement_targets){ + const chaincode = discover_results.endorsement_targets[chaincode_name]; + for(let group_name in chaincode.groups) { + logger.debug('%s - endorsing peer group %s', method, group_name); + const peers = chaincode.groups[group_name].peers; + for(let index in peers) { + const peer = peers[index]; + peer.name = self._buildPeerName( + peer.endpoint, + peer.mspid, + discover_results.msps, + request + ); + logger.debug('%s - peer:%j', method, peer); + } + } + } + } + + discover_results.timestamp = Date.now(); + self._discovery_results = discover_results; + self._discovery_peer = target_peer; + self._last_discover_timestamp = discover_results.timestamp; + + return Promise.resolve(discover_results); + } else { + + return Promise.reject(new Error('initialization failed')); + } + }).catch((error) => { + + return Promise.reject(error); + }); + } else { + target_peer = this._getFirstAvailableTarget(target_peer); + if(!target_peer) { + throw new Error('No target provided for initialization'); + } + return this.getChannelConfig(target_peer).then((config_envelope) => { + logger.debug('initialize - got config envelope from getChannelConfig :: %j', config_envelope); + const config_items = self.loadConfigEnvelope(config_envelope); + + return Promise.resolve(config_items); + }).catch((error) => { + logger.error('initialize - system error ::' + error.stack ? error.stack : error); + + return Promise.reject(new Error(error)); + }); + } } /** @@ -160,25 +353,70 @@ const Channel = class { return this._name; } + /** + * @typedef {Object} ChaincodeInfo Object used internally to store chaincodes + * names and versions found on this channel. + * @property {string} name - The name of the chaincode + * @property {string} version - The version of the chaincode + */ + + /** + * Return the discovery results. + * Discovery results are only available if this channel has been initialized. + * If the results are too old, they will be refreshed + */ + getDiscoveryResults() { + if(this._discovery_results) { + const allowed_age = sdk_utils.getConfigSetting('discovery-cache-age', 300000); //default is 5 minutes + const now = Date.now(); + if(now - this._last_discover_timestamp > allowed_age) { + return this.refresh(); + } else { + return Promise.resolve(this._discovery_results); + } + } else { + // not working with discovery or we have not been initialized + return Promise.reject(new Error('This Channel has not been initialized or not initialized with discovery support')); + } + } + + /** + * Refresh the channel's configuration. The MSP configurations, peers, + * orderers, and endorsement plans will be queired from the peer using + * the Discover Service. The queries will be made to the peer used previously + * for discovery if the 'target' parameter is not provided. + * + * @param {DiscoveryRequest} request - {@link DiscoveryRequest} + * @return {DiscoveryResults} - The results of refreshing + */ + refresh(request) { + if(!request) { + request = this._last_refresh_request; + } + + return this.initialize(request); + } + /** * Get organization identifiers from the MSP's for this channel * @returns {string[]} Array of MSP identifiers representing the channel's * participating organizations */ getOrganizations() { - logger.debug('getOrganizationUnits - start'); + const method = 'getOrganizations'; + logger.debug('%s - start', method); const msps = this._msp_manager.getMSPs(); const orgs = []; if (msps) { - var keys = Object.keys(msps); - for (var key in keys) { - var msp = msps[keys[key]]; - var msp_org = { id: msp.getId() }; - logger.debug('getOrganizationUnits - found %j', msp_org); + const keys = Object.keys(msps); + for (let key in keys) { + const msp = msps[keys[key]]; + const msp_org = { id: msp.getId() }; + logger.debug('%s - found %j', method, msp_org); orgs.push(msp_org); } } - logger.debug('getOrganizationUnits - orgs::%j', orgs); + logger.debug('%s - orgs::%j', method, orgs); return orgs; } @@ -226,7 +464,7 @@ const Channel = class { this.removePeer(check); } else { - var error = new Error(); + const error = new Error(); error.name = 'DuplicatePeer'; error.message = 'Peer ' + name + ' already exists'; logger.error(error.message); @@ -315,7 +553,7 @@ const Channel = class { if(replace) { this.removeOrderer(check); } else { - var error = new Error(); + const error = new Error(); error.name = 'DuplicateOrderer'; error.message = 'Orderer ' + name + ' already exists'; logger.error(error.message); @@ -512,17 +750,17 @@ const Channel = class { seekInfo.setBehavior(_abProto.SeekInfo.SeekBehavior.BLOCK_UNTIL_READY); // build the header for use with the seekInfo payload - const seekInfoHeader = clientUtils.buildChannelHeader( + const seekInfoHeader = client_utils.buildChannelHeader( _commonProto.HeaderType.DELIVER_SEEK_INFO, this._name, tx_id.getTransactionID(), this._initial_epoch, null, - clientUtils.buildCurrentTimestamp(), - orderer.getClientCertHash() + client_utils.buildCurrentTimestamp(), + this._clientContext.getClientCertHash() ); - const seekHeader = clientUtils.buildHeader(signer, seekInfoHeader, tx_id.getNonce()); + const seekHeader = client_utils.buildHeader(signer, seekInfoHeader, tx_id.getNonce()); const seekPayload = new _commonProto.Payload(); seekPayload.setHeader(seekHeader); seekPayload.setData(seekInfo.toBuffer()); @@ -532,7 +770,7 @@ const Channel = class { const signature = Buffer.from(sig); // building manually or will get protobuf errors on send - var envelope = { + const envelope = { signature: signature, payload: seekPayloadBytes }; @@ -547,7 +785,18 @@ const Channel = class { * will be asked to discovery information about this channel. * Default is the first peer assigned to this channel that has the * 'discovery' role. - * @property {chaincode} else - Required. Description + * @property {string[]} chaincodes - Optional. An Array of chaincodes to get + * the endorsement target selection layouts + * @property {boolean} endpoint_names - Optional. To indicate if the names of + * endpoints should be added to the return attributes of an end point. + * Requesting the name will require that the endpoints (Peer and Orderer) + * be added to this channel instance with the returned name. + * @property {boolean} initialize_msps - Optional. To indicate that the MSPs + * discovered in this query should be added to this channel instance. + * @property {boolean} config - Optional. To indicate that the channel configuration + * should be included in the discovery query. + * @property {boolean} local - Optonal. To indicate that the local endpoints + * should be included in the discovery query. */ /** @@ -571,8 +820,9 @@ const Channel = class { const authentication = new _discoveryProto.AuthInfo(); authentication.setClientIdentity(signer.serialize()); - if(target_peer.getClientCertHash()) { - authentication.setClientTlsCertHash(target_peer.getClientCertHash()); + const cert_hash = this._clientContext.getClientCertHash(); + if(cert_hash) { + authentication.setClientTlsCertHash(cert_hash); } discovery_request.setAuthentication(authentication); @@ -580,48 +830,53 @@ const Channel = class { // grpc object const queries = []; - // always add ConfigQuery - { + // if doing local it will be index 0 of the results + if(request.local) { const query = new _discoveryProto.Query(); queries.push(query); + + const local_peers = new _discoveryProto.LocalPeerQuery(); + query.setLocalPeers(local_peers); + logger.debug('%s - adding local peers query', method); + } + + if(request.config){ + let query = new _discoveryProto.Query(); + queries.push(query); query.setChannel(this.getName()); const config_query = new _discoveryProto.ConfigQuery(); query.setConfigQuery(config_query); logger.debug('%s - adding config query', method); - } - // always add PeerMembershipQuery - { - const query = new _discoveryProto.Query(); + query = new _discoveryProto.Query(); queries.push(query); query.setChannel(this.getName()); const peer_query = new _discoveryProto.PeerMembershipQuery(); query.setPeerQuery(peer_query); - logger.debug('%s - adding peer query', method); + logger.debug('%s - adding channel peers query', method); } - // add a chaincode query to get endorsement layouts if chaincodeId set - if(request.chaincodeId) { + // add a chaincode query to get endorsement layouts if chaincodeId is set + if(request.chaincodes && request.chaincodes.length > 0) { const query = new _discoveryProto.Query(); queries.push(query); query.setChannel(this.getName()); - const chaincode_call = new _discoveryProto.ChaincodeCall(); - chaincode_call.setName(request.chaincodeId); - // TODO - handle collection names - // chaincode_call.setCollectionNames(request.collectionNames); - - const interest1 = new _discoveryProto.ChaincodeInterest(); - const chaincodes = []; - chaincodes.push(chaincode_call); - interest1.setChaincodes(chaincodes); + const interests = []; + for(let index in request.chaincodes) { + const chaincodes = []; + const chaincode_name = request.chaincodes[index]; + const chaincode_call = new _discoveryProto.ChaincodeCall(); + chaincode_call.setName(chaincode_name); + chaincodes.push(chaincode_call); + const interest = new _discoveryProto.ChaincodeInterest(); + interest.setChaincodes(chaincodes); + interests.push(interest); + } const cc_query = new _discoveryProto.ChaincodeQuery(); - const interests = []; - interests.push(interest1); - // be sure to set the array after completely building it cc_query.setInterests(interests); query.setCcQuery(cc_query); logger.debug('%s - adding chaincode query', method); @@ -655,10 +910,16 @@ const Channel = class { } else { logger.debug('%s - process results', method); if (result.config_result) { - results.config = self._processDiscoveryConfigResults(result.config_result); + const config = self._processDiscoveryConfigResults(result.config_result); + results.msps = config.msps; + results.orderers = config.orderers; } if (result.members) { - results.peers_by_org = self._processDiscoveryMembershipResults(result.members); + if(request.local && index == 0) { + results.local_peers = self._processDiscoveryMembershipResults(result.members); + } else { + results.peers_by_org = self._processDiscoveryMembershipResults(result.members); + } } if (result.cc_query_res) { results.endorsement_targets = self._processDiscoveryChaincodeResults(result.cc_query_res); @@ -668,70 +929,14 @@ const Channel = class { } if(error_msg) { + return Promise.reject('Channel:' + self.getName() + ' Discovery error:' + error_msg); } else { - if(request.target_names) { - logger.debug('%s - return target names', method); - // add orderers names - if(results.config && results.config.orderers){ - for(let msp_id in results.config.orderers) { - logger.debug('%s - orderers msp:%s', method, msp_id); - const endpoints = results.config.orderers[msp_id].endpoints; - for(let index in endpoints) { - const endpoint = endpoints[index]; - logger.debug('%s - orderer mspid:%s endpoint:%s:%s', method, msp_id, endpoint.host, endpoint.port); - endpoint.name = self._buildOrdererName( - msp_id, - endpoint.host, - endpoint.port, - results.config.msps, - request - ); - } - } - } - // add peer names - if(results.peers_by_org){ - for(let msp_id in results.peers_by_org) { - logger.debug('%s - peers msp:%s', method, msp_id); - const peers = results.peers_by_org[msp_id].peers; - for(let index in peers) { - const peer = peers[index]; - peer.name = self._buildPeerName( - peer.endpoint, - peer.mspid, - results.config.msps, - request - ); - logger.debug('%s - peer:%j', method, peer); - } - } - } - //add peer names for setEndorsements - if(results.endorsement_targets) { - for(let chaincode_name in results.endorsement_targets){ - const chaincode = results.endorsement_targets[chaincode_name]; - for(let group_name in chaincode.groups) { - logger.debug('%s - endorsing peer group %s', method, group_name); - const peers = chaincode.groups[group_name].peers; - for(let index in peers) { - const peer = peers[index]; - peer.name = self._buildPeerName( - peer.endpoint, - peer.mspid, - results.config.msps, - request - ); - logger.debug('%s - peer:%j', method, peer); - } - } - } - } - } return Promise.resolve(results); } } else { + return Promise.reject(new Error('Discovery has failed to return results')); } }); @@ -763,9 +968,8 @@ const Channel = class { for(let index in q_endors_desc.layouts){ const q_layout = q_endors_desc.layouts[index]; const layout = {}; - layout.quantities_by_group = {}; for(let group_name in q_layout.quantities_by_group) { - layout.quantities_by_group[group_name] = q_layout.quantities_by_group[group_name]; + layout[group_name] = q_layout.quantities_by_group[group_name]; } logger.debug('%s - layout :%j', method, layout); endorsement_descriptor.layouts.push(layout); @@ -790,11 +994,11 @@ const Channel = class { const msp_config = { id: id, orgs : q_msp.organizational_unit_identifiers, - rootCerts: utils.convertBytetoString(q_msp.root_certs), - intermediateCerts: utils.convertBytetoString(q_msp.intermediate_certs), - admins: utils.convertBytetoString(q_msp.admins), - tls_root_certs: utils.convertBytetoString(q_msp.tls_root_certs), - tls_intermediate_certs: utils.convertBytetoString(q_msp.tls_intermediate_certs) + rootCerts: sdk_utils.convertBytetoString(q_msp.root_certs), + intermediateCerts: sdk_utils.convertBytetoString(q_msp.intermediate_certs), + admins: sdk_utils.convertBytetoString(q_msp.admins), + tls_root_certs: sdk_utils.convertBytetoString(q_msp.tls_root_certs), + tls_intermediate_certs: sdk_utils.convertBytetoString(q_msp.tls_intermediate_certs) }; config.msps[id] = msp_config; } @@ -849,18 +1053,20 @@ const Channel = class { logger.debug('%s - found peer :%s', method, peer.endpoint); // STATE - const message_s = _gossipProto.GossipMessage.decode(q_peer.state_info.payload); - peer.ledger_height = message_s.state_info.properties.ledger_height; - logger.debug('%s - found ledger_height :%s', method, peer.ledger_height); - peer.chaincodes = []; - for(let index in message_s.state_info.properties.chaincodes) { - const q_chaincode = message_s.state_info.properties.chaincodes[index]; - const chaincode = {}; - chaincode.name = q_chaincode.getName(); - chaincode.version = q_chaincode.getVersion(); - //TODO metadata ? - logger.debug('%s - found chaincode :%j', method, chaincode); - peer.chaincodes.push(chaincode); + if(q_peer.state_info) { + const message_s = _gossipProto.GossipMessage.decode(q_peer.state_info.payload); + peer.ledger_height = message_s.state_info.properties.ledger_height; + logger.debug('%s - found ledger_height :%s', method, peer.ledger_height); + peer.chaincodes = []; + for(let index in message_s.state_info.properties.chaincodes) { + const q_chaincode = message_s.state_info.properties.chaincodes[index]; + const chaincode = {}; + chaincode.name = q_chaincode.getName(); + chaincode.version = q_chaincode.getVersion(); + //TODO metadata ? + logger.debug('%s - found chaincode :%j', method, chaincode); + peer.chaincodes.push(chaincode); + } } //all done with this peer @@ -874,7 +1080,7 @@ const Channel = class { const method = '_buildOrdererName'; logger.debug('%s - start', method); - const name = 'grpcs://' + host + ':' + port; + const name = host + ':' + port; const url = this._buildUrl(host, port, request); let found = null; this._orderers.forEach((orderer)=>{if(orderer.getUrl() === url){ @@ -898,7 +1104,7 @@ const Channel = class { const method = '_buildPeerName'; logger.debug('%s - start', method); - const name = 'grpcs://' + endpoint; + const name = endpoint; const host_port = endpoint.split(':'); const url = this._buildUrl(host_port[0], host_port[1], request); let found = null; @@ -925,10 +1131,13 @@ const Channel = class { let t_hostname = hostname; - if(request && request.hostname) { - t_hostname = request.hostname; + // endpoints may be running in containers on the local system + if(request && request.asLocalhost) { + t_hostname = 'localhost'; } - const url = 'grpcs://' + t_hostname + ':' + port; + + const protocol = sdk_utils.getConfigSetting('discovery-protocol', 'grpcs'); + const url = protocol + '://' + t_hostname + ':' + port; return url; } @@ -1049,21 +1258,21 @@ const Channel = class { chaincodeSpec.setChaincodeId(chaincodeID); chaincodeSpec.setInput(chaincodeInput); - const channelHeader = clientUtils.buildChannelHeader( + const channelHeader = client_utils.buildChannelHeader( _commonProto.HeaderType.ENDORSER_TRANSACTION, '', request.txId.getTransactionID(), null, //no epoch Constants.CSCC, - clientUtils.buildCurrentTimestamp(), - targets[0].getClientCertHash() + client_utils.buildCurrentTimestamp(), + this._clientContext.getClientCertHash() ); - const header = clientUtils.buildHeader(signer, channelHeader, request.txId.getNonce()); - const proposal = clientUtils.buildProposal(chaincodeSpec, header); - const signed_proposal = clientUtils.signProposal(signer, proposal); + const header = client_utils.buildHeader(signer, channelHeader, request.txId.getNonce()); + const proposal = client_utils.buildProposal(chaincodeSpec, header); + const signed_proposal = client_utils.signProposal(signer, proposal); - return clientUtils.sendPeersProposal(targets, signed_proposal, timeout) + return client_utils.sendPeersProposal(targets, signed_proposal, timeout) .then( function (responses) { return Promise.resolve(responses); @@ -1081,7 +1290,7 @@ const Channel = class { * request. * @returns {Promise} A Promise for a {@link ConfigEnvelope} object containing the configuration items. */ - getChannelConfig(target) { + getChannelConfig(target, timeout) { const method = 'getChannelConfig'; logger.debug('%s - start for channel %s', method, this._name); const targets = this._getTargetForQuery(target); @@ -1095,11 +1304,13 @@ const Channel = class { fcn: 'GetConfigBlock', args: [this._name] }; - return this.sendTransactionProposal(request) + request.targets = this._getTargets(request.targets, Constants.NetworkConfig.ENDORSING_PEER_ROLE); + + return Channel.sendTransactionProposal(request, this._name, this._clientContext, timeout) .then( function (results) { const responses = results[0]; - // var proposal = results[1]; + // const proposal = results[1]; logger.debug('%s - results received', method); if (responses && Array.isArray(responses)) { const response = responses[0]; @@ -1162,17 +1373,17 @@ const Channel = class { seekInfo.setBehavior(_abProto.SeekInfo.SeekBehavior.BLOCK_UNTIL_READY); // build the header for use with the seekInfo payload - const seekInfoHeader = clientUtils.buildChannelHeader( + const seekInfoHeader = client_utils.buildChannelHeader( _commonProto.HeaderType.DELIVER_SEEK_INFO, self._name, txId.getTransactionID(), self._initial_epoch, null, - clientUtils.buildCurrentTimestamp(), - orderer.getClientCertHash() + client_utils.buildCurrentTimestamp(), + this._clientContext.getClientCertHash() ); - const seekHeader = clientUtils.buildHeader(signer, seekInfoHeader, txId.getNonce()); + const seekHeader = client_utils.buildHeader(signer, seekInfoHeader, txId.getNonce()); const seekPayload = new _commonProto.Payload(); seekPayload.setHeader(seekHeader); seekPayload.setData(seekInfo.toBuffer()); @@ -1229,17 +1440,17 @@ const Channel = class { //logger.debug('initializeChannel - seekInfo ::' + JSON.stringify(seekInfo)); // build the header for use with the seekInfo payload - const seekInfoHeader = clientUtils.buildChannelHeader( + const seekInfoHeader = client_utils.buildChannelHeader( _commonProto.HeaderType.DELIVER_SEEK_INFO, self._name, txId.getTransactionID(), self._initial_epoch, null, - clientUtils.buildCurrentTimestamp(), - orderer.getClientCertHash() + client_utils.buildCurrentTimestamp(), + self._clientContext.getClientCertHash() ); - const seekHeader = clientUtils.buildHeader(signer, seekInfoHeader, txId.getNonce()); + const seekHeader = client_utils.buildHeader(signer, seekInfoHeader, txId.getNonce()); const seekPayload = new _commonProto.Payload(); seekPayload.setHeader(seekHeader); seekPayload.setData(seekInfo.toBuffer()); @@ -1393,7 +1604,8 @@ const Channel = class { fcn: 'GetChainInfo', args: [this._name] }; - return this.sendTransactionProposal(request) + + return Channel.sendTransactionProposal(request, this._name, this._clientContext, null) .then( function (results) { const responses = results[0]; @@ -1407,13 +1619,14 @@ const Channel = class { if (response instanceof Error) { return Promise.reject(response); } - if (response.response) { + if (response.response && response.response.status && response.response.status == 200) { logger.debug('queryInfo - response status %d:', response.response.status); const chain_info = _ledgerProto.BlockchainInfo.decode(response.response.payload); return Promise.resolve(chain_info); + } else if (response.response && response.response.status) { + // no idea what we have, lets fail it and send it back + return Promise.reject(new Error(response.response.message)); } - // no idea what we have, lets fail it and send it back - return Promise.reject(response); } return Promise.reject(new Error('Payload results are missing from the query channel info')); } @@ -1452,7 +1665,8 @@ const Channel = class { fcn: 'GetBlockByTxID', args }; - return this.sendTransactionProposal(request) + + return Channel.sendTransactionProposal(request, this._name, this._clientContext, null) .then((results) => { const responses = results[0]; if (responses && Array.isArray(responses)) { @@ -1465,14 +1679,15 @@ const Channel = class { if (response instanceof Error) { return Promise.reject(response); } - if (response.response) { + if (response.response && response.response.status && response.response.status == 200) { logger.debug('queryBlockByTxID - response status %d:', response.response.status); const block = BlockDecoder.decode(response.response.payload); logger.debug('queryBlockByTxID - looking at block :: %s', block.header.number); return Promise.resolve(block); + } else if (response.response && response.response.status) { + // no idea what we have, lets fail it and send it back + return Promise.reject(new Error(response.response.message)); } - // no idea what we have, lets fail it and send it back - return Promise.reject(response); } return Promise.reject(new Error('Payload results are missing from the query')); }).catch((err) => { @@ -1508,10 +1723,11 @@ const Channel = class { args: [this._name], argbytes: blockHash }; - return this.sendTransactionProposal(request) + + return Channel.sendTransactionProposal(request, this._name, this._clientContext, null) .then( function (results) { - var responses = results[0]; + const responses = results[0]; logger.debug('queryBlockByHash - got response'); if (responses && Array.isArray(responses)) { //will only be one response as we are only querying the primary peer @@ -1522,14 +1738,15 @@ const Channel = class { if (response instanceof Error) { return Promise.reject(response); } - if (response.response) { + if (response.response && response.response.status && response.response.status == 200) { logger.debug('queryBlockByHash - response status %d:', response.response.status); - var block = BlockDecoder.decode(response.response.payload); + const block = BlockDecoder.decode(response.response.payload); logger.debug('queryBlockByHash - looking at block :: %s', block.header.number); return Promise.resolve(block); + } else if (response.response && response.response.status) { + // no idea what we have, lets fail it and send it back + return Promise.reject(new Error(response.response.message)); } - // no idea what we have, lets fail it and send it back - return Promise.reject(response); } return Promise.reject(new Error('Payload results are missing from the query')); } @@ -1553,7 +1770,7 @@ const Channel = class { */ queryBlock(blockNumber, target, useAdmin) { logger.debug('queryBlock - start blockNumber %s', blockNumber); - var block_number = null; + let block_number = null; if (Number.isInteger(blockNumber) && blockNumber >= 0) { block_number = blockNumber.toString(); } else { @@ -1570,10 +1787,11 @@ const Channel = class { fcn: 'GetBlockByNumber', args: [this._name, block_number] }; - return this.sendTransactionProposal(request) + + return Channel.sendTransactionProposal(request, this._name, this._clientContext, null) .then( function (results) { - var responses = results[0]; + const responses = results[0]; logger.debug('queryBlock - got response'); if (responses && Array.isArray(responses)) { //will only be one response as we are only querying the primary peer @@ -1584,14 +1802,15 @@ const Channel = class { if (response instanceof Error) { return Promise.reject(response); } - if (response.response) { + if (response.response && response.response.status && response.response.status == 200) { logger.debug('queryBlock - response status %d:', response.response.status); - var block = BlockDecoder.decode(response.response.payload); + const block = BlockDecoder.decode(response.response.payload); logger.debug('queryBlock - looking at block :: %s', block.header.number); return Promise.resolve(block); + } else if (response.response && response.response.status) { + // no idea what we have, lets fail it and send it back + return Promise.reject(new Error(response.response.message)); } - // no idea what we have, lets fail it and send it back - return Promise.reject(response); } return Promise.reject(new Error('Payload results are missing from the query')); } @@ -1631,10 +1850,11 @@ const Channel = class { fcn: 'GetTransactionByID', args: [this._name, tx_id] }; - return this.sendTransactionProposal(request) + + return Channel.sendTransactionProposal(request, this._name, this._clientContext, null) .then( function (results) { - var responses = results[0]; + const responses = results[0]; logger.debug('queryTransaction - got response'); if (responses && Array.isArray(responses)) { //will only be one response as we are only querying the primary peer @@ -1645,14 +1865,15 @@ const Channel = class { if (response instanceof Error) { return Promise.reject(response); } - let processTrans; - if (response.response) { + if (response.response && response.response.status && response.response.status == 200) { logger.debug('queryTransaction - response status :: %d', response.response.status); - processTrans = BlockDecoder.decodeTransaction(response.response.payload); + const processTrans = BlockDecoder.decodeTransaction(response.response.payload); return Promise.resolve(processTrans); + } else if (response.response && response.response.status) { + // no idea what we have, lets fail it and send it back + return Promise.reject(new Error(response.response.message)); } - // no idea what we have, lets fail it and send it back - return Promise.reject(processTrans); + } return Promise.reject(new Error('Payload results are missing from the query')); } @@ -1689,7 +1910,8 @@ const Channel = class { fcn: 'getchaincodes', args: [] }; - return this.sendTransactionProposal(request) + + return Channel.sendTransactionProposal(request, this._name, this._clientContext, null) .then( function (results) { const responses = results[0]; @@ -1836,8 +2058,8 @@ const Channel = class { let errorMsg = null; //validate the incoming request - if (!errorMsg) errorMsg = clientUtils.checkProposalRequest(request); - if (!errorMsg) errorMsg = clientUtils.checkInstallRequest(request); + if (!errorMsg) errorMsg = client_utils.checkProposalRequest(request, true); + if (!errorMsg) errorMsg = client_utils.checkInstallRequest(request); if (errorMsg) { logger.error('sendChainCodeProposal error ' + errorMsg); return Promise.reject(new Error(errorMsg)); @@ -1857,7 +2079,7 @@ const Channel = class { args.push(Buffer.from(arg, 'utf8')); const ccSpec = { - type: clientUtils.translateCCType(request.chaincodeType), + type: client_utils.translateCCType(request.chaincodeType), chaincode_id: { name: request.chaincodeId, version: request.chaincodeVersion @@ -1902,25 +2124,25 @@ const Channel = class { const lcccSpec = { // type: _ccProto.ChaincodeSpec.Type.GOLANG, - type: clientUtils.translateCCType(request.chaincodeType), + type: client_utils.translateCCType(request.chaincodeType), chaincode_id: { name: Constants.LSCC }, input: { args: lcccSpec_args } }; - const channelHeader = clientUtils.buildChannelHeader( + const channelHeader = client_utils.buildChannelHeader( _commonProto.HeaderType.ENDORSER_TRANSACTION, this._name, request.txId.getTransactionID(), null, Constants.LSCC, - clientUtils.buildCurrentTimestamp(), - peers[0].getClientCertHash() + client_utils.buildCurrentTimestamp(), + this._clientContext.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); + const header = client_utils.buildHeader(signer, channelHeader, request.txId.getNonce()); + const proposal = client_utils.buildProposal(lcccSpec, header, request.transientMap); + const signed_proposal = client_utils.signProposal(signer, proposal); - return clientUtils.sendPeersProposal(peers, signed_proposal, timeout) + return client_utils.sendPeersProposal(peers, signed_proposal, timeout) .then( function (responses) { return [responses, proposal]; @@ -1959,23 +2181,59 @@ const Channel = class { * @returns {Promise} A Promise for the {@link ProposalResponseObject} */ sendTransactionProposal(request, timeout) { - logger.debug('sendTransactionProposal - start'); + const method = 'sendTransactionProposal'; + logger.debug('%s - start', method); - if (!request) { - throw new Error('Missing request object for this transaction proposal'); + let errorMsg = client_utils.checkProposalRequest(request, true); + + 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'; } - request.targets = this._getTargets(request.targets, Constants.NetworkConfig.ENDORSING_PEER_ROLE); - return Channel.sendTransactionProposal(request, this._name, this._clientContext, timeout); + if (errorMsg) { + logger.error('%s error %s', method, errorMsg); + throw new Error(errorMsg); + } + + + if(this._endorsement_handler) { + logger.debug('%s - running with endorsement handler'); + const proposal = Channel._buildSignedProposal(request, this._name, this._clientContext); + + const params = { + request: request, + signed_proposal: proposal.signed, + timeout: timeout + }; + + return this._endorsement_handler.endorse(params).then((responses) => { + + return Promise.resolve([responses, proposal.source]); + }).catch((err) => { + logger.error('%s - Failed Proposal. Error: %s', method, err.stack ? err.stack : err); + + return Promise.reject(err); + }); + } else { + logger.debug('%s - running without endorsement handler'); + 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); + static sendTransactionProposal(request, channelId, client_context, timeout) { + const method = 'sendTransactionProposal(static)'; + logger.debug('%s - start'); + + let errorMsg = client_utils.checkProposalRequest(request, true); if (errorMsg) { // do nothing so we skip the rest of the checks @@ -1984,72 +2242,74 @@ const Channel = class { errorMsg = 'Missing "args" in Transaction proposal request'; } else if (!request.targets || request.targets.length < 1) { errorMsg = 'Missing peer objects in Transaction proposal'; - } else if (!request.chaincodeId) { - errorMsg = 'Missing "chaincodeId" parameter in Transaction proposal request'; } if (errorMsg) { - logger.error('sendTransactionProposal error ' + errorMsg); + logger.error('%s error %s', method, errorMsg); throw new Error(errorMsg); } - var args = []; + const proposal = Channel._buildSignedProposal(request, channelId, client_context); + + return client_utils.sendPeersProposal(request.targets, proposal.signed, timeout).then((responses) => { + + return Promise.resolve([responses, proposal.source]); + }).catch((err) => { + logger.error('%s - Failed Proposal. Error: %s', method, err.stack ? err.stack : err); + + return Promise.reject(err); + }); + } + + static _buildSignedProposal(request, channelId, client_context) { + const method = '_buildSignedProposal'; + logger.debug('%s - start', method); + + const args = []; args.push(Buffer.from(request.fcn ? request.fcn : 'invoke', 'utf8')); - logger.debug('sendTransactionProposal - adding function arg:%s', request.fcn ? request.fcn : 'invoke'); + logger.debug('%s - adding function arg:%s', method, request.fcn ? request.fcn : 'invoke'); for (let i = 0; i < request.args.length; i++) { - //logger.debug('sendTransactionProposal - adding arg:%s', request.args[i]); + logger.debug('%s - adding arg', method); 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'); + logger.debug('%s - adding the argument :: argbytes', method); args.push(request.argbytes); } else { - logger.debug('sendTransactionProposal - not adding the argument :: argbytes'); + logger.debug('%s - not adding the argument :: argbytes', method); } - let invokeSpec = { + + const invokeSpec = { type: _ccProto.ChaincodeSpec.Type.GOLANG, - chaincode_id: { - name: request.chaincodeId - }, - input: { - args: args - } + chaincode_id: { name: request.chaincodeId }, + input: {args: args} }; - let proposal, header; let signer = null; if (request.signer) { signer = request.signer; } else { - signer = clientContext._getSigningIdentity(request.txId.isAdmin()); + signer = client_context._getSigningIdentity(request.txId.isAdmin()); } - const channelHeader = clientUtils.buildChannelHeader( + + const channelHeader = client_utils.buildChannelHeader( _commonProto.HeaderType.ENDORSER_TRANSACTION, channelId, request.txId.getTransactionID(), null, request.chaincodeId, - clientUtils.buildCurrentTimestamp(), - request.targets[0].getClientCertHash() + client_utils.buildCurrentTimestamp(), + client_context.getClientCertHash() ); - header = clientUtils.buildHeader(signer, channelHeader, request.txId.getNonce()); - proposal = clientUtils.buildProposal(invokeSpec, header, request.transientMap); - const 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); - } - ); + const header = client_utils.buildHeader(signer, channelHeader, request.txId.getNonce()); + const proposal = client_utils.buildProposal(invokeSpec, header, request.transientMap); + const signed_proposal = client_utils.signProposal(signer, proposal); + + return {signed :signed_proposal, source: proposal}; } /** @@ -2205,7 +2465,7 @@ const Channel = class { * It will be an acknowledgement from the orderer of successfully submitted transaction. */ executeTransaction(request) { - var errorMsg = clientUtils.checkProposalRequest(request, false); + let errorMsg = client_utils.checkProposalRequest(request, true); if (errorMsg) { throw new Error(errorMsg); } else if (!request.args) { @@ -2213,10 +2473,10 @@ const Channel = class { throw new Error('Missing "args" in Transaction proposal request'); } - var self = this; - var eventHubs = request.eventHubs; - var txId = request.txId; - var timeout = request.timeout; + const self = this; + const eventHubs = request.eventHubs; + const txId = request.txId; + const timeout = request.timeout; return this.sendTransactionProposal(request) .then( @@ -2274,6 +2534,8 @@ const Channel = class { return Promise.all(promises); } return Promise.reject('Failed to execute transaction: ' + errorMsg); + }).catch(error => { + return Promise.reject(error); }); } @@ -2321,7 +2583,7 @@ const Channel = class { const txId = new TransactionID(signer, useAdmin); // make a new request object so we can add in the txId and not change the user's - const trans_request = { + const query_request = { targets: targets, chaincodeId: request.chaincodeId, fcn: request.fcn, @@ -2331,11 +2593,10 @@ const Channel = class { signer: signer }; - return this.sendTransactionProposal(trans_request) + return Channel.sendTransactionProposal(query_request, this._name, this._clientContext, null) .then( function (results) { const responses = results[0]; - // var proposal = results[1]; logger.debug('queryByChaincode - results received'); if (responses && Array.isArray(responses)) { const results = []; @@ -2461,8 +2722,8 @@ const Channel = class { throw new Error('Parameter proposal responses does not contain a PorposalResponse'); } const first_one = _getProposalResponseResults(proposal_responses[0]); - for (var i = 1; i < proposal_responses.length; i++) { - var next_one = _getProposalResponseResults(proposal_responses[i]); + for (let i = 1; i < proposal_responses.length; i++) { + const next_one = _getProposalResponseResults(proposal_responses[i]); if (next_one.equals(first_one)) { logger.debug('compareProposalResponseResults - read/writes result sets match index=%s', i); } @@ -2485,13 +2746,26 @@ const Channel = class { } let targets = this._getTargets(target, Constants.NetworkConfig.LEDGER_QUERY_ROLE, true); // only want to query one peer - if (targets && targets.length > 1) { + if (targets && targets.length > 0) { targets = [targets[0]]; } return targets; } + /* + * utility method to decide on the target for queries that only need ledger access + */ + _getFirstAvailableTarget(target) { + let targets = this._getTargets(target, Constants.NetworkConfig.ALL_ROLES, true); + // only want to query one peer + if (targets && targets.length > 0) { + targets = targets[0]; + } + + return targets; + } + /* * utility method to decide on the target for discovery */ @@ -2668,7 +2942,7 @@ function loadConfigGroup(config_items, versions, group, name, org, top) { logger.debug('loadConfigGroup - %s - << values', name); logger.debug('loadConfigGroup - %s - >> policies', name); - var policies = null; + let policies = null; if (top) { policies = group.policies; } @@ -2711,7 +2985,7 @@ function loadConfigValue(config_items, versions, config_value, group_name, org, case 'AnchorPeers': { const anchor_peers = _peerConfigurationProto.AnchorPeers.decode(config_value.value.value); logger.debug('loadConfigValue - %s - AnchorPeers :: %s', group_name, anchor_peers); - if (anchor_peers && anchor_peers.anchor_peers) for (var i in anchor_peers.anchor_peers) { + if (anchor_peers && anchor_peers.anchor_peers) for (let i in anchor_peers.anchor_peers) { const anchor_peer = { host: anchor_peers.anchor_peers[i].host, port: anchor_peers.anchor_peers[i].port, @@ -2836,7 +3110,7 @@ function loadConfigValue(config_items, versions, config_value, group_name, org, * * @class */ -var ChannelPeer = class { +const ChannelPeer = class { /** * Construct a ChannelPeer object with the given Peer and opts. * A channel peer object holds channel based references: @@ -2912,14 +3186,6 @@ var ChannelPeer = class { return this._peer.getUrl(); } - /** - * Get the client certificate hash - * @returns {byte[]} The hash of the client certificate - */ - getClientCertHash() { - return this._peer.getClientCertHash(); - } - /** * Set a role for this peer. * @@ -3023,7 +3289,7 @@ function loadPolicy(config_items, versions, key, policy, group_name) { } function decodeSignaturePolicy(identities) { - var results = []; + const results = []; for (let i in identities) { let identity = identities[i]; switch (identity.getPrincipalClassification()) { @@ -3035,14 +3301,14 @@ function decodeSignaturePolicy(identities) { } function eventHubPromises(eventHubs, txId, timeout) { - var message = ''; - var promises = []; + let message = ''; + const promises = []; if (!eventHubs) { return promises; } if (!timeout) { - timeout = utils.getConfigSetting('request-timeout', 30000); + timeout = sdk_utils.getConfigSetting('request-timeout', 30000); } eventHubs.forEach((eh) => { diff --git a/fabric-client/lib/ChannelEventHub.js b/fabric-client/lib/ChannelEventHub.js index 203fb83e67..9f5e89d759 100644 --- a/fabric-client/lib/ChannelEventHub.js +++ b/fabric-client/lib/ChannelEventHub.js @@ -528,7 +528,7 @@ var ChannelEventHub = class { this._initial_epoch, null, clientUtils.buildCurrentTimestamp(), - this._peer.getClientCertHash() + this._clientContext.getClientCertHash() ); let seekHeader = clientUtils.buildHeader(signer, seekInfoHeader, tx_id.getNonce()); diff --git a/fabric-client/lib/Client.js b/fabric-client/lib/Client.js index 70a2a18cbb..696b8da048 100644 --- a/fabric-client/lib/Client.js +++ b/fabric-client/lib/Client.js @@ -23,27 +23,28 @@ const TransactionID = require('./TransactionID.js'); const idModule = require('./msp/identity.js'); const SigningIdentity = idModule.SigningIdentity; const Signer = idModule.Signer; +const crypto = require('crypto'); -var util = require('util'); -var fs = require('fs-extra'); -var path = require('path'); -var yaml = require('js-yaml'); -var Constants = require('./Constants.js'); +const util = require('util'); +const fs = require('fs-extra'); +const path = require('path'); +const yaml = require('js-yaml'); +const Constants = require('./Constants.js'); -var grpc = require('grpc'); -var _commonProto = grpc.load(__dirname + '/protos/common/common.proto').common; -var _configtxProto = grpc.load(__dirname + '/protos/common/configtx.proto').common; -var _ccProto = grpc.load(__dirname + '/protos/peer/chaincode.proto').protos; -var _queryProto = grpc.load(__dirname + '/protos/peer/query.proto').protos; +const grpc = require('grpc'); +const _commonProto = grpc.load(__dirname + '/protos/common/common.proto').common; +const _configtxProto = grpc.load(__dirname + '/protos/common/configtx.proto').common; +const _ccProto = grpc.load(__dirname + '/protos/peer/chaincode.proto').protos; +const _queryProto = grpc.load(__dirname + '/protos/peer/query.proto').protos; -var config = sdkUtils.getConfig(); +const config = sdkUtils.getConfig(); // setup the location of the default config shipped with code -var default_config = path.resolve( __dirname, '../config/default.json'); +const default_config = path.resolve( __dirname, '../config/default.json'); config.reorderFileStores(default_config); //make sure this default has precedences // set default SSL ciphers for gRPC process.env.GRPC_SSL_CIPHER_SUITES = sdkUtils.getConfigSetting('grpc-ssl-cipher-suites'); -var logger = sdkUtils.getLogger('Client.js'); +const logger = sdkUtils.getLogger('Client.js'); /** * A client instance provides the main API surface to interact with a network of @@ -72,7 +73,7 @@ var logger = sdkUtils.getLogger('Client.js'); * @extends BaseClient * */ -var Client = class extends BaseClient { +const Client = class extends BaseClient { constructor() { super(); @@ -108,7 +109,7 @@ var Client = class extends BaseClient { * @return {Client} An instance of this class initialized with the network end points. */ static loadFromConfig(config) { - var client = new Client(); + const client = new Client(); client._network_config = _getNetworkConfig(config, client); if(client._network_config.hasClient()) { client._setAdminFromConfig(); @@ -125,7 +126,7 @@ var Client = class extends BaseClient { * @param {object | string} config - This may be the config object or a path to the configuration file */ loadFromConfig(config) { - var additional_network_config = _getNetworkConfig(config, this); + const additional_network_config = _getNetworkConfig(config, this); if(!this._network_config) { this._network_config = additional_network_config; } else { @@ -146,7 +147,9 @@ var Client = class extends BaseClient { * @param {byte[]} clientKey - The client key. */ setTlsClientCertAndKey(clientCert, clientKey) { - logger.debug('setTlsClientCertAndKey - start'); + const method = 'setTlsClientCertAndKey'; + logger.debug('%s - start'); + this._tls_mutual.clientCert = clientCert; this._tls_mutual.clientKey = clientKey; } @@ -281,7 +284,7 @@ var Client = class extends BaseClient { * @returns {Peer} The Peer instance. */ newPeer(url, opts) { - return new Peer(url, opts); + return new Peer(url, this._checkTLScert_n_key(opts)); } /** @@ -344,7 +347,7 @@ var Client = class extends BaseClient { * @returns {Orderer} The Orderer instance. */ newOrderer(url, opts) { - let orderer = new Orderer(url, opts); + let orderer = new Orderer(url, this._checkTLScert_n_key(opts)); return orderer; } @@ -380,7 +383,7 @@ var Client = class extends BaseClient { * @returns {EventHub} The EventHub instance */ newEventHub() { - var event_hub = new EventHub(this); + const event_hub = new EventHub(this); return event_hub; } @@ -394,7 +397,7 @@ var Client = class extends BaseClient { * @returns {EventHub} The EventHub instance that has had the event hub address assigned */ getEventHub(peer_name) { - var event_hub = null; + let event_hub = null; if(this._network_config) { event_hub = this._network_config.getEventHub(peer_name); } @@ -413,7 +416,7 @@ var Client = class extends BaseClient { * @returns {EventHub[]} An array of EventHub instances that are defined for this organization */ getEventHubsForOrg(org_name) { - var event_hubs = []; + let event_hubs = []; if(this._network_config) { if(!org_name && this._network_config.hasClient()) { let client = this._network_config.getClientConfig(); @@ -487,21 +490,21 @@ var Client = class extends BaseClient { } else { tlsCACerts = []; } - let connection_options = ca_info.getConnectionOptions(); + const connection_options = ca_info.getConnectionOptions(); let verify = true; //default if not found if(connection_options && typeof connection_options.verify === 'boolean') { verify = connection_options.verify; } - let tls_options = { + const tls_options = { trustedRoots: tlsCACerts, verify }; - let ca_url = ca_info.getUrl(); - let ca_name = ca_info.getCaName(); + const ca_url = ca_info.getUrl(); + const ca_name = ca_info.getCaName(); - let ca_service_class = Client.getConfigSetting('certificate-authority-client'); - let ca_service_impl = require(ca_service_class); - let ca_service = new ca_service_impl( {url : ca_url, tlsOptions : tls_options, caName : ca_name, cryptoSuite : this._cryptoSuite}); + const ca_service_class = Client.getConfigSetting('certificate-authority-client'); + const ca_service_impl = require(ca_service_class); + const ca_service = new ca_service_impl( {url : ca_url, tlsOptions : tls_options, caName : ca_name, cryptoSuite : this._cryptoSuite}); return ca_service; } @@ -571,9 +574,9 @@ var Client = class extends BaseClient { 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()); + const envelope = _commonProto.Envelope.decode(config_envelope); + const payload = _commonProto.Payload.decode(envelope.getPayload().toBuffer()); + const configtx = _configtxProto.ConfigUpdateEnvelope.decode(payload.getData().toBuffer()); return configtx.getConfigUpdate().toBuffer(); } catch(err) { @@ -620,21 +623,21 @@ var Client = class extends BaseClient { } // should try to use the admin signer if assigned // then use the assigned user - var signer = this._getSigningIdentity(true); + const signer = this._getSigningIdentity(true); // signature is across a signature header and the config update - let proto_signature_header = new _commonProto.SignatureHeader(); + const 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(); + const 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); + const signing_bytes = Buffer.concat([signature_header_bytes, config]); + const sig = signer.sign(signing_bytes); + const signature_bytes = Buffer.from(sig); // build the return object - let proto_config_signature = new _configtxProto.ConfigSignature(); + const proto_config_signature = new _configtxProto.ConfigSignature(); proto_config_signature.setSignatureHeader(signature_header_bytes); proto_config_signature.setSignature(signature_bytes); @@ -678,7 +681,7 @@ var Client = class extends BaseClient { * the channel has been created completely or not. */ createChannel(request) { - var have_envelope = false; + let have_envelope = false; if(request && request.envelope) { logger.debug('createChannel - have envelope'); have_envelope = true; @@ -701,7 +704,7 @@ var Client = class extends BaseClient { * to connect to the peers and register a block listener. */ updateChannel(request) { - var have_envelope = false; + let have_envelope = false; if(request && request.envelope) { logger.debug('updateChannel - have envelope'); have_envelope = true; @@ -714,8 +717,8 @@ var Client = class extends BaseClient { */ _createOrUpdateChannel(request, have_envelope) { logger.debug('_createOrUpdateChannel - start'); - var error_msg = null; - var orderer = null; + let error_msg = null; + let orderer = null; if(!request) { error_msg = 'Missing all required input request parameters for initialize channel'; @@ -751,44 +754,44 @@ var Client = class extends BaseClient { // 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()); + const signer = this._getSigningIdentity(request.txId.isAdmin()); - var signature = null; - var payload = null; + let signature = null; + let payload = null; if (have_envelope) { logger.debug('_createOrUpdateChannel - have envelope'); - var envelope = _commonProto.Envelope.decode(request.envelope); + const 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(); + const proto_config_Update_envelope = new _configtxProto.ConfigUpdateEnvelope(); proto_config_Update_envelope.setConfigUpdate(request.config); - var signatures = _stringToSignature(request.signatures); + const signatures = _stringToSignature(request.signatures); proto_config_Update_envelope.setSignatures(signatures); - var proto_channel_header = clientUtils.buildChannelHeader( + const 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(); + const proto_header = clientUtils.buildHeader(signer, proto_channel_header, request.txId.getNonce()); + const proto_payload = new _commonProto.Payload(); proto_payload.setHeader(proto_header); proto_payload.setData(proto_config_Update_envelope.toBuffer()); - var payload_bytes = proto_payload.toBuffer(); + const payload_bytes = proto_payload.toBuffer(); - let sig = signer.sign(payload_bytes); - let signature_bytes = Buffer.from(sig); + const sig = signer.sign(payload_bytes); + const signature_bytes = Buffer.from(sig); signature = signature_bytes; payload = payload_bytes; } // building manually or will get protobuf errors on send - var out_envelope = { + const out_envelope = { signature: signature, payload : payload }; @@ -1094,7 +1097,7 @@ var Client = class extends BaseClient { error_msg = 'Missing input request object on install chaincode request'; } - if (!error_msg) error_msg = clientUtils.checkProposalRequest(request, true); + if (!error_msg) error_msg = clientUtils.checkProposalRequest(request, false); if (!error_msg) error_msg = clientUtils.checkInstallRequest(request); if (error_msg) { @@ -1790,8 +1793,10 @@ var Client = class extends BaseClient { } if(targets.length > 0) { + return targets; } else { + return null; } } @@ -1839,8 +1844,44 @@ var Client = class extends BaseClient { return orderer; } + + /** + * Get the client certificate hash + * @returns {byte[]} The hash of the client certificate + */ + getClientCertHash() { + const method = 'getClientCertHash'; + logger.debug('%s - start', method); + + let hash = null; + if(this._tls_mutual.clientCert) { + logger.debug('%s - using clientCert %s', method, this._tls_mutual.clientCert); + let der_cert = sdkUtils.pemToDER(this._tls_mutual.clientCert); + hash = computeHash(der_cert); + } else { + logger.debug('%s - no tls client cert', method); + } + + return hash; + } + + _checkTLScert_n_key(opts) { + const new_opts = Object.assign({},opts); + if(opts && !opts.clientCert) { + this.addTlsClientCertAndKey(new_opts); + } + + return new_opts; + } }; +// Compute hash for replay protection +function computeHash(data) { + const sha256 = crypto.createHash('sha256'); + + return sha256.update(data).digest(); +} + function readFile(path) { return new Promise(function(resolve, reject) { fs.readFile(path, 'utf8', function (err, data) { @@ -1851,6 +1892,7 @@ function readFile(path) { return resolve(null); } } + return resolve(data); }); }); @@ -1861,9 +1903,11 @@ function _getChaincodePackageData(request, devMode) { if (!request.chaincodePackage) { logger.debug('_getChaincodePackageData - build package with chaincodepath %s, chaincodeType %s, devMode %s, metadataPath %s', request.chaincodePath, request.chaincodeType, devMode, request.metadataPath); + return Promise.resolve(Packager.package(request.chaincodePath, request.chaincodeType, devMode, request.metadataPath)); } else { logger.debug('_getChaincodePackageData - working with included chaincodePackage'); + return Promise.resolve(request.chaincodePackage); } } @@ -1883,6 +1927,7 @@ function _stringToSignature(string_signatures) { } signatures.push(signature); } + return signatures; } diff --git a/fabric-client/lib/Constants.js b/fabric-client/lib/Constants.js index 3f4c1d1ed0..6993ef5dc1 100755 --- a/fabric-client/lib/Constants.js +++ b/fabric-client/lib/Constants.js @@ -17,10 +17,12 @@ module.exports.NetworkConfig.CHAINCODE_QUERY_ROLE = 'chaincodeQuery'; module.exports.NetworkConfig.LEDGER_QUERY_ROLE = 'ledgerQuery'; module.exports.NetworkConfig.EVENT_SOURCE_ROLE = 'eventSource'; module.exports.NetworkConfig.DISCOVERY_ROLE = 'discover'; +module.exports.NetworkConfig.ALL_ROLES = 'all'; module.exports.NetworkConfig.ROLES = [ module.exports.NetworkConfig.ENDORSING_PEER_ROLE, module.exports.NetworkConfig.CHAINCODE_QUERY_ROLE, module.exports.NetworkConfig.LEDGER_QUERY_ROLE, module.exports.NetworkConfig.EVENT_SOURCE_ROLE, - module.exports.NetworkConfig.DISCOVERY_ROLE + module.exports.NetworkConfig.DISCOVERY_ROLE, + module.exports.NetworkConfig.ALL_ROLES ]; diff --git a/fabric-client/lib/EventHub.js b/fabric-client/lib/EventHub.js index 0533604bb0..c06c332bd8 100644 --- a/fabric-client/lib/EventHub.js +++ b/fabric-client/lib/EventHub.js @@ -422,7 +422,7 @@ var EventHub = class { event.setCreator(identity.serialize()); event.setTimestamp(clientUtils.buildCurrentTimestamp()); - let client_cert_hash = this._ep.getClientCertHash(); + let client_cert_hash = this._clientContext.getClientCertHash(); if(client_cert_hash) { event.setTlsCertHash(client_cert_hash); } diff --git a/fabric-client/lib/Remote.js b/fabric-client/lib/Remote.js index 9ea933edf5..3e8dbf6223 100644 --- a/fabric-client/lib/Remote.js +++ b/fabric-client/lib/Remote.js @@ -101,13 +101,18 @@ class Remote { } this._options[MAX_SEND] = grpc_send_max; - // what shall we call this remote object - this._name = name ? name : url; - // service connection this._url = url; this._endpoint = new Endpoint(url, pem, clientKey, this.clientCert); + // what shall we call this remote object + if(opts && opts.name) { + this._name = opts.name; + } else { + const split = url.split('//'); + this._name = split[1]; + } + // node.js based timeout if (utils.checkIntegerConfig(opts, 'request-timeout')) { this._request_timeout = opts['request-timeout']; diff --git a/fabric-client/lib/api.js b/fabric-client/lib/api.js index 47ca04e5ba..b1fe7d059a 100755 --- a/fabric-client/lib/api.js +++ b/fabric-client/lib/api.js @@ -308,4 +308,55 @@ module.exports.Hash = class { finalize() { } -}; \ No newline at end of file +}; + +/** + * Base class for endorsement handling + * @class + */ +module.exports.EndorsementHandler = class { + + /** + * @typedef {Object} EndorsementHandlerParameters + * @property {Peer[]} request - {@link ChaincodeInvokeRequest} + * @property {Object} signed_proposal - the encoded protobuf "SignedProposal" + * created by the sendTransactionProposal method before calling the + * handler. Will be the object to be endorsed by the target peers. + * @property {Number} timeout - the timeout setting passed on sendTransactionProposal + * method. + */ + + /** + * This method will process the request object to calculate the target peers. + * Once the targets have been determined, the channel to send the endorsement + * transaction to all targets. The results will be analized to see if + * enough good endorsments have been received. + * + * @param {EndorsementHandlerRequest} params - A {@link EndorsementHandlerParameters} + * that contains enough information to determine the targets and contains + * a {@link ChaincodeInvokeRequest} to be sent using the included channel + * with the {@link Channel} 'sendTransactionProposal' method. + * @returns {Promise} A Promise for the {@link ProposalResponseObject}, the + * same results as calling the {@link Channel} 'sendTransactionProposal' + * method directly. + */ + endorse(params) { + throw new Error('The "endorse" method must be implemented'); + } + + /** + * This method will be called by the channel when the channel is initialzied. + */ + initialize() { + throw new Error('The "initialize" method must be implemented'); + } + + /** + * This static method will be called by the channel to create an instance of + * this handler. It will be passed the channel object this handler is working + * with. + */ + static create(channel) { + throw new Error('The "create" method must be implemented'); + } +}; diff --git a/fabric-client/lib/client-utils.js b/fabric-client/lib/client-utils.js index 3aa1529461..0ca915328d 100644 --- a/fabric-client/lib/client-utils.js +++ b/fabric-client/lib/client-utils.js @@ -160,13 +160,13 @@ module.exports.buildHeader = function (creator, channelHeader, nonce) { return header; }; -module.exports.checkProposalRequest = function (request, skip) { +module.exports.checkProposalRequest = function (request, all) { var errorMsg = null; if (request) { if (!request.chaincodeId) { errorMsg = 'Missing "chaincodeId" parameter in the proposal request'; - } else if (!request.txId && !skip) { + } else if (!request.txId && all) { errorMsg = 'Missing "txId" parameter in the proposal request'; } } else { diff --git a/fabric-client/lib/impl/DiscoveryEndorsementHandler.js b/fabric-client/lib/impl/DiscoveryEndorsementHandler.js new file mode 100644 index 0000000000..488688ed41 --- /dev/null +++ b/fabric-client/lib/impl/DiscoveryEndorsementHandler.js @@ -0,0 +1,112 @@ +/* + Copyright 2016, 2018 IBM All Rights Reserved. + + SPDX-License-Identifier: Apache-2.0 + +*/ + +'use strict'; + +const fs = require('fs-extra'); +const path = require('path'); +const utils = require('../utils'); +const client_utils = require('../client-utils.js'); +const Constants = require('../Constants.js'); +const Channel = require('../Channel.js'); +const Peer = require('../Peer.js'); +const ChannelEventHub = require('../ChannelEventHub.js'); +const Orderer = require('../Orderer.js'); +const api = require('../api.js'); +const logger = utils.getLogger('DiscoveryEndorsementHandler'); + + +/** + * This is an implementation of the [EndorsementHandler]{@link module:api.EndorsementHandler} API. + * It will submit transactions to be endorsed to a target list generated from the + * results of service discovery. + * + * @class + * @extends module:api.EndorsementHandler + */ +class DiscoveryEndorsementHandler extends api.EndorsementHandler { + + /** + * constructor + * + * @param {Object} parm1 - Something + */ + constructor(channel) { + super(); + this._channel = channel; + } + + /** + * Factory method to create an instance of an endorsement handler. + * + * @param {Channel} channel - the channel instance that this endorsement + * handler will be servicing. + * @returns {DiscoveryEndorsementHandler} The instance of the handler + */ + static create(channel) { + return new DiscoveryEndorsementHandler(channel); + } + + initialize() { + logger.debug('initialize - start'); + } + + async endorse(params) { + const method = 'endorse'; + logger.debug('%s - start', method); + + + let errorMsg = null; + if (!params) { + errorMsg = 'Missing all required input parameters'; + } else if (!params.request){ + errorMsg = 'Missing "request" input parameter'; + } else if (!params.signed_proposal) { + errorMsg = 'Missing "signed_proposal" input request parameter'; + } + + if (errorMsg) { + logger.error('Endorsement Handler error:' + errorMsg); + throw new Error(errorMsg); + } + + const request = Object.assign({}, params.request); + + if (!request.chaincodeId) { + errorMsg = 'Missing "chaincodeId" parameter in the proposal request'; + } else if (!request.txId) { + errorMsg = 'Missing "txId" parameter in the proposal request'; + } else if (!request.args) { + errorMsg = 'Missing "args" in Transaction proposal request'; + } + + if (errorMsg) { + logger.error('Endorsement Handler error:' + errorMsg); + throw new Error(errorMsg); + } + + let timeout = utils.getConfigSetting('request-timeout'); + if(params.timeout) { + timeout = params.timeout; + } + + + // this will check the age of the results and get new results if needed + // let discovery_results = await this._channel.getDiscoveryResults(); + // if(discovery_results && !request.targets) { + // + // } else { + logger.debug('%s - not using discovery', method); + const targets = this._channel._getTargets(request.targets, Constants.NetworkConfig.ENDORSING_PEER_ROLE); + + return client_utils.sendPeersProposal(targets, params.signed_proposal, timeout); + // } + + } +} + +module.exports = DiscoveryEndorsementHandler; diff --git a/fabric-client/lib/impl/NetworkConfig_1_0.js b/fabric-client/lib/impl/NetworkConfig_1_0.js index 593a914ee6..d67a81bd8a 100644 --- a/fabric-client/lib/impl/NetworkConfig_1_0.js +++ b/fabric-client/lib/impl/NetworkConfig_1_0.js @@ -67,7 +67,7 @@ var NetworkConfig_1_0 = class { } mergeSettings(additions) { - var method = 'mergeSettings'; + const method = 'mergeSettings'; logger.debug('%s - additions start',method); if(additions && additions._network_config) { if(additions._network_config.client) { @@ -100,9 +100,9 @@ var NetworkConfig_1_0 = class { } getClientConfig() { - var result = {}; + const result = {}; if(this._network_config && this._network_config.client) { - let client_config = this._network_config.client; + const client_config = this._network_config.client; for(let setting in client_config) { // copy all except credentialStore, special case to handle paths if(setting !== 'credentialStore') { @@ -137,7 +137,7 @@ var NetworkConfig_1_0 = class { } } if(client_config.tlsClient) { - if(client_config.tlsClient.clientCert && client_config.tlsClient.clientKey);//TODO + if(client_config.tlsClient.clientCert && client_config.tlsClient.clientKey); } } @@ -145,11 +145,11 @@ var NetworkConfig_1_0 = class { } getChannel(name) { - var method = 'getChannel'; + const method = 'getChannel'; logger.debug('%s - name %s',method, name); - var channel = null; + let channel = null; if(name && this._network_config && this._network_config[CHANNELS_CONFIG]) { - var channel_config = this._network_config[CHANNELS_CONFIG][name]; + const channel_config = this._network_config[CHANNELS_CONFIG][name]; if(channel_config) { channel = new Channel(name, this._client_context); this._addPeersToChannel(channel); @@ -161,18 +161,17 @@ var NetworkConfig_1_0 = class { } getPeer(name, channel_org) { - var method = 'getPeer'; + const method = 'getPeer'; logger.debug('%s - name %s, channel_org: %j',method, name, channel_org); - var peer = this._peers.get(name); + let peer = this._peers.get(name); if(!peer && this._network_config && this._network_config[PEERS_CONFIG]) { - let peer_config = this._network_config[PEERS_CONFIG][name]; + const peer_config = this._network_config[PEERS_CONFIG][name]; if(peer_config) { - let opts = {name: name}; + const opts = {name: name}; opts.pem = getTLSCACert(peer_config); Object.assign(opts, peer_config[GRPC_CONNECTION_OPTIONS]); this.addTimeout(opts, ENDORSER); - this._client_context.addTlsClientCertAndKey(opts); - peer = new Peer(peer_config[URL], opts); + peer = this._client_context.newPeer(peer_config[URL], opts); this._peers.set(name, peer); } } @@ -181,7 +180,7 @@ var NetworkConfig_1_0 = class { } addTimeout(opts, type) { - var method = 'addTimeout'; + const method = 'addTimeout'; if(opts && opts[REQUEST_TIMEOUT]) { logger.debug('%s - request-timeout exist',method); return; @@ -189,7 +188,7 @@ var NetworkConfig_1_0 = class { if(opts && this.hasClient() && this._network_config.client.connection && this._network_config.client.connection.timeout) { - let timeouts = this._network_config.client.connection.timeout; + const timeouts = this._network_config.client.connection.timeout; let timeout = ''; if(type === ENDORSER && timeouts.peer && timeouts.peer.endorser) { timeout = timeouts.peer.endorser; @@ -211,13 +210,13 @@ var NetworkConfig_1_0 = class { } getEventHub(name) { - var method = 'getEventHub'; + const method = 'getEventHub'; logger.debug('%s - name %s',method, name); - var event_hub = null; + let event_hub = null; if(this._network_config && this._network_config[PEERS_CONFIG]) { - let peer_config = this._network_config[PEERS_CONFIG][name]; + const peer_config = this._network_config[PEERS_CONFIG][name]; if(peer_config && peer_config[EVENT_URL]) { - let opts = {}; + const opts = {}; opts.pem = getTLSCACert(peer_config); this._client_context.addTlsClientCertAndKey(opts); Object.assign(opts, peer_config[GRPC_CONNECTION_OPTIONS]); @@ -231,18 +230,17 @@ var NetworkConfig_1_0 = class { } getOrderer(name) { - var method = 'getOrderer'; + const method = 'getOrderer'; logger.debug('%s - name %s',method, name); - var orderer = null; + let orderer = null; if(this._network_config && this._network_config[ORDERERS_CONFIG]) { - let orderer_config = this._network_config[ORDERERS_CONFIG][name]; + const orderer_config = this._network_config[ORDERERS_CONFIG][name]; if(orderer_config) { - let opts = {name: name}; + const opts = {name: name}; opts.pem = getTLSCACert(orderer_config); Object.assign(opts, orderer_config[GRPC_CONNECTION_OPTIONS]); this.addTimeout(opts, ORDERER); - this._client_context.addTlsClientCertAndKey(opts); - orderer = new Orderer(orderer_config[URL], opts); + orderer = this._client_context.newOrderer(orderer_config[URL], opts); } } @@ -250,20 +248,20 @@ var NetworkConfig_1_0 = class { } getOrganization(name, only_client) { - var method = 'getOrganization'; + const method = 'getOrganization'; logger.debug('%s - name %s',method, name); - var organization = null; + let organization = null; if(name && this._network_config && this._network_config[ORGS_CONFIG]) { - var organization_config = this._network_config[ORGS_CONFIG][name]; + const organization_config = this._network_config[ORGS_CONFIG][name]; if(organization_config) { organization = new Organization(name, organization_config.mspid); if(organization_config[PEERS_CONFIG] && !only_client) { for(let i in organization_config[PEERS_CONFIG]) { - let peer_name = organization_config[PEERS_CONFIG][i]; - let peer = this.getPeer(peer_name); + const peer_name = organization_config[PEERS_CONFIG][i]; + const peer = this.getPeer(peer_name); if(peer) { organization.addPeer(peer); - let event_hub = this.getEventHub(peer_name); + const event_hub = this.getEventHub(peer_name); if(event_hub) { organization.addEventHub(event_hub); } @@ -272,17 +270,17 @@ var NetworkConfig_1_0 = class { } if(organization_config[CAS_CONFIG]) { for(let i in organization_config[CAS_CONFIG]) { - let ca_name = organization_config[CAS_CONFIG][i]; - let ca = this.getCertificateAuthority(ca_name); + const ca_name = organization_config[CAS_CONFIG][i]; + const ca = this.getCertificateAuthority(ca_name); if(ca) organization.addCertificateAuthority(ca); } } if(organization_config[ADMIN_PRIVATE_KEY]) { - let key = getPEMfromConfig(organization_config[ADMIN_PRIVATE_KEY]); + const key = getPEMfromConfig(organization_config[ADMIN_PRIVATE_KEY]); organization.setAdminPrivateKey(key); } if(organization_config[ADMIN_CERT]) { - let cert = getPEMfromConfig(organization_config[ADMIN_CERT]); + const cert = getPEMfromConfig(organization_config[ADMIN_CERT]); organization.setAdminCert(cert); } } @@ -292,12 +290,12 @@ var NetworkConfig_1_0 = class { } getOrganizations() { - var method = 'getOrganizations'; + const method = 'getOrganizations'; logger.debug('%s - start',method); - var organizations = []; + const organizations = []; if(this._network_config && this._network_config[ORGS_CONFIG]) { for(let organization_name in this._network_config[ORGS_CONFIG]) { - let organization = this.getOrganization(organization_name); + const organization = this.getOrganization(organization_name); organizations.push(organization); } } @@ -306,11 +304,11 @@ var NetworkConfig_1_0 = class { } getCertificateAuthority(name) { - var method = 'getCertificateAuthority'; + const method = 'getCertificateAuthority'; logger.debug('%s - name %s',method, name); - var certificateAuthority = null; + let certificateAuthority = null; if(name && this._network_config && this._network_config[CAS_CONFIG]) { - var certificateAuthority_config = this._network_config[CAS_CONFIG][name]; + const certificateAuthority_config = this._network_config[CAS_CONFIG][name]; if(certificateAuthority_config) { certificateAuthority = new CertificateAuthority( name, @@ -337,8 +335,8 @@ var NetworkConfig_1_0 = class { this._network_config[CHANNELS_CONFIG][channel.getName()] ) { let orderer_names = this._network_config[CHANNELS_CONFIG][channel.getName()][ORDERERS_CONFIG]; if(Array.isArray(orderer_names)) for(let i in orderer_names){ - let orderer_name = orderer_names[i]; - let orderer = this.getOrderer(orderer_name); + const orderer_name = orderer_names[i]; + const orderer = this.getOrderer(orderer_name); if(orderer) channel.addOrderer(orderer); } } @@ -355,9 +353,9 @@ var NetworkConfig_1_0 = class { this._network_config[CHANNELS_CONFIG][channel.getName()] ) { let channel_peers = this._network_config[CHANNELS_CONFIG][channel.getName()][PEERS_CONFIG]; if(channel_peers) for(let peer_name in channel_peers) { - let channel_peer = channel_peers[peer_name]; - let peer = this.getPeer(peer_name); - let roles = {}; + const channel_peer = channel_peers[peer_name]; + const peer = this.getPeer(peer_name); + const roles = {}; for(let i in ROLES) { if(typeof channel_peer[ROLES[i]] === 'boolean') { roles[ROLES[i]] = channel_peer[ROLES[i]]; @@ -376,7 +374,7 @@ var NetworkConfig_1_0 = class { /* * Internal utility method to get the organization the peer belongs */ - _getOrganizationForPeer(peer_name) { + _getOrganizationForPeer(peer_name) { if(this._network_config && this._network_config[ORGS_CONFIG]) { for(let organization_name in this._network_config[ORGS_CONFIG]) { let organization = this.getOrganization(organization_name); @@ -387,7 +385,7 @@ var NetworkConfig_1_0 = class { } } } - } + } }; function getTLSCACert(config) { @@ -398,7 +396,7 @@ function getTLSCACert(config) { } function getPEMfromConfig(config) { - var result = null; + let result = null; if(config) { if(config[PEM]) { // cert value is directly in the configuration diff --git a/package-lock.json b/package-lock.json index f2e14e60e5..65dd4b6ce5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7033,12 +7033,6 @@ "integrity": "sha1-Jp1cR2gQ7JLtvntsLygxY4T5p+g=", "dev": true }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", - "dev": true - }, "qs": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/qs/-/qs-2.4.2.tgz", @@ -7277,6 +7271,12 @@ "text-encoding": "0.6.4" } }, + "node-uuid": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", + "dev": true + }, "nodemailer": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-2.7.2.tgz", diff --git a/test/fixtures/docker-compose.yaml b/test/fixtures/docker-compose.yaml index b63d2776a0..039e3ec4c5 100644 --- a/test/fixtures/docker-compose.yaml +++ b/test/fixtures/docker-compose.yaml @@ -72,12 +72,15 @@ services: environment: - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock - CORE_PEER_ID=peer0.org1.example.com + - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 + - CORE_PEER_GOSSIP_ENDPOINT=peer0.org1.example.com:7051 + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051 + - CORE_PEER_EVENTS_ADDRESS=0.0.0.0:7053 - CORE_LOGGING_LEVEL=debug ## the following setting redirects chaincode container logs to the peer container logs - CORE_VM_DOCKER_ATTACHSTDOUT=true - CORE_PEER_LOCALMSPID=Org1MSP - CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/peer/ - - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 - CORE_PEER_TLS_ENABLED=true - CORE_PEER_TLS_CLIENTAUTHREQUIRED=true - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/msp/peer/tls/key.pem @@ -88,7 +91,6 @@ services: # # bridge network as the peers # # https://docs.docker.com/compose/networking/ - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=fixtures_default - - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051 working_dir: /opt/gopath/src/github.com/hyperledger/fabric command: peer node start ports: @@ -106,10 +108,13 @@ services: environment: - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock - CORE_PEER_ID=peer0.org2.example.com + - CORE_PEER_ADDRESS=peer0.org2.example.com:7051 + - CORE_PEER_GOSSIP_ENDPOINT=peer0.org2.example.com:7051 + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org2.example.com:7051 + #- CORE_PEER_EVENTS_ADDRESS=0.0.0.0:8053 - CORE_PEER_LOCALMSPID=Org2MSP - CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/peer/ - - CORE_PEER_ADDRESS=peer0.org2.example.com:7051 - #- CORE_LOGGING_LEVEL=debug + - CORE_LOGGING_LEVEL=debug - CORE_PEER_TLS_ENABLED=true - CORE_PEER_TLS_CLIENTAUTHREQUIRED=true - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/msp/peer/tls/key.pem @@ -120,7 +125,6 @@ services: # # bridge network as the peers # # https://docs.docker.com/compose/networking/ - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=fixtures_default - - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org2.example.com:7051 working_dir: /opt/gopath/src/github.com/hyperledger/fabric command: peer node start ports: diff --git a/test/integration/channel-event-hub.js b/test/integration/channel-event-hub.js index 64092c9fd1..b39982e542 100644 --- a/test/integration/channel-event-hub.js +++ b/test/integration/channel-event-hub.js @@ -70,6 +70,7 @@ test('Test chaincode instantiate with event, transaction invocation with chainco .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); return Client.newDefaultKeyValueStore({path: testUtil.storePathForOrg('peerOrg1')}); }).then((store) => { client.setStateStore(store); @@ -84,8 +85,6 @@ test('Test chaincode instantiate with event, transaction invocation with chainco 'grpcs://localhost:7050', { 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': 'orderer.example.com' } ); @@ -96,8 +95,6 @@ test('Test chaincode instantiate with event, transaction invocation with chainco 'grpcs://localhost:7051', { pem: Buffer.from(data).toString(), - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': 'peer0.org1.example.com' } ); diff --git a/test/integration/configtxlator.js b/test/integration/configtxlator.js index 5837816a46..7d1fd2926f 100644 --- a/test/integration/configtxlator.js +++ b/test/integration/configtxlator.js @@ -66,6 +66,7 @@ test('\n\n***** configtxlator flow for create and then update *****\n\n', async try { const tlsInfo = await e2eUtils.tlsEnroll(org); + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); t.pass('Successfully retrieved TLS certificate'); let store = await Client.newDefaultKeyValueStore({path: testUtil.storePathForOrg(org)}); client.setStateStore(store); @@ -97,8 +98,6 @@ test('\n\n***** configtxlator flow for create and then update *****\n\n', async ORGS.orderer.url, { 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS.orderer['server-hostname'] } ); diff --git a/test/integration/create-configtx-channel.js b/test/integration/create-configtx-channel.js index 6e3a6e14d9..8fd2c1a3a3 100644 --- a/test/integration/create-configtx-channel.js +++ b/test/integration/create-configtx-channel.js @@ -58,6 +58,7 @@ test('\n\n***** Configtx Built config create flow *****\n\n', function(t) { .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); return Client.newDefaultKeyValueStore({path: testUtil.storePathForOrg(org)}); }).then((store) => { client.setStateStore(store); @@ -70,8 +71,6 @@ test('\n\n***** Configtx Built config create flow *****\n\n', function(t) { ORGS.orderer.url, { 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS.orderer['server-hostname'] } ); diff --git a/test/integration/e2e/create-channel.js b/test/integration/e2e/create-channel.js index 1be63a2a68..e7baff7ba9 100644 --- a/test/integration/e2e/create-channel.js +++ b/test/integration/e2e/create-channel.js @@ -57,6 +57,8 @@ test('\n\n***** SDK Built config update create flow *****\n\n', function(t) { .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); + return Client.newDefaultKeyValueStore({path: testUtil.storePathForOrg(org)}); }).then((store) => { client.setStateStore(store); @@ -103,8 +105,6 @@ test('\n\n***** SDK Built config update create flow *****\n\n', function(t) { { name: 'bad orderer', 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS.orderer['server-hostname'], 'grpc.max_send_message_length': 1000 //something too small for the request } @@ -143,8 +143,6 @@ test('\n\n***** SDK Built config update create flow *****\n\n', function(t) { { name: 'new orderer', 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS.orderer['server-hostname'] } ); diff --git a/test/integration/e2e/e2eUtils.js b/test/integration/e2e/e2eUtils.js index 8b1606be23..58ddded47c 100644 --- a/test/integration/e2e/e2eUtils.js +++ b/test/integration/e2e/e2eUtils.js @@ -58,6 +58,8 @@ function installChaincode(org, chaincode_path, metadata_path, version, language, .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); + return Client.newDefaultKeyValueStore({path: testUtil.storePathForOrg(orgName)}); }).then((store) => { client.setStateStore(store); @@ -73,8 +75,6 @@ function installChaincode(org, chaincode_path, metadata_path, version, language, ORGS.orderer.url, { 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS.orderer['server-hostname'] } ) @@ -89,8 +89,6 @@ function installChaincode(org, chaincode_path, metadata_path, version, language, ORGS[org][key].requests, { pem: Buffer.from(data).toString(), - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS[org][key]['server-hostname'] } ); @@ -189,6 +187,8 @@ function instantiateChaincode(userOrg, chaincode_path, version, language, upgrad .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); + return Client.newDefaultKeyValueStore({path: testUtil.storePathForOrg(orgName)}); }).then((store) => { @@ -205,8 +205,6 @@ function instantiateChaincode(userOrg, chaincode_path, version, language, upgrad ORGS.orderer.url, { 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS.orderer['server-hostname'] } ) @@ -221,8 +219,6 @@ function instantiateChaincode(userOrg, chaincode_path, version, language, upgrad ORGS[org][key].requests, { pem: Buffer.from(data).toString(), - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS[org][key]['server-hostname'] } ); @@ -487,6 +483,8 @@ function invokeChaincode(userOrg, version, chaincodeId, t, useStore){ .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); + return promise; }).then((store) => { if (store) { @@ -503,8 +501,6 @@ function invokeChaincode(userOrg, version, chaincodeId, t, useStore){ ORGS.orderer.url, { 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS.orderer['server-hostname'] } ) @@ -519,8 +515,6 @@ function invokeChaincode(userOrg, version, chaincodeId, t, useStore){ ORGS[key].peer1.requests, { pem: Buffer.from(data).toString(), - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS[key].peer1['server-hostname'] } ); @@ -725,6 +719,8 @@ function queryChaincode(org, version, value, chaincodeId, t, transientMap) { .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); + return Client.newDefaultKeyValueStore({path: testUtil.storePathForOrg(orgName)}); }).then((store) => { @@ -745,8 +741,6 @@ function queryChaincode(org, version, value, chaincodeId, t, transientMap) { ORGS[key].peer1.requests, { pem: Buffer.from(data).toString(), - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS[key].peer1['server-hostname'] }); channel.addPeer(peer); diff --git a/test/integration/e2e/join-channel.js b/test/integration/e2e/join-channel.js index 4bb720181a..c86542e6dd 100644 --- a/test/integration/e2e/join-channel.js +++ b/test/integration/e2e/join-channel.js @@ -75,6 +75,8 @@ function joinChannel(org, t) { .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); + return Client.newDefaultKeyValueStore({path: testUtil.storePathForOrg(orgName)}); }).then((store) => { client.setStateStore(store); @@ -87,8 +89,6 @@ function joinChannel(org, t) { ORGS.orderer.url, { 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS.orderer['server-hostname'] } ) @@ -119,8 +119,6 @@ function joinChannel(org, t) { ORGS[org][key].requests, { pem: Buffer.from(data).toString(), - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS[org][key]['server-hostname'] } ) diff --git a/test/integration/events.js b/test/integration/events.js index eaf119ad9e..ea73b80a0a 100644 --- a/test/integration/events.js +++ b/test/integration/events.js @@ -73,6 +73,7 @@ test('Test chaincode instantiate with event, transaction invocation with chainco .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); return Client.newDefaultKeyValueStore({path: testUtil.storePathForOrg(orgName)}); }).then((store) => { client.setStateStore(store); @@ -88,8 +89,6 @@ test('Test chaincode instantiate with event, transaction invocation with chainco ORGS.orderer.url, { 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS.orderer['server-hostname'] } ) @@ -103,8 +102,6 @@ test('Test chaincode instantiate with event, transaction invocation with chainco ORGS[org][key].requests, { pem: Buffer.from(data).toString(), - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS[org][key]['server-hostname'] }); channel.addPeer(peer); diff --git a/test/integration/get-config.js b/test/integration/get-config.js index 332d867533..e944de0bdf 100644 --- a/test/integration/get-config.js +++ b/test/integration/get-config.js @@ -68,6 +68,7 @@ test(' ---->>>>> get config <<<<<-----', function(t) { .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); return Client.newDefaultKeyValueStore({path: testUtil.storePathForOrg(orgName)}); }).then( function (store) { client.setStateStore(store); diff --git a/test/integration/install.js b/test/integration/install.js index 1811324476..f96da63479 100644 --- a/test/integration/install.js +++ b/test/integration/install.js @@ -157,6 +157,7 @@ function installChaincode(params, t) { .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); return Client.newDefaultKeyValueStore({path: testUtil.storePathForOrg(orgName)}); }).then((store) => { client.setStateStore(store); diff --git a/test/integration/instantiate.js b/test/integration/instantiate.js index 61d3fb1889..2906b3de7e 100644 --- a/test/integration/instantiate.js +++ b/test/integration/instantiate.js @@ -110,6 +110,7 @@ function instantiateChaincodeForError(request, error_snip, t) { .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); return Client.newDefaultKeyValueStore({path: testUtil.storePathForOrg(orgName)}); }).then((store) => { client.setStateStore(store); diff --git a/test/integration/invoke.js b/test/integration/invoke.js index 0ae3743cb6..b76ec06a16 100644 --- a/test/integration/invoke.js +++ b/test/integration/invoke.js @@ -145,6 +145,7 @@ function invokeChaincode(userOrg, version, t, shouldFail, peers){ .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); return testUtil.getSubmitter(client, t, userOrg); }).then((admin) => { t.pass('Successfully enrolled user \'admin\''); @@ -348,6 +349,7 @@ function invokeChaincodeSingleCall(userOrg, version, t, shouldFail, peers){ .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); return testUtil.getSubmitter(client, t, userOrg); }).then((admin) => { t.pass('Successfully enrolled user \'admin\''); @@ -358,8 +360,6 @@ function invokeChaincodeSingleCall(userOrg, version, t, shouldFail, peers){ ORGS.orderer.url, { 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS.orderer['server-hostname'] } ) @@ -370,8 +370,6 @@ function invokeChaincodeSingleCall(userOrg, version, t, shouldFail, peers){ peers[key].requests, { 'pem': peers[key].pem, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': peers[key]['server-hostname'], }); channel.addPeer(peer); diff --git a/test/integration/only-admin.js b/test/integration/only-admin.js index c3807fa3ac..6922606520 100644 --- a/test/integration/only-admin.js +++ b/test/integration/only-admin.js @@ -35,7 +35,9 @@ test('\n\n***** use only admin identity *****\n\n', async function(t) { const client_org1 = await getClientForOrg(t, 'org1'); const client_org2 = await getClientForOrg(t, 'org2'); + client_org2.setConfigSetting('initialize-with-discovery', false); const channel = await setupChannel(t, client_org1, client_org2, channel_name); + channel._endorsement_handler = null; const tx_id_string = await invoke(t, client_org1, channel); await queries(t, client_org1, channel, tx_id_string); @@ -46,6 +48,8 @@ test('\n\n***** use only admin identity *****\n\n', async function(t) { test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) { const client = await getClientForOrg(t, 'org1'); + client.setConfigSetting('initialize-with-discovery', true); + const channel = client.getChannel('adminconfig'); let q_results = await channel.queryInstantiatedChaincodes('peer0.org1.example.com', true); @@ -54,14 +58,15 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) { let results = await channel._discover({ target:'peer0.org1.example.com', - chaincodeId: chaincode_id //to get a chaincode query + chaincodes: [chaincode_id], + config: true }); - t.equals(results.config.msps.OrdererMSP.id, 'OrdererMSP', 'Checking MSP ID'); - t.equals(results.config.msps.Org1MSP.id, 'Org1MSP', 'Checking MSP ID'); - t.equals(results.config.msps.Org2MSP.id, 'Org2MSP', 'Checking MSP ID'); - t.equals(results.config.orderers.OrdererMSP.endpoints[0].host, 'orderer.example.com', 'Checking orderer host'); - t.equals(results.config.orderers.OrdererMSP.endpoints[0].port, 7050, 'Checking orderer port'); + t.equals(results.msps.OrdererMSP.id, 'OrdererMSP', 'Checking MSP ID'); + t.equals(results.msps.Org1MSP.id, 'Org1MSP', 'Checking MSP ID'); + t.equals(results.msps.Org2MSP.id, 'Org2MSP', 'Checking MSP ID'); + t.equals(results.orderers.OrdererMSP.endpoints[0].host, 'orderer.example.com', 'Checking orderer host'); + t.equals(results.orderers.OrdererMSP.endpoints[0].port, 7050, 'Checking orderer port'); t.equals(results.peers_by_org.Org1MSP.peers[0].endpoint, 'peer0.org1.example.com:7051', 'Checking peer endpoint'); t.equals(results.peers_by_org.Org1MSP.peers[0].ledger_height.low, 3, 'Checking peer ledger_height'); t.equals(results.peers_by_org.Org1MSP.peers[0].chaincodes[0].name, 'example', 'Checking peer chaincode name'); @@ -70,19 +75,20 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) { t.equals(results.endorsement_targets.example.groups.G0.peers[0].ledger_height.low, 3, 'Checking peer ledger_height'); t.equals(results.endorsement_targets.example.groups.G0.peers[0].chaincodes[0].name, chaincode_id, 'Checking peer chaincode name'); t.equals(results.endorsement_targets.example.groups.G0.peers[0].chaincodes[0].version, version, 'Checking peer chaincode version'); - t.equals(results.endorsement_targets.example.layouts[0].quantities_by_group.G0, 1, 'Checking layout quantities_by_group'); + t.equals(results.endorsement_targets.example.layouts[0].G0, 1, 'Checking layout quantities_by_group'); //logger.info('D I S C O V E R Y R E S U L T S \n %j', results); // try without the target specfied results = await channel._discover({ - chaincodeId: chaincode_id //to get a chaincode query + chaincodes: [chaincode_id], + config: true }); - t.equals(results.config.msps.OrdererMSP.id, 'OrdererMSP', 'Checking MSP ID'); - t.equals(results.config.msps.Org1MSP.id, 'Org1MSP', 'Checking MSP ID'); - t.equals(results.config.msps.Org2MSP.id, 'Org2MSP', 'Checking MSP ID'); - t.equals(results.config.orderers.OrdererMSP.endpoints[0].host, 'orderer.example.com', 'Checking orderer host'); - t.equals(results.config.orderers.OrdererMSP.endpoints[0].port, 7050, 'Checking orderer port'); + t.equals(results.msps.OrdererMSP.id, 'OrdererMSP', 'Checking MSP ID'); + t.equals(results.msps.Org1MSP.id, 'Org1MSP', 'Checking MSP ID'); + t.equals(results.msps.Org2MSP.id, 'Org2MSP', 'Checking MSP ID'); + t.equals(results.orderers.OrdererMSP.endpoints[0].host, 'orderer.example.com', 'Checking orderer host'); + t.equals(results.orderers.OrdererMSP.endpoints[0].port, 7050, 'Checking orderer port'); t.equals(results.peers_by_org.Org1MSP.peers[0].endpoint, 'peer0.org1.example.com:7051', 'Checking peer endpoint'); t.equals(results.peers_by_org.Org1MSP.peers[0].ledger_height.low, 3, 'Checking peer ledger_height'); t.equals(results.peers_by_org.Org1MSP.peers[0].chaincodes[0].name, 'example', 'Checking peer chaincode name'); @@ -91,13 +97,22 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) { t.equals(results.endorsement_targets.example.groups.G0.peers[0].ledger_height.low, 3, 'Checking peer ledger_height'); t.equals(results.endorsement_targets.example.groups.G0.peers[0].chaincodes[0].name, 'example', 'Checking peer chaincode name'); t.equals(results.endorsement_targets.example.groups.G0.peers[0].chaincodes[0].version, 'v2', 'Checking peer chaincode version'); - t.equals(results.endorsement_targets.example.layouts[0].quantities_by_group.G0, 1, 'Checking layout quantities_by_group'); + t.equals(results.endorsement_targets.example.layouts[0].G0, 1, 'Checking layout quantities_by_group'); + + // This will call the discovery under the covers and load the channel with msps, orderers, and peers + results = await channel.initialize({asLocalhost: true}); + + // check orgs ... actually gets names from the msps loaded + const orgs = channel.getOrganizations(); + for(let index in orgs) { + const org = orgs[index].id; + if(org === 'Org1MSP' || org === 'Org2MSP' || org === 'OrdererMSP') { + t.pass('Checking call to get organizations on the channel after using the discovery service for ' + org); + } else { + t.fail('Checking call to get organizations on the channel after using the discovery service for '+ org); + } + } - results = await channel._discover({ - chaincodeId: chaincode_id, - target_names: true, - hostname: 'localhost' - }); t.equals(channel.getOrderers()[0].getUrl(), 'grpcs://localhost:7050', 'Checking orderer url'); t.equals(channel.getPeers()[0].getUrl(), 'grpcs://localhost:7051', 'Checking peer url'); @@ -108,7 +123,7 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) { await queries(t, client, channel, tx_id_string); - t.pass('Successfully completed testing'); + t.pass('End discovery testing'); t.end(); }); @@ -207,6 +222,7 @@ async function setupChannel(t, client_org1, client_org2, channel_name) { throw new Error('Failed to create the channel. '); } } catch(error) { + logger.error('catch network config test error:: %s', error.stack ? error.stack : error); t.fail('Failed to create channel :'+ error); throw new Error('Failed to create the channel. '); } @@ -521,6 +537,7 @@ function transaction_monitor(t, channel_event_hub, tx_id) { } async function queries(t, client, channel, tx_id_string) { + logger.info('\n\nStart queries\n\n\n'); try { const request = { chaincodeId : 'example', diff --git a/test/integration/orderer-channel-tests.js b/test/integration/orderer-channel-tests.js index 0b2ae6279e..828344ffaa 100644 --- a/test/integration/orderer-channel-tests.js +++ b/test/integration/orderer-channel-tests.js @@ -110,6 +110,7 @@ test('\n\n** TEST ** orderer via member null data', function(t) { .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); return testUtil.getSubmitter(client, t, org); }).then( function(admin) { @@ -120,8 +121,6 @@ test('\n\n** TEST ** orderer via member null data', function(t) { ORGS.orderer.url, { 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS.orderer['server-hostname'] } ) @@ -192,6 +191,7 @@ test('\n\n** TEST ** orderer via member bad request', function(t) { .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); return testUtil.getSubmitter(client, t, org); }).then( function(admin) { diff --git a/test/integration/perf/orderer.js b/test/integration/perf/orderer.js index e1407836f2..d0b4d2b059 100644 --- a/test/integration/perf/orderer.js +++ b/test/integration/perf/orderer.js @@ -54,14 +54,13 @@ async function perfTest1(t) { let caroots = Buffer.from(data).toString(); let tlsInfo = await e2eUtils.tlsEnroll(org); + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); let orderer = client.newOrderer( ORGS.orderer.url, { name: 'perfTest1', 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS.orderer['server-hostname'], 'request-timeout': 120000 } @@ -185,14 +184,13 @@ async function perfTest2(t) { let caroots = Buffer.from(data).toString(); let tlsInfo = await e2eUtils.tlsEnroll(org); + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); let orderer = client.newOrderer( ORGS.orderer.url, { name: 'perfTest2', 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS.orderer['server-hostname'], 'request-timeout': 120000 } diff --git a/test/integration/perf/peer.js b/test/integration/perf/peer.js index 0be57b7d29..6dde524902 100644 --- a/test/integration/perf/peer.js +++ b/test/integration/perf/peer.js @@ -53,13 +53,12 @@ async function perfTest3(t) { let caroots = Buffer.from(data).toString(); let tlsInfo = await e2eUtils.tlsEnroll(org); + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); let peer = client.newPeer( ORGS[org].peer1.requests, { 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS[org].peer1['server-hostname'], 'request-timeout': 120000 } diff --git a/test/integration/query.js b/test/integration/query.js index 3d756af837..b64d9c3ea7 100644 --- a/test/integration/query.js +++ b/test/integration/query.js @@ -61,6 +61,7 @@ test(' ---->>>>> Query channel working <<<<<-----', (t) => { return e2eUtils.tlsEnroll(org).then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); return Client.newDefaultKeyValueStore({ path: testUtil.storePathForOrg(orgName) }); }).then((store) => { client.setStateStore(store); diff --git a/test/integration/upgrade.js b/test/integration/upgrade.js index eed13afd52..0bd18acb2f 100644 --- a/test/integration/upgrade.js +++ b/test/integration/upgrade.js @@ -49,6 +49,7 @@ test('\n\n **** E R R O R T E S T I N G on upgrade call', (t) => { .then((enrollment) => { t.pass('Successfully retrieved TLS certificate'); tlsInfo = enrollment; + client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); return Client.newDefaultKeyValueStore({path: testUtil.storePathForOrg(orgName)}); }).then((store) => { client.setStateStore(store); @@ -64,8 +65,6 @@ test('\n\n **** E R R O R T E S T I N G on upgrade call', (t) => { ORGS.orderer.url, { 'pem': caroots, - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS.orderer['server-hostname'] } ) @@ -80,8 +79,6 @@ test('\n\n **** E R R O R T E S T I N G on upgrade call', (t) => { ORGS[org][key].requests, { pem: Buffer.from(data).toString(), - 'clientCert': tlsInfo.certificate, - 'clientKey': tlsInfo.key, 'ssl-target-name-override': ORGS[org][key]['server-hostname'] } ); diff --git a/test/unit/channel-event-hub.js b/test/unit/channel-event-hub.js index 4c47e70f45..52934eea15 100644 --- a/test/unit/channel-event-hub.js +++ b/test/unit/channel-event-hub.js @@ -755,12 +755,10 @@ test('\n\n** ChannelEventHub test connect failure on transaction registration \n ); event_hub.connect(); - let sleep_time = 3000; - t.comment('about to sleep '+sleep_time); - return sleep(sleep_time); + return true; }).then((nothing) => { - t.pass('Sleep complete'); + t.pass('#2 call back tx test complete '); // eventhub is now actually not connected }).catch((err) => { t.fail(err.stack ? err.stack : err); @@ -798,14 +796,14 @@ test('\n\n** EventHub test reconnect on block registration \n\n', (t) => { event_hub = channel.newChannelEventHub(peer); event_hub.registerBlockEvent( (tx_id, code) => { - t.fail('Failed callback should not have been called - block test 2'); + t.fail('Failed callback should not have been called - block test 3'); t.end(); }, (error) =>{ if(error.toString().indexOf('Connect Failed')) { - t.pass('Successfully got the error call back block test 2 ::'+error); + t.pass('Successfully got the error call back block test 3 ::'+error); } else { - t.failed('Failed to get connection failed error block test 2 ::'+error); + t.failed('Failed to get connection failed error block test 3 ::'+error); } t.end(); } @@ -819,11 +817,10 @@ test('\n\n** EventHub test reconnect on block registration \n\n', (t) => { // failure will be reported to an error callback state = event_hub.checkConnection(true); t.equals(state, 'UNKNOWN_STATE', 'Check the state of the connection'); - let sleep_time = 5000; //need to sleep longer than request timeout - t.comment('about to sleep '+sleep_time); - return sleep(sleep_time); + + return true; }).then((nothing) => { - t.pass('Sleep complete'); + t.pass('#3 callback block test complete'); // t.end() should come from the callback }).catch((err) => { t.fail(err.stack ? err.stack : err); @@ -845,7 +842,3 @@ test('\n\n** Test the state conversion\n\n', (t) => { t.end(); }); - -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} diff --git a/test/unit/channel.js b/test/unit/channel.js index aaba388db8..c6d376bcb1 100644 --- a/test/unit/channel.js +++ b/test/unit/channel.js @@ -115,7 +115,6 @@ test('\n\n ** Channel - method tests **\n\n', function (t) { cp.getOrganizationName(); t.equals(cp.getName(), 'peer1', 'Checking channel peer getName'); cp.getUrl(); - cp.getClientCertHash(); cp.setRole('role',false); t.equals(cp.isInRole('role'), false, 'Checking isInRole'); t.equals(cp.isInRole('unknown'), true, 'Checking isInRole'); @@ -671,29 +670,17 @@ test('\n\n ** Channel _buildDefaultEndorsementPolicy() tests **\n\n', function ( test('\n\n ** Channel sendTransactionProposal() tests **\n\n', function (t) { var client = new Client(); var channel = new Channel('does-not-matter', client); + channel._use_discovery = false; var peer = new Peer('grpc://localhost:7051'); - t.throws( - function () { - channel.sendTransactionProposal({ - chaincodeId: 'blah', - fcn: 'init', - args: ['a', '100', 'b', '200'], - txId: 'blah' - }); - }, - /"targets" parameter not specified and no peers are set on this Channel/, - 'Channel tests, sendTransactionProposal(): "targets" parameter not specified and no peers are set on this Channel' - ); - channel.addPeer(peer); t.throws( function () { channel.sendTransactionProposal(); }, - /Missing request object for this transaction proposal/, - 'Channel tests, sendTransactionProposal(): Missing request object for this transaction proposal' + /Missing input request object on the proposal request/, + 'Channel tests, sendTransactionProposal(): Missing input request object on the proposal request' ); t.throws( @@ -735,7 +722,7 @@ test('\n\n ** Channel sendTransactionProposal() tests **\n\n', function (t) { t.end(); }); -test('\n\n ** Channel queryByChaincode() tests **\n\n', function (t) { +test('\n\n ** Channel queryByChaincode() tests **\n\n', async function (t) { let client = new Client(); var _channel = new Channel('testchannel', client); @@ -755,56 +742,35 @@ test('\n\n ** Channel queryByChaincode() tests **\n\n', function (t) { 'Channel tests, queryByChaincode(): "targets" parameter not specified and no peers are set.' ); - var TEST_CERT_PEM = require('./user.js').TEST_CERT_PEM; - var member = new User('admin'); client = new Client(); + await setMember(client); - // do some setup for following test - utils.setConfigSetting('key-value-store', 'fabric-client/lib/impl/FileKeyValueStore.js'); - Client.newDefaultKeyValueStore({ - path: testutil.KVS - }).then(function (store) { - client.setStateStore(store); - var cryptoUtils = utils.newCryptoSuite(); - return cryptoUtils.generateKey({ ephemeral: true }); - }).then(function (key) { - // the private key and cert don't match, but it's ok, the code doesn't check - return member.setEnrollment(key, TEST_CERT_PEM, 'DEFAULT'); - }).then(function () { - client.setUserContext(member, true); - var channel = client.newChannel('any-channel-goes'); - var peer = client.newPeer('grpc://localhost:7051'); - channel.addPeer(peer); - - t.throws( - function () { - channel.queryByChaincode({ - chaincodeId: 'blah', - fcn: 'invoke' - }); - }, - /Missing "args" in Transaction/, - 'Channel tests, queryByChaincode(): Missing "args" in Transaction' - ); - - t.throws( - function () { - channel.queryByChaincode({ - fcn: 'init', - args: ['a', '100', 'b', '200'] - }); - }, - /Missing "chaincodeId" parameter/, - 'Channel tests, queryByChaincode(): Missing "chaincodeId" parameter' - ); - t.end(); - }).catch( - function (err) { - t.fail('Channel queryByChaincode() failed '); - logger.error(err.stack ? err.stack : err); - t.end(); - } + var channel = client.newChannel('any-channel-goes'); + var peer = client.newPeer('grpc://localhost:7051'); + channel.addPeer(peer); + + t.throws( + function () { + channel.queryByChaincode({ + chaincodeId: 'blah', + fcn: 'invoke' + }); + }, + /Missing "args" in Transaction/, + 'Channel tests, queryByChaincode(): Missing "args" in Transaction' + ); + + t.throws( + function () { + channel.queryByChaincode({ + fcn: 'init', + args: ['a', '100', 'b', '200'] + }); + }, + /Missing "chaincodeId" parameter/, + 'Channel tests, queryByChaincode(): Missing "chaincodeId" parameter' ); + t.end(); }); test('\n\n ** Channel sendTransaction() tests **\n\n', function (t) { @@ -1040,10 +1006,10 @@ test('\n\n*** Test per-call timeout support ***\n', function (t) { // stub out the calls that requires getting MSPs from the orderer, or // a valid user context - let clientUtils = Channel.__get__('clientUtils'); - sandbox.stub(clientUtils, 'buildHeader').returns(Buffer.from('dummyHeader')); - sandbox.stub(clientUtils, 'buildProposal').returns(Buffer.from('dummyProposal')); - sandbox.stub(clientUtils, 'signProposal').returns(Buffer.from('dummyProposal')); + let client_utils = Channel.__get__('client_utils'); + sandbox.stub(client_utils, 'buildHeader').returns(Buffer.from('dummyHeader')); + sandbox.stub(client_utils, 'buildProposal').returns(Buffer.from('dummyProposal')); + sandbox.stub(client_utils, 'signProposal').returns(Buffer.from('dummyProposal')); client._userContext = { getIdentity: function () { return ''; }, getSigningIdentity: function () { return ''; } @@ -1134,3 +1100,98 @@ test('\n\n ** Channel executeTransaction() tests **\n\n', function (t) { t.end(); }); + +test('\n\n ** Channel Discover) tests **\n\n', async function (t) { + const client = new Client(); + const channel = new Channel('does-not-matter', client); + channel._use_discovery = true; + t.throws( + function () { + channel.initialize(); + }, + /"target" parameter not specified and no peers are set on this Channel/, + 'Channel tests, sendTransactionProposal(): "target" parameter not specified and no peers are set on this Channeln' + ); + + + var peer = new Peer('grpc://localhost:9999'); + + channel.addPeer(peer); + + t.throws( + function () { + channel.initialize({ + target: peer + }); + }, + /No identity has been assigned to this client/, + 'Channel tests, sendTransactionProposal(): No identity has been assigned to this client' + ); + + t.throws( + function () { + channel.initialize({ + discover: 'BAD' + }); + }, + /Reqauest parameter "discover" must be boolean/, + 'Channel tests, Reqauest parameter "discover" must be boolean' + ); + + await setMember(client); + + try { + await channel.initialize({ + target: peer + }); + } catch(error) { + if(error.message.includes('deadline')) { + t.pass('Check Failed to connect before the deadline'); + } else { + t.fail('Did not get Failed to connect before the deadline'); + } + } + + try { + await channel.initialize({ + target: peer, + endorsementHandler: 'no.where' + }); + t.fail('able to initialize channel with a bad endorsement handler path'); + } catch(error) { + if(error.message.includes('Cannot find module')) { + t.pass('Check Failed to initialize channel with bad endorsement handler path'); + } else { + t.fail('Receive other failure '+ error.toString()); + } + } + + const handler_path_temp = client.getConfigSetting('endorsement-handler-path'); + try { + client.setConfigSetting('endorsement-handler-path', 'bad.path'); + client.newChannel('test-channel'); + t.fail('able to create channel with a bad endorsement handler path'); + } catch(error) { + if(error.message.includes('Cannot find module')) { + t.pass('Check Failed to create channel with bad endorsement handler path'); + } else { + t.fail('Receive other failure '+ error.toString()); + } + } + client.setConfigSetting('endorsement-handler-path', handler_path_temp); + + t.end(); +}); + +async function setMember(client) { + // do some setup for following test + var member = new User('admin'); + utils.setConfigSetting('key-value-store', 'fabric-client/lib/impl/FileKeyValueStore.js'); + const store = await Client.newDefaultKeyValueStore({path: testutil.KVS}); + client.setStateStore(store); + const cryptoUtils = utils.newCryptoSuite(); + const key = await cryptoUtils.generateKey({ ephemeral: true }); + var TEST_CERT_PEM = require('./user.js').TEST_CERT_PEM; + await member.setEnrollment(key, TEST_CERT_PEM, 'DEFAULT'); + client.setUserContext(member, true); +} diff --git a/test/unit/client.js b/test/unit/client.js index 28dc8d01ed..1ac1db8fc5 100644 --- a/test/unit/client.js +++ b/test/unit/client.js @@ -28,6 +28,19 @@ var _configtxProto = grpc.load(__dirname + '/../../fabric-client/lib/protos/comm var rewire = require('rewire'); var ClientRewired = rewire('fabric-client/lib/Client.js'); +var aPem = '-----BEGIN CERTIFICATE-----' + + 'MIIBwTCCAUegAwIBAgIBATAKBggqhkjOPQQDAzApMQswCQYDVQQGEwJVUzEMMAoG' + + 'A1UEChMDSUJNMQwwCgYDVQQDEwNPQkMwHhcNMTYwMTIxMjI0OTUxWhcNMTYwNDIw' + + 'MjI0OTUxWjApMQswCQYDVQQGEwJVUzEMMAoGA1UEChMDSUJNMQwwCgYDVQQDEwNP' + + 'QkMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAR6YAoPOwMzIVi+P83V79I6BeIyJeaM' + + 'meqWbmwQsTRlKD6g0L0YvczQO2vp+DbxRN11okGq3O/ctcPzvPXvm7Mcbb3whgXW' + + 'RjbsX6wn25tF2/hU6fQsyQLPiJuNj/yxknSjQzBBMA4GA1UdDwEB/wQEAwIChDAP' + + 'BgNVHRMBAf8EBTADAQH/MA0GA1UdDgQGBAQBAgMEMA8GA1UdIwQIMAaABAECAwQw' + + 'CgYIKoZIzj0EAwMDaAAwZQIxAITGmq+x5N7Q1jrLt3QFRtTKsuNIosnlV4LR54l3' + + 'yyDo17Ts0YLyC0pZQFd+GURSOQIwP/XAwoMcbJJtOVeW/UL2EOqmKA2ygmWX5kte' + + '9Lngf550S6gPEWuDQOcY95B+x3eH' + + '-----END CERTIFICATE-----'; + test('\n\n ** index.js **\n\n', function (t) { testutil.resetDefaults(); @@ -1134,7 +1147,7 @@ test('\n\n*** Test normalizeX509 ***\n', function(t) { Client.normalizeX509('cause error'); }, /Failed to find start line or end line of the certificate./, - 'Check that a bad strean will throw error' + 'Check that a bad stream will throw error' ); var TEST_CERT_PEM = '-----BEGIN CERTIFICATE-----' + @@ -1149,3 +1162,17 @@ test('\n\n*** Test normalizeX509 ***\n', function(t) { t.end(); }); + +test('\n\n*** Test TLS ClientCert ***\n', function(t) { + let client = new Client(); + client.setTlsClientCertAndKey(aPem, aPem); + t.pass('Able to set the client cert and client key'); + const tls_cert_key = {}; + client.addTlsClientCertAndKey(tls_cert_key); + t.equals(tls_cert_key.clientCert, aPem, 'Checking being able to update an options object with the client cert'); + t.equals(tls_cert_key.clientKey, aPem, 'Checking being able to update an options object with the client key'); + + t.equals(client.getClientCertHash().toString('hex'), 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', 'checking the client certificate hash'); + + t.end(); +}); diff --git a/test/unit/endorser-handler.js b/test/unit/endorser-handler.js new file mode 100644 index 0000000000..a621aea4c8 --- /dev/null +++ b/test/unit/endorser-handler.js @@ -0,0 +1,163 @@ +/** + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +const tape = require('tape'); +const _test = require('tape-promise').default; +const test = _test(tape); + + + +const Client = require('fabric-client'); +const testutil = require('./util.js'); + +const utils = require('fabric-client/lib/utils.js'); +const logger = utils.getLogger('channel'); +const DiscoveryEndorsementHandler = require('fabric-client/lib/impl/DiscoveryEndorsementHandler.js'); + +const results = { + msps:{ + OrdererMSP:{ + id:'OrdererMSP', + orgs:[ ], + rootCerts:'-----BEGIN CERTIFICATE----- -----END CERTIFICATE-----\n', + intermediateCerts:'', + admins:'-----BEGIN CERTIFICATE----- -----END CERTIFICATE-----\n', + tls_intermediate_certs:'' + }, + Org2MSP:{ + id:'Org2MSP', + orgs:[ ], + rootCerts:'-----BEGIN CERTIFICATE----- -----END CERTIFICATE-----\n', + intermediateCerts:'', + admins:'-----BEGIN CERTIFICATE----- -----END CERTIFICATE-----\n', + tls_intermediate_certs:'' + }, + Org1MSP:{ + id:'Org1MSP', + orgs:[ ], + rootCerts:'-----BEGIN CERTIFICATE----- -----END CERTIFICATE-----\n', + intermediateCerts:'', + admins:'-----BEGIN CERTIFICATE----- -----END CERTIFICATE-----\n', + tls_intermediate_certs:'' + }, + }, + orderers:{ + OrdererMSP:{ + endpoints:[ + { + host:'orderer.example.com', + port:7050, + name:'orderer.example.com' + } + ] + } + }, + peers_by_org:{ + Org1MSP:{ + peers:[ + { + mspid:'Org1MSP', + endpoint:'peer0.org1.example.com:7051', + ledger_height:4, + chaincodes:[{name:'example',version:'v2'}], + name:'peer0.org1.example.com' + } + ] + }, + Org2MSP:{ + peers:[ + { + mspid:'Org2MSP', + endpoint:'peer0.org2.example.com:7051', + ledger_height:4, + chaincodes:[{name:'example',version:'v2'}], + name:'peer0.org2.example.com' + } + ] + } + }, + endorsement_targets:{ + example:{ + groups:{ + G0:{ + peers:[ + { + mspid:'Org1MSP', + endpoint:'peer0.org1.example.com:7051', + ledger_height:4, + chaincodes:[{name:'example',version:'v2'}], + name:'peer0.org1.example.com' + }, + { + mspid:'Org2MSP', + endpoint:'peer0.org2.example.com:7051', + ledger_height:4, + chaincodes:[{name:'example',version:'v2'}], + name:'peer0.org2.example.com' + }, + ] + } + }, + layouts:[{G0:1}] + } + } +}; + + +test('\n\n ** DiscoveryEndorsementHandler - test **\n\n', async (t) => { + + const client = new Client(); + const temp = client.getConfigSetting('endorsement-handler-path'); + client.setConfigSetting('endorsement-handler-path', 'fabric-client/lib/impl/DiscoveryEndorsementHandler.js'); + const channel = client.newChannel('handlertest'); + const handler = channel._endorsement_handler; + if(handler && handler.endorse) { + t.pass('Able to have the channel create the handler'); + } else { + t.fail('Channel was not able to create the handler'); + t.end(); + return; + } + let parameters = null; + await errorChecker(t, handler, parameters, 'Missing all'); + parameters = {}; + await errorChecker(t, handler, parameters, 'Missing "request"'); + parameters.request = {}; + await errorChecker(t, handler, parameters, 'Missing "signed_proposal"'); + parameters.signed_proposal = {}; + await errorChecker(t, handler, parameters, 'Missing "chaincodeId"'); + parameters.request.chaincodeId = 'somename'; + await errorChecker(t, handler, parameters, 'Missing "txId"'); + parameters.request.txId = 'someid'; + await errorChecker(t, handler, parameters, 'Missing "args"'); + + if(temp) client.setConfigSetting('endorsement-handler-path', temp); + t.end(); +}); + +async function errorChecker(t, handler, parameters, error_text) { + try { + await handler.endorse(parameters); + } catch(error) { + if(error.toString().indexOf(error_text)) { + t.pass('Check for :' + error_text); + } else { + t.fail('Check for :' + error_text); + } + } +} diff --git a/test/unit/remote.js b/test/unit/remote.js index 9efdd36cfc..3610f63062 100644 --- a/test/unit/remote.js +++ b/test/unit/remote.js @@ -6,18 +6,18 @@ 'use strict'; -var tape = require('tape'); -var _test = require('tape-promise').default; -var test = _test(tape); +const tape = require('tape'); +const _test = require('tape-promise').default; +const test = _test(tape); -var testutil = require('./util.js'); +const testutil = require('./util.js'); -var Remote = require('fabric-client/lib/Remote.js'); -var Peer = require('fabric-client/lib/Peer.js'); -var Orderer = require('fabric-client/lib/Orderer.js'); -var utils = require('fabric-client/lib/utils.js'); +const Remote = require('fabric-client/lib/Remote.js'); +const Peer = require('fabric-client/lib/Peer.js'); +const Orderer = require('fabric-client/lib/Orderer.js'); +const utils = require('fabric-client/lib/utils.js'); -var aPem = '-----BEGIN CERTIFICATE-----' + +const aPem = '-----BEGIN CERTIFICATE-----' + 'MIIBwTCCAUegAwIBAgIBATAKBggqhkjOPQQDAzApMQswCQYDVQQGEwJVUzEMMAoG' + 'A1UEChMDSUJNMQwwCgYDVQQDEwNPQkMwHhcNMTYwMTIxMjI0OTUxWhcNMTYwNDIw' + 'MjI0OTUxWjApMQswCQYDVQQGEwJVUzEMMAoGA1UEChMDSUJNMQwwCgYDVQQDEwNP' + @@ -29,22 +29,22 @@ var aPem = '-----BEGIN CERTIFICATE-----' + 'yyDo17Ts0YLyC0pZQFd+GURSOQIwP/XAwoMcbJJtOVeW/UL2EOqmKA2ygmWX5kte' + '9Lngf550S6gPEWuDQOcY95B+x3eH' + '-----END CERTIFICATE-----'; -var defaultCiphers = 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256' + +const defaultCiphers = 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256' + ':ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-GCM-SHA384' + ':ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256' + ':ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384'; -var aHostname = 'atesthostname'; -var aHostnameOverride = 'atesthostnameoverride'; +const aHost = 'atesthostname:9999'; +const url = 'grpcs://' + aHost; +const aHostnameOverride = 'atesthostnameoverride'; test('\n\n ** Remote node tests **\n\n', function (t) { testutil.resetDefaults(); console.log('\n * REMOTE *'); //Peer: secure grpcs, requires opts.pem - var url = 'grpcs://' + aHostname + ':aport'; - var opts = { pem: aPem }; - var remote = null; + let opts = { pem: aPem }; + let remote = null; t.throws( function () { remote = new Remote(url); @@ -71,7 +71,6 @@ test('\n\n ** Remote node tests **\n\n', function (t) { t.throws( function () { - url = 'grpcs://' + aHostname + ':aport'; remote = new Remote(url, {pem: aPem, clientCert: aPem}); }, /^Error: clientKey and clientCert are both required./, @@ -105,7 +104,6 @@ test('\n\n ** Remote node tests **\n\n', function (t) { opts = {pem: aPem, 'grpc-wait-for-ready-timeout': '1000'}; t.throws( function () { - url = 'grpcs://' + aHostname + ':aport'; remote = new Remote(url, opts); }, /^Error: Expect an integer value of grpc-wait-for-ready-timeout, found string/, @@ -114,22 +112,22 @@ test('\n\n ** Remote node tests **\n\n', function (t) { opts = { pem: aPem, 'ssl-target-name-override': aHostnameOverride }; remote = new Remote(url, opts); - t.equal(aHostname, remote._endpoint.addr, 'GRPC Options tests: new Remote grpcs with opts created'); - t.equal(remote.toString(), ' Remote : {url:grpcs://' + aHostname + ':aport}', 'Checking that peer.toString() reports correctly'); + t.equal(remote._endpoint.addr, aHost, 'GRPC Options tests: new Remote grpcs with opts created'); + t.equal(remote.getName(), aHost, 'checking the name assignment'); + t.equal(remote.toString(), ' Remote : {url:grpcs://atesthostname:9999}', 'Checking that peer.toString() reports correctly'); t.equal(remote._grpc_wait_for_ready_timeout, 3000, 'Remote should have grpc waitForReady timeout default to 3000'); - - url = 'grpc://' + aHostname + ':aport'; - remote = new Remote(url); - t.equal(aHostname, remote._endpoint.addr, 'GRPC Options tests: new Remote grpc with opts = null _endpoint.addr created'); t.ok(remote._endpoint.creds, 'GRPC Options tests: new Remote grpc with opts = null _endpoint.creds created'); + const dummy_name = 'areallygreatname'; opts = { pem: aPem, 'grpc.dummy_property': 'some_value', 'ssl-target-name-override': aHostnameOverride, - 'grpc-wait-for-ready-timeout': 500 + 'grpc-wait-for-ready-timeout': 500, + 'name': dummy_name }; remote = new Remote(url, opts); + t.equal(remote.getName(), dummy_name, 'Check passing in the name option'); t.equal(aHostnameOverride, remote._options['grpc.ssl_target_name_override'], 'GRPC Options tests: new Remote grpc with opts ssl-target-name-override created'); t.ok(remote._endpoint.creds, 'GRPC Options tests: new Remote grpc with opts _endpoint.creds created'); t.equal('some_value', remote._options['grpc.dummy_property'], 'GRPC options tests: pass-through option properties'); @@ -138,9 +136,8 @@ test('\n\n ** Remote node tests **\n\n', function (t) { console.log('\n * PEER *'); //Peer: secure grpcs, requires opts.pem - url = 'grpcs://' + aHostname + ':aport'; opts = { pem: aPem }; - var peer = null; + let peer = null; t.doesNotThrow( function () { peer = new Peer(url, opts); @@ -151,25 +148,20 @@ test('\n\n ** Remote node tests **\n\n', function (t) { opts = { pem: aPem, 'ssl-target-name-override': aHostnameOverride }; peer = new Peer(url, opts); - t.equal(aHostname, peer._endpoint.addr, 'GRPC Options tests: new Peer grpcs with opts created'); - t.equal(peer.toString(), 'Peer:{url:grpcs://' + aHostname + ':aport}', 'Checking that peer.toString() reports correctly'); - //Peer: insecure grpc, opts.pem optional - url = 'grpc://' + aHostname + ':aport'; - peer = new Peer(url); - t.equal(aHostname, peer._endpoint.addr, 'GRPC Options tests: new Peer grpc with opts = null _endpoint.addr created'); + t.equal(aHost, peer._endpoint.addr, 'GRPC Options tests: new Peer grpcs with opts created'); + t.equal(peer.toString(), 'Peer:{url:grpcs://atesthostname:9999}', 'Checking that peer.toString() reports correctly'); t.equal(peer._grpc_wait_for_ready_timeout, 3000, 'Peer should have _grpc_wait_for_ready_timeout equals 3000'); t.ok(peer._endpoint.creds, 'GRPC Options tests: new Peer grpc with opts = null _endpoint.creds created'); opts = { pem: aPem, 'ssl-target-name-override': aHostnameOverride }; peer = new Peer(url, opts); - t.equal(aHostname, peer._endpoint.addr, 'GRPC Options tests: new Peer grpc with opts _endpoint.addr created'); + t.equal(aHost, peer._endpoint.addr, 'GRPC Options tests: new Peer grpc with opts _endpoint.addr created'); t.ok(peer._endpoint.creds, 'GRPC Options tests: new Peer grpc with opts _endpoint.creds created'); t.equal(peer.getUrl(), url, 'checking that getURL works'); t.throws( function () { - url = 'http://' + aHostname + ':aport'; - peer = new Peer(url, opts); + peer = new Peer('http://somehost:8888', opts); }, /^InvalidProtocol: Invalid protocol: http./, 'GRPC Options tests: new Peer http should throw ' + @@ -179,7 +171,6 @@ test('\n\n ** Remote node tests **\n\n', function (t) { opts = {}; t.throws( function () { - url = 'grpcs://' + aHostname + ':aport'; peer = new Peer(url, opts); }, /^Error: PEM encoded certificate is required./, @@ -189,7 +180,6 @@ test('\n\n ** Remote node tests **\n\n', function (t) { t.throws( function () { - url = 'grpcs://' + aHostname + ':aport'; peer = new Peer(url); }, /^Error: PEM encoded certificate is required./, @@ -200,7 +190,6 @@ test('\n\n ** Remote node tests **\n\n', function (t) { opts = {pem: aPem, clientKey: aPem, clientCert: aPem}; t.doesNotThrow( function () { - url = 'grpcs://' + aHostname + ':aport'; peer = new Peer(url, opts); }, null, @@ -210,7 +199,6 @@ test('\n\n ** Remote node tests **\n\n', function (t) { opts = {pem: aPem, clientKey: aPem}; t.throws( function () { - url = 'grpcs://' + aHostname + ':aport'; peer = new Peer(url, opts); }, /^Error: clientKey and clientCert are both required./, @@ -221,7 +209,6 @@ test('\n\n ** Remote node tests **\n\n', function (t) { opts = {pem: aPem, clientCert: aPem}; t.throws( function () { - url = 'grpcs://' + aHostname + ':aport'; peer = new Peer(url, opts); }, /^Error: clientKey and clientCert are both required./, @@ -231,9 +218,8 @@ test('\n\n ** Remote node tests **\n\n', function (t) { console.log('\n * ORDERER *'); //Peer: secure grpcs, requires opts.pem - url = 'grpcs://' + aHostname + ':aport'; opts = { pem: aPem }; - var orderer = null; + let orderer = null; t.doesNotThrow( function () { orderer = new Orderer(url, opts); @@ -244,19 +230,14 @@ test('\n\n ** Remote node tests **\n\n', function (t) { opts = { pem: aPem, 'ssl-target-name-override': aHostnameOverride }; orderer = new Orderer(url, opts); - t.equal(aHostname, orderer._endpoint.addr, 'GRPC Options tests: new Orderer grpcs with opts created'); - t.equal(orderer.toString(), 'Orderer:{url:grpcs://' + aHostname + ':aport}', 'Checking that orderer.toString() reports correctly'); + t.equal(aHost, orderer._endpoint.addr, 'GRPC Options tests: new Orderer grpcs with opts created'); + t.equal(orderer.toString(), 'Orderer:{url:grpcs://atesthostname:9999}', 'Checking that orderer.toString() reports correctly'); t.equal(orderer._grpc_wait_for_ready_timeout, 3000, 'orderer should have _grpc_wait_for_ready_timeout equals 3000'); - - //Orderer: insecure grpc, opts.pem optional - url = 'grpc://' + aHostname + ':aport'; - orderer = new Orderer(url); - t.equal(aHostname, orderer._endpoint.addr, 'GRPC Options tests: new Orederer grpc with opts = null _endpoint.addr created'); t.ok(orderer._endpoint.creds, 'GRPC Options tests: new Orderer grpc with opts = null _endpoint.creds created'); opts = { pem: aPem, 'ssl-target-name-override': aHostnameOverride }; orderer = new Orderer(url, opts); - t.equal(aHostname, orderer._endpoint.addr, 'GRPC Options tests: new Orederer grpc with opts _endpoint.addr created'); + t.equal(aHost, orderer._endpoint.addr, 'GRPC Options tests: new Orederer grpc with opts _endpoint.addr created'); t.ok(orderer._endpoint.creds, 'GRPC Options tests: new Orderer grpc with opts _endpoint.creds created'); opts = { pem: aPem, 'request-timeout': 2000 }; @@ -265,8 +246,7 @@ test('\n\n ** Remote node tests **\n\n', function (t) { t.throws( function () { - url = 'http://' + aHostname + ':aport'; - orderer = new Orderer(url, opts); + orderer = new Orderer('http://somehost:8888', opts); }, /^InvalidProtocol: Invalid protocol: http./, 'GRPC Options tests: new Orderer should throw ' + @@ -276,7 +256,6 @@ test('\n\n ** Remote node tests **\n\n', function (t) { opts = {}; t.throws( function () { - url = 'grpcs://' + aHostname + ':aport'; orderer = new Orderer(url, opts); }, /^Error: PEM encoded certificate is required./, @@ -286,7 +265,6 @@ test('\n\n ** Remote node tests **\n\n', function (t) { t.throws( function () { - url = 'grpcs://' + aHostname + ':aport'; orderer = new Orderer(url); }, /^Error: PEM encoded certificate is required./, @@ -297,7 +275,6 @@ test('\n\n ** Remote node tests **\n\n', function (t) { opts = {pem: aPem, clientKey: aPem, clientCert: aPem}; t.doesNotThrow( function () { - url = 'grpcs://' + aHostname + ':aport'; orderer = new Orderer(url, opts); }, null, @@ -307,7 +284,6 @@ test('\n\n ** Remote node tests **\n\n', function (t) { opts = {pem: aPem, clientKey: aPem}; t.throws( function () { - url = 'grpcs://' + aHostname + ':aport'; orderer = new Orderer(url, opts); }, /^Error: clientKey and clientCert are both required./, @@ -318,7 +294,6 @@ test('\n\n ** Remote node tests **\n\n', function (t) { opts = {pem: aPem, clientCert: aPem}; t.throws( function () { - url = 'grpcs://' + aHostname + ':aport'; orderer = new Orderer(url, opts); }, /^Error: clientKey and clientCert are both required./, @@ -336,11 +311,3 @@ test('\n\n ** Remote node tests **\n\n', function (t) { t.end(); }); - -test('Orderer clientCert test', function(t) { - var orderer = new Orderer('grpc://127.0.0.1:5005', {clientCert: aPem}); - - t.equals(orderer.getClientCertHash().toString('hex'), 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', 'checking the default client certificate hash'); - - t.end(); -});