// ValidateProposalMessage checks the validity of a SignedProposal message // this function returns Header and ChaincodeHeaderExtension messages since they // have been unmarshalled and validated func ValidateProposalMessage(signedProp *pb.SignedProposal) (*pb.Proposal, *common.Header, *pb.ChaincodeHeaderExtension, error) { putilsLogger.Infof("ValidateProposalMessage starts for signed proposal %p", signedProp) // extract the Proposal message from signedProp prop, err := utils.GetProposal(signedProp.ProposalBytes) if err != nil { return nil, nil, nil, err } // 1) look at the ProposalHeader hdr, err := utils.GetHeader(prop.Header) if err != nil { return nil, nil, nil, err } // validate the header err = validateCommonHeader(hdr) if err != nil { return nil, nil, nil, err } // validate the signature err = checkSignatureFromCreator(hdr.SignatureHeader.Creator, signedProp.Signature, signedProp.ProposalBytes, hdr.ChainHeader.ChainID) if err != nil { return nil, nil, nil, err } // TODO: ensure that creator can transact with us (some ACLs?) which set of APIs is supposed to give us this info? // TODO: perform a check against replay attacks // continue the validation in a way that depends on the type specified in the header switch common.HeaderType(hdr.ChainHeader.Type) { case common.HeaderType_ENDORSER_TRANSACTION: // validation of the proposal message knowing it's of type CHAINCODE chaincodeHdrExt, err := validateChaincodeProposalMessage(prop, hdr) if err != nil { return nil, nil, nil, err } return prop, hdr, chaincodeHdrExt, err default: return nil, nil, nil, fmt.Errorf("Unsupported proposal type %d", common.HeaderType(hdr.ChainHeader.Type)) } }
// ProcessProposal process the Proposal func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) { // at first, we check whether the message is valid prop, _, hdrExt, err := peer.ValidateProposalMessage(signedProp) if err != nil { return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } hdr, err := putils.GetHeader(prop.Header) if err != nil { return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } chainID := hdr.ChainHeader.ChainID //chainless MSPs have "" chain name ischainless := chaincode.IsChainlessSysCC(hdrExt.ChaincodeID.Name) //chainID should be empty for chainless SysCC (such as CSCC for Join proposal) and for //nothing else if chainID == "" && !ischainless { err = fmt.Errorf("chainID not provided for chaincode %s", hdrExt.ChaincodeID.Name) return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } else if chainID != "" && ischainless { err = fmt.Errorf("chainID %s provided for a chainless syscc", hdrExt.ChaincodeID.Name) return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } //TODO check for uniqueness of prop.TxID with ledger txid := hdr.ChainHeader.TxID if txid == "" { err = fmt.Errorf("Invalid txID") return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } // obtaining once the tx simulator for this proposal. This will be nil // for chainless proposals var txsim ledger.TxSimulator if chainID != "" { if txsim, err = e.getTxSimulator(chainID); err != nil { return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } 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 //TODO what do we do with response ? We need it for Invoke responses for sure //Which field in PayloadResponse will carry return value ? result, simulationResult, ccevent, err := e.simulateProposal(ctx, chainID, txid, prop, hdrExt.ChaincodeID, txsim) if err != nil { return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } //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 ischainless { pResp = &pb.ProposalResponse{Response: &pb.Response{}} } else { pResp, err = e.endorseProposal(ctx, chainID, txid, prop, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeID, txsim) if err != nil { return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } } //TODO what do we do with response ? We need it for Invoke responses for sure // Set the proposal response payload - it // contains the "return value" from the // chaincode invocation pResp.Response.Payload = result return pResp, nil }