1use crate::{Batch, BatchId, PostageContext};
4
5pub trait BatchStore {
15 type Error: std::error::Error;
17
18 fn get(
22 &self,
23 id: &BatchId,
24 ) -> impl std::future::Future<Output = Result<Option<Batch>, Self::Error>> + Send;
25
26 fn put(
28 &self,
29 batch: Batch,
30 ) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send;
31
32 fn remove(
36 &self,
37 id: &BatchId,
38 ) -> impl std::future::Future<Output = Result<bool, Self::Error>> + Send;
39
40 fn contains(
42 &self,
43 id: &BatchId,
44 ) -> impl std::future::Future<Output = Result<bool, Self::Error>> + Send;
45
46 fn context(
48 &self,
49 ) -> impl std::future::Future<Output = Result<PostageContext, Self::Error>> + Send;
50
51 fn set_context(
53 &self,
54 state: PostageContext,
55 ) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send;
56
57 fn batch_ids(
59 &self,
60 ) -> impl std::future::Future<Output = Result<Vec<BatchId>, Self::Error>> + Send;
61
62 fn count(&self) -> impl std::future::Future<Output = Result<usize, Self::Error>> + Send;
64}
65
66pub trait BatchStoreExt: BatchStore + Sync {
68 fn get_usable(
73 &self,
74 id: &BatchId,
75 confirmation_threshold: u64,
76 ) -> impl std::future::Future<Output = Result<Batch, BatchStoreError<Self::Error>>> + Send {
77 async move {
78 let batch = self
79 .get(id)
80 .await
81 .map_err(BatchStoreError::Store)?
82 .ok_or(BatchStoreError::NotFound(*id))?;
83
84 let state = self.context().await.map_err(BatchStoreError::Store)?;
85
86 if !batch.is_usable(state.block(), confirmation_threshold) {
87 return Err(BatchStoreError::NotUsable {
88 batch_id: *id,
89 created: batch.start(),
90 current: state.block(),
91 threshold: confirmation_threshold,
92 });
93 }
94
95 if batch.is_expired(state.total_amount()) {
96 return Err(BatchStoreError::Expired {
97 batch_id: *id,
98 value: batch.value(),
99 total_amount: state.total_amount(),
100 });
101 }
102
103 Ok(batch)
104 }
105 }
106}
107
108impl<T: BatchStore + Sync> BatchStoreExt for T {}
110
111#[derive(Debug)]
113pub enum BatchStoreError<E> {
114 NotFound(BatchId),
116 NotUsable {
118 batch_id: BatchId,
120 created: u64,
122 current: u64,
124 threshold: u64,
126 },
127 Expired {
129 batch_id: BatchId,
131 value: u128,
133 total_amount: u128,
135 },
136 Store(E),
138}
139
140impl<E: std::fmt::Display> std::fmt::Display for BatchStoreError<E> {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 match self {
143 Self::NotFound(id) => write!(f, "batch not found: {}", id),
144 Self::NotUsable {
145 batch_id,
146 created,
147 current,
148 threshold,
149 } => write!(
150 f,
151 "batch {} not usable: created at block {}, current block {}, need {} confirmations",
152 batch_id, created, current, threshold
153 ),
154 Self::Expired {
155 batch_id,
156 value,
157 total_amount,
158 } => write!(
159 f,
160 "batch {} expired: value {} <= total_amount {}",
161 batch_id, value, total_amount
162 ),
163 Self::Store(e) => write!(f, "store error: {}", e),
164 }
165 }
166}
167
168impl<E: std::error::Error + 'static> std::error::Error for BatchStoreError<E> {
169 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
170 match self {
171 Self::Store(e) => Some(e),
172 _ => None,
173 }
174 }
175}