1 Star 0 Fork 0

peter / fabric

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
scc.go 30.78 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package lifecycle
import (
"fmt"
"regexp"
"github.com/hyperledger/fabric-chaincode-go/shim"
"github.com/hyperledger/fabric-protos-go/common"
mspprotos "github.com/hyperledger/fabric-protos-go/msp"
pb "github.com/hyperledger/fabric-protos-go/peer"
lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle"
"github.com/hyperledger/fabric/common/cauthdsl"
"github.com/hyperledger/fabric/common/chaincode"
"github.com/hyperledger/fabric/common/channelconfig"
"github.com/hyperledger/fabric/core/aclmgmt"
"github.com/hyperledger/fabric/core/chaincode/persistence"
"github.com/hyperledger/fabric/core/dispatcher"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/msp"
"github.com/golang/protobuf/proto"
"github.com/pkg/errors"
"go.uber.org/zap/zapcore"
)
const (
// LifecycleNamespace is the namespace in the statedb where lifecycle
// information is stored
LifecycleNamespace = "_lifecycle"
// InstallChaincodeFuncName is the chaincode function name used to install
// a chaincode
InstallChaincodeFuncName = "InstallChaincode"
// QueryInstalledChaincodeFuncName is the chaincode function name used to
// query an installed chaincode
QueryInstalledChaincodeFuncName = "QueryInstalledChaincode"
// QueryInstalledChaincodesFuncName is the chaincode function name used to
// query all installed chaincodes
QueryInstalledChaincodesFuncName = "QueryInstalledChaincodes"
// ApproveChaincodeDefinitionForMyOrgFuncName is the chaincode function name
// used to approve a chaincode definition for execution by the user's own org
ApproveChaincodeDefinitionForMyOrgFuncName = "ApproveChaincodeDefinitionForMyOrg"
// CheckCommitReadinessFuncName is the chaincode function name used to check
// a specified chaincode definition is ready to be committed. It returns the
// approval status for a given definition over a given set of orgs
CheckCommitReadinessFuncName = "CheckCommitReadiness"
// CommitChaincodeDefinitionFuncName is the chaincode function name used to
// 'commit' (previously 'instantiate') a chaincode in a channel.
CommitChaincodeDefinitionFuncName = "CommitChaincodeDefinition"
// QueryChaincodeDefinitionFuncName is the chaincode function name used to
// query a committed chaincode definition in a channel.
QueryChaincodeDefinitionFuncName = "QueryChaincodeDefinition"
// QueryChaincodeDefinitionsFuncName is the chaincode function name used to
// query the committed chaincode definitions in a channel.
QueryChaincodeDefinitionsFuncName = "QueryChaincodeDefinitions"
)
// SCCFunctions provides a backing implementation with concrete arguments
// for each of the SCC functions
type SCCFunctions interface {
// InstallChaincode persists a chaincode definition to disk
InstallChaincode([]byte) (*chaincode.InstalledChaincode, error)
// QueryInstalledChaincode returns metadata for the chaincode with the supplied package ID.
QueryInstalledChaincode(packageID string) (*chaincode.InstalledChaincode, error)
// GetInstalledChaincodePackage returns the chaincode package
// installed on the peer as bytes.
GetInstalledChaincodePackage(packageID string) ([]byte, error)
// QueryInstalledChaincodes returns the currently installed chaincodes
QueryInstalledChaincodes() []*chaincode.InstalledChaincode
// ApproveChaincodeDefinitionForOrg records a chaincode definition into this org's implicit collection.
ApproveChaincodeDefinitionForOrg(chname, ccname string, cd *ChaincodeDefinition, packageID string, publicState ReadableState, orgState ReadWritableState) error
// CheckCommitReadiness returns a map containing the orgs
// whose orgStates were supplied and whether or not they have approved
// the specified definition.
CheckCommitReadiness(chname, ccname string, cd *ChaincodeDefinition, publicState ReadWritableState, orgStates []OpaqueState) (map[string]bool, error)
// CommitChaincodeDefinition records a new chaincode definition into the
// public state and returns a map containing the orgs whose orgStates
// were supplied and whether or not they have approved the definition.
CommitChaincodeDefinition(chname, ccname string, cd *ChaincodeDefinition, publicState ReadWritableState, orgStates []OpaqueState) (map[string]bool, error)
// QueryChaincodeDefinition returns a chaincode definition from the public
// state.
QueryChaincodeDefinition(name string, publicState ReadableState) (*ChaincodeDefinition, error)
// QueryOrgApprovals returns a map containing the orgs whose orgStates were
// supplied and whether or not they have approved a chaincode definition with
// the specified parameters.
QueryOrgApprovals(name string, cd *ChaincodeDefinition, orgStates []OpaqueState) (map[string]bool, error)
// QueryNamespaceDefinitions returns all defined namespaces
QueryNamespaceDefinitions(publicState RangeableState) (map[string]string, error)
}
//go:generate counterfeiter -o mock/channel_config_source.go --fake-name ChannelConfigSource . ChannelConfigSource
// ChannelConfigSource provides a way to retrieve the channel config for a given
// channel ID.
type ChannelConfigSource interface {
// GetStableChannelConfig returns the channel config for a given channel id.
// Note, it is a stable bundle, which means it will not be updated, even if
// the channel is, so it should be discarded after use.
GetStableChannelConfig(channelID string) channelconfig.Resources
}
//go:generate counterfeiter -o mock/queryexecutor_provider.go --fake-name QueryExecutorProvider . QueryExecutorProvider
// QueryExecutorProvider provides a way to retrieve the query executor assosciated with an invocation
type QueryExecutorProvider interface {
TxQueryExecutor(channelID, txID string) ledger.SimpleQueryExecutor
}
// SCC implements the required methods to satisfy the chaincode interface.
// It routes the invocation calls to the backing implementations.
type SCC struct {
OrgMSPID string
ACLProvider aclmgmt.ACLProvider
ChannelConfigSource ChannelConfigSource
DeployedCCInfoProvider ledger.DeployedChaincodeInfoProvider
QueryExecutorProvider QueryExecutorProvider
// Functions provides the backing implementation of lifecycle.
Functions SCCFunctions
// Dispatcher handles the rote protobuf boilerplate for unmarshaling/marshaling
// the inputs and outputs of the SCC functions.
Dispatcher *dispatcher.Dispatcher
}
// Name returns "_lifecycle"
func (scc *SCC) Name() string {
return LifecycleNamespace
}
// Chaincode returns a reference to itself
func (scc *SCC) Chaincode() shim.Chaincode {
return scc
}
// Init is mostly useless for system chaincodes and always returns success
func (scc *SCC) Init(stub shim.ChaincodeStubInterface) pb.Response {
return shim.Success(nil)
}
// Invoke takes chaincode invocation arguments and routes them to the correct
// underlying lifecycle operation. All functions take a single argument of
// type marshaled lb.<FunctionName>Args and return a marshaled lb.<FunctionName>Result
func (scc *SCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
args := stub.GetArgs()
if len(args) == 0 {
return shim.Error("lifecycle scc must be invoked with arguments")
}
if len(args) != 2 {
return shim.Error(fmt.Sprintf("lifecycle scc operations require exactly two arguments but received %d", len(args)))
}
var ac channelconfig.Application
var channelID string
if channelID = stub.GetChannelID(); channelID != "" {
channelConfig := scc.ChannelConfigSource.GetStableChannelConfig(channelID)
if channelConfig == nil {
return shim.Error(fmt.Sprintf("could not get channelconfig for channel '%s'", channelID))
}
var ok bool
ac, ok = channelConfig.ApplicationConfig()
if !ok {
return shim.Error(fmt.Sprintf("could not get application config for channel '%s'", channelID))
}
if !ac.Capabilities().LifecycleV20() {
return shim.Error(fmt.Sprintf("cannot use new lifecycle for channel '%s' as it does not have the required capabilities enabled", channelID))
}
}
// Handle ACL:
sp, err := stub.GetSignedProposal()
if err != nil {
return shim.Error(fmt.Sprintf("Failed getting signed proposal from stub: [%s]", err))
}
err = scc.ACLProvider.CheckACL(fmt.Sprintf("%s/%s", LifecycleNamespace, args[0]), stub.GetChannelID(), sp)
if err != nil {
return shim.Error(fmt.Sprintf("Failed to authorize invocation due to failed ACL check: %s", err))
}
outputBytes, err := scc.Dispatcher.Dispatch(
args[1],
string(args[0]),
&Invocation{
ChannelID: channelID,
ApplicationConfig: ac,
SCC: scc,
Stub: stub,
},
)
if err != nil {
switch err.(type) {
case ErrNamespaceNotDefined, persistence.CodePackageNotFoundErr:
return pb.Response{
Status: 404,
Message: err.Error(),
}
default:
return shim.Error(fmt.Sprintf("failed to invoke backing implementation of '%s': %s", string(args[0]), err.Error()))
}
}
return shim.Success(outputBytes)
}
type Invocation struct {
ChannelID string
ApplicationConfig channelconfig.Application // Note this may be nil
Stub shim.ChaincodeStubInterface
SCC *SCC
}
// InstallChaincode is a SCC function that may be dispatched to which routes
// to the underlying lifecycle implementation.
func (i *Invocation) InstallChaincode(input *lb.InstallChaincodeArgs) (proto.Message, error) {
if logger.IsEnabledFor(zapcore.DebugLevel) {
end := 35
if len(input.ChaincodeInstallPackage) < end {
end = len(input.ChaincodeInstallPackage)
}
// the first tens of bytes contain the (compressed) portion
// of the package metadata and so they'll be different across
// different packages, acting as a package fingerprint useful
// to identify various packages from the content
packageFingerprint := input.ChaincodeInstallPackage[0:end]
logger.Debugf("received invocation of InstallChaincode for install package %x...",
packageFingerprint,
)
}
installedCC, err := i.SCC.Functions.InstallChaincode(input.ChaincodeInstallPackage)
if err != nil {
return nil, err
}
return &lb.InstallChaincodeResult{
Label: installedCC.Label,
PackageId: installedCC.PackageID,
}, nil
}
// QueryInstalledChaincode is a SCC function that may be dispatched to which
// routes to the underlying lifecycle implementation.
func (i *Invocation) QueryInstalledChaincode(input *lb.QueryInstalledChaincodeArgs) (proto.Message, error) {
logger.Debugf("received invocation of QueryInstalledChaincode for install package ID '%s'",
input.PackageId,
)
chaincode, err := i.SCC.Functions.QueryInstalledChaincode(input.PackageId)
if err != nil {
return nil, err
}
references := map[string]*lb.QueryInstalledChaincodeResult_References{}
for channel, chaincodeMetadata := range chaincode.References {
chaincodes := make([]*lb.QueryInstalledChaincodeResult_Chaincode, len(chaincodeMetadata))
for i, metadata := range chaincodeMetadata {
chaincodes[i] = &lb.QueryInstalledChaincodeResult_Chaincode{
Name: metadata.Name,
Version: metadata.Version,
}
}
references[channel] = &lb.QueryInstalledChaincodeResult_References{
Chaincodes: chaincodes,
}
}
return &lb.QueryInstalledChaincodeResult{
Label: chaincode.Label,
PackageId: chaincode.PackageID,
References: references,
}, nil
}
// GetInstalledChaincodePackage is a SCC function that may be dispatched to
// which routes to the underlying lifecycle implementation.
func (i *Invocation) GetInstalledChaincodePackage(input *lb.GetInstalledChaincodePackageArgs) (proto.Message, error) {
logger.Debugf("received invocation of GetInstalledChaincodePackage")
pkgBytes, err := i.SCC.Functions.GetInstalledChaincodePackage(input.PackageId)
if err != nil {
return nil, err
}
return &lb.GetInstalledChaincodePackageResult{
ChaincodeInstallPackage: pkgBytes,
}, nil
}
// QueryInstalledChaincodes is a SCC function that may be dispatched to which
// routes to the underlying lifecycle implementation.
func (i *Invocation) QueryInstalledChaincodes(input *lb.QueryInstalledChaincodesArgs) (proto.Message, error) {
logger.Debugf("received invocation of QueryInstalledChaincodes")
chaincodes := i.SCC.Functions.QueryInstalledChaincodes()
result := &lb.QueryInstalledChaincodesResult{}
for _, chaincode := range chaincodes {
references := map[string]*lb.QueryInstalledChaincodesResult_References{}
for channel, chaincodeMetadata := range chaincode.References {
chaincodes := make([]*lb.QueryInstalledChaincodesResult_Chaincode, len(chaincodeMetadata))
for i, metadata := range chaincodeMetadata {
chaincodes[i] = &lb.QueryInstalledChaincodesResult_Chaincode{
Name: metadata.Name,
Version: metadata.Version,
}
}
references[channel] = &lb.QueryInstalledChaincodesResult_References{
Chaincodes: chaincodes,
}
}
result.InstalledChaincodes = append(result.InstalledChaincodes,
&lb.QueryInstalledChaincodesResult_InstalledChaincode{
Label: chaincode.Label,
PackageId: chaincode.PackageID,
References: references,
})
}
return result, nil
}
// ApproveChaincodeDefinitionForMyOrg is a SCC function that may be dispatched
// to which routes to the underlying lifecycle implementation.
func (i *Invocation) ApproveChaincodeDefinitionForMyOrg(input *lb.ApproveChaincodeDefinitionForMyOrgArgs) (proto.Message, error) {
if err := i.validateInput(input.Name, input.Version, input.Collections); err != nil {
return nil, err
}
collectionName := ImplicitCollectionNameForOrg(i.SCC.OrgMSPID)
var collectionConfig []*pb.CollectionConfig
if input.Collections != nil {
collectionConfig = input.Collections.Config
}
var packageID string
if input.Source != nil {
switch source := input.Source.Type.(type) {
case *lb.ChaincodeSource_LocalPackage:
packageID = source.LocalPackage.PackageId
case *lb.ChaincodeSource_Unavailable_:
default:
}
}
cd := &ChaincodeDefinition{
Sequence: input.Sequence,
EndorsementInfo: &lb.ChaincodeEndorsementInfo{
Version: input.Version,
EndorsementPlugin: input.EndorsementPlugin,
InitRequired: input.InitRequired,
},
ValidationInfo: &lb.ChaincodeValidationInfo{
ValidationPlugin: input.ValidationPlugin,
ValidationParameter: input.ValidationParameter,
},
Collections: &pb.CollectionConfigPackage{
Config: collectionConfig,
},
}
logger.Debugf("received invocation of ApproveChaincodeDefinitionForMyOrg on channel '%s' for definition '%s'",
i.Stub.GetChannelID(),
cd,
)
if err := i.SCC.Functions.ApproveChaincodeDefinitionForOrg(
i.Stub.GetChannelID(),
input.Name,
cd,
packageID,
i.Stub,
&ChaincodePrivateLedgerShim{
Collection: collectionName,
Stub: i.Stub,
},
); err != nil {
return nil, err
}
return &lb.ApproveChaincodeDefinitionForMyOrgResult{}, nil
}
// CheckCommitReadiness is a SCC function that may be dispatched
// to the underlying lifecycle implementation.
func (i *Invocation) CheckCommitReadiness(input *lb.CheckCommitReadinessArgs) (proto.Message, error) {
opaqueStates, err := i.createOpaqueStates()
if err != nil {
return nil, err
}
cd := &ChaincodeDefinition{
Sequence: input.Sequence,
EndorsementInfo: &lb.ChaincodeEndorsementInfo{
Version: input.Version,
EndorsementPlugin: input.EndorsementPlugin,
InitRequired: input.InitRequired,
},
ValidationInfo: &lb.ChaincodeValidationInfo{
ValidationPlugin: input.ValidationPlugin,
ValidationParameter: input.ValidationParameter,
},
Collections: input.Collections,
}
logger.Debugf("received invocation of CheckCommitReadiness on channel '%s' for definition '%s'",
i.Stub.GetChannelID(),
cd,
)
approvals, err := i.SCC.Functions.CheckCommitReadiness(
i.Stub.GetChannelID(),
input.Name,
cd,
i.Stub,
opaqueStates,
)
if err != nil {
return nil, err
}
return &lb.CheckCommitReadinessResult{
Approvals: approvals,
}, nil
}
// CommitChaincodeDefinition is a SCC function that may be dispatched
// to which routes to the underlying lifecycle implementation.
func (i *Invocation) CommitChaincodeDefinition(input *lb.CommitChaincodeDefinitionArgs) (proto.Message, error) {
if err := i.validateInput(input.Name, input.Version, input.Collections); err != nil {
return nil, err
}
if i.ApplicationConfig == nil {
return nil, errors.Errorf("no application config for channel '%s'", i.Stub.GetChannelID())
}
orgs := i.ApplicationConfig.Organizations()
opaqueStates := make([]OpaqueState, 0, len(orgs))
var myOrg string
for _, org := range orgs {
opaqueStates = append(opaqueStates, &ChaincodePrivateLedgerShim{
Collection: ImplicitCollectionNameForOrg(org.MSPID()),
Stub: i.Stub,
})
if org.MSPID() == i.SCC.OrgMSPID {
myOrg = i.SCC.OrgMSPID
}
}
if myOrg == "" {
return nil, errors.Errorf("impossibly, this peer's org is processing requests for a channel it is not a member of")
}
cd := &ChaincodeDefinition{
Sequence: input.Sequence,
EndorsementInfo: &lb.ChaincodeEndorsementInfo{
Version: input.Version,
EndorsementPlugin: input.EndorsementPlugin,
InitRequired: input.InitRequired,
},
ValidationInfo: &lb.ChaincodeValidationInfo{
ValidationPlugin: input.ValidationPlugin,
ValidationParameter: input.ValidationParameter,
},
Collections: input.Collections,
}
logger.Debugf("received invocation of CommitChaincodeDefinition on channel '%s' for definition '%s'",
i.Stub.GetChannelID(),
cd,
)
approvals, err := i.SCC.Functions.CommitChaincodeDefinition(
i.Stub.GetChannelID(),
input.Name,
cd,
i.Stub,
opaqueStates,
)
if err != nil {
return nil, err
}
if !approvals[myOrg] {
return nil, errors.Errorf("chaincode definition not agreed to by this org (%s)", i.SCC.OrgMSPID)
}
logger.Infof("Successfully endorsed commit for chaincode name '%s' on channel '%s' with definition {%s}", input.Name, i.Stub.GetChannelID(), cd)
return &lb.CommitChaincodeDefinitionResult{}, nil
}
// QueryChaincodeDefinition is a SCC function that may be dispatched
// to which routes to the underlying lifecycle implementation.
func (i *Invocation) QueryChaincodeDefinition(input *lb.QueryChaincodeDefinitionArgs) (proto.Message, error) {
logger.Debugf("received invocation of QueryChaincodeDefinition on channel '%s' for chaincode '%s'",
i.Stub.GetChannelID(),
input.Name,
)
definedChaincode, err := i.SCC.Functions.QueryChaincodeDefinition(input.Name, i.Stub)
if err != nil {
return nil, err
}
opaqueStates, err := i.createOpaqueStates()
if err != nil {
return nil, err
}
var approvals map[string]bool
if approvals, err = i.SCC.Functions.QueryOrgApprovals(input.Name, definedChaincode, opaqueStates); err != nil {
return nil, err
}
return &lb.QueryChaincodeDefinitionResult{
Sequence: definedChaincode.Sequence,
Version: definedChaincode.EndorsementInfo.Version,
EndorsementPlugin: definedChaincode.EndorsementInfo.EndorsementPlugin,
ValidationPlugin: definedChaincode.ValidationInfo.ValidationPlugin,
ValidationParameter: definedChaincode.ValidationInfo.ValidationParameter,
InitRequired: definedChaincode.EndorsementInfo.InitRequired,
Collections: definedChaincode.Collections,
Approvals: approvals,
}, nil
}
// QueryChaincodeDefinitions is a SCC function that may be dispatched
// to which routes to the underlying lifecycle implementation.
func (i *Invocation) QueryChaincodeDefinitions(input *lb.QueryChaincodeDefinitionsArgs) (proto.Message, error) {
logger.Debugf("received invocation of QueryChaincodeDefinitions on channel '%s'",
i.Stub.GetChannelID(),
)
namespaces, err := i.SCC.Functions.QueryNamespaceDefinitions(&ChaincodePublicLedgerShim{ChaincodeStubInterface: i.Stub})
if err != nil {
return nil, err
}
chaincodeDefinitions := []*lb.QueryChaincodeDefinitionsResult_ChaincodeDefinition{}
for namespace, nType := range namespaces {
if nType == FriendlyChaincodeDefinitionType {
definedChaincode, err := i.SCC.Functions.QueryChaincodeDefinition(namespace, i.Stub)
if err != nil {
return nil, err
}
chaincodeDefinitions = append(chaincodeDefinitions, &lb.QueryChaincodeDefinitionsResult_ChaincodeDefinition{
Name: namespace,
Sequence: definedChaincode.Sequence,
Version: definedChaincode.EndorsementInfo.Version,
EndorsementPlugin: definedChaincode.EndorsementInfo.EndorsementPlugin,
ValidationPlugin: definedChaincode.ValidationInfo.ValidationPlugin,
ValidationParameter: definedChaincode.ValidationInfo.ValidationParameter,
InitRequired: definedChaincode.EndorsementInfo.InitRequired,
Collections: definedChaincode.Collections,
})
}
}
return &lb.QueryChaincodeDefinitionsResult{
ChaincodeDefinitions: chaincodeDefinitions,
}, nil
}
var (
// NOTE the chaincode name/version regular expressions should stay in sync
// with those defined in core/scc/lscc/lscc.go until LSCC has been removed.
ChaincodeNameRegExp = regexp.MustCompile("^[a-zA-Z0-9]+([-_][a-zA-Z0-9]+)*$")
ChaincodeVersionRegExp = regexp.MustCompile("^[A-Za-z0-9_.+-]+$")
collectionNameRegExp = regexp.MustCompile("^[A-Za-z0-9-]+([A-Za-z0-9_-]+)*$")
// currently defined system chaincode names that shouldn't
// be allowed as user-defined chaincode names
systemChaincodeNames = map[string]struct{}{
"cscc": {},
"escc": {},
"lscc": {},
"qscc": {},
"vscc": {},
}
)
func (i *Invocation) validateInput(name, version string, collections *pb.CollectionConfigPackage) error {
if !ChaincodeNameRegExp.MatchString(name) {
return errors.Errorf("invalid chaincode name '%s'. Names can only consist of alphanumerics, '_', and '-' and can only begin with alphanumerics", name)
}
if _, ok := systemChaincodeNames[name]; ok {
return errors.Errorf("chaincode name '%s' is the name of a system chaincode", name)
}
if !ChaincodeVersionRegExp.MatchString(version) {
return errors.Errorf("invalid chaincode version '%s'. Versions can only consist of alphanumerics, '_', '-', '+', and '.'", version)
}
collConfigs, err := extractStaticCollectionConfigs(collections)
if err != nil {
return err
}
// we extract the channel config to check whether the supplied collection configuration
// complies to the given msp configuration and performs semantic validation.
// Channel config may change afterwards (i.e., after endorsement or commit of this transaction).
// Fabric will deal with the situation where some collection configs are no longer meaningful.
// Therefore, the use of channel config for verifying during endorsement is more
// towards catching manual errors in the config as oppose to any attempt of serializability.
channelConfig := i.SCC.ChannelConfigSource.GetStableChannelConfig(i.ChannelID)
if channelConfig == nil {
return errors.Errorf("could not get channelconfig for channel '%s'", i.ChannelID)
}
mspMgr := channelConfig.MSPManager()
if mspMgr == nil {
return errors.Errorf("could not get MSP manager for channel '%s'", i.ChannelID)
}
if err := validateCollectionConfigs(collConfigs, mspMgr); err != nil {
return err
}
// validate against collection configs in the committed definition
qe := i.SCC.QueryExecutorProvider.TxQueryExecutor(i.Stub.GetChannelID(), i.Stub.GetTxID())
committedCCDef, err := i.SCC.DeployedCCInfoProvider.ChaincodeInfo(i.ChannelID, name, qe)
if err != nil {
return errors.Wrapf(err, "could not retrieve committed definition for chaincode '%s'", name)
}
if committedCCDef == nil {
return nil
}
if err := validateCollConfigsAgainstCommittedDef(collConfigs, committedCCDef.ExplicitCollectionConfigPkg); err != nil {
return err
}
return nil
}
func extractStaticCollectionConfigs(collConfigPkg *pb.CollectionConfigPackage) ([]*pb.StaticCollectionConfig, error) {
if collConfigPkg == nil || len(collConfigPkg.Config) == 0 {
return nil, nil
}
collConfigs := make([]*pb.StaticCollectionConfig, len(collConfigPkg.Config))
for i, c := range collConfigPkg.Config {
switch t := c.Payload.(type) {
case *pb.CollectionConfig_StaticCollectionConfig:
collConfig := t.StaticCollectionConfig
if collConfig == nil {
return nil, errors.Errorf("collection configuration is empty")
}
collConfigs[i] = collConfig
default:
// this should only occur if a developer has added a new
// collection config type
return nil, errors.Errorf("collection config contains unexpected payload type: %T", t)
}
}
return collConfigs, nil
}
func validateCollectionConfigs(collConfigs []*pb.StaticCollectionConfig, mspMgr msp.MSPManager) error {
if len(collConfigs) == 0 {
return nil
}
collNamesMap := map[string]struct{}{}
// Process each collection config from a set of collection configs
for _, c := range collConfigs {
if !collectionNameRegExp.MatchString(c.Name) {
return errors.Errorf("invalid collection name '%s'. Names can only consist of alphanumerics, '_', and '-' and cannot begin with '_'",
c.Name)
}
// Ensure that there are no duplicate collection names
if _, ok := collNamesMap[c.Name]; ok {
return errors.Errorf("collection-name: %s -- found duplicate in collection configuration",
c.Name)
}
collNamesMap[c.Name] = struct{}{}
// Validate gossip related parameters present in the collection config
if c.MaximumPeerCount < c.RequiredPeerCount {
return errors.Errorf("collection-name: %s -- maximum peer count (%d) cannot be less than the required peer count (%d)",
c.Name, c.MaximumPeerCount, c.RequiredPeerCount)
}
if c.RequiredPeerCount < 0 {
return errors.Errorf("collection-name: %s -- requiredPeerCount (%d) cannot be less than zero",
c.Name, c.RequiredPeerCount)
}
if err := validateCollectionConfigMemberOrgsPolicy(c, mspMgr); err != nil {
return err
}
}
return nil
}
// validateCollectionConfigAgainstMsp checks whether the supplied collection configuration
// complies to the given msp configuration
func validateCollectionConfigMemberOrgsPolicy(coll *pb.StaticCollectionConfig, mspMgr msp.MSPManager) error {
if coll.MemberOrgsPolicy == nil {
return errors.Errorf("collection member policy is not set for collection '%s'", coll.Name)
}
if coll.MemberOrgsPolicy.GetSignaturePolicy() == nil {
return errors.Errorf("collection member org policy is empty for collection '%s'", coll.Name)
}
// calling this constructor ensures extra semantic validation for the policy
pp := &cauthdsl.EnvelopeBasedPolicyProvider{Deserializer: mspMgr}
if _, err := pp.NewPolicy(coll.MemberOrgsPolicy.GetSignaturePolicy()); err != nil {
return errors.WithMessagef(err, "invalid member org policy for collection '%s'", coll.Name)
}
// make sure that the signature policy is meaningful (only consists of ORs)
if err := validateSpOrConcat(coll.MemberOrgsPolicy.GetSignaturePolicy().Rule); err != nil {
return errors.WithMessagef(err, "collection-name: %s -- error in member org policy", coll.Name)
}
msps, err := mspMgr.GetMSPs()
if err != nil {
return errors.Wrapf(err, "could not get MSPs")
}
// make sure that the orgs listed are actually part of the channel
// check all principals in the signature policy
for _, principal := range coll.MemberOrgsPolicy.GetSignaturePolicy().Identities {
var orgID string
// the member org policy only supports certain principal types
switch principal.PrincipalClassification {
case mspprotos.MSPPrincipal_ROLE:
msprole := &mspprotos.MSPRole{}
err := proto.Unmarshal(principal.Principal, msprole)
if err != nil {
return errors.Wrapf(err, "collection-name: %s -- cannot unmarshal identity bytes into MSPRole", coll.GetName())
}
orgID = msprole.MspIdentifier
// the msp map is indexed using msp IDs - this behavior is implementation specific, making the following check a bit of a hack
_, ok := msps[orgID]
if !ok {
return errors.Errorf("collection-name: %s -- collection member '%s' is not part of the channel", coll.GetName(), orgID)
}
case mspprotos.MSPPrincipal_ORGANIZATION_UNIT:
mspou := &mspprotos.OrganizationUnit{}
err := proto.Unmarshal(principal.Principal, mspou)
if err != nil {
return errors.Wrapf(err, "collection-name: %s -- cannot unmarshal identity bytes into OrganizationUnit", coll.GetName())
}
orgID = mspou.MspIdentifier
// the msp map is indexed using msp IDs - this behavior is implementation specific, making the following check a bit of a hack
_, ok := msps[orgID]
if !ok {
return errors.Errorf("collection-name: %s -- collection member '%s' is not part of the channel", coll.GetName(), orgID)
}
case mspprotos.MSPPrincipal_IDENTITY:
if _, err := mspMgr.DeserializeIdentity(principal.Principal); err != nil {
return errors.Errorf("collection-name: %s -- contains an identity that is not part of the channel", coll.GetName())
}
default:
return errors.Errorf("collection-name: %s -- principal type %v is not supported", coll.GetName(), principal.PrincipalClassification)
}
}
return nil
}
// validateSpOrConcat checks if the supplied signature policy is just an OR-concatenation of identities
func validateSpOrConcat(sp *common.SignaturePolicy) error {
if sp.GetNOutOf() == nil {
return nil
}
// check if N == 1 (OR concatenation)
if sp.GetNOutOf().N != 1 {
return errors.Errorf("signature policy is not an OR concatenation, NOutOf %d", sp.GetNOutOf().N)
}
// recurse into all sub-rules
for _, rule := range sp.GetNOutOf().Rules {
err := validateSpOrConcat(rule)
if err != nil {
return err
}
}
return nil
}
func validateCollConfigsAgainstCommittedDef(
proposedCollConfs []*pb.StaticCollectionConfig,
committedCollConfPkg *pb.CollectionConfigPackage,
) error {
if committedCollConfPkg == nil || len(committedCollConfPkg.Config) == 0 {
return nil
}
if len(proposedCollConfs) == 0 {
return errors.Errorf("the proposed collection config does not contain previously defined collections")
}
proposedCollsMap := map[string]*pb.StaticCollectionConfig{}
for _, c := range proposedCollConfs {
proposedCollsMap[c.Name] = c
}
// In the new collection config package, ensure that there is one entry per old collection. Any
// number of new collections are allowed.
for _, committedCollConfig := range committedCollConfPkg.Config {
committedColl := committedCollConfig.GetStaticCollectionConfig()
// It cannot be nil
if committedColl == nil {
return errors.Errorf("unknown collection configuration type")
}
newCollection, ok := proposedCollsMap[committedColl.Name]
if !ok {
return errors.Errorf("existing collection [%s] missing in the proposed collection configuration", committedColl.Name)
}
if newCollection.BlockToLive != committedColl.BlockToLive {
return errors.Errorf("the BlockToLive in an existing collection [%s] modified. Existing value [%d]", committedColl.Name, committedColl.BlockToLive)
}
}
return nil
}
func (i *Invocation) createOpaqueStates() ([]OpaqueState, error) {
if i.ApplicationConfig == nil {
return nil, errors.Errorf("no application config for channel '%s'", i.Stub.GetChannelID())
}
orgs := i.ApplicationConfig.Organizations()
opaqueStates := make([]OpaqueState, 0, len(orgs))
for _, org := range orgs {
opaqueStates = append(opaqueStates, &ChaincodePrivateLedgerShim{
Collection: ImplicitCollectionNameForOrg(org.MSPID()),
Stub: i.Stub,
})
}
return opaqueStates, nil
}
1
https://gitee.com/peter_code_git/fabric.git
git@gitee.com:peter_code_git/fabric.git
peter_code_git
fabric
fabric
v2.1.1

搜索帮助