feat(payjoin): harden core engine (watchers, resume, guards) - #2478
Conversation
WatchPayjoinUsecase now emits both PayjoinReceiver and PayjoinSender (scoped by ids); the send flow needs sender completion events a receiver-only filter would swallow. ReceiveBloc keeps its receiver-only behaviour by filtering the concrete type at its own call site.
Adds a sealed WalletFailure family and a single-transaction lookup use-case returning Result<WalletTransaction?, WalletTransactionLookupFailure> (Ok(null) = synced-and-absent, Err = infra failure), per the #1895 Result migration. Consumed by the payjoin engine and transaction details.
PdkPayjoinDatasource gains stopPolling(id) and an idempotent dispose() so a resolved session's directory poll can be cut (no more stale expiry events) and tests get a clean teardown. The local datasource filters include isAborted and fetchByTxId matches originalTxId too, and sender poll logs/exceptions use a hashed logRef instead of the raw BIP21 URI.
The core of the fix. A unified backoff-poll engine arms two per-session watchers — one for the real payjoin tx, one for the original (fallback) tx — so a session always reaches a terminal state (completed or aborted) instead of hanging. tryBroadcastOriginalTransaction is split into a guarded public entry (refuses via canManuallyBroadcastOriginal, emits on the stream) and the internal mechanism; expiry/proposal processing re-fetch the fresh row and bail on terminal; resume moves out of the constructor to resumePayjoinsOnStartup, called from AppLocator once wallet/labels deps are registered. Wallet repos are injected as lazy closures; the directory receiveTimeout is raised to 35s (above the 30s long-poll hold). Adds the PAYJOIN --> LABELS edge to FEATURES.md.
cc2d099 to
8a0e24c
Compare
5a245c5 to
4aee114
Compare
|
@claude audit this PR please |
|
Claude finished @ethicnology's task in 8m 37s —— View job Audit of PR #2478 — payjoin core engine hardening
Overall: solid work. The engine design is coherent — explicit Findings1.
|
Hardens the core payjoin engine: watchers, session-resume logic, and guards against invalid states. Models the fallback-to-normal-tx path explicitly via PayjoinStatus.aborted + a DB isAborted flag, instead of deriving it from isCompleted && txId == null. Extends the Payjoin entity with logRef, canManuallyBroadcastOriginal, and isCompleted/isAborted/isExpired getters.
Builds on #2443's typestate API. Addresses part of #2416 (payjoin improvements).
Merge order: 3rd, after PR1. Base of PR3, PR4, PR5 — must merge before all three.
PR1 → PR2 (this) → {PR3, PR4, PR5}