Skip to content

Fix electrum do_reorganized: reset() (not flush()) the status accumulator#786

Open
echennells wants to merge 1 commit into
libbitcoin:masterfrom
echennells:electrum-reorganized-accumulator-reset
Open

Fix electrum do_reorganized: reset() (not flush()) the status accumulator#786
echennells wants to merge 1 commit into
libbitcoin:masterfrom
echennells:electrum-reorganized-accumulator-reset

Conversation

@echennells
Copy link
Copy Markdown

Fixes #785

On a chain regression, protocol_electrum::do_reorganized clears each address subscription's Electrum status-hash cursor and calls accumulator.flush() to clear the persistent midstate. But flush() finalizes the digest in place (non-idempotent, non-destructive) and does not reset the midstate; reset() is the operation intended to reuse the accumulator. Because do_reorganized also clears the cursor, the next confirmed-history scan rebuilds the midstate from height 0 on top of that non-reset state, so the recomputed status hash no longer equals sha256 of the intended "::" history string.

Electrum clients key history re-fetch on status equality, so a wrong-but-stable status silently diverges a wallet's view from the chain.

Call reset() instead, which restores the accumulator to its initial state for reuse, so re-accumulation reproduces the correct status.

Adds a regression test
(electrum__blockchain_scripthash_subscribe__reorganized_notify__status_recomputed_clean) mirroring the progressive_notify case: it subscribes over a confirmed history, fires reorganized then organized, and asserts the recomputed pushed status equals the golden hash. Pre-fix it fails (stale midstate); post-fix it passes. The full electrum_tests suite passes.

…ator

On a chain regression, protocol_electrum::do_reorganized clears each
address subscription's Electrum status-hash cursor and calls
accumulator.flush() to clear the persistent midstate. But flush()
finalizes the digest in place (non-idempotent, non-destructive) and does
not reset the midstate; reset() is the operation intended to reuse the
accumulator. Because do_reorganized also clears the cursor, the next
confirmed-history scan rebuilds the midstate from height 0 on top of that
non-reset state, so the recomputed status hash no longer equals sha256 of
the intended "<tx>:<height>:" history string.

Electrum clients key history re-fetch on status equality, so a
wrong-but-stable status silently diverges a wallet's view from the chain.

Call reset() instead, which restores the accumulator to its initial state
for reuse, so re-accumulation reproduces the correct status.

Adds a regression test
(electrum__blockchain_scripthash_subscribe__reorganized_notify__status_recomputed_clean)
mirroring the progressive_notify case: it subscribes over a confirmed
history, fires reorganized then organized, and asserts the recomputed
pushed status equals the golden hash. Pre-fix it fails (stale midstate);
post-fix it passes. The full electrum_tests suite passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Electrum do_reorganized calls accumulator.flush() instead of reset(), corrupting subscription status hashes after every reorg

1 participant