Skip to content

Commit

Permalink
Extend _lifecycle functions to query all approved chaincode definitions
Browse files Browse the repository at this point in the history
This patch updates some _lifecycle functions on queryapproved to
query all approved chaincode definitions.

Signed-off-by: Tatsuya Sato <tatsuya.sato.so@hitachi.com>
  • Loading branch information
satota2 authored and denyeart committed Jan 9, 2024
1 parent 3271ad9 commit 4014666
Show file tree
Hide file tree
Showing 8 changed files with 552 additions and 9 deletions.
1 change: 1 addition & 0 deletions core/aclmgmt/defaultaclprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func newDefaultACLProvider(policyChecker policy.PolicyChecker) defaultACLProvide
d.pResourcePolicyMap[resources.Lifecycle_QueryInstalledChaincodes] = policy.Admins
d.pResourcePolicyMap[resources.Lifecycle_ApproveChaincodeDefinitionForMyOrg] = policy.Admins
d.pResourcePolicyMap[resources.Lifecycle_QueryApprovedChaincodeDefinition] = policy.Admins
d.pResourcePolicyMap[resources.Lifecycle_QueryApprovedChaincodeDefinitions] = policy.Admins

d.cResourcePolicyMap[resources.Lifecycle_CommitChaincodeDefinition] = CHANNELWRITERS
d.cResourcePolicyMap[resources.Lifecycle_QueryChaincodeDefinition] = CHANNELWRITERS
Expand Down
3 changes: 2 additions & 1 deletion core/aclmgmt/resources/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

// Package roesources contains resource names used in fabric for ACL checks.
// Package resources contains resource names used in fabric for ACL checks.
// Note that some of the checks such as Lscc_INSTALL are "peer wide" (current
// access checks in peer are based on local MSP). These are not currently
// covered by resource or default ACLProviders
Expand All @@ -18,6 +18,7 @@ const (
Lifecycle_QueryInstalledChaincodes = "_lifecycle/QueryInstalledChaincodes"
Lifecycle_ApproveChaincodeDefinitionForMyOrg = "_lifecycle/ApproveChaincodeDefinitionForMyOrg"
Lifecycle_QueryApprovedChaincodeDefinition = "_lifecycle/QueryApprovedChaincodeDefinition"
Lifecycle_QueryApprovedChaincodeDefinitions = "_lifecycle/QueryApprovedChaincodeDefinitions"
Lifecycle_CommitChaincodeDefinition = "_lifecycle/CommitChaincodeDefinition"
Lifecycle_QueryChaincodeDefinition = "_lifecycle/QueryChaincodeDefinition"
Lifecycle_QueryChaincodeDefinitions = "_lifecycle/QueryChaincodeDefinitions"
Expand Down
72 changes: 66 additions & 6 deletions core/chaincode/lifecycle/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ package lifecycle
import (
"bytes"
"fmt"
"strconv"
"strings"
"sync"

cb "github.com/hyperledger/fabric-protos-go/common"
Expand Down Expand Up @@ -85,7 +87,7 @@ var DefaultEndorsementPolicyBytes = protoutil.MarshalOrPanic(&pb.ApplicationPoli
// namespaces/fields/mycc/ValidationInfo: {ValidationPlugin: "builtin", ValidationParameter: <application-policy>}
// namespaces/fields/mycc/Collections {<collection info>}
//
// Private/Org Scope Implcit Collection layout looks like the following
// Private/Org Scope Implicit Collection layout looks like the following
// namespaces/metadata/<namespace>#<sequence_number> -> namespace metadata, including type
// namespaces/fields/<namespace>#<sequence_number>/<field> -> field of namespace type
//
Expand Down Expand Up @@ -575,26 +577,63 @@ func (ef *ExternalFunctions) QueryApprovedChaincodeDefinition(chname, ccname str
}

if !ok {
return nil, errors.Errorf("could not fetch approved chaincode definition (name: '%s', sequence: '%d') on channel '%s'", ccname, sequence, chname)
return nil, errors.Errorf("could not fetch approved chaincode definition (name: '%s', sequence: '%d') on channel '%s'", ccname, requestedSequence, chname)
}

return ef.fetchApprovedChaincodeDefinition(ccname, requestedSequence, metadata, orgState)
}

// QueryApprovedChaincodeDefinitions returns all approved chaincode definitions in Org state for the specified channel.
// The return value is a map where the key is a combination of chaincode namespace and sequence number in the format "<namespace>#<sequence_number>",
// and the value is the corresponding ApprovedChaincodeDefinition object.
func (ef *ExternalFunctions) QueryApprovedChaincodeDefinitions(chname string, orgState ReadRangeableState) (map[string]*ApprovedChaincodeDefinition, error) {
// Get all metadatas for the channel
metadatas, err := ef.Resources.Serializer.DeserializeAllMetadata(NamespacesName, orgState)
if err != nil {
return nil, errors.WithMessage(err, "could not fetch metadatas")
}

// Get all approved chaincode definitions using the metadatas
definitions := map[string]*ApprovedChaincodeDefinition{}
for privateName, metadata := range metadatas {
// Extract chaincode name and sequence number from metadata
ccname, sequence, err := extractChaincodeNameAndSequence(privateName)
if err != nil {
return nil, errors.WithMessagef(err, "could not extract chaincode name and sequence number from metadata: %s", privateName)
}

// Fetch the approved chaincode definition
definition, err := ef.fetchApprovedChaincodeDefinition(ccname, sequence, metadata, orgState)
if err != nil {
return nil, errors.WithMessagef(err, "could not fetch approved chaincode definition (name: '%s', sequence: '%d') on channel '%s'", ccname, sequence, chname)
}

definitions[privateName] = definition
}

return definitions, nil
}

func (ef *ExternalFunctions) fetchApprovedChaincodeDefinition(ccname string, sequence int64, metadata *lb.StateMetadata, orgState ReadableState) (*ApprovedChaincodeDefinition, error) {
privateName := fmt.Sprintf("%s#%d", ccname, sequence)
// Verify that metadata type is chaincode parameters type
if metadata.Datatype != ChaincodeParametersType {
return nil, errors.Errorf("not a chaincode parameters type: %s", metadata.Datatype)
}

// Get chaincode parameters for the request sequence
// Get chaincode parameters for the requested sequence
ccParameters := &ChaincodeParameters{}
if err := ef.Resources.Serializer.Deserialize(NamespacesName, privateName, metadata, ccParameters, orgState); err != nil {
return nil, errors.WithMessagef(err, "could not deserialize chaincode parameters for %s", privateName)
}

// Get package ID for the requested sequence
metadata, ok, err = ef.Resources.Serializer.DeserializeMetadata(ChaincodeSourcesName, privateName, orgState)
metadata, ok, err := ef.Resources.Serializer.DeserializeMetadata(ChaincodeSourcesName, privateName, orgState)
if err != nil {
return nil, errors.WithMessagef(err, "could not deserialize chaincode-source metadata for %s", privateName)
}
if !ok {
return nil, errors.Errorf("could not fetch approved chaincode definition (name: '%s', sequence: '%d') on channel '%s'", ccname, sequence, chname)
return nil, errors.Errorf("could not fetch chaincode-source metadata for %s", privateName)
}
if metadata.Datatype != ChaincodeLocalPackageType {
return nil, errors.Errorf("not a chaincode local package type: %s", metadata.Datatype)
Expand Down Expand Up @@ -625,14 +664,35 @@ func (ef *ExternalFunctions) QueryApprovedChaincodeDefinition(chname, ccname str
}

return &ApprovedChaincodeDefinition{
Sequence: requestedSequence,
Sequence: sequence,
EndorsementInfo: ccParameters.EndorsementInfo,
ValidationInfo: ccParameters.ValidationInfo,
Collections: ccParameters.Collections,
Source: ccsrc,
}, nil
}

func extractChaincodeNameAndSequence(privateName string) (string, int64, error) {
var ccname string
var sequence int64

split := strings.Split(privateName, "#")
if len(split) != 2 {
return "", 0, errors.Errorf("invalid private name format: %s", privateName)
}

ccname = split[0]
sequenceStr := split[1]

var err error
sequence, err = strconv.ParseInt(sequenceStr, 10, 64)
if err != nil {
return "", 0, errors.WithMessagef(err, "invalid sequence number in private name: %s", privateName)
}

return ccname, sequence, nil
}

// ErrNamespaceNotDefined is the error returned when a namespace
// is not defined. This indicates that the chaincode definition
// has not been committed.
Expand Down
130 changes: 129 additions & 1 deletion core/chaincode/lifecycle/lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1381,7 +1381,7 @@ var _ = Describe("ExternalFunctions", func() {

It("wraps and returns the error", func() {
cc, err := ef.QueryApprovedChaincodeDefinition("my-channel", "cc-name", 5, fakePublicState, fakeOrgStates[0])
Expect(err).To(MatchError("could not fetch approved chaincode definition (name: 'cc-name', sequence: '5') on channel 'my-channel'"))
Expect(err).To(MatchError("could not fetch chaincode-source metadata for cc-name#5"))
Expect(cc).To(BeNil())
})
})
Expand Down Expand Up @@ -1426,6 +1426,134 @@ var _ = Describe("ExternalFunctions", func() {
})
})

Describe("QueryApprovedChaincodeDefinitions", func() {
var (
fakePublicState *mock.ReadWritableState
fakeOrgStates []*mock.ReadWritableState

testDefinition *lifecycle.ChaincodeDefinition
testChaincodeLocalPackage *lifecycle.ChaincodeLocalPackage

publicKVS, org0KVS, org1KVS MapLedgerShim
)

BeforeEach(func() {
testDefinition = &lifecycle.ChaincodeDefinition{
Sequence: 4,
EndorsementInfo: &lb.ChaincodeEndorsementInfo{
Version: "version",
EndorsementPlugin: "endorsement-plugin",
},
ValidationInfo: &lb.ChaincodeValidationInfo{
ValidationPlugin: "validation-plugin",
ValidationParameter: []byte("validation-parameter"),
},
}

testChaincodeLocalPackage = &lifecycle.ChaincodeLocalPackage{
PackageID: "hash",
}

publicKVS = map[string][]byte{}
fakePublicState = &mock.ReadWritableState{}
fakePublicState.GetStateStub = publicKVS.GetState
fakePublicState.PutStateStub = publicKVS.PutState

resources.Serializer.Serialize("namespaces", "cc-name", testDefinition, publicKVS)

org0KVS = map[string][]byte{}
org1KVS = map[string][]byte{}
fakeOrg0State := &mock.ReadWritableState{}
fakeOrg0State.CollectionNameReturns("_implicit_org_org0")
fakeOrg1State := &mock.ReadWritableState{}
fakeOrg1State.CollectionNameReturns("_implicit_org_org1")
fakeOrgStates = []*mock.ReadWritableState{
fakeOrg0State,
fakeOrg1State,
}
for i, kvs := range []MapLedgerShim{org0KVS, org1KVS} {
kvs := kvs
fakeOrgStates[i].GetStateStub = kvs.GetState
fakeOrgStates[i].GetStateHashStub = kvs.GetStateHash
fakeOrgStates[i].PutStateStub = kvs.PutState
fakeOrgStates[i].GetStateRangeStub = kvs.GetStateRange
}

resources.Serializer.Serialize("namespaces", "cc-name#3", testDefinition.Parameters(), fakeOrgStates[0])
resources.Serializer.Serialize("chaincode-sources", "cc-name#3", testChaincodeLocalPackage, fakeOrgStates[0])
resources.Serializer.Serialize("namespaces", "cc-name#3", testDefinition.Parameters(), fakeOrgStates[1])
resources.Serializer.Serialize("chaincode-sources", "cc-name#3", testChaincodeLocalPackage, fakeOrgStates[1])

resources.Serializer.Serialize("namespaces", "cc-name#4", testDefinition.Parameters(), fakeOrgStates[0])
resources.Serializer.Serialize("chaincode-sources", "cc-name#4", testChaincodeLocalPackage, fakeOrgStates[0])
resources.Serializer.Serialize("namespaces", "cc-name#4", testDefinition.Parameters(), fakeOrgStates[1])
resources.Serializer.Serialize("chaincode-sources", "cc-name#4", testChaincodeLocalPackage, fakeOrgStates[1])

resources.Serializer.Serialize("namespaces", "cc-name2#1", testDefinition.Parameters(), fakeOrgStates[0])
resources.Serializer.Serialize("chaincode-sources", "cc-name2#1", testChaincodeLocalPackage, fakeOrgStates[0])
resources.Serializer.Serialize("namespaces", "cc-name2#1", testDefinition.Parameters(), fakeOrgStates[1])
resources.Serializer.Serialize("chaincode-sources", "cc-name2#1", testChaincodeLocalPackage, fakeOrgStates[1])
})

It("returns the approved chaincodes", func() {
acds, err := ef.QueryApprovedChaincodeDefinitions("my-channel", fakeOrgStates[0])
Expect(len(acds)).To(Equal(3))
Expect(err).NotTo(HaveOccurred())
})

Context("when deserializing namespace metadatas fails", func() {
BeforeEach(func() {
org0KVS["namespaces/metadata/cc-name#5"] = []byte("garbage")
})

It("wraps and returns the error", func() {
cc, err := ef.QueryApprovedChaincodeDefinitions("my-channel", fakeOrgStates[0])
Expect(err).To(Not(BeNil()))
Expect(err.Error()).To(HavePrefix("could not fetch metadatas"))
Expect(cc).To(BeNil())
})
})

Context("when namespace metadata has a bad key", func() {
BeforeEach(func() {
resources.Serializer.Serialize("namespaces", "badkey", testDefinition.Parameters(), fakeOrgStates[0])
})

It("wraps and returns the error", func() {
cc, err := ef.QueryApprovedChaincodeDefinitions("my-channel", fakeOrgStates[0])
Expect(err).To(Not(BeNil()))
Expect(err.Error()).To(HavePrefix("could not extract chaincode name and sequence number from metadata: badkey: invalid private name format: badkey"))
Expect(cc).To(BeNil())
})
})

Context("when namespace metadata has a key with bad sequence", func() {
BeforeEach(func() {
resources.Serializer.Serialize("namespaces", "cc-name#bad-sequence", testDefinition.Parameters(), fakeOrgStates[0])
})

It("wraps and returns the error", func() {
cc, err := ef.QueryApprovedChaincodeDefinitions("my-channel", fakeOrgStates[0])
Expect(err).To(Not(BeNil()))
Expect(err.Error()).To(HavePrefix("could not extract chaincode name and sequence number from metadata: cc-name#bad-sequence: invalid sequence number in private name"))
Expect(cc).To(BeNil())
})
})

Context("when deserializing chaincode-source metadata fails", func() {
BeforeEach(func() {
org0KVS["chaincode-sources/metadata/cc-name#4"] = []byte("garbage")
})

It("wraps and returns the error", func() {
cc, err := ef.QueryApprovedChaincodeDefinitions("my-channel", fakeOrgStates[0])
Expect(err).To(Not(BeNil()))
Expect(err.Error()).To(HavePrefix("could not fetch approved chaincode definition (name: 'cc-name', sequence: '4') on channel 'my-channel': could not deserialize chaincode-source metadata for cc-name#4"))
Expect(cc).To(BeNil())
})
})
})

Describe("CommitChaincodeDefinition", func() {
var (
fakePublicState *mock.ReadWritableState
Expand Down
81 changes: 81 additions & 0 deletions core/chaincode/lifecycle/mock/scc_functions.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 4014666

Please sign in to comment.