github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/handlers/validation/builtin/v12/validation_logic.go (about) 1 /* 2 Copyright hechain. 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 commonerrors "github.com/hechain20/hechain/common/errors" 16 "github.com/hechain20/hechain/common/flogging" 17 "github.com/hechain20/hechain/core/common/ccprovider" 18 "github.com/hechain20/hechain/core/common/privdata" 19 vc "github.com/hechain20/hechain/core/handlers/validation/api/capabilities" 20 vi "github.com/hechain20/hechain/core/handlers/validation/api/identities" 21 vp "github.com/hechain20/hechain/core/handlers/validation/api/policies" 22 vs "github.com/hechain20/hechain/core/handlers/validation/api/state" 23 "github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/rwsetutil" 24 "github.com/hechain20/hechain/core/scc/lscc" 25 "github.com/hechain20/hechain/protoutil" 26 "github.com/hyperledger/fabric-protos-go/common" 27 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 28 "github.com/hyperledger/fabric-protos-go/msp" 29 pb "github.com/hyperledger/fabric-protos-go/peer" 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 if requiredPeerCount < 0 { 233 return fmt.Errorf("collection-name: %s -- requiredPeerCount (%d) cannot be less than zero", 234 collectionName, requiredPeerCount) 235 } 236 237 // make sure that the signature policy is meaningful (only consists of ORs) 238 err := validateSpOrConcat(newCollection.MemberOrgsPolicy.GetSignaturePolicy().Rule) 239 if err != nil { 240 return errors.WithMessagef(err, "collection-name: %s -- error in member org policy", collectionName) 241 } 242 } 243 return nil 244 } 245 246 // validateSpOrConcat checks if the supplied signature policy is just an OR-concatenation of identities. 247 func validateSpOrConcat(sp *common.SignaturePolicy) error { 248 if sp.GetNOutOf() == nil { 249 return nil 250 } 251 // check if N == 1 (OR concatenation) 252 if sp.GetNOutOf().N != 1 { 253 return errors.New(fmt.Sprintf("signature policy is not an OR concatenation, NOutOf %d", sp.GetNOutOf().N)) 254 } 255 // recurse into all sub-rules 256 for _, rule := range sp.GetNOutOf().Rules { 257 err := validateSpOrConcat(rule) 258 if err != nil { 259 return err 260 } 261 } 262 return nil 263 } 264 265 func checkForMissingCollections(newCollectionsMap map[string]*pb.StaticCollectionConfig, oldCollectionConfigs []*pb.CollectionConfig, 266 ) error { 267 var missingCollections []string 268 269 // In the new collection config package, ensure that there is one entry per old collection. Any 270 // number of new collections are allowed. 271 for _, oldCollectionConfig := range oldCollectionConfigs { 272 273 oldCollection := oldCollectionConfig.GetStaticCollectionConfig() 274 // It cannot be nil 275 if oldCollection == nil { 276 return policyErr(fmt.Errorf("unknown collection configuration type")) 277 } 278 279 // All old collection must exist in the new collection config package 280 oldCollectionName := oldCollection.GetName() 281 _, ok := newCollectionsMap[oldCollectionName] 282 if !ok { 283 missingCollections = append(missingCollections, oldCollectionName) 284 } 285 } 286 287 if len(missingCollections) > 0 { 288 return policyErr(fmt.Errorf("the following existing collections are missing in the new collection configuration package: %v", 289 missingCollections)) 290 } 291 292 return nil 293 } 294 295 func checkForModifiedCollectionsBTL(newCollectionsMap map[string]*pb.StaticCollectionConfig, oldCollectionConfigs []*pb.CollectionConfig, 296 ) error { 297 var modifiedCollectionsBTL []string 298 299 // In the new collection config package, ensure that the block to live value is not 300 // modified for the existing collections. 301 for _, oldCollectionConfig := range oldCollectionConfigs { 302 303 oldCollection := oldCollectionConfig.GetStaticCollectionConfig() 304 // It cannot be nil 305 if oldCollection == nil { 306 return policyErr(fmt.Errorf("unknown collection configuration type")) 307 } 308 309 oldCollectionName := oldCollection.GetName() 310 newCollection := newCollectionsMap[oldCollectionName] 311 // BlockToLive cannot be changed 312 if newCollection.GetBlockToLive() != oldCollection.GetBlockToLive() { 313 modifiedCollectionsBTL = append(modifiedCollectionsBTL, oldCollectionName) 314 } 315 } 316 317 if len(modifiedCollectionsBTL) > 0 { 318 return policyErr(fmt.Errorf("the BlockToLive in the following existing collections must not be modified: %v", 319 modifiedCollectionsBTL)) 320 } 321 322 return nil 323 } 324 325 func validateNewCollectionConfigsAgainstOld(newCollectionConfigs []*pb.CollectionConfig, oldCollectionConfigs []*pb.CollectionConfig, 326 ) error { 327 newCollectionsMap := make(map[string]*pb.StaticCollectionConfig, len(newCollectionConfigs)) 328 329 for _, newCollectionConfig := range newCollectionConfigs { 330 newCollection := newCollectionConfig.GetStaticCollectionConfig() 331 // Collection object itself is stored as value so that we can 332 // check whether the block to live is changed -- FAB-7810 333 newCollectionsMap[newCollection.GetName()] = newCollection 334 } 335 336 if err := checkForMissingCollections(newCollectionsMap, oldCollectionConfigs); err != nil { 337 return err 338 } 339 340 if err := checkForModifiedCollectionsBTL(newCollectionsMap, oldCollectionConfigs); err != nil { 341 return err 342 } 343 344 return nil 345 } 346 347 func validateCollectionName(collectionName string) error { 348 if collectionName == "" { 349 return fmt.Errorf("empty collection-name is not allowed") 350 } 351 match := validCollectionNameRegex.FindString(collectionName) 352 if len(match) != len(collectionName) { 353 return fmt.Errorf("collection-name: %s not allowed. A valid collection name follows the pattern: %s", 354 collectionName, AllowedCharsCollectionName) 355 } 356 return nil 357 } 358 359 // validateRWSetAndCollection performs validation of the rwset 360 // of an LSCC deploy operation and then it validates any collection 361 // configuration. 362 func (vscc *Validator) validateRWSetAndCollection( 363 lsccrwset *kvrwset.KVRWSet, 364 cdRWSet *ccprovider.ChaincodeData, 365 lsccArgs [][]byte, 366 lsccFunc string, 367 ac vc.Capabilities, 368 channelName string, 369 ) commonerrors.TxValidationError { 370 /********************************************/ 371 /* security check 0.a - validation of rwset */ 372 /********************************************/ 373 // there can only be one or two writes 374 if len(lsccrwset.Writes) > 2 { 375 return policyErr(fmt.Errorf("LSCC can only issue one or two putState upon deploy")) 376 } 377 378 /**********************************************************/ 379 /* security check 0.b - validation of the collection data */ 380 /**********************************************************/ 381 var collectionsConfigArg []byte 382 if len(lsccArgs) > 5 { 383 collectionsConfigArg = lsccArgs[5] 384 } 385 386 var collectionsConfigLedger []byte 387 if len(lsccrwset.Writes) == 2 { 388 key := privdata.BuildCollectionKVSKey(cdRWSet.Name) 389 if lsccrwset.Writes[1].Key != key { 390 return policyErr(fmt.Errorf("invalid key for the collection of chaincode %s:%s; expected '%s', received '%s'", 391 cdRWSet.Name, cdRWSet.Version, key, lsccrwset.Writes[1].Key)) 392 } 393 394 collectionsConfigLedger = lsccrwset.Writes[1].Value 395 } 396 397 if !bytes.Equal(collectionsConfigArg, collectionsConfigLedger) { 398 return policyErr(fmt.Errorf("collection configuration arguments supplied for chaincode %s:%s do not match the configuration in the lscc writeset", 399 cdRWSet.Name, cdRWSet.Version)) 400 } 401 402 channelState, err := vscc.stateFetcher.FetchState() 403 if err != nil { 404 return &commonerrors.VSCCExecutionFailureError{Err: fmt.Errorf("failed obtaining query executor: %v", err)} 405 } 406 defer channelState.Done() 407 408 state := &state{channelState} 409 410 // The following condition check added in v1.1 may not be needed as it is not possible to have the chaincodeName~collection key in 411 // the lscc namespace before a chaincode deploy. To avoid forks in v1.2, the following condition is retained. 412 if lsccFunc == lscc.DEPLOY { 413 colCriteria := privdata.CollectionCriteria{Channel: channelName, Namespace: cdRWSet.Name} 414 ccp, err := privdata.RetrieveCollectionConfigPackageFromState(colCriteria, state) 415 if err != nil { 416 // fail if we get any error other than NoSuchCollectionError 417 // because it means something went wrong while looking up the 418 // older collection 419 if _, ok := err.(privdata.NoSuchCollectionError); !ok { 420 return &commonerrors.VSCCExecutionFailureError{ 421 Err: fmt.Errorf("unable to check whether collection existed earlier for chaincode %s:%s", 422 cdRWSet.Name, cdRWSet.Version), 423 } 424 } 425 } 426 if ccp != nil { 427 return policyErr(fmt.Errorf("collection data should not exist for chaincode %s:%s", cdRWSet.Name, cdRWSet.Version)) 428 } 429 } 430 431 // TODO: Once the new chaincode lifecycle is available (FAB-8724), the following validation 432 // and other validation performed in ValidateLSCCInvocation can be moved to LSCC itself. 433 newCollectionConfigPackage := &pb.CollectionConfigPackage{} 434 435 if collectionsConfigArg != nil { 436 err := proto.Unmarshal(collectionsConfigArg, newCollectionConfigPackage) 437 if err != nil { 438 return policyErr(fmt.Errorf("invalid collection configuration supplied for chaincode %s:%s", 439 cdRWSet.Name, cdRWSet.Version)) 440 } 441 } else { 442 return nil 443 } 444 445 if ac.V1_2Validation() { 446 newCollectionConfigs := newCollectionConfigPackage.GetConfig() 447 if err := validateNewCollectionConfigs(newCollectionConfigs); err != nil { 448 return policyErr(err) 449 } 450 451 if lsccFunc == lscc.UPGRADE { 452 453 collectionCriteria := privdata.CollectionCriteria{Channel: channelName, Namespace: cdRWSet.Name} 454 // oldCollectionConfigPackage denotes the existing collection config package in the ledger 455 oldCollectionConfigPackage, err := privdata.RetrieveCollectionConfigPackageFromState(collectionCriteria, state) 456 if err != nil { 457 // fail if we get any error other than NoSuchCollectionError 458 // because it means something went wrong while looking up the 459 // older collection 460 if _, ok := err.(privdata.NoSuchCollectionError); !ok { 461 return &commonerrors.VSCCExecutionFailureError{ 462 Err: fmt.Errorf("unable to check whether collection existed earlier for chaincode %s:%s: %v", 463 cdRWSet.Name, cdRWSet.Version, err), 464 } 465 } 466 } 467 468 // oldCollectionConfigPackage denotes the existing collection config package in the ledger 469 if oldCollectionConfigPackage != nil { 470 oldCollectionConfigs := oldCollectionConfigPackage.GetConfig() 471 if err := validateNewCollectionConfigsAgainstOld(newCollectionConfigs, oldCollectionConfigs); err != nil { 472 return policyErr(err) 473 } 474 475 } 476 } 477 } 478 479 return nil 480 } 481 482 func (vscc *Validator) ValidateLSCCInvocation( 483 chid string, 484 env *common.Envelope, 485 cap *pb.ChaincodeActionPayload, 486 payl *common.Payload, 487 ac vc.Capabilities, 488 ) commonerrors.TxValidationError { 489 cpp, err := protoutil.UnmarshalChaincodeProposalPayload(cap.ChaincodeProposalPayload) 490 if err != nil { 491 logger.Errorf("VSCC error: GetChaincodeProposalPayload failed, err %s", err) 492 return policyErr(err) 493 } 494 495 cis := &pb.ChaincodeInvocationSpec{} 496 err = proto.Unmarshal(cpp.Input, cis) 497 if err != nil { 498 logger.Errorf("VSCC error: Unmarshal ChaincodeInvocationSpec failed, err %s", err) 499 return policyErr(err) 500 } 501 502 if cis.ChaincodeSpec == nil || 503 cis.ChaincodeSpec.Input == nil || 504 cis.ChaincodeSpec.Input.Args == nil { 505 logger.Errorf("VSCC error: committing invalid vscc invocation") 506 return policyErr(fmt.Errorf("malformed chaincode invocation spec")) 507 } 508 509 lsccFunc := string(cis.ChaincodeSpec.Input.Args[0]) 510 lsccArgs := cis.ChaincodeSpec.Input.Args[1:] 511 512 logger.Debugf("VSCC info: ValidateLSCCInvocation acting on %s %#v", lsccFunc, lsccArgs) 513 514 switch lsccFunc { 515 case lscc.UPGRADE, lscc.DEPLOY: 516 logger.Debugf("VSCC info: validating invocation of lscc function %s on arguments %#v", lsccFunc, lsccArgs) 517 518 if len(lsccArgs) < 2 { 519 return policyErr(fmt.Errorf("Wrong number of arguments for invocation lscc(%s): expected at least 2, received %d", lsccFunc, len(lsccArgs))) 520 } 521 522 if (!ac.PrivateChannelData() && len(lsccArgs) > 5) || 523 (ac.PrivateChannelData() && len(lsccArgs) > 6) { 524 return policyErr(fmt.Errorf("Wrong number of arguments for invocation lscc(%s): received %d", lsccFunc, len(lsccArgs))) 525 } 526 527 cdsArgs, err := protoutil.UnmarshalChaincodeDeploymentSpec(lsccArgs[1]) 528 if err != nil { 529 return policyErr(fmt.Errorf("GetChaincodeDeploymentSpec error %s", err)) 530 } 531 532 if cdsArgs == nil || cdsArgs.ChaincodeSpec == nil || cdsArgs.ChaincodeSpec.ChaincodeId == nil || 533 cap.Action == nil || cap.Action.ProposalResponsePayload == nil { 534 return policyErr(fmt.Errorf("VSCC error: invocation of lscc(%s) does not have appropriate arguments", lsccFunc)) 535 } 536 537 switch cdsArgs.ChaincodeSpec.Type.String() { 538 case "GOLANG", "NODE", "JAVA", "CAR": 539 default: 540 return policyErr(fmt.Errorf("unexpected chaincode spec type: %s", cdsArgs.ChaincodeSpec.Type.String())) 541 } 542 543 // validate chaincode name 544 ccName := cdsArgs.ChaincodeSpec.ChaincodeId.Name 545 // it must comply with the lscc.ChaincodeNameRegExp 546 if !lscc.ChaincodeNameRegExp.MatchString(ccName) { 547 return policyErr(errors.Errorf("invalid chaincode name '%s'", ccName)) 548 } 549 // it can't match the name of one of the system chaincodes 550 if _, in := systemChaincodeNames[ccName]; in { 551 return policyErr(errors.Errorf("chaincode name '%s' is reserved for system chaincodes", ccName)) 552 } 553 554 // validate chaincode version 555 ccVersion := cdsArgs.ChaincodeSpec.ChaincodeId.Version 556 // it must comply with the lscc.ChaincodeVersionRegExp 557 if !lscc.ChaincodeVersionRegExp.MatchString(ccVersion) { 558 return policyErr(errors.Errorf("invalid chaincode version '%s'", ccVersion)) 559 } 560 561 // get the rwset 562 pRespPayload, err := protoutil.UnmarshalProposalResponsePayload(cap.Action.ProposalResponsePayload) 563 if err != nil { 564 return policyErr(fmt.Errorf("GetProposalResponsePayload error %s", err)) 565 } 566 if pRespPayload.Extension == nil { 567 return policyErr(fmt.Errorf("nil pRespPayload.Extension")) 568 } 569 respPayload, err := protoutil.UnmarshalChaincodeAction(pRespPayload.Extension) 570 if err != nil { 571 return policyErr(fmt.Errorf("GetChaincodeAction error %s", err)) 572 } 573 txRWSet := &rwsetutil.TxRwSet{} 574 if err = txRWSet.FromProtoBytes(respPayload.Results); err != nil { 575 return policyErr(fmt.Errorf("txRWSet.FromProtoBytes error %s", err)) 576 } 577 578 // extract the rwset for lscc 579 var lsccrwset *kvrwset.KVRWSet 580 for _, ns := range txRWSet.NsRwSets { 581 logger.Debugf("Namespace %s", ns.NameSpace) 582 if ns.NameSpace == "lscc" { 583 lsccrwset = ns.KvRwSet 584 break 585 } 586 } 587 588 // retrieve from the ledger the entry for the chaincode at hand 589 cdLedger, ccExistsOnLedger, err := vscc.getInstantiatedCC(chid, cdsArgs.ChaincodeSpec.ChaincodeId.Name) 590 if err != nil { 591 return &commonerrors.VSCCExecutionFailureError{Err: err} 592 } 593 594 /******************************************/ 595 /* security check 0 - validation of rwset */ 596 /******************************************/ 597 // there has to be a write-set 598 if lsccrwset == nil { 599 return policyErr(fmt.Errorf("No read write set for lscc was found")) 600 } 601 // there must be at least one write 602 if len(lsccrwset.Writes) < 1 { 603 return policyErr(fmt.Errorf("LSCC must issue at least one single putState upon deploy/upgrade")) 604 } 605 // the first key name must be the chaincode id provided in the deployment spec 606 if lsccrwset.Writes[0].Key != cdsArgs.ChaincodeSpec.ChaincodeId.Name { 607 return policyErr(fmt.Errorf("expected key %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Name, lsccrwset.Writes[0].Key)) 608 } 609 // the value must be a ChaincodeData struct 610 cdRWSet := &ccprovider.ChaincodeData{} 611 err = proto.Unmarshal(lsccrwset.Writes[0].Value, cdRWSet) 612 if err != nil { 613 return policyErr(fmt.Errorf("unmarhsalling of ChaincodeData failed, error %s", err)) 614 } 615 // the chaincode name in the lsccwriteset must match the chaincode name in the deployment spec 616 if cdRWSet.Name != cdsArgs.ChaincodeSpec.ChaincodeId.Name { 617 return policyErr(fmt.Errorf("expected cc name %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Name, cdRWSet.Name)) 618 } 619 // the chaincode version in the lsccwriteset must match the chaincode version in the deployment spec 620 if cdRWSet.Version != cdsArgs.ChaincodeSpec.ChaincodeId.Version { 621 return policyErr(fmt.Errorf("expected cc version %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Version, cdRWSet.Version)) 622 } 623 // it must only write to 2 namespaces: LSCC's and the cc that we are deploying/upgrading 624 for _, ns := range txRWSet.NsRwSets { 625 if ns.NameSpace != "lscc" && ns.NameSpace != cdRWSet.Name && len(ns.KvRwSet.Writes) > 0 { 626 return policyErr(fmt.Errorf("LSCC invocation is attempting to write to namespace %s", ns.NameSpace)) 627 } 628 } 629 630 logger.Debugf("Validating %s for cc %s version %s", lsccFunc, cdRWSet.Name, cdRWSet.Version) 631 632 switch lsccFunc { 633 case lscc.DEPLOY: 634 635 /******************************************************************/ 636 /* security check 1 - cc not in the LCCC table of instantiated cc */ 637 /******************************************************************/ 638 if ccExistsOnLedger { 639 return policyErr(fmt.Errorf("Chaincode %s is already instantiated", cdsArgs.ChaincodeSpec.ChaincodeId.Name)) 640 } 641 642 /****************************************************************************/ 643 /* security check 2 - validation of rwset (and of collections if enabled) */ 644 /****************************************************************************/ 645 if ac.PrivateChannelData() { 646 // do extra validation for collections 647 err := vscc.validateRWSetAndCollection(lsccrwset, cdRWSet, lsccArgs, lsccFunc, ac, chid) 648 if err != nil { 649 return err 650 } 651 } else { 652 // there can only be a single ledger write 653 if len(lsccrwset.Writes) != 1 { 654 return policyErr(fmt.Errorf("LSCC can only issue a single putState upon deploy")) 655 } 656 } 657 658 /*****************************************************/ 659 /* security check 3 - check the instantiation policy */ 660 /*****************************************************/ 661 pol := cdRWSet.InstantiationPolicy 662 if pol == nil { 663 return policyErr(fmt.Errorf("no instantiation policy was specified")) 664 } 665 // FIXME: could we actually pull the cds package from the 666 // file system to verify whether the policy that is specified 667 // here is the same as the one on disk? 668 // PROS: we prevent attacks where the policy is replaced 669 // CONS: this would be a point of non-determinism 670 err := vscc.checkInstantiationPolicy(chid, env, pol, payl) 671 if err != nil { 672 return err 673 } 674 675 case lscc.UPGRADE: 676 /**************************************************************/ 677 /* security check 1 - cc in the LCCC table of instantiated cc */ 678 /**************************************************************/ 679 if !ccExistsOnLedger { 680 return policyErr(fmt.Errorf("Upgrading non-existent chaincode %s", cdsArgs.ChaincodeSpec.ChaincodeId.Name)) 681 } 682 683 /**********************************************************/ 684 /* security check 2 - existing cc's version was different */ 685 /**********************************************************/ 686 if cdLedger.Version == cdsArgs.ChaincodeSpec.ChaincodeId.Version { 687 return policyErr(fmt.Errorf("Existing version of the cc on the ledger (%s) should be different from the upgraded one", cdsArgs.ChaincodeSpec.ChaincodeId.Version)) 688 } 689 690 /****************************************************************************/ 691 /* security check 3 validation of rwset (and of collections if enabled) */ 692 /****************************************************************************/ 693 // Only in v1.2, a collection can be updated during a chaincode upgrade 694 if ac.V1_2Validation() { 695 // do extra validation for collections 696 err := vscc.validateRWSetAndCollection(lsccrwset, cdRWSet, lsccArgs, lsccFunc, ac, chid) 697 if err != nil { 698 return err 699 } 700 } else { 701 // there can only be a single ledger write 702 if len(lsccrwset.Writes) != 1 { 703 return policyErr(fmt.Errorf("LSCC can only issue a single putState upon upgrade")) 704 } 705 } 706 707 /*****************************************************/ 708 /* security check 4 - check the instantiation policy */ 709 /*****************************************************/ 710 pol := cdLedger.InstantiationPolicy 711 if pol == nil { 712 return policyErr(fmt.Errorf("No instantiation policy was specified")) 713 } 714 // FIXME: could we actually pull the cds package from the 715 // file system to verify whether the policy that is specified 716 // here is the same as the one on disk? 717 // PROS: we prevent attacks where the policy is replaced 718 // CONS: this would be a point of non-determinism 719 err := vscc.checkInstantiationPolicy(chid, env, pol, payl) 720 if err != nil { 721 return err 722 } 723 724 /******************************************************************/ 725 /* security check 5 - check the instantiation policy in the rwset */ 726 /******************************************************************/ 727 if ac.V1_1Validation() { 728 polNew := cdRWSet.InstantiationPolicy 729 if polNew == nil { 730 return policyErr(fmt.Errorf("No instantiation policy was specified")) 731 } 732 733 // no point in checking it again if they are the same policy 734 if !bytes.Equal(polNew, pol) { 735 err = vscc.checkInstantiationPolicy(chid, env, polNew, payl) 736 if err != nil { 737 return err 738 } 739 } 740 } 741 } 742 743 // all is good! 744 return nil 745 default: 746 return policyErr(fmt.Errorf("VSCC error: committing an invocation of function %s of lscc is invalid", lsccFunc)) 747 } 748 } 749 750 func (vscc *Validator) getInstantiatedCC(chid, ccid string) (cd *ccprovider.ChaincodeData, exists bool, err error) { 751 qe, err := vscc.stateFetcher.FetchState() 752 if err != nil { 753 err = fmt.Errorf("could not retrieve QueryExecutor for channel %s, error %s", chid, err) 754 return 755 } 756 defer qe.Done() 757 channelState := &state{qe} 758 bytes, err := channelState.GetState("lscc", ccid) 759 if err != nil { 760 err = fmt.Errorf("could not retrieve state for chaincode %s on channel %s, error %s", ccid, chid, err) 761 return 762 } 763 764 if bytes == nil { 765 return 766 } 767 768 cd = &ccprovider.ChaincodeData{} 769 err = proto.Unmarshal(bytes, cd) 770 if err != nil { 771 err = fmt.Errorf("unmarshalling ChaincodeQueryResponse failed, error %s", err) 772 return 773 } 774 775 exists = true 776 return 777 } 778 779 func (vscc *Validator) deduplicateIdentity(cap *pb.ChaincodeActionPayload) ([]*protoutil.SignedData, error) { 780 // this is the first part of the signed message 781 prespBytes := cap.Action.ProposalResponsePayload 782 783 // build the signature set for the evaluation 784 signatureSet := []*protoutil.SignedData{} 785 signatureMap := make(map[string]struct{}) 786 // loop through each of the endorsements and build the signature set 787 for _, endorsement := range cap.Action.Endorsements { 788 // unmarshal endorser bytes 789 serializedIdentity := &msp.SerializedIdentity{} 790 if err := proto.Unmarshal(endorsement.Endorser, serializedIdentity); err != nil { 791 logger.Errorf("Unmarshal endorser error: %s", err) 792 return nil, policyErr(fmt.Errorf("Unmarshal endorser error: %s", err)) 793 } 794 identity := serializedIdentity.Mspid + string(serializedIdentity.IdBytes) 795 if _, ok := signatureMap[identity]; ok { 796 // Endorsement with the same identity has already been added 797 logger.Warningf("Ignoring duplicated identity, Mspid: %s, pem:\n%s", serializedIdentity.Mspid, serializedIdentity.IdBytes) 798 continue 799 } 800 data := make([]byte, len(prespBytes)+len(endorsement.Endorser)) 801 copy(data, prespBytes) 802 copy(data[len(prespBytes):], endorsement.Endorser) 803 signatureSet = append(signatureSet, &protoutil.SignedData{ 804 // set the data that is signed; concatenation of proposal response bytes and endorser ID 805 Data: data, 806 // set the identity that signs the message: it's the endorser 807 Identity: endorsement.Endorser, 808 // set the signature 809 Signature: endorsement.Signature, 810 }) 811 signatureMap[identity] = struct{}{} 812 } 813 814 logger.Debugf("Signature set is of size %d out of %d endorsement(s)", len(signatureSet), len(cap.Action.Endorsements)) 815 return signatureSet, nil 816 } 817 818 type state struct { 819 vs.State 820 } 821 822 // GetState retrieves the value for the given key in the given namespace. 823 func (s *state) GetState(namespace string, key string) ([]byte, error) { 824 values, err := s.GetStateMultipleKeys(namespace, []string{key}) 825 if err != nil { 826 return nil, err 827 } 828 if len(values) == 0 { 829 return nil, nil 830 } 831 return values[0], nil 832 } 833 834 func policyErr(err error) *commonerrors.VSCCEndorsementPolicyError { 835 return &commonerrors.VSCCEndorsementPolicyError{ 836 Err: err, 837 } 838 }