1mod sort;
40
41#[cfg(feature = "filestore")]
42pub mod filestore;
43pub mod memory;
44pub use self::sort::{SortKey, SortKeyBuilder};
45
46#[cfg(feature = "indexed-db")]
47pub mod indexed_db;
48
49use std::ops::RangeBounds;
50
51use anyhow::Context;
52use bitcoin::{Amount, Transaction, Txid};
53use bitcoin::secp256k1::PublicKey;
54use bitcoin::hashes::Hash;
55#[cfg(feature = "onchain-bdk")]
56use bdk_core::Merge;
57#[cfg(feature = "onchain-bdk")]
58use bdk_wallet::ChangeSet;
59use chrono::{DateTime, Local};
60use lightning_invoice::Bolt11Invoice;
61use serde::{de::DeserializeOwned, Serialize};
62
63use ark::lightning::{PaymentHash, Preimage};
64use ark::{Vtxo, VtxoId};
65use ark::vtxo::Full;
66
67use crate::actions::{WalletActionCheckpoint, WalletActionId};
68use crate::exit::{ExitStateKind, ExitTxOrigin};
69use crate::movement::{
70 Movement, MovementId, MovementStatus, MovementSubsystem, PaymentMethod,
71};
72use crate::movement::update::MovementUpdate;
73use crate::persist::BarkPersister;
74use crate::persist::models::{
75 PaidInvoice, RoundStateId, SerdeExitChildTx, SerdeRoundState, SerdeVtxo, SerdeVtxoKey,
76 SettledLightningReceive, StoredExit, StoredRoundState, Unlocked, wallet_vtxo_from_full,
77};
78use crate::round::RoundState;
79use crate::vtxo::{VtxoState, VtxoStateKind};
80use crate::{WalletProperties, WalletVtxo};
81
82
83pub mod partition {
84 pub const PROPERTIES: u8 = 0;
85 #[allow(unused)]
86 pub const BDK_CHANGESET: u8 = 1;
87 pub const VTXO: u8 = 2;
88 pub const PUBLIC_KEY: u8 = 3;
89 pub const PENDING_BOARD: u8 = 4;
90 pub const ROUND_STATE: u8 = 5;
91 pub const MOVEMENT: u8 = 6;
92 #[allow(unused)]
94 pub const LEGACY_LIGHTNING_SEND: u8 = 7;
95 #[allow(unused)]
99 pub const LEGACY_LIGHTNING_RECEIVE: u8 = 8;
100 pub const EXIT_VTXO: u8 = 9;
101 pub const EXIT_CHILD_TX: u8 = 10;
102 pub const MAILBOX_CHECKPOINT: u8 = 11;
103 #[allow(unused)]
106 pub const LEGACY_PENDING_OFFBOARD: u8 = 12;
107 pub const MOVEMENT_PAYMENT_METHOD: u8 = 13;
109 pub const WALLET_ACTION_CHECKPOINT: u8 = 14;
111 pub const PAID_INVOICE: u8 = 15;
114 pub const SETTLED_LIGHTNING_RECEIVE: u8 = 16;
117 pub const MOVEMENT_ACTION: u8 = 17;
120
121 pub const LAST_IDS: u8 = u8::MAX;
122}
123
124#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
126pub struct Record {
127 pub partition: u8,
131
132 pub pk: Vec<u8>,
134
135 pub sort_key: Option<SortKey>,
144
145 pub data: Vec<u8>,
147}
148
149impl Record {
150 fn to_data<T: DeserializeOwned>(&self) -> anyhow::Result<T> {
152 serde_json::from_slice(&self.data).map_err(Into::into)
153 }
154
155 fn from_data<T: Serialize>(
157 partition: u8,
158 pk: &[u8],
159 sort_key: Option<SortKey>,
160 data: &T,
161 ) -> anyhow::Result<Record> {
162 Ok(Record {
163 partition,
164 pk: pk.to_vec(),
165 sort_key,
166 data: serde_json::to_vec(data)?,
167 })
168 }
169}
170
171pub trait QueryRange: RangeBounds<SortKey> + Send {}
173
174impl<R: RangeBounds<SortKey> + Send> QueryRange for R {}
175
176#[derive(Debug, Clone)]
178pub struct Query<R: QueryRange> {
179 pub partition: u8,
181
182 pub range: R,
184
185 pub limit: Option<usize>,
187}
188
189impl<R: QueryRange> Query<R> {
190 pub fn new(partition: u8, range: R) -> Self {
192 Self {
193 partition,
194 range,
195 limit: None,
196 }
197 }
198
199 pub fn limit(mut self, limit: usize) -> Self {
204 self.limit = Some(limit);
205 self
206 }
207}
208
209impl Query<std::ops::RangeFull> {
210 pub fn new_full_range(partition: u8) -> Self {
211 Self::new(partition, ..)
212 }
213}
214
215fn serialize_payment_method(pm: &PaymentMethod) -> Vec<u8> {
216 let body = pm.value_string();
217
218 let mut buf = Vec::with_capacity(pm.type_str().len() + 1 + body.len());
219 buf.extend(pm.type_str().as_bytes().iter().copied());
220 buf.push(0xfe);
223 buf.extend(body.into_bytes());
224 buf
225}
226
227async fn write_movement_records<S: StorageAdaptor>(
231 guard: &mut S,
232 movement: &Movement,
233) -> anyhow::Result<()> {
234 let record = Record::from_data(
235 partition::MOVEMENT,
236 &movement.id.to_bytes(),
237 Some(sort::movement_sort_key(&movement.time.created_at)),
238 movement,
239 )?;
240 guard.put(record).await?;
241
242 let sent = movement.sent_to.iter().map(|d| &d.destination);
244 let rcvd = movement.received_on.iter().map(|d| &d.destination);
245 for pm in sent.chain(rcvd) {
246 let pm_bytes = serialize_payment_method(pm);
247 let primary_key = {
248 let mut buf = Vec::with_capacity(pm_bytes.len() + 4);
250 buf.extend(pm_bytes.iter().copied());
251 buf.extend(movement.id.to_bytes());
252 buf
253 };
254 let record = Record::from_data(
255 partition::MOVEMENT_PAYMENT_METHOD,
256 &primary_key,
257 Some(SortKey::from_bytes(pm_bytes)),
258 &movement.id.0,
259 )?;
260 guard.put(record).await?;
261 }
262 Ok(())
263}
264
265#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
312#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
313pub trait StorageAdaptor: Send + Sync + 'static {
314 async fn put(&mut self, record: Record) -> anyhow::Result<()>;
316
317 async fn get(&self, partition: u8, pk: &[u8]) -> anyhow::Result<Option<Record>>;
321
322 async fn delete(&mut self, partition: u8, pk: &[u8]) -> anyhow::Result<Option<Record>>;
326
327 async fn query_sorted<R: QueryRange>(&self, query: Query<R>)
333 -> anyhow::Result<Vec<Record>>;
334
335 async fn get_all(&self, partition: u8) -> anyhow::Result<Vec<Record>>;
339
340 async fn incremental_id(&mut self, partition: u8) -> anyhow::Result<u32> {
342 let last_partition_id = self.get(partition::LAST_IDS, &[partition]).await?
343 .map(|r| r.to_data::<u32>()).unwrap_or(Ok(0))?;
344 let next_partition_id = last_partition_id + 1;
345
346 let record = Record::from_data(
347 partition::LAST_IDS,
348 &[partition],
349 None,
350 &next_partition_id,
351 )?;
352
353 self.put(record).await?;
354 Ok(next_partition_id)
355 }
356}
357
358async fn get_vtxo<S: StorageAdaptor>(adaptor: &S, id: VtxoId) -> anyhow::Result<Option<SerdeVtxo>> {
359 match adaptor.get(partition::VTXO, &id.to_bytes()).await? {
360 Some(record) => Ok(Some(record.to_data::<SerdeVtxo>()?)),
361 None => Ok(None),
362 }
363}
364
365async fn get_check_vtxo_state<S: StorageAdaptor>(
366 adaptor: &S,
367 vtxo_id: VtxoId,
368 allowed_states: &[VtxoStateKind],
369) -> anyhow::Result<SerdeVtxo> {
370 let vtxo = get_vtxo(adaptor, vtxo_id).await?
371 .context("vtxo not found")?;
372
373 let current_state = vtxo.current_state().context("vtxo has no state")?;
374 if !allowed_states.contains(¤t_state.kind()) {
375 bail!("current state {:?} not in allowed states {:?}",
376 current_state.kind(), allowed_states
377 );
378 }
379
380 Ok(vtxo)
381}
382
383async fn update_vtxo_state_checked<S: StorageAdaptor>(
384 adaptor: &mut S,
385 vtxo_id: VtxoId,
386 new_state: VtxoState,
387 allowed_old_states: &[VtxoStateKind],
388) -> anyhow::Result<WalletVtxo> {
389 let mut serde_vtxo = get_check_vtxo_state(adaptor, vtxo_id, allowed_old_states).await?;
390
391 let sk = sort::vtxo_sort_key(
392 new_state.kind(), serde_vtxo.vtxo.expiry_height(), serde_vtxo.vtxo.amount()
393 );
394
395 serde_vtxo.states.push(new_state.clone());
396 let updated_record = Record::from_data(
397 partition::VTXO,
398 &vtxo_id.to_bytes(),
399 Some(sk),
400 &serde_vtxo,
401 )?;
402
403 adaptor.put(updated_record).await?;
404
405 Ok(wallet_vtxo_from_full(&serde_vtxo.vtxo, new_state, serde_vtxo.registered))
406}
407
408pub struct StorageAdaptorWrapper<S: StorageAdaptor> {
409 inner: tokio::sync::RwLock<S>,
410}
411
412impl<S: StorageAdaptor> StorageAdaptorWrapper<S> {
413 pub fn new(inner: S) -> Self {
414 Self {
415 inner: tokio::sync::RwLock::new(inner),
416 }
417 }
418}
419
420#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
422#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
423impl <S: StorageAdaptor> BarkPersister for StorageAdaptorWrapper<S> {
424 async fn init_wallet(&self, properties: &WalletProperties) -> anyhow::Result<()> {
425 let record = Record::from_data(
426 partition::PROPERTIES,
427 &[],
429 None,
430 properties,
431 )?;
432 self.inner.write().await.put(record).await
433 }
434
435 async fn read_properties(&self) -> anyhow::Result<Option<WalletProperties>> {
436 match self.inner.read().await.get(partition::PROPERTIES, &[]).await? {
437 Some(record) => Ok(Some(record.to_data()?)),
438 None => Ok(None),
439 }
440 }
441
442 async fn set_server_pubkey(&self, server_pubkey: PublicKey) -> anyhow::Result<()> {
443 let mut properties = match self.read_properties().await? {
444 Some(properties) => properties,
445 None => bail!("wallet not initialized"),
446 };
447
448 properties.server_pubkey = Some(server_pubkey);
449
450 let record = Record::from_data(partition::PROPERTIES, &[], None, &properties)?;
451 self.inner.write().await.put(record).await
452 }
453
454 async fn set_server_mailbox_pubkey(&self, server_mailbox_pubkey: PublicKey) -> anyhow::Result<()> {
455 let mut properties = match self.read_properties().await? {
456 Some(properties) => properties,
457 None => bail!("wallet not initialized"),
458 };
459
460 properties.server_mailbox_pubkey = Some(server_mailbox_pubkey);
461
462 let record = Record::from_data(partition::PROPERTIES, &[], None, &properties)?;
463 self.inner.write().await.put(record).await
464 }
465
466 #[cfg(feature = "onchain-bdk")]
467 async fn initialize_bdk_wallet(&self) -> anyhow::Result<ChangeSet> {
468 match self.inner.read().await.get(partition::BDK_CHANGESET, &[]).await? {
469 Some(record) => record.to_data(),
470 None => Ok(ChangeSet::default()),
471 }
472 }
473
474 #[cfg(feature = "onchain-bdk")]
475 async fn store_bdk_wallet_changeset(&self, changeset: &ChangeSet) -> anyhow::Result<()> {
476 let mut current = self.initialize_bdk_wallet().await?;
477 current.merge(changeset.clone());
478
479 let record = Record::from_data(
480 partition::BDK_CHANGESET,
481 &[],
483 None,
484 ¤t,
485 )?;
486 self.inner.write().await.put(record).await
487 }
488
489 async fn create_new_movement(
490 &self,
491 status: MovementStatus,
492 subsystem: &MovementSubsystem,
493 time: DateTime<Local>,
494 action_id: Option<&str>,
495 ) -> anyhow::Result<MovementId> {
496 let mut lock = self.inner.write().await;
497
498 let id = MovementId(lock.incremental_id(partition::MOVEMENT).await?);
499 let movement = Movement::new(id, status, subsystem, time);
500
501 let record = Record::from_data(
502 partition::MOVEMENT,
503 &id.to_bytes(),
504 Some(sort::movement_sort_key(&time)),
505 &movement,
506 )?;
507 lock.put(record).await?;
508
509 if let Some(action_id) = action_id {
511 let idx = Record::from_data(
512 partition::MOVEMENT_ACTION,
513 action_id.as_bytes(),
514 None,
515 &id.0,
516 )?;
517 lock.put(idx).await?;
518 }
519
520 Ok(id)
521 }
522
523 async fn get_or_create_movement_for_action(
524 &self,
525 subsystem: &MovementSubsystem,
526 time: DateTime<Local>,
527 action_id: &str,
528 update: MovementUpdate,
529 ) -> anyhow::Result<(MovementId, bool)> {
530 let mut guard = self.inner.write().await;
531
532 if let Some(rec) = guard.get(partition::MOVEMENT_ACTION, action_id.as_bytes()).await? {
534 let id = MovementId::new(
535 rec.to_data::<u32>().context("corrupt db: movement action index value")?);
536 return Ok((id, false));
537 }
538
539 let id = MovementId(guard.incremental_id(partition::MOVEMENT).await?);
540 let mut movement = Movement::new(id, MovementStatus::Pending, subsystem, time);
541 update.apply_to(&mut movement, time);
542 write_movement_records(&mut *guard, &movement).await?;
543
544 let idx = Record::from_data(
546 partition::MOVEMENT_ACTION,
547 action_id.as_bytes(),
548 None,
549 &id.0,
550 )?;
551 guard.put(idx).await?;
552
553 Ok((id, true))
554 }
555
556 async fn update_movement(&self, movement: &Movement) -> anyhow::Result<()> {
557 let mut guard = self.inner.write().await;
558 write_movement_records(&mut *guard, movement).await
559 }
560
561 async fn get_movement_by_id(&self, movement_id: MovementId) -> anyhow::Result<Movement> {
562 self.inner.read().await.get(partition::MOVEMENT, &movement_id.to_bytes())
563 .await?
564 .context("movement not found")?
565 .to_data()
566 }
567
568 async fn get_all_movements(&self) -> anyhow::Result<Vec<Movement>> {
569 let records = self.inner.read().await
570 .query_sorted(Query::new_full_range(partition::MOVEMENT)).await?;
571 records.into_iter().map(|r| r.to_data()).collect()
572 }
573
574 async fn get_movements_by_payment_method(
575 &self,
576 payment_method: &PaymentMethod,
577 ) -> anyhow::Result<Vec<Movement>> {
578 let pm_bytes = serialize_payment_method(payment_method);
579 let sort_key = SortKey::from_bytes(pm_bytes);
580
581 let guard = self.inner.read().await;
582 let idx_recs = guard.query_sorted(Query::new(
583 partition::MOVEMENT_PAYMENT_METHOD,
584 sort_key.clone()..=sort_key,
585 )).await?;
586
587 let mut ret = Vec::with_capacity(idx_recs.len());
588 for idx_rec in idx_recs {
589 let id = MovementId::new(idx_rec.to_data::<u32>()
590 .context("corrupt db: movement payment method index value")?);
591
592 ret.push(
593 guard.get(partition::MOVEMENT, &id.to_bytes()).await?
594 .context("corrupt db: movement payment method entry for unknown movement")?
595 .to_data()
596 .context("corrupt db: invalid movement record")?
597 );
598 }
599 Ok(ret)
600 }
601
602 async fn store_round_state(&self, round_state: &RoundState) -> anyhow::Result<RoundStateId> {
603 let mut lock = self.inner.write().await;
604
605 let id = RoundStateId(lock.incremental_id(partition::ROUND_STATE).await?);
606 let serde_state = SerdeRoundState::from(round_state);
607 let record = Record::from_data(
608 partition::ROUND_STATE,
609 &id.to_bytes(),
610 Some(sort::SortKey::u32_asc(id.0)),
611 &serde_state,
612 )?;
613 lock.put(record).await?;
614
615 Ok(id)
616 }
617
618 async fn update_round_state(&self, round_state: &StoredRoundState) -> anyhow::Result<()> {
619 let serde_state = SerdeRoundState::from(round_state.state());
620 let record = Record::from_data(
621 partition::ROUND_STATE,
622 &round_state.id().to_bytes(),
623 Some(sort::SortKey::u32_asc(round_state.id().0)),
624 &serde_state,
625 )?;
626 self.inner.write().await.put(record).await
627 }
628
629 async fn remove_round_state(&self, round_state: &StoredRoundState) -> anyhow::Result<()> {
630 self.inner.write().await
631 .delete(partition::ROUND_STATE, &round_state.id().to_bytes()).await?;
632 Ok(())
633 }
634
635 async fn get_round_state_by_id(&self, _id: RoundStateId) -> anyhow::Result<Option<StoredRoundState<Unlocked>>> {
636 let record = self.inner.read().await
637 .get(partition::ROUND_STATE, &_id.to_bytes()).await?;
638 match record {
639 Some(r) => {
640 let pk_slice: [u8; 4] = r.pk[..4].try_into().expect("4 bytes shouldn't fail");
641 let id = RoundStateId(u32::from_be_bytes(pk_slice));
642 let state = r.to_data::<SerdeRoundState>()?.into();
643 Ok(Some(StoredRoundState::new(id, state)))
644 },
645 None => Ok(None),
646 }
647 }
648
649 async fn get_pending_round_state_ids(&self) -> anyhow::Result<Vec<RoundStateId>> {
650 let records = self.inner.read().await
651 .get_all(partition::ROUND_STATE).await?;
652 records.into_iter()
653 .map(|r| {
654 let pk_slice: [u8; 4] = r.pk[..4].try_into().expect("4 bytes shouldn't fail");
655 Ok(RoundStateId(u32::from_be_bytes(pk_slice)))
656 })
657 .collect()
658 }
659
660 async fn store_vtxos(&self, vtxos: &[(&Vtxo<Full>, &VtxoState)]) -> anyhow::Result<()> {
661 let mut lock = self.inner.write().await;
662
663 for (vtxo, state) in vtxos {
664 let serde_vtxo = SerdeVtxo {
665 vtxo: (*vtxo).clone(),
666 states: vec![(*state).clone()],
667 registered: false,
668 };
669
670 let sk = sort::vtxo_sort_key(
671 state.kind(), vtxo.expiry_height(), vtxo.amount(),
672 );
673 let record = Record::from_data(
674 partition::VTXO,
675 &vtxo.id().to_bytes(),
676 Some(sk),
677 &serde_vtxo,
678 )?;
679 lock.put(record).await?;
680 }
681 Ok(())
682 }
683
684 async fn get_wallet_vtxo(&self, id: VtxoId) -> anyhow::Result<Option<WalletVtxo>> {
685 let lock = self.inner.read().await;
686 match get_vtxo(&*lock, id).await? {
687 Some(serde_vtxo) => {
688 let state = serde_vtxo.current_state()
689 .context("vtxo has no state")?.clone();
690 Ok(Some(wallet_vtxo_from_full(
691 &serde_vtxo.vtxo, state, serde_vtxo.registered,
692 )))
693 },
694 None => Ok(None),
695 }
696 }
697
698 async fn get_wallet_vtxos(&self, ids: &[VtxoId]) -> anyhow::Result<Vec<WalletVtxo>> {
699 let lock = self.inner.read().await;
700 let mut out = Vec::with_capacity(ids.len());
701 for id in ids {
702 let serde_vtxo = get_vtxo(&*lock, *id).await?
703 .with_context(|| format!("vtxo {id} not found"))?;
704 let state = serde_vtxo.current_state()
705 .context("vtxo has no state")?.clone();
706 out.push(wallet_vtxo_from_full(
707 &serde_vtxo.vtxo, state, serde_vtxo.registered,
708 ));
709 }
710 Ok(out)
711 }
712
713 async fn get_all_vtxos(&self) -> anyhow::Result<Vec<WalletVtxo>> {
714 let records = self.inner.read().await
715 .query_sorted(Query::new_full_range(partition::VTXO)).await?;
716
717 records
718 .into_iter()
719 .map(|r| {
720 let serde_vtxo = r.to_data::<SerdeVtxo>()?;
721 let state = serde_vtxo
722 .current_state()
723 .cloned()
724 .context("vtxo has no state")?;
725 Ok(wallet_vtxo_from_full(
726 &serde_vtxo.vtxo, state, serde_vtxo.registered,
727 ))
728 })
729 .collect()
730 }
731
732 async fn get_vtxos_by_state(
733 &self,
734 states: &[VtxoStateKind],
735 ) -> anyhow::Result<Vec<WalletVtxo>> {
736 let lock = self.inner.read().await;
737
738 let range = |state: VtxoStateKind| {
739 let start = sort::vtxo_sort_key(state, u32::MIN, Amount::ZERO);
740 let end = sort::vtxo_sort_key(state, u32::MAX, Amount::MAX);
741 (start, end)
742 };
743
744 let mut records = Vec::new();
745 for state in states {
746 let (start, end) = range(*state);
747 let query = Query::new(partition::VTXO, start..=end);
748
749 for record in lock.query_sorted(query).await? {
750 let serde_vtxo = record.to_data::<SerdeVtxo>()?;
751 let current_state = serde_vtxo.current_state()
752 .context("vtxo has no current state")?.clone();
753 debug_assert_eq!(current_state.kind(), *state);
754 records.push(wallet_vtxo_from_full(
755 &serde_vtxo.vtxo, current_state, serde_vtxo.registered,
756 ));
757 }
758 }
759
760 records.sort_by(|a, b| {
764 a.vtxo.expiry_height().cmp(&b.vtxo.expiry_height())
765 .then(b.vtxo.amount().cmp(&a.vtxo.amount()))
766 });
767
768 Ok(records)
769 }
770
771 async fn get_full_vtxo(&self, id: VtxoId) -> anyhow::Result<Option<Vtxo<Full>>> {
772 let lock = self.inner.read().await;
773 Ok(get_vtxo(&*lock, id).await?.map(|s| s.vtxo))
774 }
775
776 async fn get_full_vtxos(&self, ids: &[VtxoId]) -> anyhow::Result<Vec<Vtxo<Full>>> {
777 let lock = self.inner.read().await;
778 let mut out = Vec::with_capacity(ids.len());
779 for id in ids {
780 let serde_vtxo = get_vtxo(&*lock, *id).await?
781 .with_context(|| format!("vtxo {id} not found"))?;
782 out.push(serde_vtxo.vtxo);
783 }
784 Ok(out)
785 }
786
787 async fn remove_vtxo(&self, id: VtxoId) -> anyhow::Result<Option<Vtxo<Full>>> {
788 match self.inner.write().await.delete(partition::VTXO, &id.to_bytes()).await? {
789 Some(record) => Ok(Some(record.to_data::<SerdeVtxo>()?.vtxo)),
790 None => Ok(None),
791 }
792 }
793
794 async fn has_spent_vtxo(&self, id: VtxoId) -> anyhow::Result<bool> {
795 match self.get_wallet_vtxo(id).await? {
796 Some(vtxo) => Ok(vtxo.state.kind() == VtxoStateKind::Spent),
797 None => Ok(false),
798 }
799 }
800
801 async fn update_vtxo_state_checked(
802 &self,
803 vtxo_id: VtxoId,
804 new_state: VtxoState,
805 allowed_old_states: &[VtxoStateKind],
806 ) -> anyhow::Result<WalletVtxo> {
807 let mut lock = self.inner.write().await;
808 update_vtxo_state_checked(&mut *lock, vtxo_id, new_state, allowed_old_states).await
809 }
810
811 async fn update_vtxo_states_checked(
812 &self,
813 vtxo_ids: &[VtxoId],
814 new_state: VtxoState,
815 allowed_old_states: &[VtxoStateKind],
816 ) -> anyhow::Result<()> {
817 let mut lock = self.inner.write().await;
818 for id in vtxo_ids {
824 get_check_vtxo_state(&*lock, *id, allowed_old_states).await?;
825 }
826 for id in vtxo_ids {
827 update_vtxo_state_checked(&mut *lock, *id, new_state.clone(), allowed_old_states).await?;
828 }
829 Ok(())
830 }
831
832 async fn mark_vtxos_registered(&self, vtxo_ids: &[VtxoId]) -> anyhow::Result<()> {
833 let mut lock = self.inner.write().await;
834 for id in vtxo_ids {
835 let mut serde_vtxo = get_vtxo(&*lock, *id).await?
836 .with_context(|| format!("vtxo {id} not found"))?;
837 if serde_vtxo.registered {
838 continue;
839 }
840 serde_vtxo.registered = true;
841
842 let state = serde_vtxo.current_state().context("vtxo has no state")?;
845 let sk = sort::vtxo_sort_key(
846 state.kind(), serde_vtxo.vtxo.expiry_height(), serde_vtxo.vtxo.amount(),
847 );
848 let record = Record::from_data(
849 partition::VTXO,
850 &id.to_bytes(),
851 Some(sk),
852 &serde_vtxo,
853 )?;
854 lock.put(record).await?;
855 }
856 Ok(())
857 }
858
859 async fn get_unregistered_vtxo_ids(&self) -> anyhow::Result<Vec<VtxoId>> {
860 let records = self.inner.read().await
861 .query_sorted(Query::new_full_range(partition::VTXO)).await?;
862
863 let mut ids = Vec::new();
864 for record in records {
865 let serde_vtxo = record.to_data::<SerdeVtxo>()?;
866 if serde_vtxo.registered {
867 continue;
868 }
869 let state = serde_vtxo.current_state()
870 .context("vtxo has no state")?;
871 if state.kind() != VtxoStateKind::Spent {
872 ids.push(serde_vtxo.vtxo.id());
873 }
874 }
875 Ok(ids)
876 }
877
878 async fn store_vtxo_key(&self, index: u32, public_key: PublicKey) -> anyhow::Result<()> {
879 let vtxo_key = SerdeVtxoKey { index, public_key };
880 let record = Record::from_data(
881 partition::PUBLIC_KEY,
882 &public_key.serialize()[..],
883 Some(sort::SortKey::u64_desc(index as u64)),
884 &vtxo_key,
885 )?;
886 self.inner.write().await.put(record).await
887 }
888
889 async fn get_last_vtxo_key_index(&self) -> anyhow::Result<Option<u32>> {
890 let query = Query::new_full_range(partition::PUBLIC_KEY).limit(1);
892 let records = self.inner.read().await.query_sorted(query).await?;
893
894 match records.into_iter().next() {
895 Some(record) => {
896 let vtxo_key = record.to_data::<SerdeVtxoKey>()?;
897 Ok(Some(vtxo_key.index))
898 }
899 None => Ok(None),
900 }
901 }
902
903 async fn get_public_key_idx(&self, public_key: &PublicKey) -> anyhow::Result<Option<u32>> {
904 match self.inner.read().await
905 .get(partition::PUBLIC_KEY, &public_key.serialize()[..]).await?
906 {
907 Some(record) => {
908 let vtxo_key = record.to_data::<SerdeVtxoKey>()?;
909 Ok(Some(vtxo_key.index))
910 }
911 None => Ok(None),
912 }
913 }
914
915 async fn get_mailbox_checkpoint(&self) -> anyhow::Result<u64> {
916 match self.inner.read().await
917 .get(partition::MAILBOX_CHECKPOINT, &[]).await?
918 {
919 Some(record) => Ok(record.to_data::<u64>()?),
920 None => Ok(0),
921 }
922 }
923
924 async fn store_mailbox_checkpoint(&self, checkpoint: u64) -> anyhow::Result<()> {
925 let mut lock = self.inner.write().await;
926 let record = Record::from_data(
927 partition::MAILBOX_CHECKPOINT,
928 &[],
929 None,
930 &checkpoint,
931 )?;
932 lock.put(record).await?;
933 Ok(())
934 }
935
936 async fn upsert_wallet_action_checkpoint(
937 &self,
938 id: &WalletActionId,
939 checkpoint: &WalletActionCheckpoint,
940 ) -> anyhow::Result<()> {
941 let record = Record::from_data(
942 partition::WALLET_ACTION_CHECKPOINT,
943 id.as_bytes(),
944 None,
945 checkpoint,
946 )?;
947 self.inner.write().await.put(record).await
948 }
949
950 async fn get_wallet_action_checkpoint(
951 &self,
952 id: &WalletActionId,
953 ) -> anyhow::Result<Option<WalletActionCheckpoint>> {
954 match self.inner.read().await
955 .get(partition::WALLET_ACTION_CHECKPOINT, id.as_bytes()).await?
956 {
957 Some(record) => Ok(Some(record.to_data()?)),
958 None => Ok(None),
959 }
960 }
961
962 async fn get_all_wallet_action_checkpoints(
963 &self,
964 ) -> anyhow::Result<Vec<WalletActionCheckpoint>> {
965 let records = self.inner.read().await
966 .get_all(partition::WALLET_ACTION_CHECKPOINT).await?;
967 records.into_iter().map(|r| r.to_data()).collect()
968 }
969
970 async fn remove_wallet_action_checkpoint(
971 &self,
972 id: &WalletActionId,
973 ) -> anyhow::Result<()> {
974 self.inner.write().await
975 .delete(partition::WALLET_ACTION_CHECKPOINT, id.as_bytes()).await?;
976 Ok(())
977 }
978
979 async fn record_paid_invoice(
980 &self,
981 payment_hash: PaymentHash,
982 preimage: Preimage,
983 ) -> anyhow::Result<()> {
984 let key = payment_hash.to_byte_array();
985 let mut lock = self.inner.write().await;
987 if lock.get(partition::PAID_INVOICE, &key).await?.is_some() {
988 return Ok(());
989 }
990 let paid = PaidInvoice {
991 payment_hash,
992 preimage,
993 paid_at: chrono::Local::now(),
994 };
995 let record = Record::from_data(partition::PAID_INVOICE, &key, None, &paid)?;
996 lock.put(record).await
997 }
998
999 async fn get_paid_invoice(
1000 &self,
1001 payment_hash: PaymentHash,
1002 ) -> anyhow::Result<Option<PaidInvoice>> {
1003 match self.inner.read().await
1004 .get(partition::PAID_INVOICE, &payment_hash.to_byte_array()).await?
1005 {
1006 Some(record) => Ok(Some(record.to_data()?)),
1007 None => Ok(None),
1008 }
1009 }
1010
1011 async fn record_settled_lightning_receive(
1012 &self,
1013 payment_hash: PaymentHash,
1014 preimage: Preimage,
1015 invoice: &Bolt11Invoice,
1016 amount: Amount,
1017 ) -> anyhow::Result<()> {
1018 let key = payment_hash.to_byte_array();
1019
1020 let mut lock = self.inner.write().await;
1021 if lock.get(partition::SETTLED_LIGHTNING_RECEIVE, &key).await?.is_some() {
1022 return Ok(());
1023 }
1024 let settled = SettledLightningReceive {
1025 payment_hash,
1026 preimage,
1027 invoice: invoice.clone(),
1028 amount,
1029 settled_at: chrono::Local::now(),
1030 };
1031 let record = Record::from_data(partition::SETTLED_LIGHTNING_RECEIVE, &key, None, &settled)?;
1032 lock.put(record).await
1033 }
1034
1035 async fn get_settled_lightning_receive(
1036 &self,
1037 payment_hash: PaymentHash,
1038 ) -> anyhow::Result<Option<SettledLightningReceive>> {
1039 match self.inner.read().await
1040 .get(partition::SETTLED_LIGHTNING_RECEIVE, &payment_hash.to_byte_array()).await?
1041 {
1042 Some(record) => Ok(Some(record.to_data()?)),
1043 None => Ok(None),
1044 }
1045 }
1046
1047
1048 async fn store_exit_vtxo_entry(&self, exit: &StoredExit) -> anyhow::Result<()> {
1049 let record = Record::from_data(
1050 partition::EXIT_VTXO,
1051 &exit.vtxo_id.to_bytes(),
1052 None,
1053 exit,
1054 )?;
1055 self.inner.write().await.put(record).await
1056 }
1057
1058 async fn remove_exit_vtxo_entry(&self, id: &VtxoId) -> anyhow::Result<()> {
1059 self.inner.write().await.delete(partition::EXIT_VTXO, &id.to_bytes()).await?;
1060 Ok(())
1061 }
1062
1063 async fn get_exit_vtxo_entries(&self) -> anyhow::Result<Vec<StoredExit>> {
1064 let records = self.inner.read().await.get_all(partition::EXIT_VTXO).await?;
1065 records.into_iter().map(|r| r.to_data()).collect()
1066 }
1067
1068 async fn get_exit_vtxo_entries_with_states(
1069 &self,
1070 states: &[ExitStateKind],
1071 ) -> anyhow::Result<Vec<StoredExit>> {
1072 let records = self.inner.read().await.get_all(partition::EXIT_VTXO).await?;
1075 records.into_iter()
1076 .map(|r| r.to_data::<StoredExit>())
1077 .filter(|e| e.as_ref().map_or(true, |e| states.contains(&e.state.kind())))
1078 .collect()
1079 }
1080
1081 async fn get_exit_vtxo_entry(&self, id: &VtxoId) -> anyhow::Result<Option<StoredExit>> {
1082 match self.inner.read().await.get(partition::EXIT_VTXO, &id.to_bytes()).await? {
1083 Some(record) => Ok(Some(record.to_data::<StoredExit>()?)),
1084 None => Ok(None),
1085 }
1086 }
1087
1088 async fn store_exit_child_tx(
1089 &self,
1090 exit_txid: Txid,
1091 child_tx: &Transaction,
1092 origin: ExitTxOrigin,
1093 ) -> anyhow::Result<()> {
1094 let exit_child = SerdeExitChildTx {
1095 child_tx: child_tx.clone(),
1096 origin,
1097 };
1098 let record = Record::from_data(
1099 partition::EXIT_CHILD_TX,
1100 &exit_txid.to_byte_array(),
1101 None,
1102 &exit_child,
1103 )?;
1104 self.inner.write().await.put(record).await
1105 }
1106
1107 async fn get_exit_child_tx(
1108 &self,
1109 exit_txid: Txid,
1110 ) -> anyhow::Result<Option<(Transaction, ExitTxOrigin)>> {
1111 match self.inner.read().await
1112 .get(partition::EXIT_CHILD_TX, &exit_txid.to_byte_array()).await?
1113 {
1114 Some(record) => {
1115 let exit_child = record.to_data::<SerdeExitChildTx>()?;
1116 Ok(Some((exit_child.child_tx, exit_child.origin)))
1117 }
1118 None => Ok(None),
1119 }
1120 }
1121}
1122
1123#[cfg(test)]
1124mod tests {
1125 use super::*;
1126
1127 #[test]
1128 fn storage_query_builder() {
1129 let query = Query::new_full_range(0).limit(10);
1130
1131 assert_eq!(query.partition, 0);
1132 assert_eq!(query.limit, Some(10));
1133 assert_eq!(query.range, ..);
1134 }
1135}
1136
1137#[cfg(test)]
1152pub mod test_suite {
1153 use super::*;
1154 use super::partition::LAST_IDS;
1155 use super::sort::SortKey;
1156
1157 async fn clear_partitions<S: StorageAdaptor>(storage: &mut S, partitions: &[u8]) -> anyhow::Result<()> {
1158 for partition in partitions {
1159 let records = storage.get_all(*partition).await?;
1160 for record in records {
1161 storage.delete(record.partition, &record.pk).await?;
1162 }
1163 }
1164 Ok(())
1165 }
1166
1167 pub async fn run_all<S: StorageAdaptor>(storage: &mut S) {
1169 test_put_insert(storage).await;
1171 test_put_upsert(storage).await;
1172 test_put_with_sort_key(storage).await;
1173 test_put_without_sort_key(storage).await;
1174 test_put_multiple_partitions(storage).await;
1175
1176 test_get_existing(storage).await;
1178 test_get_after_update(storage).await;
1179
1180 test_delete_existing(storage).await;
1182 test_delete_nonexistent(storage).await;
1183 test_delete_idempotent(storage).await;
1184
1185 test_query_empty_partition(storage).await;
1187 test_query_returns_partition_records(storage).await;
1188 test_query_ordering(storage).await;
1189 test_query_with_limit(storage).await;
1190 test_query_null_sort_key_excluded(storage).await;
1191 test_query_partition_isolation(storage).await;
1192 test_query_range(storage).await;
1193 test_query_exclusive_end_range(storage).await;
1194 test_query_full_range_limit_one(storage).await;
1195
1196 test_get_all_empty_partition(storage).await;
1198 test_get_all_returns_all_records(storage).await;
1199 test_get_all_includes_records_without_sort_key(storage).await;
1200 test_get_all_partition_isolation(storage).await;
1201 test_get_all_after_delete(storage).await;
1202
1203 test_incremental_id_starts_at_one(storage).await;
1205 test_incremental_id_increments(storage).await;
1206 test_incremental_id_partition_isolation(storage).await;
1207 test_incremental_id_persists_across_operations(storage).await;
1208 }
1209
1210 pub async fn test_put_insert<S: StorageAdaptor>(storage: &mut S) {
1212 let record = Record {
1213 pk: "put_insert_1".into(),
1214 partition: 0,
1215 sort_key: None,
1216 data: b"test data".to_vec(),
1217 };
1218
1219 storage.put(record).await.expect("put should succeed");
1220
1221 let retrieved = storage
1222 .get(0, b"put_insert_1")
1223 .await
1224 .expect("get should succeed")
1225 .expect("record should exist");
1226
1227 assert_eq!(retrieved.pk, b"put_insert_1");
1228 assert_eq!(retrieved.partition, 0);
1229 assert_eq!(retrieved.data, b"test data");
1230 }
1231
1232 pub async fn test_put_upsert<S: StorageAdaptor>(storage: &mut S) {
1234 let record1 = Record {
1235 pk: b"put_upsert_1".into(),
1236 partition: 0,
1237 sort_key: None,
1238 data: b"original".to_vec(),
1239 };
1240 storage.put(record1).await.expect("first put should succeed");
1241
1242 let record2 = Record {
1243 pk: "put_upsert_1".into(),
1244 partition: 0,
1245 sort_key: None,
1246 data: b"updated".to_vec(),
1247 };
1248 storage
1249 .put(record2)
1250 .await
1251 .expect("second put should succeed");
1252
1253 let retrieved = storage
1254 .get(0, b"put_upsert_1")
1255 .await
1256 .expect("get should succeed")
1257 .expect("record should exist");
1258
1259 assert_eq!(retrieved.data, b"updated", "data should be updated");
1260 }
1261
1262 pub async fn test_put_with_sort_key<S: StorageAdaptor>(storage: &mut S) {
1264 let sort_key = SortKey::u32_asc(42);
1265 let record = Record {
1266 pk: b"put_sort_key_1".into(),
1267 partition: 0,
1268 sort_key: Some(sort_key.clone()),
1269 data: b"with sort key".to_vec(),
1270 };
1271
1272 storage.put(record).await.expect("put should succeed");
1273
1274 let retrieved = storage
1275 .get(0, b"put_sort_key_1")
1276 .await
1277 .expect("get should succeed")
1278 .expect("record should exist");
1279
1280 assert_eq!(retrieved.sort_key, Some(sort_key));
1281 }
1282
1283 pub async fn test_put_without_sort_key<S: StorageAdaptor>(storage: &mut S) {
1285 let record = Record {
1286 pk: b"put_no_sort_key_1".into(),
1287 partition: 0,
1288 sort_key: None,
1289 data: b"no sort key".to_vec(),
1290 };
1291
1292 storage.put(record).await.expect("put should succeed");
1293
1294 let retrieved = storage
1295 .get(0, b"put_no_sort_key_1")
1296 .await
1297 .expect("get should succeed")
1298 .expect("record should exist");
1299
1300 assert!(retrieved.sort_key.is_none());
1301 }
1302
1303 pub async fn test_put_multiple_partitions<S: StorageAdaptor>(storage: &mut S) {
1305 let record_a = Record {
1306 pk: "put_multi_a".into(),
1307 partition: 0,
1308 sort_key: None,
1309 data: b"in partition a".to_vec(),
1310 };
1311 let record_b = Record {
1312 pk: "put_multi_b".into(),
1313 partition: 1,
1314 sort_key: None,
1315 data: b"in partition b".to_vec(),
1316 };
1317
1318 storage.put(record_a).await.expect("put a should succeed");
1319 storage.put(record_b).await.expect("put b should succeed");
1320
1321 let retrieved_a = storage
1322 .get(0, b"put_multi_a")
1323 .await
1324 .expect("get should succeed")
1325 .expect("record a should exist");
1326 let retrieved_b = storage
1327 .get(1, b"put_multi_b")
1328 .await
1329 .expect("get should succeed")
1330 .expect("record b should exist");
1331
1332 assert_eq!(retrieved_a.partition, 0);
1333 assert_eq!(retrieved_b.partition, 1);
1334 }
1335
1336 pub async fn test_get_existing<S: StorageAdaptor>(storage: &mut S) {
1338 let record = Record {
1339 pk: b"get_existing_1".into(),
1340 partition: 0,
1341 sort_key: Some(SortKey::u32_asc(100)),
1342 data: b"test".to_vec(),
1343 };
1344 storage.put(record).await.expect("put should succeed");
1345
1346 let retrieved = storage
1347 .get(0, b"get_existing_1")
1348 .await
1349 .expect("get should succeed");
1350
1351 assert!(retrieved.is_some());
1352 let retrieved = retrieved.unwrap();
1353 assert_eq!(retrieved.pk, b"get_existing_1");
1354 assert_eq!(retrieved.partition, 0);
1355 assert_eq!(retrieved.data, b"test");
1356
1357 assert!(storage.get(0, b"get_existing_1_").await.unwrap().is_none());
1359 assert!(storage.get(0, b"get_existing_").await.unwrap().is_none());
1361
1362 assert!(storage.get(0, b"get_nonexistent_does_not_exist").await.unwrap().is_none());
1364 }
1365
1366 pub async fn test_get_after_update<S: StorageAdaptor>(storage: &mut S) {
1368 let record1 = Record {
1369 pk: b"get_after_update_1".into(),
1370 partition: 0,
1371 sort_key: None,
1372 data: b"version1".to_vec(),
1373 };
1374 storage.put(record1).await.expect("put should succeed");
1375
1376 let record2 = Record {
1377 pk: b"get_after_update_1".into(),
1378 partition: 0,
1379 sort_key: None,
1380 data: b"version2".to_vec(),
1381 };
1382 storage.put(record2).await.expect("put should succeed");
1383
1384 let retrieved = storage
1385 .get(0, b"get_after_update_1")
1386 .await
1387 .expect("get should succeed")
1388 .expect("record should exist");
1389
1390 assert_eq!(retrieved.data, b"version2");
1391 }
1392
1393 pub async fn test_delete_existing<S: StorageAdaptor>(storage: &mut S) {
1395 let record = Record {
1396 pk: b"delete_existing_1".into(),
1397 partition: 0,
1398 sort_key: None,
1399 data: b"to delete".to_vec(),
1400 };
1401 storage.put(record.clone()).await.expect("put should succeed");
1402
1403 let deleted_record = storage
1404 .delete(0, b"delete_existing_1")
1405 .await
1406 .expect("delete should succeed");
1407
1408 assert_eq!(deleted_record, Some(record));
1409
1410 let retrieved = storage
1411 .get(0, b"delete_existing_1")
1412 .await
1413 .expect("get should succeed");
1414 assert!(retrieved.is_none(), "record should no longer exist");
1415 }
1416
1417 pub async fn test_delete_nonexistent<S: StorageAdaptor>(storage: &mut S) {
1419 let deleted_record = storage
1420 .delete(0, b"delete_nonexistent_does_not_exist")
1421 .await
1422 .expect("delete should succeed");
1423
1424 assert!(
1425 deleted_record.is_none(),
1426 "delete should return None for non-existent record"
1427 );
1428 }
1429
1430 pub async fn test_delete_idempotent<S: StorageAdaptor>(storage: &mut S) {
1432 let record = Record {
1433 pk: b"delete_idempotent_1".into(),
1434 partition: 0,
1435 sort_key: None,
1436 data: b"delete twice".to_vec(),
1437 };
1438 storage.put(record.clone()).await.expect("put should succeed");
1439
1440 let first_delete = storage
1441 .delete(0, b"delete_idempotent_1")
1442 .await
1443 .expect("first delete should succeed");
1444 let second_delete = storage
1445 .delete(0, b"delete_idempotent_1")
1446 .await
1447 .expect("second delete should succeed");
1448
1449 assert_eq!(first_delete, Some(record), "first delete should return the record");
1450 assert_eq!(second_delete, None, "second delete should return None");
1451 }
1452
1453 pub async fn test_query_empty_partition<S: StorageAdaptor>(storage: &mut S) {
1455 clear_partitions(storage, &[0]).await.unwrap();
1456 let results = storage
1457 .query_sorted(Query::new_full_range(0))
1458 .await
1459 .expect("query should succeed");
1460
1461 assert!(results.is_empty());
1462 }
1463
1464 pub async fn test_query_returns_partition_records<S: StorageAdaptor>(storage: &mut S) {
1466 clear_partitions(storage, &[0]).await.unwrap();
1467 for i in 0..3 {
1468 let record = Record {
1469 pk: format!("query_partition_{}", i).into(),
1470 partition: 0,
1471 sort_key: Some(SortKey::u32_asc(i)),
1472 data: format!("record_{}", i).as_bytes().to_vec(),
1473 };
1474 storage.put(record).await.expect("put should succeed");
1475 }
1476
1477 let results = storage
1478 .query_sorted(Query::new_full_range(0))
1479 .await
1480 .expect("query should succeed");
1481
1482 assert_eq!(results.len(), 3);
1483 }
1484
1485 pub async fn test_query_ordering<S: StorageAdaptor>(storage: &mut S) {
1487 clear_partitions(storage, &[0]).await.unwrap();
1488 for i in [5, 2, 8, 1, 9] {
1490 let record = Record {
1491 pk: format!("query_asc_{}", i).into(),
1492 partition: 0,
1493 sort_key: Some(SortKey::u32_asc(i)),
1494 data: format!("record_{}", i).as_bytes().to_vec(),
1495 };
1496 storage.put(record).await.expect("put should succeed");
1497 }
1498
1499 let results = storage
1500 .query_sorted(Query::new_full_range(0))
1501 .await
1502 .expect("query should succeed");
1503
1504 let values = results.iter().map(|r| r.data.clone()).collect::<Vec<_>>();
1505 assert_eq!(
1506 values,
1507 vec![b"record_1".to_vec(), b"record_2".to_vec(), b"record_5".to_vec(), b"record_8".to_vec(), b"record_9".to_vec()],
1508 "should be in ascending order"
1509 );
1510 }
1511
1512 pub async fn test_query_with_limit<S: StorageAdaptor>(storage: &mut S) {
1514 clear_partitions(storage, &[0]).await.unwrap();
1515 for i in 0..10 {
1516 let record = Record {
1517 pk: format!("query_limit_{}", i).into(),
1518 partition: 0,
1519 sort_key: Some(SortKey::u32_asc(i)),
1520 data: format!("record_{}", i).as_bytes().to_vec(),
1521 };
1522 storage.put(record).await.expect("put should succeed");
1523 }
1524
1525 let results = storage
1526 .query_sorted(Query::new_full_range(0).limit(3))
1527 .await
1528 .expect("query should succeed");
1529
1530 assert_eq!(results.len(), 3);
1531 let values = results.iter().map(|r| r.data.clone()).collect::<Vec<_>>();
1532 assert_eq!(
1533 values,
1534 vec![b"record_0".to_vec(), b"record_1".to_vec(), b"record_2".to_vec()],
1535 "should return first 3 records"
1536 );
1537 }
1538
1539 pub async fn test_query_null_sort_key_excluded<S: StorageAdaptor>(storage: &mut S) {
1541 clear_partitions(storage, &[0]).await.unwrap();
1542 let with_key_1 = Record {
1544 pk: "query_null_with_1".into(),
1545 partition: 0,
1546 sort_key: Some(SortKey::u32_asc(1)),
1547 data: b"with_key_1".to_vec(),
1548 };
1549 let with_key_2 = Record {
1550 pk: "query_null_with_2".into(),
1551 partition: 0,
1552 sort_key: Some(SortKey::u32_asc(2)),
1553 data: b"with_key_2".to_vec(),
1554 };
1555
1556 let without_key = Record {
1558 pk: "query_null_without".into(),
1559 partition: 0,
1560 sort_key: None,
1561 data: b"no_key".to_vec(),
1562 };
1563
1564 storage.put(with_key_1).await.expect("put should succeed");
1565 storage.put(without_key).await.expect("put should succeed");
1566 storage.put(with_key_2).await.expect("put should succeed");
1567
1568 let results_query = storage.query_sorted(Query::new_full_range(0)).await
1570 .expect("query should succeed");
1571 assert_eq!(results_query.len(), 2, "query should only return records with sort keys");
1572 assert_eq!(results_query[0].data, b"with_key_1");
1573 assert_eq!(results_query[1].data, b"with_key_2");
1574
1575 let results_all = storage.get_all(0).await
1577 .expect("get_all should succeed");
1578 assert_eq!(results_all.len(), 3, "get_all should return all records including those without sort keys");
1579
1580 let has_with_key_1 = results_all.iter().any(|r| r.data == b"with_key_1");
1582 let has_with_key_2 = results_all.iter().any(|r| r.data == b"with_key_2");
1583 let has_without_key = results_all.iter().any(|r| r.data == b"no_key");
1584 assert!(has_with_key_1, "get_all should include with_key_1");
1585 assert!(has_with_key_2, "get_all should include with_key_2");
1586 assert!(has_without_key, "get_all should include record without sort key");
1587 }
1588
1589 pub async fn test_query_partition_isolation<S: StorageAdaptor>(storage: &mut S) {
1591 clear_partitions(storage, &[0, 1]).await.unwrap();
1592 for i in 0..3 {
1594 let record = Record {
1595 pk: format!("query_iso_a_{}", i).into(),
1596 partition: 0,
1597 sort_key: Some(SortKey::u32_asc(i)),
1598 data: format!("record_{}", i).as_bytes().to_vec(),
1599 };
1600 storage.put(record).await.expect("put should succeed");
1601 }
1602
1603 for i in 0..5 {
1605 let record = Record {
1606 pk: format!("query_iso_b_{}", i).into(),
1607 partition: 1,
1608 sort_key: Some(SortKey::u32_asc(i)),
1609 data: format!("record_{}", i + 100).as_bytes().to_vec(),
1610 };
1611 storage.put(record).await.expect("put should succeed");
1612 }
1613
1614 let results_a = storage
1615 .query_sorted(Query::new_full_range(0))
1616 .await
1617 .expect("query should succeed");
1618
1619 let results_b = storage
1620 .query_sorted(Query::new_full_range(1))
1621 .await
1622 .expect("query should succeed");
1623
1624 assert_eq!(results_a.len(), 3, "partition A should have 3 records");
1625 assert_eq!(results_b.len(), 5, "partition B should have 5 records");
1626
1627 assert!(results_a
1629 .iter()
1630 .all(|r| r.partition == 0));
1631
1632 assert!(results_b
1634 .iter()
1635 .all(|r| r.partition == 1));
1636 }
1637
1638 pub async fn test_query_range<S: StorageAdaptor>(storage: &mut S) {
1640 clear_partitions(storage, &[0]).await.unwrap();
1641
1642 for i in 1..=10u32 {
1644 let record = Record {
1645 pk: format!("query_range_{}", i).into(),
1646 partition: 0,
1647 sort_key: Some(SortKey::u32_asc(i)),
1648 data: format!("record_{}", i).as_bytes().to_vec(),
1649 };
1650 storage.put(record).await.expect("put should succeed");
1651 }
1652
1653 let results_start = storage
1655 .query_sorted(Query::new(0, SortKey::u32_asc(5)..))
1656 .await
1657 .expect("query should succeed");
1658
1659 assert_eq!(results_start.len(), 6, "should return records 5-10");
1660 let values: Vec<_> = results_start.iter().map(|r| r.data.clone()).collect();
1661 assert_eq!(
1662 values,
1663 vec![
1664 b"record_5".to_vec(),
1665 b"record_6".to_vec(),
1666 b"record_7".to_vec(),
1667 b"record_8".to_vec(),
1668 b"record_9".to_vec(),
1669 b"record_10".to_vec(),
1670 ],
1671 "should return records from 5 onwards"
1672 );
1673
1674 let results_end = storage
1676 .query_sorted(Query::new(0, ..=SortKey::u32_asc(3)))
1677 .await
1678 .expect("query should succeed");
1679
1680 assert_eq!(results_end.len(), 3, "should return records 1-3");
1681 let values: Vec<_> = results_end.iter().map(|r| r.data.clone()).collect();
1682 assert_eq!(
1683 values,
1684 vec![
1685 b"record_1".to_vec(),
1686 b"record_2".to_vec(),
1687 b"record_3".to_vec(),
1688 ],
1689 "should return records up to 3"
1690 );
1691
1692 let results_range = storage
1694 .query_sorted(Query::new(0, SortKey::u32_asc(3)..=SortKey::u32_asc(7)))
1695 .await
1696 .expect("query should succeed");
1697
1698 assert_eq!(results_range.len(), 5, "should return records 3-7");
1699 let values: Vec<_> = results_range.iter().map(|r| r.data.clone()).collect();
1700 assert_eq!(
1701 values,
1702 vec![
1703 b"record_3".to_vec(),
1704 b"record_4".to_vec(),
1705 b"record_5".to_vec(),
1706 b"record_6".to_vec(),
1707 b"record_7".to_vec(),
1708 ],
1709 "should return records in range 3-7"
1710 );
1711
1712 let results_range_limit = storage
1714 .query_sorted(Query::new(0, SortKey::u32_asc(2)..=SortKey::u32_asc(8)).limit(3))
1715 .await
1716 .expect("query should succeed");
1717
1718 assert_eq!(results_range_limit.len(), 3, "should return only 3 records due to limit");
1719 let values: Vec<_> = results_range_limit.iter().map(|r| r.data.clone()).collect();
1720 assert_eq!(
1721 values,
1722 vec![
1723 b"record_2".to_vec(),
1724 b"record_3".to_vec(),
1725 b"record_4".to_vec(),
1726 ],
1727 "should return first 3 records in range"
1728 );
1729
1730 let results_empty = storage
1732 .query_sorted(Query::new(0, SortKey::u32_asc(100)..=SortKey::u32_asc(200)))
1733 .await
1734 .expect("query should succeed");
1735
1736 assert!(results_empty.is_empty(), "should return no records for out-of-range query");
1737 }
1738
1739 pub async fn test_query_exclusive_end_range<S: StorageAdaptor>(storage: &mut S) {
1741 clear_partitions(storage, &[0]).await.unwrap();
1742
1743 for i in 1..=10u32 {
1744 let record = Record {
1745 pk: format!("query_excl_{}", i).into(),
1746 partition: 0,
1747 sort_key: Some(SortKey::u32_asc(i)),
1748 data: format!("record_{}", i).as_bytes().to_vec(),
1749 };
1750 storage.put(record).await.expect("put should succeed");
1751 }
1752
1753 let results = storage
1755 .query_sorted(Query::new(0, SortKey::u32_asc(3)..SortKey::u32_asc(7)))
1756 .await
1757 .expect("query should succeed");
1758
1759 assert_eq!(results.len(), 4, "exclusive end should not include record_7");
1760 let values: Vec<_> = results.iter().map(|r| r.data.clone()).collect();
1761 assert_eq!(
1762 values,
1763 vec![
1764 b"record_3".to_vec(),
1765 b"record_4".to_vec(),
1766 b"record_5".to_vec(),
1767 b"record_6".to_vec(),
1768 ],
1769 );
1770
1771 let results = storage
1773 .query_sorted(Query::new(0, ..SortKey::u32_asc(4)))
1774 .await
1775 .expect("query should succeed");
1776
1777 assert_eq!(results.len(), 3, "exclusive upper bound should not include record_4");
1778 let values: Vec<_> = results.iter().map(|r| r.data.clone()).collect();
1779 assert_eq!(
1780 values,
1781 vec![
1782 b"record_1".to_vec(),
1783 b"record_2".to_vec(),
1784 b"record_3".to_vec(),
1785 ],
1786 );
1787 }
1788
1789 pub async fn test_query_full_range_limit_one<S: StorageAdaptor>(storage: &mut S) {
1791 clear_partitions(storage, &[0]).await.unwrap();
1792
1793 for i in [5u32, 2, 8, 1, 9] {
1794 let record = Record {
1795 pk: format!("query_limit1_{}", i).into(),
1796 partition: 0,
1797 sort_key: Some(SortKey::u32_asc(i)),
1798 data: format!("record_{}", i).as_bytes().to_vec(),
1799 };
1800 storage.put(record).await.expect("put should succeed");
1801 }
1802
1803 let results = storage
1804 .query_sorted(Query::new_full_range(0).limit(1))
1805 .await
1806 .expect("query should succeed");
1807
1808 assert_eq!(results.len(), 1);
1809 assert_eq!(results[0].data, b"record_1".to_vec(), "limit 1 should return the first record in sort order");
1810 }
1811
1812 pub async fn test_incremental_id_starts_at_one<S: StorageAdaptor>(storage: &mut S) {
1814 storage.delete(LAST_IDS, b"0").await.unwrap();
1816 let id = storage.incremental_id(0).await
1817 .expect("incremental_id should succeed");
1818
1819 assert_eq!(id, 1, "first id should be 1");
1820 }
1821
1822 pub async fn test_incremental_id_increments<S: StorageAdaptor>(storage: &mut S) {
1824 clear_partitions(storage, &[0, LAST_IDS]).await.unwrap();
1825
1826 let id1 = storage.incremental_id(0).await
1827 .expect("incremental_id should succeed");
1828 let id2 = storage.incremental_id(0).await
1829 .expect("incremental_id should succeed");
1830 let id3 = storage.incremental_id(0).await
1831 .expect("incremental_id should succeed");
1832
1833 assert_eq!(id1, 1, "first id should be 1");
1834 assert_eq!(id2, 2, "second id should be 2");
1835 assert_eq!(id3, 3, "third id should be 3");
1836 }
1837
1838 pub async fn test_incremental_id_partition_isolation<S: StorageAdaptor>(storage: &mut S) {
1840 clear_partitions(storage, &[0, 1, LAST_IDS]).await.unwrap();
1841
1842 let a1 = storage.incremental_id(0).await
1844 .expect("incremental_id should succeed");
1845 let a2 = storage.incremental_id(0).await
1846 .expect("incremental_id should succeed");
1847 let a3 = storage.incremental_id(0).await
1848 .expect("incremental_id should succeed");
1849
1850 let b1 = storage.incremental_id(1).await
1852 .expect("incremental_id should succeed");
1853 let b2 = storage.incremental_id(1).await
1854 .expect("incremental_id should succeed");
1855
1856 assert_eq!(a1, 1);
1858 assert_eq!(a2, 2);
1859 assert_eq!(a3, 3);
1860
1861 assert_eq!(b1, 1);
1863 assert_eq!(b2, 2);
1864
1865 let a4 = storage.incremental_id(0).await.expect("incremental_id should succeed");
1867 assert_eq!(a4, 4);
1868 }
1869
1870 pub async fn test_incremental_id_persists_across_operations<S: StorageAdaptor>(storage: &mut S) {
1872 clear_partitions(storage, &[0, 1, LAST_IDS]).await.unwrap();
1873
1874 let id1 = storage.incremental_id(0).await
1876 .expect("incremental_id should succeed");
1877 let id2 = storage.incremental_id(0).await
1878 .expect("incremental_id should succeed");
1879 assert_eq!(id1, 1);
1880 assert_eq!(id2, 2);
1881
1882 let stored = storage
1884 .get(LAST_IDS, &[0])
1885 .await
1886 .expect("get should succeed")
1887 .expect("id record should exist");
1888 let stored_id: u32 = serde_json::from_slice(&stored.data).expect("should deserialize");
1889 assert_eq!(stored_id, 2, "stored id should be 2");
1890
1891 let id3 = storage.incremental_id(0).await.expect("incremental_id should succeed");
1893 assert_eq!(id3, 3);
1894 }
1895
1896 pub async fn test_get_all_empty_partition<S: StorageAdaptor>(storage: &mut S) {
1898 clear_partitions(storage, &[0]).await.unwrap();
1899 let results = storage
1900 .get_all(0)
1901 .await
1902 .expect("get_all should succeed");
1903
1904 assert!(results.is_empty(), "get_all should return empty for empty partition");
1905 }
1906
1907 pub async fn test_get_all_returns_all_records<S: StorageAdaptor>(storage: &mut S) {
1909 clear_partitions(storage, &[0]).await.unwrap();
1910
1911 for i in 0..5 {
1913 let record = Record {
1914 pk: format!("get_all_{}", i).into(),
1915 partition: 0,
1916 sort_key: Some(SortKey::u32_asc(i)),
1917 data: format!("record_{}", i).as_bytes().to_vec(),
1918 };
1919 storage.put(record).await.expect("put should succeed");
1920 }
1921
1922 let results = storage
1923 .get_all(0)
1924 .await
1925 .expect("get_all should succeed");
1926
1927 assert_eq!(results.len(), 5, "get_all should return all 5 records");
1928
1929 for i in 0..5 {
1931 let expected_data = format!("record_{}", i).as_bytes().to_vec();
1932 let found = results.iter().any(|r| r.data == expected_data);
1933 assert!(found, "get_all should include record_{}", i);
1934 }
1935 }
1936
1937 pub async fn test_get_all_includes_records_without_sort_key<S: StorageAdaptor>(storage: &mut S) {
1939 clear_partitions(storage, &[0]).await.unwrap();
1940
1941 let with_key_1 = Record {
1943 pk: "get_all_with_1".into(),
1944 partition: 0,
1945 sort_key: Some(SortKey::u32_asc(1)),
1946 data: b"with_key_1".to_vec(),
1947 };
1948 let with_key_2 = Record {
1949 pk: "get_all_with_2".into(),
1950 partition: 0,
1951 sort_key: Some(SortKey::u32_asc(2)),
1952 data: b"with_key_2".to_vec(),
1953 };
1954
1955 let without_key_1 = Record {
1957 pk: "get_all_without_1".into(),
1958 partition: 0,
1959 sort_key: None,
1960 data: b"without_key_1".to_vec(),
1961 };
1962 let without_key_2 = Record {
1963 pk: "get_all_without_2".into(),
1964 partition: 0,
1965 sort_key: None,
1966 data: b"without_key_2".to_vec(),
1967 };
1968
1969 storage.put(with_key_1).await.expect("put should succeed");
1970 storage.put(without_key_1).await.expect("put should succeed");
1971 storage.put(with_key_2).await.expect("put should succeed");
1972 storage.put(without_key_2).await.expect("put should succeed");
1973
1974 let results = storage
1975 .get_all(0)
1976 .await
1977 .expect("get_all should succeed");
1978
1979 assert_eq!(results.len(), 4, "get_all should return all 4 records");
1980
1981 let has_with_1 = results.iter().any(|r| r.data == b"with_key_1");
1983 let has_with_2 = results.iter().any(|r| r.data == b"with_key_2");
1984 let has_without_1 = results.iter().any(|r| r.data == b"without_key_1");
1985 let has_without_2 = results.iter().any(|r| r.data == b"without_key_2");
1986
1987 assert!(has_with_1, "get_all should include with_key_1");
1988 assert!(has_with_2, "get_all should include with_key_2");
1989 assert!(has_without_1, "get_all should include without_key_1");
1990 assert!(has_without_2, "get_all should include without_key_2");
1991
1992 let query_results = storage
1994 .query_sorted(Query::new_full_range(0))
1995 .await
1996 .expect("query should succeed");
1997
1998 assert_eq!(query_results.len(), 2, "query should only return records with sort keys");
1999 let query_has_without = query_results.iter().any(|r| r.sort_key.is_none());
2000 assert!(!query_has_without, "query should not include records without sort keys");
2001 }
2002
2003 pub async fn test_get_all_partition_isolation<S: StorageAdaptor>(storage: &mut S) {
2005 clear_partitions(storage, &[0, 1]).await.unwrap();
2006
2007 for i in 0..3 {
2009 let record = Record {
2010 pk: format!("partition_0_{}", i).into(),
2011 partition: 0,
2012 sort_key: Some(SortKey::u32_asc(i)),
2013 data: format!("p0_record_{}", i).as_bytes().to_vec(),
2014 };
2015 storage.put(record).await.expect("put should succeed");
2016 }
2017
2018 for i in 0..2 {
2020 let record = Record {
2021 pk: format!("partition_1_{}", i).into(),
2022 partition: 1,
2023 sort_key: Some(SortKey::u32_asc(i)),
2024 data: format!("p1_record_{}", i).as_bytes().to_vec(),
2025 };
2026 storage.put(record).await.expect("put should succeed");
2027 }
2028
2029 let results_0 = storage.get_all(0).await.expect("get_all should succeed");
2031 assert_eq!(results_0.len(), 3, "partition 0 should have 3 records");
2032 assert!(results_0.iter().all(|r| r.partition == 0), "all records should be from partition 0");
2033
2034 let results_1 = storage.get_all(1).await.expect("get_all should succeed");
2036 assert_eq!(results_1.len(), 2, "partition 1 should have 2 records");
2037 assert!(results_1.iter().all(|r| r.partition == 1), "all records should be from partition 1");
2038 }
2039
2040 pub async fn test_get_all_after_delete<S: StorageAdaptor>(storage: &mut S) {
2042 clear_partitions(storage, &[0]).await.unwrap();
2043
2044 for i in 0..3u32 {
2045 let record = Record {
2046 pk: format!("get_all_del_{}", i).into(),
2047 partition: 0,
2048 sort_key: Some(SortKey::u32_asc(i)),
2049 data: format!("record_{}", i).as_bytes().to_vec(),
2050 };
2051 storage.put(record).await.expect("put should succeed");
2052 }
2053
2054 storage.delete(0, b"get_all_del_1").await.expect("delete should succeed");
2055
2056 let results = storage.get_all(0).await.expect("get_all should succeed");
2057 assert_eq!(results.len(), 2, "get_all should reflect the deletion");
2058
2059 let has_deleted = results.iter().any(|r| r.data == b"record_1".to_vec());
2060 assert!(!has_deleted, "deleted record should not appear");
2061 }
2062}