sumchain_primitives/
address.rs1use serde::{Deserialize, Serialize};
7use std::fmt;
8
9use crate::{PrimitiveError, Result};
10
11#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord, Serialize, Deserialize)]
13pub struct Address([u8; 20]);
14
15impl Address {
16 pub const SIZE: usize = 20;
18
19 pub const ZERO: Address = Address([0u8; 20]);
21
22 pub fn new(bytes: [u8; 20]) -> Self {
24 Address(bytes)
25 }
26
27 pub fn from_slice(slice: &[u8]) -> Result<Self> {
29 if slice.len() != Self::SIZE {
30 return Err(PrimitiveError::InvalidLength {
31 expected: Self::SIZE,
32 got: slice.len(),
33 });
34 }
35 let mut bytes = [0u8; 20];
36 bytes.copy_from_slice(slice);
37 Ok(Address(bytes))
38 }
39
40 pub fn from_public_key(pubkey: &[u8; 32]) -> Self {
43 let hash = blake3::hash(pubkey);
44 let hash_bytes = hash.as_bytes();
45 let mut bytes = [0u8; 20];
46 bytes.copy_from_slice(&hash_bytes[12..32]); Address(bytes)
48 }
49
50 pub fn from_base58(s: &str) -> Result<Self> {
53 let decoded = bs58::decode(s)
54 .into_vec()
55 .map_err(|e| PrimitiveError::InvalidBase58(e.to_string()))?;
56
57 if decoded.len() != Self::SIZE + 4 {
58 return Err(PrimitiveError::InvalidLength {
59 expected: Self::SIZE + 4,
60 got: decoded.len(),
61 });
62 }
63
64 let (addr_bytes, checksum) = decoded.split_at(Self::SIZE);
65
66 let hash1 = blake3::hash(addr_bytes);
68 let hash2 = blake3::hash(hash1.as_bytes());
69
70 if &hash2.as_bytes()[0..4] != checksum {
71 return Err(PrimitiveError::InvalidChecksum);
72 }
73
74 Self::from_slice(addr_bytes)
75 }
76
77 pub fn to_base58(&self) -> String {
79 let hash1 = blake3::hash(&self.0);
80 let hash2 = blake3::hash(hash1.as_bytes());
81
82 let mut with_checksum = Vec::with_capacity(Self::SIZE + 4);
83 with_checksum.extend_from_slice(&self.0);
84 with_checksum.extend_from_slice(&hash2.as_bytes()[0..4]);
85
86 bs58::encode(with_checksum).into_string()
87 }
88
89 pub fn from_hex(s: &str) -> Result<Self> {
91 let s = s.strip_prefix("0x").unwrap_or(s);
92 let bytes = hex::decode(s).map_err(|e| PrimitiveError::InvalidHex(e.to_string()))?;
93 Self::from_slice(&bytes)
94 }
95
96 pub fn to_hex(&self) -> String {
98 format!("0x{}", hex::encode(self.0))
99 }
100
101 pub fn as_bytes(&self) -> &[u8; 20] {
103 &self.0
104 }
105
106 pub fn is_zero(&self) -> bool {
108 self.0 == [0u8; 20]
109 }
110}
111
112impl fmt::Debug for Address {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 write!(f, "Address({})", self.to_base58())
115 }
116}
117
118impl fmt::Display for Address {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 write!(f, "{}", self.to_base58())
121 }
122}
123
124impl AsRef<[u8]> for Address {
125 fn as_ref(&self) -> &[u8] {
126 &self.0
127 }
128}
129
130impl From<[u8; 20]> for Address {
131 fn from(bytes: [u8; 20]) -> Self {
132 Address(bytes)
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn test_from_public_key() {
142 let pubkey = [0u8; 32];
143 let addr = Address::from_public_key(&pubkey);
144 assert!(!addr.is_zero()); }
146
147 #[test]
148 fn test_base58_roundtrip() {
149 let addr = Address::from_public_key(&[42u8; 32]);
150 let b58 = addr.to_base58();
151 let addr2 = Address::from_base58(&b58).unwrap();
152 assert_eq!(addr, addr2);
153 }
154
155 #[test]
156 fn test_hex_roundtrip() {
157 let addr = Address::from_public_key(&[1u8; 32]);
158 let hex = addr.to_hex();
159 let addr2 = Address::from_hex(&hex).unwrap();
160 assert_eq!(addr, addr2);
161 }
162
163 #[test]
164 fn test_invalid_checksum() {
165 let addr = Address::from_public_key(&[1u8; 32]);
166 let mut b58 = addr.to_base58();
167
168 let chars: Vec<char> = b58.chars().collect();
170 let last = chars.last().unwrap();
171 let new_last = if *last == 'a' { 'b' } else { 'a' };
172 b58.pop();
173 b58.push(new_last);
174
175 let result = Address::from_base58(&b58);
176 assert!(result.is_err());
177 }
178
179 #[test]
180 fn test_zero_address() {
181 assert!(Address::ZERO.is_zero());
182 }
183}