Summary
BlockProducer.get_state_root() computes the block state commitment from the legacy balances account-model table even when the node is operating against the UTXO database. In UTXO mode, block headers can therefore commit to a state root that does not match the actual spendable state.
Impact
This is a consensus-correctness bug. Two honest validators with the same UTXO set can derive different block state_root values if their legacy balances tables diverge or are stale. A produced block can then carry a header commitment that does not match the underlying UTXO ledger state.
Root cause
node/rustchain_block_producer.py always does:
- query
balances
- hash
(wallet, balance_urtc, wallet_nonce) rows
and never consults UtxoDB.compute_state_root().
Fix
When a utxo_db handle is available, BlockProducer should use utxo_db.compute_state_root() and only fall back to the legacy account-model path when no UTXO database is attached (or when UTXO root computation fails).
Validation
I prepared a fix with focused coverage for:
- UTXO root preferred when
utxo_db is present
- legacy fallback when no
utxo_db is attached
- roots differ between account-model and UTXO-mode state
- empty UTXO root handling
- state-root changes after spends
- deterministic root behavior
- graceful fallback if UTXO root computation raises
Payout wallet: RTC1d48d848a5aa5ecf2c5f01aa5fb64837daaf2f35
Summary
BlockProducer.get_state_root()computes the block state commitment from the legacybalancesaccount-model table even when the node is operating against the UTXO database. In UTXO mode, block headers can therefore commit to a state root that does not match the actual spendable state.Impact
This is a consensus-correctness bug. Two honest validators with the same UTXO set can derive different block
state_rootvalues if their legacybalancestables diverge or are stale. A produced block can then carry a header commitment that does not match the underlying UTXO ledger state.Root cause
node/rustchain_block_producer.pyalways does:balances(wallet, balance_urtc, wallet_nonce)rowsand never consults
UtxoDB.compute_state_root().Fix
When a
utxo_dbhandle is available,BlockProducershould useutxo_db.compute_state_root()and only fall back to the legacy account-model path when no UTXO database is attached (or when UTXO root computation fails).Validation
I prepared a fix with focused coverage for:
utxo_dbis presentutxo_dbis attachedPayout wallet:
RTC1d48d848a5aa5ecf2c5f01aa5fb64837daaf2f35