Skip to content

Commit 715be74

Browse files
committed
[FAB-9948] Collection retrieval for service discovery
This change set adds collection retrieval for service discovery. Change-Id: Ie8ba6c76f20465deee0471e5be858de0ecb77f89 Signed-off-by: yacovm <yacovm@il.ibm.com>
1 parent 58d2b88 commit 715be74

File tree

12 files changed

+78
-26
lines changed

12 files changed

+78
-26
lines changed

common/chaincode/metadata.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ type InstalledChaincode struct {
2121

2222
// Metadata defines channel-scoped metadata of a chaincode
2323
type Metadata struct {
24-
Name string
25-
Version string
26-
Policy []byte
27-
Id []byte
24+
Name string
25+
Version string
26+
Policy []byte
27+
Id []byte
28+
CollectionsConfig []byte
2829
}
2930

3031
// MetadataSet defines an aggregation of Metadata

core/cclifecycle/lifecycle.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,15 @@ func NewLifeCycle(installedChaincodes Enumerator) (*Lifecycle, error) {
108108

109109
// Metadata returns the metadata of the chaincode on the given channel,
110110
// or nil if not found or an error occurred at retrieving it
111-
func (lc *Lifecycle) Metadata(channel string, cc string) *chaincode.Metadata {
111+
func (lc *Lifecycle) Metadata(channel string, cc string, collections bool) *chaincode.Metadata {
112112
queryCreator := lc.queryCreatorsByChannel[channel]
113113
if queryCreator == nil {
114114
Logger.Warning("Requested Metadata for non-existent channel", channel)
115115
return nil
116116
}
117-
if md, found := lc.deployedCCsByChannel[channel].Lookup(cc); found {
117+
// Search the metadata in our local cache, and if it exists - return it, but only if
118+
// no collections were specified in the invocation.
119+
if md, found := lc.deployedCCsByChannel[channel].Lookup(cc); found && !collections {
118120
Logger.Debug("Returning metadata for channel", channel, ", chaincode", cc, ":", md)
119121
return &md
120122
}
@@ -123,7 +125,7 @@ func (lc *Lifecycle) Metadata(channel string, cc string) *chaincode.Metadata {
123125
Logger.Error("Failed obtaining new query for channel", channel, ":", err)
124126
return nil
125127
}
126-
md, err := DeployedChaincodes(query, AcceptAll, cc)
128+
md, err := DeployedChaincodes(query, AcceptAll, collections, cc)
127129
if err != nil {
128130
Logger.Error("Failed querying LSCC for channel", channel, ":", err)
129131
return nil

core/cclifecycle/lifecycle_test.go

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/hyperledger/fabric/core/cclifecycle"
2020
"github.com/hyperledger/fabric/core/cclifecycle/mocks"
2121
"github.com/hyperledger/fabric/core/common/ccprovider"
22+
"github.com/hyperledger/fabric/core/common/privdata"
2223
"github.com/hyperledger/fabric/core/ledger/cceventmgmt"
2324
"github.com/hyperledger/fabric/protos/utils"
2425
"github.com/op/go-logging"
@@ -304,7 +305,7 @@ func TestMetadata(t *testing.T) {
304305
assert.NoError(t, err)
305306

306307
// Scenario I: No subscription was invoked on the lifecycle
307-
md := lc.Metadata("mychannel", "cc1")
308+
md := lc.Metadata("mychannel", "cc1", false)
308309
assert.Nil(t, md)
309310
logger.AssertLogged("Requested Metadata for non-existent channel mychannel")
310311

@@ -316,7 +317,7 @@ func TestMetadata(t *testing.T) {
316317
defer sub.ChaincodeDeployDone(true)
317318
assert.NoError(t, err)
318319
assert.NotNil(t, sub)
319-
md = lc.Metadata("mychannel", "cc1")
320+
md = lc.Metadata("mychannel", "cc1", false)
320321
assert.Equal(t, &chaincode.Metadata{
321322
Name: "cc1",
322323
Version: "1.0",
@@ -328,36 +329,62 @@ func TestMetadata(t *testing.T) {
328329
// Scenario III: A metadata retrieval is made and the chaincode is not in memory yet,
329330
// and when the query is attempted to be made - it fails.
330331
queryCreator.On("NewQuery").Return(nil, errors.New("failed obtaining query executor")).Once()
331-
md = lc.Metadata("mychannel", "cc2")
332+
md = lc.Metadata("mychannel", "cc2", false)
332333
assert.Nil(t, md)
333334
logger.AssertLogged("Failed obtaining new query for channel mychannel : failed obtaining query executor")
334335

335336
// Scenario IV: A metadata retrieval is made and the chaincode is not in memory yet,
336337
// and when the query is attempted to be made - it succeeds, but GetState fails.
337338
queryCreator.On("NewQuery").Return(query, nil).Once()
338339
query.On("GetState", "lscc", "cc2").Return(nil, errors.New("GetState failed")).Once()
339-
md = lc.Metadata("mychannel", "cc2")
340+
md = lc.Metadata("mychannel", "cc2", false)
340341
assert.Nil(t, md)
341342
logger.AssertLogged("Failed querying LSCC for channel mychannel : GetState failed")
342343

343344
// Scenario V: A metadata retrieval is made and the chaincode is not in memory yet,
344345
// and both the query and the GetState succeed, however - GetState returns nil
345346
queryCreator.On("NewQuery").Return(query, nil).Once()
346347
query.On("GetState", "lscc", "cc2").Return(nil, nil).Once()
347-
md = lc.Metadata("mychannel", "cc2")
348+
md = lc.Metadata("mychannel", "cc2", false)
348349
assert.Nil(t, md)
349350
logger.AssertLogged("Chaincode cc2 isn't defined in channel mychannel")
350351

351352
// Scenario VI: A metadata retrieval is made and the chaincode is not in memory yet,
352353
// and both the query and the GetState succeed, however - GetState returns a valid metadata
353354
queryCreator.On("NewQuery").Return(query, nil).Once()
354355
query.On("GetState", "lscc", "cc2").Return(cc2Bytes, nil).Once()
355-
md = lc.Metadata("mychannel", "cc2")
356+
md = lc.Metadata("mychannel", "cc2", false)
356357
assert.Equal(t, &chaincode.Metadata{
357358
Name: "cc2",
358359
Version: "1.0",
359360
Id: []byte{42},
360361
}, md)
362+
363+
// Scenario VII: A metadata retrieval is made and the chaincode is in the memory,
364+
// but a collection is also specified, thus - the retrieval should bypass the memory cache
365+
// and go straight into the stateDB.
366+
queryCreator.On("NewQuery").Return(query, nil).Once()
367+
query.On("GetState", "lscc", "cc1").Return(cc1Bytes, nil).Once()
368+
query.On("GetState", "lscc", privdata.BuildCollectionKVSKey("cc1")).Return([]byte{10, 10, 10}, nil).Once()
369+
md = lc.Metadata("mychannel", "cc1", true)
370+
assert.Equal(t, &chaincode.Metadata{
371+
Name: "cc1",
372+
Version: "1.0",
373+
Id: []byte{42},
374+
Policy: []byte{1, 2, 3, 4, 5},
375+
CollectionsConfig: []byte{10, 10, 10},
376+
}, md)
377+
logger.AssertLogged("Retrieved collection config for cc1 from cc1~collection")
378+
379+
// Scenario VIII: A metadata retrieval is made and the chaincode is in the memory,
380+
// but a collection is also specified, thus - the retrieval should bypass the memory cache
381+
// and go straight into the stateDB. However - the retrieval fails
382+
queryCreator.On("NewQuery").Return(query, nil).Once()
383+
query.On("GetState", "lscc", "cc1").Return(cc1Bytes, nil).Once()
384+
query.On("GetState", "lscc", privdata.BuildCollectionKVSKey("cc1")).Return(nil, errors.New("foo")).Once()
385+
md = lc.Metadata("mychannel", "cc1", true)
386+
assert.Nil(t, md)
387+
logger.AssertLogged("Failed querying lscc namespace for cc1~collection: foo")
361388
}
362389

363390
type logAsserter struct {

core/cclifecycle/subscription.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type Subscription struct {
2424
pendingUpdates chan *cceventmgmt.ChaincodeDefinition
2525
}
2626

27-
type depCCsRetriever func(Query, ChaincodePredicate, ...string) (chaincode.MetadataSet, error)
27+
type depCCsRetriever func(Query, ChaincodePredicate, bool, ...string) (chaincode.MetadataSet, error)
2828

2929
// HandleChaincodeDeploy is expected to be invoked when a chaincode is deployed via a deploy transaction and the chaicndoe was already
3030
// installed on the peer. This also gets invoked when an already deployed chaincode is installed on the peer
@@ -97,5 +97,5 @@ func queryChaincodeDefinitions(query Query, ccs []chaincode.InstalledChaincode,
9797
return true
9898
}
9999

100-
return deployedCCs(query, filter, names(ccs)...)
100+
return deployedCCs(query, filter, false, names(ccs)...)
101101
}

core/cclifecycle/util.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/golang/protobuf/proto"
1414
"github.com/hyperledger/fabric/common/chaincode"
1515
"github.com/hyperledger/fabric/core/common/ccprovider"
16+
"github.com/hyperledger/fabric/core/common/privdata"
1617
"github.com/pkg/errors"
1718
)
1819

@@ -76,7 +77,7 @@ func InstalledCCs(dir string, ls DirEnumerator, ccFromPath ChaincodeExtractor) (
7677
type ChaincodePredicate func(cc chaincode.Metadata) bool
7778

7879
// DeployedChaincodes retrieves the metadata of the given deployed chaincodes
79-
func DeployedChaincodes(q Query, filter ChaincodePredicate, chaincodes ...string) (chaincode.MetadataSet, error) {
80+
func DeployedChaincodes(q Query, filter ChaincodePredicate, loadCollections bool, chaincodes ...string) (chaincode.MetadataSet, error) {
8081
defer q.Done()
8182

8283
var res chaincode.MetadataSet
@@ -111,6 +112,18 @@ func DeployedChaincodes(q Query, filter ChaincodePredicate, chaincodes ...string
111112
Logger.Debug("Filtered out", instCC)
112113
continue
113114
}
115+
116+
if loadCollections {
117+
key := privdata.BuildCollectionKVSKey(cc)
118+
collectionData, err := q.GetState("lscc", key)
119+
if err != nil {
120+
Logger.Errorf("Failed querying lscc namespace for %s: %v", key, err)
121+
return nil, errors.WithStack(err)
122+
}
123+
instCC.CollectionsConfig = collectionData
124+
Logger.Debug("Retrieved collection config for", cc, "from", key)
125+
}
126+
114127
res = append(res, instCC)
115128
}
116129
Logger.Debug("Returning", res)

core/cclifecycle/util_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ func TestChaincodeInspection(t *testing.T) {
253253
query.On("Done")
254254
query.On("GetState", mock.Anything, mock.Anything).Return(test.returnedCCBytes, test.queryErr).Once()
255255
query.On("GetState", mock.Anything, mock.Anything).Return(cc2Bytes, nil).Once()
256-
ccInfo, err := cc.DeployedChaincodes(query, test.filter, test.queriedChaincodes...)
256+
ccInfo, err := cc.DeployedChaincodes(query, test.filter, false, test.queriedChaincodes...)
257257
if test.queryErr != nil {
258258
assert.Error(t, err)
259259
} else {

core/common/privdata/store.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,19 @@ func RetrieveCollectionConfigPackageFromState(cc common.CollectionCriteria, stat
6868
if cb == nil {
6969
return nil, NoSuchCollectionError(cc)
7070
}
71+
conf, err := ParseCollectionConfig(cb)
72+
if err != nil {
73+
return nil, errors.Wrapf(err, "invalid configuration for collection criteria %#v", cc)
74+
}
75+
return conf, nil
76+
}
7177

78+
// ParseCollectionConfig parses the collection configuration from the given serialized representation
79+
func ParseCollectionConfig(colBytes []byte) (*common.CollectionConfigPackage, error) {
7280
collections := &common.CollectionConfigPackage{}
73-
err = proto.Unmarshal(cb, collections)
81+
err := proto.Unmarshal(colBytes, collections)
7482
if err != nil {
75-
return nil, errors.Wrapf(err, "invalid configuration for collection criteria %#v", cc)
83+
return nil, errors.WithStack(err)
7684
}
7785

7886
return collections, nil

discovery/client/client_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ type ccMetadataFetcher struct {
570570
mock.Mock
571571
}
572572

573-
func (mdf *ccMetadataFetcher) Metadata(channel string, cc string) *chaincode.Metadata {
573+
func (mdf *ccMetadataFetcher) Metadata(channel string, cc string, _ bool) *chaincode.Metadata {
574574
return mdf.Called().Get(0).(*chaincode.Metadata)
575575
}
576576

discovery/endorsement/endorsement.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type principalEvaluator interface {
3737
type chaincodeMetadataFetcher interface {
3838
// ChaincodeMetadata returns the metadata of the chaincode as appears in the ledger,
3939
// or nil if the channel doesn't exist, or the chaincode isn't found in the ledger
40-
Metadata(channel string, cc string) *chaincode.Metadata
40+
Metadata(channel string, cc string, loadCollections bool) *chaincode.Metadata
4141
}
4242

4343
type policyFetcher interface {
@@ -80,7 +80,8 @@ type peerPrincipalEvaluator func(member discovery2.NetworkMember, principal *msp
8080
// PeersForEndorsement returns an EndorsementDescriptor for a given set of peers, channel, and chaincode
8181
func (ea *endorsementAnalyzer) PeersForEndorsement(chainID common.ChainID, interest *discovery.ChaincodeInterest) (*discovery.EndorsementDescriptor, error) {
8282
chaincode := interest.Chaincodes[0]
83-
ccMD := ea.Metadata(string(chainID), chaincode.Name)
83+
loadCollections := len(chaincode.CollectionNames) > 0
84+
ccMD := ea.Metadata(string(chainID), chaincode.Name, loadCollections)
8485
if ccMD == nil {
8586
return nil, errors.Errorf("No metadata was found for chaincode %s in channel %s", chaincode.Name, string(chainID))
8687
}

discovery/endorsement/endorsement_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ type metadataFetcher struct {
332332
mock.Mock
333333
}
334334

335-
func (mf *metadataFetcher) Metadata(channel string, cc string) *chaincode.Metadata {
335+
func (mf *metadataFetcher) Metadata(channel string, cc string, _ bool) *chaincode.Metadata {
336336
arg := mf.Called().Get(0)
337337
if arg == nil {
338338
return nil

0 commit comments

Comments
 (0)