github.com/lzy4123/fabric@v2.1.1+incompatible/core/handlers/validation/builtin/v12/validation_logic.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package v12 8 9 import ( 10 "bytes" 11 "fmt" 12 "regexp" 13 14 "github.com/golang/protobuf/proto" 15 "github.com/hyperledger/fabric-protos-go/common" 16 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 17 "github.com/hyperledger/fabric-protos-go/msp" 18 pb "github.com/hyperledger/fabric-protos-go/peer" 19 commonerrors "github.com/hyperledger/fabric/common/errors" 20 "github.com/hyperledger/fabric/common/flogging" 21 "github.com/hyperledger/fabric/core/common/ccprovider" 22 "github.com/hyperledger/fabric/core/common/privdata" 23 vc "github.com/hyperledger/fabric/core/handlers/validation/api/capabilities" 24 vi "github.com/hyperledger/fabric/core/handlers/validation/api/identities" 25 vp "github.com/hyperledger/fabric/core/handlers/validation/api/policies" 26 vs "github.com/hyperledger/fabric/core/handlers/validation/api/state" 27 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil" 28 "github.com/hyperledger/fabric/core/scc/lscc" 29 "github.com/hyperledger/fabric/protoutil" 30 "github.com/pkg/errors" 31 ) 32 33 var ( 34 logger = flogging.MustGetLogger("vscc") 35 36 // currently defined system chaincode names that shouldn't 37 // be allowed as user-defined chaincode names 38 systemChaincodeNames = map[string]struct{}{ 39 "cscc": {}, 40 "escc": {}, 41 "lscc": {}, 42 "qscc": {}, 43 "vscc": {}, 44 } 45 ) 46 47 const ( 48 DUPLICATED_IDENTITY_ERROR = "Endorsement policy evaluation failure might be caused by duplicated identities" 49 ) 50 51 const AllowedCharsCollectionName = "[A-Za-z0-9_-]+" 52 53 var validCollectionNameRegex = regexp.MustCompile(AllowedCharsCollectionName) 54 55 //go:generate mockery -dir . -name Capabilities -case underscore -output mocks/ 56 57 // Capabilities is the local interface that used to generate mocks for foreign interface. 58 type Capabilities interface { 59 vc.Capabilities 60 } 61 62 //go:generate mockery -dir . -name StateFetcher -case underscore -output mocks/ 63 64 // StateFetcher is the local interface that used to generate mocks for foreign interface. 65 type StateFetcher interface { 66 vs.StateFetcher 67 } 68 69 //go:generate mockery -dir . -name IdentityDeserializer -case underscore -output mocks/ 70 71 // IdentityDeserializer is the local interface that used to generate mocks for foreign interface. 72 type IdentityDeserializer interface { 73 vi.IdentityDeserializer 74 } 75 76 //go:generate mockery -dir . -name PolicyEvaluator -case underscore -output mocks/ 77 78 // PolicyEvaluator is the local interface that used to generate mocks for foreign interface. 79 type PolicyEvaluator interface { 80 vp.PolicyEvaluator 81 } 82 83 // New creates a new instance of the default VSCC 84 // Typically this will only be invoked once per peer. 85 func New(c vc.Capabilities, s vs.StateFetcher, d vi.IdentityDeserializer, pe vp.PolicyEvaluator) *Validator { 86 return &Validator{ 87 capabilities: c, 88 stateFetcher: s, 89 deserializer: d, 90 policyEvaluator: pe, 91 } 92 } 93 94 // Validator implements the default transaction validation policy, 95 // which is to check the correctness of the read-write set and the endorsement 96 // signatures against an endorsement policy that is supplied as argument to 97 // every invoke. 98 type Validator struct { 99 deserializer vi.IdentityDeserializer 100 capabilities vc.Capabilities 101 stateFetcher vs.StateFetcher 102 policyEvaluator vp.PolicyEvaluator 103 } 104 105 // Validate validates the given envelope corresponding to a transaction with an endorsement 106 // policy as given in its serialized form. 107 func (vscc *Validator) Validate( 108 block *common.Block, 109 namespace string, 110 txPosition int, 111 actionPosition int, 112 policyBytes []byte, 113 ) commonerrors.TxValidationError { 114 // get the envelope... 115 env, err := protoutil.GetEnvelopeFromBlock(block.Data.Data[txPosition]) 116 if err != nil { 117 logger.Errorf("VSCC error: GetEnvelope failed, err %s", err) 118 return policyErr(err) 119 } 120 121 // ...and the payload... 122 payl, err := protoutil.UnmarshalPayload(env.Payload) 123 if err != nil { 124 logger.Errorf("VSCC error: GetPayload failed, err %s", err) 125 return policyErr(err) 126 } 127 128 chdr, err := protoutil.UnmarshalChannelHeader(payl.Header.ChannelHeader) 129 if err != nil { 130 return policyErr(err) 131 } 132 133 // validate the payload type 134 if common.HeaderType(chdr.Type) != common.HeaderType_ENDORSER_TRANSACTION { 135 logger.Errorf("Only Endorser Transactions are supported, provided type %d", chdr.Type) 136 return policyErr(fmt.Errorf("Only Endorser Transactions are supported, provided type %d", chdr.Type)) 137 } 138 139 // ...and the transaction... 140 tx, err := protoutil.UnmarshalTransaction(payl.Data) 141 if err != nil { 142 logger.Errorf("VSCC error: GetTransaction failed, err %s", err) 143 return policyErr(err) 144 } 145 146 cap, err := protoutil.UnmarshalChaincodeActionPayload(tx.Actions[actionPosition].Payload) 147 if err != nil { 148 logger.Errorf("VSCC error: GetChaincodeActionPayload failed, err %s", err) 149 return policyErr(err) 150 } 151 152 signatureSet, err := vscc.deduplicateIdentity(cap) 153 if err != nil { 154 return policyErr(err) 155 } 156 157 // evaluate the signature set against the policy 158 err = vscc.policyEvaluator.Evaluate(policyBytes, signatureSet) 159 if err != nil { 160 logger.Warningf("Endorsement policy failure for transaction txid=%s, err: %s", chdr.GetTxId(), err.Error()) 161 if len(signatureSet) < len(cap.Action.Endorsements) { 162 // Warning: duplicated identities exist, endorsement failure might be cause by this reason 163 return policyErr(errors.New(DUPLICATED_IDENTITY_ERROR)) 164 } 165 return policyErr(fmt.Errorf("VSCC error: endorsement policy failure, err: %s", err)) 166 } 167 168 // do some extra validation that is specific to lscc 169 if namespace == "lscc" { 170 logger.Debugf("VSCC info: doing special validation for LSCC") 171 err := vscc.ValidateLSCCInvocation(chdr.ChannelId, env, cap, payl, vscc.capabilities) 172 if err != nil { 173 logger.Errorf("VSCC error: ValidateLSCCInvocation failed, err %s", err) 174 return err 175 } 176 } 177 178 return nil 179 } 180 181 // checkInstantiationPolicy evaluates an instantiation policy against a signed proposal. 182 func (vscc *Validator) checkInstantiationPolicy(chainName string, env *common.Envelope, instantiationPolicy []byte, payl *common.Payload) commonerrors.TxValidationError { 183 // get the signature header 184 shdr, err := protoutil.UnmarshalSignatureHeader(payl.Header.SignatureHeader) 185 if err != nil { 186 return policyErr(err) 187 } 188 189 // construct signed data we can evaluate the instantiation policy against 190 sd := []*protoutil.SignedData{{ 191 Data: env.Payload, 192 Identity: shdr.Creator, 193 Signature: env.Signature, 194 }} 195 err = vscc.policyEvaluator.Evaluate(instantiationPolicy, sd) 196 if err != nil { 197 return policyErr(fmt.Errorf("chaincode instantiation policy violated, error %s", err)) 198 } 199 return nil 200 } 201 202 func validateNewCollectionConfigs(newCollectionConfigs []*pb.CollectionConfig) error { 203 newCollectionsMap := make(map[string]bool, len(newCollectionConfigs)) 204 // Process each collection config from a set of collection configs 205 for _, newCollectionConfig := range newCollectionConfigs { 206 207 newCollection := newCollectionConfig.GetStaticCollectionConfig() 208 if newCollection == nil { 209 return errors.New("unknown collection configuration type") 210 } 211 212 // Ensure that there are no duplicate collection names 213 collectionName := newCollection.GetName() 214 215 if err := validateCollectionName(collectionName); err != nil { 216 return err 217 } 218 219 if _, ok := newCollectionsMap[collectionName]; !ok { 220 newCollectionsMap[collectionName] = true 221 } else { 222 return fmt.Errorf("collection-name: %s -- found duplicate collection configuration", collectionName) 223 } 224 225 // Validate gossip related parameters present in the collection config 226 maximumPeerCount := newCollection.GetMaximumPeerCount() 227 requiredPeerCount := newCollection.GetRequiredPeerCount() 228 if maximumPeerCount < requiredPeerCount { 229 return fmt.Errorf("collection-name: %s -- maximum peer count (%d) cannot be less than the required peer count (%d)", 230 collectionName, maximumPeerCount, requiredPeerCount) 231 232 } 233 if requiredPeerCount < 0 { 234 return fmt.Errorf("collection-name: %s -- requiredPeerCount (%d) cannot be less than zero", 235 collectionName, requiredPeerCount) 236 237 } 238 239 // make sure that the signature policy is meaningful (only consists of ORs) 240 err := validateSpOrConcat(newCollection.MemberOrgsPolicy.GetSignaturePolicy().Rule) 241 if err != nil { 242 return errors.WithMessagef(err, "collection-name: %s -- error in member org policy", collectionName) 243 } 244 } 245 return nil 246 } 247 248 // validateSpOrConcat checks if the supplied signature policy is just an OR-concatenation of identities. 249 func validateSpOrConcat(sp *common.SignaturePolicy) error { 250 if sp.GetNOutOf() == nil { 251 return nil 252 } 253 // check if N == 1 (OR concatenation) 254 if sp.GetNOutOf().N != 1 { 255 return errors.New(fmt.Sprintf("signature policy is not an OR concatenation, NOutOf %d", sp.GetNOutOf().N)) 256 } 257 // recurse into all sub-rules 258 for _, rule := range sp.GetNOutOf().Rules { 259 err := validateSpOrConcat(rule) 260 if err != nil { 261 return err 262 } 263 } 264 return nil 265 } 266 267 func checkForMissingCollections(newCollectionsMap map[string]*pb.StaticCollectionConfig, oldCollectionConfigs []*pb.CollectionConfig, 268 ) error { 269 var missingCollections []string 270 271 // In the new collection config package, ensure that there is one entry per old collection. Any 272 // number of new collections are allowed. 273 for _, oldCollectionConfig := range oldCollectionConfigs { 274 275 oldCollection := oldCollectionConfig.GetStaticCollectionConfig() 276 // It cannot be nil 277 if oldCollection == nil { 278 return policyErr(fmt.Errorf("unknown collection configuration type")) 279 } 280 281 // All old collection must exist in the new collection config package 282 oldCollectionName := oldCollection.GetName() 283 _, ok := newCollectionsMap[oldCollectionName] 284 if !ok { 285 missingCollections = append(missingCollections, oldCollectionName) 286 } 287 } 288 289 if len(missingCollections) > 0 { 290 return policyErr(fmt.Errorf("the following existing collections are missing in the new collection configuration package: %v", 291 missingCollections)) 292 } 293 294 return nil 295 } 296 297 func checkForModifiedCollectionsBTL(newCollectionsMap map[string]*pb.StaticCollectionConfig, oldCollectionConfigs []*pb.CollectionConfig, 298 ) error { 299 var modifiedCollectionsBTL []string 300 301 // In the new collection config package, ensure that the block to live value is not 302 // modified for the existing collections. 303 for _, oldCollectionConfig := range oldCollectionConfigs { 304 305 oldCollection := oldCollectionConfig.GetStaticCollectionConfig() 306 // It cannot be nil 307 if oldCollection == nil { 308 return policyErr(fmt.Errorf("unknown collection configuration type")) 309 } 310 311 oldCollectionName := oldCollection.GetName() 312 newCollection, _ := newCollectionsMap[oldCollectionName] 313 // BlockToLive cannot be changed 314 if newCollection.GetBlockToLive() != oldCollection.GetBlockToLive() { 315 modifiedCollectionsBTL = append(modifiedCollectionsBTL, oldCollectionName) 316 } 317 } 318 319 if len(modifiedCollectionsBTL) > 0 { 320 return policyErr(fmt.Errorf("the BlockToLive in the following existing collections must not be modified: %v", 321 modifiedCollectionsBTL)) 322 } 323 324 return nil 325 } 326 327 func validateNewCollectionConfigsAgainstOld(newCollectionConfigs []*pb.CollectionConfig, oldCollectionConfigs []*pb.CollectionConfig, 328 ) error { 329 newCollectionsMap := make(map[string]*pb.StaticCollectionConfig, len(newCollectionConfigs)) 330 331 for _, newCollectionConfig := range newCollectionConfigs { 332 newCollection := newCollectionConfig.GetStaticCollectionConfig() 333 // Collection object itself is stored as value so that we can 334 // check whether the block to live is changed -- FAB-7810 335 newCollectionsMap[newCollection.GetName()] = newCollection 336 } 337 338 if err := checkForMissingCollections(newCollectionsMap, oldCollectionConfigs); err != nil { 339 return err 340 } 341 342 if err := checkForModifiedCollectionsBTL(newCollectionsMap, oldCollectionConfigs); err != nil { 343 return err 344 } 345 346 return nil 347 } 348 349 func validateCollectionName(collectionName string) error { 350 if collectionName == "" { 351 return fmt.Errorf("empty collection-name is not allowed") 352 } 353 match := validCollectionNameRegex.FindString(collectionName) 354 if len(match) != len(collectionName) { 355 return fmt.Errorf("collection-name: %s not allowed. A valid collection name follows the pattern: %s", 356 collectionName, AllowedCharsCollectionName) 357 } 358 return nil 359 } 360 361 // validateRWSetAndCollection performs validation of the rwset 362 // of an LSCC deploy operation and then it validates any collection 363 // configuration. 364 func (vscc *Validator) validateRWSetAndCollection( 365 lsccrwset *kvrwset.KVRWSet, 366 cdRWSet *ccprovider.ChaincodeData, 367 lsccArgs [][]byte, 368 lsccFunc string, 369 ac vc.Capabilities, 370 channelName string, 371 ) commonerrors.TxValidationError { 372 /********************************************/ 373 /* security check 0.a - validation of rwset */ 374 /********************************************/ 375 // there can only be one or two writes 376 if len(lsccrwset.Writes) > 2 { 377 return policyErr(fmt.Errorf("LSCC can only issue one or two putState upon deploy")) 378 } 379 380 /**********************************************************/ 381 /* security check 0.b - validation of the collection data */ 382 /**********************************************************/ 383 var collectionsConfigArg []byte 384 if len(lsccArgs) > 5 { 385 collectionsConfigArg = lsccArgs[5] 386 } 387 388 var collectionsConfigLedger []byte 389 if len(lsccrwset.Writes) == 2 { 390 key := privdata.BuildCollectionKVSKey(cdRWSet.Name) 391 if lsccrwset.Writes[1].Key != key { 392 return policyErr(fmt.Errorf("invalid key for the collection of chaincode %s:%s; expected '%s', received '%s'", 393 cdRWSet.Name, cdRWSet.Version, key, lsccrwset.Writes[1].Key)) 394 395 } 396 397 collectionsConfigLedger = lsccrwset.Writes[1].Value 398 } 399 400 if !bytes.Equal(collectionsConfigArg, collectionsConfigLedger) { 401 return policyErr(fmt.Errorf("collection configuration arguments supplied for chaincode %s:%s do not match the configuration in the lscc writeset", 402 cdRWSet.Name, cdRWSet.Version)) 403 404 } 405 406 channelState, err := vscc.stateFetcher.FetchState() 407 if err != nil { 408 return &commonerrors.VSCCExecutionFailureError{Err: fmt.Errorf("failed obtaining query executor: %v", err)} 409 } 410 defer channelState.Done() 411 412 state := &state{channelState} 413 414 // The following condition check added in v1.1 may not be needed as it is not possible to have the chaincodeName~collection key in 415 // the lscc namespace before a chaincode deploy. To avoid forks in v1.2, the following condition is retained. 416 if lsccFunc == lscc.DEPLOY { 417 colCriteria := privdata.CollectionCriteria{Channel: channelName, Namespace: cdRWSet.Name} 418 ccp, err := privdata.RetrieveCollectionConfigPackageFromState(colCriteria, state) 419 if err != nil { 420 // fail if we get any error other than NoSuchCollectionError 421 // because it means something went wrong while looking up the 422 // older collection 423 if _, ok := err.(privdata.NoSuchCollectionError); !ok { 424 return &commonerrors.VSCCExecutionFailureError{Err: fmt.Errorf("unable to check whether collection existed earlier for chaincode %s:%s", 425 cdRWSet.Name, cdRWSet.Version), 426 } 427 } 428 } 429 if ccp != nil { 430 return policyErr(fmt.Errorf("collection data should not exist for chaincode %s:%s", cdRWSet.Name, cdRWSet.Version)) 431 } 432 } 433 434 // TODO: Once the new chaincode lifecycle is available (FAB-8724), the following validation 435 // and other validation performed in ValidateLSCCInvocation can be moved to LSCC itself. 436 newCollectionConfigPackage := &pb.CollectionConfigPackage{} 437 438 if collectionsConfigArg != nil { 439 err := proto.Unmarshal(collectionsConfigArg, newCollectionConfigPackage) 440 if err != nil { 441 return policyErr(fmt.Errorf("invalid collection configuration supplied for chaincode %s:%s", 442 cdRWSet.Name, cdRWSet.Version)) 443 } 444 } else { 445 return nil 446 } 447 448 if ac.V1_2Validation() { 449 newCollectionConfigs := newCollectionConfigPackage.GetConfig() 450 if err := validateNewCollectionConfigs(newCollectionConfigs); err != nil { 451 return policyErr(err) 452 } 453 454 if lsccFunc == lscc.UPGRADE { 455 456 collectionCriteria := privdata.CollectionCriteria{Channel: channelName, Namespace: cdRWSet.Name} 457 // oldCollectionConfigPackage denotes the existing collection config package in the ledger 458 oldCollectionConfigPackage, err := privdata.RetrieveCollectionConfigPackageFromState(collectionCriteria, state) 459 if err != nil { 460 // fail if we get any error other than NoSuchCollectionError 461 // because it means something went wrong while looking up the 462 // older collection 463 if _, ok := err.(privdata.NoSuchCollectionError); !ok { 464 return &commonerrors.VSCCExecutionFailureError{Err: fmt.Errorf("unable to check whether collection existed earlier for chaincode %s:%s: %v", 465 cdRWSet.Name, cdRWSet.Version, err), 466 } 467 } 468 } 469 470 // oldCollectionConfigPackage denotes the existing collection config package in the ledger 471 if oldCollectionConfigPackage != nil { 472 oldCollectionConfigs := oldCollectionConfigPackage.GetConfig() 473 if err := validateNewCollectionConfigsAgainstOld(newCollectionConfigs, oldCollectionConfigs); err != nil { 474 return policyErr(err) 475 } 476 477 } 478 } 479 } 480 481 return nil 482 } 483 484 func (vscc *Validator) ValidateLSCCInvocation( 485 chid string, 486 env *common.Envelope, 487 cap *pb.ChaincodeActionPayload, 488 payl *common.Payload, 489 ac vc.Capabilities, 490 ) commonerrors.TxValidationError { 491 cpp, err := protoutil.UnmarshalChaincodeProposalPayload(cap.ChaincodeProposalPayload) 492 if err != nil { 493 logger.Errorf("VSCC error: GetChaincodeProposalPayload failed, err %s", err) 494 return policyErr(err) 495 } 496 497 cis := &pb.ChaincodeInvocationSpec{} 498 err = proto.Unmarshal(cpp.Input, cis) 499 if err != nil { 500 logger.Errorf("VSCC error: Unmarshal ChaincodeInvocationSpec failed, err %s", err) 501 return policyErr(err) 502 } 503 504 if cis.ChaincodeSpec == nil || 505 cis.ChaincodeSpec.Input == nil || 506 cis.ChaincodeSpec.Input.Args == nil { 507 logger.Errorf("VSCC error: committing invalid vscc invocation") 508 return policyErr(fmt.Errorf("malformed chaincode invocation spec")) 509 } 510 511 lsccFunc := string(cis.ChaincodeSpec.Input.Args[0]) 512 lsccArgs := cis.ChaincodeSpec.Input.Args[1:] 513 514 logger.Debugf("VSCC info: ValidateLSCCInvocation acting on %s %#v", lsccFunc, lsccArgs) 515 516 switch lsccFunc { 517 case lscc.UPGRADE, lscc.DEPLOY: 518 logger.Debugf("VSCC info: validating invocation of lscc function %s on arguments %#v", lsccFunc, lsccArgs) 519 520 if len(lsccArgs) < 2 { 521 return policyErr(fmt.Errorf("Wrong number of arguments for invocation lscc(%s): expected at least 2, received %d", lsccFunc, len(lsccArgs))) 522 } 523 524 if (!ac.PrivateChannelData() && len(lsccArgs) > 5) || 525 (ac.PrivateChannelData() && len(lsccArgs) > 6) { 526 return policyErr(fmt.Errorf("Wrong number of arguments for invocation lscc(%s): received %d", lsccFunc, len(lsccArgs))) 527 } 528 529 cdsArgs, err := protoutil.UnmarshalChaincodeDeploymentSpec(lsccArgs[1]) 530 if err != nil { 531 return policyErr(fmt.Errorf("GetChaincodeDeploymentSpec error %s", err)) 532 } 533 534 if cdsArgs == nil || cdsArgs.ChaincodeSpec == nil || cdsArgs.ChaincodeSpec.ChaincodeId == nil || 535 cap.Action == nil || cap.Action.ProposalResponsePayload == nil { 536 return policyErr(fmt.Errorf("VSCC error: invocation of lscc(%s) does not have appropriate arguments", lsccFunc)) 537 } 538 539 switch cdsArgs.ChaincodeSpec.Type.String() { 540 case "GOLANG", "NODE", "JAVA", "CAR": 541 default: 542 return policyErr(fmt.Errorf("unexpected chaincode spec type: %s", cdsArgs.ChaincodeSpec.Type.String())) 543 } 544 545 // validate chaincode name 546 ccName := cdsArgs.ChaincodeSpec.ChaincodeId.Name 547 // it must comply with the lscc.ChaincodeNameRegExp 548 if !lscc.ChaincodeNameRegExp.MatchString(ccName) { 549 return policyErr(errors.Errorf("invalid chaincode name '%s'", ccName)) 550 } 551 // it can't match the name of one of the system chaincodes 552 if _, in := systemChaincodeNames[ccName]; in { 553 return policyErr(errors.Errorf("chaincode name '%s' is reserved for system chaincodes", ccName)) 554 } 555 556 // validate chaincode version 557 ccVersion := cdsArgs.ChaincodeSpec.ChaincodeId.Version 558 // it must comply with the lscc.ChaincodeVersionRegExp 559 if !lscc.ChaincodeVersionRegExp.MatchString(ccVersion) { 560 return policyErr(errors.Errorf("invalid chaincode version '%s'", ccVersion)) 561 } 562 563 // get the rwset 564 pRespPayload, err := protoutil.UnmarshalProposalResponsePayload(cap.Action.ProposalResponsePayload) 565 if err != nil { 566 return policyErr(fmt.Errorf("GetProposalResponsePayload error %s", err)) 567 } 568 if pRespPayload.Extension == nil { 569 return policyErr(fmt.Errorf("nil pRespPayload.Extension")) 570 } 571 respPayload, err := protoutil.UnmarshalChaincodeAction(pRespPayload.Extension) 572 if err != nil { 573 return policyErr(fmt.Errorf("GetChaincodeAction error %s", err)) 574 } 575 txRWSet := &rwsetutil.TxRwSet{} 576 if err = txRWSet.FromProtoBytes(respPayload.Results); err != nil { 577 return policyErr(fmt.Errorf("txRWSet.FromProtoBytes error %s", err)) 578 } 579 580 // extract the rwset for lscc 581 var lsccrwset *kvrwset.KVRWSet 582 for _, ns := range txRWSet.NsRwSets { 583 logger.Debugf("Namespace %s", ns.NameSpace) 584 if ns.NameSpace == "lscc" { 585 lsccrwset = ns.KvRwSet 586 break 587 } 588 } 589 590 // retrieve from the ledger the entry for the chaincode at hand 591 cdLedger, ccExistsOnLedger, err := vscc.getInstantiatedCC(chid, cdsArgs.ChaincodeSpec.ChaincodeId.Name) 592 if err != nil { 593 return &commonerrors.VSCCExecutionFailureError{Err: err} 594 } 595 596 /******************************************/ 597 /* security check 0 - validation of rwset */ 598 /******************************************/ 599 // there has to be a write-set 600 if lsccrwset == nil { 601 return policyErr(fmt.Errorf("No read write set for lscc was found")) 602 } 603 // there must be at least one write 604 if len(lsccrwset.Writes) < 1 { 605 return policyErr(fmt.Errorf("LSCC must issue at least one single putState upon deploy/upgrade")) 606 } 607 // the first key name must be the chaincode id provided in the deployment spec 608 if lsccrwset.Writes[0].Key != cdsArgs.ChaincodeSpec.ChaincodeId.Name { 609 return policyErr(fmt.Errorf("expected key %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Name, lsccrwset.Writes[0].Key)) 610 } 611 // the value must be a ChaincodeData struct 612 cdRWSet := &ccprovider.ChaincodeData{} 613 err = proto.Unmarshal(lsccrwset.Writes[0].Value, cdRWSet) 614 if err != nil { 615 return policyErr(fmt.Errorf("unmarhsalling of ChaincodeData failed, error %s", err)) 616 } 617 // the chaincode name in the lsccwriteset must match the chaincode name in the deployment spec 618 if cdRWSet.Name != cdsArgs.ChaincodeSpec.ChaincodeId.Name { 619 return policyErr(fmt.Errorf("expected cc name %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Name, cdRWSet.Name)) 620 } 621 // the chaincode version in the lsccwriteset must match the chaincode version in the deployment spec 622 if cdRWSet.Version != cdsArgs.ChaincodeSpec.ChaincodeId.Version { 623 return policyErr(fmt.Errorf("expected cc version %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Version, cdRWSet.Version)) 624 } 625 // it must only write to 2 namespaces: LSCC's and the cc that we are deploying/upgrading 626 for _, ns := range txRWSet.NsRwSets { 627 if ns.NameSpace != "lscc" && ns.NameSpace != cdRWSet.Name && len(ns.KvRwSet.Writes) > 0 { 628 return policyErr(fmt.Errorf("LSCC invocation is attempting to write to namespace %s", ns.NameSpace)) 629 } 630 } 631 632 logger.Debugf("Validating %s for cc %s version %s", lsccFunc, cdRWSet.Name, cdRWSet.Version) 633 634 switch lsccFunc { 635 case lscc.DEPLOY: 636 637 /******************************************************************/ 638 /* security check 1 - cc not in the LCCC table of instantiated cc */ 639 /******************************************************************/ 640 if ccExistsOnLedger { 641 return policyErr(fmt.Errorf("Chaincode %s is already instantiated", cdsArgs.ChaincodeSpec.ChaincodeId.Name)) 642 } 643 644 /****************************************************************************/ 645 /* security check 2 - validation of rwset (and of collections if enabled) */ 646 /****************************************************************************/ 647 if ac.PrivateChannelData() { 648 // do extra validation for collections 649 err := vscc.validateRWSetAndCollection(lsccrwset, cdRWSet, lsccArgs, lsccFunc, ac, chid) 650 if err != nil { 651 return err 652 } 653 } else { 654 // there can only be a single ledger write 655 if len(lsccrwset.Writes) != 1 { 656 return policyErr(fmt.Errorf("LSCC can only issue a single putState upon deploy")) 657 } 658 } 659 660 /*****************************************************/ 661 /* security check 3 - check the instantiation policy */ 662 /*****************************************************/ 663 pol := cdRWSet.InstantiationPolicy 664 if pol == nil { 665 return policyErr(fmt.Errorf("no instantiation policy was specified")) 666 } 667 // FIXME: could we actually pull the cds package from the 668 // file system to verify whether the policy that is specified 669 // here is the same as the one on disk? 670 // PROS: we prevent attacks where the policy is replaced 671 // CONS: this would be a point of non-determinism 672 err := vscc.checkInstantiationPolicy(chid, env, pol, payl) 673 if err != nil { 674 return err 675 } 676 677 case lscc.UPGRADE: 678 /**************************************************************/ 679 /* security check 1 - cc in the LCCC table of instantiated cc */ 680 /**************************************************************/ 681 if !ccExistsOnLedger { 682 return policyErr(fmt.Errorf("Upgrading non-existent chaincode %s", cdsArgs.ChaincodeSpec.ChaincodeId.Name)) 683 } 684 685 /**********************************************************/ 686 /* security check 2 - existing cc's version was different */ 687 /**********************************************************/ 688 if cdLedger.Version == cdsArgs.ChaincodeSpec.ChaincodeId.Version { 689 return policyErr(fmt.Errorf("Existing version of the cc on the ledger (%s) should be different from the upgraded one", cdsArgs.ChaincodeSpec.ChaincodeId.Version)) 690 } 691 692 /****************************************************************************/ 693 /* security check 3 validation of rwset (and of collections if enabled) */ 694 /****************************************************************************/ 695 // Only in v1.2, a collection can be updated during a chaincode upgrade 696 if ac.V1_2Validation() { 697 // do extra validation for collections 698 err := vscc.validateRWSetAndCollection(lsccrwset, cdRWSet, lsccArgs, lsccFunc, ac, chid) 699 if err != nil { 700 return err 701 } 702 } else { 703 // there can only be a single ledger write 704 if len(lsccrwset.Writes) != 1 { 705 return policyErr(fmt.Errorf("LSCC can only issue a single putState upon upgrade")) 706 } 707 } 708 709 /*****************************************************/ 710 /* security check 4 - check the instantiation policy */ 711 /*****************************************************/ 712 pol := cdLedger.InstantiationPolicy 713 if pol == nil { 714 return policyErr(fmt.Errorf("No instantiation policy was specified")) 715 } 716 // FIXME: could we actually pull the cds package from the 717 // file system to verify whether the policy that is specified 718 // here is the same as the one on disk? 719 // PROS: we prevent attacks where the policy is replaced 720 // CONS: this would be a point of non-determinism 721 err := vscc.checkInstantiationPolicy(chid, env, pol, payl) 722 if err != nil { 723 return err 724 } 725 726 /******************************************************************/ 727 /* security check 5 - check the instantiation policy in the rwset */ 728 /******************************************************************/ 729 if ac.V1_1Validation() { 730 polNew := cdRWSet.InstantiationPolicy 731 if polNew == nil { 732 return policyErr(fmt.Errorf("No instantiation policy was specified")) 733 } 734 735 // no point in checking it again if they are the same policy 736 if !bytes.Equal(polNew, pol) { 737 err = vscc.checkInstantiationPolicy(chid, env, polNew, payl) 738 if err != nil { 739 return err 740 } 741 } 742 } 743 } 744 745 // all is good! 746 return nil 747 default: 748 return policyErr(fmt.Errorf("VSCC error: committing an invocation of function %s of lscc is invalid", lsccFunc)) 749 } 750 } 751 752 func (vscc *Validator) getInstantiatedCC(chid, ccid string) (cd *ccprovider.ChaincodeData, exists bool, err error) { 753 qe, err := vscc.stateFetcher.FetchState() 754 if err != nil { 755 err = fmt.Errorf("could not retrieve QueryExecutor for channel %s, error %s", chid, err) 756 return 757 } 758 defer qe.Done() 759 channelState := &state{qe} 760 bytes, err := channelState.GetState("lscc", ccid) 761 if err != nil { 762 err = fmt.Errorf("could not retrieve state for chaincode %s on channel %s, error %s", ccid, chid, err) 763 return 764 } 765 766 if bytes == nil { 767 return 768 } 769 770 cd = &ccprovider.ChaincodeData{} 771 err = proto.Unmarshal(bytes, cd) 772 if err != nil { 773 err = fmt.Errorf("unmarshalling ChaincodeQueryResponse failed, error %s", err) 774 return 775 } 776 777 exists = true 778 return 779 } 780 781 func (vscc *Validator) deduplicateIdentity(cap *pb.ChaincodeActionPayload) ([]*protoutil.SignedData, error) { 782 // this is the first part of the signed message 783 prespBytes := cap.Action.ProposalResponsePayload 784 785 // build the signature set for the evaluation 786 signatureSet := []*protoutil.SignedData{} 787 signatureMap := make(map[string]struct{}) 788 // loop through each of the endorsements and build the signature set 789 for _, endorsement := range cap.Action.Endorsements { 790 //unmarshal endorser bytes 791 serializedIdentity := &msp.SerializedIdentity{} 792 if err := proto.Unmarshal(endorsement.Endorser, serializedIdentity); err != nil { 793 logger.Errorf("Unmarshal endorser error: %s", err) 794 return nil, policyErr(fmt.Errorf("Unmarshal endorser error: %s", err)) 795 } 796 identity := serializedIdentity.Mspid + string(serializedIdentity.IdBytes) 797 if _, ok := signatureMap[identity]; ok { 798 // Endorsement with the same identity has already been added 799 logger.Warningf("Ignoring duplicated identity, Mspid: %s, pem:\n%s", serializedIdentity.Mspid, serializedIdentity.IdBytes) 800 continue 801 } 802 data := make([]byte, len(prespBytes)+len(endorsement.Endorser)) 803 copy(data, prespBytes) 804 copy(data[len(prespBytes):], endorsement.Endorser) 805 signatureSet = append(signatureSet, &protoutil.SignedData{ 806 // set the data that is signed; concatenation of proposal response bytes and endorser ID 807 Data: data, 808 // set the identity that signs the message: it's the endorser 809 Identity: endorsement.Endorser, 810 // set the signature 811 Signature: endorsement.Signature}) 812 signatureMap[identity] = struct{}{} 813 } 814 815 logger.Debugf("Signature set is of size %d out of %d endorsement(s)", len(signatureSet), len(cap.Action.Endorsements)) 816 return signatureSet, nil 817 } 818 819 type state struct { 820 vs.State 821 } 822 823 // GetState retrieves the value for the given key in the given namespace. 824 func (s *state) GetState(namespace string, key string) ([]byte, error) { 825 values, err := s.GetStateMultipleKeys(namespace, []string{key}) 826 if err != nil { 827 return nil, err 828 } 829 if len(values) == 0 { 830 return nil, nil 831 } 832 return values[0], nil 833 } 834 835 func policyErr(err error) *commonerrors.VSCCEndorsementPolicyError { 836 return &commonerrors.VSCCEndorsementPolicyError{ 837 Err: err, 838 } 839 }