nectar_postage_issuer/
factory.rs1use nectar_postage::{Batch, BatchId, BatchParams};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct CreateResult {
8 pub batch: Batch,
10 pub tx_hash: Option<alloy_primitives::B256>,
12}
13
14pub trait BatchFactory {
19 type Error: std::error::Error;
21
22 fn create(
35 &self,
36 params: BatchParams,
37 ) -> impl std::future::Future<Output = Result<CreateResult, Self::Error>> + Send;
38
39 fn top_up(
50 &self,
51 batch_id: BatchId,
52 amount: u128,
53 ) -> impl std::future::Future<Output = Result<u128, Self::Error>> + Send;
54
55 fn dilute(
65 &self,
66 batch_id: BatchId,
67 new_depth: u8,
68 ) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send;
69}
70
71#[derive(Debug)]
76pub struct MemoryBatchFactory {
77 next_id: std::sync::atomic::AtomicU64,
79 current_block: u64,
81}
82
83impl MemoryBatchFactory {
84 pub const fn new(current_block: u64) -> Self {
86 Self {
87 next_id: std::sync::atomic::AtomicU64::new(0),
88 current_block,
89 }
90 }
91
92 pub const fn set_current_block(&mut self, block: u64) {
94 self.current_block = block;
95 }
96
97 fn generate_batch_id(&self) -> BatchId {
98 use alloy_primitives::B256;
99
100 let id = self
101 .next_id
102 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
103 let mut bytes = [0u8; 32];
104 bytes[24..32].copy_from_slice(&id.to_be_bytes());
105 B256::from(bytes)
106 }
107}
108
109impl Default for MemoryBatchFactory {
110 fn default() -> Self {
111 Self::new(0)
112 }
113}
114
115#[derive(Debug, Clone, PartialEq, Eq)]
117pub enum MemoryBatchError {
118 NotFound(BatchId),
120 Immutable(BatchId),
122 InvalidDepth {
124 batch_id: BatchId,
126 current: u8,
128 requested: u8,
130 },
131}
132
133impl std::fmt::Display for MemoryBatchError {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 match self {
136 Self::NotFound(id) => write!(f, "batch not found: {}", id),
137 Self::Immutable(id) => write!(f, "batch is immutable: {}", id),
138 Self::InvalidDepth {
139 batch_id,
140 current,
141 requested,
142 } => write!(
143 f,
144 "invalid depth for batch {}: current {}, requested {}",
145 batch_id, current, requested
146 ),
147 }
148 }
149}
150
151impl std::error::Error for MemoryBatchError {}
152
153impl BatchFactory for MemoryBatchFactory {
154 type Error = std::convert::Infallible;
155
156 async fn create(&self, params: BatchParams) -> Result<CreateResult, Self::Error> {
157 let batch_id = self.generate_batch_id();
158
159 let batch = Batch::new(
160 batch_id,
161 params.amount,
162 self.current_block,
163 params.owner,
164 params.depth,
165 params.bucket_depth,
166 params.immutable,
167 );
168
169 Ok(CreateResult {
170 batch,
171 tx_hash: None,
172 })
173 }
174
175 async fn top_up(&self, _batch_id: BatchId, _amount: u128) -> Result<u128, Self::Error> {
176 Ok(0)
178 }
179
180 async fn dilute(&self, _batch_id: BatchId, _new_depth: u8) -> Result<(), Self::Error> {
181 Ok(())
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use alloy_primitives::Address;
190
191 #[tokio::test]
192 async fn test_memory_factory_create() {
193 let factory = MemoryBatchFactory::new(100);
194
195 let params = BatchParams::new(Address::ZERO, 20, 16, 1000);
196 let result = factory.create(params).await.unwrap();
197
198 assert_eq!(result.batch.owner(), Address::ZERO);
199 assert_eq!(result.batch.depth(), 20);
200 assert_eq!(result.batch.bucket_depth(), 16);
201 assert_eq!(result.batch.value(), 1000);
202 assert_eq!(result.batch.start(), 100);
203 assert!(result.tx_hash.is_none());
204 }
205
206 #[tokio::test]
207 async fn test_memory_factory_unique_ids() {
208 let factory = MemoryBatchFactory::new(0);
209
210 let params = BatchParams::new(Address::ZERO, 20, 16, 1000);
211
212 let r1 = factory.create(params.clone()).await.unwrap();
213 let r2 = factory.create(params.clone()).await.unwrap();
214 let r3 = factory.create(params).await.unwrap();
215
216 assert_ne!(r1.batch.id(), r2.batch.id());
217 assert_ne!(r2.batch.id(), r3.batch.id());
218 }
219
220 #[tokio::test]
221 async fn test_memory_factory_immutable() {
222 let factory = MemoryBatchFactory::new(0);
223
224 let params = BatchParams::new(Address::ZERO, 20, 16, 1000).immutable(true);
225 let result = factory.create(params).await.unwrap();
226
227 assert!(result.batch.immutable());
228 }
229}