Skip to content

Commit db6711b

Browse files
authored
FABN-1539 NodeSDK limit "all" transaction event listening (#212)
Only notify tranaction event listeners of 'ENDORSER_TRANSACTION' block type and not include other types like config changes. V2 lifecycle changes are included as they are this type. Moved the JS scenario fabric-common testing to TS scenario testing when adding tests for the 'ALL' transaction listener. Signed-off-by: Bret Harrison <[email protected]>
1 parent 48d1c4b commit db6711b

File tree

631 files changed

+1107
-464922
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

631 files changed

+1107
-464922
lines changed

fabric-common/lib/Commit.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class Commit extends Proposal {
7272
const endorsements = [];
7373
for (const proposalResponse of this._endorsement._proposalResponses) {
7474

75-
logger.debug('proposalResponse is:', proposalResponse);
75+
logger.debug('proposalResponse is:', proposalResponse.status);
7676

7777
if (proposalResponse && proposalResponse.response && proposalResponse.endorsement) {
7878
endorsements.push(proposalResponse.endorsement);

fabric-common/lib/EventService.js

+40-20
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ class EventService extends ServiceAction {
411411
logger.debug('%s - create stream based on blockType', method, this.blockType);
412412
eventer.setStreamByType(this.blockType);
413413

414-
// the promise and streams live on and need we need
414+
// the promise and streams live on and we need
415415
// to check at times to be sure we are working with the
416416
// correct one if the target gets restarted
417417
const stream = eventer.stream;
@@ -421,7 +421,7 @@ class EventService extends ServiceAction {
421421
logger.debug('%s - create stream listening callbacks - onData, onEnd, onStatus, onError', method);
422422

423423
eventer.stream.on('data', (deliverResponse) => {
424-
logger.debug(`on.data - peer:${eventer.endpoint.url} - ${mystreamCount}`);
424+
logger.debug(`on.data - peer:${eventer.endpoint.url} - stream:${mystreamCount}`);
425425
if (stream !== eventer.stream) {
426426
logger.debug('on.data - incoming block was from a cancelled stream');
427427
return;
@@ -488,10 +488,24 @@ class EventService extends ServiceAction {
488488
logger.debug('on.data - status received when newest block seen: %s block_num: %s',
489489
deliverResponse.status, this.lastBlockNumber.toNumber());
490490
this._close(new Error(`Newest block received:${this.lastBlockNumber.toNumber()} status:${deliverResponse.status}`));
491-
} else if (this.endBlock) {
492-
logger.debug('on.data - status received before the endblock has been seen');
493-
this._close(new Error(`End block of ${this.endBlock.toNumber()}` +
491+
} else if (this.endBlock && this.endBlock.greaterThan(this.lastBlockNumber)) {
492+
logger.error('on.data - status SUCCESS received before the configured endblock has been seen');
493+
this._close(new Error(`Connection Shutdown. End block of ${this.endBlock.toNumber()}` +
494494
`not received. Last block received ${this.lastBlockNumber.toNumber()}`));
495+
} else {
496+
logger.error('on.data - status SUCCESS received while blocks are required');
497+
this._close(new Error('Event Service connection has been shutdown. ' +
498+
`Last block received ${this.lastBlockNumber.toNumber()}`));
499+
}
500+
} else if (deliverResponse.status === 'NOT_FOUND') {
501+
logger.debug('%s - on.data received type status of NOT_FOUND', method);
502+
if (this.endBlock) {
503+
logger.error('on.data - Configured endblock does not exist');
504+
this._close(new Error(`End block of ${this.endBlock.toNumber()}` +
505+
` does not exist. Last block received ${this.lastBlockNumber.toNumber()}`));
506+
} else {
507+
logger.error('on.data - NOT_FOUND status received - last block received %s', this.lastBlockNumber.toNumber());
508+
this._close(new Error(`Event stream has received an unexpected status message. status:${deliverResponse.status}`));
495509
}
496510
} else {
497511
// tell all registered users that something is wrong and shutting down
@@ -505,11 +519,11 @@ class EventService extends ServiceAction {
505519
});
506520

507521
eventer.stream.on('status', (response) => {
508-
logger.debug('on status - status received: %j peer:%s - %s', response, eventer.endpoint.url, mystreamCount);
522+
logger.debug('on status - status received: %j peer:%s - stream:%s', response, eventer.endpoint.url, mystreamCount);
509523
});
510524

511525
eventer.stream.on('end', () => {
512-
logger.debug('on.end - peer:%s - %', eventer.endpoint.url, mystreamCount);
526+
logger.debug('on.end - peer:%s - stream:%s', eventer.endpoint.url, mystreamCount);
513527
if (stream !== eventer.stream) {
514528
logger.debug('on.data - incoming message was from a cancelled stream');
515529
return;
@@ -536,7 +550,7 @@ class EventService extends ServiceAction {
536550
});
537551

538552
eventer.stream.on('error', (err) => {
539-
logger.debug('on.error - block peer:%s - %s', eventer.endpoint.url, mystreamCount);
553+
logger.debug('on.error - block peer:%s - stream:%s', eventer.endpoint.url, mystreamCount);
540554
if (stream !== eventer.stream) {
541555
logger.debug('%s - on.error - incoming error was from a cancelled stream - %s', method, err);
542556
return;
@@ -758,7 +772,7 @@ class EventService extends ServiceAction {
758772
*/
759773
registerChaincodeListener(chaincodeId = checkParameter('chaincodeId'), eventName = checkParameter('eventName'), callback = checkParameter('callback'), options) {
760774
const method = `registerChaincodeListener[${this.name}] - #${this.myNumber}`;
761-
logger.debug('%s - start', method);
775+
logger.debug('%s - start - %s - %s', method, chaincodeId, eventName);
762776

763777
const event_name = new RegExp(eventName);
764778
const event_reg = new EventListener(this, CHAINCODE, callback, options, event_name, chaincodeId);
@@ -982,19 +996,23 @@ class EventService extends ServiceAction {
982996
logger.debug('%s filtered block number=%s', method, filtered_block.number);
983997
if (filtered_block.filtered_transactions) {
984998
for (const filtered_transaction of filtered_block.filtered_transactions) {
985-
this._callTransactionListener(filtered_transaction.txid,
986-
filtered_transaction.tx_validation_code,
987-
filtered_block.number);
999+
if (filtered_transaction.type === 'ENDORSER_TRANSACTION') {
1000+
this._callTransactionListener(filtered_transaction.txid,
1001+
filtered_transaction.tx_validation_code,
1002+
filtered_block.number);
1003+
}
9881004
}
9891005
}
9901006
} else {
9911007
logger.debug('%s full block number=%s', method, full_block.header.number);
9921008
const txStatusCodes = full_block.metadata.metadata[fabprotos.common.BlockMetadataIndex.TRANSACTIONS_FILTER];
9931009
for (let index = 0; index < full_block.data.data.length; index++) {
9941010
const channel_header = full_block.data.data[index].payload.header.channel_header;
995-
this._callTransactionListener(channel_header.tx_id,
996-
txStatusCodes[index],
997-
full_block.header.number);
1011+
if (channel_header.type === fabprotos.common.HeaderType.ENDORSER_TRANSACTION) {
1012+
this._callTransactionListener(channel_header.tx_id,
1013+
txStatusCodes[index],
1014+
full_block.header.number);
1015+
}
9981016
}
9991017
}
10001018
}
@@ -1052,7 +1070,7 @@ class EventService extends ServiceAction {
10521070
if (filtered_transaction.transaction_actions) {
10531071
if (filtered_transaction.transaction_actions.chaincode_actions) {
10541072
for (const chaincode_action of filtered_transaction.transaction_actions.chaincode_actions) {
1055-
logger.debug('%s - filtered block chaincode_event %s', method, chaincode_action);
1073+
logger.debug('%s - filtered block chaincode_event %j', method, chaincode_action);
10561074
// need to remove the payload since with filtered blocks it
10571075
// has an empty byte array value which is not the real value
10581076
// we do not want the listener to think that is the value
@@ -1073,12 +1091,13 @@ class EventService extends ServiceAction {
10731091
try {
10741092
const env = full_block.data.data[index];
10751093
const channel_header = env.payload.header.channel_header;
1076-
if (channel_header.type === 3) { // only ENDORSER_TRANSACTION have chaincode events
1094+
// only ENDORSER_TRANSACTION have chaincode events
1095+
if (channel_header.type === fabprotos.common.HeaderType.ENDORSER_TRANSACTION) {
10771096
const tx = env.payload.data;
10781097
if (tx && tx.actions) {
10791098
for (const {payload} of tx.actions) {
10801099
const chaincode_event = payload.action.proposal_response_payload.extension.events;
1081-
logger.debug('%s - full block chaincode_event %s', method, chaincode_event);
1100+
logger.debug('%s - full block chaincode_event %j', method, chaincode_event);
10821101

10831102
const txStatusCodes = full_block.metadata.metadata[fabprotos.common.BlockMetadataIndex.TRANSACTIONS_FILTER];
10841103
const val_code = txStatusCodes[index];
@@ -1124,13 +1143,14 @@ class EventService extends ServiceAction {
11241143

11251144
_queueChaincodeEvent(chaincode_event, block_num, tx_id, val_code, all_events) {
11261145
const method = `_queueChaincodeEvent[${this.name}] - #${this.myNumber}`;
1127-
logger.debug('%s - start - chaincode_event %s', method, chaincode_event);
1146+
logger.debug('%s - start - chaincode_event %j', method, chaincode_event);
11281147

11291148
const tx_status = convertValidationCode(val_code);
11301149

11311150
logger.debug('%s - txid=%s val_code=%s', method, tx_id, tx_status);
11321151

11331152
for (const chaincode_reg of this._eventListenerRegistrations.values()) {
1153+
logger.debug('%s - checking regisistered chaincode event %s %s', method, chaincode_reg.event, chaincode_reg.chaincodeId);
11341154
// check each listener to see if this chaincode event matches
11351155
if (chaincode_reg.listenerType === CHAINCODE &&
11361156
chaincode_reg.chaincodeId === chaincode_event.chaincode_id &&
@@ -1152,7 +1172,7 @@ class EventService extends ServiceAction {
11521172
chaincode_event.payload
11531173
));
11541174
} else {
1155-
logger.debug('%s - NOT queuing chaincode event: %s', method, chaincode_event. event_name);
1175+
logger.debug('%s - NOT queuing chaincode event: %s', method, chaincode_event.event_name);
11561176
}
11571177
}
11581178
}

fabric-common/test/EventService.js

+60-6
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ describe('EventService', () => {
6969
]
7070
},
7171
header: {
72-
channel_header: {type: 3, tx_id: 'tx1'}
72+
channel_header: {type: 3, tx_id: 'tx1', typeString: 'ENDORSER_TRANSACTION'}
7373
}
7474
}
7575
}
@@ -647,7 +647,7 @@ describe('EventService', () => {
647647
sinon.assert.called(eventService._close);
648648
sinon.assert.calledWith(FakeLogger.error, '%s EventService has detected an error %s');
649649
});
650-
it('should call stream on data with status and end block seen', async () => {
650+
it('should call close when on data with SUCCESS status and end block seen', async () => {
651651
const eventer1 = client.newEventer('eventer1');
652652
eventer1.checkConnection = sinon.stub().returns(true);
653653
eventer1.waitForReady = sandbox.stub().resolves();
@@ -665,7 +665,7 @@ describe('EventService', () => {
665665
sinon.assert.calledWith(FakeLogger.debug, '%s - on.data received type status of SUCCESS');
666666
sinon.assert.calledWith(FakeLogger.debug, 'on.data - status received after last block seen: %s block_num: %s');
667667
});
668-
it('should call stream on data with status and end block seen and newest block seen', async () => {
668+
it('should call close when on data with status SUCCESS and end block seen and newest block seen', async () => {
669669
const eventer1 = client.newEventer('eventer1');
670670
eventer1.checkConnection = sinon.stub().returns(true);
671671
eventer1.waitForReady = sandbox.stub().resolves();
@@ -683,7 +683,7 @@ describe('EventService', () => {
683683
sinon.assert.calledWith(FakeLogger.debug, '%s - on.data received type status of SUCCESS');
684684
sinon.assert.calledWith(FakeLogger.debug, 'on.data - status received when newest block seen: %s block_num: %s');
685685
});
686-
it('should call stream on data with status and end block not seen', async () => {
686+
it('should call close when on data with status SUCCESS and end block not seen', async () => {
687687
const eventer1 = client.newEventer('eventer1');
688688
eventer1.checkConnection = sinon.stub().returns(true);
689689
eventer1.waitForReady = sandbox.stub().resolves();
@@ -699,7 +699,61 @@ describe('EventService', () => {
699699
sinon.assert.called(deliverStub);
700700
sinon.assert.called(eventService._close);
701701
sinon.assert.calledWith(FakeLogger.debug, '%s - on.data received type status of SUCCESS');
702-
sinon.assert.calledWith(FakeLogger.debug, 'on.data - status received before the endblock has been seen');
702+
sinon.assert.calledWith(FakeLogger.error, 'on.data - status SUCCESS received before the configured endblock has been seen');
703+
});
704+
it('should close when on data with status of SUCCESS and end block not seen with still need blocks', async () => {
705+
const eventer1 = client.newEventer('eventer1');
706+
eventer1.checkConnection = sinon.stub().returns(true);
707+
eventer1.waitForReady = sandbox.stub().resolves();
708+
await eventer1.connect(endpoint);
709+
eventService.blockType = 'full';
710+
onStub.yields({Type: 'status', status: 'SUCCESS'});
711+
eventService._close = sinon.stub();
712+
eventService._end_block_seen = false;
713+
eventService.lastBlockNumber = Long.fromValue(4);
714+
eventService.endBlock = Long.fromValue(3);
715+
// TEST CALL
716+
await eventService._startService(eventer1, {}, 3000);
717+
sinon.assert.called(deliverStub);
718+
sinon.assert.called(eventService._close);
719+
sinon.assert.calledWith(FakeLogger.debug, '%s - on.data received type status of SUCCESS');
720+
sinon.assert.calledWith(FakeLogger.error, 'on.data - status SUCCESS received while blocks are required');
721+
});
722+
it('should close when on data with status of NOT FOUND end block not seen', async () => {
723+
const eventer1 = client.newEventer('eventer1');
724+
eventer1.checkConnection = sinon.stub().returns(true);
725+
eventer1.waitForReady = sandbox.stub().resolves();
726+
await eventer1.connect(endpoint);
727+
eventService.blockType = 'full';
728+
onStub.yields({Type: 'status', status: 'NOT_FOUND'});
729+
eventService._close = sinon.stub();
730+
eventService._end_block_seen = false;
731+
eventService.lastBlockNumber = Long.fromValue(1);
732+
eventService.endBlock = Long.fromValue(3);
733+
// TEST CALL
734+
await eventService._startService(eventer1, {}, 3000);
735+
sinon.assert.called(deliverStub);
736+
sinon.assert.called(eventService._close);
737+
sinon.assert.calledWith(FakeLogger.debug, '%s - on.data received type status of NOT_FOUND');
738+
sinon.assert.calledWith(FakeLogger.error, 'on.data - Configured endblock does not exist');
739+
});
740+
it('should close when on data with status of NOT FOUND and still need blocks', async () => {
741+
const eventer1 = client.newEventer('eventer1');
742+
eventer1.checkConnection = sinon.stub().returns(true);
743+
eventer1.waitForReady = sandbox.stub().resolves();
744+
await eventer1.connect(endpoint);
745+
eventService.blockType = 'full';
746+
onStub.yields({Type: 'status', status: 'NOT_FOUND'});
747+
eventService._close = sinon.stub();
748+
eventService._end_block_seen = false;
749+
eventService.lastBlockNumber = Long.fromValue(1);
750+
eventService.endBlock = null;
751+
// TEST CALL
752+
await eventService._startService(eventer1, {}, 3000);
753+
sinon.assert.called(deliverStub);
754+
sinon.assert.called(eventService._close);
755+
sinon.assert.calledWith(FakeLogger.debug, '%s - on.data received type status of NOT_FOUND');
756+
sinon.assert.calledWith(FakeLogger.error, 'on.data - NOT_FOUND status received - last block received %s');
703757
});
704758
it('should call stream on data with type status of SUCCESS', async () => {
705759
const eventer1 = client.newEventer('eventer1');
@@ -1005,7 +1059,7 @@ describe('EventService', () => {
10051059
eventService.registerTransactionListener('tx1', sinon.stub(), {unregister: false});
10061060
const fake_callTransactionListener = sinon.stub();
10071061
eventService._callTransactionListener = fake_callTransactionListener;
1008-
eventService._processTxEvents('full', {filtered_transactions: [{txid: 'tx1', tx_validation_code: 'valid'}], number: 1});
1062+
eventService._processTxEvents('full', {filtered_transactions: [{txid: 'tx1', tx_validation_code: 'valid', type: 'ENDORSER_TRANSACTION'}], number: 1});
10091063
sinon.assert.calledWith(FakeLogger.debug, '%s filtered block number=%s');
10101064
sinon.assert.called(fake_callTransactionListener);
10111065
});

fabric-common/types/index.d.ts

+11-6
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,9 @@ export interface ChaincodeEvent {
240240
export type BlockType = 'filtered' | 'full' | 'private';
241241

242242
export class EventService extends ServiceAction {
243-
public startBlock: Long | string;
244-
endBlock?: Long | string;
245-
blockType: BlockType;
243+
readonly startBlock: Long | string;
244+
readonly endBlock?: Long | string;
245+
readonly blockType: BlockType;
246246
constructor(chaincodeName: string, channel: Channel);
247247
public setEventer(discoverer: Eventer): EventService;
248248
public getLastBlockNumber(): Long;
@@ -252,15 +252,15 @@ export class EventService extends ServiceAction {
252252
public isListening(): boolean;
253253
public unregisterEventListener(eventListener: EventListener): EventService;
254254
public registerTransactionListener(txid: string, callback: EventCallback, options: EventRegistrationOptions): EventListener;
255-
public registerChaincodeListener(eventName: string, callback: EventCallback, options: EventRegistrationOptions): EventListener;
255+
public registerChaincodeListener(chaincodeId: string, eventName: string, callback: EventCallback, options: EventRegistrationOptions): EventListener;
256256
public registerBlockListener(callback: EventCallback, options: EventRegistrationOptions): EventListener;
257257
setTargets(targets: Eventer[]): void;
258258
isStarted(): boolean;
259259
}
260260

261261
export interface StartEventRequest {
262-
targets: Eventer[];
263-
requestTimeout: number;
262+
targets?: Eventer[];
263+
requestTimeout?: number;
264264
}
265265

266266
export interface StartRequestOptions {
@@ -269,6 +269,11 @@ export interface StartRequestOptions {
269269
endBlock?: number | string | Long;
270270
}
271271

272+
export interface SendEventOptions {
273+
targets?: Eventer[];
274+
requestTimeout?: number;
275+
}
276+
272277
export class Client {
273278
public static newClient(name: string): Client;
274279

fabric-network/src/network.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export class NetworkImpl implements Network {
6363
/*
6464
* Network constructor for internal use only.
6565
* @param {Gateway} gateway The owning gateway instance
66-
* @param {Channel} channel The fabric-base channel instance
66+
* @param {Channel} channel The fabric-common channel instance
6767
*/
6868
constructor(gateway: Gateway, channel: Channel) {
6969
const method = 'constructor';

fabric-network/test/impl/event/stubeventservice.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ export class StubEventService implements EventService {
134134
return listener;
135135
}
136136

137-
registerChaincodeListener(eventName: string, callback: EventCallback, options: EventRegistrationOptions): import('fabric-common').EventListener {
137+
registerChaincodeListener(chaincodeId: string, eventName: string, callback: EventCallback, options: EventRegistrationOptions): import('fabric-common').EventListener {
138138
throw new Error('Method not implemented.');
139139
}
140140

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"test:cucumber": "cucumber-js -f node_modules/cucumber-pretty ./test/scenario/features/*.feature",
3838
"test:ts-cucumber": "cucumber-js -f node_modules/cucumber-pretty ./test/ts-scenario/features/*.feature --require './test/ts-scenario/steps/**/*.ts' --require './test/ts-scenario/support/**/*.ts' --require-module ts-node/register",
3939
"test:ts-cucumberNoHSM": "cucumber-js -f node_modules/cucumber-pretty ./test/ts-scenario/features/*.feature --require './test/ts-scenario/steps/**/*.ts' --require './test/ts-scenario/support/**/*.ts' --require-module ts-node/register --tags 'not @gateway_hsm'",
40-
"test:ts-cucumber-tagged": "npm run test:ts-cucumber -- --tags @gateway_hsm",
40+
"test:ts-cucumber-tagged": "npm run test:ts-cucumber -- --tags @base_api_1",
4141
"testHeadless": "run-s cleanUp compile lint unitTest:all",
4242
"tapeAndCucumber": "run-s tapeIntegration dockerClean cucumberScenario"
4343
},

test/scenario/DEPRECATED.md

-7
This file was deleted.

0 commit comments

Comments
 (0)