// checks for a valid ChainHeader func validateChainHeader(cHdr *common.ChainHeader) error { // check for nil argument if cHdr == nil { return fmt.Errorf("Nil ChainHeader provided") } // validate the header type if common.HeaderType(cHdr.Type) != common.HeaderType_ENDORSER_TRANSACTION && common.HeaderType(cHdr.Type) != common.HeaderType_CONFIGURATION_ITEM && common.HeaderType(cHdr.Type) != common.HeaderType_CONFIGURATION_TRANSACTION { return fmt.Errorf("invalid header type %s", common.HeaderType(cHdr.Type)) } putilsLogger.Infof("validateChainHeader info: header type %d", common.HeaderType(cHdr.Type)) // TODO: validate chainID in cHdr.ChainID // TODO: validate epoch in cHdr.Epoch // TODO: validate type in cHdr.Type // TODO: validate version in cHdr.Version return nil }
// 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)) } }
// ValidateTransaction checks that the transaction envelope is properly formed func ValidateTransaction(e *common.Envelope) (*common.Payload, []*pb.TransactionAction, error) { putilsLogger.Infof("ValidateTransactionEnvelope starts for envelope %p", e) // check for nil argument if e == nil { return nil, nil, fmt.Errorf("Nil Envelope") } // get the payload from the envelope payload, err := utils.GetPayload(e) if err != nil { return nil, nil, fmt.Errorf("Could not extract payload from envelope, err %s", err) } putilsLogger.Infof("Header is %s", payload.Header) // validate the header err = validateCommonHeader(payload.Header) if err != nil { return nil, nil, err } // validate the signature in the envelope err = checkSignatureFromCreator(payload.Header.SignatureHeader.Creator, e.Signature, e.Payload, payload.Header.ChainHeader.ChainID) if err != nil { return 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(payload.Header.ChainHeader.Type) { case common.HeaderType_ENDORSER_TRANSACTION: rv, err := validateEndorserTransaction(payload.Data, payload.Header) putilsLogger.Infof("ValidateTransactionEnvelope returns %p, err %s", rv, err) return payload, rv, err default: return nil, nil, fmt.Errorf("Unsupported transaction payload type %d", common.HeaderType(payload.Header.ChainHeader.Type)) } }
// GetChaincodeHeaderExtension get chaincode header extension given header func GetChaincodeHeaderExtension(hdr *common.Header) (*peer.ChaincodeHeaderExtension, error) { if common.HeaderType(hdr.ChainHeader.Type) != common.HeaderType_ENDORSER_TRANSACTION { return nil, fmt.Errorf("invalid proposal type expected ENDORSER_TRANSACTION") } chaincodeHdrExt := &peer.ChaincodeHeaderExtension{} err := proto.Unmarshal(hdr.ChainHeader.Extension, chaincodeHdrExt) if err != nil { return nil, err } return chaincodeHdrExt, nil }
// GetChaincodeInvocationSpec get the ChaincodeInvocationSpec from the proposal func GetChaincodeInvocationSpec(prop *peer.Proposal) (*peer.ChaincodeInvocationSpec, error) { txhdr := &common.Header{} err := proto.Unmarshal(prop.Header, txhdr) if err != nil { return nil, err } if common.HeaderType(txhdr.ChainHeader.Type) != common.HeaderType_ENDORSER_TRANSACTION { return nil, fmt.Errorf("invalid proposal type expected ENDORSER_TRANSACTION") } ccPropPayload := &peer.ChaincodeProposalPayload{} err = proto.Unmarshal(prop.Payload, ccPropPayload) if err != nil { return nil, err } cis := &peer.ChaincodeInvocationSpec{} err = proto.Unmarshal(ccPropPayload.Input, cis) if err != nil { return nil, err } return cis, nil }
// SendProducerBlockEvent sends block event to clients func SendProducerBlockEvent(block *common.Block) error { bevent := &common.Block{} bevent.Header = block.Header bevent.Metadata = block.Metadata bevent.Data = &common.BlockData{} for _, d := range block.Data.Data { if d != nil { if env, err := utils.GetEnvelopeFromBlock(d); err != nil { logger.Errorf("Error getting tx from block(%s)\n", err) } else if env != nil { // get the payload from the envelope payload, err := utils.GetPayload(env) if err != nil { return fmt.Errorf("Could not extract payload from envelope, err %s", err) } if common.HeaderType(payload.Header.ChainHeader.Type) == common.HeaderType_ENDORSER_TRANSACTION { tx, err := utils.GetTransaction(payload.Data) if err != nil { logger.Errorf("Error unmarshalling transaction payload for block event: %s", err) continue } chaincodeActionPayload := &pb.ChaincodeActionPayload{} err = proto.Unmarshal(tx.Actions[0].Payload, chaincodeActionPayload) if err != nil { logger.Errorf("Error unmarshalling transaction action payload for block event: %s", err) continue } propRespPayload := &pb.ProposalResponsePayload{} err = proto.Unmarshal(chaincodeActionPayload.Action.ProposalResponsePayload, propRespPayload) if err != nil { logger.Errorf("Error unmarshalling proposal response payload for block event: %s", err) continue } //ENDORSER_ACTION, ProposalResponsePayload.Extension field contains ChaincodeAction caPayload := &pb.ChaincodeAction{} err = proto.Unmarshal(propRespPayload.Extension, caPayload) if err != nil { logger.Errorf("Error unmarshalling chaincode action for block event: %s", err) continue } // Drop read write set from transaction before sending block event caPayload.Results = nil propRespPayload.Extension, err = proto.Marshal(caPayload) if err != nil { logger.Errorf("Error marshalling tx proposal extension payload for block event: %s", err) continue } // Marshal Transaction again and append to block to be sent chaincodeActionPayload.Action.ProposalResponsePayload, err = proto.Marshal(propRespPayload) if err != nil { logger.Errorf("Error marshalling tx proposal payload for block event: %s", err) continue } tx.Actions[0].Payload, err = proto.Marshal(chaincodeActionPayload) if err != nil { logger.Errorf("Error marshalling tx action payload for block event: %s", err) continue } if t, err := proto.Marshal(tx); err == nil { bevent.Data.Data = append(bevent.Data.Data, t) logger.Infof("calling sendProducerBlockEvent\n") } else { logger.Infof("Cannot marshal transaction %s\n", err) } } } } } return Send(CreateBlockEvent(bevent)) }
// Invoke is called to validate the specified block of transactions // This validation system chaincode will check the read-write set validity and at least 1 // correct endorsement. Later we can create more validation system // chaincodes to provide more sophisticated policy processing such as enabling // policy specification to be coded as a transaction of the chaincode and the client // selecting which policy to use for validation using parameter function // @return serialized Block of valid and invalid transactions indentified // Note that Peer calls this function with 2 arguments, where args[0] is the // function name and args[1] is the Envelope func (vscc *ValidatorOneValidSignature) Invoke(stub shim.ChaincodeStubInterface) ([]byte, error) { // args[0] - function name (not used now) // args[1] - serialized Envelope args := stub.GetArgs() if len(args) < 2 { return nil, errors.New("Incorrect number of arguments") } if args[1] == nil { return nil, errors.New("No block to validate") } logger.Infof("VSCC invoked") // get the envelope... env, err := utils.GetEnvelope(args[1]) if err != nil { logger.Errorf("VSCC error: GetEnvelope failed, err %s", err) return nil, err } // ...and the payload... payl, err := utils.GetPayload(env) if err != nil { logger.Errorf("VSCC error: GetPayload failed, err %s", err) return nil, err } // validate the payload type if common.HeaderType(payl.Header.ChainHeader.Type) != common.HeaderType_ENDORSER_TRANSACTION { logger.Errorf("Only Endorser Transactions are supported, provided type %d", payl.Header.ChainHeader.Type) return nil, fmt.Errorf("Only Endorser Transactions are supported, provided type %d", payl.Header.ChainHeader.Type) } // ...and the transaction... tx, err := utils.GetTransaction(payl.Data) if err != nil { logger.Errorf("VSCC error: GetTransaction failed, err %s", err) return nil, err } // loop through each of the actions within for _, act := range tx.Actions { cap, err := utils.GetChaincodeActionPayload(act.Payload) if err != nil { logger.Errorf("VSCC error: GetChaincodeActionPayload failed, err %s", err) return nil, err } // this is what is being signed prespBytes := cap.Action.ProposalResponsePayload // loop through each of the endorsements for _, endorsement := range cap.Action.Endorsements { // extract the identity of the signer end, err := mspmgmt.GetManagerForChain(payl.Header.ChainHeader.ChainID).DeserializeIdentity(endorsement.Endorser) if err != nil { logger.Errorf("VSCC error: DeserializeIdentity failed, err %s", err) return nil, err } // validate it err = end.Validate() if err != nil { return nil, fmt.Errorf("Invalid endorser identity, err %s", err) } // verify the signature err = end.Verify(append(prespBytes, endorsement.Endorser...), endorsement.Signature) if err != nil { return nil, fmt.Errorf("Invalid signature, err %s", err) } } } logger.Infof("VSCC exists successfully") return nil, nil }