github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/chaincode/lifecycle/deployedcc_infoprovider.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package lifecycle 8 9 import ( 10 "fmt" 11 "regexp" 12 "strings" 13 14 cb "github.com/hyperledger/fabric-protos-go/common" 15 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 16 pb "github.com/hyperledger/fabric-protos-go/peer" 17 "github.com/hyperledger/fabric/common/cauthdsl" 18 "github.com/hyperledger/fabric/common/util" 19 validationState "github.com/hyperledger/fabric/core/handlers/validation/api/state" 20 "github.com/hyperledger/fabric/core/ledger" 21 "github.com/hyperledger/fabric/core/peer" 22 "github.com/hyperledger/fabric/protoutil" 23 24 "github.com/pkg/errors" 25 ) 26 27 //go:generate counterfeiter -o mock/legacy_ccinfo.go --fake-name LegacyDeployedCCInfoProvider . LegacyDeployedCCInfoProvider 28 type LegacyDeployedCCInfoProvider interface { 29 ledger.DeployedChaincodeInfoProvider 30 } 31 32 const ( 33 LifecycleEndorsementPolicyRef = "/Channel/Application/LifecycleEndorsement" 34 ) 35 36 var ( 37 // This is a channel which was created with a lifecycle endorsement policy 38 LifecycleDefaultEndorsementPolicyBytes = protoutil.MarshalOrPanic(&cb.ApplicationPolicy{ 39 Type: &cb.ApplicationPolicy_ChannelConfigPolicyReference{ 40 ChannelConfigPolicyReference: LifecycleEndorsementPolicyRef, 41 }, 42 }) 43 ) 44 45 type ValidatorCommitter struct { 46 CoreConfig *peer.Config 47 Resources *Resources 48 LegacyDeployedCCInfoProvider LegacyDeployedCCInfoProvider 49 } 50 51 // Namespaces returns the list of namespaces which are relevant to chaincode lifecycle 52 func (vc *ValidatorCommitter) Namespaces() []string { 53 return append([]string{LifecycleNamespace}, vc.LegacyDeployedCCInfoProvider.Namespaces()...) 54 } 55 56 var SequenceMatcher = regexp.MustCompile("^" + NamespacesName + "/fields/([^/]+)/Sequence$") 57 58 // UpdatedChaincodes returns the chaincodes that are getting updated by the supplied 'stateUpdates' 59 func (vc *ValidatorCommitter) UpdatedChaincodes(stateUpdates map[string][]*kvrwset.KVWrite) ([]*ledger.ChaincodeLifecycleInfo, error) { 60 lifecycleInfo := []*ledger.ChaincodeLifecycleInfo{} 61 62 // If the lifecycle table was updated, report only modified chaincodes 63 lifecycleUpdates := stateUpdates[LifecycleNamespace] 64 65 for _, kvWrite := range lifecycleUpdates { 66 matches := SequenceMatcher.FindStringSubmatch(kvWrite.Key) 67 if len(matches) != 2 { 68 continue 69 } 70 // XXX Note, this may not be a chaincode namespace, handle this later 71 lifecycleInfo = append(lifecycleInfo, &ledger.ChaincodeLifecycleInfo{Name: matches[1]}) 72 } 73 74 legacyUpdates, err := vc.LegacyDeployedCCInfoProvider.UpdatedChaincodes(stateUpdates) 75 if err != nil { 76 return nil, errors.WithMessage(err, "error invoking legacy deployed cc info provider") 77 } 78 79 return append(lifecycleInfo, legacyUpdates...), nil 80 } 81 82 func (vc *ValidatorCommitter) ChaincodeInfo(channelName, chaincodeName string, qe ledger.SimpleQueryExecutor) (*ledger.DeployedChaincodeInfo, error) { 83 exists, definedChaincode, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &SimpleQueryExecutorShim{ 84 Namespace: LifecycleNamespace, 85 SimpleQueryExecutor: qe, 86 }) 87 if err != nil { 88 return nil, errors.WithMessage(err, "could not get info about chaincode") 89 } 90 91 if !exists { 92 return vc.LegacyDeployedCCInfoProvider.ChaincodeInfo(channelName, chaincodeName, qe) 93 } 94 95 return &ledger.DeployedChaincodeInfo{ 96 Name: chaincodeName, 97 Version: definedChaincode.EndorsementInfo.Version, 98 Hash: util.ComputeSHA256([]byte(chaincodeName + ":" + definedChaincode.EndorsementInfo.Version)), 99 ExplicitCollectionConfigPkg: definedChaincode.Collections, 100 IsLegacy: false, 101 }, nil 102 } 103 104 var ImplicitCollectionMatcher = regexp.MustCompile("^" + ImplicitCollectionNameForOrg("(.+)") + "$") 105 106 // AllCollectionsConfigPkg implements function in interface ledger.DeployedChaincodeInfoProvider 107 // this implementation returns a combined collection config pkg that contains both explicit and implicit collections 108 func (vc *ValidatorCommitter) AllCollectionsConfigPkg(channelName, chaincodeName string, qe ledger.SimpleQueryExecutor) (*pb.CollectionConfigPackage, error) { 109 chaincodeInfo, err := vc.ChaincodeInfo(channelName, chaincodeName, qe) 110 if err != nil { 111 return nil, err 112 } 113 explicitCollectionConfigPkg := chaincodeInfo.ExplicitCollectionConfigPkg 114 115 if chaincodeInfo.IsLegacy { 116 return explicitCollectionConfigPkg, nil 117 } 118 119 implicitCollections, err := vc.ImplicitCollections(channelName, chaincodeName, qe) 120 if err != nil { 121 return nil, err 122 } 123 124 var combinedColls []*pb.CollectionConfig 125 if explicitCollectionConfigPkg != nil { 126 combinedColls = append(combinedColls, explicitCollectionConfigPkg.Config...) 127 } 128 for _, implicitColl := range implicitCollections { 129 c := &pb.CollectionConfig{} 130 c.Payload = &pb.CollectionConfig_StaticCollectionConfig{StaticCollectionConfig: implicitColl} 131 combinedColls = append(combinedColls, c) 132 } 133 return &pb.CollectionConfigPackage{ 134 Config: combinedColls, 135 }, nil 136 } 137 138 // CollectionInfo implements function in interface ledger.DeployedChaincodeInfoProvider, it returns config for 139 // both static and implicit collections. 140 func (vc *ValidatorCommitter) CollectionInfo(channelName, chaincodeName, collectionName string, qe ledger.SimpleQueryExecutor) (*pb.StaticCollectionConfig, error) { 141 exists, definedChaincode, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &SimpleQueryExecutorShim{ 142 Namespace: LifecycleNamespace, 143 SimpleQueryExecutor: qe, 144 }) 145 if err != nil { 146 return nil, errors.WithMessage(err, "could not get chaincode") 147 } 148 149 if !exists { 150 return vc.LegacyDeployedCCInfoProvider.CollectionInfo(channelName, chaincodeName, collectionName, qe) 151 } 152 153 matches := ImplicitCollectionMatcher.FindStringSubmatch(collectionName) 154 if len(matches) == 2 { 155 return vc.GenerateImplicitCollectionForOrg(matches[1]), nil 156 } 157 158 if definedChaincode.Collections != nil { 159 for _, conf := range definedChaincode.Collections.Config { 160 staticCollConfig := conf.GetStaticCollectionConfig() 161 if staticCollConfig != nil && staticCollConfig.Name == collectionName { 162 return staticCollConfig, nil 163 } 164 } 165 } 166 return nil, nil 167 } 168 169 // ImplicitCollections implements function in interface ledger.DeployedChaincodeInfoProvider. It returns 170 //a slice that contains one proto msg for each of the implicit collections 171 func (vc *ValidatorCommitter) ImplicitCollections(channelName, chaincodeName string, qe ledger.SimpleQueryExecutor) ([]*pb.StaticCollectionConfig, error) { 172 exists, _, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &SimpleQueryExecutorShim{ 173 Namespace: LifecycleNamespace, 174 SimpleQueryExecutor: qe, 175 }) 176 if err != nil { 177 return nil, errors.WithMessage(err, "could not get info about chaincode") 178 } 179 180 if !exists { 181 // Implicit collections are a v2.0 lifecycle concept, if the chaincode is not in the new lifecycle, return nothing 182 return nil, nil 183 } 184 185 return vc.ChaincodeImplicitCollections(channelName) 186 } 187 188 // ChaincodeImplicitCollections assumes the chaincode exists in the new lifecycle and returns the implicit collections 189 func (vc *ValidatorCommitter) ChaincodeImplicitCollections(channelName string) ([]*pb.StaticCollectionConfig, error) { 190 channelConfig := vc.Resources.ChannelConfigSource.GetStableChannelConfig(channelName) 191 if channelConfig == nil { 192 return nil, errors.Errorf("could not get channelconfig for channel %s", channelName) 193 } 194 ac, ok := channelConfig.ApplicationConfig() 195 if !ok { 196 return nil, errors.Errorf("could not get application config for channel %s", channelName) 197 } 198 199 orgs := ac.Organizations() 200 implicitCollections := make([]*pb.StaticCollectionConfig, 0, len(orgs)) 201 for _, org := range orgs { 202 implicitCollections = append(implicitCollections, vc.GenerateImplicitCollectionForOrg(org.MSPID())) 203 } 204 205 return implicitCollections, nil 206 } 207 208 // GenerateImplicitCollectionForOrg generates implicit collection for the org 209 func (vc *ValidatorCommitter) GenerateImplicitCollectionForOrg(mspid string) *pb.StaticCollectionConfig { 210 // set collection MaximumPeerCount to 0 when its mspid does not match the local mspid 211 // set collection MaximumPeerCount to 1 when its mspid matches the local mspid (i.e., peer's own org) 212 maxPeerCount := int32(0) 213 if mspid == vc.CoreConfig.LocalMSPID { 214 maxPeerCount = 1 215 } 216 return &pb.StaticCollectionConfig{ 217 Name: ImplicitCollectionNameForOrg(mspid), 218 MemberOrgsPolicy: &pb.CollectionPolicyConfig{ 219 Payload: &pb.CollectionPolicyConfig_SignaturePolicy{ 220 SignaturePolicy: cauthdsl.SignedByMspMember(mspid), 221 }, 222 }, 223 RequiredPeerCount: 0, 224 MaximumPeerCount: maxPeerCount, 225 } 226 } 227 228 func ImplicitCollectionNameForOrg(mspid string) string { 229 return fmt.Sprintf("_implicit_org_%s", mspid) 230 } 231 232 func OrgFromImplicitCollectionName(name string) string { 233 return strings.TrimPrefix(name, "_implicit_org_") 234 } 235 236 func (vc *ValidatorCommitter) ImplicitCollectionEndorsementPolicyAsBytes(channelID, orgMSPID string) (policy []byte, unexpectedErr, validationErr error) { 237 channelConfig := vc.Resources.ChannelConfigSource.GetStableChannelConfig(channelID) 238 if channelConfig == nil { 239 return nil, errors.Errorf("could not get channel config for channel '%s'", channelID), nil 240 } 241 242 ac, ok := channelConfig.ApplicationConfig() 243 if !ok { 244 return nil, errors.Errorf("could not get application config for channel '%s'", channelID), nil 245 } 246 247 matchedOrgName := "" 248 for orgName, org := range ac.Organizations() { 249 if org.MSPID() == orgMSPID { 250 matchedOrgName = orgName 251 break 252 } 253 } 254 255 if matchedOrgName == "" { 256 return nil, nil, errors.Errorf("no org found in channel with MSPID '%s'", orgMSPID) 257 } 258 259 policyName := fmt.Sprintf("/Channel/Application/%s/Endorsement", matchedOrgName) 260 if _, ok := channelConfig.PolicyManager().GetPolicy(policyName); ok { 261 return protoutil.MarshalOrPanic(&cb.ApplicationPolicy{ 262 Type: &cb.ApplicationPolicy_ChannelConfigPolicyReference{ 263 ChannelConfigPolicyReference: policyName, 264 }, 265 }), nil, nil 266 } 267 268 // This was a channel which was upgraded or did not define an org level endorsement policy, use a default 269 // of "any member of the org" 270 271 return protoutil.MarshalOrPanic(&cb.ApplicationPolicy{ 272 Type: &cb.ApplicationPolicy_SignaturePolicy{ 273 SignaturePolicy: cauthdsl.SignedByAnyMember([]string{orgMSPID}), 274 }, 275 }), nil, nil 276 } 277 278 // ValidationInfo returns the name and arguments of the validation plugin for the supplied 279 // chaincode. The function returns two types of errors, unexpected errors and validation 280 // errors. The reason for this is that this function is called from the validation code, 281 // which needs to differentiate the two types of error to halt processing on the channel 282 // if the unexpected error is not nil and mark the transaction as invalid if the validation 283 // error is not nil. 284 func (vc *ValidatorCommitter) ValidationInfo(channelID, chaincodeName string, qe ledger.SimpleQueryExecutor) (plugin string, args []byte, unexpectedErr error, validationErr error) { 285 // TODO, this is a bit of an overkill check, and will need to be scaled back for non-chaincode type namespaces 286 exists, definedChaincode, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &SimpleQueryExecutorShim{ 287 Namespace: LifecycleNamespace, 288 SimpleQueryExecutor: qe, 289 }) 290 if err != nil { 291 return "", nil, errors.WithMessage(err, "could not get chaincode"), nil 292 } 293 294 if !exists { 295 // TODO, this is inconsistent with how the legacy lifecycle reports 296 // that a missing chaincode is a validation error. But, for now 297 // this is required to make the passthrough work. 298 return "", nil, nil, nil 299 } 300 301 if chaincodeName == LifecycleNamespace { 302 b, err := vc.Resources.LifecycleEndorsementPolicyAsBytes(channelID) 303 if err != nil { 304 return "", nil, errors.WithMessage(err, "unexpected failure to create lifecycle endorsement policy"), nil 305 } 306 return "vscc", b, nil, nil 307 } 308 309 return definedChaincode.ValidationInfo.ValidationPlugin, definedChaincode.ValidationInfo.ValidationParameter, nil, nil 310 } 311 312 // CollectionValidationInfo returns information about collections to the validation component 313 func (vc *ValidatorCommitter) CollectionValidationInfo(channelID, chaincodeName, collectionName string, state validationState.State) (args []byte, unexpectedErr, validationErr error) { 314 exists, definedChaincode, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &ValidatorStateShim{ 315 Namespace: LifecycleNamespace, 316 ValidatorState: state, 317 }) 318 if err != nil { 319 return nil, errors.WithMessage(err, "could not get chaincode"), nil 320 } 321 322 if !exists { 323 // TODO, this is inconsistent with how the legacy lifecycle reports 324 // that a missing chaincode is a validation error. But, for now 325 // this is required to make the passthrough work. 326 return nil, nil, nil 327 } 328 329 matches := ImplicitCollectionMatcher.FindStringSubmatch(collectionName) 330 if len(matches) == 2 { 331 return vc.ImplicitCollectionEndorsementPolicyAsBytes(channelID, matches[1]) 332 } 333 334 if definedChaincode.Collections != nil { 335 for _, conf := range definedChaincode.Collections.Config { 336 staticCollConfig := conf.GetStaticCollectionConfig() 337 if staticCollConfig != nil && staticCollConfig.Name == collectionName { 338 if staticCollConfig.EndorsementPolicy != nil { 339 return protoutil.MarshalOrPanic(staticCollConfig.EndorsementPolicy), nil, nil 340 } 341 // default to chaincode endorsement policy 342 return definedChaincode.ValidationInfo.ValidationParameter, nil, nil 343 } 344 } 345 } 346 347 return nil, nil, errors.Errorf("no such collection '%s'", collectionName) 348 }