1 Star 0 Fork 0

陈文甲 / fabric

Create your Gitee Account
Explore and code with more than 12 million developers,Free private repositories !:)
Sign up
Clone or Download
endorser.go 19.11 KB
Copy Edit Raw Blame History
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
/*
Copyright IBM Corp. 2016 All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package endorser
import (
"fmt"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/flogging"
"golang.org/x/net/context"
"errors"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/core/chaincode"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/core/common/ccprovider"
"github.com/hyperledger/fabric/core/common/validation"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/core/policy"
syscc "github.com/hyperledger/fabric/core/scc"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/msp/mgmt"
"github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"
putils "github.com/hyperledger/fabric/protos/utils"
)
// >>>>> begin errors section >>>>>
//chaincodeError is a fabric error signifying error from chaincode
type chaincodeError struct {
status int32
msg string
}
func (ce chaincodeError) Error() string {
return fmt.Sprintf("chaincode error (status: %d, message: %s)", ce.status, ce.msg)
}
// <<<<< end errors section <<<<<<
var endorserLogger = flogging.MustGetLogger("endorser")
// The Jira issue that documents Endorser flow along with its relationship to
// the lifecycle chaincode - https://jira.hyperledger.org/browse/FAB-181
// Endorser provides the Endorser service ProcessProposal
type Endorser struct {
policyChecker policy.PolicyChecker
}
// NewEndorserServer creates and returns a new Endorser server instance.
func NewEndorserServer() pb.EndorserServer {
e := new(Endorser)
e.policyChecker = policy.NewPolicyChecker(
peer.NewChannelPolicyManagerGetter(),
mgmt.GetLocalMSP(),
mgmt.NewLocalMSPPrincipalGetter(),
)
return e
}
// checkACL checks that the supplied proposal complies
// with the writers policy of the chain
func (e *Endorser) checkACL(signedProp *pb.SignedProposal, chdr *common.ChannelHeader, shdr *common.SignatureHeader, hdrext *pb.ChaincodeHeaderExtension) error {
return e.policyChecker.CheckPolicy(chdr.ChannelId, policies.ChannelApplicationWriters, signedProp)
}
//TODO - check for escc and vscc
func (*Endorser) checkEsccAndVscc(prop *pb.Proposal) error {
return nil
}
func (*Endorser) getTxSimulator(ledgername string) (ledger.TxSimulator, error) {
lgr := peer.GetLedger(ledgername)
if lgr == nil {
return nil, fmt.Errorf("channel does not exist: %s", ledgername)
}
return lgr.NewTxSimulator()
}
func (*Endorser) getHistoryQueryExecutor(ledgername string) (ledger.HistoryQueryExecutor, error) {
lgr := peer.GetLedger(ledgername)
if lgr == nil {
return nil, fmt.Errorf("channel does not exist: %s", ledgername)
}
return lgr.NewHistoryQueryExecutor()
}
//call specified chaincode (system or user)
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) {
endorserLogger.Debugf("Entry - txid: %s channel id: %s version: %s", txid, chainID, version)
defer endorserLogger.Debugf("Exit")
var err error
var res *pb.Response
var ccevent *pb.ChaincodeEvent
if txsim != nil {
ctxt = context.WithValue(ctxt, chaincode.TXSimulatorKey, txsim)
}
//is this a system chaincode
scc := syscc.IsSysCC(cid.Name)
cccid := ccprovider.NewCCContext(chainID, cid.Name, version, txid, scc, signedProp, prop)
res, ccevent, err = chaincode.ExecuteChaincode(ctxt, cccid, cis.ChaincodeSpec.Input.Args)
if err != nil {
return nil, nil, err
}
//per doc anything < 400 can be sent as TX.
//fabric errors will always be >= 400 (ie, unambiguous errors )
//"lscc" will respond with status 200 or 500 (ie, unambiguous OK or ERROR)
if res.Status >= shim.ERRORTHRESHOLD {
return res, nil, nil
}
//----- BEGIN - SECTION THAT MAY NEED TO BE DONE IN LSCC ------
//if this a call to deploy a chaincode, We need a mechanism
//to pass TxSimulator into LSCC. Till that is worked out this
//special code does the actual deploy, upgrade here so as to collect
//all state under one TxSimulator
//
//NOTE that if there's an error all simulation, including the chaincode
//table changes in lscc will be thrown away
if cid.Name == "lscc" && len(cis.ChaincodeSpec.Input.Args) >= 3 && (string(cis.ChaincodeSpec.Input.Args[0]) == "deploy" || string(cis.ChaincodeSpec.Input.Args[0]) == "upgrade") {
var cds *pb.ChaincodeDeploymentSpec
cds, err = putils.GetChaincodeDeploymentSpec(cis.ChaincodeSpec.Input.Args[2])
if err != nil {
return nil, nil, err
}
//this should not be a system chaincode
if syscc.IsSysCC(cds.ChaincodeSpec.ChaincodeId.Name) {
return nil, nil, fmt.Errorf("attempting to deploy a system chaincode %s/%s", cds.ChaincodeSpec.ChaincodeId.Name, chainID)
}
cccid = ccprovider.NewCCContext(chainID, cds.ChaincodeSpec.ChaincodeId.Name, cds.ChaincodeSpec.ChaincodeId.Version, txid, false, signedProp, prop)
_, _, err = chaincode.Execute(ctxt, cccid, cds)
if err != nil {
return nil, nil, fmt.Errorf("%s", err)
}
}
//----- END -------
return res, ccevent, err
}
//TO BE REMOVED WHEN JAVA CC IS ENABLED
//disableJavaCCInst if trying to install, instantiate or upgrade Java CC
func (e *Endorser) disableJavaCCInst(cid *pb.ChaincodeID, cis *pb.ChaincodeInvocationSpec) error {
//if not lscc we don't care
if cid.Name != "lscc" {
return nil
}
//non-nil spec ? leave it to callers to handle error if this is an error
if cis.ChaincodeSpec == nil || cis.ChaincodeSpec.Input == nil {
return nil
}
//should at least have a command arg, leave it to callers if this is an error
if len(cis.ChaincodeSpec.Input.Args) < 1 {
return nil
}
var argNo int
switch string(cis.ChaincodeSpec.Input.Args[0]) {
case "install":
argNo = 1
case "deploy", "upgrade":
argNo = 2
default:
//what else can it be ? leave it caller to handle it if error
return nil
}
if argNo >= len(cis.ChaincodeSpec.Input.Args) {
return errors.New("Too few arguments passed")
}
//the inner dep spec will contain the type
ccpack, err := ccprovider.GetCCPackage(cis.ChaincodeSpec.Input.Args[argNo])
if err != nil {
return err
}
cds := ccpack.GetDepSpec()
//finally, if JAVA error out
if cds.ChaincodeSpec.Type == pb.ChaincodeSpec_JAVA {
return fmt.Errorf("Java chaincode is work-in-progress and disabled")
}
//not a java install, instantiate or upgrade op
return nil
}
//simulate the proposal by calling the chaincode
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) {
endorserLogger.Debugf("Entry - txid: %s channel id: %s", txid, chainID)
defer endorserLogger.Debugf("Exit")
//we do expect the payload to be a ChaincodeInvocationSpec
//if we are supporting other payloads in future, this be glaringly point
//as something that should change
cis, err := putils.GetChaincodeInvocationSpec(prop)
if err != nil {
return nil, nil, nil, nil, err
}
//disable Java install,instantiate,upgrade for now
if err = e.disableJavaCCInst(cid, cis); err != nil {
return nil, nil, nil, nil, err
}
//---1. check ESCC and VSCC for the chaincode
if err = e.checkEsccAndVscc(prop); err != nil {
return nil, nil, nil, nil, err
}
var cdLedger *ccprovider.ChaincodeData
var version string
if !syscc.IsSysCC(cid.Name) {
cdLedger, err = e.getCDSFromLSCC(ctx, chainID, txid, signedProp, prop, cid.Name, txsim)
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("%s - make sure the chaincode %s has been successfully instantiated and try again", err, cid.Name)
}
version = cdLedger.Version
err = ccprovider.CheckInsantiationPolicy(cid.Name, version, cdLedger)
if err != nil {
return nil, nil, nil, nil, err
}
} else {
version = util.GetSysCCVersion()
}
//---3. execute the proposal and get simulation results
var simResult []byte
var res *pb.Response
var ccevent *pb.ChaincodeEvent
res, ccevent, err = e.callChaincode(ctx, chainID, version, txid, signedProp, prop, cis, cid, txsim)
if err != nil {
endorserLogger.Errorf("failed to invoke chaincode %s on transaction %s, error: %s", cid, txid, err)
return nil, nil, nil, nil, err
}
if txsim != nil {
if simResult, err = txsim.GetTxSimulationResults(); err != nil {
return nil, nil, nil, nil, err
}
}
return cdLedger, res, simResult, ccevent, nil
}
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) {
ctxt := ctx
if txsim != nil {
ctxt = context.WithValue(ctx, chaincode.TXSimulatorKey, txsim)
}
return chaincode.GetChaincodeDataFromLSCC(ctxt, txid, signedProp, prop, chainID, chaincodeID)
}
//endorse the proposal by calling the ESCC
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) {
endorserLogger.Debugf("Entry - txid: %s channel id: %s chaincode id: %s", txid, chainID, ccid)
defer endorserLogger.Debugf("Exit")
isSysCC := cd == nil
// 1) extract the name of the escc that is requested to endorse this chaincode
var escc string
//ie, not "lscc" or system chaincodes
if isSysCC {
// FIXME: getCDSFromLSCC seems to fail for lscc - not sure this is expected?
// TODO: who should endorse a call to LSCC?
escc = "escc"
} else {
escc = cd.Escc
if escc == "" { // this should never happen, LSCC always fills this field
panic("No ESCC specified in ChaincodeData")
}
}
endorserLogger.Debugf("info: escc for chaincode id %s is %s", ccid, escc)
// marshalling event bytes
var err error
var eventBytes []byte
if event != nil {
eventBytes, err = putils.GetBytesChaincodeEvent(event)
if err != nil {
return nil, fmt.Errorf("failed to marshal event bytes - %s", err)
}
}
resBytes, err := putils.GetBytesResponse(response)
if err != nil {
return nil, fmt.Errorf("failed to marshal response bytes - %s", err)
}
// set version of executing chaincode
if isSysCC {
// if we want to allow mixed fabric levels we should
// set syscc version to ""
ccid.Version = util.GetSysCCVersion()
} else {
ccid.Version = cd.Version
}
ccidBytes, err := putils.Marshal(ccid)
if err != nil {
return nil, fmt.Errorf("failed to marshal ChaincodeID - %s", err)
}
// 3) call the ESCC we've identified
// arguments:
// args[0] - function name (not used now)
// args[1] - serialized Header object
// args[2] - serialized ChaincodeProposalPayload object
// args[3] - ChaincodeID of executing chaincode
// args[4] - result of executing chaincode
// args[5] - binary blob of simulation results
// args[6] - serialized events
// args[7] - payloadVisibility
args := [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, resBytes, simRes, eventBytes, visibility}
version := util.GetSysCCVersion()
ecccis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeId: &pb.ChaincodeID{Name: escc}, Input: &pb.ChaincodeInput{Args: args}}}
res, _, err := e.callChaincode(ctx, chainID, version, txid, signedProp, proposal, ecccis, &pb.ChaincodeID{Name: escc}, txsim)
if err != nil {
return nil, err
}
if res.Status >= shim.ERRORTHRESHOLD {
return &pb.ProposalResponse{Response: res}, nil
}
prBytes := res.Payload
// Note that we do not extract any simulation results from
// the call to ESCC. This is intentional becuse ESCC is meant
// to endorse (i.e. sign) the simulation results of a chaincode,
// but it can't obviously sign its own. Furthermore, ESCC runs
// on private input (its own signing key) and so if it were to
// produce simulationr results, they are likely to be different
// from other ESCCs, which would stand in the way of the
// endorsement process.
//3 -- respond
pResp, err := putils.GetProposalResponse(prBytes)
if err != nil {
return nil, err
}
return pResp, nil
}
// ProcessProposal process the Proposal
func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) {
endorserLogger.Debugf("Entry")
defer endorserLogger.Debugf("Exit")
// at first, we check whether the message is valid
prop, hdr, hdrExt, err := validation.ValidateProposalMessage(signedProp)
if err != nil {
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
}
chdr, err := putils.UnmarshalChannelHeader(hdr.ChannelHeader)
if err != nil {
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
}
shdr, err := putils.GetSignatureHeader(hdr.SignatureHeader)
if err != nil {
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
}
// block invocations to security-sensitive system chaincodes
if syscc.IsSysCCAndNotInvokableExternal(hdrExt.ChaincodeId.Name) {
endorserLogger.Errorf("Error: an attempt was made by %#v to invoke system chaincode %s",
shdr.Creator, hdrExt.ChaincodeId.Name)
err = fmt.Errorf("Chaincode %s cannot be invoked through a proposal", hdrExt.ChaincodeId.Name)
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
}
chainID := chdr.ChannelId
// Check for uniqueness of prop.TxID with ledger
// Notice that ValidateProposalMessage has already verified
// that TxID is computed properly
txid := chdr.TxId
if txid == "" {
err = errors.New("Invalid txID. It must be different from the empty string.")
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
}
endorserLogger.Debugf("processing txid: %s", txid)
if chainID != "" {
// here we handle uniqueness check and ACLs for proposals targeting a chain
lgr := peer.GetLedger(chainID)
if lgr == nil {
return nil, fmt.Errorf("failure while looking up the ledger %s", chainID)
}
if _, err := lgr.GetTransactionByID(txid); err == nil {
return nil, fmt.Errorf("Duplicate transaction found [%s]. Creator [%x]. [%s]", txid, shdr.Creator, err)
}
// check ACL only for application chaincodes; ACLs
// for system chaincodes are checked elsewhere
if !syscc.IsSysCC(hdrExt.ChaincodeId.Name) {
// check that the proposal complies with the channel's writers
if err = e.checkACL(signedProp, chdr, shdr, hdrExt); err != nil {
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
}
}
} else {
// chainless proposals do not/cannot affect ledger and cannot be submitted as transactions
// ignore uniqueness checks; also, chainless proposals are not validated using the policies
// of the chain since by definition there is no chain; they are validated against the local
// MSP of the peer instead by the call to ValidateProposalMessage above
}
// obtaining once the tx simulator for this proposal. This will be nil
// for chainless proposals
// Also obtain a history query executor for history queries, since tx simulator does not cover history
var txsim ledger.TxSimulator
var historyQueryExecutor ledger.HistoryQueryExecutor
if chainID != "" {
if txsim, err = e.getTxSimulator(chainID); err != nil {
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
}
if historyQueryExecutor, err = e.getHistoryQueryExecutor(chainID); err != nil {
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
}
// Add the historyQueryExecutor to context
// TODO shouldn't we also add txsim to context here as well? Rather than passing txsim parameter
// around separately, since eventually it gets added to context anyways
ctx = context.WithValue(ctx, chaincode.HistoryQueryExecutorKey, historyQueryExecutor)
defer txsim.Done()
}
//this could be a request to a chainless SysCC
// TODO: if the proposal has an extension, it will be of type ChaincodeAction;
// if it's present it means that no simulation is to be performed because
// we're trying to emulate a submitting peer. On the other hand, we need
// to validate the supplied action before endorsing it
//1 -- simulate
cd, res, simulationResult, ccevent, err := e.simulateProposal(ctx, chainID, txid, signedProp, prop, hdrExt.ChaincodeId, txsim)
if err != nil {
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
}
if res != nil {
if res.Status >= shim.ERROR {
endorserLogger.Errorf("simulateProposal() resulted in chaincode response status %d for txid: %s", res.Status, txid)
var cceventBytes []byte
if ccevent != nil {
cceventBytes, err = putils.GetBytesChaincodeEvent(ccevent)
if err != nil {
return nil, fmt.Errorf("failed to marshal event bytes - %s", err)
}
}
pResp, err := putils.CreateProposalResponseFailure(prop.Header, prop.Payload, res, simulationResult, cceventBytes, hdrExt.ChaincodeId, hdrExt.PayloadVisibility)
if err != nil {
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
}
return pResp, &chaincodeError{res.Status, res.Message}
}
}
//2 -- endorse and get a marshalled ProposalResponse message
var pResp *pb.ProposalResponse
//TODO till we implement global ESCC, CSCC for system chaincodes
//chainless proposals (such as CSCC) don't have to be endorsed
if chainID == "" {
pResp = &pb.ProposalResponse{Response: res}
} else {
pResp, err = e.endorseProposal(ctx, chainID, txid, signedProp, prop, res, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeId, txsim, cd)
if err != nil {
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
}
if pResp != nil {
if res.Status >= shim.ERRORTHRESHOLD {
endorserLogger.Debugf("endorseProposal() resulted in chaincode error for txid: %s", txid)
return pResp, &chaincodeError{res.Status, res.Message}
}
}
}
// Set the proposal response payload - it
// contains the "return value" from the
// chaincode invocation
pResp.Response.Payload = res.Payload
return pResp, nil
}
// Only exposed for testing purposes - commit the tx simulation so that
// a deploy transaction is persisted and that chaincode can be invoked.
// This makes the endorser test self-sufficient
func (e *Endorser) commitTxSimulation(proposal *pb.Proposal, chainID string, signer msp.SigningIdentity, pResp *pb.ProposalResponse, blockNumber uint64) error {
tx, err := putils.CreateSignedTx(proposal, signer, pResp)
if err != nil {
return err
}
lgr := peer.GetLedger(chainID)
if lgr == nil {
return fmt.Errorf("failure while looking up the ledger")
}
txBytes, err := proto.Marshal(tx)
if err != nil {
return err
}
block := common.NewBlock(blockNumber, []byte{})
block.Data.Data = [][]byte{txBytes}
block.Header.DataHash = block.Data.Hash()
if err = lgr.Commit(block); err != nil {
return err
}
return nil
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/venjia/fabric.git
git@gitee.com:venjia/fabric.git
venjia
fabric
fabric
v1.0.2

Search

344bd9b3 5694891 D2dac590 5694891