github.com/inklabsfoundation/inkchain@v0.17.1-0.20181025012015-c3cef8062f19/core/endorser/endorser.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package endorser 8 9 import ( 10 "encoding/json" 11 "fmt" 12 "math/big" 13 14 "github.com/golang/protobuf/proto" 15 "github.com/inklabsfoundation/inkchain/common/flogging" 16 "golang.org/x/net/context" 17 18 "errors" 19 20 "github.com/inklabsfoundation/inkchain/common/policies" 21 "github.com/inklabsfoundation/inkchain/common/util" 22 "github.com/inklabsfoundation/inkchain/core/chaincode" 23 "github.com/inklabsfoundation/inkchain/core/chaincode/shim" 24 "github.com/inklabsfoundation/inkchain/core/common/ccprovider" 25 "github.com/inklabsfoundation/inkchain/core/common/validation" 26 "github.com/inklabsfoundation/inkchain/core/ledger" 27 "github.com/inklabsfoundation/inkchain/core/peer" 28 "github.com/inklabsfoundation/inkchain/core/policy" 29 syscc "github.com/inklabsfoundation/inkchain/core/scc" 30 "github.com/inklabsfoundation/inkchain/core/wallet" 31 "github.com/inklabsfoundation/inkchain/core/wallet/ink" 32 "github.com/inklabsfoundation/inkchain/core/wallet/ink/impl" 33 "github.com/inklabsfoundation/inkchain/msp" 34 "github.com/inklabsfoundation/inkchain/msp/mgmt" 35 "github.com/inklabsfoundation/inkchain/protos/common" 36 pb "github.com/inklabsfoundation/inkchain/protos/peer" 37 putils "github.com/inklabsfoundation/inkchain/protos/utils" 38 ) 39 40 // >>>>> begin errors section >>>>> 41 //chaincodeError is a inkchain error signifying error from chaincode 42 type chaincodeError struct { 43 status int32 44 msg string 45 } 46 47 func (ce chaincodeError) Error() string { 48 return fmt.Sprintf("chaincode error (status: %d, message: %s)", ce.status, ce.msg) 49 } 50 51 // <<<<< end errors section <<<<<< 52 53 var endorserLogger = flogging.MustGetLogger("endorser") 54 55 // The Jira issue that documents Endorser flow along with its relationship to 56 57 // Endorser provides the Endorser service ProcessProposal 58 type Endorser struct { 59 policyChecker policy.PolicyChecker 60 inkCalculator ink.InkAlg 61 } 62 63 // NewEndorserServer creates and returns a new Endorser server instance. 64 func NewEndorserServer() pb.EndorserServer { 65 e := new(Endorser) 66 e.policyChecker = policy.NewPolicyChecker( 67 peer.NewChannelPolicyManagerGetter(), 68 mgmt.GetLocalMSP(), 69 mgmt.NewLocalMSPPrincipalGetter(), 70 ) 71 e.inkCalculator = impl.NewSimpleInkAlg() 72 return e 73 } 74 75 // checkACL checks that the supplied proposal complies 76 // with the writers policy of the chain 77 func (e *Endorser) checkACL(signedProp *pb.SignedProposal, chdr *common.ChannelHeader, shdr *common.SignatureHeader, hdrext *pb.ChaincodeHeaderExtension) error { 78 return e.policyChecker.CheckPolicy(chdr.ChannelId, policies.ChannelApplicationWriters, signedProp) 79 } 80 81 //TODO - check for escc and vscc 82 func (*Endorser) checkEsccAndVscc(prop *pb.Proposal) error { 83 return nil 84 } 85 86 //INKCHAIN - check invoke signature 87 func (*Endorser) checkInvokeSigAndSetSender(cis *pb.ChaincodeInvocationSpec, txsim ledger.TxSimulator) error { 88 if cis.Sig == nil || cis.SenderSpec == nil { 89 return nil 90 } 91 hash, err := wallet.GetInvokeHash(cis.ChaincodeSpec, cis.IdGenerationAlg, cis.SenderSpec) 92 if err != nil { 93 return err 94 } 95 address, err := wallet.GetSenderFromSignature(hash, cis.Sig) 96 if err != nil { 97 return err 98 } 99 sender := address.ToString() 100 if string(cis.SenderSpec.Sender[:]) != sender { 101 return fmt.Errorf("endorser: invalid signature") 102 } 103 txsim.SetSender(sender) 104 return nil 105 } 106 107 func (e *Endorser) checkCounterAndInk(cis *pb.ChaincodeInvocationSpec, txsim ledger.TxSimulator, byteCount int, account *wallet.Account) error { 108 if cis.Sig == nil || cis.SenderSpec == nil { 109 return nil 110 } 111 if cis.SenderSpec.Counter < account.Counter { 112 return fmt.Errorf("endorser: invalid counter") 113 } 114 feeTmp, err := e.inkCalculator.CalcInk(byteCount) 115 fee := big.NewInt(feeTmp) 116 if err != nil { 117 return fmt.Errorf("endorser: error when calculating ink") 118 } 119 inkBalance, ok := account.Balance[wallet.MAIN_BALANCE_NAME] 120 if !ok || inkBalance.Cmp(fee) < 0 { 121 return fmt.Errorf("endorser: insufficient balance for ink consumption") 122 } 123 feeLimit, ok := new(big.Int).SetString(string(cis.SenderSpec.FeeLimit), 10) 124 if !ok { 125 return fmt.Errorf("endorser: invalid feeLimit.") 126 } 127 if fee.Cmp(feeLimit) > 0 { 128 return fmt.Errorf("endorser: fee exceeds feeLimit.") 129 } 130 if fee.Cmp(wallet.InkMinimumFee) < 0 { 131 return fmt.Errorf("endorser: fee too low") 132 } 133 return nil 134 } 135 136 func (*Endorser) getTxSimulator(ledgername string) (ledger.TxSimulator, error) { 137 lgr := peer.GetLedger(ledgername) 138 if lgr == nil { 139 return nil, fmt.Errorf("channel does not exist: %s", ledgername) 140 } 141 return lgr.NewTxSimulator() 142 } 143 144 func (*Endorser) getHistoryQueryExecutor(ledgername string) (ledger.HistoryQueryExecutor, error) { 145 lgr := peer.GetLedger(ledgername) 146 if lgr == nil { 147 return nil, fmt.Errorf("channel does not exist: %s", ledgername) 148 } 149 return lgr.NewHistoryQueryExecutor() 150 } 151 152 //call specified chaincode (system or user) 153 func (e *Endorser) callChaincode(ctxt context.Context, chainID string, version string, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, cis *pb.ChaincodeInvocationSpec, cid *pb.ChaincodeID, txsim ledger.TxSimulator) (*pb.Response, *pb.ChaincodeEvent, error) { 154 endorserLogger.Debugf("Entry - txid: %s channel id: %s version: %s", txid, chainID, version) 155 defer endorserLogger.Debugf("Exit") 156 var err error 157 var res *pb.Response 158 var ccevent *pb.ChaincodeEvent 159 160 if txsim != nil { 161 ctxt = context.WithValue(ctxt, chaincode.TXSimulatorKey, txsim) 162 } 163 164 //is this a system chaincode 165 scc := syscc.IsSysCC(cid.Name) 166 167 cccid := ccprovider.NewCCContext(chainID, cid.Name, version, txid, scc, signedProp, prop) 168 169 res, ccevent, err = chaincode.ExecuteChaincode(ctxt, cccid, cis.ChaincodeSpec.Input.Args) 170 171 if err != nil { 172 return nil, nil, err 173 } 174 175 //per doc anything < 400 can be sent as TX. 176 //inkchain errors will always be >= 400 (ie, unambiguous errors ) 177 //"lscc" will respond with status 200 or 500 (ie, unambiguous OK or ERROR) 178 if res.Status >= shim.ERRORTHRESHOLD { 179 return res, nil, nil 180 } 181 182 //----- BEGIN - SECTION THAT MAY NEED TO BE DONE IN LSCC ------ 183 //if this a call to deploy a chaincode, We need a mechanism 184 //to pass TxSimulator into LSCC. Till that is worked out this 185 //special code does the actual deploy, upgrade here so as to collect 186 //all state under one TxSimulator 187 // 188 //NOTE that if there's an error all simulation, including the chaincode 189 //table changes in lscc will be thrown away 190 if cid.Name == "lscc" && len(cis.ChaincodeSpec.Input.Args) >= 3 && (string(cis.ChaincodeSpec.Input.Args[0]) == "deploy" || string(cis.ChaincodeSpec.Input.Args[0]) == "upgrade") { 191 var cds *pb.ChaincodeDeploymentSpec 192 cds, err = putils.GetChaincodeDeploymentSpec(cis.ChaincodeSpec.Input.Args[2]) 193 if err != nil { 194 return nil, nil, err 195 } 196 197 //this should not be a system chaincode 198 if syscc.IsSysCC(cds.ChaincodeSpec.ChaincodeId.Name) { 199 return nil, nil, fmt.Errorf("attempting to deploy a system chaincode %s/%s", cds.ChaincodeSpec.ChaincodeId.Name, chainID) 200 } 201 202 cccid = ccprovider.NewCCContext(chainID, cds.ChaincodeSpec.ChaincodeId.Name, cds.ChaincodeSpec.ChaincodeId.Version, txid, false, signedProp, prop) 203 204 _, _, err = chaincode.Execute(ctxt, cccid, cds) 205 if err != nil { 206 return nil, nil, fmt.Errorf("%s", err) 207 } 208 } 209 //----- END ------- 210 211 return res, ccevent, err 212 } 213 214 //TO BE REMOVED WHEN JAVA CC IS ENABLED 215 //disableJavaCCInst if trying to install, instantiate or upgrade Java CC 216 func (e *Endorser) disableJavaCCInst(cid *pb.ChaincodeID, cis *pb.ChaincodeInvocationSpec) error { 217 //if not lscc we don't care 218 if cid.Name != "lscc" { 219 return nil 220 } 221 222 //non-nil spec ? leave it to callers to handle error if this is an error 223 if cis.ChaincodeSpec == nil || cis.ChaincodeSpec.Input == nil { 224 return nil 225 } 226 227 //should at least have a command arg, leave it to callers if this is an error 228 if len(cis.ChaincodeSpec.Input.Args) < 1 { 229 return nil 230 } 231 232 var argNo int 233 switch string(cis.ChaincodeSpec.Input.Args[0]) { 234 case "install": 235 argNo = 1 236 case "deploy", "upgrade": 237 argNo = 2 238 default: 239 //what else can it be ? leave it caller to handle it if error 240 return nil 241 } 242 243 if argNo >= len(cis.ChaincodeSpec.Input.Args) { 244 return errors.New("Too few arguments passed") 245 } 246 247 //the inner dep spec will contain the type 248 cds, err := putils.GetChaincodeDeploymentSpec(cis.ChaincodeSpec.Input.Args[argNo]) 249 if err != nil { 250 return err 251 } 252 253 //finally, if JAVA error out 254 if cds.ChaincodeSpec.Type == pb.ChaincodeSpec_JAVA { 255 return fmt.Errorf("Java chaincode is work-in-progress and disabled") 256 } 257 258 //not a java install, instantiate or upgrade op 259 return nil 260 } 261 262 //simulate the proposal by calling the chaincode 263 func (e *Endorser) simulateProposal(ctx context.Context, chainID string, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, cid *pb.ChaincodeID, txsim ledger.TxSimulator) (*ccprovider.ChaincodeData, *pb.Response, []byte, *pb.ChaincodeEvent, error) { 264 endorserLogger.Debugf("Entry - txid: %s channel id: %s", txid, chainID) 265 defer endorserLogger.Debugf("Exit") 266 //we do expect the payload to be a ChaincodeInvocationSpec 267 //if we are supporting other payloads in future, this be glaringly point 268 //as something that should change 269 cis, err := putils.GetChaincodeInvocationSpec(prop) 270 if err != nil { 271 return nil, nil, nil, nil, err 272 } 273 274 //disable Java install,instantiate,upgrade for now 275 if err = e.disableJavaCCInst(cid, cis); err != nil { 276 return nil, nil, nil, nil, err 277 } 278 //---1. check Invoke Signature 279 if err = e.checkInvokeSigAndSetSender(cis, txsim); err != nil { 280 return nil, nil, nil, nil, err 281 } 282 //---2. check ESCC and VSCC for the chaincode 283 if err = e.checkEsccAndVscc(prop); err != nil { 284 return nil, nil, nil, nil, err 285 } 286 287 var cdLedger *ccprovider.ChaincodeData 288 var version string 289 290 if !syscc.IsSysCC(cid.Name) { 291 cdLedger, err = e.getCDSFromLSCC(ctx, chainID, txid, signedProp, prop, cid.Name, txsim) 292 if err != nil { 293 return nil, nil, nil, nil, fmt.Errorf("%s - make sure the chaincode %s has been successfully instantiated and try again", err, cid.Name) 294 } 295 version = cdLedger.Version 296 297 err = ccprovider.CheckInsantiationPolicy(cid.Name, version, cdLedger) 298 if err != nil { 299 return nil, nil, nil, nil, err 300 } 301 } else { 302 version = util.GetSysCCVersion() 303 } 304 305 //*** added: get account for checking ink & counter. GetState() could not be called after GetTxSimulationResults() 306 var account *wallet.Account 307 if cis.Sig != nil && cis.SenderSpec != nil { 308 account = &wallet.Account{} 309 var resBytes []byte 310 resBytes, err = txsim.GetState(wallet.WALLET_NAMESPACE, string(cis.SenderSpec.Sender[:])) 311 if err != nil { 312 return nil, nil, nil, nil, err 313 } 314 err = json.Unmarshal(resBytes, account) 315 if err != nil { 316 return nil, nil, nil, nil, err 317 } 318 } 319 320 //---3. execute the proposal and get simulation results 321 var simResult []byte 322 var res *pb.Response 323 var ccevent *pb.ChaincodeEvent 324 res, ccevent, err = e.callChaincode(ctx, chainID, version, txid, signedProp, prop, cis, cid, txsim) 325 if err != nil { 326 endorserLogger.Errorf("failed to invoke chaincode %s on transaction %s, error: %s", cid, txid, err) 327 return nil, nil, nil, nil, err 328 } 329 330 if txsim != nil { 331 if simResult, err = txsim.GetTxSimulationResults(); err != nil { 332 return nil, nil, nil, nil, err 333 } 334 } 335 336 //---4. check counter and ink 337 contentLength := 0 338 if cis.SenderSpec!=nil{ 339 contentLength+= len(cis.SenderSpec.Msg) 340 } 341 err = e.checkCounterAndInk(cis, txsim, contentLength, account) 342 if err != nil { 343 return nil, nil, nil, nil, err 344 } 345 346 return cdLedger, res, simResult, ccevent, nil 347 } 348 349 func (e *Endorser) getCDSFromLSCC(ctx context.Context, chainID string, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, chaincodeID string, txsim ledger.TxSimulator) (*ccprovider.ChaincodeData, error) { 350 ctxt := ctx 351 if txsim != nil { 352 ctxt = context.WithValue(ctx, chaincode.TXSimulatorKey, txsim) 353 } 354 355 return chaincode.GetChaincodeDataFromLSCC(ctxt, txid, signedProp, prop, chainID, chaincodeID) 356 } 357 358 //endorse the proposal by calling the ESCC 359 func (e *Endorser) endorseProposal(ctx context.Context, chainID string, txid string, signedProp *pb.SignedProposal, proposal *pb.Proposal, response *pb.Response, simRes []byte, event *pb.ChaincodeEvent, visibility []byte, ccid *pb.ChaincodeID, txsim ledger.TxSimulator, cd *ccprovider.ChaincodeData) (*pb.ProposalResponse, error) { 360 endorserLogger.Debugf("Entry - txid: %s channel id: %s chaincode id: %s", txid, chainID, ccid) 361 defer endorserLogger.Debugf("Exit") 362 363 isSysCC := cd == nil 364 // 1) extract the name of the escc that is requested to endorse this chaincode 365 var escc string 366 //ie, not "lscc" or system chaincodes 367 if isSysCC { 368 // FIXME: getCDSFromLSCC seems to fail for lscc - not sure this is expected? 369 // TODO: who should endorse a call to LSCC? 370 escc = "escc" 371 } else { 372 escc = cd.Escc 373 if escc == "" { // this should never happen, LSCC always fills this field 374 panic("No ESCC specified in ChaincodeData") 375 } 376 } 377 378 endorserLogger.Debugf("info: escc for chaincode id %s is %s", ccid, escc) 379 380 // marshalling event bytes 381 var err error 382 var eventBytes []byte 383 if event != nil { 384 eventBytes, err = putils.GetBytesChaincodeEvent(event) 385 if err != nil { 386 return nil, fmt.Errorf("failed to marshal event bytes - %s", err) 387 } 388 } 389 390 resBytes, err := putils.GetBytesResponse(response) 391 if err != nil { 392 return nil, fmt.Errorf("failed to marshal response bytes - %s", err) 393 } 394 395 // set version of executing chaincode 396 if isSysCC { 397 // if we want to allow mixed inkchain levels we should 398 // set syscc version to "" 399 ccid.Version = util.GetSysCCVersion() 400 } else { 401 ccid.Version = cd.Version 402 } 403 404 ccidBytes, err := putils.Marshal(ccid) 405 if err != nil { 406 return nil, fmt.Errorf("failed to marshal ChaincodeID - %s", err) 407 } 408 409 // 3) call the ESCC we've identified 410 // arguments: 411 // args[0] - function name (not used now) 412 // args[1] - serialized Header object 413 // args[2] - serialized ChaincodeProposalPayload object 414 // args[3] - ChaincodeID of executing chaincode 415 // args[4] - result of executing chaincode 416 // args[5] - binary blob of simulation results 417 // args[6] - serialized events 418 // args[7] - payloadVisibility 419 args := [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, resBytes, simRes, eventBytes, visibility} 420 version := util.GetSysCCVersion() 421 ecccis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeId: &pb.ChaincodeID{Name: escc}, Input: &pb.ChaincodeInput{Args: args}}} 422 res, _, err := e.callChaincode(ctx, chainID, version, txid, signedProp, proposal, ecccis, &pb.ChaincodeID{Name: escc}, txsim) 423 if err != nil { 424 return nil, err 425 } 426 427 if res.Status >= shim.ERRORTHRESHOLD { 428 return &pb.ProposalResponse{Response: res}, nil 429 } 430 431 prBytes := res.Payload 432 // Note that we do not extract any simulation results from 433 // the call to ESCC. This is intentional becuse ESCC is meant 434 // to endorse (i.e. sign) the simulation results of a chaincode, 435 // but it can't obviously sign its own. Furthermore, ESCC runs 436 // on private input (its own signing key) and so if it were to 437 // produce simulationr results, they are likely to be different 438 // from other ESCCs, which would stand in the way of the 439 // endorsement process. 440 441 //3 -- respond 442 pResp, err := putils.GetProposalResponse(prBytes) 443 if err != nil { 444 return nil, err 445 } 446 447 return pResp, nil 448 } 449 450 // ProcessProposal process the Proposal 451 func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) { 452 endorserLogger.Debugf("Entry") 453 defer endorserLogger.Debugf("Exit") 454 // at first, we check whether the message is valid 455 prop, hdr, hdrExt, err := validation.ValidateProposalMessage(signedProp) 456 if err != nil { 457 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err 458 } 459 460 chdr, err := putils.UnmarshalChannelHeader(hdr.ChannelHeader) 461 if err != nil { 462 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err 463 } 464 465 shdr, err := putils.GetSignatureHeader(hdr.SignatureHeader) 466 if err != nil { 467 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err 468 } 469 470 // block invocations to security-sensitive system chaincodes 471 if syscc.IsSysCCAndNotInvokableExternal(hdrExt.ChaincodeId.Name) { 472 endorserLogger.Errorf("Error: an attempt was made by %#v to invoke system chaincode %s", 473 shdr.Creator, hdrExt.ChaincodeId.Name) 474 err = fmt.Errorf("Chaincode %s cannot be invoked through a proposal", hdrExt.ChaincodeId.Name) 475 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err 476 } 477 478 chainID := chdr.ChannelId 479 480 // Check for uniqueness of prop.TxID with ledger 481 // Notice that ValidateProposalMessage has already verified 482 // that TxID is computed properly 483 txid := chdr.TxId 484 if txid == "" { 485 err = errors.New("Invalid txID. It must be different from the empty string.") 486 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err 487 } 488 endorserLogger.Debugf("processing txid: %s", txid) 489 if chainID != "" { 490 // here we handle uniqueness check and ACLs for proposals targeting a chain 491 lgr := peer.GetLedger(chainID) 492 if lgr == nil { 493 return nil, fmt.Errorf("failure while looking up the ledger %s", chainID) 494 } 495 if _, err := lgr.GetTransactionByID(txid); err == nil { 496 return nil, fmt.Errorf("Duplicate transaction found [%s]. Creator [%x]. [%s]", txid, shdr.Creator, err) 497 } 498 499 // check ACL only for application chaincodes; ACLs 500 // for system chaincodes are checked elsewhere 501 if !syscc.IsSysCC(hdrExt.ChaincodeId.Name) { 502 // check that the proposal complies with the channel's writers 503 if err = e.checkACL(signedProp, chdr, shdr, hdrExt); err != nil { 504 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err 505 } 506 } 507 } else { 508 // chainless proposals do not/cannot affect ledger and cannot be submitted as transactions 509 // ignore uniqueness checks; also, chainless proposals are not validated using the policies 510 // of the chain since by definition there is no chain; they are validated against the local 511 // MSP of the peer instead by the call to ValidateProposalMessage above 512 } 513 514 // obtaining once the tx simulator for this proposal. This will be nil 515 // for chainless proposals 516 // Also obtain a history query executor for history queries, since tx simulator does not cover history 517 var txsim ledger.TxSimulator 518 var historyQueryExecutor ledger.HistoryQueryExecutor 519 if chainID != "" { 520 if txsim, err = e.getTxSimulator(chainID); err != nil { 521 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err 522 } 523 if historyQueryExecutor, err = e.getHistoryQueryExecutor(chainID); err != nil { 524 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err 525 } 526 // Add the historyQueryExecutor to context 527 // TODO shouldn't we also add txsim to context here as well? Rather than passing txsim parameter 528 // around separately, since eventually it gets added to context anyways 529 ctx = context.WithValue(ctx, chaincode.HistoryQueryExecutorKey, historyQueryExecutor) 530 531 defer txsim.Done() 532 } 533 //this could be a request to a chainless SysCC 534 535 // TODO: if the proposal has an extension, it will be of type ChaincodeAction; 536 // if it's present it means that no simulation is to be performed because 537 // we're trying to emulate a submitting peer. On the other hand, we need 538 // to validate the supplied action before endorsing it 539 540 //1 -- simulate 541 cd, res, simulationResult, ccevent, err := e.simulateProposal(ctx, chainID, txid, signedProp, prop, hdrExt.ChaincodeId, txsim) 542 if err != nil { 543 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err 544 } 545 if res != nil { 546 if res.Status >= shim.ERROR { 547 endorserLogger.Errorf("simulateProposal() resulted in chaincode response status %d for txid: %s", res.Status, txid) 548 var cceventBytes []byte 549 if ccevent != nil { 550 cceventBytes, err = putils.GetBytesChaincodeEvent(ccevent) 551 if err != nil { 552 return nil, fmt.Errorf("failed to marshal event bytes - %s", err) 553 } 554 } 555 pResp, err := putils.CreateProposalResponseFailure(prop.Header, prop.Payload, res, simulationResult, cceventBytes, hdrExt.ChaincodeId, hdrExt.PayloadVisibility) 556 if err != nil { 557 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err 558 } 559 560 return pResp, &chaincodeError{res.Status, res.Message} 561 } 562 } 563 564 //2 -- endorse and get a marshalled ProposalResponse message 565 var pResp *pb.ProposalResponse 566 567 //TODO till we implement global ESCC, CSCC for system chaincodes 568 //chainless proposals (such as CSCC) don't have to be endorsed 569 if chainID == "" { 570 pResp = &pb.ProposalResponse{Response: res} 571 } else { 572 pResp, err = e.endorseProposal(ctx, chainID, txid, signedProp, prop, res, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeId, txsim, cd) 573 if err != nil { 574 return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err 575 } 576 if pResp != nil { 577 if res.Status >= shim.ERRORTHRESHOLD { 578 endorserLogger.Debugf("endorseProposal() resulted in chaincode error for txid: %s", txid) 579 return pResp, &chaincodeError{res.Status, res.Message} 580 } 581 } 582 } 583 584 // Set the proposal response payload - it 585 // contains the "return value" from the 586 // chaincode invocation 587 pResp.Response.Payload = res.Payload 588 589 return pResp, nil 590 } 591 592 // Only exposed for testing purposes - commit the tx simulation so that 593 // a deploy transaction is persisted and that chaincode can be invoked. 594 // This makes the endorser test self-sufficient 595 func (e *Endorser) commitTxSimulation(proposal *pb.Proposal, chainID string, signer msp.SigningIdentity, pResp *pb.ProposalResponse, blockNumber uint64) error { 596 tx, err := putils.CreateSignedTx(proposal, signer, pResp) 597 if err != nil { 598 return err 599 } 600 601 lgr := peer.GetLedger(chainID) 602 if lgr == nil { 603 return fmt.Errorf("failure while looking up the ledger") 604 } 605 606 txBytes, err := proto.Marshal(tx) 607 if err != nil { 608 return err 609 } 610 block := common.NewBlock(blockNumber, []byte{}, []byte{}, 0) 611 block.Data.Data = [][]byte{txBytes} 612 block.Header.DataHash = block.Data.Hash() 613 if err = lgr.Commit(block); err != nil { 614 return err 615 } 616 617 return nil 618 }