Skip to content

ddk: make the contract UTXO tracker available without manager - #227

Open
bennyhodl wants to merge 1 commit into
masterfrom
benny/225-contract-tracker
Open

bennyhodl wants to merge 1 commit into
masterfrom
benny/225-contract-tracker

Conversation

@bennyhodl

Copy link
Copy Markdown
Owner

Summary

Item 4 of #225. ContractUtxoTracker depends only on bdk_chain, bitcoin, serde and ContractId, but it lived under the manager-only wallet module, so the Lygos wallet carried a 415-line copy. It now lives at ddk::contract::tracker, builds with default-features = false, and has the three methods the Lygos copy added.

Changes

  • Move wallet/contract_tracker.rs to contract/tracker.rs. ddk::wallet::contract_tracker stays as a re-export, so manager users' imports don't change.
  • Add to ContractUtxoTracker:
    • expected_spk_txids(&LocalChain) -> Vec<(ScriptBuf, Txid)>
    • transaction(&Txid) -> Option<Arc<Transaction>>
    • a borrowing staged() -> Option<&ChangeSet> beside take_staged
  • DDK's own tracker sync now passes expected_spk_txids, through the same builder call the wallet sync uses. Before this, a replaced or dropped CET stayed canonical in the tracker graph.
  • The wallet sync persists the tracker changeset before clearing it. Before, it took the changeset before a write that can fail, and a failed write lost it.
  • Declare bdk_chain's serde feature directly. It used to come in only through bdk_wallet.
  • Storage is unchanged: same changeset shape, same sled key and postgres table.

Known edge (not fixed here)

bdk_esplora stamps evictions and sightings in one sync with the same start_time, and bdk_chain keeps a transaction canonical only while last_evicted < last_seen. If Esplora's script history leaves out a known CET while the outpoint lookup in the same sync reports it, the CET drops out of the canonical view. That needs Esplora to contradict itself within one sync, and it already applies to DDK's wallet sync. If it needs fixing, the fix belongs in the shared sync step (or upstream in bdk_esplora), not in a tracker-only filter.

Interaction with the other #225 PRs

The own-broadcast grace PR (item 5) filters evictions in the wallet sync. The tracker only learns transactions from chain sync, never from local broadcast inserts, so the tracker sync doesn't need the grace today.

Testing

  • New dropped_cet_is_evicted_by_a_sync_with_expected_txids: a CET left the mempool, and a sync built from expected_spk_txids evicts it and restores the funding output.
  • New staged_changes_stay_until_taken.
  • cargo test -p ddk --lib: 82 passed, including the wallet tests against regtest.
  • cargo test -p ddk --test enumeration -- --ignored: passed. This is the full lifecycle through the tracker sync.
  • contract_tracker_round_trips passes on sled. The postgres variant couldn't start locally (the embedded postgres needs libxml2.so.2), so CI covers it.
  • cargo fmt --check, cargo clippy -- -D warnings and cargo check -p ddk --no-default-features pass.
  • Reviewed by GPT-6 Astra: no blockers. The one minor finding is the edge above.

The tracker depends only on bdk_chain, bitcoin, serde and ContractId, but
it lived in the `wallet` module, which is gated behind `manager`. A
stateless wallet built on `ddk::contract` had to carry its own copy.
It moves to `ddk::contract::tracker`; `ddk::wallet::contract_tracker`
re-exports it so manager users' imports keep working.

Three methods come from that copy:
- `expected_spk_txids` lists the txids a sync should still find under the
  funding scripts. DDK's own tracker sync now passes them, the same way
  the wallet sync already does, so a replaced or dropped CET is evicted
  instead of staying canonical in the graph.
- `transaction` returns a transaction the tracker has seen.
- `staged` borrows the staged changeset, so a persister clears it only
  once the write is durable. DDK's sync now does that instead of taking
  the changeset before a write that can fail.
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