github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/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, 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) 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 // GetBytesProposalPayloadForTx takes a ChaincodeProposalPayload and returns its serialized 274 // version according to the visibility field 275 func GetBytesProposalPayloadForTx(payload *peer.ChaincodeProposalPayload, visibility []byte) ([]byte, error) { 276 // check for nil argument 277 if payload == nil /* || visibility == nil */ { 278 return nil, fmt.Errorf("Nil arguments") 279 } 280 281 // strip the transient bytes off the payload - this needs to be done no matter the visibility mode 282 cppNoTransient := &peer.ChaincodeProposalPayload{Input: payload.Input, TransientMap: nil} 283 cppBytes, err := GetBytesChaincodeProposalPayload(cppNoTransient) 284 if err != nil { 285 return nil, errors.New("Failure while marshalling the ChaincodeProposalPayload!") 286 } 287 288 // currently the fabric only supports full visibility: this means that 289 // there are no restrictions on which parts of the proposal payload will 290 // be visible in the final transaction; this default approach requires 291 // no additional instructions in the PayloadVisibility field; however 292 // the fabric may be extended to encode more elaborate visibility 293 // mechanisms that shall be encoded in this field (and handled 294 // appropriately by the peer) 295 296 return cppBytes, nil 297 } 298 299 // GetProposalHash2 gets the proposal hash - this version 300 // is called by the committer where the visibility policy 301 // has already been enforced and so we already get what 302 // we have to get in ccPropPayl 303 func GetProposalHash2(header *common.Header, ccPropPayl []byte) ([]byte, error) { 304 // check for nil argument 305 if header == nil || 306 header.ChannelHeader == nil || 307 header.SignatureHeader == nil || 308 ccPropPayl == nil { 309 return nil, fmt.Errorf("Nil arguments") 310 } 311 312 hash, err := factory.GetDefault().GetHash(&bccsp.SHAOpts{}) 313 if err != nil { 314 return nil, fmt.Errorf("Failed instantiating hash function [%s]", err) 315 } 316 hash.Write(header.ChannelHeader) // hash the serialized Channel Header object 317 hash.Write(header.SignatureHeader) // hash the serialized Signature Header object 318 hash.Write(ccPropPayl) // hash the bytes of the chaincode proposal payload that we are given 319 320 return hash.Sum(nil), nil 321 } 322 323 // GetProposalHash1 gets the proposal hash bytes after sanitizing the 324 // chaincode proposal payload according to the rules of visibility 325 func GetProposalHash1(header *common.Header, ccPropPayl []byte, visibility []byte) ([]byte, error) { 326 // check for nil argument 327 if header == nil || 328 header.ChannelHeader == nil || 329 header.SignatureHeader == nil || 330 ccPropPayl == nil /* || visibility == nil */ { 331 return nil, fmt.Errorf("Nil arguments") 332 } 333 334 // unmarshal the chaincode proposal payload 335 cpp := &peer.ChaincodeProposalPayload{} 336 err := proto.Unmarshal(ccPropPayl, cpp) 337 if err != nil { 338 return nil, errors.New("Failure while unmarshalling the ChaincodeProposalPayload!") 339 } 340 341 ppBytes, err := GetBytesProposalPayloadForTx(cpp, visibility) 342 if err != nil { 343 return nil, err 344 } 345 346 hash2, err := factory.GetDefault().GetHash(&bccsp.SHAOpts{}) 347 if err != nil { 348 return nil, fmt.Errorf("Failed instantiating hash function [%s]", err) 349 } 350 hash2.Write(header.ChannelHeader) // hash the serialized Channel Header object 351 hash2.Write(header.SignatureHeader) // hash the serialized Signature Header object 352 hash2.Write(ppBytes) // hash of the part of the chaincode proposal payload that will go to the tx 353 354 return hash2.Sum(nil), nil 355 }