Skip to content
This repository was archived by the owner on Apr 22, 2025. It is now read-only.

Commit 475fc7d

Browse files
authored
FABN-1612 cherrypick from master to release-2.2 (#305)
* FABN-1608 remove error message from discovery (#289) When a group has insufficient members after filtering based on user's requirements (specific mspids, specific peers, ledger height) an error message was posted. This will be changed to a debug message as this is not an error, but an expected result of the filtering. Signed-off-by: Bret Harrison <[email protected]> * FABN-1607 Allow future startBlocks on EventService (#286) Allow the setting of startBlocks when setting up an EventService that have not happened and may not happen for sometime. We must keep the stream setup timer to be able to monitor for errors from the Peer event service, however we will check if the timer pops if we are waiting for a defined start block. In this case we will assume that the stream is all good and resolve the start service for the user call. Signed-off-by: Bret Harrison <[email protected]> * FABN-1560 add disconnected peer to channel (#288) Allow for all service endpoints to be disconnected when added to a channel. When a service endpoint (peer, events, orderer, discovery) is added to a channel, it may not be active or it may go down between usages. The service connection will be checked and reset when an outbound request is made. Signed-off-by: Bret Harrison <[email protected]> * FABN-1601 discovery required organizations (#291) When a discovery user knows the organizations that are needed to endorse a proposal, they may use the 'requiredOrgs' setting to have the DiscoveryHandler send it to peer that have been discovered for the required organizations. Signed-off-by: Bret Harrison <[email protected]> * FABN-1596 add noPrivateReads (#293) Allow users to set the "noPrivateReads" when setting up a discovery hint that will be used as the interest when the discovery service builds an endorsement plan. Signed-off-by: Bret Harrison <[email protected]> * FABN-1611 Blockdecoder needs kv_reads array (#297) The RangeQueryInfo raw_reads attribute is a QueryRead message object which has an array of KVRead objects and is not an array itself. Signed-off-by: Bret Harrison <[email protected]>
1 parent 8f36a57 commit 475fc7d

24 files changed

+570
-130
lines changed

fabric-common/lib/BlockDecoder.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1513,7 +1513,7 @@ function decodeRangeQueryInfo(rangeQueryInfoProto) {
15131513
if (rangeQueryInfoProto.raw_reads) {
15141514
range_query_info.raw_reads = {};
15151515
range_query_info.raw_reads.kv_reads = [];
1516-
for (const kVReadProto of rangeQueryInfoProto.raw_reads) {
1516+
for (const kVReadProto of rangeQueryInfoProto.raw_reads.kv_reads) {
15171517
range_query_info.raw_reads.kv_reads.push(decodeKVRead(kVReadProto));
15181518
}
15191519
} else if (rangeQueryInfoProto.reads_merkle_hashes) {

fabric-common/lib/Channel.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,8 @@ const Channel = class {
255255
if (!(endorser.type === 'Endorser')) {
256256
throw Error('Missing valid endorser instance');
257257
}
258-
if (!(endorser.connected)) {
259-
throw Error('Endorser must be connected');
258+
if (!endorser.isConnectable()) {
259+
throw Error('Endorser must be connectable');
260260
}
261261
const name = endorser.name;
262262
const check = this.endorsers.get(name);
@@ -336,8 +336,8 @@ const Channel = class {
336336
if (!(committer.type === 'Committer')) {
337337
throw Error('Missing valid committer instance');
338338
}
339-
if (!(committer.connected)) {
340-
throw Error('Committer must be connected');
339+
if (!committer.isConnectable()) {
340+
throw Error('Committer must be connectable');
341341
}
342342
const name = committer.name;
343343
const check = this.committers.get(name);

fabric-common/lib/Commit.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,19 +182,28 @@ class Commit extends Proposal {
182182
} else if (targets) {
183183
logger.debug('%s - sending to the targets', method);
184184
const committers = this.channel.getTargetCommitters(targets);
185-
let bad_result = {};
186-
bad_result.status = 'UNKNOWN';
185+
let result;
187186
for (const committer of committers) {
188-
const result = await committer.sendBroadcast(envelope, requestTimeout);
189-
if (result.status === 'SUCCESS') {
190-
191-
return result;
187+
const isConnected = await committer.checkConnection();
188+
if (isConnected) {
189+
try {
190+
result = await committer.sendBroadcast(envelope, requestTimeout);
191+
if (result.status === 'SUCCESS') {
192+
break;
193+
}
194+
} catch (error) {
195+
logger.error('%s - Unable to commit on %s ::%s', method, committer.name, error);
196+
result = error;
197+
}
192198
} else {
193-
bad_result = result;
199+
result = new Error(`Committer ${committer.name} is not connected`);
194200
}
195201
}
202+
if (result instanceof Error) {
203+
throw result;
204+
}
196205

197-
return bad_result;
206+
return result;
198207
} else {
199208
throw checkParameter('targets');
200209
}

fabric-common/lib/DiscoveryHandler.js

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -112,18 +112,23 @@ class DiscoveryHandler extends ServiceHandler {
112112
for (const committer of committers) {
113113
logger.debug('%s - sending to committer %s', method, committer.name);
114114
try {
115-
const results = await committer.sendBroadcast(signedEnvelope, timeout);
116-
if (results) {
117-
if (results.status === 'SUCCESS') {
118-
logger.debug('%s - Successfully sent transaction to the committer %s', method, committer.name);
119-
return results;
115+
const isConnected = await committer.checkConnection();
116+
if (isConnected) {
117+
const results = await committer.sendBroadcast(signedEnvelope, timeout);
118+
if (results) {
119+
if (results.status === 'SUCCESS') {
120+
logger.debug('%s - Successfully sent transaction to the committer %s', method, committer.name);
121+
return results;
122+
} else {
123+
logger.debug('%s - Failed to send transaction successfully to the committer status:%s', method, results.status);
124+
return_error = new Error('Failed to send transaction successfully to the committer status:' + results.status);
125+
}
120126
} else {
121-
logger.debug('%s - Failed to send transaction successfully to the committer status:%s', method, results.status);
122-
return_error = new Error('Failed to send transaction successfully to the committer status:' + results.status);
127+
return_error = new Error('Failed to send transaction to the committer');
128+
logger.debug('%s - Failed to send transaction to the committer %s', method, committer.name);
123129
}
124130
} else {
125-
return_error = new Error('Failed to send transaction to the committer');
126-
logger.debug('%s - Failed to send transaction to the committer %s', method, committer.name);
131+
return_error = new Error(`Committer ${committer.name} is not connected`);
127132
}
128133
} catch (error) {
129134
logger.debug('%s - Caught: %s', method, error.toString());
@@ -155,7 +160,20 @@ class DiscoveryHandler extends ServiceHandler {
155160

156161
const results = await this.discovery.getDiscoveryResults(true);
157162

158-
if (results && results.endorsement_plan) {
163+
if (results && request.requiredOrgs) {
164+
// special case when user knows which organizations to send the endorsement
165+
// let's build our own endorsement plan so that we can use the sorting and sending code
166+
const endorsement_plan = this._buildRequiredOrgPlan(results.peers_by_org);
167+
168+
// remove all org and peer
169+
const orgs_request = {
170+
sort: request.sort,
171+
preferredHeightGap: request.preferredHeightGap
172+
};
173+
174+
return this._endorse(endorsement_plan, orgs_request, signedProposal, timeout);
175+
} else if (results && results.endorsement_plan) {
176+
// normal processing of the discovery results
159177
const working_discovery = JSON.parse(JSON.stringify(results.endorsement_plan));
160178

161179
return this._endorse(working_discovery, request, signedProposal, timeout);
@@ -259,7 +277,7 @@ class DiscoveryHandler extends ServiceHandler {
259277
if (required > group.peers.length) {
260278
results.success = false;
261279
const error = new Error(`Endorsement plan group does not contain enough peers (${group.peers.length}) to satisfy policy (required:${required})`);
262-
logger.error(error);
280+
logger.debug(error.message);
263281
results.endorsements.push(error);
264282
break; // no need to look at other groups, this layout failed
265283
}
@@ -302,6 +320,23 @@ class DiscoveryHandler extends ServiceHandler {
302320
return responses;
303321
}
304322

323+
_buildRequiredOrgPlan(peers_by_org) {
324+
const method = '_buildRequiredOrgPlan';
325+
logger.debug('%s - starting', method);
326+
const endorsement_plan = {plan_id: 'required organizations'};
327+
endorsement_plan.groups = {};
328+
endorsement_plan.layouts = [{}]; // only one layout which will have all organizations
329+
330+
for (const mspid in peers_by_org) {
331+
logger.debug(`${method} - found org:${mspid}`);
332+
endorsement_plan.groups[mspid] = {}; // make a group for each organization
333+
endorsement_plan.groups[mspid].peers = peers_by_org[mspid].peers; // now put in all peers from that organization
334+
endorsement_plan.layouts[0][mspid] = 1; // add this org to the one layout and require one peer to endorse
335+
}
336+
337+
return endorsement_plan;
338+
}
339+
305340
/*
306341
* utility method to build a promise that will return one of the required
307342
* endorsements or an error object
@@ -326,9 +361,14 @@ class DiscoveryHandler extends ServiceHandler {
326361
logger.debug('%s - send endorsement to %s', method, peer_info.name);
327362
peer_info.in_use = true;
328363
try {
329-
endorsement = await peer.sendProposal(proposal, timeout);
330-
// save this endorsement results in case we try this peer again
331-
logger.debug('%s - endorsement completed to %s', method, peer_info.name);
364+
const isConnected = await peer.checkConnection();
365+
if (isConnected) {
366+
endorsement = await peer.sendProposal(proposal, timeout);
367+
// save this endorsement results in case we try this peer again
368+
logger.debug('%s - endorsement completed to %s', method, peer_info.name);
369+
} else {
370+
endorsement = new Error(`Peer ${peer.name} is not connected`);
371+
}
332372
} catch (error) {
333373
endorsement = error;
334374
logger.error('%s - error on endorsement to %s error %s', method, peer_info.name, error);

fabric-common/lib/DiscoveryService.js

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ class DiscoveryService extends ServiceAction {
7070
}
7171

7272
for (const discoverer of targets) {
73-
if (discoverer.connected || discoverer.isConnectable()) {
74-
logger.debug('%s - target is or could be connected %s', method, discoverer.name);
73+
if (discoverer.isConnectable()) {
74+
logger.debug('%s - target is connectable%s', method, discoverer.name);
7575
} else {
76-
throw Error(`Discoverer ${discoverer.name} is not connected`);
76+
throw Error(`Discoverer ${discoverer.name} is not connectable`);
7777
}
7878
}
7979
// must be all targets are connected
@@ -103,14 +103,18 @@ class DiscoveryService extends ServiceAction {
103103
* sending to the peer.
104104
* @property {Endorsement} [endorsement] - Optional. Include the endorsement
105105
* instance to build the discovery request based on the proposal.
106-
* This will get the discovery interest (chaincode names and collections)
106+
* This will get the discovery interest (chaincode names, collections and "no private reads")
107107
* from the endorsement instance. Use the {@link Proposal#addCollectionInterest}
108-
* to add collections to the endorsement's chaincode. Use the
109-
* {@link Proposal#addChaincodeCollectionsInterest} to add chaincodes
110-
* and collections that will be called by the endorsement's chaincode.
108+
* to add collections to the endorsement's chaincode.
109+
* Use the {@link Proposal#setNoPrivateReads} to set the proposals "no private reads"
110+
* setting of the discovery interest.
111+
* Use the {@link Proposal#addCollectionInterest} to add chaincodes,
112+
* collections, and no private reads that will be used to get an endorsement plan
113+
* from the peer's discovery service.
111114
* @property {DiscoveryChaincode} [interest] - Optional. An
112-
* array of {@link DiscoveryChaincodeInterest} that have chaincodes
113-
* and collections to calculate the endorsement plans.
115+
* array of {@link DiscoveryChaincodeInterest} that have chaincodes, collections,
116+
* and "no private reads" to help the peer's discovery service calculate the
117+
* endorsement plan.
114118
* @example <caption>"single chaincode"</caption>
115119
* [
116120
* { name: "mychaincode"}
@@ -121,17 +125,21 @@ class DiscoveryService extends ServiceAction {
121125
* ]
122126
* @example <caption>"single chaincode with a collection"</caption>
123127
* [
124-
* { name: "mychaincode", collection_names: ["mycollection"] }
128+
* { name: "mychaincode", collectionNames: ["mycollection"] }
129+
* ]
130+
* @example <caption>"single chaincode with a collection allowing no private data reads"</caption>
131+
* [
132+
* { name: "mychaincode", collectionNames: ["mycollection"], noPrivateReads: true }
125133
* ]
126134
* @example <caption>"chaincode to chaincode with a collection"</caption>
127135
* [
128-
* { name: "mychaincode", collection_names: ["mycollection"] },
129-
* { name: "myotherchaincode", collection_names: ["mycollection"] }}
136+
* { name: "mychaincode", collectionNames: ["mycollection"] },
137+
* { name: "myotherchaincode", collectionNames: ["mycollection"] }}
130138
* ]
131139
* @example <caption>"chaincode to chaincode with collections"</caption>
132140
* [
133-
* { name: "mychaincode", collection_names: ["mycollection", "myothercollection"] },
134-
* { name: "myotherchaincode", collection_names: ["mycollection", "myothercollection"] }}
141+
* { name: "mychaincode", collectionNames: ["mycollection", "myothercollection"] },
142+
* { name: "myotherchaincode", collectionNames: ["mycollection", "myothercollection"] }}
135143
* ]
136144
*/
137145

@@ -144,7 +152,8 @@ class DiscoveryService extends ServiceAction {
144152
/**
145153
* @typedef {Object} DiscoveryChaincodeCall
146154
* @property {string} name - The name of the chaincode
147-
* @property {string[]} [collection_names] - The names of the related collections
155+
* @property {string[]} [collectionNames] - The names of the related collections
156+
* @property {boolean} [noPrivateReads] - Indicates we do not need to read from private data
148157
*/
149158

150159
/**
@@ -195,7 +204,7 @@ class DiscoveryService extends ServiceAction {
195204
queries.push(localQuery);
196205
}
197206

198-
// add a chaincode query to get endorsement plans
207+
// add a discovery chaincode query to get endorsement plans
199208
if (endorsement || interest) {
200209
const interests = [];
201210

@@ -285,9 +294,12 @@ class DiscoveryService extends ServiceAction {
285294
for (const target of this.targets) {
286295
logger.debug(`${method} - about to discover on ${target.endpoint.url}`);
287296
try {
288-
response = await target.sendDiscovery(signedEnvelope, this.requestTimeout);
289-
this.currentTarget = target;
290-
break;
297+
const isConnected = await target.checkConnection();
298+
if (isConnected) {
299+
response = await target.sendDiscovery(signedEnvelope, this.requestTimeout);
300+
this.currentTarget = target;
301+
break;
302+
}
291303
} catch (error) {
292304
response = error;
293305
}
@@ -376,6 +388,9 @@ class DiscoveryService extends ServiceAction {
376388
const chaincodeCall = fabproto6.discovery.ChaincodeCall.create();
377389
if (typeof chaincode.name === 'string') {
378390
chaincodeCall.name = chaincode.name;
391+
if (chaincode.noPrivateReads) {
392+
chaincodeCall.no_private_reads = chaincode.noPrivateReads;
393+
}
379394
// support both names
380395
if (chaincode.collection_names) {
381396
_getCollectionNames(chaincode.collection_names, chaincodeCall);
@@ -738,7 +753,7 @@ class DiscoveryService extends ServiceAction {
738753
}
739754
}
740755

741-
function _getCollectionNames(names, chaincode_call) {
756+
function _getCollectionNames(names, chaincodeCall) {
742757
if (Array.isArray(names)) {
743758
const collection_names = [];
744759
names.map(name => {
@@ -748,7 +763,9 @@ function _getCollectionNames(names, chaincode_call) {
748763
throw Error('The collection name must be a string');
749764
}
750765
});
751-
chaincode_call.collection_names = collection_names;
766+
// this collection_names must be in snake case as it will
767+
// be used by the gRPC create message
768+
chaincodeCall.collection_names = collection_names;
752769
} else {
753770
throw Error('Collection names must be an array of strings');
754771
}

fabric-common/lib/EventService.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ class EventService extends ServiceAction {
8989
// will be set during the .build call
9090
this.blockType = FILTERED_BLOCK;
9191
this.replay = false;
92+
this.startSpecified = false;
9293

9394
this.myNumber = count++;
9495
}
@@ -113,13 +114,13 @@ class EventService extends ServiceAction {
113114
}
114115

115116
for (const eventer of targets) {
116-
if (eventer.connected || eventer.isConnectable()) {
117-
logger.debug('%s - target is or could be connected %s', method, eventer.name);
117+
if (eventer.isConnectable()) {
118+
logger.debug('%s - target is connectable %s', method, eventer.name);
118119
} else {
119120
throw Error(`Eventer ${eventer.name} is not connectable`);
120121
}
121122
}
122-
// must be all targets are connected
123+
// must be all targets are connectable
123124
this.targets = targets;
124125

125126
return this;
@@ -266,6 +267,7 @@ class EventService extends ServiceAction {
266267
number: this.startBlock
267268
});
268269
this.replay = true;
270+
this.startSpecified = true;
269271
}
270272

271273
// build stop proto
@@ -356,10 +358,6 @@ class EventService extends ServiceAction {
356358
logger.debug('%s - target has a stream, is already listening %s', method, target.toString());
357359
startError = Error(`Event service ${target.name} is currently listening`);
358360
} else {
359-
if (target.isConnectable()) {
360-
logger.debug('%s - target needs to connect %s', method, target.toString());
361-
await target.connect(); // target endpoint has been previously assigned, but not connected yet
362-
}
363361
const isConnected = await target.checkConnection();
364362
if (!isConnected) {
365363
startError = Error(`Event service ${target.name} is not connected`);
@@ -409,8 +407,19 @@ class EventService extends ServiceAction {
409407

410408
logger.debug('%s - create stream setup timeout', method);
411409
const connectionSetupTimeout = setTimeout(() => {
412-
logger.error(`EventService[${this.name}] timed out after:${requestTimeout}`);
413-
reject(Error('Event service timed out - Unable to start listening'));
410+
// this service may be waiting for a start block that has not happened
411+
if (this.startSpecified) {
412+
logger.debug(`EventService[${this.name}] timed out after:${requestTimeout}`);
413+
logger.debug(`EventService[${this.name}] not stopping service, wait indefinitely`);
414+
// resolve the promise as if we did get a good response from the peer, since we did
415+
// not get an "end" or "error" back indicating that the request was invalid
416+
// application should have a timer just in case this peer never gets this block
417+
resolve(eventer);
418+
} else {
419+
logger.error(`EventService[${this.name}] timed out after:${requestTimeout}`);
420+
reject(Error('Event service timed out - Unable to start listening'));
421+
}
422+
414423
}, requestTimeout);
415424

416425
logger.debug('%s - create stream based on blockType', method, this.blockType);

fabric-common/lib/Eventer.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ class Eventer extends ServiceEndpoint {
8383
const method = `checkConnection[${this.name}:${this.myCount}]`;
8484
logger.debug(`${method} - start`);
8585

86+
super.checkConnection();
87+
8688
let result = false;
8789
if (this.service) {
8890
try {

0 commit comments

Comments
 (0)