From ba6d0860829753d977de2a3f847d8216250d7315 Mon Sep 17 00:00:00 2001 From: ptrus Date: Mon, 22 Dec 2025 11:56:44 +0100 Subject: [PATCH] common: Fix NumericToBigInt returning zero for negative exponents --- .changelog/1230.bugfix.1.md | 5 ++++ .changelog/1230.bugfix.2.md | 5 ++++ analyzer/runtime/runtime.go | 2 +- common/types.go | 4 +-- common/types_test.go | 52 +++++++++++++++++++++++++++++++++++++ 5 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 .changelog/1230.bugfix.1.md create mode 100644 .changelog/1230.bugfix.2.md diff --git a/.changelog/1230.bugfix.1.md b/.changelog/1230.bugfix.1.md new file mode 100644 index 000000000..2f83eb0a1 --- /dev/null +++ b/.changelog/1230.bugfix.1.md @@ -0,0 +1,5 @@ +analyzer/runtime: Fix pointer comparison in balance update check + +The condition `change != big.NewInt(0)` was comparing pointers instead +of values, causing it to always be true. Changed to `change.Sign() != 0` +for proper value comparison. diff --git a/.changelog/1230.bugfix.2.md b/.changelog/1230.bugfix.2.md new file mode 100644 index 000000000..60a34ed41 --- /dev/null +++ b/.changelog/1230.bugfix.2.md @@ -0,0 +1,5 @@ +common: Fix NumericToBigInt returning zero for negative exponents + +The function was returning `*big0` instead of `*bi` for negative +exponents, causing it to always return zero. Added tests for the +function. diff --git a/analyzer/runtime/runtime.go b/analyzer/runtime/runtime.go index 03b5bbf27..a76e36da5 100644 --- a/analyzer/runtime/runtime.go +++ b/analyzer/runtime/runtime.go @@ -617,7 +617,7 @@ func (m *processor) queueDbUpdates(batch *storage.QueryBatch, data *BlockData) { // Update EVM token balances (dead reckoning). for key, change := range data.TokenBalanceChanges { // Update (dead-reckon) the DB balance only if it's actually changed. - if change != big.NewInt(0) && m.mode != analyzer.FastSyncMode { + if change.Sign() != 0 && m.mode != analyzer.FastSyncMode { if key.TokenAddress == evm.NativeRuntimeTokenAddress { batch.Queue(queries.RuntimeNativeBalanceUpsert, m.runtime, key.AccountAddress, nativeTokenSymbol(m.sdkPT), change.String()) } else { diff --git a/common/types.go b/common/types.go index 11589a68e..8aab6d218 100644 --- a/common/types.go +++ b/common/types.go @@ -168,9 +168,9 @@ func NumericToBigInt(n pgtype.Numeric) (BigInt, error) { remainder := &big.Int{} bi.DivMod(bi, div, remainder) if remainder.Cmp(big0) != 0 { - return BigInt{Int: *big0}, fmt.Errorf("cannot convert %v to integer", n) + return BigInt{}, fmt.Errorf("cannot convert %v to integer", n) } - return BigInt{Int: *big0}, nil + return BigInt{Int: *bi}, nil } // Decimal is a wrapper around apd.Decimal to allow for custom JSON marshaling. diff --git a/common/types_test.go b/common/types_test.go index ced3ee1b3..74a855f26 100644 --- a/common/types_test.go +++ b/common/types_test.go @@ -4,8 +4,10 @@ import ( "encoding/json" "fmt" "math" + "math/big" "testing" + "github.com/jackc/pgx/v5/pgtype" "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/stretchr/testify/require" ) @@ -122,3 +124,53 @@ func TestBigDecimalNumeric(t *testing.T) { require.EqualValues(t, dec, roundTripped, "BigDecimal should match after Numeric conversion for value %s", tc.value) } } + +func TestNumericToBigInt(t *testing.T) { + for _, tc := range []struct { + name string + numeric pgtype.Numeric + expected int64 + hasError bool + }{ + { + name: "zero exponent", + numeric: pgtype.Numeric{Int: big.NewInt(12345), Exp: 0, Valid: true}, + expected: 12345, + }, + { + name: "positive exponent", + numeric: pgtype.Numeric{Int: big.NewInt(123), Exp: 2, Valid: true}, + expected: 12300, + }, + { + name: "negative exponent exact division", + numeric: pgtype.Numeric{Int: big.NewInt(12300), Exp: -2, Valid: true}, + expected: 123, + }, + { + name: "negative exponent with remainder", + numeric: pgtype.Numeric{Int: big.NewInt(12345), Exp: -2, Valid: true}, + hasError: true, + }, + { + name: "zero value", + numeric: pgtype.Numeric{Int: big.NewInt(0), Exp: 0, Valid: true}, + expected: 0, + }, + { + name: "negative value", + numeric: pgtype.Numeric{Int: big.NewInt(-500), Exp: 1, Valid: true}, + expected: -5000, + }, + } { + t.Run(tc.name, func(t *testing.T) { + result, err := NumericToBigInt(tc.numeric) + if tc.hasError { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expected, result.Int64()) + } + }) + } +}