github.com/kaituanwang/hyperledger@v2.0.1+incompatible/protoutil/proputils.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  	"crypto/sha256"
    11  	"encoding/hex"
    12  	"time"
    13  
    14  	"github.com/golang/protobuf/proto"
    15  	"github.com/golang/protobuf/ptypes"
    16  	"github.com/hyperledger/fabric-protos-go/common"
    17  	"github.com/hyperledger/fabric-protos-go/peer"
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  func validateChannelHeaderType(chdr *common.ChannelHeader, expectedTypes []common.HeaderType) error {
    22  	for _, t := range expectedTypes {
    23  		if common.HeaderType(chdr.Type) == t {
    24  			return nil
    25  		}
    26  	}
    27  	return errors.Errorf("invalid channel header type. expected one of %s, received %s", expectedTypes, common.HeaderType(chdr.Type))
    28  }
    29  
    30  // CreateChaincodeProposal creates a proposal from given input.
    31  // It returns the proposal and the transaction id associated to the proposal
    32  func CreateChaincodeProposal(typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, creator []byte) (*peer.Proposal, string, error) {
    33  	return CreateChaincodeProposalWithTransient(typ, channelID, cis, creator, nil)
    34  }
    35  
    36  // CreateChaincodeProposalWithTransient creates a proposal from given input
    37  // It returns the proposal and the transaction id associated to the proposal
    38  func CreateChaincodeProposalWithTransient(typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) {
    39  	// generate a random nonce
    40  	nonce, err := getRandomNonce()
    41  	if err != nil {
    42  		return nil, "", err
    43  	}
    44  
    45  	// compute txid
    46  	txid := ComputeTxID(nonce, creator)
    47  
    48  	return CreateChaincodeProposalWithTxIDNonceAndTransient(txid, typ, channelID, cis, nonce, creator, transientMap)
    49  }
    50  
    51  // CreateChaincodeProposalWithTxIDAndTransient creates a proposal from given
    52  // input. It returns the proposal and the transaction id associated with the
    53  // proposal
    54  func CreateChaincodeProposalWithTxIDAndTransient(typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, creator []byte, txid string, transientMap map[string][]byte) (*peer.Proposal, string, error) {
    55  	// generate a random nonce
    56  	nonce, err := getRandomNonce()
    57  	if err != nil {
    58  		return nil, "", err
    59  	}
    60  
    61  	// compute txid unless provided by tests
    62  	if txid == "" {
    63  		txid = ComputeTxID(nonce, creator)
    64  	}
    65  
    66  	return CreateChaincodeProposalWithTxIDNonceAndTransient(txid, typ, channelID, cis, nonce, creator, transientMap)
    67  }
    68  
    69  // CreateChaincodeProposalWithTxIDNonceAndTransient creates a proposal from
    70  // given input
    71  func CreateChaincodeProposalWithTxIDNonceAndTransient(txid string, typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, nonce, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) {
    72  	ccHdrExt := &peer.ChaincodeHeaderExtension{ChaincodeId: cis.ChaincodeSpec.ChaincodeId}
    73  	ccHdrExtBytes, err := proto.Marshal(ccHdrExt)
    74  	if err != nil {
    75  		return nil, "", errors.Wrap(err, "error marshaling ChaincodeHeaderExtension")
    76  	}
    77  
    78  	cisBytes, err := proto.Marshal(cis)
    79  	if err != nil {
    80  		return nil, "", errors.Wrap(err, "error marshaling ChaincodeInvocationSpec")
    81  	}
    82  
    83  	ccPropPayload := &peer.ChaincodeProposalPayload{Input: cisBytes, TransientMap: transientMap}
    84  	ccPropPayloadBytes, err := proto.Marshal(ccPropPayload)
    85  	if err != nil {
    86  		return nil, "", errors.Wrap(err, "error marshaling ChaincodeProposalPayload")
    87  	}
    88  
    89  	// TODO: epoch is now set to zero. This must be changed once we
    90  	// get a more appropriate mechanism to handle it in.
    91  	var epoch uint64
    92  
    93  	timestamp, err := ptypes.TimestampProto(time.Now().UTC())
    94  	if err != nil {
    95  		return nil, "", errors.Wrap(err, "error validating Timestamp")
    96  	}
    97  
    98  	hdr := &common.Header{
    99  		ChannelHeader: MarshalOrPanic(
   100  			&common.ChannelHeader{
   101  				Type:      int32(typ),
   102  				TxId:      txid,
   103  				Timestamp: timestamp,
   104  				ChannelId: channelID,
   105  				Extension: ccHdrExtBytes,
   106  				Epoch:     epoch,
   107  			},
   108  		),
   109  		SignatureHeader: MarshalOrPanic(
   110  			&common.SignatureHeader{
   111  				Nonce:   nonce,
   112  				Creator: creator,
   113  			},
   114  		),
   115  	}
   116  
   117  	hdrBytes, err := proto.Marshal(hdr)
   118  	if err != nil {
   119  		return nil, "", err
   120  	}
   121  
   122  	prop := &peer.Proposal{
   123  		Header:  hdrBytes,
   124  		Payload: ccPropPayloadBytes,
   125  	}
   126  	return prop, txid, nil
   127  }
   128  
   129  // GetBytesProposalResponsePayload gets proposal response payload
   130  func GetBytesProposalResponsePayload(hash []byte, response *peer.Response, result []byte, event []byte, ccid *peer.ChaincodeID) ([]byte, error) {
   131  	cAct := &peer.ChaincodeAction{
   132  		Events: event, Results: result,
   133  		Response:    response,
   134  		ChaincodeId: ccid,
   135  	}
   136  	cActBytes, err := proto.Marshal(cAct)
   137  	if err != nil {
   138  		return nil, errors.Wrap(err, "error marshaling ChaincodeAction")
   139  	}
   140  
   141  	prp := &peer.ProposalResponsePayload{
   142  		Extension:    cActBytes,
   143  		ProposalHash: hash,
   144  	}
   145  	prpBytes, err := proto.Marshal(prp)
   146  	return prpBytes, errors.Wrap(err, "error marshaling ProposalResponsePayload")
   147  }
   148  
   149  // GetBytesChaincodeProposalPayload gets the chaincode proposal payload
   150  func GetBytesChaincodeProposalPayload(cpp *peer.ChaincodeProposalPayload) ([]byte, error) {
   151  	cppBytes, err := proto.Marshal(cpp)
   152  	return cppBytes, errors.Wrap(err, "error marshaling ChaincodeProposalPayload")
   153  }
   154  
   155  // GetBytesResponse gets the bytes of Response
   156  func GetBytesResponse(res *peer.Response) ([]byte, error) {
   157  	resBytes, err := proto.Marshal(res)
   158  	return resBytes, errors.Wrap(err, "error marshaling Response")
   159  }
   160  
   161  // GetBytesChaincodeEvent gets the bytes of ChaincodeEvent
   162  func GetBytesChaincodeEvent(event *peer.ChaincodeEvent) ([]byte, error) {
   163  	eventBytes, err := proto.Marshal(event)
   164  	return eventBytes, errors.Wrap(err, "error marshaling ChaincodeEvent")
   165  }
   166  
   167  // GetBytesChaincodeActionPayload get the bytes of ChaincodeActionPayload from
   168  // the message
   169  func GetBytesChaincodeActionPayload(cap *peer.ChaincodeActionPayload) ([]byte, error) {
   170  	capBytes, err := proto.Marshal(cap)
   171  	return capBytes, errors.Wrap(err, "error marshaling ChaincodeActionPayload")
   172  }
   173  
   174  // GetBytesProposalResponse gets proposal bytes response
   175  func GetBytesProposalResponse(pr *peer.ProposalResponse) ([]byte, error) {
   176  	respBytes, err := proto.Marshal(pr)
   177  	return respBytes, errors.Wrap(err, "error marshaling ProposalResponse")
   178  }
   179  
   180  // GetBytesHeader get the bytes of Header from the message
   181  func GetBytesHeader(hdr *common.Header) ([]byte, error) {
   182  	bytes, err := proto.Marshal(hdr)
   183  	return bytes, errors.Wrap(err, "error marshaling Header")
   184  }
   185  
   186  // GetBytesSignatureHeader get the bytes of SignatureHeader from the message
   187  func GetBytesSignatureHeader(hdr *common.SignatureHeader) ([]byte, error) {
   188  	bytes, err := proto.Marshal(hdr)
   189  	return bytes, errors.Wrap(err, "error marshaling SignatureHeader")
   190  }
   191  
   192  // GetBytesTransaction get the bytes of Transaction from the message
   193  func GetBytesTransaction(tx *peer.Transaction) ([]byte, error) {
   194  	bytes, err := proto.Marshal(tx)
   195  	return bytes, errors.Wrap(err, "error unmarshaling Transaction")
   196  }
   197  
   198  // GetBytesPayload get the bytes of Payload from the message
   199  func GetBytesPayload(payl *common.Payload) ([]byte, error) {
   200  	bytes, err := proto.Marshal(payl)
   201  	return bytes, errors.Wrap(err, "error marshaling Payload")
   202  }
   203  
   204  // GetBytesEnvelope get the bytes of Envelope from the message
   205  func GetBytesEnvelope(env *common.Envelope) ([]byte, error) {
   206  	bytes, err := proto.Marshal(env)
   207  	return bytes, errors.Wrap(err, "error marshaling Envelope")
   208  }
   209  
   210  // GetActionFromEnvelope extracts a ChaincodeAction message from a
   211  // serialized Envelope
   212  // TODO: fix function name as per FAB-11831
   213  func GetActionFromEnvelope(envBytes []byte) (*peer.ChaincodeAction, error) {
   214  	env, err := GetEnvelopeFromBlock(envBytes)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  	return GetActionFromEnvelopeMsg(env)
   219  }
   220  
   221  func GetActionFromEnvelopeMsg(env *common.Envelope) (*peer.ChaincodeAction, error) {
   222  	payl, err := UnmarshalPayload(env.Payload)
   223  	if err != nil {
   224  		return nil, err
   225  	}
   226  
   227  	tx, err := UnmarshalTransaction(payl.Data)
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  
   232  	if len(tx.Actions) == 0 {
   233  		return nil, errors.New("at least one TransactionAction required")
   234  	}
   235  
   236  	_, respPayload, err := GetPayloads(tx.Actions[0])
   237  	return respPayload, err
   238  }
   239  
   240  // CreateProposalFromCISAndTxid returns a proposal given a serialized identity
   241  // and a ChaincodeInvocationSpec
   242  func CreateProposalFromCISAndTxid(txid string, typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, creator []byte) (*peer.Proposal, string, error) {
   243  	nonce, err := getRandomNonce()
   244  	if err != nil {
   245  		return nil, "", err
   246  	}
   247  	return CreateChaincodeProposalWithTxIDNonceAndTransient(txid, typ, channelID, cis, nonce, creator, nil)
   248  }
   249  
   250  // CreateProposalFromCIS returns a proposal given a serialized identity and a
   251  // ChaincodeInvocationSpec
   252  func CreateProposalFromCIS(typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, creator []byte) (*peer.Proposal, string, error) {
   253  	return CreateChaincodeProposal(typ, channelID, cis, creator)
   254  }
   255  
   256  // CreateGetChaincodesProposal returns a GETCHAINCODES proposal given a
   257  // serialized identity
   258  func CreateGetChaincodesProposal(channelID string, creator []byte) (*peer.Proposal, string, error) {
   259  	ccinp := &peer.ChaincodeInput{Args: [][]byte{[]byte("getchaincodes")}}
   260  	lsccSpec := &peer.ChaincodeInvocationSpec{
   261  		ChaincodeSpec: &peer.ChaincodeSpec{
   262  			Type:        peer.ChaincodeSpec_GOLANG,
   263  			ChaincodeId: &peer.ChaincodeID{Name: "lscc"},
   264  			Input:       ccinp,
   265  		},
   266  	}
   267  	return CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, channelID, lsccSpec, creator)
   268  }
   269  
   270  // CreateGetInstalledChaincodesProposal returns a GETINSTALLEDCHAINCODES
   271  // proposal given a serialized identity
   272  func CreateGetInstalledChaincodesProposal(creator []byte) (*peer.Proposal, string, error) {
   273  	ccinp := &peer.ChaincodeInput{Args: [][]byte{[]byte("getinstalledchaincodes")}}
   274  	lsccSpec := &peer.ChaincodeInvocationSpec{
   275  		ChaincodeSpec: &peer.ChaincodeSpec{
   276  			Type:        peer.ChaincodeSpec_GOLANG,
   277  			ChaincodeId: &peer.ChaincodeID{Name: "lscc"},
   278  			Input:       ccinp,
   279  		},
   280  	}
   281  	return CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, "", lsccSpec, creator)
   282  }
   283  
   284  // CreateInstallProposalFromCDS returns a install proposal given a serialized
   285  // identity and a ChaincodeDeploymentSpec
   286  func CreateInstallProposalFromCDS(ccpack proto.Message, creator []byte) (*peer.Proposal, string, error) {
   287  	return createProposalFromCDS("", ccpack, creator, "install")
   288  }
   289  
   290  // CreateDeployProposalFromCDS returns a deploy proposal given a serialized
   291  // identity and a ChaincodeDeploymentSpec
   292  func CreateDeployProposalFromCDS(
   293  	channelID string,
   294  	cds *peer.ChaincodeDeploymentSpec,
   295  	creator []byte,
   296  	policy []byte,
   297  	escc []byte,
   298  	vscc []byte,
   299  	collectionConfig []byte) (*peer.Proposal, string, error) {
   300  	if collectionConfig == nil {
   301  		return createProposalFromCDS(channelID, cds, creator, "deploy", policy, escc, vscc)
   302  	}
   303  	return createProposalFromCDS(channelID, cds, creator, "deploy", policy, escc, vscc, collectionConfig)
   304  }
   305  
   306  // CreateUpgradeProposalFromCDS returns a upgrade proposal given a serialized
   307  // identity and a ChaincodeDeploymentSpec
   308  func CreateUpgradeProposalFromCDS(
   309  	channelID string,
   310  	cds *peer.ChaincodeDeploymentSpec,
   311  	creator []byte,
   312  	policy []byte,
   313  	escc []byte,
   314  	vscc []byte,
   315  	collectionConfig []byte) (*peer.Proposal, string, error) {
   316  	if collectionConfig == nil {
   317  		return createProposalFromCDS(channelID, cds, creator, "upgrade", policy, escc, vscc)
   318  	}
   319  	return createProposalFromCDS(channelID, cds, creator, "upgrade", policy, escc, vscc, collectionConfig)
   320  }
   321  
   322  // createProposalFromCDS returns a deploy or upgrade proposal given a
   323  // serialized identity and a ChaincodeDeploymentSpec
   324  func createProposalFromCDS(channelID string, msg proto.Message, creator []byte, propType string, args ...[]byte) (*peer.Proposal, string, error) {
   325  	// in the new mode, cds will be nil, "deploy" and "upgrade" are instantiates.
   326  	var ccinp *peer.ChaincodeInput
   327  	var b []byte
   328  	var err error
   329  	if msg != nil {
   330  		b, err = proto.Marshal(msg)
   331  		if err != nil {
   332  			return nil, "", err
   333  		}
   334  	}
   335  	switch propType {
   336  	case "deploy":
   337  		fallthrough
   338  	case "upgrade":
   339  		cds, ok := msg.(*peer.ChaincodeDeploymentSpec)
   340  		if !ok || cds == nil {
   341  			return nil, "", errors.New("invalid message for creating lifecycle chaincode proposal")
   342  		}
   343  		Args := [][]byte{[]byte(propType), []byte(channelID), b}
   344  		Args = append(Args, args...)
   345  
   346  		ccinp = &peer.ChaincodeInput{Args: Args}
   347  	case "install":
   348  		ccinp = &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), b}}
   349  	}
   350  
   351  	// wrap the deployment in an invocation spec to lscc...
   352  	lsccSpec := &peer.ChaincodeInvocationSpec{
   353  		ChaincodeSpec: &peer.ChaincodeSpec{
   354  			Type:        peer.ChaincodeSpec_GOLANG,
   355  			ChaincodeId: &peer.ChaincodeID{Name: "lscc"},
   356  			Input:       ccinp,
   357  		},
   358  	}
   359  
   360  	// ...and get the proposal for it
   361  	return CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, channelID, lsccSpec, creator)
   362  }
   363  
   364  // ComputeTxID computes TxID as the Hash computed
   365  // over the concatenation of nonce and creator.
   366  func ComputeTxID(nonce, creator []byte) string {
   367  	// TODO: Get the Hash function to be used from
   368  	// channel configuration
   369  	hasher := sha256.New()
   370  	hasher.Write(nonce)
   371  	hasher.Write(creator)
   372  	return hex.EncodeToString(hasher.Sum(nil))
   373  }
   374  
   375  // CheckTxID checks that txid is equal to the Hash computed
   376  // over the concatenation of nonce and creator.
   377  func CheckTxID(txid string, nonce, creator []byte) error {
   378  	computedTxID := ComputeTxID(nonce, creator)
   379  
   380  	if txid != computedTxID {
   381  		return errors.Errorf("invalid txid. got [%s], expected [%s]", txid, computedTxID)
   382  	}
   383  
   384  	return nil
   385  }
   386  
   387  // InvokedChaincodeName takes the proposal bytes of a SignedProposal, and unpacks it all the way down,
   388  // until either an error is encountered, or the chaincode name is found. This is useful primarily
   389  // for chaincodes which wish to know the chaincode name originally invoked, in order to deny cc2cc
   390  // invocations (or, perhaps to deny direct invocations and require cc2cc).
   391  func InvokedChaincodeName(proposalBytes []byte) (string, error) {
   392  	proposal := &peer.Proposal{}
   393  	err := proto.Unmarshal(proposalBytes, proposal)
   394  	if err != nil {
   395  		return "", errors.WithMessage(err, "could not unmarshal proposal")
   396  	}
   397  
   398  	proposalPayload := &peer.ChaincodeProposalPayload{}
   399  	err = proto.Unmarshal(proposal.Payload, proposalPayload)
   400  	if err != nil {
   401  		return "", errors.WithMessage(err, "could not unmarshal chaincode proposal payload")
   402  	}
   403  
   404  	cis := &peer.ChaincodeInvocationSpec{}
   405  	err = proto.Unmarshal(proposalPayload.Input, cis)
   406  	if err != nil {
   407  		return "", errors.WithMessage(err, "could not unmarshal chaincode invocation spec")
   408  	}
   409  
   410  	if cis.ChaincodeSpec == nil {
   411  		return "", errors.Errorf("chaincode spec is nil")
   412  	}
   413  
   414  	if cis.ChaincodeSpec.ChaincodeId == nil {
   415  		return "", errors.Errorf("chaincode id is nil")
   416  	}
   417  
   418  	return cis.ChaincodeSpec.ChaincodeId.Name, nil
   419  }