github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/endorser/endorser.go (about) 1 /* 2 Copyright hechain. 2022 All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package endorser 8 9 import ( 10 "context" 11 "fmt" 12 "strconv" 13 "time" 14 15 "github.com/golang/protobuf/proto" 16 "github.com/hechain20/hechain/common/flogging" 17 "github.com/hechain20/hechain/common/util" 18 "github.com/hechain20/hechain/core/chaincode/lifecycle" 19 "github.com/hechain20/hechain/core/common/ccprovider" 20 "github.com/hechain20/hechain/core/ledger" 21 "github.com/hechain20/hechain/internal/pkg/identity" 22 "github.com/hechain20/hechain/msp" 23 "github.com/hechain20/hechain/protoutil" 24 "github.com/hyperledger/fabric-chaincode-go/shim" 25 "github.com/hyperledger/fabric-protos-go/common" 26 pb "github.com/hyperledger/fabric-protos-go/peer" 27 "github.com/hyperledger/fabric-protos-go/transientstore" 28 "github.com/pkg/errors" 29 "go.uber.org/zap" 30 ) 31 32 var endorserLogger = flogging.MustGetLogger("endorser") 33 34 // The Jira issue that documents Endorser flow along with its relationship to 35 // the lifecycle chaincode - https://jira.hyperledger.org/browse/FAB-181 36 37 //go:generate counterfeiter -o fake/prvt_data_distributor.go --fake-name PrivateDataDistributor . PrivateDataDistributor 38 39 type PrivateDataDistributor interface { 40 DistributePrivateData(channel string, txID string, privateData *transientstore.TxPvtReadWriteSetWithConfigInfo, blkHt uint64) error 41 } 42 43 // Support contains functions that the endorser requires to execute its tasks 44 type Support interface { 45 identity.SignerSerializer 46 // GetTxSimulator returns the transaction simulator for the specified ledger 47 // a client may obtain more than one such simulator; they are made unique 48 // by way of the supplied txid 49 GetTxSimulator(ledgername string, txid string) (ledger.TxSimulator, error) 50 51 // GetHistoryQueryExecutor gives handle to a history query executor for the 52 // specified ledger 53 GetHistoryQueryExecutor(ledgername string) (ledger.HistoryQueryExecutor, error) 54 55 // GetTransactionByID retrieves a transaction by id 56 GetTransactionByID(chid, txID string) (*pb.ProcessedTransaction, error) 57 58 // IsSysCC returns true if the name matches a system chaincode's 59 // system chaincode names are system, chain wide 60 IsSysCC(name string) bool 61 62 // Execute - execute proposal, return original response of chaincode 63 Execute(txParams *ccprovider.TransactionParams, name string, input *pb.ChaincodeInput) (*pb.Response, *pb.ChaincodeEvent, error) 64 65 // ExecuteLegacyInit - executes a deployment proposal, return original response of chaincode 66 ExecuteLegacyInit(txParams *ccprovider.TransactionParams, name, version string, spec *pb.ChaincodeInput) (*pb.Response, *pb.ChaincodeEvent, error) 67 68 // ChaincodeEndorsementInfo returns the information from lifecycle required to endorse the chaincode. 69 ChaincodeEndorsementInfo(channelID, chaincodeID string, txsim ledger.QueryExecutor) (*lifecycle.ChaincodeEndorsementInfo, error) 70 71 // CheckACL checks the ACL for the resource for the channel using the 72 // SignedProposal from which an id can be extracted for testing against a policy 73 CheckACL(channelID string, signedProp *pb.SignedProposal) error 74 75 // EndorseWithPlugin endorses the response with a plugin 76 EndorseWithPlugin(pluginName, channnelID string, prpBytes []byte, signedProposal *pb.SignedProposal) (*pb.Endorsement, []byte, error) 77 78 // GetLedgerHeight returns ledger height for given channelID 79 GetLedgerHeight(channelID string) (uint64, error) 80 81 // GetDeployedCCInfoProvider returns ledger.DeployedChaincodeInfoProvider 82 GetDeployedCCInfoProvider() ledger.DeployedChaincodeInfoProvider 83 } 84 85 //go:generate counterfeiter -o fake/channel_fetcher.go --fake-name ChannelFetcher . ChannelFetcher 86 87 // ChannelFetcher fetches the channel context for a given channel ID. 88 type ChannelFetcher interface { 89 Channel(channelID string) *Channel 90 } 91 92 type Channel struct { 93 IdentityDeserializer msp.IdentityDeserializer 94 } 95 96 // Endorser provides the Endorser service ProcessProposal 97 type Endorser struct { 98 ChannelFetcher ChannelFetcher 99 LocalMSP msp.IdentityDeserializer 100 PrivateDataDistributor PrivateDataDistributor 101 Support Support 102 PvtRWSetAssembler PvtRWSetAssembler 103 Metrics *Metrics 104 } 105 106 // call specified chaincode (system or user) 107 func (e *Endorser) callChaincode(txParams *ccprovider.TransactionParams, input *pb.ChaincodeInput, chaincodeName string) (*pb.Response, *pb.ChaincodeEvent, error) { 108 defer func(start time.Time) { 109 logger := endorserLogger.WithOptions(zap.AddCallerSkip(1)) 110 logger = decorateLogger(logger, txParams) 111 elapsedMillisec := time.Since(start).Milliseconds() 112 logger.Infof("finished chaincode: %s duration: %dms", chaincodeName, elapsedMillisec) 113 }(time.Now()) 114 115 meterLabels := []string{ 116 "channel", txParams.ChannelID, 117 "chaincode", chaincodeName, 118 } 119 120 res, ccevent, err := e.Support.Execute(txParams, chaincodeName, input) 121 if err != nil { 122 e.Metrics.SimulationFailure.With(meterLabels...).Add(1) 123 return nil, nil, err 124 } 125 126 // per doc anything < 400 can be sent as TX. 127 // fabric errors will always be >= 400 (ie, unambiguous errors ) 128 // "lscc" will respond with status 200 or 500 (ie, unambiguous OK or ERROR) 129 if res.Status >= shim.ERRORTHRESHOLD { 130 return res, nil, nil 131 } 132 133 // Unless this is the weirdo LSCC case, just return 134 if chaincodeName != "lscc" || len(input.Args) < 3 || (string(input.Args[0]) != "deploy" && string(input.Args[0]) != "upgrade") { 135 return res, ccevent, nil 136 } 137 138 // ----- BEGIN - SECTION THAT MAY NEED TO BE DONE IN LSCC ------ 139 // if this a call to deploy a chaincode, We need a mechanism 140 // to pass TxSimulator into LSCC. Till that is worked out this 141 // special code does the actual deploy, upgrade here so as to collect 142 // all state under one TxSimulator 143 // 144 // NOTE that if there's an error all simulation, including the chaincode 145 // table changes in lscc will be thrown away 146 cds, err := protoutil.UnmarshalChaincodeDeploymentSpec(input.Args[2]) 147 if err != nil { 148 e.Metrics.SimulationFailure.With(meterLabels...).Add(1) 149 return nil, nil, err 150 } 151 152 // this should not be a system chaincode 153 if e.Support.IsSysCC(cds.ChaincodeSpec.ChaincodeId.Name) { 154 e.Metrics.SimulationFailure.With(meterLabels...).Add(1) 155 return nil, nil, errors.Errorf("attempting to deploy a system chaincode %s/%s", cds.ChaincodeSpec.ChaincodeId.Name, txParams.ChannelID) 156 } 157 158 if len(cds.CodePackage) != 0 { 159 e.Metrics.SimulationFailure.With(meterLabels...).Add(1) 160 return nil, nil, errors.Errorf("lscc upgrade/deploy should not include a code packages") 161 } 162 163 _, _, err = e.Support.ExecuteLegacyInit(txParams, cds.ChaincodeSpec.ChaincodeId.Name, cds.ChaincodeSpec.ChaincodeId.Version, cds.ChaincodeSpec.Input) 164 if err != nil { 165 // increment the failure to indicate instantion/upgrade failures 166 meterLabels = []string{ 167 "channel", txParams.ChannelID, 168 "chaincode", cds.ChaincodeSpec.ChaincodeId.Name, 169 } 170 e.Metrics.InitFailed.With(meterLabels...).Add(1) 171 return nil, nil, err 172 } 173 174 return res, ccevent, err 175 } 176 177 // SimulateProposal simulates the proposal by calling the chaincode 178 func (e *Endorser) simulateProposal(txParams *ccprovider.TransactionParams, chaincodeName string, chaincodeInput *pb.ChaincodeInput) (*pb.Response, []byte, *pb.ChaincodeEvent, *pb.ChaincodeInterest, error) { 179 logger := decorateLogger(endorserLogger, txParams) 180 181 meterLabels := []string{ 182 "channel", txParams.ChannelID, 183 "chaincode", chaincodeName, 184 } 185 186 // ---3. execute the proposal and get simulation results 187 res, ccevent, err := e.callChaincode(txParams, chaincodeInput, chaincodeName) 188 if err != nil { 189 logger.Errorf("failed to invoke chaincode %s, error: %+v", chaincodeName, err) 190 return nil, nil, nil, nil, err 191 } 192 193 if txParams.TXSimulator == nil { 194 return res, nil, ccevent, nil, nil 195 } 196 197 // Note, this is a little goofy, as if there is private data, Done() gets called 198 // early, so this is invoked multiple times, but that is how the code worked before 199 // this change, so, should be safe. Long term, let's move the Done up to the create. 200 defer txParams.TXSimulator.Done() 201 202 simResult, err := txParams.TXSimulator.GetTxSimulationResults() 203 if err != nil { 204 e.Metrics.SimulationFailure.With(meterLabels...).Add(1) 205 return nil, nil, nil, nil, err 206 } 207 208 if simResult.PvtSimulationResults != nil { 209 if chaincodeName == "lscc" { 210 // TODO: remove once we can store collection configuration outside of LSCC 211 e.Metrics.SimulationFailure.With(meterLabels...).Add(1) 212 return nil, nil, nil, nil, errors.New("Private data is forbidden to be used in instantiate") 213 } 214 pvtDataWithConfig, err := AssemblePvtRWSet(txParams.ChannelID, simResult.PvtSimulationResults, txParams.TXSimulator, e.Support.GetDeployedCCInfoProvider()) 215 // To read collection config need to read collection updates before 216 // releasing the lock, hence txParams.TXSimulator.Done() moved down here 217 txParams.TXSimulator.Done() 218 219 if err != nil { 220 e.Metrics.SimulationFailure.With(meterLabels...).Add(1) 221 return nil, nil, nil, nil, errors.WithMessage(err, "failed to obtain collections config") 222 } 223 endorsedAt, err := e.Support.GetLedgerHeight(txParams.ChannelID) 224 if err != nil { 225 e.Metrics.SimulationFailure.With(meterLabels...).Add(1) 226 return nil, nil, nil, nil, errors.WithMessage(err, fmt.Sprintf("failed to obtain ledger height for channel '%s'", txParams.ChannelID)) 227 } 228 // Add ledger height at which transaction was endorsed, 229 // `endorsedAt` is obtained from the block storage and at times this could be 'endorsement Height + 1'. 230 // However, since we use this height only to select the configuration (3rd parameter in distributePrivateData) and 231 // manage transient store purge for orphaned private writesets (4th parameter in distributePrivateData), this works for now. 232 // Ideally, ledger should add support in the simulator as a first class function `GetHeight()`. 233 pvtDataWithConfig.EndorsedAt = endorsedAt 234 if err := e.PrivateDataDistributor.DistributePrivateData(txParams.ChannelID, txParams.TxID, pvtDataWithConfig, endorsedAt); err != nil { 235 e.Metrics.SimulationFailure.With(meterLabels...).Add(1) 236 return nil, nil, nil, nil, err 237 } 238 } 239 240 ccInterest, err := e.buildChaincodeInterest(simResult) 241 if err != nil { 242 return nil, nil, nil, nil, err 243 } 244 245 pubSimResBytes, err := simResult.GetPubSimulationBytes() 246 if err != nil { 247 e.Metrics.SimulationFailure.With(meterLabels...).Add(1) 248 return nil, nil, nil, nil, err 249 } 250 251 return res, pubSimResBytes, ccevent, ccInterest, nil 252 } 253 254 // preProcess checks the tx proposal headers, uniqueness and ACL 255 func (e *Endorser) preProcess(up *UnpackedProposal, channel *Channel) error { 256 // at first, we check whether the message is valid 257 258 err := up.Validate(channel.IdentityDeserializer) 259 if err != nil { 260 e.Metrics.ProposalValidationFailed.Add(1) 261 return errors.WithMessage(err, "error validating proposal") 262 } 263 264 if up.ChannelHeader.ChannelId == "" { 265 // chainless proposals do not/cannot affect ledger and cannot be submitted as transactions 266 // ignore uniqueness checks; also, chainless proposals are not validated using the policies 267 // of the chain since by definition there is no chain; they are validated against the local 268 // MSP of the peer instead by the call to ValidateUnpackProposal above 269 return nil 270 } 271 272 // labels that provide context for failure metrics 273 meterLabels := []string{ 274 "channel", up.ChannelHeader.ChannelId, 275 "chaincode", up.ChaincodeName, 276 } 277 278 // Here we handle uniqueness check and ACLs for proposals targeting a chain 279 // Notice that ValidateProposalMessage has already verified that TxID is computed properly 280 if _, err = e.Support.GetTransactionByID(up.ChannelHeader.ChannelId, up.ChannelHeader.TxId); err == nil { 281 // increment failure due to duplicate transactions. Useful for catching replay attacks in 282 // addition to benign retries 283 e.Metrics.DuplicateTxsFailure.With(meterLabels...).Add(1) 284 return errors.Errorf("duplicate transaction found [%s]. Creator [%x]", up.ChannelHeader.TxId, up.SignatureHeader.Creator) 285 } 286 287 // check ACL only for application chaincodes; ACLs 288 // for system chaincodes are checked elsewhere 289 if !e.Support.IsSysCC(up.ChaincodeName) { 290 // check that the proposal complies with the Channel's writers 291 if err = e.Support.CheckACL(up.ChannelHeader.ChannelId, up.SignedProposal); err != nil { 292 e.Metrics.ProposalACLCheckFailed.With(meterLabels...).Add(1) 293 return err 294 } 295 } 296 297 return nil 298 } 299 300 // ProcessProposal process the Proposal 301 // Errors related to the proposal itself are returned with an error that results in a grpc error. 302 // Errors related to proposal processing (either infrastructure errors or chaincode errors) are returned with a nil error, 303 // clients are expected to look at the ProposalResponse response status code (e.g. 500) and message. 304 func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) { 305 // start time for computing elapsed time metric for successfully endorsed proposals 306 startTime := time.Now() 307 e.Metrics.ProposalsReceived.Add(1) 308 309 addr := util.ExtractRemoteAddress(ctx) 310 endorserLogger.Debug("request from", addr) 311 312 // variables to capture proposal duration metric 313 success := false 314 315 up, err := UnpackProposal(signedProp) 316 if err != nil { 317 e.Metrics.ProposalValidationFailed.Add(1) 318 endorserLogger.Warnw("Failed to unpack proposal", "error", err.Error()) 319 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err 320 } 321 322 var channel *Channel 323 if up.ChannelID() != "" { 324 channel = e.ChannelFetcher.Channel(up.ChannelID()) 325 if channel == nil { 326 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: fmt.Sprintf("channel '%s' not found", up.ChannelHeader.ChannelId)}}, nil 327 } 328 } else { 329 channel = &Channel{ 330 IdentityDeserializer: e.LocalMSP, 331 } 332 } 333 334 // 0 -- check and validate 335 err = e.preProcess(up, channel) 336 if err != nil { 337 endorserLogger.Warnw("Failed to preProcess proposal", "error", err.Error()) 338 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err 339 } 340 341 defer func() { 342 meterLabels := []string{ 343 "channel", up.ChannelHeader.ChannelId, 344 "chaincode", up.ChaincodeName, 345 "success", strconv.FormatBool(success), 346 } 347 e.Metrics.ProposalDuration.With(meterLabels...).Observe(time.Since(startTime).Seconds()) 348 }() 349 350 pResp, err := e.ProcessProposalSuccessfullyOrError(up) 351 if err != nil { 352 endorserLogger.Warnw("Failed to invoke chaincode", "channel", up.ChannelHeader.ChannelId, "chaincode", up.ChaincodeName, "error", err.Error()) 353 // Return a nil error since clients are expected to look at the ProposalResponse response status code (500) and message. 354 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, nil 355 } 356 357 if pResp.Endorsement != nil || up.ChannelHeader.ChannelId == "" { 358 // We mark the tx as successful only if it was successfully endorsed, or 359 // if it was a system chaincode on a channel-less channel and therefore 360 // cannot be endorsed. 361 success = true 362 363 // total failed proposals = ProposalsReceived-SuccessfulProposals 364 e.Metrics.SuccessfulProposals.Add(1) 365 } 366 return pResp, nil 367 } 368 369 func (e *Endorser) ProcessProposalSuccessfullyOrError(up *UnpackedProposal) (*pb.ProposalResponse, error) { 370 txParams := &ccprovider.TransactionParams{ 371 ChannelID: up.ChannelHeader.ChannelId, 372 TxID: up.ChannelHeader.TxId, 373 SignedProp: up.SignedProposal, 374 Proposal: up.Proposal, 375 } 376 377 logger := decorateLogger(endorserLogger, txParams) 378 379 if acquireTxSimulator(up.ChannelHeader.ChannelId, up.ChaincodeName) { 380 txSim, err := e.Support.GetTxSimulator(up.ChannelID(), up.TxID()) 381 if err != nil { 382 return nil, err 383 } 384 385 // txsim acquires a shared lock on the stateDB. As this would impact the block commits (i.e., commit 386 // of valid write-sets to the stateDB), we must release the lock as early as possible. 387 // Hence, this txsim object is closed in simulateProposal() as soon as the tx is simulated and 388 // rwset is collected before gossip dissemination if required for privateData. For safety, we 389 // add the following defer statement and is useful when an error occur. Note that calling 390 // txsim.Done() more than once does not cause any issue. If the txsim is already 391 // released, the following txsim.Done() simply returns. 392 defer txSim.Done() 393 394 hqe, err := e.Support.GetHistoryQueryExecutor(up.ChannelID()) 395 if err != nil { 396 return nil, err 397 } 398 399 txParams.TXSimulator = txSim 400 txParams.HistoryQueryExecutor = hqe 401 } 402 403 cdLedger, err := e.Support.ChaincodeEndorsementInfo(up.ChannelID(), up.ChaincodeName, txParams.TXSimulator) 404 if err != nil { 405 return nil, errors.WithMessagef(err, "make sure the chaincode %s has been successfully defined on channel %s and try again", up.ChaincodeName, up.ChannelID()) 406 } 407 408 // 1 -- simulate 409 res, simulationResult, ccevent, ccInterest, err := e.simulateProposal(txParams, up.ChaincodeName, up.Input) 410 if err != nil { 411 return nil, errors.WithMessage(err, "error in simulation") 412 } 413 414 cceventBytes, err := CreateCCEventBytes(ccevent) 415 if err != nil { 416 return nil, errors.Wrap(err, "failed to marshal chaincode event") 417 } 418 419 prpBytes, err := protoutil.GetBytesProposalResponsePayload(up.ProposalHash, res, simulationResult, cceventBytes, &pb.ChaincodeID{ 420 Name: up.ChaincodeName, 421 Version: cdLedger.Version, 422 }) 423 if err != nil { 424 logger.Warning("Failed marshaling the proposal response payload to bytes", err) 425 return nil, errors.WithMessage(err, "failed to create the proposal response") 426 } 427 428 // if error, capture endorsement failure metric 429 meterLabels := []string{ 430 "channel", up.ChannelID(), 431 "chaincode", up.ChaincodeName, 432 } 433 434 switch { 435 case res.Status >= shim.ERROR: 436 return &pb.ProposalResponse{ 437 Response: res, 438 Payload: prpBytes, 439 Interest: ccInterest, 440 }, nil 441 case up.ChannelID() == "": 442 // Chaincode invocations without a channel ID is a broken concept 443 // that should be removed in the future. For now, return unendorsed 444 // success. 445 return &pb.ProposalResponse{ 446 Response: res, 447 }, nil 448 case res.Status >= shim.ERRORTHRESHOLD: 449 meterLabels = append(meterLabels, "chaincodeerror", strconv.FormatBool(true)) 450 e.Metrics.EndorsementsFailed.With(meterLabels...).Add(1) 451 logger.Debugf("chaincode error %d", res.Status) 452 return &pb.ProposalResponse{ 453 Response: res, 454 }, nil 455 } 456 457 escc := cdLedger.EndorsementPlugin 458 459 logger.Debugf("escc for chaincode %s is %s", up.ChaincodeName, escc) 460 461 // Note, mPrpBytes is the same as prpBytes by default endorsement plugin, but others could change it. 462 endorsement, mPrpBytes, err := e.Support.EndorseWithPlugin(escc, up.ChannelID(), prpBytes, up.SignedProposal) 463 if err != nil { 464 meterLabels = append(meterLabels, "chaincodeerror", strconv.FormatBool(false)) 465 e.Metrics.EndorsementsFailed.With(meterLabels...).Add(1) 466 return nil, errors.WithMessage(err, "endorsing with plugin failed") 467 } 468 469 return &pb.ProposalResponse{ 470 Version: 1, 471 Endorsement: endorsement, 472 Payload: mPrpBytes, 473 Response: res, 474 Interest: ccInterest, 475 }, nil 476 } 477 478 // Using the simulation results, build the ChaincodeInterest structure that the client can pass to the discovery service 479 // to get the correct endorsement policy for the chaincode(s) and any collections encountered. 480 func (e *Endorser) buildChaincodeInterest(simResult *ledger.TxSimulationResults) (*pb.ChaincodeInterest, error) { 481 // build a structure that collates all the information needed for the chaincode interest: 482 policies, err := parseWritesetMetadata(simResult.WritesetMetadata) 483 if err != nil { 484 return nil, err 485 } 486 487 // There might be public states that are read and not written. Need to add these to the policyRequired structure. 488 // This will also include private reads, because the hashed read will appear in the public RWset. 489 for _, nsrws := range simResult.PubSimulationResults.GetNsRwset() { 490 if e.Support.IsSysCC(nsrws.Namespace) { 491 // skip system chaincodes 492 continue 493 } 494 if _, ok := policies.policyRequired[nsrws.Namespace]; !ok { 495 // There's a public RWset for this namespace, but no public or private writes, so chaincode policy is required. 496 policies.add(nsrws.Namespace, "", true) 497 } 498 } 499 500 for chaincode, collections := range simResult.PrivateReads { 501 for collection := range collections { 502 policies.add(chaincode, collection, true) 503 } 504 } 505 506 ccInterest := &pb.ChaincodeInterest{} 507 for chaincode, collections := range policies.policyRequired { 508 if e.Support.IsSysCC(chaincode) { 509 // skip system chaincodes 510 continue 511 } 512 for collection := range collections { 513 ccCall := &pb.ChaincodeCall{ 514 Name: chaincode, 515 } 516 if collection == "" { // the empty collection name here represents the public RWset 517 keyPolicies := policies.sbePolicies[chaincode] 518 if len(keyPolicies) > 0 { 519 // For simplicity, we'll always add the SBE policies to the public ChaincodeCall, and set the disregard flag if the chaincode policy is not required. 520 ccCall.KeyPolicies = keyPolicies 521 if !policies.requireChaincodePolicy(chaincode) { 522 ccCall.DisregardNamespacePolicy = true 523 } 524 } else if !policies.requireChaincodePolicy(chaincode) { 525 continue 526 } 527 } else { 528 // Since each collection in a chaincode could have different values of the NoPrivateReads flag, create a new Chaincode entry for each. 529 ccCall.CollectionNames = []string{collection} 530 ccCall.NoPrivateReads = !simResult.PrivateReads.Exists(chaincode, collection) 531 } 532 ccInterest.Chaincodes = append(ccInterest.Chaincodes, ccCall) 533 } 534 } 535 536 endorserLogger.Debug("ccInterest", ccInterest) 537 return ccInterest, nil 538 } 539 540 type metadataPolicies struct { 541 // Map of SBE policies: namespace -> array of policies. 542 sbePolicies map[string][]*common.SignaturePolicyEnvelope 543 // Whether the chaincode/collection policy is required for endorsement: namespace -> collection -> isRequired 544 // Empty collection name represents the public rwset 545 // Each entry in this map represents a ChaincodeCall structure in the final ChaincodeInterest. The boolean 546 // flag isRequired is used to control whether the DisregardNamespacePolicy flag should be set. 547 policyRequired map[string]map[string]bool 548 } 549 550 func parseWritesetMetadata(metadata ledger.WritesetMetadata) (*metadataPolicies, error) { 551 mp := &metadataPolicies{ 552 sbePolicies: map[string][]*common.SignaturePolicyEnvelope{}, 553 policyRequired: map[string]map[string]bool{}, 554 } 555 for ns, cmap := range metadata { 556 mp.policyRequired[ns] = map[string]bool{"": false} 557 for coll, kmap := range cmap { 558 // look through each of the states that were written to 559 for _, stateMetadata := range kmap { 560 if policyBytes, sbeExists := stateMetadata[pb.MetaDataKeys_VALIDATION_PARAMETER.String()]; sbeExists { 561 policy, err := protoutil.UnmarshalSignaturePolicy(policyBytes) 562 if err != nil { 563 return nil, err 564 } 565 mp.sbePolicies[ns] = append(mp.sbePolicies[ns], policy) 566 } else { 567 // the state metadata doesn't contain data relating to SBE policy, so the chaincode/collection policy is required 568 mp.policyRequired[ns][coll] = true 569 } 570 } 571 } 572 } 573 574 return mp, nil 575 } 576 577 func (mp *metadataPolicies) add(ns string, coll string, required bool) { 578 if entry, ok := mp.policyRequired[ns]; ok { 579 entry[coll] = required 580 } else { 581 mp.policyRequired[ns] = map[string]bool{coll: required} 582 } 583 } 584 585 func (mp *metadataPolicies) requireChaincodePolicy(ns string) bool { 586 // if any of the states (keys) were written to without those states having a SBE policy, then the chaincode policy will be required for this namespace 587 return mp.policyRequired[ns][""] 588 } 589 590 // determine whether or not a transaction simulator should be 591 // obtained for a proposal. 592 func acquireTxSimulator(chainID string, chaincodeName string) bool { 593 if chainID == "" { 594 return false 595 } 596 597 // ¯\_(ツ)_/¯ locking. 598 // Don't get a simulator for the query and config system chaincode. 599 // These don't need the simulator and its read lock results in deadlocks. 600 switch chaincodeName { 601 case "qscc", "cscc": 602 return false 603 default: 604 return true 605 } 606 } 607 608 // shorttxid replicates the chaincode package function to shorten txids. 609 // ~~TODO utilize a common shorttxid utility across packages.~~ 610 // TODO use a formal type for transaction ID and make it a stringer 611 func shorttxid(txid string) string { 612 if len(txid) < 8 { 613 return txid 614 } 615 return txid[0:8] 616 } 617 618 func CreateCCEventBytes(ccevent *pb.ChaincodeEvent) ([]byte, error) { 619 if ccevent == nil { 620 return nil, nil 621 } 622 623 return proto.Marshal(ccevent) 624 } 625 626 func decorateLogger(logger *flogging.FabricLogger, txParams *ccprovider.TransactionParams) *flogging.FabricLogger { 627 return logger.With("channel", txParams.ChannelID, "txID", shorttxid(txParams.TxID)) 628 }