Skip to content

Resolve fees for pre-662000 witness-hash transactions - #8

Merged
droplister merged 1 commit into
mainfrom
witness-hash-fee-resolution
Aug 23, 2026
Merged

Resolve fees for pre-662000 witness-hash transactions#8
droplister merged 1 commit into
mainfrom
witness-hash-fee-resolution

Conversation

@droplister

Copy link
Copy Markdown
Member

What this fixes

932 transactions between blocks 557,368 and 661,989 carried a witness hash (wtxid) as their Counterparty tx_hash. Electrs, mempool.space and blockstream.info all 404 on them permanently. They sat at the top of transactions WHERE fee IS NULL ORDER BY tx_index DESC and ate the entire fee-backfill budget every cron tick — requested: 1000, updated: 0 after 128 s — so the runner never reached the 1.08M pre-segwit rows below them.

Root cause (upstream, and permanent)

counterparty-core/counterpartycore/protocol_changes.json:

"correct_segwit_txids": { "block_index": 662000, "minimum_version_minor": 59 }

counterparty-rs/src/indexer/bitcoin_client.rs:907:

let tx_id = tx.compute_txid().to_string();
if segwit && config.correct_segwit_txids_enabled(height) {
    tx_hash = tx_id.clone();                            // >= 662000: the real txid
} else {
    tx_hash = Sha256dHash::hash(&tx_bytes).to_string(); // full bytes incl. witness = the wtxid
}

That else branch is still in current master by design — these hashes are consensus identity throughout the ledger (sends, orders, dispensers reference them), so the fix is forward-only and history can never be rewritten. No node upgrade will change these values; the live node is on 11.3.0 and still serves the wtxid for them, correctly.

So the mirror keeps the wtxid (CLAUDE.md rule 7 — raw 1:1 capture) and only the derived fee is repaired.

The footprint matches the protocol gate exactly on both ends: 932 missing rows below block 662,000, 0 at or above it; highest 661,989 (eleven blocks before activation), lowest 557,368 (just after segwit_support at 557,236).

Approach

No Bitcoin node needed. For each affected block: fetch the raw block, compute both identities for every transaction — txid from the serialization without witness, wtxid from the full serialization — match ours by wtxid, then read the fee from the canonical txid. Exact by construction, no heuristic matching. Resumable via its output file.

Results

  • 932/932 resolved, 0 unresolved, 0 failed blocks across 685 distinct blocks.
  • Every row matched through the wtxid path — none via the txid fallback, exactly as predicted.
  • 932 distinct txids (clean 1:1), no zero fees, range 113–736,011 sats.
  • Five rows spot-verified against Electrs, a different provider from the one used to resolve: 5/5 exact on both fee and block height.
  • Published to xcpio-core: 3,708 writes = 932 rows × 4 (row + 3 trigger writes), guarded with WHERE fee IS NULL.
  • Missing fees above block 550,000: now 0. Highest remaining is 547,696 — below segwit activation, so every remaining row is hash-fetchable.
  • The trigger-maintained bitcoin_fees_remaining counter agrees exactly with COUNT(*) at 1,079,101.

Relationship to #7

Independent — different files, no conflict. #7 fixes the cron job (persisted walk cursor, tip pass, provider-budget pacing) for the remaining 1,079,101 pre-segwit rows. This PR clears the blockage that made #7's walk necessary to diagnose in the first place.

npm run check passes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VkV3DqBuyfsJj9bo76xiDM

932 transactions between blocks 557,368 and 661,989 carried a witness hash
(wtxid) as their Counterparty tx_hash, so Electrs, mempool.space and
blockstream all returned 404 for them permanently. They sat at the top of
the missing-fee index and consumed the entire fee-backfill budget every
cron tick, resolving nothing and never reaching the 1.08M pre-segwit rows
below.

Upstream gates this in protocol_changes.json as correct_segwit_txids at
block 662,000; counterparty-rs/src/indexer/bitcoin_client.rs computes
tx_hash as sha256d over the full witness-inclusive serialization below
that height and the txid at or above it. The old branch remains in current
master by design: these hashes are consensus identity throughout the
ledger, so the fix is forward-only and history can never be rewritten.
The mirror therefore keeps the wtxid (rule 7) and only the derived fee is
repaired.

This script recovers the mapping exactly and without a Bitcoin node: it
reads each block's raw bytes, computes both the txid and the wtxid for
every transaction, matches ours by wtxid, then reads the fee from the
canonical txid.

All 932 resolved, 0 unresolved, 0 failed blocks. Every row mapped through
the wtxid path (none matched by txid), 932 distinct txids, fees 113 to
736,011 sats. Five spot-checks verified against Electrs, an independent
provider from the one used to resolve. Published to xcpio-core; missing
fees above block 550,000 are now 0 and the highest remaining is 547,696,
below segwit activation, so every remaining row is hash-fetchable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkV3DqBuyfsJj9bo76xiDM
@droplister
droplister merged commit 80c8c7c into main Aug 23, 2026
2 checks passed
@droplister
droplister deleted the witness-hash-fee-resolution branch August 23, 2026 14:39
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.

1 participant