From 25fa4b03a626184ef45fd17356eb80f705a1a0b2 Mon Sep 17 00:00:00 2001 From: Dave Kelsey Date: Mon, 23 Jul 2018 09:53:32 +0100 Subject: [PATCH] FABN-826 restore backward compatibility for errors Error handling was still not backward compatible with previous release of fabric for invoke, install, instantiate, upgrade. This fix should restore backward compatibility Change-Id: I70dc50261e8cd3acf20c58590006defa699e6251 Signed-off-by: Dave Kelsey --- fabric-client/lib/Peer.js | 14 +++-- .../src/github.com/example_cc/example_cc.go | 8 +-- .../src/github.com/example_cc1/example_cc1.go | 8 +-- .../src/node_cc/example_cc/chaincode.js | 8 +-- .../src/node_cc/example_cc1/chaincode.js | 8 +-- test/integration/e2e/e2eUtils.js | 53 ++++++++++++------- test/integration/e2e/invoke-transaction.js | 43 +++++++-------- test/integration/e2e/query.js | 6 +-- test/integration/install.js | 4 +- test/integration/nodechaincode/invoke.js | 50 +++++++++-------- test/integration/nodechaincode/query.js | 6 +-- test/unit/util.js | 8 +-- 12 files changed, 124 insertions(+), 92 deletions(-) diff --git a/fabric-client/lib/Peer.js b/fabric-client/lib/Peer.js index 5792aafa85..8197395d7b 100644 --- a/fabric-client/lib/Peer.js +++ b/fabric-client/lib/Peer.js @@ -107,10 +107,18 @@ var Peer = class extends Remote { } else { if (proposalResponse) { logger.debug('%s - Received proposal response from peer "%s": status - %s', method, self._url, proposalResponse.response.status); - resolve(proposalResponse); + // 400 is the error threshold level, anything below that the endorser will endorse it. + if (proposalResponse.response && proposalResponse.response.status < 400) { + resolve(proposalResponse); + } else if (proposalResponse.response && proposalResponse.response.message) { + reject(new Error(proposalResponse.response.message)); + } else { + logger.error('GRPC client failed to get a proper response from the peer "%s".', self._url); + reject(new Error(util.format('GRPC client failed to get a proper response from the peer "%s".', self._url))); + } } else { - logger.error('GRPC client failed to get a proper response from the peer "%s".', self._url); - reject(new Error(util.format('GRPC client failed to get a proper response from the peer "%s".', self._url))); + logger.error('GRPC client got a null or undefined response from the peer "%s".', self._url); + reject(new Error(util.format('GRPC client got a null or undefined response from the peer "%s".', self._url))); } } }); diff --git a/test/fixtures/src/github.com/example_cc/example_cc.go b/test/fixtures/src/github.com/example_cc/example_cc.go index 2ace261a34..6e12fc00cc 100644 --- a/test/fixtures/src/github.com/example_cc/example_cc.go +++ b/test/fixtures/src/github.com/example_cc/example_cc.go @@ -87,8 +87,8 @@ func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { return t.query(stub, args) } - if function == "queryError" { - return t.queryError(stub, args) + if function == "throwError" { + return t.throwError(stub, args) } if function == "move" { @@ -203,8 +203,8 @@ func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) return shim.Success(Avalbytes) } -func (t *SimpleChaincode) queryError(stub shim.ChaincodeStubInterface, args []string) pb.Response { - err := fmt.Errorf("queryError: an error occurred") +func (t *SimpleChaincode) throwError(stub shim.ChaincodeStubInterface, args []string) pb.Response { + err := fmt.Errorf("throwError: an error occurred") return shim.Error(err.Error()) } diff --git a/test/fixtures/src/github.com/example_cc1/example_cc1.go b/test/fixtures/src/github.com/example_cc1/example_cc1.go index 9c8d20eac0..06c6184838 100644 --- a/test/fixtures/src/github.com/example_cc1/example_cc1.go +++ b/test/fixtures/src/github.com/example_cc1/example_cc1.go @@ -56,8 +56,8 @@ func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { return t.query(stub, args) } - if function == "queryError" { - return t.queryError(stub, args) + if function == "throwError" { + return t.throwError(stub, args) } if function == "move" { @@ -178,8 +178,8 @@ func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) return shim.Success(Avalbytes) } -func (t *SimpleChaincode) queryError(stub shim.ChaincodeStubInterface, args []string) pb.Response { - err := fmt.Errorf("queryError: an error occurred") +func (t *SimpleChaincode) throwError(stub shim.ChaincodeStubInterface, args []string) pb.Response { + err := fmt.Errorf("throwError: an error occurred") return shim.Error(err.Error()) } diff --git a/test/fixtures/src/node_cc/example_cc/chaincode.js b/test/fixtures/src/node_cc/example_cc/chaincode.js index b62d40e320..ea7fab078e 100644 --- a/test/fixtures/src/node_cc/example_cc/chaincode.js +++ b/test/fixtures/src/node_cc/example_cc/chaincode.js @@ -75,8 +75,8 @@ var Chaincode = class { return this.query(stub, args); } - if (fcn === 'queryError') { - return this.queryError(stub, args); + if (fcn === 'throwError') { + return this.throwError(stub, args); } if (fcn === 'move') { @@ -183,8 +183,8 @@ var Chaincode = class { return shim.success(Buffer.from(Aval.toString())); } - async queryError(stub, args) { - return shim.error(new Error('queryError: an error occurred')); + async throwError(stub, args) { + return shim.error(new Error('throwError: an error occurred')); } }; diff --git a/test/fixtures/src/node_cc/example_cc1/chaincode.js b/test/fixtures/src/node_cc/example_cc1/chaincode.js index 6c57597248..89d41fac59 100644 --- a/test/fixtures/src/node_cc/example_cc1/chaincode.js +++ b/test/fixtures/src/node_cc/example_cc1/chaincode.js @@ -46,8 +46,8 @@ var Chaincode = class { return this.query(stub, args); } - if (fcn === 'queryError') { - return this.queryError(stub, args); + if (fcn === 'throwError') { + return this.throwError(stub, args); } if (fcn === 'move') { @@ -160,8 +160,8 @@ var Chaincode = class { return shim.success(Buffer.from(Aval.toString())); } - async queryError(stub, args) { - return shim.error(new Error('queryError: an error occurred')); + async throwError(stub, args) { + return shim.error(new Error('throwError: an error occurred')); } async testTransient(stub) { diff --git a/test/integration/e2e/e2eUtils.js b/test/integration/e2e/e2eUtils.js index cdfdb1304e..d381138c25 100644 --- a/test/integration/e2e/e2eUtils.js +++ b/test/integration/e2e/e2eUtils.js @@ -287,8 +287,8 @@ function instantiateChaincodeWithId(userOrg, chaincode_id, chaincode_path, versi let success = false; if (proposalResponses && proposalResponses.length > 0) { proposalResponses.forEach((response) => { - if (response && - response.response.message.indexOf('Did not find expected key "test" in the transient map of the proposal')) { + if (response && response instanceof Error && + response.message.includes('Did not find expected key "test" in the transient map of the proposal')) { success = true; } else { success = false; @@ -577,28 +577,38 @@ function invokeChaincode(userOrg, version, chaincodeId, t, useStore, fcn, args, for(let i in proposalResponses) { let one_good = false; let proposal_response = proposalResponses[i]; - logger.debug('invoke chaincode, proposal response: ' + util.inspect(proposal_response, {depth: null})); - if( proposal_response.response && proposal_response.response.status === 200) { - t.pass('transaction proposal has response status of good'); - one_good = channel.verifyProposalResponse(proposal_response); - if(one_good) { - t.pass('transaction proposal signature and endorser are valid'); - } - // check payload - let payload = proposal_response.response.payload.toString(); - // verify payload is equal to expectedResult - if (payload === expectedResult){ - t.pass('transaction proposal payloads are valid'); + if (expectedResult instanceof Error) { + t.true((proposal_response instanceof Error), 'proposal response should be an instance of error'); + t.true(proposal_response.message.includes(expectedResult.message), 'error should contain the correct message: ' + expectedResult.message); + } else { + logger.debug('invoke chaincode, proposal response: ' + util.inspect(proposal_response, {depth: null})); + if( proposal_response.response && proposal_response.response.status === 200) { + t.pass('transaction proposal has response status of good'); + one_good = channel.verifyProposalResponse(proposal_response); + if(one_good) { + t.pass('transaction proposal signature and endorser are valid'); + } + + // check payload + let payload = proposal_response.response.payload.toString(); + // verify payload is equal to expectedResult + if (payload === expectedResult){ + t.pass('transaction proposal payloads are valid'); + } else { + one_good = false; + t.fail('transaction proposal payloads are invalid, expect ' + expectedResult + ', but got ' + payload); + } } else { - one_good = false; - t.fail('transaction proposal payloads are invalid, expect ' + expectedResult + ', but got ' + payload); + t.fail('invokeChaincode: transaction proposal was bad'); } - } else { - t.fail('invokeChaincode: transaction proposal was bad'); + all_good = all_good & one_good; } - all_good = all_good & one_good; } + if (expectedResult instanceof Error) { + return; + } + if (all_good) { // check all the read/write sets to see if the same, verify that each peer // got the same results on the proposal @@ -679,6 +689,11 @@ function invokeChaincode(userOrg, version, chaincodeId, t, useStore, fcn, args, throw new Error('Failed to send proposal due to error: ' + err.stack ? err.stack : err); }).then((response) => { + if (expectedResult instanceof Error) { + channel.close(); + t.pass('Successfully closed all connections'); + return true; + } if (response.status === 'SUCCESS') { t.pass('Successfully sent transaction to the orderer.'); diff --git a/test/integration/e2e/invoke-transaction.js b/test/integration/e2e/invoke-transaction.js index 797ad237ba..eb933bc4b4 100755 --- a/test/integration/e2e/invoke-transaction.js +++ b/test/integration/e2e/invoke-transaction.js @@ -15,29 +15,30 @@ const e2eUtils = require('./e2eUtils.js'); const testUtils = require('../../unit/util'); const chaincodeId = testUtils.END2END.chaincodeId; -test('\n\n***** End-to-end flow: invoke transaction to move money *****\n\n', (t) => { +test('\n\n***** End-to-end flow: invoke transaction to move money *****\n\n', async (t) => { const fcn = 'move'; const args = ['a', 'b','100']; - const expectedResult = 'move succeed'; - e2eUtils.invokeChaincode('org2', 'v0', chaincodeId, t, false/*useStore*/, fcn, args, expectedResult) - .then((result) => { - if(result){ - t.pass('Successfully invoke transaction chaincode on channel'); - return sleep(5000); - } - else { - t.fail('Failed to invoke transaction chaincode '); - t.end(); - } - }, (err) => { - t.fail('Failed to invoke transaction chaincode on channel. ' + err.stack ? err.stack : err); - t.end(); - }).then(() => { - t.end(); - }).catch((err) => { - t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); - t.end(); - }); + let expectedResult = 'move succeed'; + try { + let result = await e2eUtils.invokeChaincode('org2', 'v0', chaincodeId, t, false/*useStore*/, fcn, args, expectedResult); + if(result){ + t.pass('Successfully invoke transaction chaincode on channel'); + await sleep(5000); + } + else { + t.fail('Failed to invoke transaction chaincode '); + } + } catch(err) { + t.fail('Failed to invoke transaction chaincode on channel. ' + err.stack ? err.stack : err); + } + + try { + expectedResult = new Error('throwError: an error occurred'); + await e2eUtils.invokeChaincode('org2', 'v0', chaincodeId, t, false/*useStore*/, 'throwError', args, expectedResult); + } catch(err) { + t.fail('Failed to query chaincode on the channel. ' + err.stack ? err.stack : err); + } + t.end(); }); function sleep(ms) { diff --git a/test/integration/e2e/query.js b/test/integration/e2e/query.js index 84374678a5..99f19176e2 100644 --- a/test/integration/e2e/query.js +++ b/test/integration/e2e/query.js @@ -33,10 +33,10 @@ test('\n\n***** End-to-end flow: query chaincode *****\n\n', async (t) => { } try { - expectedResult = new Error('queryError: an error occurred'); - let result = await e2eUtils.queryChaincode('org2', 'v0', targets, 'queryError', args, expectedResult, chaincodeId, t); + expectedResult = new Error('throwError: an error occurred'); + let result = await e2eUtils.queryChaincode('org2', 'v0', targets, 'throwError', args, expectedResult, chaincodeId, t); if(result){ - t.pass('Successfully query chaincode on the channel'); + t.pass('Successfully handled error from query'); } else { t.fail('Failed to query chaincode '); diff --git a/test/integration/install.js b/test/integration/install.js index ac47c2e15f..4870299e8d 100644 --- a/test/integration/install.js +++ b/test/integration/install.js @@ -62,7 +62,7 @@ test('\n\n** Test chaincode install using chaincodePath to create chaincodePacka params.testDesc = params.testDesc+'0'; installChaincode(params, t) .then((info) => { - if (info && info.response.message.indexOf('install.'+params.chaincodeVersion+' exists') > 0) { + if (info && info instanceof Error && info.message.includes('install.'+params.chaincodeVersion+' exists')) { t.pass('passed check for exists on install again'); t.end(); } else { @@ -115,7 +115,7 @@ test('\n\n** Test chaincode install using chaincodePackage[byte] **\n\n', (t) => params.testDesc = params.testDesc+'0'; installChaincode(params, t) .then((info) => { - if (info && info.response.message.indexOf('install-package.'+params.chaincodeVersion+' exists') > 0) { + if (info && info instanceof Error && info.message.includes('install-package.'+params.chaincodeVersion+' exists')) { t.pass('passed check for exists same code again'); t.end(); } else { diff --git a/test/integration/nodechaincode/invoke.js b/test/integration/nodechaincode/invoke.js index 65f172ade4..48ebd123a7 100644 --- a/test/integration/nodechaincode/invoke.js +++ b/test/integration/nodechaincode/invoke.js @@ -15,29 +15,37 @@ var e2eUtils = require('../e2e/e2eUtils.js'); var testUtils = require('../../unit/util'); var chaincodeId = testUtils.NODE_END2END.chaincodeId; -test('\n\n***** Node-Chaincode End-to-end flow: invoke transaction to move money *****\n\n', (t) => { +test('\n\n***** Node-Chaincode End-to-end flow: invoke transaction to move money *****\n\n', async (t) => { const fcn = 'move'; const args = ['a', 'b','100']; - const expectedResult = 'move succeed'; - e2eUtils.invokeChaincode('org2', 'v0', chaincodeId, t, false/*useStore*/, fcn, args, expectedResult) - .then((result) => { - if(result){ - t.pass('Successfully invoke transaction chaincode on channel'); - return sleep(5000); - } - else { - t.fail('Failed to invoke transaction chaincode '); - t.end(); - } - }, (err) => { - t.fail('Failed to invoke transaction chaincode on channel. ' + err.stack ? err.stack : err); - t.end(); - }).then(() => { - t.end(); - }).catch((err) => { - t.fail('Test failed due to unexpected reasons. ' + err.stack ? err.stack : err); - t.end(); - }); + let expectedResult = 'move succeed'; + try { + let result = await e2eUtils.invokeChaincode('org2', 'v0', chaincodeId, t, false/*useStore*/, fcn, args, expectedResult); + if(result){ + t.pass('Successfully invoke transaction chaincode on channel'); + await sleep(5000); + } + else { + t.fail('Failed to invoke transaction chaincode '); + } + } catch(err) { + t.fail('Failed to invoke transaction chaincode on channel. ' + err.stack ? err.stack : err); + } + + try { + expectedResult = new Error('throwError: an error occurred'); + let result = await e2eUtils.invokeChaincode('org2', 'v0', chaincodeId, t, false/*useStore*/, 'throwError', args, expectedResult); + if(result){ + t.pass('Successfully handled invocation errors'); + } + else { + t.fail('Failed to invoke transaction chaincode '); + } + + } catch(err) { + t.fail('Failed to query chaincode on the channel. ' + err.stack ? err.stack : err); + } + t.end(); }); function sleep(ms) { diff --git a/test/integration/nodechaincode/query.js b/test/integration/nodechaincode/query.js index 55e5668a1c..d237388569 100644 --- a/test/integration/nodechaincode/query.js +++ b/test/integration/nodechaincode/query.js @@ -33,10 +33,10 @@ test('\n\n***** Node-Chaincode End-to-end flow: query chaincode *****\n\n', asyn } try { - expectedResult = new Error('queryError: an error occurred'); - let result = await e2eUtils.queryChaincode('org2', 'v0', targets, 'queryError', args, expectedResult, chaincodeId, t); + expectedResult = new Error('throwError: an error occurred'); + let result = await e2eUtils.queryChaincode('org2', 'v0', targets, 'throwError', args, expectedResult, chaincodeId, t); if(result){ - t.pass('Successfully query chaincode on the channel'); + t.pass('Sucessfully handled error from a query'); } else { t.fail('Failed to query chaincode '); diff --git a/test/unit/util.js b/test/unit/util.js index fabe5c886c..49d7324730 100644 --- a/test/unit/util.js +++ b/test/unit/util.js @@ -266,15 +266,15 @@ module.exports.checkResults = function(results, error_snip, t) { var proposalResponses = results[0]; for(var i in proposalResponses) { let proposal_response = proposalResponses[i]; - if(proposal_response.response && proposal_response.response.message) { - if(proposal_response.response.message.indexOf(error_snip) > -1) { + if(proposal_response instanceof Error) { + if(proposal_response.message.includes(error_snip)) { t.pass('Successfully got the error' + error_snip); } else { - t.fail( 'Failed to get error with ' + error_snip + ' :: response message ' + proposal_response.response.message); + t.fail( 'Failed to get error with ' + error_snip + ' :: response message ' + proposal_response.message); } } else { - t.fail(' Failed :: no response message found and should have had an error with '+ error_snip); + t.fail(' Failed :: no Error response message found and should have had an error with '+ error_snip); } } };