github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/protoutil/txutils_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package protoutil_test 8 9 import ( 10 "encoding/hex" 11 "errors" 12 "strconv" 13 "testing" 14 15 "github.com/golang/protobuf/proto" 16 cb "github.com/hyperledger/fabric-protos-go/common" 17 pb "github.com/hyperledger/fabric-protos-go/peer" 18 19 "github.com/osdi23p228/fabric/protoutil" 20 "github.com/osdi23p228/fabric/protoutil/fakes" 21 "github.com/stretchr/testify/assert" 22 ) 23 24 func TestGetPayloads(t *testing.T) { 25 var txAction *pb.TransactionAction 26 var err error 27 28 // good 29 ccActionBytes, _ := proto.Marshal(&pb.ChaincodeAction{ 30 Results: []byte("results"), 31 }) 32 proposalResponsePayload := &pb.ProposalResponsePayload{ 33 Extension: ccActionBytes, 34 } 35 proposalResponseBytes, err := proto.Marshal(proposalResponsePayload) 36 assert.NoError(t, err) 37 ccActionPayload := &pb.ChaincodeActionPayload{ 38 Action: &pb.ChaincodeEndorsedAction{ 39 ProposalResponsePayload: proposalResponseBytes, 40 }, 41 } 42 ccActionPayloadBytes, _ := proto.Marshal(ccActionPayload) 43 txAction = &pb.TransactionAction{ 44 Payload: ccActionPayloadBytes, 45 } 46 _, _, err = protoutil.GetPayloads(txAction) 47 assert.NoError(t, err, "Unexpected error getting payload bytes") 48 t.Logf("error1 [%s]", err) 49 50 // nil proposal response extension 51 proposalResponseBytes, err = proto.Marshal(&pb.ProposalResponsePayload{ 52 Extension: nil, 53 }) 54 assert.NoError(t, err) 55 ccActionPayloadBytes, _ = proto.Marshal(&pb.ChaincodeActionPayload{ 56 Action: &pb.ChaincodeEndorsedAction{ 57 ProposalResponsePayload: proposalResponseBytes, 58 }, 59 }) 60 txAction = &pb.TransactionAction{ 61 Payload: ccActionPayloadBytes, 62 } 63 _, _, err = protoutil.GetPayloads(txAction) 64 assert.Error(t, err, "Expected error with nil proposal response extension") 65 t.Logf("error2 [%s]", err) 66 67 // malformed proposal response payload 68 ccActionPayloadBytes, _ = proto.Marshal(&pb.ChaincodeActionPayload{ 69 Action: &pb.ChaincodeEndorsedAction{ 70 ProposalResponsePayload: []byte("bad payload"), 71 }, 72 }) 73 txAction = &pb.TransactionAction{ 74 Payload: ccActionPayloadBytes, 75 } 76 _, _, err = protoutil.GetPayloads(txAction) 77 assert.Error(t, err, "Expected error with malformed proposal response payload") 78 t.Logf("error3 [%s]", err) 79 80 // malformed proposal response payload extension 81 proposalResponseBytes, _ = proto.Marshal(&pb.ProposalResponsePayload{ 82 Extension: []byte("bad extension"), 83 }) 84 ccActionPayloadBytes, _ = proto.Marshal(&pb.ChaincodeActionPayload{ 85 Action: &pb.ChaincodeEndorsedAction{ 86 ProposalResponsePayload: proposalResponseBytes, 87 }, 88 }) 89 txAction = &pb.TransactionAction{ 90 Payload: ccActionPayloadBytes, 91 } 92 _, _, err = protoutil.GetPayloads(txAction) 93 assert.Error(t, err, "Expected error with malformed proposal response extension") 94 t.Logf("error4 [%s]", err) 95 96 // nil proposal response payload extension 97 proposalResponseBytes, _ = proto.Marshal(&pb.ProposalResponsePayload{ 98 ProposalHash: []byte("hash"), 99 }) 100 ccActionPayloadBytes, _ = proto.Marshal(&pb.ChaincodeActionPayload{ 101 Action: &pb.ChaincodeEndorsedAction{ 102 ProposalResponsePayload: proposalResponseBytes, 103 }, 104 }) 105 txAction = &pb.TransactionAction{ 106 Payload: ccActionPayloadBytes, 107 } 108 _, _, err = protoutil.GetPayloads(txAction) 109 assert.Error(t, err, "Expected error with nil proposal response extension") 110 t.Logf("error5 [%s]", err) 111 112 // malformed transaction action payload 113 txAction = &pb.TransactionAction{ 114 Payload: []byte("bad payload"), 115 } 116 _, _, err = protoutil.GetPayloads(txAction) 117 assert.Error(t, err, "Expected error with malformed transaction action payload") 118 t.Logf("error6 [%s]", err) 119 120 } 121 122 func TestCreateSignedTx(t *testing.T) { 123 var err error 124 prop := &pb.Proposal{} 125 126 signID := &fakes.SignerSerializer{} 127 signID.SerializeReturns([]byte("signer"), nil) 128 signerBytes, err := signID.Serialize() 129 assert.NoError(t, err, "Unexpected error serializing signing identity") 130 131 ccHeaderExtensionBytes := protoutil.MarshalOrPanic(&pb.ChaincodeHeaderExtension{}) 132 chdrBytes := protoutil.MarshalOrPanic(&cb.ChannelHeader{ 133 Extension: ccHeaderExtensionBytes, 134 }) 135 shdrBytes := protoutil.MarshalOrPanic(&cb.SignatureHeader{ 136 Creator: signerBytes, 137 }) 138 responses := []*pb.ProposalResponse{{}} 139 140 // malformed signature header 141 headerBytes := protoutil.MarshalOrPanic(&cb.Header{ 142 SignatureHeader: []byte("bad signature header"), 143 }) 144 prop.Header = headerBytes 145 _, err = protoutil.CreateSignedTx(prop, signID, responses...) 146 assert.Error(t, err, "Expected error with malformed signature header") 147 148 // set up the header bytes for the remaining tests 149 headerBytes, _ = proto.Marshal(&cb.Header{ 150 ChannelHeader: chdrBytes, 151 SignatureHeader: shdrBytes, 152 }) 153 prop.Header = headerBytes 154 155 nonMatchingTests := []struct { 156 responses []*pb.ProposalResponse 157 expectedError string 158 }{ 159 // good responses, but different payloads 160 { 161 []*pb.ProposalResponse{ 162 {Payload: []byte("payload"), Response: &pb.Response{Status: int32(200)}}, 163 {Payload: []byte("payload2"), Response: &pb.Response{Status: int32(200)}}, 164 }, 165 "ProposalResponsePayloads do not match", 166 }, 167 // good response followed by bad response 168 { 169 []*pb.ProposalResponse{ 170 {Payload: []byte("payload"), Response: &pb.Response{Status: int32(200)}}, 171 {Payload: []byte{}, Response: &pb.Response{Status: int32(500), Message: "failed to endorse"}}, 172 }, 173 "proposal response was not successful, error code 500, msg failed to endorse", 174 }, 175 // bad response followed by good response 176 { 177 []*pb.ProposalResponse{ 178 {Payload: []byte{}, Response: &pb.Response{Status: int32(500), Message: "failed to endorse"}}, 179 {Payload: []byte("payload"), Response: &pb.Response{Status: int32(200)}}, 180 }, 181 "proposal response was not successful, error code 500, msg failed to endorse", 182 }, 183 } 184 for i, nonMatchingTest := range nonMatchingTests { 185 _, err = protoutil.CreateSignedTx(prop, signID, nonMatchingTest.responses...) 186 assert.EqualErrorf(t, err, nonMatchingTest.expectedError, "Expected non-matching response error '%v' for test %d", nonMatchingTest.expectedError, i) 187 } 188 189 // no endorsement 190 responses = []*pb.ProposalResponse{{ 191 Payload: []byte("payload"), 192 Response: &pb.Response{ 193 Status: int32(200), 194 }, 195 }} 196 _, err = protoutil.CreateSignedTx(prop, signID, responses...) 197 assert.Error(t, err, "Expected error with no endorsements") 198 199 // success 200 responses = []*pb.ProposalResponse{{ 201 Payload: []byte("payload"), 202 Endorsement: &pb.Endorsement{}, 203 Response: &pb.Response{ 204 Status: int32(200), 205 }, 206 }} 207 _, err = protoutil.CreateSignedTx(prop, signID, responses...) 208 assert.NoError(t, err, "Unexpected error creating signed transaction") 209 t.Logf("error: [%s]", err) 210 211 // 212 // 213 // additional failure cases 214 prop = &pb.Proposal{} 215 responses = []*pb.ProposalResponse{} 216 // no proposal responses 217 _, err = protoutil.CreateSignedTx(prop, signID, responses...) 218 assert.Error(t, err, "Expected error with no proposal responses") 219 220 // missing proposal header 221 responses = append(responses, &pb.ProposalResponse{}) 222 _, err = protoutil.CreateSignedTx(prop, signID, responses...) 223 assert.Error(t, err, "Expected error with no proposal header") 224 225 // bad proposal payload 226 prop.Payload = []byte("bad payload") 227 _, err = protoutil.CreateSignedTx(prop, signID, responses...) 228 assert.Error(t, err, "Expected error with malformed proposal payload") 229 230 // bad payload header 231 prop.Header = []byte("bad header") 232 _, err = protoutil.CreateSignedTx(prop, signID, responses...) 233 assert.Error(t, err, "Expected error with malformed proposal header") 234 235 } 236 237 func TestCreateSignedTxStatus(t *testing.T) { 238 serializedExtension, err := proto.Marshal(&pb.ChaincodeHeaderExtension{}) 239 assert.NoError(t, err) 240 serializedChannelHeader, err := proto.Marshal(&cb.ChannelHeader{ 241 Extension: serializedExtension, 242 }) 243 assert.NoError(t, err) 244 245 signingID := &fakes.SignerSerializer{} 246 signingID.SerializeReturns([]byte("signer"), nil) 247 serializedSigningID, err := signingID.Serialize() 248 assert.NoError(t, err) 249 serializedSignatureHeader, err := proto.Marshal(&cb.SignatureHeader{ 250 Creator: serializedSigningID, 251 }) 252 assert.NoError(t, err) 253 254 header := &cb.Header{ 255 ChannelHeader: serializedChannelHeader, 256 SignatureHeader: serializedSignatureHeader, 257 } 258 259 serializedHeader, err := proto.Marshal(header) 260 assert.NoError(t, err) 261 262 proposal := &pb.Proposal{ 263 Header: serializedHeader, 264 } 265 266 tests := []struct { 267 status int32 268 expectedErr string 269 }{ 270 {status: 0, expectedErr: "proposal response was not successful, error code 0, msg response-message"}, 271 {status: 199, expectedErr: "proposal response was not successful, error code 199, msg response-message"}, 272 {status: 200, expectedErr: ""}, 273 {status: 201, expectedErr: ""}, 274 {status: 399, expectedErr: ""}, 275 {status: 400, expectedErr: "proposal response was not successful, error code 400, msg response-message"}, 276 } 277 for _, tc := range tests { 278 t.Run(strconv.Itoa(int(tc.status)), func(t *testing.T) { 279 response := &pb.ProposalResponse{ 280 Payload: []byte("payload"), 281 Endorsement: &pb.Endorsement{}, 282 Response: &pb.Response{ 283 Status: tc.status, 284 Message: "response-message", 285 }, 286 } 287 288 _, err := protoutil.CreateSignedTx(proposal, signingID, response) 289 if tc.expectedErr == "" { 290 assert.NoError(t, err) 291 } else { 292 assert.EqualError(t, err, tc.expectedErr) 293 } 294 }) 295 } 296 } 297 298 func TestCreateSignedEnvelope(t *testing.T) { 299 var env *cb.Envelope 300 channelID := "mychannelID" 301 msg := &cb.ConfigEnvelope{} 302 303 id := &fakes.SignerSerializer{} 304 id.SignReturnsOnCall(0, []byte("goodsig"), nil) 305 id.SignReturnsOnCall(1, nil, errors.New("bad signature")) 306 env, err := protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG, channelID, 307 id, msg, int32(1), uint64(1)) 308 assert.NoError(t, err, "Unexpected error creating signed envelope") 309 assert.NotNil(t, env, "Envelope should not be nil") 310 // mock sign returns the bytes to be signed 311 assert.Equal(t, []byte("goodsig"), env.Signature, "Unexpected signature returned") 312 payload := &cb.Payload{} 313 err = proto.Unmarshal(env.Payload, payload) 314 assert.NoError(t, err, "Failed to unmarshal payload") 315 data := &cb.ConfigEnvelope{} 316 err = proto.Unmarshal(payload.Data, data) 317 assert.NoError(t, err, "Expected payload data to be a config envelope") 318 assert.Equal(t, msg, data, "Payload data does not match expected value") 319 320 _, err = protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG, channelID, 321 id, &cb.ConfigEnvelope{}, int32(1), uint64(1)) 322 assert.Error(t, err, "Expected sign error") 323 } 324 325 func TestCreateSignedEnvelopeNilSigner(t *testing.T) { 326 var env *cb.Envelope 327 channelID := "mychannelID" 328 msg := &cb.ConfigEnvelope{} 329 330 env, err := protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG, channelID, 331 nil, msg, int32(1), uint64(1)) 332 assert.NoError(t, err, "Unexpected error creating signed envelope") 333 assert.NotNil(t, env, "Envelope should not be nil") 334 assert.Empty(t, env.Signature, "Signature should have been empty") 335 payload := &cb.Payload{} 336 err = proto.Unmarshal(env.Payload, payload) 337 assert.NoError(t, err, "Failed to unmarshal payload") 338 data := &cb.ConfigEnvelope{} 339 err = proto.Unmarshal(payload.Data, data) 340 assert.NoError(t, err, "Expected payload data to be a config envelope") 341 assert.Equal(t, msg, data, "Payload data does not match expected value") 342 } 343 344 func TestGetSignedProposal(t *testing.T) { 345 var signedProp *pb.SignedProposal 346 var err error 347 348 sig := []byte("signature") 349 350 signID := &fakes.SignerSerializer{} 351 signID.SignReturns(sig, nil) 352 353 prop := &pb.Proposal{} 354 propBytes, _ := proto.Marshal(prop) 355 signedProp, err = protoutil.GetSignedProposal(prop, signID) 356 assert.NoError(t, err, "Unexpected error getting signed proposal") 357 assert.Equal(t, propBytes, signedProp.ProposalBytes, 358 "Proposal bytes did not match expected value") 359 assert.Equal(t, sig, signedProp.Signature, 360 "Signature did not match expected value") 361 362 _, err = protoutil.GetSignedProposal(nil, signID) 363 assert.Error(t, err, "Expected error with nil proposal") 364 _, err = protoutil.GetSignedProposal(prop, nil) 365 assert.Error(t, err, "Expected error with nil signing identity") 366 367 } 368 369 func TestMockSignedEndorserProposalOrPanic(t *testing.T) { 370 var prop *pb.Proposal 371 var signedProp *pb.SignedProposal 372 373 ccProposal := &pb.ChaincodeProposalPayload{} 374 cis := &pb.ChaincodeInvocationSpec{} 375 chainID := "testchannelid" 376 sig := []byte("signature") 377 creator := []byte("creator") 378 cs := &pb.ChaincodeSpec{ 379 ChaincodeId: &pb.ChaincodeID{ 380 Name: "mychaincode", 381 }, 382 } 383 384 signedProp, prop = protoutil.MockSignedEndorserProposalOrPanic(chainID, cs, 385 creator, sig) 386 assert.Equal(t, sig, signedProp.Signature, 387 "Signature did not match expected result") 388 propBytes, _ := proto.Marshal(prop) 389 assert.Equal(t, propBytes, signedProp.ProposalBytes, 390 "Proposal bytes do not match expected value") 391 err := proto.Unmarshal(prop.Payload, ccProposal) 392 assert.NoError(t, err, "Expected ChaincodeProposalPayload") 393 err = proto.Unmarshal(ccProposal.Input, cis) 394 assert.NoError(t, err, "Expected ChaincodeInvocationSpec") 395 assert.Equal(t, cs.ChaincodeId.Name, cis.ChaincodeSpec.ChaincodeId.Name, 396 "Chaincode name did not match expected value") 397 } 398 399 func TestMockSignedEndorserProposal2OrPanic(t *testing.T) { 400 var prop *pb.Proposal 401 var signedProp *pb.SignedProposal 402 403 ccProposal := &pb.ChaincodeProposalPayload{} 404 cis := &pb.ChaincodeInvocationSpec{} 405 chainID := "testchannelid" 406 sig := []byte("signature") 407 signID := &fakes.SignerSerializer{} 408 signID.SignReturns(sig, nil) 409 410 signedProp, prop = protoutil.MockSignedEndorserProposal2OrPanic(chainID, 411 &pb.ChaincodeSpec{}, signID) 412 assert.Equal(t, sig, signedProp.Signature, 413 "Signature did not match expected result") 414 propBytes, _ := proto.Marshal(prop) 415 assert.Equal(t, propBytes, signedProp.ProposalBytes, 416 "Proposal bytes do not match expected value") 417 err := proto.Unmarshal(prop.Payload, ccProposal) 418 assert.NoError(t, err, "Expected ChaincodeProposalPayload") 419 err = proto.Unmarshal(ccProposal.Input, cis) 420 assert.NoError(t, err, "Expected ChaincodeInvocationSpec") 421 } 422 423 func TestGetBytesProposalPayloadForTx(t *testing.T) { 424 input := &pb.ChaincodeProposalPayload{ 425 Input: []byte("input"), 426 TransientMap: make(map[string][]byte), 427 } 428 expected, _ := proto.Marshal(&pb.ChaincodeProposalPayload{ 429 Input: []byte("input"), 430 }) 431 432 result, err := protoutil.GetBytesProposalPayloadForTx(input) 433 assert.NoError(t, err, "Unexpected error getting proposal payload") 434 assert.Equal(t, expected, result, "Payload does not match expected value") 435 436 _, err = protoutil.GetBytesProposalPayloadForTx(nil) 437 assert.Error(t, err, "Expected error with nil proposal payload") 438 } 439 440 func TestGetProposalHash2(t *testing.T) { 441 expectedHashHex := "7b622ef4e1ab9b7093ec3bbfbca17d5d6f14a437914a6839319978a7034f7960" 442 expectedHash, _ := hex.DecodeString(expectedHashHex) 443 hdr := &cb.Header{ 444 ChannelHeader: []byte("chdr"), 445 SignatureHeader: []byte("shdr"), 446 } 447 propHash, err := protoutil.GetProposalHash2(hdr, []byte("ccproppayload")) 448 assert.NoError(t, err, "Unexpected error getting hash2 for proposal") 449 assert.Equal(t, expectedHash, propHash, "Proposal hash did not match expected hash") 450 451 _, err = protoutil.GetProposalHash2(&cb.Header{}, []byte("ccproppayload")) 452 assert.Error(t, err, "Expected error with nil arguments") 453 } 454 455 func TestGetProposalHash1(t *testing.T) { 456 expectedHashHex := "d4c1e3cac2105da5fddc2cfe776d6ec28e4598cf1e6fa51122c7f70d8076437b" 457 expectedHash, _ := hex.DecodeString(expectedHashHex) 458 hdr := &cb.Header{ 459 ChannelHeader: []byte("chdr"), 460 SignatureHeader: []byte("shdr"), 461 } 462 463 ccProposal, _ := proto.Marshal(&pb.ChaincodeProposalPayload{}) 464 465 propHash, err := protoutil.GetProposalHash1(hdr, ccProposal) 466 assert.NoError(t, err, "Unexpected error getting hash for proposal") 467 assert.Equal(t, expectedHash, propHash, "Proposal hash did not match expected hash") 468 469 _, err = protoutil.GetProposalHash1(hdr, []byte("ccproppayload")) 470 assert.Error(t, err, "Expected error with malformed chaincode proposal payload") 471 472 _, err = protoutil.GetProposalHash1(&cb.Header{}, []byte("ccproppayload")) 473 assert.Error(t, err, "Expected error with nil arguments") 474 } 475 476 func TestCreateProposalResponseFailure(t *testing.T) { 477 // create a proposal from a ChaincodeInvocationSpec 478 prop, _, err := protoutil.CreateChaincodeProposal(cb.HeaderType_ENDORSER_TRANSACTION, testChannelID, createCIS(), signerSerialized) 479 if err != nil { 480 t.Fatalf("Could not create chaincode proposal, err %s\n", err) 481 return 482 } 483 484 response := &pb.Response{Status: 502, Payload: []byte("Invalid function name")} 485 result := []byte("res") 486 487 prespFailure, err := protoutil.CreateProposalResponseFailure(prop.Header, prop.Payload, response, result, nil, "foo") 488 if err != nil { 489 t.Fatalf("Could not create proposal response failure, err %s\n", err) 490 return 491 } 492 493 assert.Equal(t, int32(502), prespFailure.Response.Status) 494 // drilldown into the response to find the chaincode response 495 pRespPayload, err := protoutil.UnmarshalProposalResponsePayload(prespFailure.Payload) 496 assert.NoError(t, err, "Error while unmarshaling proposal response payload: %s", err) 497 ca, err := protoutil.UnmarshalChaincodeAction(pRespPayload.Extension) 498 assert.NoError(t, err, "Error while unmarshaling chaincode action: %s", err) 499 500 assert.Equal(t, int32(502), ca.Response.Status) 501 assert.Equal(t, "Invalid function name", string(ca.Response.Payload)) 502 } 503 504 func TestGetorComputeTxIDFromEnvelope(t *testing.T) { 505 t.Run("txID is present in the envelope", func(t *testing.T) { 506 txID := "709184f9d24f6ade8fcd4d6521a6eef295fef6c2e67216c58b68ac15e8946492" 507 envelopeBytes := createSampleTxEnvelopeBytes(txID) 508 actualTxID, err := protoutil.GetOrComputeTxIDFromEnvelope(envelopeBytes) 509 assert.Nil(t, err) 510 assert.Equal(t, "709184f9d24f6ade8fcd4d6521a6eef295fef6c2e67216c58b68ac15e8946492", actualTxID) 511 }) 512 513 t.Run("txID is not present in the envelope", func(t *testing.T) { 514 txID := "" 515 envelopeBytes := createSampleTxEnvelopeBytes(txID) 516 actualTxID, err := protoutil.GetOrComputeTxIDFromEnvelope(envelopeBytes) 517 assert.Nil(t, err) 518 assert.Equal(t, "709184f9d24f6ade8fcd4d6521a6eef295fef6c2e67216c58b68ac15e8946492", actualTxID) 519 520 }) 521 } 522 523 func createSampleTxEnvelopeBytes(txID string) []byte { 524 chdr := &cb.ChannelHeader{ 525 TxId: "709184f9d24f6ade8fcd4d6521a6eef295fef6c2e67216c58b68ac15e8946492", 526 } 527 chdrBytes := protoutil.MarshalOrPanic(chdr) 528 529 shdr := &cb.SignatureHeader{ 530 Nonce: []byte("nonce"), 531 Creator: []byte("creator"), 532 } 533 shdrBytes := protoutil.MarshalOrPanic(shdr) 534 535 hdr := &cb.Header{ 536 ChannelHeader: chdrBytes, 537 SignatureHeader: shdrBytes, 538 } 539 540 payload := &cb.Payload{ 541 Header: hdr, 542 } 543 payloadBytes := protoutil.MarshalOrPanic(payload) 544 545 envelope := &cb.Envelope{ 546 Payload: payloadBytes, 547 } 548 return protoutil.MarshalOrPanic(envelope) 549 550 }