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