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 }