1use std::fmt;
7
8use bsv_primitives::hash::{hash160, sha256d};
9
10use crate::ScriptError;
11
12const MAINNET_P2PKH: u8 = 0x00;
14const TESTNET_P2PKH: u8 = 0x6f;
16
17#[derive(Clone, Debug, PartialEq, Eq)]
19pub enum Network {
20 Mainnet,
22 Testnet,
24}
25
26#[derive(Clone, Debug, PartialEq, Eq)]
31pub struct Address {
32 pub address_string: String,
34 pub public_key_hash: [u8; 20],
36 pub network: Network,
38}
39
40impl Address {
41 pub fn from_string(addr: &str) -> Result<Self, ScriptError> {
52 let decoded = bs58::decode(addr)
53 .into_vec()
54 .map_err(|_| ScriptError::InvalidAddress(format!("bad char for '{}'", addr)))?;
55
56 if decoded.len() != 25 {
57 return Err(ScriptError::InvalidAddressLength(addr.to_string()));
58 }
59
60 let checksum = sha256d(&decoded[..21]);
62 if decoded[21..25] != checksum[..4] {
63 return Err(ScriptError::EncodingChecksumFailed);
64 }
65
66 let network = match decoded[0] {
67 MAINNET_P2PKH => Network::Mainnet,
68 TESTNET_P2PKH => Network::Testnet,
69 _ => return Err(ScriptError::UnsupportedAddress(addr.to_string())),
70 };
71
72 let mut pkh = [0u8; 20];
73 pkh.copy_from_slice(&decoded[1..21]);
74
75 Ok(Address {
76 address_string: addr.to_string(),
77 public_key_hash: pkh,
78 network,
79 })
80 }
81
82 pub fn from_public_key_hash(hash: &[u8; 20], network: Network) -> Self {
91 let version = match network {
92 Network::Mainnet => MAINNET_P2PKH,
93 Network::Testnet => TESTNET_P2PKH,
94 };
95
96 let mut payload = Vec::with_capacity(25);
97 payload.push(version);
98 payload.extend_from_slice(hash);
99 let checksum = sha256d(&payload);
100 payload.extend_from_slice(&checksum[..4]);
101
102 let address_string = bs58::encode(&payload).into_string();
103
104 Address {
105 address_string,
106 public_key_hash: *hash,
107 network,
108 }
109 }
110
111 pub fn from_public_key_string(pub_key_hex: &str, mainnet: bool) -> Result<Self, ScriptError> {
122 let pub_key_bytes = hex::decode(pub_key_hex)?;
123 let h = hash160(&pub_key_bytes);
124 let network = if mainnet {
125 Network::Mainnet
126 } else {
127 Network::Testnet
128 };
129 Ok(Self::from_public_key_hash(&h, network))
130 }
131}
132
133impl fmt::Display for Address {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 write!(f, "{}", self.address_string)
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
151
152 const TEST_PUBLIC_KEY_HASH: &str = "00ac6144c4db7b5790f343cf0477a65fb8a02eb7";
154
155 #[test]
161 fn test_from_string_mainnet() {
162 let address_str = "1E7ucTTWRTahCyViPhxSMor2pj4VGQdFMr";
163 let addr = Address::from_string(address_str).expect("should parse mainnet");
164 assert_eq!(addr.address_string, address_str);
165 assert_eq!(
166 hex::encode(addr.public_key_hash),
167 "8fe80c75c9560e8b56ed64ea3c26e18d2c52211b"
168 );
169 assert_eq!(addr.network, Network::Mainnet);
170 }
171
172 #[test]
178 fn test_from_string_testnet() {
179 let address_str = "mtdruWYVEV1wz5yL7GvpBj4MgifCB7yhPd";
180 let addr = Address::from_string(address_str).expect("should parse testnet");
181 assert_eq!(addr.address_string, address_str);
182 assert_eq!(
183 hex::encode(addr.public_key_hash),
184 "8fe80c75c9560e8b56ed64ea3c26e18d2c52211b"
185 );
186 assert_eq!(addr.network, Network::Testnet);
187 }
188
189 #[test]
191 fn test_from_string_same_pkh_different_networks() {
192 let mainnet_addr = Address::from_string("1E7ucTTWRTahCyViPhxSMor2pj4VGQdFMr")
193 .expect("mainnet should parse");
194 let testnet_addr = Address::from_string("mtdruWYVEV1wz5yL7GvpBj4MgifCB7yhPd")
195 .expect("testnet should parse");
196 assert_eq!(mainnet_addr.public_key_hash, testnet_addr.public_key_hash);
197 }
198
199 #[test]
205 fn test_from_string_short_address() {
206 let result = Address::from_string("ADD8E55");
207 assert!(result.is_err());
208 }
209
210 #[test]
212 fn test_from_string_unsupported_version() {
213 let result = Address::from_string("27BvY7rFguYQvEL872Y7Fo77Y3EBApC2EK");
214 assert!(result.is_err());
215 }
216
217 #[test]
223 fn test_from_public_key_string_mainnet() {
224 let addr = Address::from_public_key_string(
225 "026cf33373a9f3f6c676b75b543180703df225f7f8edbffedc417718a8ad4e89ce",
226 true,
227 )
228 .expect("should create address");
229 assert_eq!(hex::encode(addr.public_key_hash), TEST_PUBLIC_KEY_HASH);
230 assert_eq!(addr.address_string, "114ZWApV4EEU8frr7zygqQcB1V2BodGZuS");
231 assert_eq!(addr.network, Network::Mainnet);
232 }
233
234 #[test]
236 fn test_from_public_key_string_testnet() {
237 let addr = Address::from_public_key_string(
238 "026cf33373a9f3f6c676b75b543180703df225f7f8edbffedc417718a8ad4e89ce",
239 false,
240 )
241 .expect("should create address");
242 assert_eq!(hex::encode(addr.public_key_hash), TEST_PUBLIC_KEY_HASH);
243 assert_eq!(addr.address_string, "mfaWoDuTsFfiunLTqZx4fKpVsUctiDV9jk");
244 assert_eq!(addr.network, Network::Testnet);
245 }
246
247 #[test]
249 fn test_from_public_key_string_invalid() {
250 let result = Address::from_public_key_string("invalid_pubkey", true);
251 assert!(result.is_err());
252 }
253
254 #[test]
260 fn test_from_public_key_hash_mainnet() {
261 let hash_bytes = hex::decode(TEST_PUBLIC_KEY_HASH).expect("valid hex");
262 let mut hash = [0u8; 20];
263 hash.copy_from_slice(&hash_bytes);
264 let addr = Address::from_public_key_hash(&hash, Network::Mainnet);
265 assert_eq!(addr.public_key_hash, hash);
266 assert_eq!(addr.address_string, "114ZWApV4EEU8frr7zygqQcB1V2BodGZuS");
267 assert_eq!(addr.network, Network::Mainnet);
268 }
269
270 #[test]
272 fn test_from_public_key_hash_testnet() {
273 let hash_bytes = hex::decode(TEST_PUBLIC_KEY_HASH).expect("valid hex");
274 let mut hash = [0u8; 20];
275 hash.copy_from_slice(&hash_bytes);
276 let addr = Address::from_public_key_hash(&hash, Network::Testnet);
277 assert_eq!(addr.public_key_hash, hash);
278 assert_eq!(addr.address_string, "mfaWoDuTsFfiunLTqZx4fKpVsUctiDV9jk");
279 assert_eq!(addr.network, Network::Testnet);
280 }
281
282 #[test]
288 fn test_address_to_string_roundtrip_mainnet() {
289 let address_str = "1E7ucTTWRTahCyViPhxSMor2pj4VGQdFMr";
290 let addr = Address::from_string(address_str).expect("should parse");
291 assert_eq!(format!("{}", addr), address_str);
292 }
293
294 #[test]
296 fn test_address_to_string_roundtrip_testnet() {
297 let address_str = "mtdruWYVEV1wz5yL7GvpBj4MgifCB7yhPd";
298 let addr = Address::from_string(address_str).expect("should parse");
299 assert_eq!(format!("{}", addr), address_str);
300 }
301
302 #[test]
304 fn test_public_key_hash_to_address_to_string_roundtrip() {
305 let hash_bytes = hex::decode(TEST_PUBLIC_KEY_HASH).expect("valid hex");
306 let mut hash = [0u8; 20];
307 hash.copy_from_slice(&hash_bytes);
308
309 let addr = Address::from_public_key_hash(&hash, Network::Mainnet);
310 let parsed = Address::from_string(&addr.address_string).expect("should parse back");
311
312 assert_eq!(addr.public_key_hash, parsed.public_key_hash);
313 assert_eq!(addr.address_string, parsed.address_string);
314 assert_eq!(addr.network, parsed.network);
315 }
316}