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 }