github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/chaincode/lifecycle/deployedcc_infoprovider_test.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package lifecycle_test
     8  
     9  import (
    10  	"fmt"
    11  
    12  	"github.com/hechain20/hechain/common/channelconfig"
    13  	commonledger "github.com/hechain20/hechain/common/ledger"
    14  	"github.com/hechain20/hechain/common/util"
    15  	"github.com/hechain20/hechain/core/chaincode/lifecycle"
    16  	"github.com/hechain20/hechain/core/chaincode/lifecycle/mock"
    17  	"github.com/hechain20/hechain/core/ledger"
    18  	"github.com/hechain20/hechain/core/peer"
    19  	"github.com/hechain20/hechain/gossip/privdata"
    20  	"github.com/hechain20/hechain/protoutil"
    21  	cb "github.com/hyperledger/fabric-protos-go/common"
    22  	"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
    23  	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
    24  	"github.com/hyperledger/fabric-protos-go/msp"
    25  	pb "github.com/hyperledger/fabric-protos-go/peer"
    26  	lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle"
    27  
    28  	"github.com/golang/protobuf/proto"
    29  
    30  	. "github.com/onsi/ginkgo"
    31  	. "github.com/onsi/gomega"
    32  )
    33  
    34  var _ = Describe("ValidatorCommitter", func() {
    35  	var (
    36  		vc                      *lifecycle.ValidatorCommitter
    37  		resources               *lifecycle.Resources
    38  		privdataConfig          *privdata.PrivdataConfig
    39  		fakeLegacyProvider      *mock.LegacyDeployedCCInfoProvider
    40  		fakeQueryExecutor       *mock.SimpleQueryExecutor
    41  		fakeChannelConfigSource *mock.ChannelConfigSource
    42  		fakeChannelConfig       *mock.ChannelConfig
    43  		fakeApplicationConfig   *mock.ApplicationConfig
    44  		fakeOrgConfigs          []*mock.ApplicationOrgConfig
    45  		fakePolicyManager       *mock.PolicyManager
    46  
    47  		fakePublicState  MapLedgerShim
    48  		fakeChaincodeDef *lifecycle.ChaincodeDefinition
    49  	)
    50  
    51  	BeforeEach(func() {
    52  		fakeLegacyProvider = &mock.LegacyDeployedCCInfoProvider{}
    53  		fakeChannelConfigSource = &mock.ChannelConfigSource{}
    54  		fakeChannelConfig = &mock.ChannelConfig{}
    55  		fakeChannelConfigSource.GetStableChannelConfigReturns(fakeChannelConfig)
    56  		fakeApplicationConfig = &mock.ApplicationConfig{}
    57  		fakeChannelConfig.ApplicationConfigReturns(fakeApplicationConfig, true)
    58  		fakeOrgConfigs = []*mock.ApplicationOrgConfig{{}, {}}
    59  		fakeOrgConfigs[0].MSPIDReturns("first-mspid")
    60  		fakeOrgConfigs[1].MSPIDReturns("second-mspid")
    61  		fakePolicyManager = &mock.PolicyManager{}
    62  		fakePolicyManager.GetPolicyReturns(nil, true)
    63  		fakeChannelConfig.PolicyManagerReturns(fakePolicyManager)
    64  
    65  		fakeApplicationConfig.OrganizationsReturns(map[string]channelconfig.ApplicationOrg{
    66  			"org0": fakeOrgConfigs[0],
    67  			"org1": fakeOrgConfigs[1],
    68  		})
    69  
    70  		resources = &lifecycle.Resources{
    71  			ChannelConfigSource: fakeChannelConfigSource,
    72  			Serializer:          &lifecycle.Serializer{},
    73  		}
    74  
    75  		privdataConfig = &privdata.PrivdataConfig{
    76  			ImplicitCollDisseminationPolicy: privdata.ImplicitCollectionDisseminationPolicy{
    77  				RequiredPeerCount: 1,
    78  				MaxPeerCount:      2,
    79  			},
    80  		}
    81  		vc = &lifecycle.ValidatorCommitter{
    82  			CoreConfig:                   &peer.Config{LocalMSPID: "first-mspid"},
    83  			PrivdataConfig:               privdataConfig,
    84  			Resources:                    resources,
    85  			LegacyDeployedCCInfoProvider: fakeLegacyProvider,
    86  		}
    87  
    88  		fakePublicState = MapLedgerShim(map[string][]byte{})
    89  		fakeQueryExecutor = &mock.SimpleQueryExecutor{}
    90  		fakeQueryExecutor.GetStateStub = func(namespace, key string) ([]byte, error) {
    91  			return fakePublicState.GetState(key)
    92  		}
    93  
    94  		fakeChaincodeDef = &lifecycle.ChaincodeDefinition{
    95  			EndorsementInfo: &lb.ChaincodeEndorsementInfo{
    96  				Version: "version",
    97  			},
    98  			ValidationInfo: &lb.ChaincodeValidationInfo{
    99  				ValidationPlugin:    "validation-plugin",
   100  				ValidationParameter: []byte("validation-parameter"),
   101  			},
   102  			Collections: &pb.CollectionConfigPackage{
   103  				Config: []*pb.CollectionConfig{
   104  					{
   105  						Payload: &pb.CollectionConfig_StaticCollectionConfig{
   106  							StaticCollectionConfig: &pb.StaticCollectionConfig{
   107  								Name: "collection-name",
   108  							},
   109  						},
   110  					},
   111  				},
   112  			},
   113  		}
   114  		err := resources.Serializer.Serialize(lifecycle.NamespacesName, "cc-name", fakeChaincodeDef, fakePublicState)
   115  		Expect(err).NotTo(HaveOccurred())
   116  	})
   117  
   118  	Describe("Namespaces", func() {
   119  		BeforeEach(func() {
   120  			fakeLegacyProvider.NamespacesReturns([]string{"a", "b", "c"})
   121  		})
   122  
   123  		It("appends its own namespaces the legacy impl", func() {
   124  			res := vc.Namespaces()
   125  			Expect(res).To(Equal([]string{"_lifecycle", "a", "b", "c"}))
   126  			Expect(fakeLegacyProvider.NamespacesCallCount()).To(Equal(1))
   127  		})
   128  	})
   129  
   130  	Describe("UpdatedChaincodes", func() {
   131  		var updates map[string][]*kvrwset.KVWrite
   132  
   133  		BeforeEach(func() {
   134  			updates = map[string][]*kvrwset.KVWrite{
   135  				"_lifecycle": {
   136  					{Key: "some/random/value"},
   137  					{Key: "namespaces/fields/cc-name/Sequence"},
   138  					{Key: "prefix/namespaces/fields/cc-name/Sequence"},
   139  					{Key: "namespaces/fields/Sequence/infix"},
   140  					{Key: "namespaces/fields/cc-name/Sequence/Postfix"},
   141  				},
   142  				"other-namespace": nil,
   143  			}
   144  			fakeLegacyProvider.UpdatedChaincodesReturns([]*ledger.ChaincodeLifecycleInfo{
   145  				{Name: "foo"},
   146  				{Name: "bar"},
   147  			}, nil)
   148  		})
   149  
   150  		It("checks its own namespace, then passes through to the legacy impl", func() {
   151  			res, err := vc.UpdatedChaincodes(updates)
   152  			Expect(res).To(Equal([]*ledger.ChaincodeLifecycleInfo{
   153  				{Name: "cc-name"},
   154  				{Name: "foo"},
   155  				{Name: "bar"},
   156  			}))
   157  			Expect(err).NotTo(HaveOccurred())
   158  			Expect(fakeLegacyProvider.UpdatedChaincodesCallCount()).To(Equal(1))
   159  			Expect(fakeLegacyProvider.UpdatedChaincodesArgsForCall(0)).To(Equal(updates))
   160  		})
   161  
   162  		Context("when the legacy provider returns an error", func() {
   163  			BeforeEach(func() {
   164  				fakeLegacyProvider.UpdatedChaincodesReturns(nil, fmt.Errorf("legacy-error"))
   165  			})
   166  
   167  			It("wraps and returns the error", func() {
   168  				_, err := vc.UpdatedChaincodes(updates)
   169  				Expect(err).To(MatchError("error invoking legacy deployed cc info provider: legacy-error"))
   170  			})
   171  		})
   172  	})
   173  
   174  	Describe("ChaincodeInfo", func() {
   175  		It("returns the info found in the new lifecycle", func() {
   176  			res, err := vc.ChaincodeInfo("channel-name", "cc-name", fakeQueryExecutor)
   177  			Expect(err).NotTo(HaveOccurred())
   178  			Expect(res.Name).To(Equal("cc-name"))
   179  			Expect(res.Version).To(Equal("version"))
   180  			Expect(res.Hash).To(Equal(util.ComputeSHA256([]byte("cc-name:version"))))
   181  			Expect(len(res.ExplicitCollectionConfigPkg.Config)).To(Equal(1))
   182  		})
   183  
   184  		Context("when the requested chaincode is _lifecycle", func() {
   185  			It("returns the implicit collections only", func() {
   186  				res, err := vc.ChaincodeInfo("channel-name", "_lifecycle", fakeQueryExecutor)
   187  				Expect(err).NotTo(HaveOccurred())
   188  				Expect(res.Name).To(Equal("_lifecycle"))
   189  				Expect(res.ExplicitCollectionConfigPkg).To(BeNil())
   190  			})
   191  		})
   192  
   193  		Context("when the ledger returns an error", func() {
   194  			BeforeEach(func() {
   195  				fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("state-error"))
   196  			})
   197  
   198  			It("wraps and returns the error", func() {
   199  				_, err := vc.ChaincodeInfo("channel-name", "cc-name", fakeQueryExecutor)
   200  				Expect(err).To(MatchError("could not get info about chaincode: could not deserialize metadata for chaincode cc-name: could not query metadata for namespace namespaces/cc-name: state-error"))
   201  			})
   202  		})
   203  
   204  		Context("when the chaincode cannot be found in the new lifecycle", func() {
   205  			BeforeEach(func() {
   206  				fakeLegacyProvider.ChaincodeInfoReturns(&ledger.DeployedChaincodeInfo{
   207  					Name:    "legacy-name",
   208  					Hash:    []byte("hash"),
   209  					Version: "cc-version",
   210  				}, fmt.Errorf("chaincode-info-error"))
   211  			})
   212  
   213  			It("passes through to the legacy impl", func() {
   214  				res, err := vc.ChaincodeInfo("channel-name", "legacy-name", fakeQueryExecutor)
   215  				Expect(res).To(Equal(&ledger.DeployedChaincodeInfo{
   216  					Name:    "legacy-name",
   217  					Hash:    []byte("hash"),
   218  					Version: "cc-version",
   219  				}))
   220  				Expect(err).To(MatchError("chaincode-info-error"))
   221  				Expect(fakeLegacyProvider.ChaincodeInfoCallCount()).To(Equal(1))
   222  				channelID, ccName, qe := fakeLegacyProvider.ChaincodeInfoArgsForCall(0)
   223  				Expect(channelID).To(Equal("channel-name"))
   224  				Expect(ccName).To(Equal("legacy-name"))
   225  				Expect(qe).To(Equal(fakeQueryExecutor))
   226  			})
   227  		})
   228  
   229  		Context("when the data is corrupt", func() {
   230  			BeforeEach(func() {
   231  				fakePublicState["namespaces/fields/cc-name/ValidationInfo"] = []byte("garbage")
   232  			})
   233  
   234  			It("wraps and returns that error", func() {
   235  				_, err := vc.ChaincodeInfo("channel-name", "cc-name", fakeQueryExecutor)
   236  				Expect(err).To(MatchError("could not get info about chaincode: could not deserialize chaincode definition for chaincode cc-name: could not unmarshal state for key namespaces/fields/cc-name/ValidationInfo: proto: can't skip unknown wire type 7"))
   237  			})
   238  		})
   239  	})
   240  
   241  	Describe("AllChaincodesInfo", func() {
   242  		var fakeStateIteratorKVs MapLedgerShim
   243  		BeforeEach(func() {
   244  			fakeStateIteratorKVs = MapLedgerShim(map[string][]byte{})
   245  			err := resources.Serializer.Serialize("namespaces", "cc-name", &lifecycle.ChaincodeDefinition{}, fakeStateIteratorKVs)
   246  			Expect(err).NotTo(HaveOccurred())
   247  			fakeQueryExecutor.GetStateRangeScanIteratorStub = func(namespace, begin, end string) (commonledger.ResultsIterator, error) {
   248  				fakeResultsIterator := &mock.ResultsIterator{}
   249  				i := 0
   250  				for key, value := range fakeStateIteratorKVs {
   251  					if key >= begin && key < end {
   252  						fakeResultsIterator.NextReturnsOnCall(i, &queryresult.KV{
   253  							Key:   key,
   254  							Value: value,
   255  						}, nil)
   256  						i++
   257  					}
   258  				}
   259  				return fakeResultsIterator, nil
   260  			}
   261  		})
   262  
   263  		It("returns deployed chaincodes info for new lifecycle chaincodes", func() {
   264  			res, err := vc.AllChaincodesInfo("channel-name", fakeQueryExecutor)
   265  			Expect(err).NotTo(HaveOccurred())
   266  			Expect(res).To(HaveLen(1))
   267  			// because ExplicitCollectionConfigPkg is a protobuf object, we have to compare individual fields
   268  			ccInfo, ok := res["cc-name"]
   269  			Expect(ok).To(BeTrue())
   270  			Expect(ccInfo.Name).To(Equal("cc-name"))
   271  			Expect(ccInfo.IsLegacy).To(BeFalse())
   272  			Expect(ccInfo.Version).To(Equal(fakeChaincodeDef.EndorsementInfo.Version))
   273  			Expect(proto.Equal(ccInfo.ExplicitCollectionConfigPkg, fakeChaincodeDef.Collections)).To(BeTrue())
   274  		})
   275  
   276  		Context("when state range scan returns an error", func() {
   277  			BeforeEach(func() {
   278  				fakeQueryExecutor.GetStateRangeScanIteratorReturns(nil, fmt.Errorf("rangescan-error"))
   279  			})
   280  
   281  			It("returns the error", func() {
   282  				_, err := vc.AllChaincodesInfo("channel-name", fakeQueryExecutor)
   283  				Expect(err).To(MatchError("could not query namespace metadata: could not get state range for namespace namespaces: could not get state iterator: rangescan-error"))
   284  			})
   285  		})
   286  
   287  		Context("when get state returns an error", func() {
   288  			BeforeEach(func() {
   289  				fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("getstate-error"))
   290  			})
   291  
   292  			It("returns the error", func() {
   293  				_, err := vc.AllChaincodesInfo("channel-name", fakeQueryExecutor)
   294  				Expect(err).To(MatchError("could not get info about chaincode: could not deserialize metadata for chaincode cc-name: could not query metadata for namespace namespaces/cc-name: getstate-error"))
   295  			})
   296  		})
   297  
   298  		Context("when LegacyProvider.AllChaincodesInfo returns DeployedChaincodeInfo", func() {
   299  			var cc1Info, cc2Info *ledger.DeployedChaincodeInfo
   300  			BeforeEach(func() {
   301  				// fakeLegacyProvider returns 2 DeployedChaincodeInfo, one of them has the same name as new lifecycle chaincode "cc-name"
   302  				cc1Info = &ledger.DeployedChaincodeInfo{
   303  					Name:     "cc-name",
   304  					Hash:     []byte("hash"),
   305  					Version:  "cc-version",
   306  					IsLegacy: true,
   307  				}
   308  				cc2Info = &ledger.DeployedChaincodeInfo{
   309  					Name:     "another-cc-name",
   310  					Hash:     []byte("hash"),
   311  					Version:  "cc-version",
   312  					IsLegacy: true,
   313  					ExplicitCollectionConfigPkg: &pb.CollectionConfigPackage{
   314  						Config: []*pb.CollectionConfig{
   315  							{
   316  								Payload: &pb.CollectionConfig_StaticCollectionConfig{
   317  									StaticCollectionConfig: &pb.StaticCollectionConfig{
   318  										Name: "collection-name",
   319  									},
   320  								},
   321  							},
   322  						},
   323  					},
   324  				}
   325  				fakeLegacyProvider.AllChaincodesInfoReturns(map[string]*ledger.DeployedChaincodeInfo{
   326  					"cc-name":         cc1Info,
   327  					"another-cc-name": cc2Info,
   328  				}, nil)
   329  			})
   330  
   331  			It("returns new lifecycle and legacy chaincodes but excludes duplicate legacy chaincode", func() {
   332  				res, err := vc.AllChaincodesInfo("testchannel", fakeQueryExecutor)
   333  				Expect(err).NotTo(HaveOccurred())
   334  				Expect(res).To(HaveLen(2))
   335  				Expect(fakeLegacyProvider.AllChaincodesInfoCallCount()).To(Equal(1))
   336  				channelID, qe := fakeLegacyProvider.AllChaincodesInfoArgsForCall(0)
   337  				Expect(channelID).To(Equal("testchannel"))
   338  				Expect(qe).To(Equal(fakeQueryExecutor))
   339  
   340  				newlifecycleCCInfo, ok := res["cc-name"]
   341  				Expect(ok).To(BeTrue())
   342  				Expect(newlifecycleCCInfo.Name).To(Equal("cc-name"))
   343  				Expect(newlifecycleCCInfo.IsLegacy).To(BeFalse())
   344  				Expect(newlifecycleCCInfo.Version).To(Equal(fakeChaincodeDef.EndorsementInfo.Version))
   345  				Expect(proto.Equal(newlifecycleCCInfo.ExplicitCollectionConfigPkg, fakeChaincodeDef.Collections)).To(BeTrue())
   346  
   347  				legacyCCInfo, ok := res["another-cc-name"]
   348  				Expect(ok).To(BeTrue())
   349  				Expect(legacyCCInfo).To(Equal(cc2Info))
   350  			})
   351  		})
   352  
   353  		Context("when LegacyProvider.AllChaincodesInfo returns an error", func() {
   354  			BeforeEach(func() {
   355  				fakeLegacyProvider.AllChaincodesInfoReturns(nil, fmt.Errorf("chaincode-info-error"))
   356  			})
   357  
   358  			It("passes through to the legacy impl", func() {
   359  				_, err := vc.AllChaincodesInfo("testchannel", fakeQueryExecutor)
   360  				Expect(err).To(MatchError("chaincode-info-error"))
   361  			})
   362  		})
   363  
   364  		Context("when the data is corrupt", func() {
   365  			BeforeEach(func() {
   366  				fakePublicState["namespaces/fields/cc-name/ValidationInfo"] = []byte("garbage")
   367  			})
   368  
   369  			It("wraps and returns that error", func() {
   370  				_, err := vc.AllChaincodesInfo("channel-name", fakeQueryExecutor)
   371  				Expect(err).To(MatchError("could not get info about chaincode: could not deserialize chaincode definition for chaincode cc-name: could not unmarshal state for key namespaces/fields/cc-name/ValidationInfo: proto: can't skip unknown wire type 7"))
   372  			})
   373  		})
   374  	})
   375  
   376  	Describe("CollectionInfo", func() {
   377  		It("returns the collection info as defined in the new lifecycle", func() {
   378  			res, err := vc.CollectionInfo("channel-name", "cc-name", "collection-name", fakeQueryExecutor)
   379  			Expect(err).NotTo(HaveOccurred())
   380  			Expect(proto.Equal(res, &pb.StaticCollectionConfig{
   381  				Name: "collection-name",
   382  			})).To(BeTrue())
   383  		})
   384  
   385  		Context("when no matching collection is found", func() {
   386  			It("returns nil", func() {
   387  				res, err := vc.CollectionInfo("channel-name", "cc-name", "non-extant-name", fakeQueryExecutor)
   388  				Expect(err).NotTo(HaveOccurred())
   389  				Expect(res).To(BeNil())
   390  			})
   391  		})
   392  
   393  		Context("when the chaincode in question is _lifecycle", func() {
   394  			It("skips the existence checks and checks the implicit collections", func() {
   395  				res, err := vc.CollectionInfo("channel-name", "_lifecycle", "_implicit_org_first-mspid", fakeQueryExecutor)
   396  				Expect(err).NotTo(HaveOccurred())
   397  				Expect(res).NotTo(BeNil())
   398  			})
   399  		})
   400  
   401  		Context("when the ledger returns an error", func() {
   402  			BeforeEach(func() {
   403  				fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("state-error"))
   404  			})
   405  
   406  			It("wraps and returns the error", func() {
   407  				_, err := vc.CollectionInfo("channel-name", "cc-name", "collection-name", fakeQueryExecutor)
   408  				Expect(err).To(MatchError("could not get chaincode: could not deserialize metadata for chaincode cc-name: could not query metadata for namespace namespaces/cc-name: state-error"))
   409  			})
   410  		})
   411  
   412  		Context("when the chaincode is not in the new lifecycle", func() {
   413  			var collInfo *pb.StaticCollectionConfig
   414  
   415  			BeforeEach(func() {
   416  				collInfo = &pb.StaticCollectionConfig{}
   417  				fakeLegacyProvider.CollectionInfoReturns(collInfo, fmt.Errorf("collection-info-error"))
   418  			})
   419  
   420  			It("passes through to the legacy impl", func() {
   421  				res, err := vc.CollectionInfo("channel-name", "legacy-name", "collection-name", fakeQueryExecutor)
   422  				Expect(res).To(Equal(collInfo))
   423  				Expect(err).To(MatchError("collection-info-error"))
   424  				Expect(fakeLegacyProvider.CollectionInfoCallCount()).To(Equal(1))
   425  				channelID, ccName, collName, qe := fakeLegacyProvider.CollectionInfoArgsForCall(0)
   426  				Expect(channelID).To(Equal("channel-name"))
   427  				Expect(ccName).To(Equal("legacy-name"))
   428  				Expect(collName).To(Equal("collection-name"))
   429  				Expect(qe).To(Equal(fakeQueryExecutor))
   430  			})
   431  		})
   432  
   433  		Context("when the data is corrupt", func() {
   434  			BeforeEach(func() {
   435  				fakePublicState["namespaces/fields/cc-name/ValidationInfo"] = []byte("garbage")
   436  			})
   437  
   438  			It("wraps and returns that error", func() {
   439  				_, err := vc.CollectionInfo("channel-name", "cc-name", "collection-name", fakeQueryExecutor)
   440  				Expect(err).To(MatchError("could not get chaincode: could not deserialize chaincode definition for chaincode cc-name: could not unmarshal state for key namespaces/fields/cc-name/ValidationInfo: proto: can't skip unknown wire type 7"))
   441  			})
   442  		})
   443  	})
   444  
   445  	Describe("ImplicitCollections", func() {
   446  		It("returns an implicit collection for every org", func() {
   447  			res, err := vc.ImplicitCollections("channel-id", "cc-name", fakeQueryExecutor)
   448  			Expect(err).NotTo(HaveOccurred())
   449  			Expect(len(res)).To(Equal(2))
   450  			var firstOrg, secondOrg *pb.StaticCollectionConfig
   451  			for _, collection := range res {
   452  				switch collection.Name {
   453  				case "_implicit_org_first-mspid":
   454  					firstOrg = collection
   455  				case "_implicit_org_second-mspid":
   456  					secondOrg = collection
   457  				}
   458  			}
   459  			// Required/MaxPeerCount should match privdataConfig when the implicit collection is for peer's own org
   460  			Expect(firstOrg).NotTo(BeNil())
   461  			Expect(firstOrg.RequiredPeerCount).To(Equal(int32(privdataConfig.ImplicitCollDisseminationPolicy.RequiredPeerCount)))
   462  			Expect(firstOrg.MaximumPeerCount).To(Equal(int32(privdataConfig.ImplicitCollDisseminationPolicy.MaxPeerCount)))
   463  			// Required/MaxPeerCount should be 0 when the implicit collection is for other org
   464  			Expect(secondOrg).NotTo(BeNil())
   465  			Expect(secondOrg.RequiredPeerCount).To(Equal(int32(0)))
   466  			Expect(secondOrg.MaximumPeerCount).To(Equal(int32(0)))
   467  		})
   468  
   469  		Context("when the chaincode does not exist", func() {
   470  			It("returns nil, nil", func() {
   471  				res, err := vc.ImplicitCollections("channel-id", "missing-name", fakeQueryExecutor)
   472  				Expect(err).NotTo(HaveOccurred())
   473  				Expect(res).To(BeNil())
   474  			})
   475  		})
   476  
   477  		Context("when the ledger returns an error", func() {
   478  			BeforeEach(func() {
   479  				fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("state-error"))
   480  			})
   481  
   482  			It("wraps and returns the error", func() {
   483  				_, err := vc.ImplicitCollections("channel-id", "missing-name", fakeQueryExecutor)
   484  				Expect(err).To(MatchError("could not get info about chaincode: could not deserialize metadata for chaincode missing-name: could not query metadata for namespace namespaces/missing-name: state-error"))
   485  			})
   486  		})
   487  
   488  		Context("when there is no channel config", func() {
   489  			BeforeEach(func() {
   490  				fakeChannelConfigSource.GetStableChannelConfigReturns(nil)
   491  			})
   492  
   493  			It("returns an error", func() {
   494  				_, err := vc.ImplicitCollections("channel-id", "cc-name", fakeQueryExecutor)
   495  				Expect(err).To(MatchError("could not get channelconfig for channel channel-id"))
   496  			})
   497  		})
   498  
   499  		Context("when there is no application config", func() {
   500  			BeforeEach(func() {
   501  				fakeChannelConfig.ApplicationConfigReturns(nil, false)
   502  			})
   503  
   504  			It("returns an error", func() {
   505  				_, err := vc.ImplicitCollections("channel-id", "cc-name", fakeQueryExecutor)
   506  				Expect(err).To(MatchError("could not get application config for channel channel-id"))
   507  			})
   508  		})
   509  	})
   510  
   511  	Describe("AllCollectionsConfigPkg", func() {
   512  		It("returns the collection config package that includes both explicit and implicit collections as defined in the new lifecycle", func() {
   513  			ccPkg, err := vc.AllCollectionsConfigPkg("channel-name", "cc-name", fakeQueryExecutor)
   514  			Expect(err).NotTo(HaveOccurred())
   515  			collectionNames := []string{}
   516  			for _, config := range ccPkg.Config {
   517  				collectionNames = append(collectionNames, config.GetStaticCollectionConfig().GetName())
   518  			}
   519  			Expect(collectionNames).Should(ConsistOf("collection-name", "_implicit_org_first-mspid", "_implicit_org_second-mspid"))
   520  		})
   521  
   522  		Context("when no explicit collection config is defined", func() {
   523  			BeforeEach(func() {
   524  				err := resources.Serializer.Serialize(lifecycle.NamespacesName, "cc-without-explicit-collection",
   525  					&lifecycle.ChaincodeDefinition{
   526  						EndorsementInfo: &lb.ChaincodeEndorsementInfo{
   527  							Version: "version",
   528  						},
   529  						ValidationInfo: &lb.ChaincodeValidationInfo{
   530  							ValidationPlugin:    "validation-plugin",
   531  							ValidationParameter: []byte("validation-parameter"),
   532  						},
   533  					}, fakePublicState)
   534  				Expect(err).NotTo(HaveOccurred())
   535  			})
   536  
   537  			It("returns only implicit collections", func() {
   538  				ccPkg, err := vc.AllCollectionsConfigPkg("channel-name", "cc-without-explicit-collection", fakeQueryExecutor)
   539  				Expect(err).NotTo(HaveOccurred())
   540  				collectionNames := []string{}
   541  				for _, config := range ccPkg.Config {
   542  					collectionNames = append(collectionNames, config.GetStaticCollectionConfig().GetName())
   543  				}
   544  				Expect(collectionNames).Should(ConsistOf("_implicit_org_first-mspid", "_implicit_org_second-mspid"))
   545  			})
   546  		})
   547  
   548  		Context("when the ledger returns an error", func() {
   549  			BeforeEach(func() {
   550  				fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("state-error"))
   551  			})
   552  
   553  			It("wraps and returns the error", func() {
   554  				_, err := vc.AllCollectionsConfigPkg("channel-name", "cc-name", fakeQueryExecutor)
   555  				Expect(err).To(MatchError("could not get info about chaincode: could not deserialize metadata for chaincode cc-name: could not query metadata for namespace namespaces/cc-name: state-error"))
   556  			})
   557  		})
   558  
   559  		Context("when there is no channel config", func() {
   560  			BeforeEach(func() {
   561  				fakeChannelConfigSource.GetStableChannelConfigReturns(nil)
   562  			})
   563  
   564  			It("returns an error", func() {
   565  				_, err := vc.AllCollectionsConfigPkg("channel-id", "cc-name", fakeQueryExecutor)
   566  				Expect(err).To(MatchError("could not get channelconfig for channel channel-id"))
   567  			})
   568  		})
   569  
   570  		Context("when there is no application config", func() {
   571  			BeforeEach(func() {
   572  				fakeChannelConfig.ApplicationConfigReturns(nil, false)
   573  			})
   574  
   575  			It("returns an error", func() {
   576  				_, err := vc.AllCollectionsConfigPkg("channel-id", "cc-name", fakeQueryExecutor)
   577  				Expect(err).To(MatchError("could not get application config for channel channel-id"))
   578  			})
   579  		})
   580  
   581  		Context("when the chaincode is not in the new lifecycle", func() {
   582  			var ccPkg *pb.CollectionConfigPackage
   583  
   584  			BeforeEach(func() {
   585  				ccPkg = &pb.CollectionConfigPackage{}
   586  				fakeLegacyProvider.ChaincodeInfoReturns(
   587  					&ledger.DeployedChaincodeInfo{
   588  						ExplicitCollectionConfigPkg: ccPkg,
   589  						IsLegacy:                    true,
   590  					},
   591  					nil,
   592  				)
   593  			})
   594  
   595  			It("passes through to the legacy impl", func() {
   596  				res, err := vc.AllCollectionsConfigPkg("channel-name", "legacy-name", fakeQueryExecutor)
   597  				Expect(fakeLegacyProvider.ChaincodeInfoCallCount()).To(Equal(1))
   598  				channelID, ccName, qe := fakeLegacyProvider.ChaincodeInfoArgsForCall(0)
   599  				Expect(channelID).To(Equal("channel-name"))
   600  				Expect(ccName).To(Equal("legacy-name"))
   601  				Expect(qe).To(Equal(fakeQueryExecutor))
   602  				Expect(res).To(Equal(ccPkg))
   603  				Expect(err).NotTo(HaveOccurred())
   604  			})
   605  		})
   606  
   607  		Context("when the chaincode is not in the new lifecycle and legacy info provider returns error", func() {
   608  			BeforeEach(func() {
   609  				fakeLegacyProvider.ChaincodeInfoReturns(nil, fmt.Errorf("legacy-chaincode-info-error"))
   610  			})
   611  
   612  			It("passes through to the legacy impl", func() {
   613  				_, err := vc.AllCollectionsConfigPkg("channel-name", "legacy-name", fakeQueryExecutor)
   614  				Expect(err).To(MatchError("legacy-chaincode-info-error"))
   615  			})
   616  		})
   617  
   618  		Context("when the data is corrupt", func() {
   619  			BeforeEach(func() {
   620  				fakePublicState["namespaces/fields/cc-name/ValidationInfo"] = []byte("garbage")
   621  			})
   622  
   623  			It("wraps and returns that error", func() {
   624  				_, err := vc.AllCollectionsConfigPkg("channel-name", "cc-name", fakeQueryExecutor)
   625  				Expect(err).To(MatchError("could not get info about chaincode: could not deserialize chaincode definition for chaincode cc-name: could not unmarshal state for key namespaces/fields/cc-name/ValidationInfo: proto: can't skip unknown wire type 7"))
   626  			})
   627  		})
   628  	})
   629  
   630  	Describe("ValidationInfo", func() {
   631  		It("returns the validation info as defined in the new lifecycle", func() {
   632  			vPlugin, vParm, uerr, verr := vc.ValidationInfo("channel-id", "cc-name", fakeQueryExecutor)
   633  			Expect(uerr).NotTo(HaveOccurred())
   634  			Expect(verr).NotTo(HaveOccurred())
   635  			Expect(vPlugin).To(Equal("validation-plugin"))
   636  			Expect(vParm).To(Equal([]byte("validation-parameter")))
   637  		})
   638  
   639  		Context("when the chaincode in question is _lifecycle", func() {
   640  			It("returns the builtin plugin and the endorsement policy", func() {
   641  				vPlugin, vParm, uerr, verr := vc.ValidationInfo("channel-id", "_lifecycle", fakeQueryExecutor)
   642  				Expect(uerr).NotTo(HaveOccurred())
   643  				Expect(verr).NotTo(HaveOccurred())
   644  				Expect(vPlugin).To(Equal("vscc"))
   645  				Expect(vParm).NotTo(BeNil())
   646  			})
   647  
   648  			Context("when fetching the lifecycle endorsement policy returns an error", func() {
   649  				BeforeEach(func() {
   650  					fakeChannelConfigSource.GetStableChannelConfigReturns(nil)
   651  				})
   652  
   653  				It("treats the error as non-deterministic", func() {
   654  					_, _, uerr, _ := vc.ValidationInfo("channel-id", "_lifecycle", fakeQueryExecutor)
   655  					Expect(uerr).To(MatchError("unexpected failure to create lifecycle endorsement policy: could not get channel config for channel 'channel-id'"))
   656  				})
   657  			})
   658  		})
   659  
   660  		Context("when the ledger returns an error", func() {
   661  			BeforeEach(func() {
   662  				fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("state-error"))
   663  			})
   664  
   665  			It("wraps and returns the error", func() {
   666  				_, _, uerr, _ := vc.ValidationInfo("channel-id", "cc-name", fakeQueryExecutor)
   667  				Expect(uerr).To(MatchError("could not get chaincode: could not deserialize metadata for chaincode cc-name: could not query metadata for namespace namespaces/cc-name: state-error"))
   668  			})
   669  		})
   670  
   671  		Context("when the chaincode is not in the new lifecycle", func() {
   672  			It("passes through to the legacy impl", func() {
   673  				vPlugin, vParm, uerr, verr := vc.ValidationInfo("channel-id", "missing-name", fakeQueryExecutor)
   674  				Expect(vPlugin).To(BeEmpty())
   675  				Expect(vParm).To(BeNil())
   676  				Expect(uerr).NotTo(HaveOccurred())
   677  				Expect(verr).NotTo(HaveOccurred())
   678  			})
   679  		})
   680  
   681  		Context("when the data is corrupt", func() {
   682  			BeforeEach(func() {
   683  				fakePublicState["namespaces/fields/cc-name/ValidationInfo"] = []byte("garbage")
   684  			})
   685  
   686  			It("wraps and returns that error", func() {
   687  				_, _, uerr, _ := vc.ValidationInfo("channel-id", "cc-name", fakeQueryExecutor)
   688  				Expect(uerr).To(MatchError("could not get chaincode: could not deserialize chaincode definition for chaincode cc-name: could not unmarshal state for key namespaces/fields/cc-name/ValidationInfo: proto: can't skip unknown wire type 7"))
   689  			})
   690  		})
   691  	})
   692  
   693  	Describe("CollectionValidationInfo", func() {
   694  		var fakeValidationState *mock.ValidationState
   695  
   696  		BeforeEach(func() {
   697  			fakeValidationState = &mock.ValidationState{}
   698  			fakeValidationState.GetStateMultipleKeysStub = func(namespace string, keys []string) ([][]byte, error) {
   699  				return [][]byte{fakePublicState[keys[0]]}, nil
   700  			}
   701  		})
   702  
   703  		It("returns the endorsement policy for the collection", func() {
   704  			ep, uErr, vErr := vc.CollectionValidationInfo("channel-id", "cc-name", "collection-name", fakeValidationState)
   705  			Expect(uErr).NotTo(HaveOccurred())
   706  			Expect(vErr).NotTo(HaveOccurred())
   707  			Expect(ep).To(Equal([]byte("validation-parameter")))
   708  		})
   709  
   710  		Context("when the chaincode definition cannot be retrieved", func() {
   711  			BeforeEach(func() {
   712  				fakeValidationState.GetStateMultipleKeysReturns(nil, fmt.Errorf("state-error"))
   713  			})
   714  
   715  			It("returns an unexpected error", func() {
   716  				_, uErr, vErr := vc.CollectionValidationInfo("channel-id", "cc-name", "collection-name", fakeValidationState)
   717  				Expect(vErr).NotTo(HaveOccurred())
   718  				Expect(uErr).To(MatchError("could not get chaincode: could not deserialize metadata for chaincode cc-name: could not query metadata for namespace namespaces/cc-name: could not get state thought validatorstate shim: state-error"))
   719  			})
   720  		})
   721  
   722  		Context("when the chaincode does not exist in the new lifecycle", func() {
   723  			It("returns nil nil nil", func() {
   724  				ep, uErr, vErr := vc.CollectionValidationInfo("channel-id", "missing-name", "collection-name", fakeValidationState)
   725  				Expect(uErr).NotTo(HaveOccurred())
   726  				Expect(vErr).NotTo(HaveOccurred())
   727  				Expect(ep).To(BeNil())
   728  			})
   729  		})
   730  
   731  		Context("when the collection does not exist", func() {
   732  			It("returns a validation error", func() {
   733  				_, uErr, vErr := vc.CollectionValidationInfo("channel-id", "cc-name", "missing-collection-name", fakeValidationState)
   734  				Expect(uErr).NotTo(HaveOccurred())
   735  				Expect(vErr).To(MatchError("no such collection 'missing-collection-name'"))
   736  			})
   737  		})
   738  
   739  		Context("when the collection is an implicit collection", func() {
   740  			It("returns the implicit endorsement policy", func() {
   741  				ep, uErr, vErr := vc.CollectionValidationInfo("channel-id", "cc-name", "_implicit_org_first-mspid", fakeValidationState)
   742  				Expect(uErr).NotTo(HaveOccurred())
   743  				Expect(vErr).NotTo(HaveOccurred())
   744  				Expect(ep).NotTo(Equal([]byte("validation-parameter")))
   745  			})
   746  
   747  			Context("when the implicit endorsement policy returns an error", func() {
   748  				It("returns the error", func() {
   749  					_, _, vErr := vc.CollectionValidationInfo("channel-id", "cc-name", "_implicit_org_bad-mspid", fakeValidationState)
   750  					Expect(vErr).To(MatchError("no org found in channel with MSPID 'bad-mspid'"))
   751  				})
   752  			})
   753  		})
   754  
   755  		Context("when the endorsement policy is specified in the collection config", func() {
   756  			var expectedPolicy *pb.ApplicationPolicy
   757  
   758  			BeforeEach(func() {
   759  				expectedPolicy = &pb.ApplicationPolicy{
   760  					Type: &pb.ApplicationPolicy_SignaturePolicy{
   761  						SignaturePolicy: &cb.SignaturePolicyEnvelope{
   762  							Identities: []*msp.MSPPrincipal{
   763  								{
   764  									Principal: []byte("test"),
   765  								},
   766  							},
   767  						},
   768  					},
   769  				}
   770  				err := resources.Serializer.Serialize(lifecycle.NamespacesName, "cc-name", &lifecycle.ChaincodeDefinition{
   771  					EndorsementInfo: &lb.ChaincodeEndorsementInfo{
   772  						Version: "version",
   773  					},
   774  					ValidationInfo: &lb.ChaincodeValidationInfo{
   775  						ValidationPlugin:    "validation-plugin",
   776  						ValidationParameter: []byte("validation-parameter"),
   777  					},
   778  					Collections: &pb.CollectionConfigPackage{
   779  						Config: []*pb.CollectionConfig{
   780  							{
   781  								Payload: &pb.CollectionConfig_StaticCollectionConfig{
   782  									StaticCollectionConfig: &pb.StaticCollectionConfig{
   783  										Name:              "collection-name",
   784  										EndorsementPolicy: expectedPolicy,
   785  									},
   786  								},
   787  							},
   788  						},
   789  					},
   790  				}, fakePublicState)
   791  				Expect(err).NotTo(HaveOccurred())
   792  			})
   793  
   794  			It("returns the endorsement policy from the collection config", func() {
   795  				ep, uErr, vErr := vc.CollectionValidationInfo("channel-id", "cc-name", "collection-name", fakeValidationState)
   796  				Expect(uErr).NotTo(HaveOccurred())
   797  				Expect(vErr).NotTo(HaveOccurred())
   798  				Expect(ep).To(Equal(protoutil.MarshalOrPanic(expectedPolicy)))
   799  			})
   800  		})
   801  	})
   802  
   803  	Describe("ImplicitCollectionEndorsementPolicyAsBytes", func() {
   804  		It("returns the marshaled standard EP for an implicit collection", func() {
   805  			ep, uErr, vErr := vc.ImplicitCollectionEndorsementPolicyAsBytes("channel-id", "first-mspid")
   806  			Expect(uErr).NotTo(HaveOccurred())
   807  			Expect(vErr).NotTo(HaveOccurred())
   808  			policy := &cb.ApplicationPolicy{}
   809  			err := proto.Unmarshal(ep, policy)
   810  			Expect(err).NotTo(HaveOccurred())
   811  			Expect(policy.GetChannelConfigPolicyReference()).To(Equal("/Channel/Application/org0/Endorsement"))
   812  		})
   813  
   814  		Context("when the standard channel policy for endorsement does not exist", func() {
   815  			BeforeEach(func() {
   816  				fakePolicyManager.GetPolicyReturns(nil, false)
   817  			})
   818  
   819  			It("returns a signature policy", func() {
   820  				ep, uErr, vErr := vc.ImplicitCollectionEndorsementPolicyAsBytes("channel-id", "first-mspid")
   821  				Expect(uErr).NotTo(HaveOccurred())
   822  				Expect(vErr).NotTo(HaveOccurred())
   823  				policy := &cb.ApplicationPolicy{}
   824  				err := proto.Unmarshal(ep, policy)
   825  				Expect(err).NotTo(HaveOccurred())
   826  				Expect(policy.GetSignaturePolicy()).NotTo(BeNil())
   827  			})
   828  		})
   829  
   830  		Context("when the channel config cannot be retrieved", func() {
   831  			BeforeEach(func() {
   832  				fakeChannelConfigSource.GetStableChannelConfigReturns(nil)
   833  			})
   834  
   835  			It("returns an unexpected error", func() {
   836  				_, uErr, vErr := vc.ImplicitCollectionEndorsementPolicyAsBytes("channel-id", "first-mspid")
   837  				Expect(vErr).NotTo(HaveOccurred())
   838  				Expect(uErr).To(MatchError("could not get channel config for channel 'channel-id'"))
   839  			})
   840  		})
   841  
   842  		Context("when the application config cannot be retrieved", func() {
   843  			BeforeEach(func() {
   844  				fakeChannelConfig.ApplicationConfigReturns(nil, false)
   845  			})
   846  
   847  			It("returns an unexpected error", func() {
   848  				_, uErr, vErr := vc.ImplicitCollectionEndorsementPolicyAsBytes("channel-id", "first-mspid")
   849  				Expect(vErr).NotTo(HaveOccurred())
   850  				Expect(uErr).To(MatchError("could not get application config for channel 'channel-id'"))
   851  			})
   852  		})
   853  
   854  		Context("when the MSPID is not for any application org", func() {
   855  			It("returns a validation error", func() {
   856  				_, uErr, vErr := vc.ImplicitCollectionEndorsementPolicyAsBytes("channel-id", "bad-mspid")
   857  				Expect(uErr).NotTo(HaveOccurred())
   858  				Expect(vErr).To(MatchError("no org found in channel with MSPID 'bad-mspid'"))
   859  			})
   860  		})
   861  	})
   862  })