Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Commit 946ab57

Browse files
committed
[FABG-769] query chaincode's collection configuration
Change-Id: Ie6e82ba1980c930652e9ad5ad736ef2c6b92da81 Signed-off-by: 乔伦 徐 <jamesxql@gmail.com>
1 parent 9efe90f commit 946ab57

File tree

5 files changed

+258
-18
lines changed

5 files changed

+258
-18
lines changed

pkg/client/resmgmt/resmgmt.go

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -574,27 +574,72 @@ func (rc *Client) QueryInstantiatedChaincodes(channelID string, options ...Reque
574574
if len(opts.Targets) >= 1 {
575575
target = opts.Targets[0]
576576
} else {
577-
// discover peers on this channel
578-
discovery, err := chCtx.ChannelService().Discovery()
577+
// select random channel peer
578+
var err error
579+
target, err = rc.selectRandomChannelPeer(chCtx)
579580
if err != nil {
580-
return nil, errors.WithMessage(err, "failed to get discovery service")
581-
}
582-
// default filter will be applied (if any)
583-
targets, err2 := rc.getDefaultTargets(discovery)
584-
if err2 != nil {
585-
return nil, errors.WithMessage(err2, "failed to get default target for query instantiated chaincodes")
581+
return nil, err
586582
}
583+
}
584+
585+
l, err := channel.NewLedger(channelID)
586+
if err != nil {
587+
return nil, err
588+
}
587589

588-
// Filter by MSP since the LSCC only allows local calls
589-
targets = filterTargets(targets, &mspFilter{mspID: chCtx.Identifier().MSPID})
590+
reqCtx, cancel := rc.createRequestContext(opts, fab.PeerResponse)
591+
defer cancel()
590592

591-
if len(targets) == 0 {
592-
return nil, errors.Errorf("no targets in MSP [%s]", chCtx.Identifier().MSPID)
593-
}
593+
// Channel service membership is required to verify signature
594+
channelService := chCtx.ChannelService()
595+
596+
membership, err := channelService.Membership()
597+
if err != nil {
598+
return nil, errors.WithMessage(err, "membership creation failed")
599+
}
600+
601+
responses, err := l.QueryInstantiatedChaincodes(reqCtx, []fab.ProposalProcessor{target}, &verifier.Signature{Membership: membership})
602+
if err != nil {
603+
return nil, err
604+
}
605+
606+
return responses[0], nil
607+
}
608+
609+
// QueryCollectionsConfig queries the collections config on a peer for specific channel. If peer is not specified in options it will query random peer on this channel.
610+
// Parameters:
611+
// channel is mandatory channel name
612+
// chaincode is mandatory chaincode name
613+
// options hold optional request options
614+
//
615+
// Returns:
616+
// list of collections config
617+
func (rc *Client) QueryCollectionsConfig(channelID string, chaincodeName string, options ...RequestOption) (*common.CollectionConfigPackage, error) {
618+
opts, err := rc.prepareRequestOpts(options...)
619+
if err != nil {
620+
return nil, err
621+
}
622+
623+
chCtx, err := contextImpl.NewChannel(
624+
func() (context.Client, error) {
625+
return rc.ctx, nil
626+
},
627+
channelID,
628+
)
629+
if err != nil {
630+
return nil, errors.WithMessage(err, "failed to create channel context")
631+
}
594632

633+
var target fab.ProposalProcessor
634+
if len(opts.Targets) >= 1 {
635+
target = opts.Targets[0]
636+
} else {
595637
// select random channel peer
596-
randomNumber := rand.Intn(len(targets))
597-
target = targets[randomNumber]
638+
var err error
639+
target, err = rc.selectRandomChannelPeer(chCtx)
640+
if err != nil {
641+
return nil, err
642+
}
598643
}
599644

600645
l, err := channel.NewLedger(channelID)
@@ -613,14 +658,38 @@ func (rc *Client) QueryInstantiatedChaincodes(channelID string, options ...Reque
613658
return nil, errors.WithMessage(err, "membership creation failed")
614659
}
615660

616-
responses, err := l.QueryInstantiatedChaincodes(reqCtx, []fab.ProposalProcessor{target}, &verifier.Signature{Membership: membership})
661+
responses, err := l.QueryCollectionsConfig(reqCtx, chaincodeName, []fab.ProposalProcessor{target}, &verifier.Signature{Membership: membership})
617662
if err != nil {
618663
return nil, err
619664
}
620665

621666
return responses[0], nil
622667
}
623668

669+
func (rc *Client) selectRandomChannelPeer(ctx context.Channel) (fab.ProposalProcessor, error) {
670+
// discover peers on this channel
671+
discovery, err := ctx.ChannelService().Discovery()
672+
if err != nil {
673+
return nil, errors.WithMessage(err, "failed to get discovery service")
674+
}
675+
// default filter will be applied (if any)
676+
targets, err2 := rc.getDefaultTargets(discovery)
677+
if err2 != nil {
678+
return nil, errors.WithMessage(err2, "failed to get default target for query instantiated chaincodes")
679+
}
680+
681+
// Filter by MSP since the LSCC only allows local calls
682+
targets = filterTargets(targets, &mspFilter{mspID: ctx.Identifier().MSPID})
683+
684+
if len(targets) == 0 {
685+
return nil, errors.Errorf("no targets in MSP [%s]", ctx.Identifier().MSPID)
686+
}
687+
688+
// select random channel peer
689+
randomNumber := rand.Intn(len(targets))
690+
return targets[randomNumber], nil
691+
}
692+
624693
// QueryChannels queries the names of all the channels that a peer has joined.
625694
// Parameters:
626695
// options hold optional request options

pkg/client/resmgmt/resmgmt_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,24 @@ func TestQueryInstantiatedChaincodes(t *testing.T) {
460460
}
461461
}
462462

463+
func TestQueryCollectionsConfig(t *testing.T) {
464+
rc := setupDefaultResMgmtClient(t)
465+
466+
_, err := rc.QueryCollectionsConfig("mychannel", "mychaincode")
467+
if err == nil {
468+
t.Fatal("QueryInstalledChaincodes: peer cannot be nil")
469+
}
470+
471+
peer := &fcmocks.MockPeer{MockName: "Peer1", MockURL: "http://peer1.com", MockRoles: []string{}, MockCert: nil, MockMSP: "Org1MSP", Status: http.StatusOK}
472+
coll, err := rc.QueryCollectionsConfig("mychannel", "mychaincode", WithTargets(peer))
473+
if err != nil {
474+
t.Fatal(err)
475+
}
476+
if len(coll.Config) != 0 {
477+
t.Fatalf("There is no collection configuration on peer")
478+
}
479+
}
480+
463481
func TestQueryChannels(t *testing.T) {
464482

465483
rc := setupDefaultResMgmtClient(t)

pkg/fab/channel/ledger.go

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ import (
2525
var logger = logging.NewLogger("fabsdk/fab")
2626

2727
const (
28-
lscc = "lscc"
29-
lsccChaincodes = "getchaincodes"
28+
lscc = "lscc"
29+
lsccChaincodes = "getchaincodes"
30+
lsccCollectionsConfig = "getcollectionsconfig"
3031
)
3132

3233
// Ledger is a client that provides access to the underlying ledger of a channel.
@@ -207,6 +208,32 @@ func createChaincodeQueryResponse(tpr *fab.TransactionProposalResponse) (*pb.Cha
207208
return &response, nil
208209
}
209210

211+
// QueryCollectionsConfig queries the collections config for a chaincode on this channel.
212+
func (c *Ledger) QueryCollectionsConfig(reqCtx reqContext.Context, chaincodeName string, targets []fab.ProposalProcessor, verifier ResponseVerifier) ([]*common.CollectionConfigPackage, error) {
213+
cir := createCollectionsConfigInvokeRequest(chaincodeName)
214+
tprs, errs := queryChaincode(reqCtx, c.chName, cir, targets, verifier)
215+
216+
responses := []*common.CollectionConfigPackage{}
217+
for _, tpr := range tprs {
218+
r, err := createCollectionsConfigQueryResponse(tpr)
219+
if err != nil {
220+
errs = multi.Append(errs, errors.WithMessage(err, "From target: "+tpr.Endorser))
221+
} else {
222+
responses = append(responses, r)
223+
}
224+
}
225+
return responses, errs
226+
}
227+
228+
func createCollectionsConfigQueryResponse(tpr *fab.TransactionProposalResponse) (*common.CollectionConfigPackage, error) {
229+
response := common.CollectionConfigPackage{}
230+
err := proto.Unmarshal(tpr.ProposalResponse.GetResponse().Payload, &response)
231+
if err != nil {
232+
return nil, errors.Wrap(err, "unmarshal of transaction proposal response failed")
233+
}
234+
return &response, nil
235+
}
236+
210237
// QueryConfigBlock returns the current configuration block for the specified channel. If the
211238
// peer doesn't belong to the channel, return error
212239
func (c *Ledger) QueryConfigBlock(reqCtx reqContext.Context, targets []fab.ProposalProcessor, verifier ResponseVerifier) (*common.Block, error) {
@@ -273,3 +300,12 @@ func createChaincodeInvokeRequest() fab.ChaincodeInvokeRequest {
273300
}
274301
return cir
275302
}
303+
304+
func createCollectionsConfigInvokeRequest(chaincodeName string) fab.ChaincodeInvokeRequest {
305+
cir := fab.ChaincodeInvokeRequest{
306+
ChaincodeID: lscc,
307+
Fcn: lsccCollectionsConfig,
308+
Args: [][]byte{[]byte(chaincodeName)},
309+
}
310+
return cir
311+
}

test/integration/pkg/client/resmgmt/main_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ import (
1111
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
1212
"github.com/hyperledger/fabric-sdk-go/test/integration"
1313
"github.com/hyperledger/fabric-sdk-go/test/integration/util/runner"
14+
"github.com/stretchr/testify/require"
1415
)
1516

1617
const (
1718
org1Name = "Org1"
19+
org2Name = "Org2"
1820
org1AdminUser = "Admin"
21+
org2AdminUser = "Admin"
22+
orgChannelID = "orgchannel"
1923
)
2024

2125
var mainSDK *fabsdk.FabricSDK
@@ -31,3 +35,9 @@ func TestMain(m *testing.M) {
3135

3236
r.Run(m)
3337
}
38+
39+
func setupMultiOrgContext(t *testing.T, sdk *fabsdk.FabricSDK) []*integration.OrgContext {
40+
orgContext, err := integration.SetupMultiOrgContext(sdk, org1Name, org2Name, org1AdminUser, org2AdminUser)
41+
require.NoError(t, err)
42+
return orgContext
43+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
Copyright SecureKey Technologies Inc. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package resmgmt
8+
9+
import (
10+
"reflect"
11+
"testing"
12+
13+
"github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"
14+
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
15+
"github.com/hyperledger/fabric-sdk-go/test/integration"
16+
"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/common/cauthdsl"
17+
cb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common"
18+
"github.com/stretchr/testify/require"
19+
)
20+
21+
const (
22+
collCfgName = "collection1"
23+
collCfgBlockToLive = 1000
24+
collCfgRequiredPeerCount = 0
25+
collCfgMaximumPeerCount = 2
26+
collCfgPolicy = "OR('Org1MSP.member','Org2MSP.member')"
27+
)
28+
29+
func TestQueryCollectionsConfig(t *testing.T) {
30+
sdk := mainSDK
31+
32+
orgsContext := setupMultiOrgContext(t, sdk)
33+
err := integration.EnsureChannelCreatedAndPeersJoined(t, sdk, orgChannelID, "orgchannel.tx", orgsContext)
34+
require.NoError(t, err)
35+
36+
ccID := integration.GenerateExamplePvtID(true)
37+
collConfig, err := newCollectionConfig(collCfgName, collCfgPolicy, collCfgRequiredPeerCount, collCfgMaximumPeerCount, collCfgBlockToLive)
38+
require.NoError(t, err)
39+
40+
err = integration.InstallExamplePvtChaincode(orgsContext, ccID)
41+
require.NoError(t, err)
42+
err = integration.InstantiateExamplePvtChaincode(orgsContext, orgChannelID, ccID, "OR('Org1MSP.member','Org2MSP.member')", collConfig)
43+
require.NoError(t, err)
44+
45+
org1AdminClientContext := sdk.Context(fabsdk.WithUser(org1AdminUser), fabsdk.WithOrg(org1Name))
46+
client, err := resmgmt.New(org1AdminClientContext)
47+
if err != nil {
48+
t.Fatalf("Failed to create new resource management client: %s", err)
49+
}
50+
51+
resp, err := client.QueryCollectionsConfig(orgChannelID, ccID)
52+
if err != nil {
53+
t.Fatalf("QueryCollectionsConfig return error: %s", err)
54+
}
55+
if len(resp.Config) != 1 {
56+
t.Fatalf("The number of collection config is incorrect, expected 1, got %d", len(resp.Config))
57+
}
58+
59+
conf := resp.Config[0]
60+
switch cconf := conf.Payload.(type) {
61+
case *cb.CollectionConfig_StaticCollectionConfig:
62+
checkStaticCollectionConfig(t, cconf.StaticCollectionConfig)
63+
default:
64+
t.Fatalf("The CollectionConfig.Payload's type is incorrect, expected `CollectionConfig_StaticCollectionConfig`, got %+v", reflect.TypeOf(conf.Payload))
65+
}
66+
}
67+
68+
func checkStaticCollectionConfig(t *testing.T, collConf *cb.StaticCollectionConfig) {
69+
if collConf.Name != collCfgName {
70+
t.Fatalf("CollectionConfig'name is incorrect, expected collection1, got %s", collConf.Name)
71+
}
72+
if collConf.BlockToLive != collCfgBlockToLive {
73+
t.Fatalf("The property of BlockToLive is incorrect, expected 1000, got %d", collConf.BlockToLive)
74+
}
75+
if collConf.RequiredPeerCount != collCfgRequiredPeerCount {
76+
t.Fatalf("The property of RequiredPeerCount is incorrect, expected 0, got %d", collConf.RequiredPeerCount)
77+
}
78+
if collConf.MaximumPeerCount != collCfgMaximumPeerCount {
79+
t.Fatalf("The property of MaximumPeerCount is incorrect, expected 2, got %d", collConf.MaximumPeerCount)
80+
}
81+
if collConf.MemberOrgsPolicy.GetSignaturePolicy() == nil {
82+
t.Fatalf("The property of MemberOrgsPolicy must be SignaturePolicy")
83+
}
84+
}
85+
86+
func newCollectionConfig(colName, policy string, reqPeerCount, maxPeerCount int32, blockToLive uint64) (*cb.CollectionConfig, error) {
87+
p, err := cauthdsl.FromString(policy)
88+
if err != nil {
89+
return nil, err
90+
}
91+
cpc := &cb.CollectionPolicyConfig{
92+
Payload: &cb.CollectionPolicyConfig_SignaturePolicy{
93+
SignaturePolicy: p,
94+
},
95+
}
96+
return &cb.CollectionConfig{
97+
Payload: &cb.CollectionConfig_StaticCollectionConfig{
98+
StaticCollectionConfig: &cb.StaticCollectionConfig{
99+
Name: colName,
100+
MemberOrgsPolicy: cpc,
101+
RequiredPeerCount: reqPeerCount,
102+
MaximumPeerCount: maxPeerCount,
103+
BlockToLive: blockToLive,
104+
},
105+
},
106+
}, nil
107+
}

0 commit comments

Comments
 (0)