github.com/Hnampk/my-fabric@v0.0.0-20201028083322-75069da399c0/core/chaincode/lifecycle/deployedcc_infoprovider_test.go (about)

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