github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/tx/endorser/parser.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package endorsertx
     8  
     9  import (
    10  	"regexp"
    11  
    12  	"github.com/hyperledger/fabric-protos-go/peer"
    13  
    14  	"github.com/hechain20/hechain/pkg/tx"
    15  	"github.com/hechain20/hechain/protoutil"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  var (
    20  	// ChannelAllowedChars tracks the permitted characters for a channel name
    21  	ChannelAllowedChars = "[a-z][a-z0-9.-]*"
    22  	// MaxLength tracks the maximum length of a channel name
    23  	MaxLength = 249
    24  )
    25  
    26  // EndorserTx represents a parsed common.Envelope protobuf
    27  type EndorserTx struct {
    28  	ComputedTxID string
    29  	ChannelID    string
    30  	ChaincodeID  *peer.ChaincodeID
    31  	Creator      []byte
    32  	Response     *peer.Response
    33  	Events       []byte
    34  	Results      []byte
    35  	Endorsements []*peer.Endorsement
    36  	Type         int32
    37  	Version      int32
    38  	Epoch        uint64
    39  	Nonce        []byte
    40  }
    41  
    42  func unmarshalEndorserTx(txenv *tx.Envelope) (*EndorserTx, error) {
    43  	if len(txenv.ChannelHeader.Extension) == 0 {
    44  		return nil, errors.New("empty header extension")
    45  	}
    46  
    47  	hdrExt, err := protoutil.UnmarshalChaincodeHeaderExtension(
    48  		txenv.ChannelHeader.Extension,
    49  	)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	if len(txenv.Data) == 0 {
    55  		return nil, errors.New("nil payload data")
    56  	}
    57  
    58  	tx, err := protoutil.UnmarshalTransaction(txenv.Data)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	if len(tx.GetActions()) != 1 {
    64  		return nil, errors.Errorf("only one transaction action is supported, %d were present", len(tx.GetActions()))
    65  	}
    66  
    67  	txAction := tx.GetActions()[0]
    68  
    69  	if txAction == nil {
    70  		return nil, errors.New("nil action")
    71  	}
    72  
    73  	if len(txAction.Payload) == 0 {
    74  		return nil, errors.New("empty ChaincodeActionPayload")
    75  	}
    76  
    77  	ccActionPayload, err := protoutil.UnmarshalChaincodeActionPayload(txAction.Payload)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	if ccActionPayload.Action == nil {
    83  		return nil, errors.New("nil ChaincodeEndorsedAction")
    84  	}
    85  
    86  	if len(ccActionPayload.Action.ProposalResponsePayload) == 0 {
    87  		return nil, errors.New("empty ProposalResponsePayload")
    88  	}
    89  
    90  	proposalResponsePayload, err := protoutil.UnmarshalProposalResponsePayload(
    91  		ccActionPayload.Action.ProposalResponsePayload,
    92  	)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	if len(proposalResponsePayload.Extension) == 0 {
    98  		return nil, errors.New("nil Extension")
    99  	}
   100  
   101  	ccAction, err := protoutil.UnmarshalChaincodeAction(proposalResponsePayload.Extension)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	computedTxID := protoutil.ComputeTxID(
   107  		txenv.SignatureHeader.Nonce,
   108  		txenv.SignatureHeader.Creator,
   109  	)
   110  
   111  	return &EndorserTx{
   112  		ComputedTxID: computedTxID,
   113  		ChannelID:    txenv.ChannelHeader.ChannelId,
   114  		Creator:      txenv.SignatureHeader.Creator,
   115  		Response:     ccAction.Response,
   116  		Events:       ccAction.Events,
   117  		Results:      ccAction.Results,
   118  		Endorsements: ccActionPayload.Action.Endorsements,
   119  		ChaincodeID:  hdrExt.ChaincodeId,
   120  		Type:         txenv.ChannelHeader.Type,
   121  		Version:      txenv.ChannelHeader.Version,
   122  		Epoch:        txenv.ChannelHeader.Epoch,
   123  		Nonce:        txenv.SignatureHeader.Nonce,
   124  	}, nil
   125  }
   126  
   127  func (e *EndorserTx) validate() error {
   128  	if e.Epoch != 0 {
   129  		return errors.Errorf("invalid epoch in ChannelHeader. Expected 0, got [%d]", e.Epoch)
   130  	}
   131  
   132  	if e.Version != 0 {
   133  		return errors.Errorf("invalid version in ChannelHeader. Expected 0, got [%d]", e.Version)
   134  	}
   135  
   136  	if err := ValidateChannelID(e.ChannelID); err != nil {
   137  		return err
   138  	}
   139  
   140  	if len(e.Nonce) == 0 {
   141  		return errors.New("empty nonce")
   142  	}
   143  
   144  	if len(e.Creator) == 0 {
   145  		return errors.New("empty creator")
   146  	}
   147  
   148  	if e.ChaincodeID == nil {
   149  		return errors.New("nil ChaincodeId")
   150  	}
   151  
   152  	if e.ChaincodeID.Name == "" {
   153  		return errors.New("empty chaincode name in chaincode id")
   154  	}
   155  
   156  	// TODO FAB-16170: check proposal hash
   157  
   158  	// TODO FAB-16170: verify that txid matches the one in the header
   159  
   160  	// TODO FAB-16170: check that header in the tx action and channel header match bitwise
   161  
   162  	return nil
   163  }
   164  
   165  // UnmarshalEndorserTxAndValidate receives a tx.Envelope containing
   166  // a partially unmarshalled endorser transaction and returns an EndorserTx
   167  // instance (or an error)
   168  func UnmarshalEndorserTxAndValidate(txenv *tx.Envelope) (*EndorserTx, error) {
   169  	etx, err := unmarshalEndorserTx(txenv)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  
   174  	err = etx.validate()
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  
   179  	return etx, nil
   180  }
   181  
   182  // ValidateChannelID makes sure that proposed channel IDs comply with the
   183  // following restrictions:
   184  //      1. Contain only lower case ASCII alphanumerics, dots '.', and dashes '-'
   185  //      2. Are shorter than 250 characters.
   186  //      3. Start with a letter
   187  //
   188  // This is the intersection of the Kafka restrictions and CouchDB restrictions
   189  // with the following exception: '.' is converted to '_' in the CouchDB naming
   190  // This is to accommodate existing channel names with '.', especially in the
   191  // behave tests which rely on the dot notation for their sluggification.
   192  //
   193  // note: this function is a copy of the same in common/configtx/validator.go
   194  //
   195  func ValidateChannelID(channelID string) error {
   196  	re, _ := regexp.Compile(ChannelAllowedChars)
   197  	// Length
   198  	if len(channelID) <= 0 {
   199  		return errors.Errorf("channel ID illegal, cannot be empty")
   200  	}
   201  	if len(channelID) > MaxLength {
   202  		return errors.Errorf("channel ID illegal, cannot be longer than %d", MaxLength)
   203  	}
   204  
   205  	// Illegal characters
   206  	matched := re.FindString(channelID)
   207  	if len(matched) != len(channelID) {
   208  		return errors.Errorf("'%s' contains illegal characters", channelID)
   209  	}
   210  
   211  	return nil
   212  }