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