diff --git a/math/CHANGELOG.md b/math/CHANGELOG.md index bd38caab0e..b7de93d928 100644 --- a/math/CHANGELOG.md +++ b/math/CHANGELOG.md @@ -36,7 +36,13 @@ Ref: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.j ## [Unreleased] -## [math/v1.5.1](https://github.com/cosmos/cosmos-sdk/releases/tag/math/v1.5.0) - 2025-03-28 +## [math/v1.5.2](https://github.com/cosmos/cosmos-sdk/releases/tag/math/v1.5.2) - 2025-03-31 + +### Features + +* [#24229](https://github.com/cosmos/cosmos-sdk/pull/24229) Add `DecFromLegacyDec` migration function. + +## [math/v1.5.1](https://github.com/cosmos/cosmos-sdk/releases/tag/math/v1.5.1) - 2025-03-28 * [#24185](https://github.com/cosmos/cosmos-sdk/issues/24185) Minor dependency bumps diff --git a/math/dec_migrate.go b/math/dec_migrate.go new file mode 100644 index 0000000000..9a855a4c9c --- /dev/null +++ b/math/dec_migrate.go @@ -0,0 +1,8 @@ +package math + +// DecFromLegacyDec converts a LegacyDec to the Dec type using a string intermediate representation. +// +// This function can be used when migrating LegacyDec types to the Dec type. +func DecFromLegacyDec(legacyDec LegacyDec) (Dec, error) { + return NewDecFromString(legacyDec.String()) +} diff --git a/math/dec_migrate_test.go b/math/dec_migrate_test.go new file mode 100644 index 0000000000..5de3c47707 --- /dev/null +++ b/math/dec_migrate_test.go @@ -0,0 +1,118 @@ +package math_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "cosmossdk.io/math" +) + +// TestDecFromLegacyDec verifies that converting a LegacyDec to a Dec via string round-trip works as expected. +func TestDecFromLegacyDec(t *testing.T) { + // Define test cases: a list of valid decimal string representations. + // Note: The legacy format always prints exactly 18 decimal places. + testCases := []struct { + name string + inputStr string + }{ + {"Zero", "0"}, + {"One", "1"}, + {"NegativeOne", "-1"}, + {"IntegerWithNoDecimals", "123456789012345678"}, + {"SimpleDecimal", "123.456"}, + {"NegativeDecimal", "-9876.543210"}, + {"SmallestUnit", "0.000000000000000001"}, // 10^-18 + {"LargeNumber", "12345678901234567890.123456789012345678"}, + {"TrailingZeros", "100.000000000000000000"}, + } + + for _, tc := range testCases { + // capture range variable + t.Run(tc.name, func(t *testing.T) { + // Create a LegacyDec from the test input string. + legacyDec, err := math.LegacyNewDecFromStr(tc.inputStr) + require.NoError(t, err) + + // Convert using our conversion function. + dec, err := math.DecFromLegacyDec(legacyDec) + require.NoError(t, err) + + // Convert directly from the input string for a canonical value. + expectedDec, err := math.NewDecFromString(tc.inputStr) + require.NoError(t, err) + + // Compare the two Dec values. + require.True(t, dec.Equal(expectedDec)) + }) + } +} + +func TestDecFromLegacyDecDeterministic(t *testing.T) { + // List of test input strings in legacy format. + testInputs := []string{ + "0", + "1", + "-1", + "123.456", + "-9876.543210", + "0.000000000000000001", + "100.000000000000000000", + "12345678901234567890.123456789012345678", + } + + // For each input, convert the legacy string to LegacyDec and then run conversion multiple times. + for _, s := range testInputs { + legacyDec, err := math.LegacyNewDecFromStr(s) + require.NoError(t, err) + + // Run the conversion multiple times. + dec1, err := math.DecFromLegacyDec(legacyDec) + require.NoError(t, err) + + dec2, err := math.DecFromLegacyDec(legacyDec) + require.NoError(t, err) + + dec3, err := math.DecFromLegacyDec(legacyDec) + require.NoError(t, err) + + require.True(t, dec1.Equal(dec2) && dec2.Equal(dec3)) + } +} + +// FuzzDecFromLegacyDec fuzzes the conversion function from LegacyDec to Dec. +func FuzzDecFromLegacyDec(f *testing.F) { + // Seed the fuzzer with some valid input strings. + seedInputs := []string{ + "0", + "1", + "-1", + "123.456", + "-9876.543210", + "0.000000000000000001", + "100.000000000000000000", + "12345678901234567890.123456789012345678", + } + for _, s := range seedInputs { + f.Add(s) + } + + f.Fuzz(func(t *testing.T, inputStr string) { + // Attempt to create a LegacyDec from the fuzz input. + legacyDec, err := math.LegacyNewDecFromStr(inputStr) + if err != nil { + // Ignore inputs that do not form a valid LegacyDec. + return + } + + // Convert using the conversion function. + dec, err := math.DecFromLegacyDec(legacyDec) + require.NoError(t, err) + + // Convert directly from the legacy string output. + expectedDec, err := math.NewDecFromString(legacyDec.String()) + require.NoError(t, err) + + require.True(t, dec.Equal(expectedDec)) + }) +}