github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+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 return &lb.CommitChaincodeDefinitionResult{}, nil 518 } 519 520 // QueryChaincodeDefinition is a SCC function that may be dispatched 521 // to which routes to the underlying lifecycle implementation. 522 func (i *Invocation) QueryChaincodeDefinition(input *lb.QueryChaincodeDefinitionArgs) (proto.Message, error) { 523 logger.Debugf("received invocation of QueryChaincodeDefinition on channel '%s' for chaincode '%s'", 524 i.Stub.GetChannelID(), 525 input.Name, 526 ) 527 528 definedChaincode, err := i.SCC.Functions.QueryChaincodeDefinition(input.Name, i.Stub) 529 if err != nil { 530 return nil, err 531 } 532 533 opaqueStates, err := i.createOpaqueStates() 534 if err != nil { 535 return nil, err 536 } 537 538 var approvals map[string]bool 539 if approvals, err = i.SCC.Functions.QueryOrgApprovals(input.Name, definedChaincode, opaqueStates); err != nil { 540 return nil, err 541 } 542 543 return &lb.QueryChaincodeDefinitionResult{ 544 Sequence: definedChaincode.Sequence, 545 Version: definedChaincode.EndorsementInfo.Version, 546 EndorsementPlugin: definedChaincode.EndorsementInfo.EndorsementPlugin, 547 ValidationPlugin: definedChaincode.ValidationInfo.ValidationPlugin, 548 ValidationParameter: definedChaincode.ValidationInfo.ValidationParameter, 549 InitRequired: definedChaincode.EndorsementInfo.InitRequired, 550 Collections: definedChaincode.Collections, 551 Approvals: approvals, 552 }, nil 553 } 554 555 // QueryChaincodeDefinitions is a SCC function that may be dispatched 556 // to which routes to the underlying lifecycle implementation. 557 func (i *Invocation) QueryChaincodeDefinitions(input *lb.QueryChaincodeDefinitionsArgs) (proto.Message, error) { 558 logger.Debugf("received invocation of QueryChaincodeDefinitions on channel '%s'", 559 i.Stub.GetChannelID(), 560 ) 561 562 namespaces, err := i.SCC.Functions.QueryNamespaceDefinitions(&ChaincodePublicLedgerShim{ChaincodeStubInterface: i.Stub}) 563 if err != nil { 564 return nil, err 565 } 566 567 chaincodeDefinitions := []*lb.QueryChaincodeDefinitionsResult_ChaincodeDefinition{} 568 for namespace, nType := range namespaces { 569 if nType == FriendlyChaincodeDefinitionType { 570 definedChaincode, err := i.SCC.Functions.QueryChaincodeDefinition(namespace, i.Stub) 571 if err != nil { 572 return nil, err 573 } 574 575 chaincodeDefinitions = append(chaincodeDefinitions, &lb.QueryChaincodeDefinitionsResult_ChaincodeDefinition{ 576 Name: namespace, 577 Sequence: definedChaincode.Sequence, 578 Version: definedChaincode.EndorsementInfo.Version, 579 EndorsementPlugin: definedChaincode.EndorsementInfo.EndorsementPlugin, 580 ValidationPlugin: definedChaincode.ValidationInfo.ValidationPlugin, 581 ValidationParameter: definedChaincode.ValidationInfo.ValidationParameter, 582 InitRequired: definedChaincode.EndorsementInfo.InitRequired, 583 Collections: definedChaincode.Collections, 584 }) 585 } 586 } 587 588 return &lb.QueryChaincodeDefinitionsResult{ 589 ChaincodeDefinitions: chaincodeDefinitions, 590 }, nil 591 } 592 593 var ( 594 // NOTE the chaincode name/version regular expressions should stay in sync 595 // with those defined in core/scc/lscc/lscc.go until LSCC has been removed. 596 ChaincodeNameRegExp = regexp.MustCompile("^[a-zA-Z0-9]+([-_][a-zA-Z0-9]+)*$") 597 ChaincodeVersionRegExp = regexp.MustCompile("^[A-Za-z0-9_.+-]+$") 598 599 collectionNameRegExp = regexp.MustCompile("^[A-Za-z0-9-]+([A-Za-z0-9_-]+)*$") 600 601 // currently defined system chaincode names that shouldn't 602 // be allowed as user-defined chaincode names 603 systemChaincodeNames = map[string]struct{}{ 604 "cscc": {}, 605 "escc": {}, 606 "lscc": {}, 607 "qscc": {}, 608 "vscc": {}, 609 } 610 ) 611 612 func (i *Invocation) validateInput(name, version string, collections *pb.CollectionConfigPackage) error { 613 if !ChaincodeNameRegExp.MatchString(name) { 614 return errors.Errorf("invalid chaincode name '%s'. Names can only consist of alphanumerics, '_', and '-' and can only begin with alphanumerics", name) 615 } 616 if _, ok := systemChaincodeNames[name]; ok { 617 return errors.Errorf("chaincode name '%s' is the name of a system chaincode", name) 618 } 619 620 if !ChaincodeVersionRegExp.MatchString(version) { 621 return errors.Errorf("invalid chaincode version '%s'. Versions can only consist of alphanumerics, '_', '-', '+', and '.'", version) 622 } 623 624 collConfigs, err := extractStaticCollectionConfigs(collections) 625 if err != nil { 626 return err 627 } 628 // we extract the channel config to check whether the supplied collection configuration 629 // complies to the given msp configuration and performs semantic validation. 630 // Channel config may change afterwards (i.e., after endorsement or commit of this transaction). 631 // Fabric will deal with the situation where some collection configs are no longer meaningful. 632 // Therefore, the use of channel config for verifying during endorsement is more 633 // towards catching manual errors in the config as oppose to any attempt of serializability. 634 channelConfig := i.SCC.ChannelConfigSource.GetStableChannelConfig(i.ChannelID) 635 if channelConfig == nil { 636 return errors.Errorf("could not get channelconfig for channel '%s'", i.ChannelID) 637 } 638 mspMgr := channelConfig.MSPManager() 639 if mspMgr == nil { 640 return errors.Errorf("could not get MSP manager for channel '%s'", i.ChannelID) 641 } 642 643 if err := validateCollectionConfigs(collConfigs, mspMgr); err != nil { 644 return err 645 } 646 647 // validate against collection configs in the committed definition 648 qe := i.SCC.QueryExecutorProvider.TxQueryExecutor(i.Stub.GetChannelID(), i.Stub.GetTxID()) 649 committedCCDef, err := i.SCC.DeployedCCInfoProvider.ChaincodeInfo(i.ChannelID, name, qe) 650 if err != nil { 651 return errors.Wrapf(err, "could not retrieve committed definition for chaincode '%s'", name) 652 } 653 if committedCCDef == nil { 654 return nil 655 } 656 if err := validateCollConfigsAgainstCommittedDef(collConfigs, committedCCDef.ExplicitCollectionConfigPkg); err != nil { 657 return err 658 } 659 return nil 660 } 661 662 func extractStaticCollectionConfigs(collConfigPkg *pb.CollectionConfigPackage) ([]*pb.StaticCollectionConfig, error) { 663 if collConfigPkg == nil || len(collConfigPkg.Config) == 0 { 664 return nil, nil 665 } 666 collConfigs := make([]*pb.StaticCollectionConfig, len(collConfigPkg.Config)) 667 for i, c := range collConfigPkg.Config { 668 switch t := c.Payload.(type) { 669 case *pb.CollectionConfig_StaticCollectionConfig: 670 collConfig := t.StaticCollectionConfig 671 if collConfig == nil { 672 return nil, errors.Errorf("collection configuration is empty") 673 } 674 collConfigs[i] = collConfig 675 default: 676 // this should only occur if a developer has added a new 677 // collection config type 678 return nil, errors.Errorf("collection config contains unexpected payload type: %T", t) 679 } 680 } 681 return collConfigs, nil 682 } 683 684 func validateCollectionConfigs(collConfigs []*pb.StaticCollectionConfig, mspMgr msp.MSPManager) error { 685 if len(collConfigs) == 0 { 686 return nil 687 } 688 collNamesMap := map[string]struct{}{} 689 // Process each collection config from a set of collection configs 690 for _, c := range collConfigs { 691 if !collectionNameRegExp.MatchString(c.Name) { 692 return errors.Errorf("invalid collection name '%s'. Names can only consist of alphanumerics, '_', and '-' and cannot begin with '_'", 693 c.Name) 694 } 695 // Ensure that there are no duplicate collection names 696 if _, ok := collNamesMap[c.Name]; ok { 697 return errors.Errorf("collection-name: %s -- found duplicate in collection configuration", 698 c.Name) 699 } 700 collNamesMap[c.Name] = struct{}{} 701 // Validate gossip related parameters present in the collection config 702 if c.MaximumPeerCount < c.RequiredPeerCount { 703 return errors.Errorf("collection-name: %s -- maximum peer count (%d) cannot be less than the required peer count (%d)", 704 c.Name, c.MaximumPeerCount, c.RequiredPeerCount) 705 } 706 if c.RequiredPeerCount < 0 { 707 return errors.Errorf("collection-name: %s -- requiredPeerCount (%d) cannot be less than zero", 708 c.Name, c.RequiredPeerCount) 709 } 710 if err := validateCollectionConfigMemberOrgsPolicy(c, mspMgr); err != nil { 711 return err 712 } 713 } 714 return nil 715 } 716 717 // validateCollectionConfigAgainstMsp checks whether the supplied collection configuration 718 // complies to the given msp configuration 719 func validateCollectionConfigMemberOrgsPolicy(coll *pb.StaticCollectionConfig, mspMgr msp.MSPManager) error { 720 if coll.MemberOrgsPolicy == nil { 721 return errors.Errorf("collection member policy is not set for collection '%s'", coll.Name) 722 } 723 if coll.MemberOrgsPolicy.GetSignaturePolicy() == nil { 724 return errors.Errorf("collection member org policy is empty for collection '%s'", coll.Name) 725 } 726 727 // calling this constructor ensures extra semantic validation for the policy 728 pp := &cauthdsl.EnvelopeBasedPolicyProvider{Deserializer: mspMgr} 729 if _, err := pp.NewPolicy(coll.MemberOrgsPolicy.GetSignaturePolicy()); err != nil { 730 return errors.WithMessagef(err, "invalid member org policy for collection '%s'", coll.Name) 731 } 732 733 // make sure that the signature policy is meaningful (only consists of ORs) 734 if err := validateSpOrConcat(coll.MemberOrgsPolicy.GetSignaturePolicy().Rule); err != nil { 735 return errors.WithMessagef(err, "collection-name: %s -- error in member org policy", coll.Name) 736 } 737 738 msps, err := mspMgr.GetMSPs() 739 if err != nil { 740 return errors.Wrapf(err, "could not get MSPs") 741 } 742 743 // make sure that the orgs listed are actually part of the channel 744 // check all principals in the signature policy 745 for _, principal := range coll.MemberOrgsPolicy.GetSignaturePolicy().Identities { 746 var orgID string 747 // the member org policy only supports certain principal types 748 switch principal.PrincipalClassification { 749 750 case mspprotos.MSPPrincipal_ROLE: 751 msprole := &mspprotos.MSPRole{} 752 err := proto.Unmarshal(principal.Principal, msprole) 753 if err != nil { 754 return errors.Wrapf(err, "collection-name: %s -- cannot unmarshal identity bytes into MSPRole", coll.GetName()) 755 } 756 orgID = msprole.MspIdentifier 757 // the msp map is indexed using msp IDs - this behavior is implementation specific, making the following check a bit of a hack 758 _, ok := msps[orgID] 759 if !ok { 760 return errors.Errorf("collection-name: %s -- collection member '%s' is not part of the channel", coll.GetName(), orgID) 761 } 762 763 case mspprotos.MSPPrincipal_ORGANIZATION_UNIT: 764 mspou := &mspprotos.OrganizationUnit{} 765 err := proto.Unmarshal(principal.Principal, mspou) 766 if err != nil { 767 return errors.Wrapf(err, "collection-name: %s -- cannot unmarshal identity bytes into OrganizationUnit", coll.GetName()) 768 } 769 orgID = mspou.MspIdentifier 770 // the msp map is indexed using msp IDs - this behavior is implementation specific, making the following check a bit of a hack 771 _, ok := msps[orgID] 772 if !ok { 773 return errors.Errorf("collection-name: %s -- collection member '%s' is not part of the channel", coll.GetName(), orgID) 774 } 775 776 case mspprotos.MSPPrincipal_IDENTITY: 777 if _, err := mspMgr.DeserializeIdentity(principal.Principal); err != nil { 778 return errors.Errorf("collection-name: %s -- contains an identity that is not part of the channel", coll.GetName()) 779 } 780 781 default: 782 return errors.Errorf("collection-name: %s -- principal type %v is not supported", coll.GetName(), principal.PrincipalClassification) 783 } 784 } 785 return nil 786 } 787 788 // validateSpOrConcat checks if the supplied signature policy is just an OR-concatenation of identities 789 func validateSpOrConcat(sp *common.SignaturePolicy) error { 790 if sp.GetNOutOf() == nil { 791 return nil 792 } 793 // check if N == 1 (OR concatenation) 794 if sp.GetNOutOf().N != 1 { 795 return errors.Errorf("signature policy is not an OR concatenation, NOutOf %d", sp.GetNOutOf().N) 796 } 797 // recurse into all sub-rules 798 for _, rule := range sp.GetNOutOf().Rules { 799 err := validateSpOrConcat(rule) 800 if err != nil { 801 return err 802 } 803 } 804 return nil 805 } 806 807 func validateCollConfigsAgainstCommittedDef( 808 proposedCollConfs []*pb.StaticCollectionConfig, 809 committedCollConfPkg *pb.CollectionConfigPackage, 810 ) error { 811 if committedCollConfPkg == nil || len(committedCollConfPkg.Config) == 0 { 812 return nil 813 } 814 815 if len(proposedCollConfs) == 0 { 816 return errors.Errorf("the proposed collection config does not contain previously defined collections") 817 } 818 819 proposedCollsMap := map[string]*pb.StaticCollectionConfig{} 820 for _, c := range proposedCollConfs { 821 proposedCollsMap[c.Name] = c 822 } 823 824 // In the new collection config package, ensure that there is one entry per old collection. Any 825 // number of new collections are allowed. 826 for _, committedCollConfig := range committedCollConfPkg.Config { 827 committedColl := committedCollConfig.GetStaticCollectionConfig() 828 // It cannot be nil 829 if committedColl == nil { 830 return errors.Errorf("unknown collection configuration type") 831 } 832 833 newCollection, ok := proposedCollsMap[committedColl.Name] 834 if !ok { 835 return errors.Errorf("existing collection [%s] missing in the proposed collection configuration", committedColl.Name) 836 } 837 838 if newCollection.BlockToLive != committedColl.BlockToLive { 839 return errors.Errorf("the BlockToLive in an existing collection [%s] modified. Existing value [%d]", committedColl.Name, committedColl.BlockToLive) 840 } 841 } 842 return nil 843 } 844 845 func (i *Invocation) createOpaqueStates() ([]OpaqueState, error) { 846 if i.ApplicationConfig == nil { 847 return nil, errors.Errorf("no application config for channel '%s'", i.Stub.GetChannelID()) 848 } 849 orgs := i.ApplicationConfig.Organizations() 850 opaqueStates := make([]OpaqueState, 0, len(orgs)) 851 for _, org := range orgs { 852 opaqueStates = append(opaqueStates, &ChaincodePrivateLedgerShim{ 853 Collection: ImplicitCollectionNameForOrg(org.MSPID()), 854 Stub: i.Stub, 855 }) 856 } 857 return opaqueStates, nil 858 }