github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/protos/utils/txutils.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package utils 18 19 import ( 20 "errors" 21 "fmt" 22 23 "bytes" 24 25 "github.com/golang/protobuf/proto" 26 "github.com/hyperledger/fabric/bccsp" 27 "github.com/hyperledger/fabric/bccsp/factory" 28 "github.com/hyperledger/fabric/common/crypto" 29 "github.com/hyperledger/fabric/msp" 30 "github.com/hyperledger/fabric/protos/common" 31 "github.com/hyperledger/fabric/protos/peer" 32 ) 33 34 // GetPayloads get's the underlying payload objects in a TransactionAction 35 func GetPayloads(txActions *peer.TransactionAction) (*peer.ChaincodeActionPayload, *peer.ChaincodeAction, error) { 36 // TODO: pass in the tx type (in what follows we're assuming the type is ENDORSER_TRANSACTION) 37 ccPayload := &peer.ChaincodeActionPayload{} 38 err := proto.Unmarshal(txActions.Payload, ccPayload) 39 if err != nil { 40 return nil, nil, err 41 } 42 43 if ccPayload.Action == nil || ccPayload.Action.ProposalResponsePayload == nil { 44 return nil, nil, fmt.Errorf("no payload in ChaincodeActionPayload") 45 } 46 pRespPayload := &peer.ProposalResponsePayload{} 47 err = proto.Unmarshal(ccPayload.Action.ProposalResponsePayload, pRespPayload) 48 if err != nil { 49 return nil, nil, err 50 } 51 52 if pRespPayload.Extension == nil { 53 return nil, nil, fmt.Errorf("response payload is missing extension") 54 } 55 56 respPayload := &peer.ChaincodeAction{} 57 err = proto.Unmarshal(pRespPayload.Extension, respPayload) 58 if err != nil { 59 return ccPayload, nil, err 60 } 61 return ccPayload, respPayload, nil 62 } 63 64 // GetEnvelopeFromBlock gets an envelope from a block's Data field. 65 func GetEnvelopeFromBlock(data []byte) (*common.Envelope, error) { 66 //Block always begins with an envelope 67 var err error 68 env := &common.Envelope{} 69 if err = proto.Unmarshal(data, env); err != nil { 70 return nil, fmt.Errorf("Error getting envelope(%s)", err) 71 } 72 73 return env, nil 74 } 75 76 // CreateSignedEnvelope creates a signed envelope of the desired type, with marshaled dataMsg and signs it 77 func CreateSignedEnvelope(txType common.HeaderType, channelID string, signer crypto.LocalSigner, dataMsg proto.Message, msgVersion int32, epoch uint64) (*common.Envelope, error) { 78 payloadChannelHeader := MakeChannelHeader(txType, msgVersion, channelID, epoch) 79 80 var err error 81 payloadSignatureHeader := &common.SignatureHeader{} 82 83 if signer != nil { 84 payloadSignatureHeader, err = signer.NewSignatureHeader() 85 if err != nil { 86 return nil, err 87 } 88 } 89 90 data, err := proto.Marshal(dataMsg) 91 if err != nil { 92 return nil, err 93 } 94 95 paylBytes := MarshalOrPanic(&common.Payload{ 96 Header: MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader), 97 Data: data, 98 }) 99 100 var sig []byte 101 if signer != nil { 102 sig, err = signer.Sign(paylBytes) 103 if err != nil { 104 return nil, err 105 } 106 } 107 108 return &common.Envelope{Payload: paylBytes, Signature: sig}, nil 109 } 110 111 // CreateSignedTx assembles an Envelope message from proposal, endorsements, and a signer. 112 // This function should be called by a client when it has collected enough endorsements 113 // for a proposal to create a transaction and submit it to peers for ordering 114 func CreateSignedTx(proposal *peer.Proposal, signer msp.SigningIdentity, resps ...*peer.ProposalResponse) (*common.Envelope, error) { 115 if len(resps) == 0 { 116 return nil, fmt.Errorf("At least one proposal response is necessary") 117 } 118 119 // the original header 120 hdr, err := GetHeader(proposal.Header) 121 if err != nil { 122 return nil, fmt.Errorf("Could not unmarshal the proposal header") 123 } 124 125 // the original payload 126 pPayl, err := GetChaincodeProposalPayload(proposal.Payload) 127 if err != nil { 128 return nil, fmt.Errorf("Could not unmarshal the proposal payload") 129 } 130 131 // check that the signer is the same that is referenced in the header 132 // TODO: maybe worth removing? 133 signerBytes, err := signer.Serialize() 134 if err != nil { 135 return nil, err 136 } 137 138 shdr, err := GetSignatureHeader(hdr.SignatureHeader) 139 if err != nil { 140 return nil, err 141 } 142 143 if bytes.Compare(signerBytes, shdr.Creator) != 0 { 144 return nil, fmt.Errorf("The signer needs to be the same as the one referenced in the header") 145 } 146 147 // get header extensions so we have the visibility field 148 hdrExt, err := GetChaincodeHeaderExtension(hdr) 149 if err != nil { 150 return nil, err 151 } 152 153 // ensure that all actions are bitwise equal and that they are successful 154 var a1 []byte 155 for n, r := range resps { 156 if n == 0 { 157 a1 = r.Payload 158 if r.Response.Status != 200 { 159 return nil, fmt.Errorf("Proposal response was not successful, error code %d, msg %s", r.Response.Status, r.Response.Message) 160 } 161 continue 162 } 163 164 if bytes.Compare(a1, r.Payload) != 0 { 165 return nil, fmt.Errorf("ProposalResponsePayloads do not match") 166 } 167 } 168 169 // fill endorsements 170 endorsements := make([]*peer.Endorsement, len(resps)) 171 for n, r := range resps { 172 endorsements[n] = r.Endorsement 173 } 174 175 // create ChaincodeEndorsedAction 176 cea := &peer.ChaincodeEndorsedAction{ProposalResponsePayload: resps[0].Payload, Endorsements: endorsements} 177 178 // obtain the bytes of the proposal payload that will go to the transaction 179 propPayloadBytes, err := GetBytesProposalPayloadForTx(pPayl, hdrExt.PayloadVisibility) 180 if err != nil { 181 return nil, err 182 } 183 184 // serialize the chaincode action payload 185 cap := &peer.ChaincodeActionPayload{ChaincodeProposalPayload: propPayloadBytes, Action: cea} 186 capBytes, err := GetBytesChaincodeActionPayload(cap) 187 if err != nil { 188 return nil, err 189 } 190 191 // create a transaction 192 taa := &peer.TransactionAction{Header: hdr.SignatureHeader, Payload: capBytes} 193 taas := make([]*peer.TransactionAction, 1) 194 taas[0] = taa 195 tx := &peer.Transaction{Actions: taas} 196 197 // serialize the tx 198 txBytes, err := GetBytesTransaction(tx) 199 if err != nil { 200 return nil, err 201 } 202 203 // create the payload 204 payl := &common.Payload{Header: hdr, Data: txBytes} 205 paylBytes, err := GetBytesPayload(payl) 206 if err != nil { 207 return nil, err 208 } 209 210 // sign the payload 211 sig, err := signer.Sign(paylBytes) 212 if err != nil { 213 return nil, err 214 } 215 216 // here's the envelope 217 return &common.Envelope{Payload: paylBytes, Signature: sig}, nil 218 } 219 220 // CreateProposalResponse creates a proposal response. 221 func CreateProposalResponse(hdrbytes []byte, payl []byte, response *peer.Response, results []byte, events []byte, ccid *peer.ChaincodeID, visibility []byte, signingEndorser msp.SigningIdentity) (*peer.ProposalResponse, error) { 222 hdr, err := GetHeader(hdrbytes) 223 if err != nil { 224 return nil, err 225 } 226 227 // obtain the proposal hash given proposal header, payload and the requested visibility 228 pHashBytes, err := GetProposalHash1(hdr, payl, visibility) 229 if err != nil { 230 return nil, fmt.Errorf("Could not compute proposal hash: err %s", err) 231 } 232 233 // get the bytes of the proposal response payload - we need to sign them 234 prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events, ccid) 235 if err != nil { 236 return nil, errors.New("Failure while marshaling the ProposalResponsePayload") 237 } 238 239 // serialize the signing identity 240 endorser, err := signingEndorser.Serialize() 241 if err != nil { 242 return nil, fmt.Errorf("Could not serialize the signing identity for %s, err %s", signingEndorser.GetIdentifier(), err) 243 } 244 245 // sign the concatenation of the proposal response and the serialized endorser identity with this endorser's key 246 signature, err := signingEndorser.Sign(append(prpBytes, endorser...)) 247 if err != nil { 248 return nil, fmt.Errorf("Could not sign the proposal response payload, err %s", err) 249 } 250 251 resp := &peer.ProposalResponse{ 252 // Timestamp: TODO! 253 Version: 1, // TODO: pick right version number 254 Endorsement: &peer.Endorsement{Signature: signature, Endorser: endorser}, 255 Payload: prpBytes, 256 Response: &peer.Response{Status: 200, Message: "OK"}} 257 258 return resp, nil 259 } 260 261 // CreateProposalResponseFailure creates a proposal response for cases where 262 // endorsement proposal fails either due to a endorsement failure or a chaincode 263 // failure (chaincode response status >= shim.ERRORTHRESHOLD) 264 func CreateProposalResponseFailure(hdrbytes []byte, payl []byte, response *peer.Response, results []byte, events []byte, ccid *peer.ChaincodeID, visibility []byte) (*peer.ProposalResponse, error) { 265 hdr, err := GetHeader(hdrbytes) 266 if err != nil { 267 return nil, err 268 } 269 270 // obtain the proposal hash given proposal header, payload and the requested visibility 271 pHashBytes, err := GetProposalHash1(hdr, payl, visibility) 272 if err != nil { 273 return nil, fmt.Errorf("Could not compute proposal hash: err %s", err) 274 } 275 276 // get the bytes of the proposal response payload 277 prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events, ccid) 278 if err != nil { 279 return nil, errors.New("Failure while marshaling the ProposalResponsePayload") 280 } 281 282 resp := &peer.ProposalResponse{ 283 // Timestamp: TODO! 284 Payload: prpBytes, 285 Response: &peer.Response{Status: 500, Message: "Chaincode Error"}} 286 287 return resp, nil 288 } 289 290 // GetSignedProposal returns a signed proposal given a Proposal message and a signing identity 291 func GetSignedProposal(prop *peer.Proposal, signer msp.SigningIdentity) (*peer.SignedProposal, error) { 292 // check for nil argument 293 if prop == nil || signer == nil { 294 return nil, fmt.Errorf("Nil arguments") 295 } 296 297 propBytes, err := GetBytesProposal(prop) 298 if err != nil { 299 return nil, err 300 } 301 302 signature, err := signer.Sign(propBytes) 303 if err != nil { 304 return nil, err 305 } 306 307 return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, nil 308 } 309 310 // GetSignedEvent returns a signed event given an Event message and a signing identity 311 func GetSignedEvent(evt *peer.Event, signer msp.SigningIdentity) (*peer.SignedEvent, error) { 312 // check for nil argument 313 if evt == nil || signer == nil { 314 return nil, errors.New("nil arguments") 315 } 316 317 evtBytes, err := proto.Marshal(evt) 318 if err != nil { 319 return nil, err 320 } 321 322 signature, err := signer.Sign(evtBytes) 323 if err != nil { 324 return nil, err 325 } 326 327 return &peer.SignedEvent{EventBytes: evtBytes, Signature: signature}, nil 328 } 329 330 // MockSignedEndorserProposalOrPanic creates a SignedProposal with the passed arguments 331 func MockSignedEndorserProposalOrPanic(chainID string, cs *peer.ChaincodeSpec, creator, signature []byte) (*peer.SignedProposal, *peer.Proposal) { 332 prop, _, err := CreateChaincodeProposal( 333 common.HeaderType_ENDORSER_TRANSACTION, 334 chainID, 335 &peer.ChaincodeInvocationSpec{ChaincodeSpec: cs}, 336 creator) 337 if err != nil { 338 panic(err) 339 } 340 341 propBytes, err := GetBytesProposal(prop) 342 if err != nil { 343 panic(err) 344 } 345 346 return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, prop 347 } 348 349 func MockSignedEndorserProposal2OrPanic(chainID string, cs *peer.ChaincodeSpec, signer msp.SigningIdentity) (*peer.SignedProposal, *peer.Proposal) { 350 serializedSigner, err := signer.Serialize() 351 if err != nil { 352 panic(err) 353 } 354 355 prop, _, err := CreateChaincodeProposal( 356 common.HeaderType_ENDORSER_TRANSACTION, 357 chainID, 358 &peer.ChaincodeInvocationSpec{ChaincodeSpec: &peer.ChaincodeSpec{}}, 359 serializedSigner) 360 if err != nil { 361 panic(err) 362 } 363 364 sProp, err := GetSignedProposal(prop, signer) 365 if err != nil { 366 panic(err) 367 } 368 369 return sProp, prop 370 } 371 372 // GetBytesProposalPayloadForTx takes a ChaincodeProposalPayload and returns its serialized 373 // version according to the visibility field 374 func GetBytesProposalPayloadForTx(payload *peer.ChaincodeProposalPayload, visibility []byte) ([]byte, error) { 375 // check for nil argument 376 if payload == nil /* || visibility == nil */ { 377 return nil, fmt.Errorf("Nil arguments") 378 } 379 380 // strip the transient bytes off the payload - this needs to be done no matter the visibility mode 381 cppNoTransient := &peer.ChaincodeProposalPayload{Input: payload.Input, TransientMap: nil} 382 cppBytes, err := GetBytesChaincodeProposalPayload(cppNoTransient) 383 if err != nil { 384 return nil, errors.New("Failure while marshalling the ChaincodeProposalPayload!") 385 } 386 387 // currently the fabric only supports full visibility: this means that 388 // there are no restrictions on which parts of the proposal payload will 389 // be visible in the final transaction; this default approach requires 390 // no additional instructions in the PayloadVisibility field; however 391 // the fabric may be extended to encode more elaborate visibility 392 // mechanisms that shall be encoded in this field (and handled 393 // appropriately by the peer) 394 395 return cppBytes, nil 396 } 397 398 // GetProposalHash2 gets the proposal hash - this version 399 // is called by the committer where the visibility policy 400 // has already been enforced and so we already get what 401 // we have to get in ccPropPayl 402 func GetProposalHash2(header *common.Header, ccPropPayl []byte) ([]byte, error) { 403 // check for nil argument 404 if header == nil || 405 header.ChannelHeader == nil || 406 header.SignatureHeader == nil || 407 ccPropPayl == nil { 408 return nil, fmt.Errorf("Nil arguments") 409 } 410 411 hash, err := factory.GetDefault().GetHash(&bccsp.SHA256Opts{}) 412 if err != nil { 413 return nil, fmt.Errorf("Failed instantiating hash function [%s]", err) 414 } 415 hash.Write(header.ChannelHeader) // hash the serialized Channel Header object 416 hash.Write(header.SignatureHeader) // hash the serialized Signature Header object 417 hash.Write(ccPropPayl) // hash the bytes of the chaincode proposal payload that we are given 418 419 return hash.Sum(nil), nil 420 } 421 422 // GetProposalHash1 gets the proposal hash bytes after sanitizing the 423 // chaincode proposal payload according to the rules of visibility 424 func GetProposalHash1(header *common.Header, ccPropPayl []byte, visibility []byte) ([]byte, error) { 425 // check for nil argument 426 if header == nil || 427 header.ChannelHeader == nil || 428 header.SignatureHeader == nil || 429 ccPropPayl == nil /* || visibility == nil */ { 430 return nil, fmt.Errorf("Nil arguments") 431 } 432 433 // unmarshal the chaincode proposal payload 434 cpp := &peer.ChaincodeProposalPayload{} 435 err := proto.Unmarshal(ccPropPayl, cpp) 436 if err != nil { 437 return nil, errors.New("Failure while unmarshalling the ChaincodeProposalPayload!") 438 } 439 440 ppBytes, err := GetBytesProposalPayloadForTx(cpp, visibility) 441 if err != nil { 442 return nil, err 443 } 444 445 hash2, err := factory.GetDefault().GetHash(&bccsp.SHA256Opts{}) 446 if err != nil { 447 return nil, fmt.Errorf("Failed instantiating hash function [%s]", err) 448 } 449 hash2.Write(header.ChannelHeader) // hash the serialized Channel Header object 450 hash2.Write(header.SignatureHeader) // hash the serialized Signature Header object 451 hash2.Write(ppBytes) // hash of the part of the chaincode proposal payload that will go to the tx 452 453 return hash2.Sum(nil), nil 454 }