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  }