github.com/defanghe/fabric@v2.1.1+incompatible/core/chaincode/lifecycle/scc.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 13 "github.com/hyperledger/fabric-chaincode-go/shim" 14 "github.com/hyperledger/fabric-protos-go/common" 15 mspprotos "github.com/hyperledger/fabric-protos-go/msp" 16 pb "github.com/hyperledger/fabric-protos-go/peer" 17 lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle" 18 "github.com/hyperledger/fabric/common/cauthdsl" 19 "github.com/hyperledger/fabric/common/chaincode" 20 "github.com/hyperledger/fabric/common/channelconfig" 21 "github.com/hyperledger/fabric/core/aclmgmt" 22 "github.com/hyperledger/fabric/core/chaincode/persistence" 23 "github.com/hyperledger/fabric/core/dispatcher" 24 "github.com/hyperledger/fabric/core/ledger" 25 "github.com/hyperledger/fabric/msp" 26 27 "github.com/golang/protobuf/proto" 28 "github.com/pkg/errors" 29 "go.uber.org/zap/zapcore" 30 ) 31 32 const ( 33 // LifecycleNamespace is the namespace in the statedb where lifecycle 34 // information is stored 35 LifecycleNamespace = "_lifecycle" 36 37 // InstallChaincodeFuncName is the chaincode function name used to install 38 // a chaincode 39 InstallChaincodeFuncName = "InstallChaincode" 40 41 // QueryInstalledChaincodeFuncName is the chaincode function name used to 42 // query an installed chaincode 43 QueryInstalledChaincodeFuncName = "QueryInstalledChaincode" 44 45 // QueryInstalledChaincodesFuncName is the chaincode function name used to 46 // query all installed chaincodes 47 QueryInstalledChaincodesFuncName = "QueryInstalledChaincodes" 48 49 // ApproveChaincodeDefinitionForMyOrgFuncName is the chaincode function name 50 // used to approve a chaincode definition for execution by the user's own org 51 ApproveChaincodeDefinitionForMyOrgFuncName = "ApproveChaincodeDefinitionForMyOrg" 52 53 // CheckCommitReadinessFuncName is the chaincode function name used to check 54 // a specified chaincode definition is ready to be committed. It returns the 55 // approval status for a given definition over a given set of orgs 56 CheckCommitReadinessFuncName = "CheckCommitReadiness" 57 58 // CommitChaincodeDefinitionFuncName is the chaincode function name used to 59 // 'commit' (previously 'instantiate') a chaincode in a channel. 60 CommitChaincodeDefinitionFuncName = "CommitChaincodeDefinition" 61 62 // QueryChaincodeDefinitionFuncName is the chaincode function name used to 63 // query a committed chaincode definition in a channel. 64 QueryChaincodeDefinitionFuncName = "QueryChaincodeDefinition" 65 66 // QueryChaincodeDefinitionsFuncName is the chaincode function name used to 67 // query the committed chaincode definitions in a channel. 68 QueryChaincodeDefinitionsFuncName = "QueryChaincodeDefinitions" 69 ) 70 71 // SCCFunctions provides a backing implementation with concrete arguments 72 // for each of the SCC functions 73 type SCCFunctions interface { 74 // InstallChaincode persists a chaincode definition to disk 75 InstallChaincode([]byte) (*chaincode.InstalledChaincode, error) 76 77 // QueryInstalledChaincode returns metadata for the chaincode with the supplied package ID. 78 QueryInstalledChaincode(packageID string) (*chaincode.InstalledChaincode, error) 79 80 // GetInstalledChaincodePackage returns the chaincode package 81 // installed on the peer as bytes. 82 GetInstalledChaincodePackage(packageID string) ([]byte, error) 83 84 // QueryInstalledChaincodes returns the currently installed chaincodes 85 QueryInstalledChaincodes() []*chaincode.InstalledChaincode 86 87 // ApproveChaincodeDefinitionForOrg records a chaincode definition into this org's implicit collection. 88 ApproveChaincodeDefinitionForOrg(chname, ccname string, cd *ChaincodeDefinition, packageID string, publicState ReadableState, orgState ReadWritableState) error 89 90 // CheckCommitReadiness returns a map containing the orgs 91 // whose orgStates were supplied and whether or not they have approved 92 // the specified definition. 93 CheckCommitReadiness(chname, ccname string, cd *ChaincodeDefinition, publicState ReadWritableState, orgStates []OpaqueState) (map[string]bool, error) 94 95 // CommitChaincodeDefinition records a new chaincode definition into the 96 // public state and returns a map containing the orgs whose orgStates 97 // were supplied and whether or not they have approved the definition. 98 CommitChaincodeDefinition(chname, ccname string, cd *ChaincodeDefinition, publicState ReadWritableState, orgStates []OpaqueState) (map[string]bool, error) 99 100 // QueryChaincodeDefinition returns a chaincode definition from the public 101 // state. 102 QueryChaincodeDefinition(name string, publicState ReadableState) (*ChaincodeDefinition, error) 103 104 // QueryOrgApprovals returns a map containing the orgs whose orgStates were 105 // supplied and whether or not they have approved a chaincode definition with 106 // the specified parameters. 107 QueryOrgApprovals(name string, cd *ChaincodeDefinition, orgStates []OpaqueState) (map[string]bool, error) 108 109 // QueryNamespaceDefinitions returns all defined namespaces 110 QueryNamespaceDefinitions(publicState RangeableState) (map[string]string, error) 111 } 112 113 //go:generate counterfeiter -o mock/channel_config_source.go --fake-name ChannelConfigSource . ChannelConfigSource 114 115 // ChannelConfigSource provides a way to retrieve the channel config for a given 116 // channel ID. 117 type ChannelConfigSource interface { 118 // GetStableChannelConfig returns the channel config for a given channel id. 119 // Note, it is a stable bundle, which means it will not be updated, even if 120 // the channel is, so it should be discarded after use. 121 GetStableChannelConfig(channelID string) channelconfig.Resources 122 } 123 124 //go:generate counterfeiter -o mock/queryexecutor_provider.go --fake-name QueryExecutorProvider . QueryExecutorProvider 125 126 // QueryExecutorProvider provides a way to retrieve the query executor assosciated with an invocation 127 type QueryExecutorProvider interface { 128 TxQueryExecutor(channelID, txID string) ledger.SimpleQueryExecutor 129 } 130 131 // SCC implements the required methods to satisfy the chaincode interface. 132 // It routes the invocation calls to the backing implementations. 133 type SCC struct { 134 OrgMSPID string 135 136 ACLProvider aclmgmt.ACLProvider 137 138 ChannelConfigSource ChannelConfigSource 139 140 DeployedCCInfoProvider ledger.DeployedChaincodeInfoProvider 141 QueryExecutorProvider QueryExecutorProvider 142 143 // Functions provides the backing implementation of lifecycle. 144 Functions SCCFunctions 145 146 // Dispatcher handles the rote protobuf boilerplate for unmarshaling/marshaling 147 // the inputs and outputs of the SCC functions. 148 Dispatcher *dispatcher.Dispatcher 149 } 150 151 // Name returns "_lifecycle" 152 func (scc *SCC) Name() string { 153 return LifecycleNamespace 154 } 155 156 // Chaincode returns a reference to itself 157 func (scc *SCC) Chaincode() shim.Chaincode { 158 return scc 159 } 160 161 // Init is mostly useless for system chaincodes and always returns success 162 func (scc *SCC) Init(stub shim.ChaincodeStubInterface) pb.Response { 163 return shim.Success(nil) 164 } 165 166 // Invoke takes chaincode invocation arguments and routes them to the correct 167 // underlying lifecycle operation. All functions take a single argument of 168 // type marshaled lb.<FunctionName>Args and return a marshaled lb.<FunctionName>Result 169 func (scc *SCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response { 170 args := stub.GetArgs() 171 if len(args) == 0 { 172 return shim.Error("lifecycle scc must be invoked with arguments") 173 } 174 175 if len(args) != 2 { 176 return shim.Error(fmt.Sprintf("lifecycle scc operations require exactly two arguments but received %d", len(args))) 177 } 178 179 var ac channelconfig.Application 180 var channelID string 181 if channelID = stub.GetChannelID(); channelID != "" { 182 channelConfig := scc.ChannelConfigSource.GetStableChannelConfig(channelID) 183 if channelConfig == nil { 184 return shim.Error(fmt.Sprintf("could not get channelconfig for channel '%s'", channelID)) 185 } 186 var ok bool 187 ac, ok = channelConfig.ApplicationConfig() 188 if !ok { 189 return shim.Error(fmt.Sprintf("could not get application config for channel '%s'", channelID)) 190 } 191 if !ac.Capabilities().LifecycleV20() { 192 return shim.Error(fmt.Sprintf("cannot use new lifecycle for channel '%s' as it does not have the required capabilities enabled", channelID)) 193 } 194 } 195 196 // Handle ACL: 197 sp, err := stub.GetSignedProposal() 198 if err != nil { 199 return shim.Error(fmt.Sprintf("Failed getting signed proposal from stub: [%s]", err)) 200 } 201 202 err = scc.ACLProvider.CheckACL(fmt.Sprintf("%s/%s", LifecycleNamespace, args[0]), stub.GetChannelID(), sp) 203 if err != nil { 204 return shim.Error(fmt.Sprintf("Failed to authorize invocation due to failed ACL check: %s", err)) 205 } 206 207 outputBytes, err := scc.Dispatcher.Dispatch( 208 args[1], 209 string(args[0]), 210 &Invocation{ 211 ChannelID: channelID, 212 ApplicationConfig: ac, 213 SCC: scc, 214 Stub: stub, 215 }, 216 ) 217 if err != nil { 218 switch err.(type) { 219 case ErrNamespaceNotDefined, persistence.CodePackageNotFoundErr: 220 return pb.Response{ 221 Status: 404, 222 Message: err.Error(), 223 } 224 default: 225 return shim.Error(fmt.Sprintf("failed to invoke backing implementation of '%s': %s", string(args[0]), err.Error())) 226 } 227 } 228 229 return shim.Success(outputBytes) 230 } 231 232 type Invocation struct { 233 ChannelID string 234 ApplicationConfig channelconfig.Application // Note this may be nil 235 Stub shim.ChaincodeStubInterface 236 SCC *SCC 237 } 238 239 // InstallChaincode is a SCC function that may be dispatched to which routes 240 // to the underlying lifecycle implementation. 241 func (i *Invocation) InstallChaincode(input *lb.InstallChaincodeArgs) (proto.Message, error) { 242 if logger.IsEnabledFor(zapcore.DebugLevel) { 243 end := 35 244 if len(input.ChaincodeInstallPackage) < end { 245 end = len(input.ChaincodeInstallPackage) 246 } 247 248 // the first tens of bytes contain the (compressed) portion 249 // of the package metadata and so they'll be different across 250 // different packages, acting as a package fingerprint useful 251 // to identify various packages from the content 252 packageFingerprint := input.ChaincodeInstallPackage[0:end] 253 logger.Debugf("received invocation of InstallChaincode for install package %x...", 254 packageFingerprint, 255 ) 256 } 257 258 installedCC, err := i.SCC.Functions.InstallChaincode(input.ChaincodeInstallPackage) 259 if err != nil { 260 return nil, err 261 } 262 263 return &lb.InstallChaincodeResult{ 264 Label: installedCC.Label, 265 PackageId: installedCC.PackageID, 266 }, nil 267 } 268 269 // QueryInstalledChaincode is a SCC function that may be dispatched to which 270 // routes to the underlying lifecycle implementation. 271 func (i *Invocation) QueryInstalledChaincode(input *lb.QueryInstalledChaincodeArgs) (proto.Message, error) { 272 logger.Debugf("received invocation of QueryInstalledChaincode for install package ID '%s'", 273 input.PackageId, 274 ) 275 276 chaincode, err := i.SCC.Functions.QueryInstalledChaincode(input.PackageId) 277 if err != nil { 278 return nil, err 279 } 280 281 references := map[string]*lb.QueryInstalledChaincodeResult_References{} 282 for channel, chaincodeMetadata := range chaincode.References { 283 chaincodes := make([]*lb.QueryInstalledChaincodeResult_Chaincode, len(chaincodeMetadata)) 284 for i, metadata := range chaincodeMetadata { 285 chaincodes[i] = &lb.QueryInstalledChaincodeResult_Chaincode{ 286 Name: metadata.Name, 287 Version: metadata.Version, 288 } 289 } 290 291 references[channel] = &lb.QueryInstalledChaincodeResult_References{ 292 Chaincodes: chaincodes, 293 } 294 } 295 296 return &lb.QueryInstalledChaincodeResult{ 297 Label: chaincode.Label, 298 PackageId: chaincode.PackageID, 299 References: references, 300 }, nil 301 } 302 303 // GetInstalledChaincodePackage is a SCC function that may be dispatched to 304 // which routes to the underlying lifecycle implementation. 305 func (i *Invocation) GetInstalledChaincodePackage(input *lb.GetInstalledChaincodePackageArgs) (proto.Message, error) { 306 logger.Debugf("received invocation of GetInstalledChaincodePackage") 307 308 pkgBytes, err := i.SCC.Functions.GetInstalledChaincodePackage(input.PackageId) 309 if err != nil { 310 return nil, err 311 } 312 313 return &lb.GetInstalledChaincodePackageResult{ 314 ChaincodeInstallPackage: pkgBytes, 315 }, nil 316 } 317 318 // QueryInstalledChaincodes is a SCC function that may be dispatched to which 319 // routes to the underlying lifecycle implementation. 320 func (i *Invocation) QueryInstalledChaincodes(input *lb.QueryInstalledChaincodesArgs) (proto.Message, error) { 321 logger.Debugf("received invocation of QueryInstalledChaincodes") 322 323 chaincodes := i.SCC.Functions.QueryInstalledChaincodes() 324 325 result := &lb.QueryInstalledChaincodesResult{} 326 for _, chaincode := range chaincodes { 327 references := map[string]*lb.QueryInstalledChaincodesResult_References{} 328 for channel, chaincodeMetadata := range chaincode.References { 329 chaincodes := make([]*lb.QueryInstalledChaincodesResult_Chaincode, len(chaincodeMetadata)) 330 for i, metadata := range chaincodeMetadata { 331 chaincodes[i] = &lb.QueryInstalledChaincodesResult_Chaincode{ 332 Name: metadata.Name, 333 Version: metadata.Version, 334 } 335 } 336 337 references[channel] = &lb.QueryInstalledChaincodesResult_References{ 338 Chaincodes: chaincodes, 339 } 340 } 341 342 result.InstalledChaincodes = append(result.InstalledChaincodes, 343 &lb.QueryInstalledChaincodesResult_InstalledChaincode{ 344 Label: chaincode.Label, 345 PackageId: chaincode.PackageID, 346 References: references, 347 }) 348 } 349 350 return result, nil 351 } 352 353 // ApproveChaincodeDefinitionForMyOrg is a SCC function that may be dispatched 354 // to which routes to the underlying lifecycle implementation. 355 func (i *Invocation) ApproveChaincodeDefinitionForMyOrg(input *lb.ApproveChaincodeDefinitionForMyOrgArgs) (proto.Message, error) { 356 if err := i.validateInput(input.Name, input.Version, input.Collections); err != nil { 357 return nil, err 358 } 359 collectionName := ImplicitCollectionNameForOrg(i.SCC.OrgMSPID) 360 var collectionConfig []*pb.CollectionConfig 361 if input.Collections != nil { 362 collectionConfig = input.Collections.Config 363 } 364 365 var packageID string 366 if input.Source != nil { 367 switch source := input.Source.Type.(type) { 368 case *lb.ChaincodeSource_LocalPackage: 369 packageID = source.LocalPackage.PackageId 370 case *lb.ChaincodeSource_Unavailable_: 371 default: 372 } 373 } 374 375 cd := &ChaincodeDefinition{ 376 Sequence: input.Sequence, 377 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 378 Version: input.Version, 379 EndorsementPlugin: input.EndorsementPlugin, 380 InitRequired: input.InitRequired, 381 }, 382 ValidationInfo: &lb.ChaincodeValidationInfo{ 383 ValidationPlugin: input.ValidationPlugin, 384 ValidationParameter: input.ValidationParameter, 385 }, 386 Collections: &pb.CollectionConfigPackage{ 387 Config: collectionConfig, 388 }, 389 } 390 391 logger.Debugf("received invocation of ApproveChaincodeDefinitionForMyOrg on channel '%s' for definition '%s'", 392 i.Stub.GetChannelID(), 393 cd, 394 ) 395 396 if err := i.SCC.Functions.ApproveChaincodeDefinitionForOrg( 397 i.Stub.GetChannelID(), 398 input.Name, 399 cd, 400 packageID, 401 i.Stub, 402 &ChaincodePrivateLedgerShim{ 403 Collection: collectionName, 404 Stub: i.Stub, 405 }, 406 ); err != nil { 407 return nil, err 408 } 409 return &lb.ApproveChaincodeDefinitionForMyOrgResult{}, nil 410 } 411 412 // CheckCommitReadiness is a SCC function that may be dispatched 413 // to the underlying lifecycle implementation. 414 func (i *Invocation) CheckCommitReadiness(input *lb.CheckCommitReadinessArgs) (proto.Message, error) { 415 opaqueStates, err := i.createOpaqueStates() 416 if err != nil { 417 return nil, err 418 } 419 420 cd := &ChaincodeDefinition{ 421 Sequence: input.Sequence, 422 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 423 Version: input.Version, 424 EndorsementPlugin: input.EndorsementPlugin, 425 InitRequired: input.InitRequired, 426 }, 427 ValidationInfo: &lb.ChaincodeValidationInfo{ 428 ValidationPlugin: input.ValidationPlugin, 429 ValidationParameter: input.ValidationParameter, 430 }, 431 Collections: input.Collections, 432 } 433 434 logger.Debugf("received invocation of CheckCommitReadiness on channel '%s' for definition '%s'", 435 i.Stub.GetChannelID(), 436 cd, 437 ) 438 439 approvals, err := i.SCC.Functions.CheckCommitReadiness( 440 i.Stub.GetChannelID(), 441 input.Name, 442 cd, 443 i.Stub, 444 opaqueStates, 445 ) 446 if err != nil { 447 return nil, err 448 } 449 450 return &lb.CheckCommitReadinessResult{ 451 Approvals: approvals, 452 }, nil 453 } 454 455 // CommitChaincodeDefinition is a SCC function that may be dispatched 456 // to which routes to the underlying lifecycle implementation. 457 func (i *Invocation) CommitChaincodeDefinition(input *lb.CommitChaincodeDefinitionArgs) (proto.Message, error) { 458 if err := i.validateInput(input.Name, input.Version, input.Collections); err != nil { 459 return nil, err 460 } 461 462 if i.ApplicationConfig == nil { 463 return nil, errors.Errorf("no application config for channel '%s'", i.Stub.GetChannelID()) 464 } 465 466 orgs := i.ApplicationConfig.Organizations() 467 opaqueStates := make([]OpaqueState, 0, len(orgs)) 468 var myOrg string 469 for _, org := range orgs { 470 opaqueStates = append(opaqueStates, &ChaincodePrivateLedgerShim{ 471 Collection: ImplicitCollectionNameForOrg(org.MSPID()), 472 Stub: i.Stub, 473 }) 474 if org.MSPID() == i.SCC.OrgMSPID { 475 myOrg = i.SCC.OrgMSPID 476 } 477 } 478 479 if myOrg == "" { 480 return nil, errors.Errorf("impossibly, this peer's org is processing requests for a channel it is not a member of") 481 } 482 483 cd := &ChaincodeDefinition{ 484 Sequence: input.Sequence, 485 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 486 Version: input.Version, 487 EndorsementPlugin: input.EndorsementPlugin, 488 InitRequired: input.InitRequired, 489 }, 490 ValidationInfo: &lb.ChaincodeValidationInfo{ 491 ValidationPlugin: input.ValidationPlugin, 492 ValidationParameter: input.ValidationParameter, 493 }, 494 Collections: input.Collections, 495 } 496 497 logger.Debugf("received invocation of CommitChaincodeDefinition on channel '%s' for definition '%s'", 498 i.Stub.GetChannelID(), 499 cd, 500 ) 501 502 approvals, err := i.SCC.Functions.CommitChaincodeDefinition( 503 i.Stub.GetChannelID(), 504 input.Name, 505 cd, 506 i.Stub, 507 opaqueStates, 508 ) 509 if err != nil { 510 return nil, err 511 } 512 513 if !approvals[myOrg] { 514 return nil, errors.Errorf("chaincode definition not agreed to by this org (%s)", i.SCC.OrgMSPID) 515 } 516 517 logger.Infof("Successfully endorsed commit for chaincode name '%s' on channel '%s' with definition {%s}", input.Name, i.Stub.GetChannelID(), cd) 518 519 return &lb.CommitChaincodeDefinitionResult{}, nil 520 } 521 522 // QueryChaincodeDefinition is a SCC function that may be dispatched 523 // to which routes to the underlying lifecycle implementation. 524 func (i *Invocation) QueryChaincodeDefinition(input *lb.QueryChaincodeDefinitionArgs) (proto.Message, error) { 525 logger.Debugf("received invocation of QueryChaincodeDefinition on channel '%s' for chaincode '%s'", 526 i.Stub.GetChannelID(), 527 input.Name, 528 ) 529 530 definedChaincode, err := i.SCC.Functions.QueryChaincodeDefinition(input.Name, i.Stub) 531 if err != nil { 532 return nil, err 533 } 534 535 opaqueStates, err := i.createOpaqueStates() 536 if err != nil { 537 return nil, err 538 } 539 540 var approvals map[string]bool 541 if approvals, err = i.SCC.Functions.QueryOrgApprovals(input.Name, definedChaincode, opaqueStates); err != nil { 542 return nil, err 543 } 544 545 return &lb.QueryChaincodeDefinitionResult{ 546 Sequence: definedChaincode.Sequence, 547 Version: definedChaincode.EndorsementInfo.Version, 548 EndorsementPlugin: definedChaincode.EndorsementInfo.EndorsementPlugin, 549 ValidationPlugin: definedChaincode.ValidationInfo.ValidationPlugin, 550 ValidationParameter: definedChaincode.ValidationInfo.ValidationParameter, 551 InitRequired: definedChaincode.EndorsementInfo.InitRequired, 552 Collections: definedChaincode.Collections, 553 Approvals: approvals, 554 }, nil 555 } 556 557 // QueryChaincodeDefinitions is a SCC function that may be dispatched 558 // to which routes to the underlying lifecycle implementation. 559 func (i *Invocation) QueryChaincodeDefinitions(input *lb.QueryChaincodeDefinitionsArgs) (proto.Message, error) { 560 logger.Debugf("received invocation of QueryChaincodeDefinitions on channel '%s'", 561 i.Stub.GetChannelID(), 562 ) 563 564 namespaces, err := i.SCC.Functions.QueryNamespaceDefinitions(&ChaincodePublicLedgerShim{ChaincodeStubInterface: i.Stub}) 565 if err != nil { 566 return nil, err 567 } 568 569 chaincodeDefinitions := []*lb.QueryChaincodeDefinitionsResult_ChaincodeDefinition{} 570 for namespace, nType := range namespaces { 571 if nType == FriendlyChaincodeDefinitionType { 572 definedChaincode, err := i.SCC.Functions.QueryChaincodeDefinition(namespace, i.Stub) 573 if err != nil { 574 return nil, err 575 } 576 577 chaincodeDefinitions = append(chaincodeDefinitions, &lb.QueryChaincodeDefinitionsResult_ChaincodeDefinition{ 578 Name: namespace, 579 Sequence: definedChaincode.Sequence, 580 Version: definedChaincode.EndorsementInfo.Version, 581 EndorsementPlugin: definedChaincode.EndorsementInfo.EndorsementPlugin, 582 ValidationPlugin: definedChaincode.ValidationInfo.ValidationPlugin, 583 ValidationParameter: definedChaincode.ValidationInfo.ValidationParameter, 584 InitRequired: definedChaincode.EndorsementInfo.InitRequired, 585 Collections: definedChaincode.Collections, 586 }) 587 } 588 } 589 590 return &lb.QueryChaincodeDefinitionsResult{ 591 ChaincodeDefinitions: chaincodeDefinitions, 592 }, nil 593 } 594 595 var ( 596 // NOTE the chaincode name/version regular expressions should stay in sync 597 // with those defined in core/scc/lscc/lscc.go until LSCC has been removed. 598 ChaincodeNameRegExp = regexp.MustCompile("^[a-zA-Z0-9]+([-_][a-zA-Z0-9]+)*$") 599 ChaincodeVersionRegExp = regexp.MustCompile("^[A-Za-z0-9_.+-]+$") 600 601 collectionNameRegExp = regexp.MustCompile("^[A-Za-z0-9-]+([A-Za-z0-9_-]+)*$") 602 603 // currently defined system chaincode names that shouldn't 604 // be allowed as user-defined chaincode names 605 systemChaincodeNames = map[string]struct{}{ 606 "cscc": {}, 607 "escc": {}, 608 "lscc": {}, 609 "qscc": {}, 610 "vscc": {}, 611 } 612 ) 613 614 func (i *Invocation) validateInput(name, version string, collections *pb.CollectionConfigPackage) error { 615 if !ChaincodeNameRegExp.MatchString(name) { 616 return errors.Errorf("invalid chaincode name '%s'. Names can only consist of alphanumerics, '_', and '-' and can only begin with alphanumerics", name) 617 } 618 if _, ok := systemChaincodeNames[name]; ok { 619 return errors.Errorf("chaincode name '%s' is the name of a system chaincode", name) 620 } 621 622 if !ChaincodeVersionRegExp.MatchString(version) { 623 return errors.Errorf("invalid chaincode version '%s'. Versions can only consist of alphanumerics, '_', '-', '+', and '.'", version) 624 } 625 626 collConfigs, err := extractStaticCollectionConfigs(collections) 627 if err != nil { 628 return err 629 } 630 // we extract the channel config to check whether the supplied collection configuration 631 // complies to the given msp configuration and performs semantic validation. 632 // Channel config may change afterwards (i.e., after endorsement or commit of this transaction). 633 // Fabric will deal with the situation where some collection configs are no longer meaningful. 634 // Therefore, the use of channel config for verifying during endorsement is more 635 // towards catching manual errors in the config as oppose to any attempt of serializability. 636 channelConfig := i.SCC.ChannelConfigSource.GetStableChannelConfig(i.ChannelID) 637 if channelConfig == nil { 638 return errors.Errorf("could not get channelconfig for channel '%s'", i.ChannelID) 639 } 640 mspMgr := channelConfig.MSPManager() 641 if mspMgr == nil { 642 return errors.Errorf("could not get MSP manager for channel '%s'", i.ChannelID) 643 } 644 645 if err := validateCollectionConfigs(collConfigs, mspMgr); err != nil { 646 return err 647 } 648 649 // validate against collection configs in the committed definition 650 qe := i.SCC.QueryExecutorProvider.TxQueryExecutor(i.Stub.GetChannelID(), i.Stub.GetTxID()) 651 committedCCDef, err := i.SCC.DeployedCCInfoProvider.ChaincodeInfo(i.ChannelID, name, qe) 652 if err != nil { 653 return errors.Wrapf(err, "could not retrieve committed definition for chaincode '%s'", name) 654 } 655 if committedCCDef == nil { 656 return nil 657 } 658 if err := validateCollConfigsAgainstCommittedDef(collConfigs, committedCCDef.ExplicitCollectionConfigPkg); err != nil { 659 return err 660 } 661 return nil 662 } 663 664 func extractStaticCollectionConfigs(collConfigPkg *pb.CollectionConfigPackage) ([]*pb.StaticCollectionConfig, error) { 665 if collConfigPkg == nil || len(collConfigPkg.Config) == 0 { 666 return nil, nil 667 } 668 collConfigs := make([]*pb.StaticCollectionConfig, len(collConfigPkg.Config)) 669 for i, c := range collConfigPkg.Config { 670 switch t := c.Payload.(type) { 671 case *pb.CollectionConfig_StaticCollectionConfig: 672 collConfig := t.StaticCollectionConfig 673 if collConfig == nil { 674 return nil, errors.Errorf("collection configuration is empty") 675 } 676 collConfigs[i] = collConfig 677 default: 678 // this should only occur if a developer has added a new 679 // collection config type 680 return nil, errors.Errorf("collection config contains unexpected payload type: %T", t) 681 } 682 } 683 return collConfigs, nil 684 } 685 686 func validateCollectionConfigs(collConfigs []*pb.StaticCollectionConfig, mspMgr msp.MSPManager) error { 687 if len(collConfigs) == 0 { 688 return nil 689 } 690 collNamesMap := map[string]struct{}{} 691 // Process each collection config from a set of collection configs 692 for _, c := range collConfigs { 693 if !collectionNameRegExp.MatchString(c.Name) { 694 return errors.Errorf("invalid collection name '%s'. Names can only consist of alphanumerics, '_', and '-' and cannot begin with '_'", 695 c.Name) 696 } 697 // Ensure that there are no duplicate collection names 698 if _, ok := collNamesMap[c.Name]; ok { 699 return errors.Errorf("collection-name: %s -- found duplicate in collection configuration", 700 c.Name) 701 } 702 collNamesMap[c.Name] = struct{}{} 703 // Validate gossip related parameters present in the collection config 704 if c.MaximumPeerCount < c.RequiredPeerCount { 705 return errors.Errorf("collection-name: %s -- maximum peer count (%d) cannot be less than the required peer count (%d)", 706 c.Name, c.MaximumPeerCount, c.RequiredPeerCount) 707 } 708 if c.RequiredPeerCount < 0 { 709 return errors.Errorf("collection-name: %s -- requiredPeerCount (%d) cannot be less than zero", 710 c.Name, c.RequiredPeerCount) 711 } 712 if err := validateCollectionConfigMemberOrgsPolicy(c, mspMgr); err != nil { 713 return err 714 } 715 } 716 return nil 717 } 718 719 // validateCollectionConfigAgainstMsp checks whether the supplied collection configuration 720 // complies to the given msp configuration 721 func validateCollectionConfigMemberOrgsPolicy(coll *pb.StaticCollectionConfig, mspMgr msp.MSPManager) error { 722 if coll.MemberOrgsPolicy == nil { 723 return errors.Errorf("collection member policy is not set for collection '%s'", coll.Name) 724 } 725 if coll.MemberOrgsPolicy.GetSignaturePolicy() == nil { 726 return errors.Errorf("collection member org policy is empty for collection '%s'", coll.Name) 727 } 728 729 // calling this constructor ensures extra semantic validation for the policy 730 pp := &cauthdsl.EnvelopeBasedPolicyProvider{Deserializer: mspMgr} 731 if _, err := pp.NewPolicy(coll.MemberOrgsPolicy.GetSignaturePolicy()); err != nil { 732 return errors.WithMessagef(err, "invalid member org policy for collection '%s'", coll.Name) 733 } 734 735 // make sure that the signature policy is meaningful (only consists of ORs) 736 if err := validateSpOrConcat(coll.MemberOrgsPolicy.GetSignaturePolicy().Rule); err != nil { 737 return errors.WithMessagef(err, "collection-name: %s -- error in member org policy", coll.Name) 738 } 739 740 msps, err := mspMgr.GetMSPs() 741 if err != nil { 742 return errors.Wrapf(err, "could not get MSPs") 743 } 744 745 // make sure that the orgs listed are actually part of the channel 746 // check all principals in the signature policy 747 for _, principal := range coll.MemberOrgsPolicy.GetSignaturePolicy().Identities { 748 var orgID string 749 // the member org policy only supports certain principal types 750 switch principal.PrincipalClassification { 751 752 case mspprotos.MSPPrincipal_ROLE: 753 msprole := &mspprotos.MSPRole{} 754 err := proto.Unmarshal(principal.Principal, msprole) 755 if err != nil { 756 return errors.Wrapf(err, "collection-name: %s -- cannot unmarshal identity bytes into MSPRole", coll.GetName()) 757 } 758 orgID = msprole.MspIdentifier 759 // the msp map is indexed using msp IDs - this behavior is implementation specific, making the following check a bit of a hack 760 _, ok := msps[orgID] 761 if !ok { 762 return errors.Errorf("collection-name: %s -- collection member '%s' is not part of the channel", coll.GetName(), orgID) 763 } 764 765 case mspprotos.MSPPrincipal_ORGANIZATION_UNIT: 766 mspou := &mspprotos.OrganizationUnit{} 767 err := proto.Unmarshal(principal.Principal, mspou) 768 if err != nil { 769 return errors.Wrapf(err, "collection-name: %s -- cannot unmarshal identity bytes into OrganizationUnit", coll.GetName()) 770 } 771 orgID = mspou.MspIdentifier 772 // the msp map is indexed using msp IDs - this behavior is implementation specific, making the following check a bit of a hack 773 _, ok := msps[orgID] 774 if !ok { 775 return errors.Errorf("collection-name: %s -- collection member '%s' is not part of the channel", coll.GetName(), orgID) 776 } 777 778 case mspprotos.MSPPrincipal_IDENTITY: 779 if _, err := mspMgr.DeserializeIdentity(principal.Principal); err != nil { 780 return errors.Errorf("collection-name: %s -- contains an identity that is not part of the channel", coll.GetName()) 781 } 782 783 default: 784 return errors.Errorf("collection-name: %s -- principal type %v is not supported", coll.GetName(), principal.PrincipalClassification) 785 } 786 } 787 return nil 788 } 789 790 // validateSpOrConcat checks if the supplied signature policy is just an OR-concatenation of identities 791 func validateSpOrConcat(sp *common.SignaturePolicy) error { 792 if sp.GetNOutOf() == nil { 793 return nil 794 } 795 // check if N == 1 (OR concatenation) 796 if sp.GetNOutOf().N != 1 { 797 return errors.Errorf("signature policy is not an OR concatenation, NOutOf %d", sp.GetNOutOf().N) 798 } 799 // recurse into all sub-rules 800 for _, rule := range sp.GetNOutOf().Rules { 801 err := validateSpOrConcat(rule) 802 if err != nil { 803 return err 804 } 805 } 806 return nil 807 } 808 809 func validateCollConfigsAgainstCommittedDef( 810 proposedCollConfs []*pb.StaticCollectionConfig, 811 committedCollConfPkg *pb.CollectionConfigPackage, 812 ) error { 813 if committedCollConfPkg == nil || len(committedCollConfPkg.Config) == 0 { 814 return nil 815 } 816 817 if len(proposedCollConfs) == 0 { 818 return errors.Errorf("the proposed collection config does not contain previously defined collections") 819 } 820 821 proposedCollsMap := map[string]*pb.StaticCollectionConfig{} 822 for _, c := range proposedCollConfs { 823 proposedCollsMap[c.Name] = c 824 } 825 826 // In the new collection config package, ensure that there is one entry per old collection. Any 827 // number of new collections are allowed. 828 for _, committedCollConfig := range committedCollConfPkg.Config { 829 committedColl := committedCollConfig.GetStaticCollectionConfig() 830 // It cannot be nil 831 if committedColl == nil { 832 return errors.Errorf("unknown collection configuration type") 833 } 834 835 newCollection, ok := proposedCollsMap[committedColl.Name] 836 if !ok { 837 return errors.Errorf("existing collection [%s] missing in the proposed collection configuration", committedColl.Name) 838 } 839 840 if newCollection.BlockToLive != committedColl.BlockToLive { 841 return errors.Errorf("the BlockToLive in an existing collection [%s] modified. Existing value [%d]", committedColl.Name, committedColl.BlockToLive) 842 } 843 } 844 return nil 845 } 846 847 func (i *Invocation) createOpaqueStates() ([]OpaqueState, error) { 848 if i.ApplicationConfig == nil { 849 return nil, errors.Errorf("no application config for channel '%s'", i.Stub.GetChannelID()) 850 } 851 orgs := i.ApplicationConfig.Organizations() 852 opaqueStates := make([]OpaqueState, 0, len(orgs)) 853 for _, org := range orgs { 854 opaqueStates = append(opaqueStates, &ChaincodePrivateLedgerShim{ 855 Collection: ImplicitCollectionNameForOrg(org.MSPID()), 856 Stub: i.Stub, 857 }) 858 } 859 return opaqueStates, nil 860 }