Skip to content

Commit

Permalink
FABN-843 NodeSDK - Discovery using collections
Browse files Browse the repository at this point in the history
Allow user to specify collection names along with the
chaincode name.

Change-Id: I93afd84266c091ca01e23b46b1129bd2a9a15e1f
Signed-off-by: Bret Harrison <[email protected]>
  • Loading branch information
harrisob committed Sep 10, 2018
1 parent 7fec63f commit 19b6086
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 37 deletions.
16 changes: 9 additions & 7 deletions fabric-client/lib/Channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,9 @@ const Channel = class {

/**
* @typedef {Object} DiscoveryChaincodeInterest
* @property {DiscoveryChaincodeCall[]} chaincodes
* @property {DiscoveryChaincodeCall[]} chaincodes The chaincodes names and collections
* that will be sent to the discovery service to calculate an endorsement
* plan.
*/

/**
Expand Down Expand Up @@ -1537,15 +1539,16 @@ const Channel = class {
}

/* internal method
* takes a single string that represents a chaincode name and builds a JSON
* takes a single string that represents a chaincode and optional array of strings
* that represent collections and builds a JSON
* object that may be used as input to building of the GRPC objects to send
* to the discovery service.
*/
_buildDiscoveryInterest(name) {
_buildDiscoveryInterest(name, collections) {
logger.debug('_buildDiscoveryInterest - name %s', name);
const interest = {};
interest.chaincodes = [];
const chaincodes = this._buildDiscoveryChaincodeCall(name);
const chaincodes = this._buildDiscoveryChaincodeCall(name, collections);
interest.chaincodes.push(chaincodes);

return interest;
Expand All @@ -1562,15 +1565,14 @@ const Channel = class {
chaincode_call.name = name;
if(collection_names) {
if(Array.isArray(collection_names)) {
const collection_names = [];
chaincode_call.collection_names = [];
collection_names.map(name =>{
if(typeof name === 'string') {
collection_names.push(name);
chaincode_call.collection_names.push(name);
} else {
throw Error('The collection name must be a string');
}
});
chaincode_call.collection_names = collection_names;
} else {
throw Error('Collections names must be an array of strings');
}
Expand Down
2 changes: 1 addition & 1 deletion fabric-client/lib/impl/DiscoveryEndorsementHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ class DiscoveryEndorsementHandler extends api.EndorsementHandler {
plan.endorsements[peer_info.name] = {};
plan.endorsements[peer_info.name].endorsement = error;
plan.endorsements[peer_info.name].success = false;
logger.error('%s - endorsement failed - %s', method, error.toString());
logger.warn('%s - endorsement failed - %s', method, error.toString());
}
} else {
logger.debug('%s - peer %s not assigned to this channel', method, peer_info.name);
Expand Down
86 changes: 62 additions & 24 deletions test/integration/discovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {
t.equals(results.peers_by_org.Org1MSP.peers[0].chaincodes[0].name, first_chaincode_name, 'Checking peer chaincode name');
t.equals(results.peers_by_org.Org1MSP.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking peer chaincode version');
if(results.endorsement_plans[0].groups.G0) {
t.equals(results.endorsement_plans[0].groups.G0.peers[0].endpoint, 'peer0.org2.example.com:8051', 'Checking peer endpoint');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].ledger_height.low, ledger_height, 'Checking peer ledger_height');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].name, first_chaincode_name, 'Checking peer chaincode name');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking peer chaincode version');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].endpoint, 'peer0.org1.example.com:7051', 'Checking plan peer endpoint');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].ledger_height.low, ledger_height, 'Checking plan peer ledger_height');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].name, first_chaincode_name, 'Checking plan peer chaincode name');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking plan peer chaincode version');
t.equals(results.endorsement_plans[0].layouts[0].G0, 1, 'Checking layout quantities_by_group');
} else {
t.fail('MISSING group results');
Expand All @@ -136,10 +136,10 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {
t.equals(results.peers_by_org.Org1MSP.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking peer chaincode version');
if(results.endorsement_plans[0].groups.G0) {
t.equals(results.endorsement_plans[0].chaincode, first_chaincode_name, 'Checking plan id');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].endpoint, 'peer0.org2.example.com:8051', 'Checking peer endpoint');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].ledger_height.low, ledger_height, 'Checking peer ledger_height');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].name, first_chaincode_name, 'Checking peer chaincode name');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking peer chaincode version');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].endpoint, 'peer0.org1.example.com:7051', 'Checking plan peer endpoint');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].ledger_height.low, ledger_height, 'Checking plan peer ledger_height');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].name, first_chaincode_name, 'Checking plan peer chaincode name');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking plan peer chaincode version');
t.equals(results.endorsement_plans[0].layouts[0].G0, 1, 'Checking layout quantities_by_group');
} else {
t.fail('MISSING group results');
Expand All @@ -161,10 +161,10 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {
t.equals(results.peers_by_org.Org1MSP.peers[0].chaincodes[0].name, first_chaincode_name, 'Checking peer chaincode name');
t.equals(results.peers_by_org.Org1MSP.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking peer chaincode version');
if(results.endorsement_plans[0].groups.G0) {
t.equals(results.endorsement_plans[0].groups.G0.peers[0].endpoint, 'peer0.org2.example.com:8051', 'Checking peer endpoint');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].ledger_height.low, ledger_height, 'Checking peer ledger_height');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].name, first_chaincode_name, 'Checking peer chaincode name');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking peer chaincode version');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].endpoint, 'peer0.org1.example.com:7051', 'Checking plan peer endpoint');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].ledger_height.low, ledger_height, 'Checking plan peer ledger_height');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].name, first_chaincode_name, 'Checking plan peer chaincode name');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking plan peer chaincode version');
t.equals(results.endorsement_plans[0].layouts[0].G0, 1, 'Checking layout quantities_by_group');
} else {
t.fail('MISSING group results');
Expand Down Expand Up @@ -269,6 +269,27 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {
t.fail('Failed to have received a good chaincode to chaincode endorsement ::'+error);
}

const collections_request = {
chaincodeId : first_chaincode_name,
fcn: 'call',
args: [second_chaincode_name, 'move', 'a', 'b','100'],
txId: client_org1.newTransactionID(true),
endorsement_hint: { chaincodes: [
{name: first_chaincode_name, collection_names: ['detailCol', 'sensitiveCol']},
{name: second_chaincode_name, collection_names: ['detailCol', 'sensitiveCol']}
]}
};
try {
const results = await channel_org1.sendTransactionProposal(collections_request);
if(testUtil.checkGoodResults(t, results)) {
t.pass('Successfully endorsed chaincode to chaincode with collections');
} else {
t.fail('Failed to endorse using a chaincode to chaincode call with collections');
}
} catch(error) {
t.fail('Failed to have received a good chaincode to chaincode endorsement with collections::'+error);
}

t.pass('End discovery testing');
t.end();
});
Expand Down Expand Up @@ -305,6 +326,19 @@ async function startChaincode(t, client, channel, orderer, peers, chaincode_id,
try {
const tx_id = client.newTransactionID(true);

const policy = {
identities: [
{ role: { name: 'member', mspId: 'Org1MSP' }},
{ role: { name: 'member', mspId: 'Org2MSP' }}
],
policy: {
'1-of': [
{ 'signed-by': 0},
{ 'signed-by': 1}
]
}
};

// send proposal to endorser
const proposal_request = {
targets: peers,
Expand All @@ -318,19 +352,23 @@ async function startChaincode(t, client, channel, orderer, peers, chaincode_id,
// 'if signed by org1 admin, then that's the only signature required,
// but if that signature is missing, then the policy can also be fulfilled
// when members (non-admin) from both orgs signed'
'endorsement-policy': {
identities: [
{ role: { name: 'member', mspId: 'Org1MSP' }},
{ role: { name: 'member', mspId: 'Org2MSP' }},
{ role: { name: 'admin', mspId: 'Org1MSP' }}
],
policy: {
'1-of': [
{ 'signed-by': 1}
]
'endorsement-policy': policy,
'collections-config': [
{
'name': 'detailCol',
'policy': policy,
'requiredPeerCount': 0,
'maxPeerCount': 1,
'blockToLive': 100
},
{
'name': 'sensitiveCol',
'policy': policy,
'requiredPeerCount': 0,
'maxPeerCount': 1,
'blockToLive': 100
}
},
'collections-config': null
]
};

const proposal_results = await channel.sendInstantiateProposal(proposal_request, 10*60*1000);
Expand Down
26 changes: 21 additions & 5 deletions test/unit/channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -1239,15 +1239,31 @@ test('\n\n ** Channel Discovery tests **\n\n', async (t) => {
added = channel._merge_hints(endorsement_hint_2);
t.equal(added,true,'Check that the new endorsement hint will be added');

const plan_id = JSON.stringify(endorsement_hint_2);
const check_endorsement_hint = channel._discovery_interests.get(plan_id);
t.equals(check_endorsement_hint.chaincodes[0].name, 'somechaincode2', 'checking that the name is correct');
const plan_id_2 = JSON.stringify(endorsement_hint_2);
const check_endorsement_hint_2 = channel._discovery_interests.get(plan_id_2);
t.equals(check_endorsement_hint_2.chaincodes[0].name, 'somechaincode2', 'checking that the name is correct');

channel._last_discover_timestamp = Date.now();
channel._discovery_results = {endorsement_plans:[{plan_id: plan_id}]};
channel._discovery_results = {endorsement_plans:[{plan_id: plan_id_2}]};

const plan = await channel.getEndorsementPlan(endorsement_hint_2);
t.equals(plan.plan_id, plan_id, 'Check the name of endorsement plan retrieved');
t.equals(plan.plan_id, plan_id_2, 'Check the name of endorsement plan retrieved');

const endorsement_hint_3 = channel._buildDiscoveryInterest('somechaincode3', ['collection1', 'collection2', 'collection3']);
added = channel._merge_hints(endorsement_hint_3);
t.equal(added,true,'Check that the new endorsement hint will be added');

const plan_id_3 = JSON.stringify(endorsement_hint_3);
const check_endorsement_hint_3 = channel._discovery_interests.get(plan_id_3);
t.equals(check_endorsement_hint_3.chaincodes[0].name, 'somechaincode3', 'checking that the name is correct');
t.equals(check_endorsement_hint_3.chaincodes[0].collection_names[2], 'collection3', 'checking that the collection is correct');

const proto_interest = channel._buildProtoChaincodeInterest(endorsement_hint_3);
const proto_chaincodes = proto_interest.getChaincodes();
const proto_chaincode = proto_chaincodes[0];
t.equals(proto_chaincode.getName(), 'somechaincode3', 'Checking the protobuf name of the chaincode');
const proto_collections = proto_chaincode.getCollectionNames();
t.equals(proto_collections[2], 'collection3', 'Checking that the collection name is correct');

} catch(error) {
t.fail(error);
Expand Down

0 comments on commit 19b6086

Please sign in to comment.