[UTXO-BUG] fix compute_state_root() little-endian count_bytes — consensus divergence#6510
[UTXO-BUG] fix compute_state_root() little-endian count_bytes — consensus divergence#6510Ivan-LB wants to merge 2 commits into
Conversation
…nsus divergence Bug: utxo_db.py:861 used len(rows).to_bytes(8, 'little') while every other integer encoding in the module uses 'big' (network byte order): - compute_box_id: value_nrtc, creation_height, output_index → 'big' - compute_tx_id: timestamp → 'big' - Module docstring: "Uses big-endian (network byte order)..." Impact: nodes running old code compute a different Merkle state root than nodes running fixed code for the same UTXO set — a latent consensus fork. The single-box case is not masked (LE ≠ BE for any count > 0). Fix: change 'little' to 'big' on count_bytes line. Add PoC test confirming old/new roots diverge. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Welcome to RustChain! Thanks for your first pull request. Before we review, please make sure:
Bounty tiers: Micro (1-10 RTC) | Standard (20-50) | Major (75-100) | Critical (100-150) A maintainer will review your PR soon. Thanks for contributing! |
…de try Lines 239-251 were outdented out of the try block, placing code between the try body and the except clause — a SyntaxError on collection. Re-indent to restore the intended try/except/finally structure. Pre-existing bug in main; fixing to unblock CI on this PR.
|
Hi @Ivan-LB — I see you self-closed PR #6510 and #6511, but both findings are legitimate and reproduce as described:
These deserve bug-report bounty (#305) at the Medium tier (~15-25 RTC each) plus the fix-PR severity (#2819) at Medium-High depending on Codex audit. Could you:
We're at ~761 holders ($0.10 reference rate) — your work is paid at current rates regardless of when you respond. See rustchain-bounties#12458 for the rate-reduction schedule going forward. |
|
Hi @Scottcjn, thanks for confirming. RTC Wallet: Also, since the original branches were deleted the PRs could not be reopened, so I filed fresh ones instead:
Both include PoC tests that pass. Sorry for the confusion. |
Bug
utxo_db.pyline 861 uses little-endian forcount_bytesincompute_state_root(), while every other integer-to-bytes call in the module uses big-endian (network byte order):compute_box_id()— value_nrtc, creation_height, output_index'big'compute_tx_id()— timestamp'big'compute_state_root()— count_bytes ← line 861'little'← BUGImpact
For any UTXO set with more than 0 elements,
'little'and'big'produce different byte sequences:Every leaf hash is
SHA256(count_bytes || leaf_json), so a single-byte position difference causes completely different Merkle roots. Two nodes — one running old code and one running the fix — will disagree on the state root for the same UTXO set, triggering a consensus split the moment the fix is deployed.The module docstring claims "All nodes with the same UTXO set produce the same root" — violated as soon as nodes diverge on endianness.
Fix
Test
node/test_utxo_state_root_endian_poc.py(new file, 3 tests, all pass after fix):test_count_bytes_endian_divergence— shows LE ≠ BE for counts 1–10,000test_state_root_diverges_for_two_boxes— same UTXO set → different roots old vs newtest_fixed_code_uses_big_endian— fixedcompute_state_root()matches big-endian referenceBounty Reference
Issue #2819 — Merkle state root manipulation, Medium severity (50 RTC).
RTC Wallet: Ivan-LB