github.com/Finschia/finschia-sdk@v0.48.1/codec/unknownproto/unknown_fields_test.go (about) 1 package unknownproto 2 3 import ( 4 "reflect" 5 "testing" 6 7 "github.com/stretchr/testify/require" 8 9 "github.com/gogo/protobuf/proto" 10 11 "github.com/Finschia/finschia-sdk/codec/types" 12 "github.com/Finschia/finschia-sdk/testutil/testdata" 13 ) 14 15 func TestRejectUnknownFieldsRepeated(t *testing.T) { 16 tests := []struct { 17 name string 18 in proto.Message 19 recv proto.Message 20 wantErr error 21 allowUnknownNonCriticals bool 22 hasUnknownNonCriticals bool 23 }{ 24 { 25 name: "Unknown field in midst of repeated values", 26 in: &testdata.TestVersion2{ 27 C: []*testdata.TestVersion2{ 28 { 29 C: []*testdata.TestVersion2{ 30 { 31 Sum: &testdata.TestVersion2_F{ 32 F: &testdata.TestVersion2{ 33 A: &testdata.TestVersion2{ 34 B: &testdata.TestVersion2{ 35 H: []*testdata.TestVersion1{ 36 { 37 X: 0x01, 38 }, 39 }, 40 }, 41 }, 42 }, 43 }, 44 }, 45 { 46 Sum: &testdata.TestVersion2_F{ 47 F: &testdata.TestVersion2{ 48 A: &testdata.TestVersion2{ 49 B: &testdata.TestVersion2{ 50 H: []*testdata.TestVersion1{ 51 { 52 X: 0x02, 53 }, 54 }, 55 }, 56 }, 57 }, 58 }, 59 }, 60 { 61 Sum: &testdata.TestVersion2_F{ 62 F: &testdata.TestVersion2{ 63 NewField: 411, 64 }, 65 }, 66 }, 67 }, 68 }, 69 }, 70 }, 71 recv: new(testdata.TestVersion1), 72 wantErr: &errUnknownField{ 73 Type: "*testdata.TestVersion1", 74 TagNum: 25, 75 WireType: 0, 76 }, 77 }, 78 { 79 name: "Unknown field in midst of repeated values, allowUnknownNonCriticals set", 80 allowUnknownNonCriticals: true, 81 in: &testdata.TestVersion2{ 82 C: []*testdata.TestVersion2{ 83 { 84 C: []*testdata.TestVersion2{ 85 { 86 Sum: &testdata.TestVersion2_F{ 87 F: &testdata.TestVersion2{ 88 A: &testdata.TestVersion2{ 89 B: &testdata.TestVersion2{ 90 H: []*testdata.TestVersion1{ 91 { 92 X: 0x01, 93 }, 94 }, 95 }, 96 }, 97 }, 98 }, 99 }, 100 { 101 Sum: &testdata.TestVersion2_F{ 102 F: &testdata.TestVersion2{ 103 A: &testdata.TestVersion2{ 104 B: &testdata.TestVersion2{ 105 H: []*testdata.TestVersion1{ 106 { 107 X: 0x02, 108 }, 109 }, 110 }, 111 }, 112 }, 113 }, 114 }, 115 { 116 Sum: &testdata.TestVersion2_F{ 117 F: &testdata.TestVersion2{ 118 NewField: 411, 119 }, 120 }, 121 }, 122 }, 123 }, 124 }, 125 }, 126 recv: new(testdata.TestVersion1), 127 wantErr: &errUnknownField{ 128 Type: "*testdata.TestVersion1", 129 TagNum: 25, 130 WireType: 0, 131 }, 132 }, 133 { 134 name: "Unknown field in midst of repeated values, non-critical field to be rejected", 135 in: &testdata.TestVersion3{ 136 C: []*testdata.TestVersion3{ 137 { 138 C: []*testdata.TestVersion3{ 139 { 140 Sum: &testdata.TestVersion3_F{ 141 F: &testdata.TestVersion3{ 142 A: &testdata.TestVersion3{ 143 B: &testdata.TestVersion3{ 144 X: 0x01, 145 }, 146 }, 147 }, 148 }, 149 }, 150 { 151 Sum: &testdata.TestVersion3_F{ 152 F: &testdata.TestVersion3{ 153 A: &testdata.TestVersion3{ 154 B: &testdata.TestVersion3{ 155 X: 0x02, 156 }, 157 }, 158 }, 159 }, 160 }, 161 { 162 Sum: &testdata.TestVersion3_F{ 163 F: &testdata.TestVersion3{ 164 NonCriticalField: "non-critical", 165 }, 166 }, 167 }, 168 }, 169 }, 170 }, 171 }, 172 recv: new(testdata.TestVersion1), 173 wantErr: &errUnknownField{ 174 Type: "*testdata.TestVersion1", 175 TagNum: 1031, 176 WireType: 2, 177 }, 178 hasUnknownNonCriticals: true, 179 }, 180 { 181 name: "Unknown field in midst of repeated values, non-critical field ignored", 182 allowUnknownNonCriticals: true, 183 in: &testdata.TestVersion3{ 184 C: []*testdata.TestVersion3{ 185 { 186 C: []*testdata.TestVersion3{ 187 { 188 Sum: &testdata.TestVersion3_F{ 189 F: &testdata.TestVersion3{ 190 A: &testdata.TestVersion3{ 191 B: &testdata.TestVersion3{ 192 X: 0x01, 193 }, 194 }, 195 }, 196 }, 197 }, 198 { 199 Sum: &testdata.TestVersion3_F{ 200 F: &testdata.TestVersion3{ 201 A: &testdata.TestVersion3{ 202 B: &testdata.TestVersion3{ 203 X: 0x02, 204 }, 205 }, 206 }, 207 }, 208 }, 209 { 210 Sum: &testdata.TestVersion3_F{ 211 F: &testdata.TestVersion3{ 212 NonCriticalField: "non-critical", 213 }, 214 }, 215 }, 216 }, 217 }, 218 }, 219 }, 220 recv: new(testdata.TestVersion1), 221 wantErr: nil, 222 hasUnknownNonCriticals: true, 223 }, 224 } 225 226 for _, tt := range tests { 227 tt := tt 228 t.Run(tt.name, func(t *testing.T) { 229 protoBlob, err := proto.Marshal(tt.in) 230 if err != nil { 231 t.Fatal(err) 232 } 233 hasUnknownNonCriticals, gotErr := RejectUnknownFields(protoBlob, tt.recv, tt.allowUnknownNonCriticals, DefaultAnyResolver{}) 234 require.Equal(t, tt.wantErr, gotErr) 235 require.Equal(t, tt.hasUnknownNonCriticals, hasUnknownNonCriticals) 236 }) 237 } 238 } 239 240 func TestRejectUnknownFields_allowUnknownNonCriticals(t *testing.T) { 241 tests := []struct { 242 name string 243 in proto.Message 244 allowUnknownNonCriticals bool 245 wantErr error 246 }{ 247 { 248 name: "Field that's in the reserved range, should fail by default", 249 in: &testdata.Customer2{ 250 Id: 289, 251 Reserved: 99, 252 }, 253 wantErr: &errUnknownField{ 254 Type: "*testdata.Customer1", 255 TagNum: 1047, 256 WireType: 0, 257 }, 258 }, 259 { 260 name: "Field that's in the reserved range, toggle allowUnknownNonCriticals", 261 allowUnknownNonCriticals: true, 262 in: &testdata.Customer2{ 263 Id: 289, 264 Reserved: 99, 265 }, 266 wantErr: nil, 267 }, 268 { 269 name: "Unknown fields that are critical, but with allowUnknownNonCriticals set", 270 allowUnknownNonCriticals: true, 271 in: &testdata.Customer2{ 272 Id: 289, 273 City: testdata.Customer2_PaloAlto, 274 }, 275 wantErr: &errUnknownField{ 276 Type: "*testdata.Customer1", 277 TagNum: 6, 278 WireType: 0, 279 }, 280 }, 281 } 282 283 for _, tt := range tests { 284 tt := tt 285 t.Run(tt.name, func(t *testing.T) { 286 blob, err := proto.Marshal(tt.in) 287 if err != nil { 288 t.Fatalf("Failed to marshal input: %v", err) 289 } 290 291 c1 := new(testdata.Customer1) 292 _, gotErr := RejectUnknownFields(blob, c1, tt.allowUnknownNonCriticals, DefaultAnyResolver{}) 293 if !reflect.DeepEqual(gotErr, tt.wantErr) { 294 t.Fatalf("Error mismatch\nGot:\n%s\n\nWant:\n%s", gotErr, tt.wantErr) 295 } 296 }) 297 } 298 } 299 300 func TestRejectUnknownFieldsNested(t *testing.T) { 301 tests := []struct { 302 name string 303 in proto.Message 304 recv proto.Message 305 wantErr error 306 }{ 307 { 308 name: "TestVersion3 from TestVersionFD1", 309 in: &testdata.TestVersion2{ 310 X: 5, 311 Sum: &testdata.TestVersion2_E{ 312 E: 100, 313 }, 314 H: []*testdata.TestVersion1{ 315 {X: 999}, 316 {X: -55}, 317 { 318 X: 102, 319 Sum: &testdata.TestVersion1_F{ 320 F: &testdata.TestVersion1{ 321 X: 4, 322 }, 323 }, 324 }, 325 }, 326 Customer1: &testdata.Customer1{ 327 Id: 45, 328 Name: "customer1", 329 SubscriptionFee: 99, 330 }, 331 }, 332 recv: new(testdata.TestVersionFD1), 333 wantErr: &errUnknownField{ 334 Type: "*testdata.TestVersionFD1", 335 TagNum: 12, 336 WireType: 2, 337 }, 338 }, 339 { 340 name: "Alternating oneofs", 341 in: &testdata.TestVersion3{ 342 Sum: &testdata.TestVersion3_E{ 343 E: 99, 344 }, 345 }, 346 recv: new(testdata.TestVersion3LoneOneOfValue), 347 wantErr: nil, 348 }, 349 { 350 name: "Alternating oneofs mismatched field", 351 in: &testdata.TestVersion3{ 352 Sum: &testdata.TestVersion3_F{ 353 F: &testdata.TestVersion3{ 354 X: 99, 355 }, 356 }, 357 }, 358 recv: new(testdata.TestVersion3LoneOneOfValue), 359 wantErr: &errUnknownField{ 360 Type: "*testdata.TestVersion3LoneOneOfValue", 361 TagNum: 7, 362 WireType: 2, 363 }, 364 }, 365 { 366 name: "Discrepancy in a deeply nested one of field", 367 in: &testdata.TestVersion3{ 368 Sum: &testdata.TestVersion3_F{ 369 F: &testdata.TestVersion3{ 370 Sum: &testdata.TestVersion3_F{ 371 F: &testdata.TestVersion3{ 372 X: 19, 373 Sum: &testdata.TestVersion3_E{ 374 E: 99, 375 }, 376 }, 377 }, 378 }, 379 }, 380 }, 381 recv: new(testdata.TestVersion3LoneNesting), 382 wantErr: &errUnknownField{ 383 Type: "*testdata.TestVersion3LoneNesting", 384 TagNum: 6, 385 WireType: 0, 386 }, 387 }, 388 { 389 name: "unknown field types.Any in G", 390 in: &testdata.TestVersion3{ 391 G: &types.Any{ 392 TypeUrl: "/testdata.TestVersion1", 393 Value: mustMarshal(&testdata.TestVersion2{ 394 Sum: &testdata.TestVersion2_F{ 395 F: &testdata.TestVersion2{ 396 NewField: 999, 397 }, 398 }, 399 }), 400 }, 401 }, 402 recv: new(testdata.TestVersion3), 403 wantErr: &errUnknownField{ 404 Type: "*testdata.TestVersion1", 405 TagNum: 25, 406 }, 407 }, 408 { 409 name: "types.Any with extra fields", 410 in: &testdata.TestVersionFD1WithExtraAny{ 411 G: &testdata.AnyWithExtra{ 412 Any: &types.Any{ 413 TypeUrl: "/testdata.TestVersion1", 414 Value: mustMarshal(&testdata.TestVersion2{ 415 Sum: &testdata.TestVersion2_F{ 416 F: &testdata.TestVersion2{ 417 NewField: 999, 418 }, 419 }, 420 }), 421 }, 422 B: 3, 423 C: 2, 424 }, 425 }, 426 recv: new(testdata.TestVersion3), 427 wantErr: &errUnknownField{ 428 Type: "*types.Any", 429 TagNum: 3, 430 WireType: 0, 431 }, 432 }, 433 { 434 name: "mismatched types.Any in G", 435 in: &testdata.TestVersion1{ 436 G: &types.Any{ 437 TypeUrl: "/testdata.TestVersion4LoneNesting", 438 Value: mustMarshal(&testdata.TestVersion3LoneNesting_Inner1{ 439 Inner: &testdata.TestVersion3LoneNesting_Inner1_InnerInner{ 440 Id: "ID", 441 City: "Gotham", 442 }, 443 }), 444 }, 445 }, 446 recv: new(testdata.TestVersion1), 447 wantErr: &errMismatchedWireType{ 448 Type: "*testdata.TestVersion3", 449 TagNum: 8, 450 GotWireType: 7, 451 WantWireType: 2, 452 }, 453 }, 454 { 455 name: "From nested proto message, message index 0", 456 in: &testdata.TestVersion3LoneNesting{ 457 Inner1: &testdata.TestVersion3LoneNesting_Inner1{ 458 Id: 10, 459 Name: "foo", 460 Inner: &testdata.TestVersion3LoneNesting_Inner1_InnerInner{ 461 Id: "ID", 462 City: "Palo Alto", 463 }, 464 }, 465 }, 466 recv: new(testdata.TestVersion4LoneNesting), 467 wantErr: nil, 468 }, 469 { 470 name: "From nested proto message, message index 1", 471 in: &testdata.TestVersion3LoneNesting{ 472 Inner2: &testdata.TestVersion3LoneNesting_Inner2{ 473 Id: "ID", 474 Country: "Maldives", 475 Inner: &testdata.TestVersion3LoneNesting_Inner2_InnerInner{ 476 Id: "ID", 477 City: "Unknown", 478 }, 479 }, 480 }, 481 recv: new(testdata.TestVersion4LoneNesting), 482 wantErr: nil, 483 }, 484 } 485 486 for _, tt := range tests { 487 tt := tt 488 t.Run(tt.name, func(t *testing.T) { 489 protoBlob, err := proto.Marshal(tt.in) 490 if err != nil { 491 t.Fatal(err) 492 } 493 gotErr := RejectUnknownFieldsStrict(protoBlob, tt.recv, DefaultAnyResolver{}) 494 if !reflect.DeepEqual(gotErr, tt.wantErr) { 495 t.Fatalf("Error mismatch\nGot:\n%s\n\nWant:\n%s", gotErr, tt.wantErr) 496 } 497 }) 498 } 499 } 500 501 func TestRejectUnknownFieldsFlat(t *testing.T) { 502 tests := []struct { 503 name string 504 in proto.Message 505 wantErr error 506 }{ 507 { 508 name: "Oneof with same field number, shouldn't complain", 509 in: &testdata.Customer3{ 510 Id: 68, 511 Name: "ACME3", 512 Payment: &testdata.Customer3_CreditCardNo{ 513 CreditCardNo: "123-XXXX-XXX881", 514 }, 515 }, 516 wantErr: nil, 517 }, 518 { 519 name: "Oneof with different field number, should fail", 520 in: &testdata.Customer3{ 521 Id: 68, 522 Name: "ACME3", 523 Payment: &testdata.Customer3_ChequeNo{ 524 ChequeNo: "123XXXXXXX881", 525 }, 526 }, 527 wantErr: &errUnknownField{ 528 Type: "*testdata.Customer1", 529 TagNum: 8, WireType: 2, 530 }, 531 }, 532 { 533 name: "Any in a field, the extra field will be serialized so should fail", 534 in: &testdata.Customer2{ 535 Miscellaneous: &types.Any{}, 536 }, 537 wantErr: &errUnknownField{ 538 Type: "*testdata.Customer1", 539 TagNum: 10, 540 WireType: 2, 541 }, 542 }, 543 { 544 name: "With a nested struct as a field", 545 in: &testdata.Customer3{ 546 Id: 289, 547 Original: &testdata.Customer1{ 548 Id: 991, 549 }, 550 }, 551 wantErr: &errUnknownField{ 552 Type: "*testdata.Customer1", 553 TagNum: 9, 554 WireType: 2, 555 }, 556 }, 557 { 558 name: "An extra field that's non-existent in Customer1", 559 in: &testdata.Customer2{ 560 Id: 289, 561 Name: "Customer1", 562 Industry: 5299, 563 Fewer: 199.9, 564 }, 565 wantErr: &errMismatchedWireType{ 566 Type: "*testdata.Customer1", 567 TagNum: 2, GotWireType: 0, WantWireType: 2, 568 }, 569 }, 570 { 571 name: "Using a field that's in the reserved range, should fail by default", 572 in: &testdata.Customer2{ 573 Id: 289, 574 Reserved: 99, 575 }, 576 wantErr: &errUnknownField{ 577 Type: "*testdata.Customer1", 578 TagNum: 1047, 579 WireType: 0, 580 }, 581 }, 582 { 583 name: "Only fields matching", 584 in: &testdata.Customer2{ 585 Id: 289, 586 Name: "Customer1", 587 }, 588 wantErr: &errMismatchedWireType{ 589 Type: "*testdata.Customer1", 590 TagNum: 3, GotWireType: 2, WantWireType: 5, 591 }, 592 }, 593 { 594 name: "Extra field that's non-existent in Customer1, along with Reserved set", 595 in: &testdata.Customer2{ 596 Id: 289, 597 Name: "Customer1", 598 Industry: 5299, 599 Fewer: 199.9, 600 Reserved: 819, 601 }, 602 wantErr: &errMismatchedWireType{ 603 Type: "*testdata.Customer1", 604 TagNum: 2, GotWireType: 0, WantWireType: 2, 605 }, 606 }, 607 { 608 name: "Using enumerated field", 609 in: &testdata.Customer2{ 610 Id: 289, 611 Name: "Customer1", 612 Industry: 5299, 613 City: testdata.Customer2_PaloAlto, 614 }, 615 wantErr: &errMismatchedWireType{ 616 Type: "*testdata.Customer1", 617 TagNum: 2, 618 GotWireType: 0, WantWireType: 2, 619 }, 620 }, 621 { 622 name: "multiple extraneous fields", 623 in: &testdata.Customer2{ 624 Id: 289, 625 Name: "Customer1", 626 Industry: 5299, 627 City: testdata.Customer2_PaloAlto, 628 Fewer: 45, 629 }, 630 wantErr: &errMismatchedWireType{ 631 TagNum: 2, GotWireType: 0, WantWireType: 2, 632 Type: "*testdata.Customer1", 633 }, 634 }, 635 } 636 637 for _, tt := range tests { 638 tt := tt 639 t.Run(tt.name, func(t *testing.T) { 640 blob, err := proto.Marshal(tt.in) 641 if err != nil { 642 t.Fatalf("Failed to marshal input: %v", err) 643 } 644 645 c1 := new(testdata.Customer1) 646 gotErr := RejectUnknownFieldsStrict(blob, c1, DefaultAnyResolver{}) 647 if !reflect.DeepEqual(gotErr, tt.wantErr) { 648 t.Fatalf("Error mismatch\nGot:\n%s\n\nWant:\n%s", gotErr, tt.wantErr) 649 } 650 }) 651 } 652 } 653 654 // Issue https://github.com/cosmos/cosmos-sdk/issues/7222, we need to ensure that repeated 655 // uint64 are recognized as packed. 656 func TestPackedEncoding(t *testing.T) { 657 data := testdata.TestRepeatedUints{Nums: []uint64{12, 13}} 658 659 marshalled, err := data.Marshal() 660 require.NoError(t, err) 661 662 unmarshalled := &testdata.TestRepeatedUints{} 663 _, err = RejectUnknownFields(marshalled, unmarshalled, false, DefaultAnyResolver{}) 664 require.NoError(t, err) 665 } 666 667 func mustMarshal(msg proto.Message) []byte { 668 blob, err := proto.Marshal(msg) 669 if err != nil { 670 panic(err) 671 } 672 return blob 673 }