From 1a8be5a3ccc8c8ded426027b1f47efcd482310d1 Mon Sep 17 00:00:00 2001 From: Alessandro Sorniotti Date: Tue, 10 Oct 2017 13:08:49 +0200 Subject: [PATCH] [FAB-5871] VSCC to ensure no collection exists This change set completes the work for FAB-5871 by adding the check in VSCC to ensure that no prior collection exists for the deployed chaincode. Change-Id: I5046244ff9a8aa7a249fa79b0c8aafd263eabdb0 Signed-off-by: Alessandro Sorniotti --- core/common/privdata/collection.go | 4 ++ core/common/privdata/store.go | 18 ++++++++- core/common/privdata/store_test.go | 4 ++ core/scc/vscc/validator_onevalidsignature.go | 38 +++++++++++++++++-- .../vscc/validator_onevalidsignature_test.go | 33 ++++++++++++---- gossip/privdata/coordinator_test.go | 4 ++ gossip/privdata/pull_test.go | 4 ++ 7 files changed, 92 insertions(+), 13 deletions(-) diff --git a/core/common/privdata/collection.go b/core/common/privdata/collection.go index ffd5b332c3a..06beddc61f6 100644 --- a/core/common/privdata/collection.go +++ b/core/common/privdata/collection.go @@ -68,6 +68,10 @@ type CollectionStore interface { // GetCollectionAccessPolicy retrieves a collection's access policy RetrieveCollectionAccessPolicy(common.CollectionCriteria) (CollectionAccessPolicy, error) + + // RetrieveCollectionConfigPackage retrieves the configuration + // for the collection with the supplied criteria + RetrieveCollectionConfigPackage(common.CollectionCriteria) (*common.CollectionConfigPackage, error) } const ( diff --git a/core/common/privdata/store.go b/core/common/privdata/store.go index f94eec1fd10..2599511d8cc 100644 --- a/core/common/privdata/store.go +++ b/core/common/privdata/store.go @@ -48,7 +48,7 @@ func NewSimpleCollectionStore(s Support) CollectionStore { return &simpleCollectionStore{s} } -func (c *simpleCollectionStore) retrieveSimpleCollection(cc common.CollectionCriteria) (*SimpleCollection, error) { +func (c *simpleCollectionStore) retrieveCollectionConfigPackage(cc common.CollectionCriteria) (*common.CollectionConfigPackage, error) { qe, err := c.s.GetQueryExecutorForLedger(cc.Channel) if err != nil { return nil, errors.WithMessage(err, fmt.Sprintf("could not retrieve query executor for collection criteria %#v", cc)) @@ -69,6 +69,18 @@ func (c *simpleCollectionStore) retrieveSimpleCollection(cc common.CollectionCri return nil, errors.Wrapf(err, "invalid configuration for collection criteria %#v", cc) } + return collections, nil +} + +func (c *simpleCollectionStore) retrieveSimpleCollection(cc common.CollectionCriteria) (*SimpleCollection, error) { + collections, err := c.retrieveCollectionConfigPackage(cc) + if err != nil { + return nil, err + } + if collections == nil { + return nil, nil + } + for _, cconf := range collections.Config { switch cconf := cconf.Payload.(type) { case *common.CollectionConfig_StaticCollectionConfig: @@ -97,3 +109,7 @@ func (c *simpleCollectionStore) RetrieveCollection(cc common.CollectionCriteria) func (c *simpleCollectionStore) RetrieveCollectionAccessPolicy(cc common.CollectionCriteria) (CollectionAccessPolicy, error) { return c.retrieveSimpleCollection(cc) } + +func (c *simpleCollectionStore) RetrieveCollectionConfigPackage(cc common.CollectionCriteria) (*common.CollectionConfigPackage, error) { + return c.retrieveCollectionConfigPackage(cc) +} diff --git a/core/common/privdata/store_test.go b/core/common/privdata/store_test.go index b28dc871008..c19be8b6b42 100644 --- a/core/common/privdata/store_test.go +++ b/core/common/privdata/store_test.go @@ -93,4 +93,8 @@ func TestCollectionStore(t *testing.T) { c, err = cs.RetrieveCollection(common.CollectionCriteria{Channel: "ch", Namespace: "cc", Collection: "asd"}) assert.Error(t, err) assert.Nil(t, c) + + ccc, err := cs.RetrieveCollectionConfigPackage(ccr) + assert.NoError(t, err) + assert.NotNil(t, ccc) } diff --git a/core/scc/vscc/validator_onevalidsignature.go b/core/scc/vscc/validator_onevalidsignature.go index ec640bfec43..6e008a8fbb3 100644 --- a/core/scc/vscc/validator_onevalidsignature.go +++ b/core/scc/vscc/validator_onevalidsignature.go @@ -30,6 +30,7 @@ import ( "github.com/hyperledger/fabric/core/common/sysccprovider" "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil" "github.com/hyperledger/fabric/core/scc/lscc" + m "github.com/hyperledger/fabric/msp" mspmgmt "github.com/hyperledger/fabric/msp/mgmt" "github.com/hyperledger/fabric/protos/common" "github.com/hyperledger/fabric/protos/ledger/rwset/kvrwset" @@ -54,11 +55,29 @@ type ValidatorOneValidSignature struct { // methods of the system chaincode package without // import cycles sccprovider sysccprovider.SystemChaincodeProvider + + // collectionStore provides support to retrieve + // collections from the ledger + collectionStore privdata.CollectionStore +} + +// collectionStoreSupport implements privdata.Support +type collectionStoreSupport struct { + sysccprovider.SystemChaincodeProvider +} + +func (c *collectionStoreSupport) GetCollectionKVSKey(cc common.CollectionCriteria) string { + return privdata.BuildCollectionKVSKey(cc.Namespace) +} + +func (c *collectionStoreSupport) GetIdentityDeserializer(chainID string) m.IdentityDeserializer { + return mspmgmt.GetIdentityDeserializer(chainID) } // Init is called once when the chaincode started the first time func (vscc *ValidatorOneValidSignature) Init(stub shim.ChaincodeStubInterface) pb.Response { vscc.sccprovider = sysccprovider.GetSystemChaincodeProvider() + vscc.collectionStore = privdata.NewSimpleCollectionStore(&collectionStoreSupport{vscc.sccprovider}) return shim.Success(nil) } @@ -228,6 +247,7 @@ func (vscc *ValidatorOneValidSignature) validateDeployRWSetAndCollection( lsccrwset *kvrwset.KVRWSet, cdRWSet *ccprovider.ChaincodeData, lsccArgs [][]byte, + chid, ccid string, ) error { /********************************************/ /* security check 0.a - validation of rwset */ @@ -261,9 +281,19 @@ func (vscc *ValidatorOneValidSignature) validateDeployRWSetAndCollection( cdRWSet.Name, cdRWSet.Version) } - // TODO: make sure there isn't any existing collection on the ledger for this chaincode - // I'll take care of this as soon as we have implemented a collection store interface - // to retrieve collection configuration data from the ledger (https://jira.hyperledger.org/browse/FAB-5872) + ccp, err := vscc.collectionStore.RetrieveCollectionConfigPackage(common.CollectionCriteria{Channel: chid, Namespace: ccid}) + if err != nil { + // fail if we get any error other than NoSuchCollectionError + // because it means something went wrong while looking up the + // older collection + if _, ok := err.(privdata.NoSuchCollectionError); !ok { + return errors.WithMessage(err, fmt.Sprintf("unable to check whether collection existed earlier for chaincode %s:%s", + cdRWSet.Name, cdRWSet.Version)) + } + } + if ccp != nil { + return errors.Errorf("collection data should not exist for chaincode %s:%s", cdRWSet.Name, cdRWSet.Version) + } if collectionsConfigArgs != nil { collections := &common.CollectionConfigPackage{} @@ -413,7 +443,7 @@ func (vscc *ValidatorOneValidSignature) ValidateLSCCInvocation( /****************************************************************************/ if ac.PrivateChannelData() { // do extra validation for collections - err = vscc.validateDeployRWSetAndCollection(lsccrwset, cdRWSet, lsccArgs) + err = vscc.validateDeployRWSetAndCollection(lsccrwset, cdRWSet, lsccArgs, chid, cdsArgs.ChaincodeSpec.ChaincodeId.Name) if err != nil { return err } diff --git a/core/scc/vscc/validator_onevalidsignature_test.go b/core/scc/vscc/validator_onevalidsignature_test.go index b312f349b39..863d6c70a4c 100644 --- a/core/scc/vscc/validator_onevalidsignature_test.go +++ b/core/scc/vscc/validator_onevalidsignature_test.go @@ -1484,11 +1484,18 @@ func (c *mockPolicyChecker) CheckPolicyNoChannel(policyName string, signedProp * } func TestValidateDeployRWSetAndCollection(t *testing.T) { + chid := "ch" + ccid := "cc" + cd := &ccprovider.ChaincodeData{Name: "mycc"} v := new(ValidatorOneValidSignature) stub := shim.NewMockStub("validatoronevalidsignature", v) + State := make(map[string]map[string][]byte) + State["lscc"] = make(map[string][]byte) + sysccprovider.RegisterSystemChaincodeProviderFactory(&scc.MocksccProviderFactory{Qe: lm.NewMockQueryExecutor(State)}) + r1 := stub.MockInit("1", [][]byte{}) if r1.Status != shim.OK { fmt.Println("Init failed", string(r1.Message)) @@ -1497,38 +1504,38 @@ func TestValidateDeployRWSetAndCollection(t *testing.T) { rwset := &kvrwset.KVRWSet{Writes: []*kvrwset.KVWrite{{Key: "a"}, {Key: "b"}, {Key: "c"}}} - err := v.validateDeployRWSetAndCollection(rwset, nil, nil) + err := v.validateDeployRWSetAndCollection(rwset, nil, nil, chid, ccid) assert.Error(t, err) rwset = &kvrwset.KVRWSet{Writes: []*kvrwset.KVWrite{{Key: "a"}, {Key: "b"}}} - err = v.validateDeployRWSetAndCollection(rwset, cd, nil) + err = v.validateDeployRWSetAndCollection(rwset, cd, nil, chid, ccid) assert.Error(t, err) rwset = &kvrwset.KVRWSet{Writes: []*kvrwset.KVWrite{{Key: "a"}}} - err = v.validateDeployRWSetAndCollection(rwset, cd, nil) + err = v.validateDeployRWSetAndCollection(rwset, cd, nil, chid, ccid) assert.NoError(t, err) lsccargs := [][]byte{nil, nil, nil, nil, nil, nil} - err = v.validateDeployRWSetAndCollection(rwset, cd, lsccargs) + err = v.validateDeployRWSetAndCollection(rwset, cd, lsccargs, chid, ccid) assert.NoError(t, err) rwset = &kvrwset.KVRWSet{Writes: []*kvrwset.KVWrite{{Key: "a"}, {Key: privdata.BuildCollectionKVSKey("mycc")}}} - err = v.validateDeployRWSetAndCollection(rwset, cd, lsccargs) + err = v.validateDeployRWSetAndCollection(rwset, cd, lsccargs, chid, ccid) assert.NoError(t, err) lsccargs = [][]byte{nil, nil, nil, nil, nil, []byte("barf")} - err = v.validateDeployRWSetAndCollection(rwset, cd, lsccargs) + err = v.validateDeployRWSetAndCollection(rwset, cd, lsccargs, chid, ccid) assert.Error(t, err) lsccargs = [][]byte{nil, nil, nil, nil, nil, []byte("barf")} rwset = &kvrwset.KVRWSet{Writes: []*kvrwset.KVWrite{{Key: "a"}, {Key: privdata.BuildCollectionKVSKey("mycc"), Value: []byte("barf")}}} - err = v.validateDeployRWSetAndCollection(rwset, cd, lsccargs) + err = v.validateDeployRWSetAndCollection(rwset, cd, lsccargs, chid, ccid) assert.Error(t, err) cc := &common.CollectionConfig{Payload: &common.CollectionConfig_StaticCollectionConfig{&common.StaticCollectionConfig{Name: "mycollection"}}} @@ -1540,8 +1547,18 @@ func TestValidateDeployRWSetAndCollection(t *testing.T) { lsccargs = [][]byte{nil, nil, nil, nil, nil, ccpBytes} rwset = &kvrwset.KVRWSet{Writes: []*kvrwset.KVWrite{{Key: "a"}, {Key: privdata.BuildCollectionKVSKey("mycc"), Value: ccpBytes}}} - err = v.validateDeployRWSetAndCollection(rwset, cd, lsccargs) + err = v.validateDeployRWSetAndCollection(rwset, cd, lsccargs, chid, ccid) assert.NoError(t, err) + + State["lscc"][(&collectionStoreSupport{v.sccprovider}).GetCollectionKVSKey(common.CollectionCriteria{Channel: chid, Namespace: ccid})] = []byte("barf") + + err = v.validateDeployRWSetAndCollection(rwset, cd, lsccargs, chid, ccid) + assert.Error(t, err) + + State["lscc"][(&collectionStoreSupport{v.sccprovider}).GetCollectionKVSKey(common.CollectionCriteria{Channel: chid, Namespace: ccid})] = ccpBytes + + err = v.validateDeployRWSetAndCollection(rwset, cd, lsccargs, chid, ccid) + assert.Error(t, err) } var lccctestpath = "/tmp/lscc-validation-test" diff --git a/gossip/privdata/coordinator_test.go b/gossip/privdata/coordinator_test.go index e99f59d7d02..43deccd58a6 100644 --- a/gossip/privdata/coordinator_test.go +++ b/gossip/privdata/coordinator_test.go @@ -325,6 +325,10 @@ func (cs *collectionStore) RetrieveCollection(common.CollectionCriteria) (privda panic("implement me") } +func (cs *collectionStore) RetrieveCollectionConfigPackage(common.CollectionCriteria) (*common.CollectionConfigPackage, error) { + panic("implement me") +} + type collectionAccessPolicy struct { cs *collectionStore n uint64 diff --git a/gossip/privdata/pull_test.go b/gossip/privdata/pull_test.go index e74ffef520f..0adcbecfc83 100644 --- a/gossip/privdata/pull_test.go +++ b/gossip/privdata/pull_test.go @@ -59,6 +59,10 @@ func (cs mockCollectionStore) RetrieveCollection(fcommon.CollectionCriteria) (pr panic("implement me") } +func (cs mockCollectionStore) RetrieveCollectionConfigPackage(fcommon.CollectionCriteria) (*fcommon.CollectionConfigPackage, error) { + panic("implement me") +} + type mockCollectionAccess struct { cs *mockCollectionStore }