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