github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/protos/utils/txutils.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 utils
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  
    23  	"bytes"
    24  
    25  	"github.com/golang/protobuf/proto"
    26  	"github.com/hyperledger/fabric/bccsp"
    27  	"github.com/hyperledger/fabric/bccsp/factory"
    28  	"github.com/hyperledger/fabric/common/crypto"
    29  	"github.com/hyperledger/fabric/msp"
    30  	"github.com/hyperledger/fabric/protos/common"
    31  	"github.com/hyperledger/fabric/protos/peer"
    32  )
    33  
    34  // GetPayloads get's the underlying payload objects in a TransactionAction
    35  func GetPayloads(txActions *peer.TransactionAction) (*peer.ChaincodeActionPayload, *peer.ChaincodeAction, error) {
    36  	// TODO: pass in the tx type (in what follows we're assuming the type is ENDORSER_TRANSACTION)
    37  	ccPayload := &peer.ChaincodeActionPayload{}
    38  	err := proto.Unmarshal(txActions.Payload, ccPayload)
    39  	if err != nil {
    40  		return nil, nil, err
    41  	}
    42  
    43  	if ccPayload.Action == nil || ccPayload.Action.ProposalResponsePayload == nil {
    44  		return nil, nil, fmt.Errorf("no payload in ChaincodeActionPayload")
    45  	}
    46  	pRespPayload := &peer.ProposalResponsePayload{}
    47  	err = proto.Unmarshal(ccPayload.Action.ProposalResponsePayload, pRespPayload)
    48  	if err != nil {
    49  		return nil, nil, err
    50  	}
    51  
    52  	if pRespPayload.Extension == nil {
    53  		return nil, nil, fmt.Errorf("response payload is missing extension")
    54  	}
    55  
    56  	respPayload := &peer.ChaincodeAction{}
    57  	err = proto.Unmarshal(pRespPayload.Extension, respPayload)
    58  	if err != nil {
    59  		return ccPayload, nil, err
    60  	}
    61  	return ccPayload, respPayload, nil
    62  }
    63  
    64  // GetEnvelopeFromBlock gets an envelope from a block's Data field.
    65  func GetEnvelopeFromBlock(data []byte) (*common.Envelope, error) {
    66  	//Block always begins with an envelope
    67  	var err error
    68  	env := &common.Envelope{}
    69  	if err = proto.Unmarshal(data, env); err != nil {
    70  		return nil, fmt.Errorf("Error getting envelope(%s)", err)
    71  	}
    72  
    73  	return env, nil
    74  }
    75  
    76  // CreateSignedEnvelope creates a signed envelope of the desired type, with marshaled dataMsg and signs it
    77  func CreateSignedEnvelope(txType common.HeaderType, channelID string, signer crypto.LocalSigner, dataMsg proto.Message, msgVersion int32, epoch uint64) (*common.Envelope, error) {
    78  	payloadChannelHeader := MakeChannelHeader(txType, msgVersion, channelID, epoch)
    79  
    80  	var err error
    81  	payloadSignatureHeader := &common.SignatureHeader{}
    82  
    83  	if signer != nil {
    84  		payloadSignatureHeader, err = signer.NewSignatureHeader()
    85  		if err != nil {
    86  			return nil, err
    87  		}
    88  	}
    89  
    90  	data, err := proto.Marshal(dataMsg)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	paylBytes := MarshalOrPanic(&common.Payload{
    96  		Header: MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader),
    97  		Data:   data,
    98  	})
    99  
   100  	var sig []byte
   101  	if signer != nil {
   102  		sig, err = signer.Sign(paylBytes)
   103  		if err != nil {
   104  			return nil, err
   105  		}
   106  	}
   107  
   108  	return &common.Envelope{Payload: paylBytes, Signature: sig}, nil
   109  }
   110  
   111  // CreateSignedTx assembles an Envelope message from proposal, endorsements, and a signer.
   112  // This function should be called by a client when it has collected enough endorsements
   113  // for a proposal to create a transaction and submit it to peers for ordering
   114  func CreateSignedTx(proposal *peer.Proposal, signer msp.SigningIdentity, resps ...*peer.ProposalResponse) (*common.Envelope, error) {
   115  	if len(resps) == 0 {
   116  		return nil, fmt.Errorf("At least one proposal response is necessary")
   117  	}
   118  
   119  	// the original header
   120  	hdr, err := GetHeader(proposal.Header)
   121  	if err != nil {
   122  		return nil, fmt.Errorf("Could not unmarshal the proposal header")
   123  	}
   124  
   125  	// the original payload
   126  	pPayl, err := GetChaincodeProposalPayload(proposal.Payload)
   127  	if err != nil {
   128  		return nil, fmt.Errorf("Could not unmarshal the proposal payload")
   129  	}
   130  
   131  	// check that the signer is the same that is referenced in the header
   132  	// TODO: maybe worth removing?
   133  	signerBytes, err := signer.Serialize()
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	shdr, err := GetSignatureHeader(hdr.SignatureHeader)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	if bytes.Compare(signerBytes, shdr.Creator) != 0 {
   144  		return nil, fmt.Errorf("The signer needs to be the same as the one referenced in the header")
   145  	}
   146  
   147  	// get header extensions so we have the visibility field
   148  	hdrExt, err := GetChaincodeHeaderExtension(hdr)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	// ensure that all actions are bitwise equal and that they are successful
   154  	var a1 []byte
   155  	for n, r := range resps {
   156  		if n == 0 {
   157  			a1 = r.Payload
   158  			if r.Response.Status != 200 {
   159  				return nil, fmt.Errorf("Proposal response was not successful, error code %d, msg %s", r.Response.Status, r.Response.Message)
   160  			}
   161  			continue
   162  		}
   163  
   164  		if bytes.Compare(a1, r.Payload) != 0 {
   165  			return nil, fmt.Errorf("ProposalResponsePayloads do not match")
   166  		}
   167  	}
   168  
   169  	// fill endorsements
   170  	endorsements := make([]*peer.Endorsement, len(resps))
   171  	for n, r := range resps {
   172  		endorsements[n] = r.Endorsement
   173  	}
   174  
   175  	// create ChaincodeEndorsedAction
   176  	cea := &peer.ChaincodeEndorsedAction{ProposalResponsePayload: resps[0].Payload, Endorsements: endorsements}
   177  
   178  	// obtain the bytes of the proposal payload that will go to the transaction
   179  	propPayloadBytes, err := GetBytesProposalPayloadForTx(pPayl, hdrExt.PayloadVisibility)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  
   184  	// serialize the chaincode action payload
   185  	cap := &peer.ChaincodeActionPayload{ChaincodeProposalPayload: propPayloadBytes, Action: cea}
   186  	capBytes, err := GetBytesChaincodeActionPayload(cap)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	// create a transaction
   192  	taa := &peer.TransactionAction{Header: hdr.SignatureHeader, Payload: capBytes}
   193  	taas := make([]*peer.TransactionAction, 1)
   194  	taas[0] = taa
   195  	tx := &peer.Transaction{Actions: taas}
   196  
   197  	// serialize the tx
   198  	txBytes, err := GetBytesTransaction(tx)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	// create the payload
   204  	payl := &common.Payload{Header: hdr, Data: txBytes}
   205  	paylBytes, err := GetBytesPayload(payl)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  
   210  	// sign the payload
   211  	sig, err := signer.Sign(paylBytes)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  
   216  	// here's the envelope
   217  	return &common.Envelope{Payload: paylBytes, Signature: sig}, nil
   218  }
   219  
   220  // CreateProposalResponse creates a proposal response.
   221  func CreateProposalResponse(hdrbytes []byte, payl []byte, response *peer.Response, results []byte, events []byte, ccid *peer.ChaincodeID, visibility []byte, signingEndorser msp.SigningIdentity) (*peer.ProposalResponse, error) {
   222  	hdr, err := GetHeader(hdrbytes)
   223  	if err != nil {
   224  		return nil, err
   225  	}
   226  
   227  	// obtain the proposal hash given proposal header, payload and the requested visibility
   228  	pHashBytes, err := GetProposalHash1(hdr, payl, visibility)
   229  	if err != nil {
   230  		return nil, fmt.Errorf("Could not compute proposal hash: err %s", err)
   231  	}
   232  
   233  	// get the bytes of the proposal response payload - we need to sign them
   234  	prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events, ccid)
   235  	if err != nil {
   236  		return nil, errors.New("Failure while marshaling the ProposalResponsePayload")
   237  	}
   238  
   239  	// serialize the signing identity
   240  	endorser, err := signingEndorser.Serialize()
   241  	if err != nil {
   242  		return nil, fmt.Errorf("Could not serialize the signing identity for %s, err %s", signingEndorser.GetIdentifier(), err)
   243  	}
   244  
   245  	// sign the concatenation of the proposal response and the serialized endorser identity with this endorser's key
   246  	signature, err := signingEndorser.Sign(append(prpBytes, endorser...))
   247  	if err != nil {
   248  		return nil, fmt.Errorf("Could not sign the proposal response payload, err %s", err)
   249  	}
   250  
   251  	resp := &peer.ProposalResponse{
   252  		// Timestamp: TODO!
   253  		Version:     1, // TODO: pick right version number
   254  		Endorsement: &peer.Endorsement{Signature: signature, Endorser: endorser},
   255  		Payload:     prpBytes,
   256  		Response:    &peer.Response{Status: 200, Message: "OK"}}
   257  
   258  	return resp, nil
   259  }
   260  
   261  // CreateProposalResponseFailure creates a proposal response for cases where
   262  // endorsement proposal fails either due to a endorsement failure or a chaincode
   263  // failure (chaincode response status >= shim.ERRORTHRESHOLD)
   264  func CreateProposalResponseFailure(hdrbytes []byte, payl []byte, response *peer.Response, results []byte, events []byte, ccid *peer.ChaincodeID, visibility []byte) (*peer.ProposalResponse, error) {
   265  	hdr, err := GetHeader(hdrbytes)
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  
   270  	// obtain the proposal hash given proposal header, payload and the requested visibility
   271  	pHashBytes, err := GetProposalHash1(hdr, payl, visibility)
   272  	if err != nil {
   273  		return nil, fmt.Errorf("Could not compute proposal hash: err %s", err)
   274  	}
   275  
   276  	// get the bytes of the proposal response payload
   277  	prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events, ccid)
   278  	if err != nil {
   279  		return nil, errors.New("Failure while marshaling the ProposalResponsePayload")
   280  	}
   281  
   282  	resp := &peer.ProposalResponse{
   283  		// Timestamp: TODO!
   284  		Payload:  prpBytes,
   285  		Response: &peer.Response{Status: 500, Message: "Chaincode Error"}}
   286  
   287  	return resp, nil
   288  }
   289  
   290  // GetSignedProposal returns a signed proposal given a Proposal message and a signing identity
   291  func GetSignedProposal(prop *peer.Proposal, signer msp.SigningIdentity) (*peer.SignedProposal, error) {
   292  	// check for nil argument
   293  	if prop == nil || signer == nil {
   294  		return nil, fmt.Errorf("Nil arguments")
   295  	}
   296  
   297  	propBytes, err := GetBytesProposal(prop)
   298  	if err != nil {
   299  		return nil, err
   300  	}
   301  
   302  	signature, err := signer.Sign(propBytes)
   303  	if err != nil {
   304  		return nil, err
   305  	}
   306  
   307  	return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, nil
   308  }
   309  
   310  // GetSignedEvent returns a signed event given an Event message and a signing identity
   311  func GetSignedEvent(evt *peer.Event, signer msp.SigningIdentity) (*peer.SignedEvent, error) {
   312  	// check for nil argument
   313  	if evt == nil || signer == nil {
   314  		return nil, errors.New("nil arguments")
   315  	}
   316  
   317  	evtBytes, err := proto.Marshal(evt)
   318  	if err != nil {
   319  		return nil, err
   320  	}
   321  
   322  	signature, err := signer.Sign(evtBytes)
   323  	if err != nil {
   324  		return nil, err
   325  	}
   326  
   327  	return &peer.SignedEvent{EventBytes: evtBytes, Signature: signature}, nil
   328  }
   329  
   330  // MockSignedEndorserProposalOrPanic creates a SignedProposal with the passed arguments
   331  func MockSignedEndorserProposalOrPanic(chainID string, cs *peer.ChaincodeSpec, creator, signature []byte) (*peer.SignedProposal, *peer.Proposal) {
   332  	prop, _, err := CreateChaincodeProposal(
   333  		common.HeaderType_ENDORSER_TRANSACTION,
   334  		chainID,
   335  		&peer.ChaincodeInvocationSpec{ChaincodeSpec: cs},
   336  		creator)
   337  	if err != nil {
   338  		panic(err)
   339  	}
   340  
   341  	propBytes, err := GetBytesProposal(prop)
   342  	if err != nil {
   343  		panic(err)
   344  	}
   345  
   346  	return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, prop
   347  }
   348  
   349  func MockSignedEndorserProposal2OrPanic(chainID string, cs *peer.ChaincodeSpec, signer msp.SigningIdentity) (*peer.SignedProposal, *peer.Proposal) {
   350  	serializedSigner, err := signer.Serialize()
   351  	if err != nil {
   352  		panic(err)
   353  	}
   354  
   355  	prop, _, err := CreateChaincodeProposal(
   356  		common.HeaderType_ENDORSER_TRANSACTION,
   357  		chainID,
   358  		&peer.ChaincodeInvocationSpec{ChaincodeSpec: &peer.ChaincodeSpec{}},
   359  		serializedSigner)
   360  	if err != nil {
   361  		panic(err)
   362  	}
   363  
   364  	sProp, err := GetSignedProposal(prop, signer)
   365  	if err != nil {
   366  		panic(err)
   367  	}
   368  
   369  	return sProp, prop
   370  }
   371  
   372  // GetBytesProposalPayloadForTx takes a ChaincodeProposalPayload and returns its serialized
   373  // version according to the visibility field
   374  func GetBytesProposalPayloadForTx(payload *peer.ChaincodeProposalPayload, visibility []byte) ([]byte, error) {
   375  	// check for nil argument
   376  	if payload == nil /* || visibility == nil */ {
   377  		return nil, fmt.Errorf("Nil arguments")
   378  	}
   379  
   380  	// strip the transient bytes off the payload - this needs to be done no matter the visibility mode
   381  	cppNoTransient := &peer.ChaincodeProposalPayload{Input: payload.Input, TransientMap: nil}
   382  	cppBytes, err := GetBytesChaincodeProposalPayload(cppNoTransient)
   383  	if err != nil {
   384  		return nil, errors.New("Failure while marshalling the ChaincodeProposalPayload!")
   385  	}
   386  
   387  	// currently the fabric only supports full visibility: this means that
   388  	// there are no restrictions on which parts of the proposal payload will
   389  	// be visible in the final transaction; this default approach requires
   390  	// no additional instructions in the PayloadVisibility field; however
   391  	// the fabric may be extended to encode more elaborate visibility
   392  	// mechanisms that shall be encoded in this field (and handled
   393  	// appropriately by the peer)
   394  
   395  	return cppBytes, nil
   396  }
   397  
   398  // GetProposalHash2 gets the proposal hash - this version
   399  // is called by the committer where the visibility policy
   400  // has already been enforced and so we already get what
   401  // we have to get in ccPropPayl
   402  func GetProposalHash2(header *common.Header, ccPropPayl []byte) ([]byte, error) {
   403  	// check for nil argument
   404  	if header == nil ||
   405  		header.ChannelHeader == nil ||
   406  		header.SignatureHeader == nil ||
   407  		ccPropPayl == nil {
   408  		return nil, fmt.Errorf("Nil arguments")
   409  	}
   410  
   411  	hash, err := factory.GetDefault().GetHash(&bccsp.SHA256Opts{})
   412  	if err != nil {
   413  		return nil, fmt.Errorf("Failed instantiating hash function [%s]", err)
   414  	}
   415  	hash.Write(header.ChannelHeader)   // hash the serialized Channel Header object
   416  	hash.Write(header.SignatureHeader) // hash the serialized Signature Header object
   417  	hash.Write(ccPropPayl)             // hash the bytes of the chaincode proposal payload that we are given
   418  
   419  	return hash.Sum(nil), nil
   420  }
   421  
   422  // GetProposalHash1 gets the proposal hash bytes after sanitizing the
   423  // chaincode proposal payload according to the rules of visibility
   424  func GetProposalHash1(header *common.Header, ccPropPayl []byte, visibility []byte) ([]byte, error) {
   425  	// check for nil argument
   426  	if header == nil ||
   427  		header.ChannelHeader == nil ||
   428  		header.SignatureHeader == nil ||
   429  		ccPropPayl == nil /* || visibility == nil */ {
   430  		return nil, fmt.Errorf("Nil arguments")
   431  	}
   432  
   433  	// unmarshal the chaincode proposal payload
   434  	cpp := &peer.ChaincodeProposalPayload{}
   435  	err := proto.Unmarshal(ccPropPayl, cpp)
   436  	if err != nil {
   437  		return nil, errors.New("Failure while unmarshalling the ChaincodeProposalPayload!")
   438  	}
   439  
   440  	ppBytes, err := GetBytesProposalPayloadForTx(cpp, visibility)
   441  	if err != nil {
   442  		return nil, err
   443  	}
   444  
   445  	hash2, err := factory.GetDefault().GetHash(&bccsp.SHA256Opts{})
   446  	if err != nil {
   447  		return nil, fmt.Errorf("Failed instantiating hash function [%s]", err)
   448  	}
   449  	hash2.Write(header.ChannelHeader)   // hash the serialized Channel Header object
   450  	hash2.Write(header.SignatureHeader) // hash the serialized Signature Header object
   451  	hash2.Write(ppBytes)                // hash of the part of the chaincode proposal payload that will go to the tx
   452  
   453  	return hash2.Sum(nil), nil
   454  }