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}