github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/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, err
    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  	payloadSignatureHeader, err := signer.NewSignatureHeader()
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	data, err := proto.Marshal(dataMsg)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	paylBytes := MarshalOrPanic(&common.Payload{
    91  		Header: MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader),
    92  		Data:   data,
    93  	})
    94  
    95  	sig, err := signer.Sign(paylBytes)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	return &common.Envelope{Payload: paylBytes, Signature: sig}, nil
   101  }
   102  
   103  // CreateSignedTx assembles an Envelope message from proposal, endorsements, and a signer.
   104  // This function should be called by a client when it has collected enough endorsements
   105  // for a proposal to create a transaction and submit it to peers for ordering
   106  func CreateSignedTx(proposal *peer.Proposal, signer msp.SigningIdentity, resps ...*peer.ProposalResponse) (*common.Envelope, error) {
   107  	if len(resps) == 0 {
   108  		return nil, fmt.Errorf("At least one proposal response is necessary")
   109  	}
   110  
   111  	// the original header
   112  	hdr, err := GetHeader(proposal.Header)
   113  	if err != nil {
   114  		return nil, fmt.Errorf("Could not unmarshal the proposal header")
   115  	}
   116  
   117  	// the original payload
   118  	pPayl, err := GetChaincodeProposalPayload(proposal.Payload)
   119  	if err != nil {
   120  		return nil, fmt.Errorf("Could not unmarshal the proposal payload")
   121  	}
   122  
   123  	// check that the signer is the same that is referenced in the header
   124  	// TODO: maybe worth removing?
   125  	signerBytes, err := signer.Serialize()
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	shdr, err := GetSignatureHeader(hdr.SignatureHeader)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	if bytes.Compare(signerBytes, shdr.Creator) != 0 {
   136  		return nil, fmt.Errorf("The signer needs to be the same as the one referenced in the header")
   137  	}
   138  
   139  	// get header extensions so we have the visibility field
   140  	hdrExt, err := GetChaincodeHeaderExtension(hdr)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	// ensure that all actions are bitwise equal and that they are successful
   146  	var a1 []byte
   147  	for n, r := range resps {
   148  		if n == 0 {
   149  			a1 = r.Payload
   150  			if r.Response.Status != 200 {
   151  				return nil, fmt.Errorf("Proposal response was not successful, error code %d, msg %s", r.Response.Status, r.Response.Message)
   152  			}
   153  			continue
   154  		}
   155  
   156  		if bytes.Compare(a1, r.Payload) != 0 {
   157  			return nil, fmt.Errorf("ProposalResponsePayloads do not match")
   158  		}
   159  	}
   160  
   161  	// fill endorsements
   162  	endorsements := make([]*peer.Endorsement, len(resps))
   163  	for n, r := range resps {
   164  		endorsements[n] = r.Endorsement
   165  	}
   166  
   167  	// create ChaincodeEndorsedAction
   168  	cea := &peer.ChaincodeEndorsedAction{ProposalResponsePayload: resps[0].Payload, Endorsements: endorsements}
   169  
   170  	// obtain the bytes of the proposal payload that will go to the transaction
   171  	propPayloadBytes, err := GetBytesProposalPayloadForTx(pPayl, hdrExt.PayloadVisibility)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  
   176  	// serialize the chaincode action payload
   177  	cap := &peer.ChaincodeActionPayload{ChaincodeProposalPayload: propPayloadBytes, Action: cea}
   178  	capBytes, err := GetBytesChaincodeActionPayload(cap)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  
   183  	// create a transaction
   184  	taa := &peer.TransactionAction{Header: hdr.SignatureHeader, Payload: capBytes}
   185  	taas := make([]*peer.TransactionAction, 1)
   186  	taas[0] = taa
   187  	tx := &peer.Transaction{Actions: taas}
   188  
   189  	// serialize the tx
   190  	txBytes, err := GetBytesTransaction(tx)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  
   195  	// create the payload
   196  	payl := &common.Payload{Header: hdr, Data: txBytes}
   197  	paylBytes, err := GetBytesPayload(payl)
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	// sign the payload
   203  	sig, err := signer.Sign(paylBytes)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	// here's the envelope
   209  	return &common.Envelope{Payload: paylBytes, Signature: sig}, nil
   210  }
   211  
   212  // CreateProposalResponse creates a proposal response.
   213  func CreateProposalResponse(hdrbytes []byte, payl []byte, response *peer.Response, results []byte, events []byte, visibility []byte, signingEndorser msp.SigningIdentity) (*peer.ProposalResponse, error) {
   214  	hdr, err := GetHeader(hdrbytes)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	// obtain the proposal hash given proposal header, payload and the requested visibility
   220  	pHashBytes, err := GetProposalHash1(hdr, payl, visibility)
   221  	if err != nil {
   222  		return nil, fmt.Errorf("Could not compute proposal hash: err %s", err)
   223  	}
   224  
   225  	// get the bytes of the proposal response payload - we need to sign them
   226  	prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events)
   227  	if err != nil {
   228  		return nil, errors.New("Failure while unmarshalling the ProposalResponsePayload")
   229  	}
   230  
   231  	// serialize the signing identity
   232  	endorser, err := signingEndorser.Serialize()
   233  	if err != nil {
   234  		return nil, fmt.Errorf("Could not serialize the signing identity for %s, err %s", signingEndorser.GetIdentifier(), err)
   235  	}
   236  
   237  	// sign the concatenation of the proposal response and the serialized endorser identity with this endorser's key
   238  	signature, err := signingEndorser.Sign(append(prpBytes, endorser...))
   239  	if err != nil {
   240  		return nil, fmt.Errorf("Could not sign the proposal response payload, err %s", err)
   241  	}
   242  
   243  	resp := &peer.ProposalResponse{
   244  		// Timestamp: TODO!
   245  		Version:     1, // TODO: pick right version number
   246  		Endorsement: &peer.Endorsement{Signature: signature, Endorser: endorser},
   247  		Payload:     prpBytes,
   248  		Response:    &peer.Response{Status: 200, Message: "OK"}}
   249  
   250  	return resp, nil
   251  }
   252  
   253  // GetSignedProposal returns a signed proposal given a Proposal message and a signing identity
   254  func GetSignedProposal(prop *peer.Proposal, signer msp.SigningIdentity) (*peer.SignedProposal, error) {
   255  	// check for nil argument
   256  	if prop == nil || signer == nil {
   257  		return nil, fmt.Errorf("Nil arguments")
   258  	}
   259  
   260  	propBytes, err := GetBytesProposal(prop)
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  
   265  	signature, err := signer.Sign(propBytes)
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  
   270  	return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, nil
   271  }
   272  
   273  // GetBytesProposalPayloadForTx takes a ChaincodeProposalPayload and returns its serialized
   274  // version according to the visibility field
   275  func GetBytesProposalPayloadForTx(payload *peer.ChaincodeProposalPayload, visibility []byte) ([]byte, error) {
   276  	// check for nil argument
   277  	if payload == nil /* || visibility == nil */ {
   278  		return nil, fmt.Errorf("Nil arguments")
   279  	}
   280  
   281  	// strip the transient bytes off the payload - this needs to be done no matter the visibility mode
   282  	cppNoTransient := &peer.ChaincodeProposalPayload{Input: payload.Input, TransientMap: nil}
   283  	cppBytes, err := GetBytesChaincodeProposalPayload(cppNoTransient)
   284  	if err != nil {
   285  		return nil, errors.New("Failure while marshalling the ChaincodeProposalPayload!")
   286  	}
   287  
   288  	// currently the fabric only supports full visibility: this means that
   289  	// there are no restrictions on which parts of the proposal payload will
   290  	// be visible in the final transaction; this default approach requires
   291  	// no additional instructions in the PayloadVisibility field; however
   292  	// the fabric may be extended to encode more elaborate visibility
   293  	// mechanisms that shall be encoded in this field (and handled
   294  	// appropriately by the peer)
   295  
   296  	return cppBytes, nil
   297  }
   298  
   299  // GetProposalHash2 gets the proposal hash - this version
   300  // is called by the committer where the visibility policy
   301  // has already been enforced and so we already get what
   302  // we have to get in ccPropPayl
   303  func GetProposalHash2(header *common.Header, ccPropPayl []byte) ([]byte, error) {
   304  	// check for nil argument
   305  	if header == nil ||
   306  		header.ChannelHeader == nil ||
   307  		header.SignatureHeader == nil ||
   308  		ccPropPayl == nil {
   309  		return nil, fmt.Errorf("Nil arguments")
   310  	}
   311  
   312  	hash, err := factory.GetDefault().GetHash(&bccsp.SHAOpts{})
   313  	if err != nil {
   314  		return nil, fmt.Errorf("Failed instantiating hash function [%s]", err)
   315  	}
   316  	hash.Write(header.ChannelHeader)   // hash the serialized Channel Header object
   317  	hash.Write(header.SignatureHeader) // hash the serialized Signature Header object
   318  	hash.Write(ccPropPayl)             // hash the bytes of the chaincode proposal payload that we are given
   319  
   320  	return hash.Sum(nil), nil
   321  }
   322  
   323  // GetProposalHash1 gets the proposal hash bytes after sanitizing the
   324  // chaincode proposal payload according to the rules of visibility
   325  func GetProposalHash1(header *common.Header, ccPropPayl []byte, visibility []byte) ([]byte, error) {
   326  	// check for nil argument
   327  	if header == nil ||
   328  		header.ChannelHeader == nil ||
   329  		header.SignatureHeader == nil ||
   330  		ccPropPayl == nil /* || visibility == nil */ {
   331  		return nil, fmt.Errorf("Nil arguments")
   332  	}
   333  
   334  	// unmarshal the chaincode proposal payload
   335  	cpp := &peer.ChaincodeProposalPayload{}
   336  	err := proto.Unmarshal(ccPropPayl, cpp)
   337  	if err != nil {
   338  		return nil, errors.New("Failure while unmarshalling the ChaincodeProposalPayload!")
   339  	}
   340  
   341  	ppBytes, err := GetBytesProposalPayloadForTx(cpp, visibility)
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  
   346  	hash2, err := factory.GetDefault().GetHash(&bccsp.SHAOpts{})
   347  	if err != nil {
   348  		return nil, fmt.Errorf("Failed instantiating hash function [%s]", err)
   349  	}
   350  	hash2.Write(header.ChannelHeader)   // hash the serialized Channel Header object
   351  	hash2.Write(header.SignatureHeader) // hash the serialized Signature Header object
   352  	hash2.Write(ppBytes)                // hash of the part of the chaincode proposal payload that will go to the tx
   353  
   354  	return hash2.Sum(nil), nil
   355  }