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