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  }