github.com/true-sqn/fabric@v2.1.1+incompatible/protoutil/txutils.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package protoutil
     8  
     9  import (
    10  	"bytes"
    11  	"crypto/sha256"
    12  
    13  	"github.com/golang/protobuf/proto"
    14  	"github.com/hyperledger/fabric-protos-go/common"
    15  	"github.com/hyperledger/fabric-protos-go/peer"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  // GetPayloads gets the underlying payload objects in a TransactionAction
    20  func GetPayloads(txActions *peer.TransactionAction) (*peer.ChaincodeActionPayload, *peer.ChaincodeAction, error) {
    21  	// TODO: pass in the tx type (in what follows we're assuming the
    22  	// type is ENDORSER_TRANSACTION)
    23  	ccPayload, err := UnmarshalChaincodeActionPayload(txActions.Payload)
    24  	if err != nil {
    25  		return nil, nil, err
    26  	}
    27  
    28  	if ccPayload.Action == nil || ccPayload.Action.ProposalResponsePayload == nil {
    29  		return nil, nil, errors.New("no payload in ChaincodeActionPayload")
    30  	}
    31  	pRespPayload, err := UnmarshalProposalResponsePayload(ccPayload.Action.ProposalResponsePayload)
    32  	if err != nil {
    33  		return nil, nil, err
    34  	}
    35  
    36  	if pRespPayload.Extension == nil {
    37  		return nil, nil, errors.New("response payload is missing extension")
    38  	}
    39  
    40  	respPayload, err := UnmarshalChaincodeAction(pRespPayload.Extension)
    41  	if err != nil {
    42  		return ccPayload, nil, err
    43  	}
    44  	return ccPayload, respPayload, nil
    45  }
    46  
    47  // GetEnvelopeFromBlock gets an envelope from a block's Data field.
    48  func GetEnvelopeFromBlock(data []byte) (*common.Envelope, error) {
    49  	// Block always begins with an envelope
    50  	var err error
    51  	env := &common.Envelope{}
    52  	if err = proto.Unmarshal(data, env); err != nil {
    53  		return nil, errors.Wrap(err, "error unmarshaling Envelope")
    54  	}
    55  
    56  	return env, nil
    57  }
    58  
    59  // CreateSignedEnvelope creates a signed envelope of the desired type, with
    60  // marshaled dataMsg and signs it
    61  func CreateSignedEnvelope(
    62  	txType common.HeaderType,
    63  	channelID string,
    64  	signer Signer,
    65  	dataMsg proto.Message,
    66  	msgVersion int32,
    67  	epoch uint64,
    68  ) (*common.Envelope, error) {
    69  	return CreateSignedEnvelopeWithTLSBinding(txType, channelID, signer, dataMsg, msgVersion, epoch, nil)
    70  }
    71  
    72  // CreateSignedEnvelopeWithTLSBinding creates a signed envelope of the desired
    73  // type, with marshaled dataMsg and signs it. It also includes a TLS cert hash
    74  // into the channel header
    75  func CreateSignedEnvelopeWithTLSBinding(
    76  	txType common.HeaderType,
    77  	channelID string,
    78  	signer Signer,
    79  	dataMsg proto.Message,
    80  	msgVersion int32,
    81  	epoch uint64,
    82  	tlsCertHash []byte,
    83  ) (*common.Envelope, error) {
    84  	payloadChannelHeader := MakeChannelHeader(txType, msgVersion, channelID, epoch)
    85  	payloadChannelHeader.TlsCertHash = tlsCertHash
    86  	var err error
    87  	payloadSignatureHeader := &common.SignatureHeader{}
    88  
    89  	if signer != nil {
    90  		payloadSignatureHeader, err = NewSignatureHeader(signer)
    91  		if err != nil {
    92  			return nil, err
    93  		}
    94  	}
    95  
    96  	data, err := proto.Marshal(dataMsg)
    97  	if err != nil {
    98  		return nil, errors.Wrap(err, "error marshaling")
    99  	}
   100  
   101  	paylBytes := MarshalOrPanic(
   102  		&common.Payload{
   103  			Header: MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader),
   104  			Data:   data,
   105  		},
   106  	)
   107  
   108  	var sig []byte
   109  	if signer != nil {
   110  		sig, err = signer.Sign(paylBytes)
   111  		if err != nil {
   112  			return nil, err
   113  		}
   114  	}
   115  
   116  	env := &common.Envelope{
   117  		Payload:   paylBytes,
   118  		Signature: sig,
   119  	}
   120  
   121  	return env, nil
   122  }
   123  
   124  // Signer is the interface needed to sign a transaction
   125  type Signer interface {
   126  	Sign(msg []byte) ([]byte, error)
   127  	Serialize() ([]byte, error)
   128  }
   129  
   130  // CreateSignedTx assembles an Envelope message from proposal, endorsements,
   131  // and a signer. This function should be called by a client when it has
   132  // collected enough endorsements for a proposal to create a transaction and
   133  // submit it to peers for ordering
   134  func CreateSignedTx(
   135  	proposal *peer.Proposal,
   136  	signer Signer,
   137  	resps ...*peer.ProposalResponse,
   138  ) (*common.Envelope, error) {
   139  	if len(resps) == 0 {
   140  		return nil, errors.New("at least one proposal response is required")
   141  	}
   142  
   143  	// the original header
   144  	hdr, err := UnmarshalHeader(proposal.Header)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	// the original payload
   150  	pPayl, err := UnmarshalChaincodeProposalPayload(proposal.Payload)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	// check that the signer is the same that is referenced in the header
   156  	// TODO: maybe worth removing?
   157  	signerBytes, err := signer.Serialize()
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	shdr, err := UnmarshalSignatureHeader(hdr.SignatureHeader)
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  
   167  	if !bytes.Equal(signerBytes, shdr.Creator) {
   168  		return nil, errors.New("signer must be the same as the one referenced in the header")
   169  	}
   170  
   171  	// ensure that all actions are bitwise equal and that they are successful
   172  	var a1 []byte
   173  	for n, r := range resps {
   174  		if r.Response.Status < 200 || r.Response.Status >= 400 {
   175  			return nil, errors.Errorf("proposal response was not successful, error code %d, msg %s", r.Response.Status, r.Response.Message)
   176  		}
   177  
   178  		if n == 0 {
   179  			a1 = r.Payload
   180  			continue
   181  		}
   182  
   183  		if !bytes.Equal(a1, r.Payload) {
   184  			return nil, errors.New("ProposalResponsePayloads do not match")
   185  		}
   186  	}
   187  
   188  	// fill endorsements
   189  	endorsements := make([]*peer.Endorsement, len(resps))
   190  	for n, r := range resps {
   191  		endorsements[n] = r.Endorsement
   192  	}
   193  
   194  	// create ChaincodeEndorsedAction
   195  	cea := &peer.ChaincodeEndorsedAction{ProposalResponsePayload: resps[0].Payload, Endorsements: endorsements}
   196  
   197  	// obtain the bytes of the proposal payload that will go to the transaction
   198  	propPayloadBytes, err := GetBytesProposalPayloadForTx(pPayl)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	// serialize the chaincode action payload
   204  	cap := &peer.ChaincodeActionPayload{ChaincodeProposalPayload: propPayloadBytes, Action: cea}
   205  	capBytes, err := GetBytesChaincodeActionPayload(cap)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  
   210  	// create a transaction
   211  	taa := &peer.TransactionAction{Header: hdr.SignatureHeader, Payload: capBytes}
   212  	taas := make([]*peer.TransactionAction, 1)
   213  	taas[0] = taa
   214  	tx := &peer.Transaction{Actions: taas}
   215  
   216  	// serialize the tx
   217  	txBytes, err := GetBytesTransaction(tx)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  
   222  	// create the payload
   223  	payl := &common.Payload{Header: hdr, Data: txBytes}
   224  	paylBytes, err := GetBytesPayload(payl)
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  
   229  	// sign the payload
   230  	sig, err := signer.Sign(paylBytes)
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  
   235  	// here's the envelope
   236  	return &common.Envelope{Payload: paylBytes, Signature: sig}, nil
   237  }
   238  
   239  // CreateProposalResponse creates a proposal response.
   240  func CreateProposalResponse(
   241  	hdrbytes []byte,
   242  	payl []byte,
   243  	response *peer.Response,
   244  	results []byte,
   245  	events []byte,
   246  	ccid *peer.ChaincodeID,
   247  	signingEndorser Signer,
   248  ) (*peer.ProposalResponse, error) {
   249  	hdr, err := UnmarshalHeader(hdrbytes)
   250  	if err != nil {
   251  		return nil, err
   252  	}
   253  
   254  	// obtain the proposal hash given proposal header, payload and the
   255  	// requested visibility
   256  	pHashBytes, err := GetProposalHash1(hdr, payl)
   257  	if err != nil {
   258  		return nil, errors.WithMessage(err, "error computing proposal hash")
   259  	}
   260  
   261  	// get the bytes of the proposal response payload - we need to sign them
   262  	prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events, ccid)
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  
   267  	// serialize the signing identity
   268  	endorser, err := signingEndorser.Serialize()
   269  	if err != nil {
   270  		return nil, errors.WithMessage(err, "error serializing signing identity")
   271  	}
   272  
   273  	// sign the concatenation of the proposal response and the serialized
   274  	// endorser identity with this endorser's key
   275  	signature, err := signingEndorser.Sign(append(prpBytes, endorser...))
   276  	if err != nil {
   277  		return nil, errors.WithMessage(err, "could not sign the proposal response payload")
   278  	}
   279  
   280  	resp := &peer.ProposalResponse{
   281  		// Timestamp: TODO!
   282  		Version: 1, // TODO: pick right version number
   283  		Endorsement: &peer.Endorsement{
   284  			Signature: signature,
   285  			Endorser:  endorser,
   286  		},
   287  		Payload: prpBytes,
   288  		Response: &peer.Response{
   289  			Status:  200,
   290  			Message: "OK",
   291  		},
   292  	}
   293  
   294  	return resp, nil
   295  }
   296  
   297  // CreateProposalResponseFailure creates a proposal response for cases where
   298  // endorsement proposal fails either due to a endorsement failure or a
   299  // chaincode failure (chaincode response status >= shim.ERRORTHRESHOLD)
   300  func CreateProposalResponseFailure(
   301  	hdrbytes []byte,
   302  	payl []byte,
   303  	response *peer.Response,
   304  	results []byte,
   305  	events []byte,
   306  	chaincodeName string,
   307  ) (*peer.ProposalResponse, error) {
   308  	hdr, err := UnmarshalHeader(hdrbytes)
   309  	if err != nil {
   310  		return nil, err
   311  	}
   312  
   313  	// obtain the proposal hash given proposal header, payload and the requested visibility
   314  	pHashBytes, err := GetProposalHash1(hdr, payl)
   315  	if err != nil {
   316  		return nil, errors.WithMessage(err, "error computing proposal hash")
   317  	}
   318  
   319  	// get the bytes of the proposal response payload
   320  	prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events, &peer.ChaincodeID{Name: chaincodeName})
   321  	if err != nil {
   322  		return nil, err
   323  	}
   324  
   325  	resp := &peer.ProposalResponse{
   326  		// Timestamp: TODO!
   327  		Payload:  prpBytes,
   328  		Response: response,
   329  	}
   330  
   331  	return resp, nil
   332  }
   333  
   334  // GetSignedProposal returns a signed proposal given a Proposal message and a
   335  // signing identity
   336  func GetSignedProposal(prop *peer.Proposal, signer Signer) (*peer.SignedProposal, error) {
   337  	// check for nil argument
   338  	if prop == nil || signer == nil {
   339  		return nil, errors.New("nil arguments")
   340  	}
   341  
   342  	propBytes, err := proto.Marshal(prop)
   343  	if err != nil {
   344  		return nil, err
   345  	}
   346  
   347  	signature, err := signer.Sign(propBytes)
   348  	if err != nil {
   349  		return nil, err
   350  	}
   351  
   352  	return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, nil
   353  }
   354  
   355  // MockSignedEndorserProposalOrPanic creates a SignedProposal with the
   356  // passed arguments
   357  func MockSignedEndorserProposalOrPanic(
   358  	channelID string,
   359  	cs *peer.ChaincodeSpec,
   360  	creator,
   361  	signature []byte,
   362  ) (*peer.SignedProposal, *peer.Proposal) {
   363  	prop, _, err := CreateChaincodeProposal(
   364  		common.HeaderType_ENDORSER_TRANSACTION,
   365  		channelID,
   366  		&peer.ChaincodeInvocationSpec{ChaincodeSpec: cs},
   367  		creator)
   368  	if err != nil {
   369  		panic(err)
   370  	}
   371  
   372  	propBytes, err := proto.Marshal(prop)
   373  	if err != nil {
   374  		panic(err)
   375  	}
   376  
   377  	return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, prop
   378  }
   379  
   380  func MockSignedEndorserProposal2OrPanic(
   381  	channelID string,
   382  	cs *peer.ChaincodeSpec,
   383  	signer Signer,
   384  ) (*peer.SignedProposal, *peer.Proposal) {
   385  	serializedSigner, err := signer.Serialize()
   386  	if err != nil {
   387  		panic(err)
   388  	}
   389  
   390  	prop, _, err := CreateChaincodeProposal(
   391  		common.HeaderType_ENDORSER_TRANSACTION,
   392  		channelID,
   393  		&peer.ChaincodeInvocationSpec{ChaincodeSpec: &peer.ChaincodeSpec{}},
   394  		serializedSigner)
   395  	if err != nil {
   396  		panic(err)
   397  	}
   398  
   399  	sProp, err := GetSignedProposal(prop, signer)
   400  	if err != nil {
   401  		panic(err)
   402  	}
   403  
   404  	return sProp, prop
   405  }
   406  
   407  // GetBytesProposalPayloadForTx takes a ChaincodeProposalPayload and returns
   408  // its serialized version according to the visibility field
   409  func GetBytesProposalPayloadForTx(
   410  	payload *peer.ChaincodeProposalPayload,
   411  ) ([]byte, error) {
   412  	// check for nil argument
   413  	if payload == nil {
   414  		return nil, errors.New("nil arguments")
   415  	}
   416  
   417  	// strip the transient bytes off the payload
   418  	cppNoTransient := &peer.ChaincodeProposalPayload{Input: payload.Input, TransientMap: nil}
   419  	cppBytes, err := GetBytesChaincodeProposalPayload(cppNoTransient)
   420  	if err != nil {
   421  		return nil, err
   422  	}
   423  
   424  	return cppBytes, nil
   425  }
   426  
   427  // GetProposalHash2 gets the proposal hash - this version
   428  // is called by the committer where the visibility policy
   429  // has already been enforced and so we already get what
   430  // we have to get in ccPropPayl
   431  func GetProposalHash2(header *common.Header, ccPropPayl []byte) ([]byte, error) {
   432  	// check for nil argument
   433  	if header == nil ||
   434  		header.ChannelHeader == nil ||
   435  		header.SignatureHeader == nil ||
   436  		ccPropPayl == nil {
   437  		return nil, errors.New("nil arguments")
   438  	}
   439  
   440  	hash := sha256.New()
   441  	// hash the serialized Channel Header object
   442  	hash.Write(header.ChannelHeader)
   443  	// hash the serialized Signature Header object
   444  	hash.Write(header.SignatureHeader)
   445  	// hash the bytes of the chaincode proposal payload that we are given
   446  	hash.Write(ccPropPayl)
   447  	return hash.Sum(nil), nil
   448  }
   449  
   450  // GetProposalHash1 gets the proposal hash bytes after sanitizing the
   451  // chaincode proposal payload according to the rules of visibility
   452  func GetProposalHash1(header *common.Header, ccPropPayl []byte) ([]byte, error) {
   453  	// check for nil argument
   454  	if header == nil ||
   455  		header.ChannelHeader == nil ||
   456  		header.SignatureHeader == nil ||
   457  		ccPropPayl == nil {
   458  		return nil, errors.New("nil arguments")
   459  	}
   460  
   461  	// unmarshal the chaincode proposal payload
   462  	cpp, err := UnmarshalChaincodeProposalPayload(ccPropPayl)
   463  	if err != nil {
   464  		return nil, err
   465  	}
   466  
   467  	ppBytes, err := GetBytesProposalPayloadForTx(cpp)
   468  	if err != nil {
   469  		return nil, err
   470  	}
   471  
   472  	hash2 := sha256.New()
   473  	// hash the serialized Channel Header object
   474  	hash2.Write(header.ChannelHeader)
   475  	// hash the serialized Signature Header object
   476  	hash2.Write(header.SignatureHeader)
   477  	// hash of the part of the chaincode proposal payload that will go to the tx
   478  	hash2.Write(ppBytes)
   479  	return hash2.Sum(nil), nil
   480  }
   481  
   482  // GetOrComputeTxIDFromEnvelope gets the txID present in a given transaction
   483  // envelope. If the txID is empty, it constructs the txID from nonce and
   484  // creator fields in the envelope.
   485  func GetOrComputeTxIDFromEnvelope(txEnvelopBytes []byte) (string, error) {
   486  	txEnvelope, err := UnmarshalEnvelope(txEnvelopBytes)
   487  	if err != nil {
   488  		return "", errors.WithMessage(err, "error getting txID from envelope")
   489  	}
   490  
   491  	txPayload, err := UnmarshalPayload(txEnvelope.Payload)
   492  	if err != nil {
   493  		return "", errors.WithMessage(err, "error getting txID from payload")
   494  	}
   495  
   496  	if txPayload.Header == nil {
   497  		return "", errors.New("error getting txID from header: payload header is nil")
   498  	}
   499  
   500  	chdr, err := UnmarshalChannelHeader(txPayload.Header.ChannelHeader)
   501  	if err != nil {
   502  		return "", errors.WithMessage(err, "error getting txID from channel header")
   503  	}
   504  
   505  	if chdr.TxId != "" {
   506  		return chdr.TxId, nil
   507  	}
   508  
   509  	sighdr, err := UnmarshalSignatureHeader(txPayload.Header.SignatureHeader)
   510  	if err != nil {
   511  		return "", errors.WithMessage(err, "error getting nonce and creator for computing txID")
   512  	}
   513  
   514  	txid := ComputeTxID(sighdr.Nonce, sighdr.Creator)
   515  	return txid, nil
   516  }