Skip to main content

bark/persist/
mod.rs

1//! Persistence abstractions for Bark wallets.
2//!
3//! This module defines the [BarkPersister] trait and related data models used by the
4//! wallet to store and retrieve state. Implementors can provide their own storage backends
5//! (e.g., SQLite, PostgreSQL, in-memory, mobile key/value stores) by implementing the
6//! [BarkPersister] trait.
7//!
8//! Design goals
9//! - Clear separation between wallet logic and storage.
10//! - Transactional semantics where appropriate (round state transitions, movement recording).
11//! - Portability across different platforms and environments.
12//!
13//! Typical usage
14//! - Applications construct a concrete persister (for example, a SQLite-backed client) and
15//!   pass it to the [crate::Wallet]. The [crate::Wallet] only depends on this trait for reads/writes.
16//! - Custom wallet implementations can reuse this trait to remain compatible with Bark
17//!   storage expectations without depending on a specific database.
18//! - A default rusqlite implementation is provided by [sqlite::SqliteClient].
19
20pub mod adaptor;
21pub mod models;
22#[cfg(feature = "sqlite")]
23pub mod sqlite;
24#[cfg(test)]
25pub(crate) mod test_suite;
26
27
28use bitcoin::{Amount, Transaction, Txid};
29use bitcoin::secp256k1::PublicKey;
30use chrono::DateTime;
31use lightning_invoice::Bolt11Invoice;
32#[cfg(feature = "onchain-bdk")]
33use bdk_wallet::ChangeSet;
34
35use ark::{Vtxo, VtxoId};
36use ark::lightning::{Invoice, PaymentHash, Preimage};
37use ark::vtxo::Full;
38use bitcoin_ext::BlockDelta;
39
40use crate::WalletProperties;
41use crate::actions::{WalletActionCheckpoint, WalletActionId};
42use crate::exit::ExitTxOrigin;
43use crate::persist::models::{
44	LightningReceive, LightningSend, PendingBoard, RoundStateId, StoredExit, StoredRoundState,
45	Unlocked, PendingOffboard,
46};
47use crate::movement::{Movement, MovementId, MovementStatus, MovementSubsystem, PaymentMethod};
48use crate::round::RoundState;
49use crate::vtxo::{VtxoState, VtxoStateKind, WalletVtxo};
50
51/// Storage interface for Bark wallets.
52///
53/// Implement this trait to plug a custom persistence backend. The wallet uses it to:
54/// - Initialize and read wallet properties and configuration.
55/// - Record movements (spends/receives), recipients, and enforce [Vtxo] state transitions.
56/// - Manage round lifecycles (attempts, pending confirmation, confirmations/cancellations).
57/// - Persist ephemeral protocol artifacts (e.g., secret nonces) transactionally.
58/// - Track Lightning receives and preimage revelation.
59/// - Track exit-related data and associated child transactions.
60/// - Persist the last synchronized Ark block height.
61///
62/// Feature integration:
63/// - With the `onchain-bdk` feature, methods are provided to initialize and persist a BDK
64///   wallet ChangeSet in the same storage.
65///
66/// Notes for implementors:
67/// - Ensure that operations that change multiple records (e.g., registering a movement,
68///   storing round state transitions) are executed transactionally.
69/// - Enforce state integrity by verifying allowed_old_states before updating a [Vtxo] state.
70/// - If your backend is not thread-safe, prefer a short-lived connection per call or use
71///   an internal pool with checked-out connections per operation.
72/// - Return precise errors so callers can surface actionable diagnostics.
73#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
74#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
75pub trait BarkPersister: Send + Sync + 'static {
76	/// Check if the wallet is initialized.
77	///
78	/// Returns:
79	/// - `Ok(true)` if the wallet is initialized.
80	/// - `Ok(false)` if the wallet is not initialized.
81	///
82	/// Errors:
83	/// - Returns an error if the query fails.
84	async fn is_initialized(&self) -> anyhow::Result<bool> {
85		Ok(self.read_properties().await?.is_some())
86	}
87
88	/// Initialize a wallet in storage with the provided properties.
89	///
90	/// Call exactly once per wallet database. Subsequent calls should fail to prevent
91	/// accidental re-initialization.
92	///
93	/// Parameters:
94	/// - properties: WalletProperties to persist (e.g., network, descriptors, metadata).
95	///
96	/// Returns:
97	/// - `Ok(())` on success.
98	///
99	/// Errors:
100	/// - Returns an error if the wallet is already initialized or if persistence fails.
101	async fn init_wallet(&self, properties: &WalletProperties) -> anyhow::Result<()>;
102
103	/// Initialize the onchain BDK wallet and return any previously stored ChangeSet.
104	///
105	/// Must be called before storing any new BDK changesets to bootstrap the BDK state.
106	///
107	/// Feature: only available with `onchain-bdk`.
108	///
109	/// Returns:
110	/// - `Ok(ChangeSet)` containing the previously persisted BDK state (possibly empty).
111	///
112	/// Errors:
113	/// - Returns an error if the BDK state cannot be created or loaded.
114	#[cfg(feature = "onchain-bdk")]
115	async fn initialize_bdk_wallet(&self) -> anyhow::Result<ChangeSet>;
116
117	/// Persist an incremental BDK ChangeSet.
118	///
119	/// The changeset should be applied atomically. Callers typically obtain the changeset
120	/// from a BDK wallet instance after mutating wallet state (e.g., sync).
121	///
122	/// Feature: only available with `onchain-bdk`.
123	///
124	/// Parameters:
125	/// - changeset: The BDK ChangeSet to persist.
126	///
127	/// Errors:
128	/// - Returns an error if the changeset cannot be written.
129	#[cfg(feature = "onchain-bdk")]
130	async fn store_bdk_wallet_changeset(&self, changeset: &ChangeSet) -> anyhow::Result<()>;
131
132	/// Read wallet properties from storage.
133	///
134	/// Returns:
135	/// - `Ok(Some(WalletProperties))` if the wallet has been initialized.
136	/// - `Ok(None)` if no wallet exists yet.
137	///
138	/// Errors:
139	/// - Returns an error on I/O or deserialization failures.
140	async fn read_properties(&self) -> anyhow::Result<Option<WalletProperties>>;
141
142	/// Set the server public key in wallet properties.
143	///
144	/// This is used to store the server pubkey for existing wallets that were
145	/// created before server pubkey tracking was added. Once set, the wallet
146	/// will verify the server pubkey on every connection.
147	///
148	/// Parameters:
149	/// - server_pubkey: The server's public key to store.
150	///
151	/// Errors:
152	/// - Returns an error if the update fails.
153	async fn set_server_pubkey(&self, server_pubkey: PublicKey) -> anyhow::Result<()>;
154
155	/// Set the server's mailbox public key in wallet properties.
156	///
157	/// This is used to store the server mailbox pubkey for existing wallets that were
158	/// created before mailbox pubkey tracking was added. Once set, Ark addresses
159	/// can be generated offline without a live server connection.
160	///
161	/// Parameters:
162	/// - server_mailbox_pubkey: The server's mailbox public key to store.
163	///
164	/// Errors:
165	/// - Returns an error if the update fails.
166	async fn set_server_mailbox_pubkey(&self, server_mailbox_pubkey: PublicKey) -> anyhow::Result<()>;
167
168	/// Creates a new movement in the given state, ready to be updated.
169	///
170	/// Parameters:
171	/// - status: The desired status for the new movement.
172	/// - subsystem: The subsystem that created the movement.
173	/// - time: The time the movement should be marked as created.
174	///
175	/// Returns:
176	/// - `Ok(MovementId)` of the newly created movement.
177	///
178	/// Errors:
179	/// - Returns an error if the movement is unable to be created.
180	async fn create_new_movement(&self,
181		status: MovementStatus,
182		subsystem: &MovementSubsystem,
183		time: DateTime<chrono::Local>,
184	) -> anyhow::Result<MovementId>;
185
186	/// Persists the given movement state.
187	///
188	/// Parameters:
189	/// - movement: The movement and its associated data to be persisted.
190	///
191	/// Errors:
192	/// - Returns an error if updating the movement fails for any reason.
193	async fn update_movement(&self, movement: &Movement) -> anyhow::Result<()>;
194
195	/// Gets the movement with the given [MovementId].
196	///
197	/// Parameters:
198	/// - movement_id: The ID of the movement to retrieve.
199	///
200	/// Returns:
201	/// - `Ok(Movement)` if the movement exists.
202	///
203	/// Errors:
204	/// - If the movement does not exist.
205	/// - If retrieving the movement fails.
206	async fn get_movement_by_id(&self, movement_id: MovementId) -> anyhow::Result<Movement>;
207
208	/// Gets every stored movement.
209	///
210	/// Returns:
211	/// - `Ok(Vec<Movement>)` containing all movements, empty if none exist.
212	///
213	/// Errors:
214	/// - If retrieving the movements fails.
215	async fn get_all_movements(&self) -> anyhow::Result<Vec<Movement>>;
216
217	/// Get all movements for a given payment method
218	///
219	/// Parameters:
220	/// - `payment_method`: The [PaymentMethod] to look up.
221	///
222	/// Returns:
223	/// - `Ok(movements)` containing all relevant movements, empty if none exist.
224	///
225	/// Errors:
226	/// - Returns an error if the query fails.
227	async fn get_movements_by_payment_method(
228		&self,
229		payment_method: &PaymentMethod,
230	) -> anyhow::Result<Vec<Movement>>;
231
232	/// Store a pending board.
233	///
234	/// Parameters:
235	/// - vtxo: The [Vtxo] to store.
236	/// - funding_txid: The funding [Txid].
237	/// - movement_id: The [MovementId] associated with this board.
238	///
239	/// Errors:
240	/// - Returns an error if the board cannot be stored.
241	async fn store_pending_board(
242		&self,
243		vtxo: &Vtxo<Full>,
244		funding_tx: &Transaction,
245		movement_id: MovementId,
246	) -> anyhow::Result<()>;
247
248	/// Remove a pending board.
249	///
250	/// Parameters:
251	/// - vtxo_id: The [VtxoId] to remove.
252	///
253	/// Errors:
254	/// - Returns an error if the board cannot be removed.
255	async fn remove_pending_board(&self, vtxo_id: &VtxoId) -> anyhow::Result<()>;
256
257	/// Get the [VtxoId] for each pending board.
258	///
259	/// Returns:
260	/// - `Ok(Vec<VtxoId>)` possibly empty.
261	///
262	/// Errors:
263	/// - Returns an error if the query fails.
264	async fn get_all_pending_board_ids(&self) -> anyhow::Result<Vec<VtxoId>>;
265
266	/// Get the [PendingBoard] associated with the given [VtxoId].
267	///
268	/// Returns:
269	/// - `Ok(Some(PendingBoard))` if a matching board exists
270	/// - `Ok(None)` if no matching board exists
271	///
272	/// Errors:
273	/// - Returns an error if the query fails.
274	async fn get_pending_board_by_vtxo_id(&self, vtxo_id: VtxoId) -> anyhow::Result<Option<PendingBoard>>;
275
276	/// Store a new ongoing round state and lock the VTXOs in round
277	///
278	/// Parameters:
279	/// - `round_state`: the state to store
280	///
281	/// Returns:
282	/// - `RoundStateId`: the storaged ID of the new state
283	///
284	/// Errors:
285	/// - returns an error of the new round state could not be stored or the VTXOs
286	///   couldn't be marked as locked
287	async fn store_round_state_lock_vtxos(&self, round_state: &RoundState) -> anyhow::Result<RoundStateId>;
288
289	/// Update an existing stored pending round state
290	///
291	/// Parameters:
292	/// - `round_state`: the round state to update
293	///
294	/// Errors:
295	/// - returns an error of the existing round state could not be found or updated
296	async fn update_round_state(&self, round_state: &StoredRoundState) -> anyhow::Result<()>;
297
298	/// Remove a pending round state from the db
299	///
300	/// Parameters:
301	/// - `round_state`: the round state to remove
302	///
303	/// Errors:
304	/// - returns an error of the existing round state could not be found or removed
305	async fn remove_round_state(&self, round_state: &StoredRoundState) -> anyhow::Result<()>;
306
307	/// Load a single round state by its id
308	///
309	/// Returns:
310	/// - `Option<StoredRoundState>`: the stored round state if found, `None` otherwise
311	///
312	/// Errors:
313	/// - returns an error of the states could not be succesfully retrieved
314	async fn get_round_state_by_id(&self, id: RoundStateId) -> anyhow::Result<Option<StoredRoundState<Unlocked>>>;
315
316	/// Load all pending round states from the db
317	///
318	/// Returns:
319	/// - `Vec<RoundStateId>`: unordered vector with all stored round state ids
320	///
321	/// Errors:
322	/// - returns an error of the ids could not be succesfully retrieved
323	async fn get_pending_round_state_ids(&self) -> anyhow::Result<Vec<RoundStateId>>;
324
325	/// Stores VTXOs with their initial state.
326	///
327	/// This operation is idempotent: if a VTXO already exists (same `id`), the
328	/// implementation should succeed without modifying the existing VTXO or its
329	/// state. This allows safe retries during crash recovery scenarios.
330	///
331	/// # Parameters
332	/// - `vtxos`: Slice of VTXO and state pairs to store.
333	///
334	/// # Behavior
335	/// - For each VTXO that does not exist: inserts the VTXO and its initial state.
336	/// - For each VTXO that already exists: no-op for that VTXO.
337	///
338	/// # Errors
339	/// - Returns an error if the storage operation fails.
340	async fn store_vtxos(
341		&self,
342		vtxos: &[(&Vtxo<Full>, &VtxoState)],
343	) -> anyhow::Result<()>;
344
345	/// Fetch a wallet [Vtxo] with its current state by ID.
346	///
347	/// Parameters:
348	/// - id: [VtxoId] to look up.
349	///
350	/// Returns:
351	/// - `Ok(Some(WalletVtxo))` if found,
352	/// - `Ok(None)` otherwise.
353	///
354	/// Errors:
355	/// - Returns an error if the lookup fails.
356	async fn get_wallet_vtxo(&self, id: VtxoId) -> anyhow::Result<Option<WalletVtxo>>;
357
358	/// Fetch all wallet VTXOs in the database.
359	///
360	/// Returns:
361	/// - `Ok(Vec<WalletVtxo>)` possibly empty.
362	///
363	/// Errors:
364	/// - Returns an error if the query fails.
365	async fn get_all_vtxos(&self) -> anyhow::Result<Vec<WalletVtxo>>;
366
367	/// Fetch all wallet VTXOs whose state matches any of the provided kinds.
368	///
369	/// Parameters:
370	/// - state: Slice of `VtxoStateKind` filters.
371	///
372	/// Returns:
373	/// - `Ok(Vec<WalletVtxo>)` possibly empty.
374	///
375	/// Errors:
376	/// - Returns an error if the query fails.
377	async fn get_vtxos_by_state(&self, state: &[VtxoStateKind]) -> anyhow::Result<Vec<WalletVtxo>>;
378
379	/// Fetch a single VTXO in full form (including the unilateral exit chain).
380	///
381	/// Listing/balance/selection paths return [WalletVtxo] (which holds
382	/// [Vtxo<ark::vtxo::Bare>]) to keep memory bounded. Operations that
383	/// genuinely need the genesis chain — unilateral exit, server
384	/// registration, arkoor send, offboard — should call this method
385	/// (or [BarkPersister::get_full_vtxos] for batches) on demand.
386	async fn get_full_vtxo(&self, id: VtxoId) -> anyhow::Result<Option<Vtxo<Full>>>;
387
388	/// Hydrate a batch of VTXOs into their full form, preserving the order
389	/// of the input slice. Returns an error if any id is missing — callers
390	/// reach this from a selection step against the wallet's listings, so a
391	/// missing row indicates the wallet's state is inconsistent with the
392	/// caller's view.
393	async fn get_full_vtxos(&self, ids: &[VtxoId]) -> anyhow::Result<Vec<Vtxo<Full>>>;
394
395	/// Remove a [Vtxo] by ID.
396	///
397	/// Parameters:
398	/// - id: `VtxoId` to remove.
399	///
400	/// Returns:
401	/// - `Ok(Some(Vtxo))` with the removed [Vtxo] data if it existed,
402	/// - `Ok(None)` otherwise.
403	///
404	/// Errors:
405	/// - Returns an error if the delete operation fails.
406	async fn remove_vtxo(&self, id: VtxoId) -> anyhow::Result<Option<Vtxo<Full>>>;
407
408	/// Check whether a [Vtxo] is already marked spent.
409	///
410	/// Parameters:
411	/// - id: VtxoId to check.
412	///
413	/// Returns:
414	/// - `Ok(true)` if spent,
415	/// - `Ok(false)` if not found or not spent.
416	///
417	/// Errors:
418	/// - Returns an error if the lookup fails.
419	async fn has_spent_vtxo(&self, id: VtxoId) -> anyhow::Result<bool>;
420
421	/// Store a newly derived/assigned [Vtxo] public key index mapping.
422	///
423	/// Parameters:
424	/// - index: Derivation index.
425	/// - public_key: PublicKey at that index.
426	///
427	/// Errors:
428	/// - Returns an error if the mapping cannot be stored.
429	async fn store_vtxo_key(&self, index: u32, public_key: PublicKey) -> anyhow::Result<()>;
430
431	/// Get the last revealed/used [Vtxo] key index.
432	///
433	/// Returns:
434	/// - `Ok(Some(u32))` if a key was stored
435	/// - `Ok(None)` otherwise.
436	///
437	/// Errors:
438	/// - Returns an error if the query fails.
439	async fn get_last_vtxo_key_index(&self) -> anyhow::Result<Option<u32>>;
440
441	/// Retrieves the derivation index of the provided [PublicKey] from the database
442	///
443	/// Returns:
444	/// - `Ok(Some(u32))` if the key was stored.
445	/// - `Ok(None)` if the key was not stored.
446	///
447	/// Errors:
448	/// - Returns an error if the query fails.
449	async fn get_public_key_idx(&self, public_key: &PublicKey) -> anyhow::Result<Option<u32>>;
450
451	/// Retrieves the mailbox checkpoint from the database
452	///
453	/// Returns:
454	/// - `Ok(u64)` the stored checkpoint.
455	///
456	/// Errors:
457	/// - Returns an error if the query fails.
458	async fn get_mailbox_checkpoint(&self) -> anyhow::Result<u64>;
459
460	/// Update the mailbox checkpoint to the new checkpoint
461	///
462	/// Returns:
463	///
464	///
465	/// Errors:
466	/// - Returns error when the query fails
467	/// - Returns error when the provided checkpoint is smaller than the existing checkpoint
468	async fn store_mailbox_checkpoint(&self, checkpoint: u64) -> anyhow::Result<()>;
469
470	/// Store a new pending lightning send.
471	///
472	/// Parameters:
473	/// - invoice: The invoice of the pending lightning send.
474	/// - amount: The amount of the pending lightning send.
475	/// - fee: The fee of the pending lightning send.
476	/// - vtxos: The vtxos of the pending lightning send.
477	/// - movement_id: The movement ID associated with this send.
478	///
479	/// Errors:
480	/// - Returns an error if the pending lightning send cannot be stored.
481	async fn store_new_pending_lightning_send(
482		&self,
483		invoice: &Invoice,
484		amount: Amount,
485		fee: Amount,
486		vtxos: &[VtxoId],
487		movement_id: MovementId,
488	) -> anyhow::Result<LightningSend>;
489
490	/// Get all pending lightning sends.
491	///
492	/// Returns:
493	/// - `Ok(Vec<LightningSend>)` possibly empty.
494	///
495	/// Errors:
496	/// - Returns an error if the query fails.
497	async fn get_all_pending_lightning_send(&self) -> anyhow::Result<Vec<LightningSend>>;
498
499	/// Mark a lightning send as finished.
500	///
501	/// Parameters:
502	/// - payment_hash: The [PaymentHash] of the lightning send to update.
503	/// - preimage: The [Preimage] of the successful lightning send.
504	///
505	/// Errors:
506	/// - Returns an error if the lightning send cannot be updated.
507	async fn finish_lightning_send(
508		&self,
509		payment_hash: PaymentHash,
510		preimage: Option<Preimage>,
511	) -> anyhow::Result<()>;
512
513	/// Remove a lightning send.
514	///
515	/// Parameters:
516	/// - payment_hash: The [PaymentHash] of the lightning send to remove.
517	///
518	/// Errors:
519	/// - Returns an error if the lightning send cannot be removed.
520	async fn remove_lightning_send(&self, payment_hash: PaymentHash) -> anyhow::Result<()>;
521
522	/// Get a lightning send by payment hash
523	///
524	/// Parameters:
525	/// - payment_hash: The [PaymentHash] of the lightning send to get.
526	///
527	/// Errors:
528	/// - Returns an error if the lookup fails.
529	async fn get_lightning_send(&self, payment_hash: PaymentHash) -> anyhow::Result<Option<LightningSend>>;
530
531	/// Persist or overwrite a wallet action checkpoint.
532	///
533	/// Parameters:
534	/// - id: stable action identifier (e.g. payment hash hex for a lightning send).
535	/// - checkpoint: the payload to persist; replaces any existing row with the same id.
536	///
537	/// Errors:
538	/// - Returns an error if the write fails.
539	async fn upsert_wallet_action_checkpoint(
540		&self,
541		id: &WalletActionId,
542		checkpoint: &WalletActionCheckpoint,
543	) -> anyhow::Result<()>;
544
545	/// Fetch a wallet action checkpoint by id.
546	///
547	/// Returns:
548	/// - `Ok(Some(_))` if a row exists, `Ok(None)` otherwise.
549	///
550	/// Errors:
551	/// - Returns an error if the lookup or deserialization fails.
552	async fn get_wallet_action_checkpoint(
553		&self,
554		id: &WalletActionId,
555	) -> anyhow::Result<Option<WalletActionCheckpoint>>;
556
557	/// Fetch every persisted wallet action checkpoint, oldest first.
558	///
559	/// Used by the periodic sync to find work to re-drive.
560	async fn get_all_wallet_action_checkpoints(
561		&self,
562	) -> anyhow::Result<Vec<WalletActionCheckpoint>>;
563
564	/// Remove a wallet action checkpoint by id. No-op if absent.
565	async fn remove_wallet_action_checkpoint(
566		&self,
567		id: &WalletActionId,
568	) -> anyhow::Result<()>;
569
570	/// Store an incoming Lightning receive record.
571	///
572	/// Parameters:
573	/// - payment_hash: Unique payment hash.
574	/// - preimage: Payment preimage (kept until disclosure).
575	/// - invoice: The associated BOLT11 invoice.
576	/// - htlc_recv_cltv_delta: The CLTV delta for the HTLC VTXO.
577	///
578	/// Errors:
579	/// - Returns an error if the receive cannot be stored.
580	async fn store_lightning_receive(
581		&self,
582		payment_hash: PaymentHash,
583		preimage: Preimage,
584		invoice: &Bolt11Invoice,
585		htlc_recv_cltv_delta: BlockDelta,
586	) -> anyhow::Result<()>;
587
588	/// Returns a list of all pending lightning receives
589	///
590	/// Returns:
591	/// - `Ok(Vec<LightningReceive>)` possibly empty.
592	///
593	/// Errors:
594	/// - Returns an error if the query fails.
595	async fn get_all_pending_lightning_receives(&self) -> anyhow::Result<Vec<LightningReceive>>;
596
597	/// Mark a Lightning receive preimage as revealed (e.g., after settlement).
598	///
599	/// Parameters:
600	/// - payment_hash: The payment hash identifying the receive.
601	///
602	/// Errors:
603	/// - Returns an error if the update fails or the receive does not exist.
604	async fn set_preimage_revealed(&self, payment_hash: PaymentHash) -> anyhow::Result<()>;
605
606	/// Set the VTXO IDs and [MovementId] for a [LightningReceive].
607	///
608	/// Parameters:
609	/// - payment_hash: The payment hash identifying the receive.
610	/// - htlc_vtxo_ids: The VTXO IDs to set.
611	/// - movement_id: The movement ID associated with the invoice.
612	///
613	/// Errors:
614	/// - Returns an error if the update fails or the receive does not exist.
615	async fn update_lightning_receive(
616		&self,
617		payment_hash: PaymentHash,
618		htlc_vtxo_ids: &[VtxoId],
619		movement_id: MovementId,
620	) -> anyhow::Result<()>;
621
622	/// Fetch a Lightning receive by its payment hash.
623	///
624	/// Parameters:
625	/// - payment_hash: The payment hash to look up.
626	///
627	/// Returns:
628	/// - `Ok(Some(LightningReceive))` if found,
629	/// - `Ok(None)` otherwise.
630	///
631	/// Errors:
632	/// - Returns an error if the lookup fails.
633	async fn fetch_lightning_receive_by_payment_hash(
634		&self,
635		payment_hash: PaymentHash,
636	) -> anyhow::Result<Option<LightningReceive>>;
637
638	/// Mark a Lightning receive as finished by its payment hash.
639	///
640	/// Parameters:
641	/// - payment_hash: The payment hash of the record to mark finished
642	///
643	/// Errors:
644	/// - Returns an error if the operation fails.
645	async fn finish_pending_lightning_receive(
646		&self,
647		payment_hash: PaymentHash,
648	) -> anyhow::Result<()>;
649
650	/// Store an entry indicating a [Vtxo] is being exited.
651	///
652	/// Parameters:
653	/// - exit: StoredExit describing the exit operation.
654	///
655	/// Errors:
656	/// - Returns an error if the entry cannot be stored.
657	async fn store_exit_vtxo_entry(&self, exit: &StoredExit) -> anyhow::Result<()>;
658
659	/// Remove an exit entry for a given [Vtxo] ID.
660	///
661	/// Parameters:
662	/// - id: VtxoId to remove from exit tracking.
663	///
664	/// Errors:
665	/// - Returns an error if the removal fails.
666	async fn remove_exit_vtxo_entry(&self, id: &VtxoId) -> anyhow::Result<()>;
667
668	/// List all VTXOs currently tracked as being exited.
669	///
670	/// Returns:
671	/// - `Ok(Vec<StoredExit>)` possibly empty.
672	///
673	/// Errors:
674	/// - Returns an error if the query fails.
675	async fn get_exit_vtxo_entries(&self) -> anyhow::Result<Vec<StoredExit>>;
676
677	/// Store a child transaction related to an exit transaction.
678	///
679	/// Parameters:
680	/// - exit_txid: The parent exit transaction ID.
681	/// - child_tx: The child bitcoin Transaction to store.
682	/// - origin: Metadata describing where the child came from (ExitTxOrigin).
683	///
684	/// Errors:
685	/// - Returns an error if the transaction cannot be stored.
686	async fn store_exit_child_tx(
687		&self,
688		exit_txid: Txid,
689		child_tx: &Transaction,
690		origin: ExitTxOrigin,
691	) -> anyhow::Result<()>;
692
693	/// Retrieve a stored child transaction for a given exit transaction ID.
694	///
695	/// Parameters:
696	/// - exit_txid: The parent exit transaction ID.
697	///
698	/// Returns:
699	/// - `Ok(Some((Transaction, ExitTxOrigin)))` if found,
700	/// - `Ok(None)` otherwise.
701	///
702	/// Errors:
703	/// - Returns an error if the lookup fails.
704	async fn get_exit_child_tx(
705		&self,
706		exit_txid: Txid,
707	) -> anyhow::Result<Option<(Transaction, ExitTxOrigin)>>;
708
709	/// Updates the state of the VTXO corresponding to the given [VtxoId], provided that their
710	/// current state is one of the given `allowed_states`.
711	///
712	/// # Parameters
713	/// - `vtxo_id`: The ID of the [Vtxo] to update.
714	/// - `state`: The new state to be set for the specified [Vtxo].
715	/// - `allowed_states`: An iterable collection of allowed states ([VtxoStateKind]) that the
716	///   [Vtxo] must currently be in for their state to be updated to the new `state`.
717	///
718	/// # Returns
719	/// - `Ok(WalletVtxo)` if the state update is successful.
720	/// - `Err(anyhow::Error)` if the VTXO fails to meet the required conditions,
721	///    or if another error occurs during the operation.
722	///
723	/// # Errors
724	/// - Returns an error if the current state is not within the `allowed_states`.
725	/// - Returns an error for any other issues encountered during the operation.
726	async fn update_vtxo_state_checked(
727		&self,
728		vtxo_id: VtxoId,
729		new_state: VtxoState,
730		allowed_old_states: &[VtxoStateKind],
731	) -> anyhow::Result<WalletVtxo>;
732
733	/// Store a pending offboard record.
734	///
735	/// Parameters:
736	/// - pending: The [PendingOffboard] to store.
737	///
738	/// Errors:
739	/// - Returns an error if the record cannot be stored.
740	async fn store_pending_offboard(
741		&self,
742		pending: &PendingOffboard,
743	) -> anyhow::Result<()>;
744
745	/// Get all pending offboard records.
746	///
747	/// Returns:
748	/// - `Ok(Vec<PendingOffboard>)` possibly empty.
749	///
750	/// Errors:
751	/// - Returns an error if the query fails.
752	async fn get_pending_offboards(&self) -> anyhow::Result<Vec<PendingOffboard>>;
753
754	/// Remove a pending offboard record by its [MovementId].
755	///
756	/// Parameters:
757	/// - movement_id: The [MovementId] to remove.
758	///
759	/// Errors:
760	/// - Returns an error if the record cannot be removed.
761	async fn remove_pending_offboard(&self, movement_id: MovementId) -> anyhow::Result<()>;
762}