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