diff --git a/x/tx/signing/textual/duration_test.go b/x/tx/signing/textual/duration_test.go index 63b9463a56..baf015e5e3 100644 --- a/x/tx/signing/textual/duration_test.go +++ b/x/tx/signing/textual/duration_test.go @@ -10,7 +10,6 @@ import ( "cosmossdk.io/x/tx/signing/textual" "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" dpb "google.golang.org/protobuf/types/known/durationpb" ) @@ -54,7 +53,7 @@ func TestDurationJSON(t *testing.T) { msg := val.Message().Interface() require.IsType(t, &dpb.Duration{}, msg) duration := msg.(*dpb.Duration) - require.True(t, proto.Equal(duration, tc.Proto), "%v vs %v", duration, tc.Proto) + require.Equal(t, duration.AsDuration(), tc.Proto.AsDuration(), "%v vs %v", duration, tc.Proto) }) } } diff --git a/x/tx/signing/textual/fuzz_test.go b/x/tx/signing/textual/fuzz_test.go index fa9e466414..c2e0908a44 100644 --- a/x/tx/signing/textual/fuzz_test.go +++ b/x/tx/signing/textual/fuzz_test.go @@ -2,8 +2,13 @@ package textual_test import ( "context" + "encoding/json" + "os" "testing" + "google.golang.org/protobuf/reflect/protoreflect" + tspb "google.golang.org/protobuf/types/known/timestamppb" + "cosmossdk.io/x/tx/signing/textual" ) @@ -28,3 +33,71 @@ func FuzzIntValueRendererParse(f *testing.F) { _, _ = ivr.Parse(ctx, []textual.Screen{{Content: input}}) }) } + +func FuzzTimestampValueRendererParse(f *testing.F) { + if testing.Short() { + f.Skip() + } + + // 1. Firstly add some seed valid content. + f.Add("2006-01-02T15:04:05Z") + f.Add("1970-01-01T00:00:00.00000001Z") + f.Add("2022-07-14T11:22:20.983Z") + f.Add("1969-12-31T23:59:59Z") + + // 2. Now fuzz it. + tvr := textual.NewTimestampValueRenderer() + ctx := context.Background() + f.Fuzz(func(t *testing.T, input string) { + _, _ = tvr.Parse(ctx, []textual.Screen{{Content: input}}) + }) +} + +func FuzzTimestampJSONParseToParseRoundTrip(f *testing.F) { + // 1. Use the seeds from testdata and mutate them. + seed, err := os.ReadFile("./internal/testdata/timestamp.json") + if err != nil { + f.Fatal(err) + } + f.Add(seed) + + f.Fuzz(func(t *testing.T, input []byte) { + var testCases []timestampJsonTest + if err := json.Unmarshal(input, &testCases); err != nil { + return + } + + for _, tc := range testCases { + rend := textual.NewTimestampValueRenderer() + + // If it successfully JSON unmarshals let's test it out. + var screens []textual.Screen + var err error + + if tc.Proto != nil { + screens, err = rend.Format(context.Background(), protoreflect.ValueOf(tc.Proto.ProtoReflect())) + if err != nil { + continue + } + } + + val, err := rend.Parse(context.Background(), screens) + if err != nil { + continue + } + + msg := val.Message().Interface() + gotTs, ok := msg.(*tspb.Timestamp) + if !ok { + t.Fatalf("Wrong type for timestamp: %T", msg) + } + // Please avoid using proto.Equal to compare timestamps given they aren't + // in standardized form and will produce false positives for example given input: + // []byte(`[{"proto":{"nanos":1000000000}}]`) + // Per issue: https://github.com/cosmos/cosmos-sdk/issues/15761 + if !gotTs.AsTime().Equal(tc.Proto.AsTime()) { + t.Fatalf("Roundtrip mismatch\n\tGot: %#v\n\tWant: %#v", gotTs, tc.Proto) + } + } + }) +} diff --git a/x/tx/signing/textual/testdata/fuzz/FuzzIntValueRendererParse/5838cdfae7b16cde b/x/tx/signing/textual/testdata/fuzz/FuzzIntValueRendererParse/5838cdfae7b16cde new file mode 100644 index 0000000000..64c3abaff8 --- /dev/null +++ b/x/tx/signing/textual/testdata/fuzz/FuzzIntValueRendererParse/5838cdfae7b16cde @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/x/tx/signing/textual/testdata/fuzz/FuzzIntValueRendererParse/e521654378d1371f b/x/tx/signing/textual/testdata/fuzz/FuzzIntValueRendererParse/e521654378d1371f new file mode 100644 index 0000000000..9272cbf954 --- /dev/null +++ b/x/tx/signing/textual/testdata/fuzz/FuzzIntValueRendererParse/e521654378d1371f @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\xb0\x1b\x16d@L0@'") diff --git a/x/tx/signing/textual/testdata/fuzz/FuzzTimestampJSON/7fee543ff80f1279 b/x/tx/signing/textual/testdata/fuzz/FuzzTimestampJSON/7fee543ff80f1279 new file mode 100644 index 0000000000..7a94f035b2 --- /dev/null +++ b/x/tx/signing/textual/testdata/fuzz/FuzzTimestampJSON/7fee543ff80f1279 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("[{\"proto\":{\"nAnos\":1000000000}}]") diff --git a/x/tx/signing/textual/timestamp_test.go b/x/tx/signing/textual/timestamp_test.go index c700188dcc..c94448b433 100644 --- a/x/tx/signing/textual/timestamp_test.go +++ b/x/tx/signing/textual/timestamp_test.go @@ -11,7 +11,6 @@ import ( "cosmossdk.io/x/tx/signing/textual" "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" dur "google.golang.org/protobuf/types/known/durationpb" tspb "google.golang.org/protobuf/types/known/timestamppb" @@ -31,12 +30,21 @@ type timestampJsonTest struct { Text string } -func TestTimestampJsonTestcases(t *testing.T) { +func TestTimestampJsonTestcasesTestData(t *testing.T) { raw, err := os.ReadFile("./internal/testdata/timestamp.json") require.NoError(t, err) + testTimestampJsonTestcases(t, raw) +} +// Tests to ensure that we compare standardized forms of the final timestamppb.Timestamp. +// Please see issue https://github.com/cosmos/cosmos-sdk/issues/15761 +func TestTimestampJsonTestcasesExtraneousNanos(t *testing.T) { + testTimestampJsonTestcases(t, []byte(`[{"proto":{"nAnos":1000000000},"text":"1970-01-01T00:00:01Z"}]`)) +} + +func testTimestampJsonTestcases(t *testing.T, raw []byte) { var testcases []timestampJsonTest - err = json.Unmarshal(raw, &testcases) + err := json.Unmarshal(raw, &testcases) require.NoError(t, err) for i, tc := range testcases { @@ -64,7 +72,11 @@ func TestTimestampJsonTestcases(t *testing.T) { msg := val.Message().Interface() require.IsType(t, &tspb.Timestamp{}, msg) timestamp := msg.(*tspb.Timestamp) - require.True(t, proto.Equal(timestamp, tc.Proto)) + // Please avoid using proto.Equal to compare timestamps given they aren't + // in standardized form and will produce false positives for example given input: + // []byte(`[{"proto":{"nanos":1000000000}}]`) + // Per issue: https://github.com/cosmos/cosmos-sdk/issues/15761 + require.True(t, timestamp.AsTime().Equal(tc.Proto.AsTime())) }) } }