github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/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, ccid *peer.ChaincodeID, 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, ccid)
   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  // GetSignedEvent returns a signed event given an Event message and a signing identity
   274  func GetSignedEvent(evt *peer.Event, signer msp.SigningIdentity) (*peer.SignedEvent, error) {
   275  	// check for nil argument
   276  	if evt == nil || signer == nil {
   277  		return nil, errors.New("nil arguments")
   278  	}
   279  
   280  	evtBytes, err := proto.Marshal(evt)
   281  	if err != nil {
   282  		return nil, err
   283  	}
   284  
   285  	signature, err := signer.Sign(evtBytes)
   286  	if err != nil {
   287  		return nil, err
   288  	}
   289  
   290  	return &peer.SignedEvent{EventBytes: evtBytes, Signature: signature}, nil
   291  }
   292  
   293  // MockSignedEndorserProposalOrPanic creates a SignedProposal with the passed arguments
   294  func MockSignedEndorserProposalOrPanic(chainID string, cs *peer.ChaincodeSpec, creator, signature []byte) (*peer.SignedProposal, *peer.Proposal) {
   295  	prop, _, err := CreateChaincodeProposal(
   296  		common.HeaderType_ENDORSER_TRANSACTION,
   297  		chainID,
   298  		&peer.ChaincodeInvocationSpec{ChaincodeSpec: cs},
   299  		creator)
   300  	if err != nil {
   301  		panic(err)
   302  	}
   303  
   304  	propBytes, err := GetBytesProposal(prop)
   305  	if err != nil {
   306  		panic(err)
   307  	}
   308  
   309  	return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, prop
   310  }
   311  
   312  // GetBytesProposalPayloadForTx takes a ChaincodeProposalPayload and returns its serialized
   313  // version according to the visibility field
   314  func GetBytesProposalPayloadForTx(payload *peer.ChaincodeProposalPayload, visibility []byte) ([]byte, error) {
   315  	// check for nil argument
   316  	if payload == nil /* || visibility == nil */ {
   317  		return nil, fmt.Errorf("Nil arguments")
   318  	}
   319  
   320  	// strip the transient bytes off the payload - this needs to be done no matter the visibility mode
   321  	cppNoTransient := &peer.ChaincodeProposalPayload{Input: payload.Input, TransientMap: nil}
   322  	cppBytes, err := GetBytesChaincodeProposalPayload(cppNoTransient)
   323  	if err != nil {
   324  		return nil, errors.New("Failure while marshalling the ChaincodeProposalPayload!")
   325  	}
   326  
   327  	// currently the fabric only supports full visibility: this means that
   328  	// there are no restrictions on which parts of the proposal payload will
   329  	// be visible in the final transaction; this default approach requires
   330  	// no additional instructions in the PayloadVisibility field; however
   331  	// the fabric may be extended to encode more elaborate visibility
   332  	// mechanisms that shall be encoded in this field (and handled
   333  	// appropriately by the peer)
   334  
   335  	return cppBytes, nil
   336  }
   337  
   338  // GetProposalHash2 gets the proposal hash - this version
   339  // is called by the committer where the visibility policy
   340  // has already been enforced and so we already get what
   341  // we have to get in ccPropPayl
   342  func GetProposalHash2(header *common.Header, ccPropPayl []byte) ([]byte, error) {
   343  	// check for nil argument
   344  	if header == nil ||
   345  		header.ChannelHeader == nil ||
   346  		header.SignatureHeader == nil ||
   347  		ccPropPayl == nil {
   348  		return nil, fmt.Errorf("Nil arguments")
   349  	}
   350  
   351  	hash, err := factory.GetDefault().GetHash(&bccsp.SHA256Opts{})
   352  	if err != nil {
   353  		return nil, fmt.Errorf("Failed instantiating hash function [%s]", err)
   354  	}
   355  	hash.Write(header.ChannelHeader)   // hash the serialized Channel Header object
   356  	hash.Write(header.SignatureHeader) // hash the serialized Signature Header object
   357  	hash.Write(ccPropPayl)             // hash the bytes of the chaincode proposal payload that we are given
   358  
   359  	return hash.Sum(nil), nil
   360  }
   361  
   362  // GetProposalHash1 gets the proposal hash bytes after sanitizing the
   363  // chaincode proposal payload according to the rules of visibility
   364  func GetProposalHash1(header *common.Header, ccPropPayl []byte, visibility []byte) ([]byte, error) {
   365  	// check for nil argument
   366  	if header == nil ||
   367  		header.ChannelHeader == nil ||
   368  		header.SignatureHeader == nil ||
   369  		ccPropPayl == nil /* || visibility == nil */ {
   370  		return nil, fmt.Errorf("Nil arguments")
   371  	}
   372  
   373  	// unmarshal the chaincode proposal payload
   374  	cpp := &peer.ChaincodeProposalPayload{}
   375  	err := proto.Unmarshal(ccPropPayl, cpp)
   376  	if err != nil {
   377  		return nil, errors.New("Failure while unmarshalling the ChaincodeProposalPayload!")
   378  	}
   379  
   380  	ppBytes, err := GetBytesProposalPayloadForTx(cpp, visibility)
   381  	if err != nil {
   382  		return nil, err
   383  	}
   384  
   385  	hash2, err := factory.GetDefault().GetHash(&bccsp.SHA256Opts{})
   386  	if err != nil {
   387  		return nil, fmt.Errorf("Failed instantiating hash function [%s]", err)
   388  	}
   389  	hash2.Write(header.ChannelHeader)   // hash the serialized Channel Header object
   390  	hash2.Write(header.SignatureHeader) // hash the serialized Signature Header object
   391  	hash2.Write(ppBytes)                // hash of the part of the chaincode proposal payload that will go to the tx
   392  
   393  	return hash2.Sum(nil), nil
   394  }