Repository navigation
fix(accounts): normalize address in raw-SQL balance operations - #1773
Alicepoltora wants to merge 3 commits into
Conversation
debit_account_balance(), credit_account_balance() and credit_tx_value_once() execute raw SQL keyed by the address, but unlike get_account()/create_new_account_with_address() they never passed the address through to_checksum_address(). Accounts are stored keyed by the checksummed id, so a caller passing a lowercase address (e.g. from a JSON-RPC 'from' field) would: - debit_account_balance(): match no row and silently return False even when the account has sufficient balance; - credit_account_balance(): hit ON CONFLICT DO NOTHING on the checksummed row's id and instead INSERT a new duplicate row keyed by the lowercase address, splitting the balance across two accounts; - refund_tx_value(): credit the refund onto the lowercase duplicate row, losing the funds from the sender's perspective. _normalize_address() centralizes the to_checksum_address() normalization with the same fallback-on-error semantics get_account() already used, and is applied to every raw-SQL balance path. Unit tests (no DB required) verify the SQL parameters are normalized and that non-address values are passed through unchanged.
|
This PR targeted I retargeted it to |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
# Conflicts: # backend/database_handler/accounts_manager.py
|
Thanks! Since this was opened, #1748 added inline checksumming to |
danieljrc888
left a comment
There was a problem hiding this comment.
Reviewed for the v0.123 release candidate; unit suite and CI green.
|
/run-e2e all Frozen landing cut: #1773 alone, head |
Lands four reviewed fork PRs together after a full E2E run on this exact head: - #1765: redact validator private keys from call receipts and explorer payloads - #1715: bind postgres, redis and ollama host ports to loopback - #1773: normalize addresses in raw-SQL balance operations - #1789: normalize the address in sim_fundAccount Co-authored-by: Kanan <93033289+kriss39@users.noreply.github.com> Co-authored-by: Starling <82058629+Starling-spell@users.noreply.github.com> Co-authored-by: Alicepoltora <Alicepoltora@users.noreply.github.com>
Problem
Account rows are keyed by the checksummed address:
create_new_account_with_address()andget_account()run every address throughto_checksum_address(). But the raw-SQL balance paths —debit_account_balance(),credit_account_balance(),credit_tx_value_once()— used the address verbatim.Any caller passing a non-checksummed address (e.g. lowercase, as users commonly send in JSON-RPC — see
_sandbox_debit_senderinendpoints.py):credit_account_balance(lower_addr):INSERT ... ON CONFLICT (id) DO NOTHINGdoes not conflict with the checksummed row → creates a duplicate account row keyed by the lowercase address, and the credit lands on that duplicate. Balance gets split across two rows for the same address.debit_account_balance(lower_addr):WHERE id = lower_addrmatches nothing → silently returnsFalse(insufficient balance) even though the account has funds.refund_tx_value(): credits the refund to the lowercase duplicate row instead of the sender's real account.Fix
Added
_normalize_address()(checksum with the same fallback-on-error semantics thatget_account()already used) and applied it to every raw-SQL balance path.Tests
New
tests/unit/test_accounts_manager_normalization.py(mocked session, no DB needed):test_credit_normalizes_address_before_insert_and_updateandtest_debit_normalizes_addressandtest_credit_tx_value_once_normalizes_target— fail onmain, pass with the fixtest_credit_zero_or_negative_amount_is_noop— no-op behavior preservedtest_normalize_fallback_keeps_original— non-address values pass through unchanged (backwards compatibility)Summary by CodeRabbit