Repository navigation
electrum: hash the protocol's txid:height: status preimage - #1031
Conversation
Confirmed rows hashed txid:height:blockhash:, so the status no longer matched the one the Electrum protocol defines. Electrum desktop recomputes the status from get_history (synchronizer.py history_status). On a mismatch it does not store the history, and after the network timeout it raises SynchronizerFailure and disconnects. Every address with a confirmed transaction never synced. The status is now SHA-256 of tx_hash:height: for each get_history row. A same-height replace that keeps every (txid, height) keeps the status and pushes nothing; chain_tip on responses still marks it. The asof journey pins that instead of a changed status, and the row-order pin recomputes the protocol preimage. Fixes reardencode#1028 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Ahh. I was slightly afraid this extra hash element was gonna bite me. Seems like a good idea, but didn't think clients were depending on it in this way. Thank you! |
There was a problem hiding this comment.
Summary
Issue #1028 is right. Confirmed scripthash status hashed txid:height:blockhash:, and Electrum desktop recomputes SHA-256 of tx_hash:height: from get_history (synchronizer.history_status). Those never match, so it refuses to store the history and disconnects. This PR puts the status back on that protocol preimage.
The new digest is lowercase hex of the raw SHA-256. txid_hex is the same display-order string get_history puts in tx_hash. Heights, including mempool 0 and -1, are the same i64s on those rows. Fee is not hashed. An empty history stays "" internally and null on the wire. Subscribe and restatus still hash the full history plus the mempool tail, not a from_height / to_height window.
A same-height replace that keeps every (txid, height) no longer changes the status, and take_new_status already skips an unchanged string. blockchain.headers.subscribe still pushes the new tip header, which is what Electrum desktop uses to fork its header chain and re-verify merkle proofs. chain_tip on the next get_history still restamps. The journey now asserts no status push across that replace and the A-B-A restore, and disconnecting the spend still pushes. The socket under test is subscribed to the scripthash only, so a header notification cannot make that drain non-empty.
COMPAT, the TESTING cell, and changelog.d/electrum-protocol-status.md match. Historical CHANGELOG.md text is left alone, which is the right place for an already-released note.
Issue counts by severity
- bugs: 0
- suggestions: 0
- nits: 0
Fixes #1028.
Problem
For confirmed rows,
scripthash_statushashedtxid:height:blockhash:. The Electrum protocol defines the status as SHA-256 oftx_hash:height:perget_historyrow. Electrum desktop recomputes that fromget_history(synchronizer.pyhistory_status). On a mismatch it does not store the history, and after the network timeout it raisesSynchronizerFailureand disconnects. So any address with a confirmed transaction never synced.Change
scripthash_statushashestx_hash:height:for every row and needs noQuery.cached_confirming_hashand the header lookup are gone.(txid, height)now keeps the status, so the reorg restatus pushes nothing for that script. Thechain_tipstamp on responses still changes, and that remains the A-B-A signal (COMPAT.mdChain view). A reorg that moves a row's height, or drops a row, still changes the status and pushes, as before.COMPAT.md,TESTING.md, andchangelog.d/electrum-protocol-status.mdare updated to match.Tests
electrum_server_version_history_balance(row-order pin) now builds the expected status from the protocol preimage overget_history. It fails on master: server332d64a8…vs protocol7b3cee0c….electrum_and_esplora_asof_hides_later_spend: after the same-height replace and the A-B-A restore, no status push arrives, and a fresh subscribe answers the original status. On master a push arrives, so the test fails. The unspend step still pushes a new status.sha256("<txid>:1:").Local: fmt, clippy
-D warnings, deny,ast-grep.sh,cargo test --workspace, andci-os-smoke.shall pass.Clients still learn about a same-height replace:
blockchain.headers.subscribealready pushes the replaced tip's header on everyTipNotify, and Electrum re-verifies merkle proofs on a header reorg.🤖 Generated with Claude Code