1use hashtree_core::Hash;
12use serde::{Deserialize, Serialize};
13
14pub const MSG_TYPE_REQUEST: u8 = 0x00;
16pub const MSG_TYPE_RESPONSE: u8 = 0x01;
17pub const MSG_TYPE_QUOTE_REQUEST: u8 = 0x02;
18pub const MSG_TYPE_QUOTE_RESPONSE: u8 = 0x03;
19
20pub const FRAGMENT_SIZE: usize = 32 * 1024;
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct DataRequest {
26 #[serde(with = "serde_bytes")]
28 pub h: Vec<u8>,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub htl: Option<u8>,
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub q: Option<u64>,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct DataResponse {
40 #[serde(with = "serde_bytes")]
42 pub h: Vec<u8>,
43 #[serde(with = "serde_bytes")]
45 pub d: Vec<u8>,
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub i: Option<u32>,
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub n: Option<u32>,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct DataQuoteRequest {
57 #[serde(with = "serde_bytes")]
59 pub h: Vec<u8>,
60 pub p: u64,
62 pub t: u32,
64 #[serde(skip_serializing_if = "Option::is_none")]
66 pub m: Option<String>,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct DataQuoteResponse {
72 #[serde(with = "serde_bytes")]
74 pub h: Vec<u8>,
75 pub a: bool,
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub q: Option<u64>,
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub p: Option<u64>,
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub t: Option<u32>,
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub m: Option<String>,
89}
90
91#[derive(Debug, Clone)]
93pub enum DataMessage {
94 Request(DataRequest),
95 Response(DataResponse),
96 QuoteRequest(DataQuoteRequest),
97 QuoteResponse(DataQuoteResponse),
98}
99
100pub fn encode_request(req: &DataRequest) -> Vec<u8> {
103 let body = rmp_serde::to_vec_named(req).expect("Failed to encode request");
104 let mut result = Vec::with_capacity(1 + body.len());
105 result.push(MSG_TYPE_REQUEST);
106 result.extend(body);
107 result
108}
109
110pub fn encode_response(res: &DataResponse) -> Vec<u8> {
113 let body = rmp_serde::to_vec_named(res).expect("Failed to encode response");
114 let mut result = Vec::with_capacity(1 + body.len());
115 result.push(MSG_TYPE_RESPONSE);
116 result.extend(body);
117 result
118}
119
120pub fn encode_quote_request(req: &DataQuoteRequest) -> Vec<u8> {
122 let body = rmp_serde::to_vec_named(req).expect("Failed to encode quote request");
123 let mut result = Vec::with_capacity(1 + body.len());
124 result.push(MSG_TYPE_QUOTE_REQUEST);
125 result.extend(body);
126 result
127}
128
129pub fn encode_quote_response(res: &DataQuoteResponse) -> Vec<u8> {
131 let body = rmp_serde::to_vec_named(res).expect("Failed to encode quote response");
132 let mut result = Vec::with_capacity(1 + body.len());
133 result.push(MSG_TYPE_QUOTE_RESPONSE);
134 result.extend(body);
135 result
136}
137
138pub fn parse_message(data: &[u8]) -> Option<DataMessage> {
140 if data.len() < 2 {
141 return None;
142 }
143
144 let msg_type = data[0];
145 let body = &data[1..];
146
147 match msg_type {
148 MSG_TYPE_REQUEST => rmp_serde::from_slice::<DataRequest>(body)
149 .ok()
150 .map(DataMessage::Request),
151 MSG_TYPE_RESPONSE => rmp_serde::from_slice::<DataResponse>(body)
152 .ok()
153 .map(DataMessage::Response),
154 MSG_TYPE_QUOTE_REQUEST => rmp_serde::from_slice::<DataQuoteRequest>(body)
155 .ok()
156 .map(DataMessage::QuoteRequest),
157 MSG_TYPE_QUOTE_RESPONSE => rmp_serde::from_slice::<DataQuoteResponse>(body)
158 .ok()
159 .map(DataMessage::QuoteResponse),
160 _ => None,
161 }
162}
163
164pub fn create_request(hash: &Hash, htl: u8) -> DataRequest {
166 DataRequest {
167 h: hash.to_vec(),
168 htl: Some(htl),
169 q: None,
170 }
171}
172
173pub fn create_request_with_quote(hash: &Hash, htl: u8, quote_id: u64) -> DataRequest {
175 DataRequest {
176 h: hash.to_vec(),
177 htl: Some(htl),
178 q: Some(quote_id),
179 }
180}
181
182pub fn create_response(hash: &Hash, data: Vec<u8>) -> DataResponse {
184 DataResponse {
185 h: hash.to_vec(),
186 d: data,
187 i: None,
188 n: None,
189 }
190}
191
192pub fn create_quote_request(
194 hash: &Hash,
195 ttl_ms: u32,
196 payment_sat: u64,
197 mint_url: Option<&str>,
198) -> DataQuoteRequest {
199 DataQuoteRequest {
200 h: hash.to_vec(),
201 p: payment_sat,
202 t: ttl_ms,
203 m: mint_url.map(str::to_string),
204 }
205}
206
207pub fn create_quote_response_available(
209 hash: &Hash,
210 quote_id: u64,
211 payment_sat: u64,
212 ttl_ms: u32,
213 mint_url: Option<&str>,
214) -> DataQuoteResponse {
215 DataQuoteResponse {
216 h: hash.to_vec(),
217 a: true,
218 q: Some(quote_id),
219 p: Some(payment_sat),
220 t: Some(ttl_ms),
221 m: mint_url.map(str::to_string),
222 }
223}
224
225pub fn create_quote_response_unavailable(hash: &Hash) -> DataQuoteResponse {
227 DataQuoteResponse {
228 h: hash.to_vec(),
229 a: false,
230 q: None,
231 p: None,
232 t: None,
233 m: None,
234 }
235}
236
237pub fn create_fragment_response(
239 hash: &Hash,
240 data: Vec<u8>,
241 index: u32,
242 total: u32,
243) -> DataResponse {
244 DataResponse {
245 h: hash.to_vec(),
246 d: data,
247 i: Some(index),
248 n: Some(total),
249 }
250}
251
252pub fn is_fragmented(res: &DataResponse) -> bool {
254 res.i.is_some() && res.n.is_some()
255}
256
257pub fn hash_to_key(hash: &[u8]) -> String {
259 hex::encode(hash)
260}
261
262pub fn hash_to_bytes(hash: &Hash) -> Vec<u8> {
264 hash.to_vec()
265}
266
267pub fn bytes_to_hash(bytes: &[u8]) -> Option<Hash> {
269 if bytes.len() == 32 {
270 let mut hash = [0u8; 32];
271 hash.copy_from_slice(bytes);
272 Some(hash)
273 } else {
274 None
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281
282 #[test]
283 fn test_encode_decode_request() {
284 let hash = [0xab; 32];
285 let req = create_request(&hash, 10);
286 let encoded = encode_request(&req);
287
288 assert_eq!(encoded[0], MSG_TYPE_REQUEST);
289
290 let parsed = parse_message(&encoded).unwrap();
291 match parsed {
292 DataMessage::Request(r) => {
293 assert_eq!(r.h, hash.to_vec());
294 assert_eq!(r.htl, Some(10));
295 }
296 _ => panic!("Expected request"),
297 }
298 }
299
300 #[test]
301 fn test_encode_decode_response() {
302 let hash = [0xcd; 32];
303 let data = vec![1, 2, 3, 4, 5];
304 let res = create_response(&hash, data.clone());
305 let encoded = encode_response(&res);
306
307 assert_eq!(encoded[0], MSG_TYPE_RESPONSE);
308
309 let parsed = parse_message(&encoded).unwrap();
310 match parsed {
311 DataMessage::Response(r) => {
312 assert_eq!(r.h, hash.to_vec());
313 assert_eq!(r.d, data);
314 assert!(!is_fragmented(&r));
315 }
316 _ => panic!("Expected response"),
317 }
318 }
319
320 #[test]
321 fn test_encode_decode_fragment_response() {
322 let hash = [0xef; 32];
323 let data = vec![10, 20, 30];
324 let res = create_fragment_response(&hash, data.clone(), 2, 5);
325 let encoded = encode_response(&res);
326
327 let parsed = parse_message(&encoded).unwrap();
328 match parsed {
329 DataMessage::Response(r) => {
330 assert_eq!(r.h, hash.to_vec());
331 assert_eq!(r.d, data);
332 assert!(is_fragmented(&r));
333 assert_eq!(r.i, Some(2));
334 assert_eq!(r.n, Some(5));
335 }
336 _ => panic!("Expected response"),
337 }
338 }
339
340 #[test]
341 fn test_encode_decode_quote_request() {
342 let hash = [0x44; 32];
343 let req = create_quote_request(&hash, 7, 2_500, Some("https://mint.example"));
344 let encoded = encode_quote_request(&req);
345
346 assert_eq!(encoded[0], MSG_TYPE_QUOTE_REQUEST);
347
348 let parsed = parse_message(&encoded).unwrap();
349 match parsed {
350 DataMessage::QuoteRequest(r) => {
351 assert_eq!(r.h, hash.to_vec());
352 assert_eq!(r.t, 7);
353 assert_eq!(r.p, 2_500);
354 assert_eq!(r.m.as_deref(), Some("https://mint.example"));
355 }
356 _ => panic!("Expected quote request"),
357 }
358 }
359
360 #[test]
361 fn test_encode_decode_quote_response_and_quoted_request() {
362 let hash = [0x55; 32];
363 let quote =
364 create_quote_response_available(&hash, 19, 2_500, 7, Some("https://mint.example"));
365 let encoded_quote = encode_quote_response("e);
366
367 assert_eq!(encoded_quote[0], MSG_TYPE_QUOTE_RESPONSE);
368
369 let parsed_quote = parse_message(&encoded_quote).unwrap();
370 match parsed_quote {
371 DataMessage::QuoteResponse(r) => {
372 assert_eq!(r.h, hash.to_vec());
373 assert!(r.a);
374 assert_eq!(r.q, Some(19));
375 assert_eq!(r.p, Some(2_500));
376 assert_eq!(r.t, Some(7));
377 assert_eq!(r.m.as_deref(), Some("https://mint.example"));
378 }
379 _ => panic!("Expected quote response"),
380 }
381
382 let req = create_request_with_quote(&hash, 9, 19);
383 let encoded_req = encode_request(&req);
384 let parsed_req = parse_message(&encoded_req).unwrap();
385 match parsed_req {
386 DataMessage::Request(r) => {
387 assert_eq!(r.h, hash.to_vec());
388 assert_eq!(r.htl, Some(9));
389 assert_eq!(r.q, Some(19));
390 }
391 _ => panic!("Expected quoted request"),
392 }
393 }
394
395 #[test]
396 fn test_hash_conversions() {
397 let hash = [0x12; 32];
398 let bytes = hash_to_bytes(&hash);
399 let back = bytes_to_hash(&bytes).unwrap();
400 assert_eq!(hash, back);
401 }
402}