github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/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, err 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 payloadSignatureHeader, err := signer.NewSignatureHeader() 81 if err != nil { 82 return nil, err 83 } 84 85 data, err := proto.Marshal(dataMsg) 86 if err != nil { 87 return nil, err 88 } 89 90 paylBytes := MarshalOrPanic(&common.Payload{ 91 Header: MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader), 92 Data: data, 93 }) 94 95 sig, err := signer.Sign(paylBytes) 96 if err != nil { 97 return nil, err 98 } 99 100 return &common.Envelope{Payload: paylBytes, Signature: sig}, nil 101 } 102 103 // CreateSignedTx assembles an Envelope message from proposal, endorsements, and a signer. 104 // This function should be called by a client when it has collected enough endorsements 105 // for a proposal to create a transaction and submit it to peers for ordering 106 func CreateSignedTx(proposal *peer.Proposal, signer msp.SigningIdentity, resps ...*peer.ProposalResponse) (*common.Envelope, error) { 107 if len(resps) == 0 { 108 return nil, fmt.Errorf("At least one proposal response is necessary") 109 } 110 111 // the original header 112 hdr, err := GetHeader(proposal.Header) 113 if err != nil { 114 return nil, fmt.Errorf("Could not unmarshal the proposal header") 115 } 116 117 // the original payload 118 pPayl, err := GetChaincodeProposalPayload(proposal.Payload) 119 if err != nil { 120 return nil, fmt.Errorf("Could not unmarshal the proposal payload") 121 } 122 123 // check that the signer is the same that is referenced in the header 124 // TODO: maybe worth removing? 125 signerBytes, err := signer.Serialize() 126 if err != nil { 127 return nil, err 128 } 129 130 shdr, err := GetSignatureHeader(hdr.SignatureHeader) 131 if err != nil { 132 return nil, err 133 } 134 135 if bytes.Compare(signerBytes, shdr.Creator) != 0 { 136 return nil, fmt.Errorf("The signer needs to be the same as the one referenced in the header") 137 } 138 139 // get header extensions so we have the visibility field 140 hdrExt, err := GetChaincodeHeaderExtension(hdr) 141 if err != nil { 142 return nil, err 143 } 144 145 // ensure that all actions are bitwise equal and that they are successful 146 var a1 []byte 147 for n, r := range resps { 148 if n == 0 { 149 a1 = r.Payload 150 if r.Response.Status != 200 { 151 return nil, fmt.Errorf("Proposal response was not successful, error code %d, msg %s", r.Response.Status, r.Response.Message) 152 } 153 continue 154 } 155 156 if bytes.Compare(a1, r.Payload) != 0 { 157 return nil, fmt.Errorf("ProposalResponsePayloads do not match") 158 } 159 } 160 161 // fill endorsements 162 endorsements := make([]*peer.Endorsement, len(resps)) 163 for n, r := range resps { 164 endorsements[n] = r.Endorsement 165 } 166 167 // create ChaincodeEndorsedAction 168 cea := &peer.ChaincodeEndorsedAction{ProposalResponsePayload: resps[0].Payload, Endorsements: endorsements} 169 170 // obtain the bytes of the proposal payload that will go to the transaction 171 propPayloadBytes, err := GetBytesProposalPayloadForTx(pPayl, hdrExt.PayloadVisibility) 172 if err != nil { 173 return nil, err 174 } 175 176 // serialize the chaincode action payload 177 cap := &peer.ChaincodeActionPayload{ChaincodeProposalPayload: propPayloadBytes, Action: cea} 178 capBytes, err := GetBytesChaincodeActionPayload(cap) 179 if err != nil { 180 return nil, err 181 } 182 183 // create a transaction 184 taa := &peer.TransactionAction{Header: hdr.SignatureHeader, Payload: capBytes} 185 taas := make([]*peer.TransactionAction, 1) 186 taas[0] = taa 187 tx := &peer.Transaction{Actions: taas} 188 189 // serialize the tx 190 txBytes, err := GetBytesTransaction(tx) 191 if err != nil { 192 return nil, err 193 } 194 195 // create the payload 196 payl := &common.Payload{Header: hdr, Data: txBytes} 197 paylBytes, err := GetBytesPayload(payl) 198 if err != nil { 199 return nil, err 200 } 201 202 // sign the payload 203 sig, err := signer.Sign(paylBytes) 204 if err != nil { 205 return nil, err 206 } 207 208 // here's the envelope 209 return &common.Envelope{Payload: paylBytes, Signature: sig}, nil 210 } 211 212 // CreateProposalResponse creates a proposal response. 213 func CreateProposalResponse(hdrbytes []byte, payl []byte, response *peer.Response, results []byte, events []byte, ccid *peer.ChaincodeID, visibility []byte, signingEndorser msp.SigningIdentity) (*peer.ProposalResponse, error) { 214 hdr, err := GetHeader(hdrbytes) 215 if err != nil { 216 return nil, err 217 } 218 219 // obtain the proposal hash given proposal header, payload and the requested visibility 220 pHashBytes, err := GetProposalHash1(hdr, payl, visibility) 221 if err != nil { 222 return nil, fmt.Errorf("Could not compute proposal hash: err %s", err) 223 } 224 225 // get the bytes of the proposal response payload - we need to sign them 226 prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events, ccid) 227 if err != nil { 228 return nil, errors.New("Failure while unmarshalling the ProposalResponsePayload") 229 } 230 231 // serialize the signing identity 232 endorser, err := signingEndorser.Serialize() 233 if err != nil { 234 return nil, fmt.Errorf("Could not serialize the signing identity for %s, err %s", signingEndorser.GetIdentifier(), err) 235 } 236 237 // sign the concatenation of the proposal response and the serialized endorser identity with this endorser's key 238 signature, err := signingEndorser.Sign(append(prpBytes, endorser...)) 239 if err != nil { 240 return nil, fmt.Errorf("Could not sign the proposal response payload, err %s", err) 241 } 242 243 resp := &peer.ProposalResponse{ 244 // Timestamp: TODO! 245 Version: 1, // TODO: pick right version number 246 Endorsement: &peer.Endorsement{Signature: signature, Endorser: endorser}, 247 Payload: prpBytes, 248 Response: &peer.Response{Status: 200, Message: "OK"}} 249 250 return resp, nil 251 } 252 253 // GetSignedProposal returns a signed proposal given a Proposal message and a signing identity 254 func GetSignedProposal(prop *peer.Proposal, signer msp.SigningIdentity) (*peer.SignedProposal, error) { 255 // check for nil argument 256 if prop == nil || signer == nil { 257 return nil, fmt.Errorf("Nil arguments") 258 } 259 260 propBytes, err := GetBytesProposal(prop) 261 if err != nil { 262 return nil, err 263 } 264 265 signature, err := signer.Sign(propBytes) 266 if err != nil { 267 return nil, err 268 } 269 270 return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, nil 271 } 272 273 // GetSignedEvent returns a signed event given an Event message and a signing identity 274 func GetSignedEvent(evt *peer.Event, signer msp.SigningIdentity) (*peer.SignedEvent, error) { 275 // check for nil argument 276 if evt == nil || signer == nil { 277 return nil, errors.New("nil arguments") 278 } 279 280 evtBytes, err := proto.Marshal(evt) 281 if err != nil { 282 return nil, err 283 } 284 285 signature, err := signer.Sign(evtBytes) 286 if err != nil { 287 return nil, err 288 } 289 290 return &peer.SignedEvent{EventBytes: evtBytes, Signature: signature}, nil 291 } 292 293 // MockSignedEndorserProposalOrPanic creates a SignedProposal with the passed arguments 294 func MockSignedEndorserProposalOrPanic(chainID string, cs *peer.ChaincodeSpec, creator, signature []byte) (*peer.SignedProposal, *peer.Proposal) { 295 prop, _, err := CreateChaincodeProposal( 296 common.HeaderType_ENDORSER_TRANSACTION, 297 chainID, 298 &peer.ChaincodeInvocationSpec{ChaincodeSpec: cs}, 299 creator) 300 if err != nil { 301 panic(err) 302 } 303 304 propBytes, err := GetBytesProposal(prop) 305 if err != nil { 306 panic(err) 307 } 308 309 return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, prop 310 } 311 312 // GetBytesProposalPayloadForTx takes a ChaincodeProposalPayload and returns its serialized 313 // version according to the visibility field 314 func GetBytesProposalPayloadForTx(payload *peer.ChaincodeProposalPayload, visibility []byte) ([]byte, error) { 315 // check for nil argument 316 if payload == nil /* || visibility == nil */ { 317 return nil, fmt.Errorf("Nil arguments") 318 } 319 320 // strip the transient bytes off the payload - this needs to be done no matter the visibility mode 321 cppNoTransient := &peer.ChaincodeProposalPayload{Input: payload.Input, TransientMap: nil} 322 cppBytes, err := GetBytesChaincodeProposalPayload(cppNoTransient) 323 if err != nil { 324 return nil, errors.New("Failure while marshalling the ChaincodeProposalPayload!") 325 } 326 327 // currently the fabric only supports full visibility: this means that 328 // there are no restrictions on which parts of the proposal payload will 329 // be visible in the final transaction; this default approach requires 330 // no additional instructions in the PayloadVisibility field; however 331 // the fabric may be extended to encode more elaborate visibility 332 // mechanisms that shall be encoded in this field (and handled 333 // appropriately by the peer) 334 335 return cppBytes, nil 336 } 337 338 // GetProposalHash2 gets the proposal hash - this version 339 // is called by the committer where the visibility policy 340 // has already been enforced and so we already get what 341 // we have to get in ccPropPayl 342 func GetProposalHash2(header *common.Header, ccPropPayl []byte) ([]byte, error) { 343 // check for nil argument 344 if header == nil || 345 header.ChannelHeader == nil || 346 header.SignatureHeader == nil || 347 ccPropPayl == nil { 348 return nil, fmt.Errorf("Nil arguments") 349 } 350 351 hash, err := factory.GetDefault().GetHash(&bccsp.SHA256Opts{}) 352 if err != nil { 353 return nil, fmt.Errorf("Failed instantiating hash function [%s]", err) 354 } 355 hash.Write(header.ChannelHeader) // hash the serialized Channel Header object 356 hash.Write(header.SignatureHeader) // hash the serialized Signature Header object 357 hash.Write(ccPropPayl) // hash the bytes of the chaincode proposal payload that we are given 358 359 return hash.Sum(nil), nil 360 } 361 362 // GetProposalHash1 gets the proposal hash bytes after sanitizing the 363 // chaincode proposal payload according to the rules of visibility 364 func GetProposalHash1(header *common.Header, ccPropPayl []byte, visibility []byte) ([]byte, error) { 365 // check for nil argument 366 if header == nil || 367 header.ChannelHeader == nil || 368 header.SignatureHeader == nil || 369 ccPropPayl == nil /* || visibility == nil */ { 370 return nil, fmt.Errorf("Nil arguments") 371 } 372 373 // unmarshal the chaincode proposal payload 374 cpp := &peer.ChaincodeProposalPayload{} 375 err := proto.Unmarshal(ccPropPayl, cpp) 376 if err != nil { 377 return nil, errors.New("Failure while unmarshalling the ChaincodeProposalPayload!") 378 } 379 380 ppBytes, err := GetBytesProposalPayloadForTx(cpp, visibility) 381 if err != nil { 382 return nil, err 383 } 384 385 hash2, err := factory.GetDefault().GetHash(&bccsp.SHA256Opts{}) 386 if err != nil { 387 return nil, fmt.Errorf("Failed instantiating hash function [%s]", err) 388 } 389 hash2.Write(header.ChannelHeader) // hash the serialized Channel Header object 390 hash2.Write(header.SignatureHeader) // hash the serialized Signature Header object 391 hash2.Write(ppBytes) // hash of the part of the chaincode proposal payload that will go to the tx 392 393 return hash2.Sum(nil), nil 394 }