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