github.com/lzy4123/fabric@v2.1.1+incompatible/protoutil/txutils.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 "bytes" 11 "crypto/sha256" 12 13 "github.com/golang/protobuf/proto" 14 "github.com/hyperledger/fabric-protos-go/common" 15 "github.com/hyperledger/fabric-protos-go/peer" 16 "github.com/pkg/errors" 17 ) 18 19 // GetPayloads gets the underlying payload objects in a TransactionAction 20 func GetPayloads(txActions *peer.TransactionAction) (*peer.ChaincodeActionPayload, *peer.ChaincodeAction, error) { 21 // TODO: pass in the tx type (in what follows we're assuming the 22 // type is ENDORSER_TRANSACTION) 23 ccPayload, err := UnmarshalChaincodeActionPayload(txActions.Payload) 24 if err != nil { 25 return nil, nil, err 26 } 27 28 if ccPayload.Action == nil || ccPayload.Action.ProposalResponsePayload == nil { 29 return nil, nil, errors.New("no payload in ChaincodeActionPayload") 30 } 31 pRespPayload, err := UnmarshalProposalResponsePayload(ccPayload.Action.ProposalResponsePayload) 32 if err != nil { 33 return nil, nil, err 34 } 35 36 if pRespPayload.Extension == nil { 37 return nil, nil, errors.New("response payload is missing extension") 38 } 39 40 respPayload, err := UnmarshalChaincodeAction(pRespPayload.Extension) 41 if err != nil { 42 return ccPayload, nil, err 43 } 44 return ccPayload, respPayload, nil 45 } 46 47 // GetEnvelopeFromBlock gets an envelope from a block's Data field. 48 func GetEnvelopeFromBlock(data []byte) (*common.Envelope, error) { 49 // Block always begins with an envelope 50 var err error 51 env := &common.Envelope{} 52 if err = proto.Unmarshal(data, env); err != nil { 53 return nil, errors.Wrap(err, "error unmarshaling Envelope") 54 } 55 56 return env, nil 57 } 58 59 // CreateSignedEnvelope creates a signed envelope of the desired type, with 60 // marshaled dataMsg and signs it 61 func CreateSignedEnvelope( 62 txType common.HeaderType, 63 channelID string, 64 signer Signer, 65 dataMsg proto.Message, 66 msgVersion int32, 67 epoch uint64, 68 ) (*common.Envelope, error) { 69 return CreateSignedEnvelopeWithTLSBinding(txType, channelID, signer, dataMsg, msgVersion, epoch, nil) 70 } 71 72 // CreateSignedEnvelopeWithTLSBinding creates a signed envelope of the desired 73 // type, with marshaled dataMsg and signs it. It also includes a TLS cert hash 74 // into the channel header 75 func CreateSignedEnvelopeWithTLSBinding( 76 txType common.HeaderType, 77 channelID string, 78 signer Signer, 79 dataMsg proto.Message, 80 msgVersion int32, 81 epoch uint64, 82 tlsCertHash []byte, 83 ) (*common.Envelope, error) { 84 payloadChannelHeader := MakeChannelHeader(txType, msgVersion, channelID, epoch) 85 payloadChannelHeader.TlsCertHash = tlsCertHash 86 var err error 87 payloadSignatureHeader := &common.SignatureHeader{} 88 89 if signer != nil { 90 payloadSignatureHeader, err = NewSignatureHeader(signer) 91 if err != nil { 92 return nil, err 93 } 94 } 95 96 data, err := proto.Marshal(dataMsg) 97 if err != nil { 98 return nil, errors.Wrap(err, "error marshaling") 99 } 100 101 paylBytes := MarshalOrPanic( 102 &common.Payload{ 103 Header: MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader), 104 Data: data, 105 }, 106 ) 107 108 var sig []byte 109 if signer != nil { 110 sig, err = signer.Sign(paylBytes) 111 if err != nil { 112 return nil, err 113 } 114 } 115 116 env := &common.Envelope{ 117 Payload: paylBytes, 118 Signature: sig, 119 } 120 121 return env, nil 122 } 123 124 // Signer is the interface needed to sign a transaction 125 type Signer interface { 126 Sign(msg []byte) ([]byte, error) 127 Serialize() ([]byte, error) 128 } 129 130 // CreateSignedTx assembles an Envelope message from proposal, endorsements, 131 // and a signer. This function should be called by a client when it has 132 // collected enough endorsements for a proposal to create a transaction and 133 // submit it to peers for ordering 134 func CreateSignedTx( 135 proposal *peer.Proposal, 136 signer Signer, 137 resps ...*peer.ProposalResponse, 138 ) (*common.Envelope, error) { 139 if len(resps) == 0 { 140 return nil, errors.New("at least one proposal response is required") 141 } 142 143 // the original header 144 hdr, err := UnmarshalHeader(proposal.Header) 145 if err != nil { 146 return nil, err 147 } 148 149 // the original payload 150 pPayl, err := UnmarshalChaincodeProposalPayload(proposal.Payload) 151 if err != nil { 152 return nil, err 153 } 154 155 // check that the signer is the same that is referenced in the header 156 // TODO: maybe worth removing? 157 signerBytes, err := signer.Serialize() 158 if err != nil { 159 return nil, err 160 } 161 162 shdr, err := UnmarshalSignatureHeader(hdr.SignatureHeader) 163 if err != nil { 164 return nil, err 165 } 166 167 if !bytes.Equal(signerBytes, shdr.Creator) { 168 return nil, errors.New("signer must be the same as the one referenced in the header") 169 } 170 171 // ensure that all actions are bitwise equal and that they are successful 172 var a1 []byte 173 for n, r := range resps { 174 if r.Response.Status < 200 || r.Response.Status >= 400 { 175 return nil, errors.Errorf("proposal response was not successful, error code %d, msg %s", r.Response.Status, r.Response.Message) 176 } 177 178 if n == 0 { 179 a1 = r.Payload 180 continue 181 } 182 183 if !bytes.Equal(a1, r.Payload) { 184 return nil, errors.New("ProposalResponsePayloads do not match") 185 } 186 } 187 188 // fill endorsements 189 endorsements := make([]*peer.Endorsement, len(resps)) 190 for n, r := range resps { 191 endorsements[n] = r.Endorsement 192 } 193 194 // create ChaincodeEndorsedAction 195 cea := &peer.ChaincodeEndorsedAction{ProposalResponsePayload: resps[0].Payload, Endorsements: endorsements} 196 197 // obtain the bytes of the proposal payload that will go to the transaction 198 propPayloadBytes, err := GetBytesProposalPayloadForTx(pPayl) 199 if err != nil { 200 return nil, err 201 } 202 203 // serialize the chaincode action payload 204 cap := &peer.ChaincodeActionPayload{ChaincodeProposalPayload: propPayloadBytes, Action: cea} 205 capBytes, err := GetBytesChaincodeActionPayload(cap) 206 if err != nil { 207 return nil, err 208 } 209 210 // create a transaction 211 taa := &peer.TransactionAction{Header: hdr.SignatureHeader, Payload: capBytes} 212 taas := make([]*peer.TransactionAction, 1) 213 taas[0] = taa 214 tx := &peer.Transaction{Actions: taas} 215 216 // serialize the tx 217 txBytes, err := GetBytesTransaction(tx) 218 if err != nil { 219 return nil, err 220 } 221 222 // create the payload 223 payl := &common.Payload{Header: hdr, Data: txBytes} 224 paylBytes, err := GetBytesPayload(payl) 225 if err != nil { 226 return nil, err 227 } 228 229 // sign the payload 230 sig, err := signer.Sign(paylBytes) 231 if err != nil { 232 return nil, err 233 } 234 235 // here's the envelope 236 return &common.Envelope{Payload: paylBytes, Signature: sig}, nil 237 } 238 239 // CreateProposalResponse creates a proposal response. 240 func CreateProposalResponse( 241 hdrbytes []byte, 242 payl []byte, 243 response *peer.Response, 244 results []byte, 245 events []byte, 246 ccid *peer.ChaincodeID, 247 signingEndorser Signer, 248 ) (*peer.ProposalResponse, error) { 249 hdr, err := UnmarshalHeader(hdrbytes) 250 if err != nil { 251 return nil, err 252 } 253 254 // obtain the proposal hash given proposal header, payload and the 255 // requested visibility 256 pHashBytes, err := GetProposalHash1(hdr, payl) 257 if err != nil { 258 return nil, errors.WithMessage(err, "error computing proposal hash") 259 } 260 261 // get the bytes of the proposal response payload - we need to sign them 262 prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events, ccid) 263 if err != nil { 264 return nil, err 265 } 266 267 // serialize the signing identity 268 endorser, err := signingEndorser.Serialize() 269 if err != nil { 270 return nil, errors.WithMessage(err, "error serializing signing identity") 271 } 272 273 // sign the concatenation of the proposal response and the serialized 274 // endorser identity with this endorser's key 275 signature, err := signingEndorser.Sign(append(prpBytes, endorser...)) 276 if err != nil { 277 return nil, errors.WithMessage(err, "could not sign the proposal response payload") 278 } 279 280 resp := &peer.ProposalResponse{ 281 // Timestamp: TODO! 282 Version: 1, // TODO: pick right version number 283 Endorsement: &peer.Endorsement{ 284 Signature: signature, 285 Endorser: endorser, 286 }, 287 Payload: prpBytes, 288 Response: &peer.Response{ 289 Status: 200, 290 Message: "OK", 291 }, 292 } 293 294 return resp, nil 295 } 296 297 // CreateProposalResponseFailure creates a proposal response for cases where 298 // endorsement proposal fails either due to a endorsement failure or a 299 // chaincode failure (chaincode response status >= shim.ERRORTHRESHOLD) 300 func CreateProposalResponseFailure( 301 hdrbytes []byte, 302 payl []byte, 303 response *peer.Response, 304 results []byte, 305 events []byte, 306 chaincodeName string, 307 ) (*peer.ProposalResponse, error) { 308 hdr, err := UnmarshalHeader(hdrbytes) 309 if err != nil { 310 return nil, err 311 } 312 313 // obtain the proposal hash given proposal header, payload and the requested visibility 314 pHashBytes, err := GetProposalHash1(hdr, payl) 315 if err != nil { 316 return nil, errors.WithMessage(err, "error computing proposal hash") 317 } 318 319 // get the bytes of the proposal response payload 320 prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events, &peer.ChaincodeID{Name: chaincodeName}) 321 if err != nil { 322 return nil, err 323 } 324 325 resp := &peer.ProposalResponse{ 326 // Timestamp: TODO! 327 Payload: prpBytes, 328 Response: response, 329 } 330 331 return resp, nil 332 } 333 334 // GetSignedProposal returns a signed proposal given a Proposal message and a 335 // signing identity 336 func GetSignedProposal(prop *peer.Proposal, signer Signer) (*peer.SignedProposal, error) { 337 // check for nil argument 338 if prop == nil || signer == nil { 339 return nil, errors.New("nil arguments") 340 } 341 342 propBytes, err := proto.Marshal(prop) 343 if err != nil { 344 return nil, err 345 } 346 347 signature, err := signer.Sign(propBytes) 348 if err != nil { 349 return nil, err 350 } 351 352 return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, nil 353 } 354 355 // MockSignedEndorserProposalOrPanic creates a SignedProposal with the 356 // passed arguments 357 func MockSignedEndorserProposalOrPanic( 358 channelID string, 359 cs *peer.ChaincodeSpec, 360 creator, 361 signature []byte, 362 ) (*peer.SignedProposal, *peer.Proposal) { 363 prop, _, err := CreateChaincodeProposal( 364 common.HeaderType_ENDORSER_TRANSACTION, 365 channelID, 366 &peer.ChaincodeInvocationSpec{ChaincodeSpec: cs}, 367 creator) 368 if err != nil { 369 panic(err) 370 } 371 372 propBytes, err := proto.Marshal(prop) 373 if err != nil { 374 panic(err) 375 } 376 377 return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, prop 378 } 379 380 func MockSignedEndorserProposal2OrPanic( 381 channelID string, 382 cs *peer.ChaincodeSpec, 383 signer Signer, 384 ) (*peer.SignedProposal, *peer.Proposal) { 385 serializedSigner, err := signer.Serialize() 386 if err != nil { 387 panic(err) 388 } 389 390 prop, _, err := CreateChaincodeProposal( 391 common.HeaderType_ENDORSER_TRANSACTION, 392 channelID, 393 &peer.ChaincodeInvocationSpec{ChaincodeSpec: &peer.ChaincodeSpec{}}, 394 serializedSigner) 395 if err != nil { 396 panic(err) 397 } 398 399 sProp, err := GetSignedProposal(prop, signer) 400 if err != nil { 401 panic(err) 402 } 403 404 return sProp, prop 405 } 406 407 // GetBytesProposalPayloadForTx takes a ChaincodeProposalPayload and returns 408 // its serialized version according to the visibility field 409 func GetBytesProposalPayloadForTx( 410 payload *peer.ChaincodeProposalPayload, 411 ) ([]byte, error) { 412 // check for nil argument 413 if payload == nil { 414 return nil, errors.New("nil arguments") 415 } 416 417 // strip the transient bytes off the payload 418 cppNoTransient := &peer.ChaincodeProposalPayload{Input: payload.Input, TransientMap: nil} 419 cppBytes, err := GetBytesChaincodeProposalPayload(cppNoTransient) 420 if err != nil { 421 return nil, err 422 } 423 424 return cppBytes, nil 425 } 426 427 // GetProposalHash2 gets the proposal hash - this version 428 // is called by the committer where the visibility policy 429 // has already been enforced and so we already get what 430 // we have to get in ccPropPayl 431 func GetProposalHash2(header *common.Header, ccPropPayl []byte) ([]byte, error) { 432 // check for nil argument 433 if header == nil || 434 header.ChannelHeader == nil || 435 header.SignatureHeader == nil || 436 ccPropPayl == nil { 437 return nil, errors.New("nil arguments") 438 } 439 440 hash := sha256.New() 441 // hash the serialized Channel Header object 442 hash.Write(header.ChannelHeader) 443 // hash the serialized Signature Header object 444 hash.Write(header.SignatureHeader) 445 // hash the bytes of the chaincode proposal payload that we are given 446 hash.Write(ccPropPayl) 447 return hash.Sum(nil), nil 448 } 449 450 // GetProposalHash1 gets the proposal hash bytes after sanitizing the 451 // chaincode proposal payload according to the rules of visibility 452 func GetProposalHash1(header *common.Header, ccPropPayl []byte) ([]byte, error) { 453 // check for nil argument 454 if header == nil || 455 header.ChannelHeader == nil || 456 header.SignatureHeader == nil || 457 ccPropPayl == nil { 458 return nil, errors.New("nil arguments") 459 } 460 461 // unmarshal the chaincode proposal payload 462 cpp, err := UnmarshalChaincodeProposalPayload(ccPropPayl) 463 if err != nil { 464 return nil, err 465 } 466 467 ppBytes, err := GetBytesProposalPayloadForTx(cpp) 468 if err != nil { 469 return nil, err 470 } 471 472 hash2 := sha256.New() 473 // hash the serialized Channel Header object 474 hash2.Write(header.ChannelHeader) 475 // hash the serialized Signature Header object 476 hash2.Write(header.SignatureHeader) 477 // hash of the part of the chaincode proposal payload that will go to the tx 478 hash2.Write(ppBytes) 479 return hash2.Sum(nil), nil 480 } 481 482 // GetOrComputeTxIDFromEnvelope gets the txID present in a given transaction 483 // envelope. If the txID is empty, it constructs the txID from nonce and 484 // creator fields in the envelope. 485 func GetOrComputeTxIDFromEnvelope(txEnvelopBytes []byte) (string, error) { 486 txEnvelope, err := UnmarshalEnvelope(txEnvelopBytes) 487 if err != nil { 488 return "", errors.WithMessage(err, "error getting txID from envelope") 489 } 490 491 txPayload, err := UnmarshalPayload(txEnvelope.Payload) 492 if err != nil { 493 return "", errors.WithMessage(err, "error getting txID from payload") 494 } 495 496 if txPayload.Header == nil { 497 return "", errors.New("error getting txID from header: payload header is nil") 498 } 499 500 chdr, err := UnmarshalChannelHeader(txPayload.Header.ChannelHeader) 501 if err != nil { 502 return "", errors.WithMessage(err, "error getting txID from channel header") 503 } 504 505 if chdr.TxId != "" { 506 return chdr.TxId, nil 507 } 508 509 sighdr, err := UnmarshalSignatureHeader(txPayload.Header.SignatureHeader) 510 if err != nil { 511 return "", errors.WithMessage(err, "error getting nonce and creator for computing txID") 512 } 513 514 txid := ComputeTxID(sighdr.Nonce, sighdr.Creator) 515 return txid, nil 516 }