1use std::fmt;
9use std::str::FromStr;
10
11use sha3::{Digest, Keccak256};
12
13use crate::swarm::bytes::{decode_hex, encode_hex};
14use crate::swarm::errors::Error;
15
16pub const REFERENCE_LENGTH: usize = 32;
20pub const ENCRYPTED_REFERENCE_LENGTH: usize = 64;
22pub const BATCH_ID_LENGTH: usize = 32;
24pub const TRANSACTION_ID_LENGTH: usize = 32;
26pub const PEER_ADDRESS_LENGTH: usize = 32;
28pub const IDENTIFIER_LENGTH: usize = 32;
30pub const TOPIC_LENGTH: usize = 32;
32pub const ETH_ADDRESS_LENGTH: usize = 20;
34pub const PRIVATE_KEY_LENGTH: usize = 32;
36pub const PUBLIC_KEY_LENGTH: usize = 64;
38pub const SIGNATURE_LENGTH: usize = 65;
40pub const SPAN_LENGTH: usize = 8;
42pub const FEED_INDEX_LENGTH: usize = 8;
44
45macro_rules! define_typed_bytes {
53 (
54 $(#[$meta:meta])*
55 $name:ident, $len:expr, $kind:literal
56 ) => {
57 $(#[$meta])*
58 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
59 pub struct $name([u8; $len]);
60
61 impl $name {
62 #[doc = concat!("Length in bytes (", stringify!($len), ").")]
63 pub const LENGTH: usize = $len;
64
65 #[doc = concat!("Construct a [`", stringify!($name), "`] from bytes. Returns ")]
66 #[doc = "[`Error::LengthMismatch`] if `b.len()` does not match."]
67 pub fn new(b: &[u8]) -> Result<Self, Error> {
68 if b.len() != $len {
69 return Err(Error::LengthMismatch {
70 kind: $kind,
71 expected: &[$len],
72 got: b.len(),
73 });
74 }
75 let mut a = [0u8; $len];
76 a.copy_from_slice(b);
77 Ok(Self(a))
78 }
79
80 pub fn from_hex(s: &str) -> Result<Self, Error> {
82 Self::new(&decode_hex(s)?)
83 }
84
85 pub fn as_bytes(&self) -> &[u8] {
87 &self.0
88 }
89
90 pub fn to_vec(&self) -> Vec<u8> {
92 self.0.to_vec()
93 }
94
95 pub fn to_hex(&self) -> String {
97 encode_hex(&self.0)
98 }
99
100 pub fn into_array(self) -> [u8; $len] {
102 self.0
103 }
104 }
105
106 impl fmt::Debug for $name {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 write!(f, "{}({})", stringify!($name), self.to_hex())
109 }
110 }
111
112 impl fmt::Display for $name {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 f.write_str(&self.to_hex())
115 }
116 }
117
118 impl fmt::LowerHex for $name {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 f.write_str(&self.to_hex())
121 }
122 }
123
124 impl FromStr for $name {
125 type Err = Error;
126 fn from_str(s: &str) -> Result<Self, Self::Err> {
127 Self::from_hex(s)
128 }
129 }
130
131 impl serde::Serialize for $name {
132 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
133 s.serialize_str(&self.to_hex())
134 }
135 }
136
137 impl<'de> serde::Deserialize<'de> for $name {
138 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
139 let s = String::deserialize(d)?;
140 Self::from_hex(&s).map_err(serde::de::Error::custom)
141 }
142 }
143 };
144}
145
146#[derive(Clone, PartialEq, Eq, Hash)]
151pub enum Reference {
152 Plain([u8; REFERENCE_LENGTH]),
154 Encrypted([u8; ENCRYPTED_REFERENCE_LENGTH]),
156}
157
158impl Reference {
159 pub const ALLOWED_LENGTHS: &'static [usize] = &[REFERENCE_LENGTH, ENCRYPTED_REFERENCE_LENGTH];
161
162 pub fn new(b: &[u8]) -> Result<Self, Error> {
164 match b.len() {
165 REFERENCE_LENGTH => {
166 let mut a = [0u8; REFERENCE_LENGTH];
167 a.copy_from_slice(b);
168 Ok(Reference::Plain(a))
169 }
170 ENCRYPTED_REFERENCE_LENGTH => {
171 let mut a = [0u8; ENCRYPTED_REFERENCE_LENGTH];
172 a.copy_from_slice(b);
173 Ok(Reference::Encrypted(a))
174 }
175 n => Err(Error::LengthMismatch {
176 kind: "Reference",
177 expected: Self::ALLOWED_LENGTHS,
178 got: n,
179 }),
180 }
181 }
182
183 pub fn from_hex(s: &str) -> Result<Self, Error> {
185 Self::new(&decode_hex(s)?)
186 }
187
188 pub fn as_bytes(&self) -> &[u8] {
190 match self {
191 Reference::Plain(a) => a,
192 Reference::Encrypted(a) => a,
193 }
194 }
195
196 pub fn to_vec(&self) -> Vec<u8> {
198 self.as_bytes().to_vec()
199 }
200
201 pub fn to_hex(&self) -> String {
203 encode_hex(self.as_bytes())
204 }
205
206 pub fn is_encrypted(&self) -> bool {
208 matches!(self, Reference::Encrypted(_))
209 }
210
211 pub fn len(&self) -> usize {
213 self.as_bytes().len()
214 }
215
216 pub fn is_empty(&self) -> bool {
219 false
220 }
221}
222
223impl fmt::Debug for Reference {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 write!(f, "Reference({})", self.to_hex())
226 }
227}
228
229impl fmt::Display for Reference {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 f.write_str(&self.to_hex())
232 }
233}
234
235impl fmt::LowerHex for Reference {
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 f.write_str(&self.to_hex())
238 }
239}
240
241impl FromStr for Reference {
242 type Err = Error;
243 fn from_str(s: &str) -> Result<Self, Self::Err> {
244 Self::from_hex(s)
245 }
246}
247
248impl serde::Serialize for Reference {
249 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
250 s.serialize_str(&self.to_hex())
251 }
252}
253
254impl<'de> serde::Deserialize<'de> for Reference {
255 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
256 let s = String::deserialize(d)?;
257 Self::from_hex(&s).map_err(serde::de::Error::custom)
258 }
259}
260
261define_typed_bytes!(
264 BatchId, BATCH_ID_LENGTH, "BatchId"
266);
267
268define_typed_bytes!(
269 TransactionId, TRANSACTION_ID_LENGTH, "TransactionId"
271);
272
273define_typed_bytes!(
274 PeerAddress, PEER_ADDRESS_LENGTH, "PeerAddress"
276);
277
278define_typed_bytes!(
281 Identifier, IDENTIFIER_LENGTH, "Identifier"
284);
285
286impl Identifier {
287 pub fn from_string(s: &str) -> Self {
290 let mut h = Keccak256::new();
291 h.update(s.as_bytes());
292 let out = h.finalize();
293 let mut a = [0u8; IDENTIFIER_LENGTH];
294 a.copy_from_slice(&out);
295 Self(a)
296 }
297}
298
299define_typed_bytes!(
300 Topic, TOPIC_LENGTH, "Topic"
303);
304
305impl Topic {
306 pub fn from_string(s: &str) -> Self {
309 let mut h = Keccak256::new();
310 h.update(s.as_bytes());
311 let out = h.finalize();
312 let mut a = [0u8; TOPIC_LENGTH];
313 a.copy_from_slice(&out);
314 Self(a)
315 }
316}
317
318define_typed_bytes!(
321 EthAddress, ETH_ADDRESS_LENGTH, "EthAddress"
324);
325
326impl EthAddress {
327 pub fn to_checksum(&self) -> String {
329 let lower = self.to_hex();
330 let mut h = Keccak256::new();
331 h.update(lower.as_bytes());
332 let hash = h.finalize();
333 let mut out = String::with_capacity(2 + lower.len());
334 out.push_str("0x");
335 for (i, c) in lower.chars().enumerate() {
336 if c.is_ascii_digit() {
337 out.push(c);
338 } else {
339 let nibble = if i % 2 == 0 {
341 hash[i / 2] >> 4
342 } else {
343 hash[i / 2] & 0x0f
344 };
345 if nibble >= 8 {
346 out.push(c.to_ascii_uppercase());
347 } else {
348 out.push(c);
349 }
350 }
351 }
352 out
353 }
354}
355
356define_typed_bytes!(
359 Span, SPAN_LENGTH, "Span"
361);
362
363impl Span {
364 pub fn from_u64(n: u64) -> Self {
366 Self(n.to_le_bytes())
367 }
368
369 pub fn to_u64(&self) -> u64 {
371 u64::from_le_bytes(self.0)
372 }
373}
374
375define_typed_bytes!(
378 FeedIndex, FEED_INDEX_LENGTH, "FeedIndex"
381);
382
383impl FeedIndex {
384 pub const MINUS_ONE: FeedIndex = FeedIndex([0xff; FEED_INDEX_LENGTH]);
386
387 pub fn from_u64(n: u64) -> Self {
389 Self(n.to_be_bytes())
390 }
391
392 pub fn to_u64(&self) -> u64 {
394 u64::from_be_bytes(self.0)
395 }
396
397 pub fn next(&self) -> Self {
399 if self == &Self::MINUS_ONE {
400 Self::from_u64(0)
401 } else {
402 Self::from_u64(self.to_u64() + 1)
403 }
404 }
405}
406
407define_typed_bytes!(
410 Signature, SIGNATURE_LENGTH, "Signature"
413);
414
415#[cfg(test)]
421mod tests {
422 use super::*;
423
424 #[test]
425 fn hex_round_trip_with_and_without_0x_prefix() {
426 let hex = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
427 let a = BatchId::from_hex(hex).unwrap();
428 let b = BatchId::from_hex(&format!("0x{hex}")).unwrap();
429 assert_eq!(a, b);
430 assert_eq!(a.to_hex(), hex);
431 }
432
433 #[test]
434 fn fixed_length_rejects_wrong_size() {
435 assert!(BatchId::new(&[0u8; 31]).is_err());
436 assert!(BatchId::new(&[0u8; 33]).is_err());
437 assert!(BatchId::new(&[0u8; 32]).is_ok());
438 }
439
440 #[test]
441 fn reference_accepts_32_or_64() {
442 let r32 = "ab".repeat(32);
443 let r64 = "cd".repeat(64);
444 let a = Reference::from_hex(&r32).unwrap();
445 let b = Reference::from_hex(&r64).unwrap();
446 assert!(!a.is_encrypted());
447 assert!(b.is_encrypted());
448 assert_eq!(a.to_hex(), r32);
449 assert_eq!(b.to_hex(), r64);
450 assert!(Reference::from_hex(&"ab".repeat(31)).is_err());
451 assert!(Reference::from_hex(&"ab".repeat(48)).is_err());
452 }
453
454 #[test]
455 fn identifier_from_string_is_keccak256_utf8() {
456 let want = "1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8";
458 let id = Identifier::from_string("hello");
459 assert_eq!(id.to_hex(), want);
460 }
461
462 #[test]
463 fn topic_from_string_is_keccak256_utf8() {
464 let want_a = Topic::from_string("my-topic");
465 let want_b = Topic::from_string("my-topic");
466 assert_eq!(want_a, want_b);
467 assert_ne!(want_a, Topic::from_string("other"));
468 }
469
470 #[test]
471 fn eth_address_eip55_checksum() {
472 let raw = "fb6916095ca1df60bb79ce92ce3ea74c37c5d359";
473 let addr = EthAddress::from_hex(raw).unwrap();
474 assert_eq!(
475 addr.to_checksum(),
476 "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"
477 );
478 }
479
480 #[test]
481 fn span_round_trip_little_endian() {
482 for n in [0u64, 1, 4096, 1 << 40] {
483 let s = Span::from_u64(n);
484 assert_eq!(s.to_u64(), n);
485 }
486 assert_eq!(Span::from_u64(1).as_bytes(), &[1, 0, 0, 0, 0, 0, 0, 0]);
488 }
489
490 #[test]
491 fn feed_index_round_trip_big_endian_and_next() {
492 for n in [0u64, 1, 100, (1u64 << 32) - 1] {
493 assert_eq!(FeedIndex::from_u64(n).to_u64(), n);
494 }
495 assert_eq!(FeedIndex::from_u64(1).as_bytes(), &[0, 0, 0, 0, 0, 0, 0, 1]);
497 assert_eq!(FeedIndex::from_u64(5).next().to_u64(), 6);
498 assert_eq!(FeedIndex::MINUS_ONE.next().to_u64(), 0);
499 }
500
501 #[test]
502 fn serde_round_trip() {
503 let r = Reference::from_hex(&"ab".repeat(32)).unwrap();
504 let json = serde_json::to_string(&r).unwrap();
505 assert_eq!(json, format!("\"{}\"", "ab".repeat(32)));
506 let r2: Reference = serde_json::from_str(&json).unwrap();
507 assert_eq!(r, r2);
508 }
509
510 #[test]
511 fn from_str_works() {
512 let id: BatchId = "00".repeat(32).parse().unwrap();
513 assert_eq!(id.as_bytes(), &[0u8; 32]);
514 }
515
516 #[test]
517 fn debug_format_includes_type_name() {
518 let id = BatchId::new(&[0xab; 32]).unwrap();
519 let s = format!("{id:?}");
520 assert!(s.starts_with("BatchId("));
521 assert!(s.contains(&"ab".repeat(32)));
522 }
523}