Skip to content

Commit

Permalink
NodeSDK prepare for multiple endorsing peers
Browse files Browse the repository at this point in the history
Change the API input and outputs to be multiple targets
and return multiple responses. Test case updates to
handle the change.

Change-Id: I647b17e10d7c8e5a536290d4ae6df85f80691aae
Signed-off-by: Bret Harrison <beharrison@nc.rr.com>
  • Loading branch information
harrisob committed Nov 9, 2016
1 parent 74aaa9a commit cabab55
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 70 deletions.
114 changes: 79 additions & 35 deletions lib/Member.js
Original file line number Diff line number Diff line change
Expand Up @@ -337,17 +337,17 @@ var Member = class {
* The caller must use the proposal response returned from the endorser along
* with the original proposal request sent to the endorser.
*
* @param {ProposalResponse} proposalResponse - A ProposalResponse object containing
* @param [{ProposalResponse}] proposalResponses - An array or single ProposalResponse objects containing
* the response from the endorsement (see fabric_proposal_response.proto)
* @param {Proposal} chaincodeProposal - A Proposal object containing the original
* request for endorsement (see fabric_proposal.proto)
* request for endorsement(s) (see fabric_proposal.proto)
* @returns Promise for an acknowledgement from the orderer of successfully submitted transaction
*/
sendTransaction(proposalResponse, chaincodeProposal) {
sendTransaction(proposalResponses, chaincodeProposal) {
logger.debug('Member.sendTransaction - start :: chain '+this._chain);

// Verify that data is being passed in
if (!proposalResponse) {
if (!proposalResponses) {
logger.error('Member.sendTransaction - input proposalResponse missing');
return Promise.reject(new Error('Missing proposalResponse object parameter'));
}
Expand All @@ -361,11 +361,20 @@ var Member = class {
return Promise.reject(new Error('no Orderer defined'));
}

//logger.debug('Member.sendTransaction - proposalResponse %j', proposalResponse);
//logger.debug('Member.sendTransaction - chaincodePropsoal %j', chaincodeProposal);

var endorsements = [];
endorsements.push(proposalResponse.endorsement);
let proposalResponse = proposalResponses;
if(Array.isArray(proposalResponses)) {
for(let i=0; i<proposalResponses.length; i++) {
proposalResponse = proposalResponses[i];
endorsements.push(proposalResponse.endorsement);
}
} else {
endorsements.push(proposalResponse.endorsement);
}

// logger.debug('Member.sendTransaction - proposalResponse %j', proposalResponse);
// logger.debug('Member.sendTransaction - chaincodePropsoal %j', chaincodeProposal);

var chaincodeEndorsedAction = new _ccTransProto.ChaincodeEndorsedAction();
chaincodeEndorsedAction.setProposalResponsePayload(proposalResponse.payload);
chaincodeEndorsedAction.setEndorsements(endorsements);
Expand All @@ -388,10 +397,10 @@ var Member = class {
}

/**
* Sends a deployment proposal to an endorser.
* Sends a deployment proposal to one or more endorsing peers.
*
* @param {Object} request An object containing the following fields:
* target : Endorsing Peer Object
* targets : [{Peer}] An array or single Endorsing Peer objects as the targets of the request
* chaincodePath : String
* fcn : String
* args : Strings
Expand All @@ -404,11 +413,16 @@ var Member = class {
return Promise.reject(new Error('missing chaincodePath in Deployment proposal request'));
}
// verify that the caller has included a peer object
if(!request.target) {
logger.error('Invalid input parameter to "sendDeploymentProposal": must have "target" object');
return Promise.reject(new Error('Missing "target" for the endorsing peer object in the Deployment proposal request'));
if(!request.targets) {
logger.error('Invalid input parameter to "sendDeploymentProposal": must have "targets" object');
return Promise.reject(new Error('Missing "targets" for the endorsing peer objects in the Deployment proposal request'));
}
let peers;
if(Array.isArray(request.targets)) {
peers = request.targets;
} else {
peers = [request.targets];
}
let peer = request.target;
var chaincode_id;

// args is optional because some chaincode may not need any input parameters during initialization
Expand All @@ -423,7 +437,7 @@ var Member = class {
var hash = data[1];
chaincode_id = hash;

logger.debug('Successfully generated chaincode deploy archive and name hash (%s)', hash);
logger.debug('Member.sendDeployment- Successfully generated chaincode deploy archive and name hash (%s)', hash);

// at this point, the targzFile has been successfully generated

Expand Down Expand Up @@ -486,11 +500,16 @@ var Member = class {
payload: payload.toBuffer()
};

return peer.sendProposal(proposal)
return Member.sendPeersProposal(peers, proposal)
.then(
function(response) {
response.chaincodeId = chaincode_id;
resolve([response, proposal]);
function(responses) {
if(responses && Array.isArray(responses)) {
logger.debug('Member.sendDeploymentProposal- responses.length:%s',responses.length);
for(let i = 0; i < responses.length; i++) {
responses[i].chaincodeId = chaincode_id;
}
}
resolve([responses, proposal]);
}
);
}
Expand All @@ -506,10 +525,10 @@ var Member = class {
}

/**
* Sends a transaction proposal to an endorsing peer.
* Sends a transaction proposal to one or more endorsing peers.
*
* @param {Object} request:
* target : {Object} Endorsing Peer object as the target of the request
* targets : [{Peer}] An array or single Endorsing Peer objects as the targets of the request
* chaincodeId : {String} The id of the chaincode to perform the transaction proposal
* args : {Array} Arguments specific to the chaincode 'innvoke'
* @returns Promise for a ProposalResponse
Expand All @@ -518,9 +537,9 @@ var Member = class {
logger.debug('Member.sendTransactionProposal - start');

// verify that the caller has included a peer object
if(!request.target) {
logger.error('Missing "target" endorser peer object in the Transaction proposal request');
return Promise.reject(new Error('Missing "target" for endorser peer object in the Transaction proposal request'));
if(!request.targets) {
logger.error('Missing "targets" endorser peer objects in the Transaction proposal request');
return Promise.reject(new Error('Missing "targets" for endorser peer objects in the Transaction proposal request'));
}

if(!request.chaincodeId) {
Expand Down Expand Up @@ -576,11 +595,16 @@ var Member = class {
payload: payload.toBuffer()
};

let peer = request.target;
return peer.sendProposal(proposal)
let peers;
if(Array.isArray(request.targets)) {
peers = request.targets;
} else {
peers = [request.targets];
}
return Member.sendPeersProposal(peers, proposal)
.then(
function(response) {
return Promise.resolve([response,proposal]);
function(responses) {
return Promise.resolve([responses,proposal]);
}
).catch(
function(err) {
Expand All @@ -591,30 +615,36 @@ var Member = class {
}

/**
* Sends a proposal to an endorsing peer that will be handled by the chaincode.
* Sends a proposal to one or more endorsing peers that will be handled by the chaincode.
* This request will be presented to the chaincode 'invoke' and must understand
* from the arguments that this is a query request. The chaincode must also return
* results in the byte array format and the caller will have to be able to decode
* these results
*
* @param {Object} request:
* target : {Object} Endorsing Peer object as the target of the request
* targets : [{Peer}] An array or single Endorsing Peer objects as the targets of the request
* chaincodeId : {String} The id of the chaincode to perform the query
* args : {Array} Arguments for the 'invoke' function call on the chaincode
* that represent a query invocation
* @returns Promise for a byte array results from the chaincode
* @returns Promise for an array of byte array results from the chainco on all Endorsing Peers
*/
queryByChaincode(request) {
logger.debug('Member.sendQueryProposal - start');

return this.sendTransactionProposal(request)
.then(
function(results) {
var response = results[0];
var responses = results[0];
var proposal = results[1];
logger.debug('Member-sendQueryProposal - response %j', response);
if(response.response && response.response.payload) {
return Promise.resolve(response.response.payload);
logger.debug('Member-sendQueryProposal - response %j', responses);
if(responses && Array.isArray(responses)) {
var results = [];
for(let i = 0; i < responses.length; i++) {
if(responses[i].response && responses[i].response.payload) {
results.push(responses[i].response.payload);
}
}
return Promise.resolve(results);
}
return Promise.reject(new Error('Payload results are missing from the chaincode query'));
}
Expand All @@ -626,6 +656,20 @@ var Member = class {
);
}

/**
* internal utility method to return one Promise when sending a proposal to many peers
*/
static sendPeersProposal(peers, proposal) {
// only send to the first one for now
return peers[0].sendProposal(proposal)
.then( function(response) {
return Promise.resolve([response]);
})
.catch( function(err){
return Promise.reject(err);
});
}

/**
* Get the current state of this member as a string
* @return {string} The state of this member as a string
Expand Down
31 changes: 17 additions & 14 deletions test/unit/end-to-end.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ test('End-to-end flow of chaincode deploy, transaction invocation, and query', f

// send proposal to endorser
var request = {
target: hfc.getPeer('grpc://localhost:7051'),
targets: [hfc.getPeer('grpc://localhost:7051')],
chaincodePath: testUtil.CHAINCODE_PATH,
fcn: 'init',
args: ['a', '100', 'b', '200']
Expand All @@ -65,12 +65,13 @@ test('End-to-end flow of chaincode deploy, transaction invocation, and query', f
}
).then(
function(results) {
var proposalResponse = results[0];
var proposalResponses = results[0];
console.log('proposalResponses:'+JSON.stringify(proposalResponses));
var proposal = results[1];
if (proposalResponse && proposalResponse.response && proposalResponse.response.status === 200) {
t.pass(util.format('Successfully sent Proposal and received ProposalResponse: Status - %s, message - "%s", metadata - "%s", endorsement signature: %s', proposalResponse.response.status, proposalResponse.response.message, proposalResponse.response.payload, proposalResponse.endorsement.signature));
chaincode_id = proposalResponse.chaincodeId;
return webUser.sendTransaction(proposalResponse, proposal);
if (proposalResponses && proposalResponses[0].response && proposalResponses[0].response.status === 200) {
t.pass(util.format('Successfully sent Proposal and received ProposalResponse: Status - %s, message - "%s", metadata - "%s", endorsement signature: %s', proposalResponses[0].response.status, proposalResponses[0].response.message, proposalResponses[0].response.payload, proposalResponses[0].endorsement.signature));
chaincode_id = proposalResponses[0].chaincodeId;
return webUser.sendTransaction(proposalResponses, proposal);
} else {
t.fail('Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...');
t.end();
Expand Down Expand Up @@ -100,7 +101,7 @@ test('End-to-end flow of chaincode deploy, transaction invocation, and query', f
function() {
// send proposal to endorser
var request = {
target: hfc.getPeer('grpc://localhost:7051'),
targets: [hfc.getPeer('grpc://localhost:7051')],
chaincodeId : chaincode_id,
fcn: 'invoke',
args: ['move', 'a', 'b','100']
Expand All @@ -113,11 +114,11 @@ test('End-to-end flow of chaincode deploy, transaction invocation, and query', f
}
).then(
function(results) {
var proposalResponse = results[0];
var proposalResponses = results[0];
var proposal = results[1];
if (proposalResponse.response.status === 200) {
t.pass('Successfully obtained transaction endorsement.' + JSON.stringify(proposalResponse));
return webUser.sendTransaction(proposalResponse, proposal);
if (proposalResponses[0].response.status === 200) {
t.pass('Successfully obtained transaction endorsement.' + JSON.stringify(proposalResponses));
return webUser.sendTransaction(proposalResponses, proposal);
} else {
t.fail('Failed to obtain transaction endorsement. Error code: ' + status);
t.end();
Expand Down Expand Up @@ -146,7 +147,7 @@ test('End-to-end flow of chaincode deploy, transaction invocation, and query', f
function() {
// send query
var request = {
target: hfc.getPeer('grpc://localhost:7051'),
targets: [hfc.getPeer('grpc://localhost:7051')],
chaincodeId : chaincode_id,
fcn: 'invoke',
args: ['query','b']
Expand All @@ -158,8 +159,10 @@ test('End-to-end flow of chaincode deploy, transaction invocation, and query', f
t.end();
}
).then(
function(response_payload) {
t.equal(response_payload.toString('utf8'),'300','checking query results are correct that user b has 300 now after the move');
function(response_payloads) {
for(let i = 0; i < response_payloads.length; i++) {
t.equal(response_payloads[i].toString('utf8'),'300','checking query results are correct that user b has 300 now after the move');
}
t.end();
},
function(err) {
Expand Down
4 changes: 2 additions & 2 deletions test/unit/endorser-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ test('\n\n** TEST ** endorse chaincode deployment good test', function(t) {

// send proposal to endorser
var request = {
target: hfc.getPeer('grpc://localhost:7051'),
targets: [hfc.getPeer('grpc://localhost:7051')],
chaincodePath: testUtil.CHAINCODE_PATH,
fcn: 'init',
args: ['a', '100', 'b', '200']
Expand All @@ -171,7 +171,7 @@ test('\n\n** TEST ** endorse chaincode deployment good test', function(t) {
if (Array.isArray(data) && data.length === 2) {
let response = data[0];

if (response && response.response && response.response.status === 200) {
if (response[0] && response[0].response && response[0].response.status === 200) {
t.pass('Successfully obtained endorsement.');
} else {
t.fail('Failed to obtain endorsement. Error code: ' + status);
Expand Down
Loading

0 comments on commit cabab55

Please sign in to comment.