1use std::fmt;
21use std::str::FromStr;
22
23use crate::primitives::ec::PublicKey;
24use crate::primitives::encoding::{from_base58_check, to_base58_check};
25use crate::primitives::hash::hash160;
26use crate::{Error, Result};
27
28const MAINNET_PREFIX: u8 = 0x00;
30
31const TESTNET_PREFIX: u8 = 0x6f;
33
34#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct Address {
47 pub_key_hash: [u8; 20],
49 prefix: u8,
51}
52
53impl Address {
54 pub fn new_from_string(address: &str) -> Result<Self> {
76 let (version, payload) = from_base58_check(address)?;
77
78 if version.len() != 1 {
79 return Err(Error::InvalidAddress(format!(
80 "invalid address version length for '{}'",
81 address
82 )));
83 }
84
85 let prefix = version[0];
86 match prefix {
87 MAINNET_PREFIX | TESTNET_PREFIX => {}
88 _ => {
89 return Err(Error::UnsupportedAddress(address.to_string()));
90 }
91 }
92
93 if payload.len() != 20 {
94 return Err(Error::InvalidAddressLength(address.to_string()));
95 }
96
97 let mut pub_key_hash = [0u8; 20];
98 pub_key_hash.copy_from_slice(&payload);
99
100 Ok(Self {
101 pub_key_hash,
102 prefix,
103 })
104 }
105
106 pub fn new_from_public_key_hash(hash: &[u8], mainnet: bool) -> Result<Self> {
126 if hash.len() != 20 {
127 return Err(Error::InvalidDataLength {
128 expected: 20,
129 actual: hash.len(),
130 });
131 }
132
133 let mut pub_key_hash = [0u8; 20];
134 pub_key_hash.copy_from_slice(hash);
135
136 let prefix = if mainnet {
137 MAINNET_PREFIX
138 } else {
139 TESTNET_PREFIX
140 };
141
142 Ok(Self {
143 pub_key_hash,
144 prefix,
145 })
146 }
147
148 pub fn new_from_public_key(public_key: &PublicKey, mainnet: bool) -> Result<Self> {
172 let h = hash160(&public_key.to_compressed());
173 Self::new_from_public_key_hash(&h, mainnet)
174 }
175
176 pub fn public_key_hash(&self) -> &[u8] {
181 &self.pub_key_hash
182 }
183
184 pub fn prefix(&self) -> u8 {
189 self.prefix
190 }
191
192 pub fn is_mainnet(&self) -> bool {
194 self.prefix == MAINNET_PREFIX
195 }
196
197 pub fn is_valid_address(address: &str) -> bool {
215 Self::new_from_string(address).is_ok()
216 }
217}
218
219impl fmt::Display for Address {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 write!(f, "{}", to_base58_check(&self.pub_key_hash, &[self.prefix]))
222 }
223}
224
225impl FromStr for Address {
226 type Err = Error;
227
228 fn from_str(s: &str) -> Result<Self> {
229 Self::new_from_string(s)
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236 use crate::primitives::ec::PrivateKey;
237 use crate::primitives::encoding::{from_hex, to_hex};
238
239 const TEST_PUBLIC_KEY_HEX: &str =
240 "026cf33373a9f3f6c676b75b543180703df225f7f8edbffedc417718a8ad4e89ce";
241 const TEST_PUBLIC_KEY_HASH: &str = "00ac6144c4db7b5790f343cf0477a65fb8a02eb7";
242
243 #[test]
244 fn test_new_from_string_mainnet() {
245 let address_str = "1E7ucTTWRTahCyViPhxSMor2pj4VGQdFMr";
246 let addr = Address::new_from_string(address_str).unwrap();
247
248 assert_eq!(
249 to_hex(addr.public_key_hash()),
250 "8fe80c75c9560e8b56ed64ea3c26e18d2c52211b"
251 );
252 assert_eq!(addr.to_string(), address_str);
253 assert!(addr.is_mainnet());
254 }
255
256 #[test]
257 fn test_new_from_string_testnet() {
258 let address_str = "mtdruWYVEV1wz5yL7GvpBj4MgifCB7yhPd";
259 let addr = Address::new_from_string(address_str).unwrap();
260
261 assert_eq!(
262 to_hex(addr.public_key_hash()),
263 "8fe80c75c9560e8b56ed64ea3c26e18d2c52211b"
264 );
265 assert_eq!(addr.to_string(), address_str);
266 assert!(!addr.is_mainnet());
267 }
268
269 #[test]
270 fn test_new_from_string_short_address() {
271 let result = Address::new_from_string("ADD8E55");
272 assert!(result.is_err());
273 }
274
275 #[test]
276 fn test_new_from_string_unsupported_address() {
277 let result = Address::new_from_string("27BvY7rFguYQvEL872Y7Fo77Y3EBApC2EK");
278 assert!(result.is_err());
279 }
280
281 #[test]
282 fn test_new_from_public_key_hash_mainnet() {
283 let hash = from_hex(TEST_PUBLIC_KEY_HASH).unwrap();
284 let addr = Address::new_from_public_key_hash(&hash, true).unwrap();
285
286 assert_eq!(to_hex(addr.public_key_hash()), TEST_PUBLIC_KEY_HASH);
287 assert_eq!(addr.to_string(), "114ZWApV4EEU8frr7zygqQcB1V2BodGZuS");
288 }
289
290 #[test]
291 fn test_new_from_public_key_hash_testnet() {
292 let hash = from_hex(TEST_PUBLIC_KEY_HASH).unwrap();
293 let addr = Address::new_from_public_key_hash(&hash, false).unwrap();
294
295 assert_eq!(to_hex(addr.public_key_hash()), TEST_PUBLIC_KEY_HASH);
296 assert_eq!(addr.to_string(), "mfaWoDuTsFfiunLTqZx4fKpVsUctiDV9jk");
297 }
298
299 #[test]
300 fn test_new_from_public_key_hash_invalid_length() {
301 let result = Address::new_from_public_key_hash(&[0u8; 19], true);
302 assert!(result.is_err());
303
304 let result = Address::new_from_public_key_hash(&[0u8; 21], true);
305 assert!(result.is_err());
306 }
307
308 #[test]
309 fn test_new_from_public_key_mainnet() {
310 let pubkey = PublicKey::from_hex(TEST_PUBLIC_KEY_HEX).unwrap();
311 let addr = Address::new_from_public_key(&pubkey, true).unwrap();
312
313 assert_eq!(to_hex(addr.public_key_hash()), TEST_PUBLIC_KEY_HASH);
314 assert_eq!(addr.to_string(), "114ZWApV4EEU8frr7zygqQcB1V2BodGZuS");
315 }
316
317 #[test]
318 fn test_new_from_public_key_testnet() {
319 let pubkey = PublicKey::from_hex(TEST_PUBLIC_KEY_HEX).unwrap();
320 let addr = Address::new_from_public_key(&pubkey, false).unwrap();
321
322 assert_eq!(to_hex(addr.public_key_hash()), TEST_PUBLIC_KEY_HASH);
323 assert_eq!(addr.to_string(), "mfaWoDuTsFfiunLTqZx4fKpVsUctiDV9jk");
324 }
325
326 #[test]
327 fn test_roundtrip_mainnet() {
328 let address_str = "114ZWApV4EEU8frr7zygqQcB1V2BodGZuS";
329 let addr = Address::new_from_string(address_str).unwrap();
330 assert_eq!(addr.to_string(), address_str);
331 }
332
333 #[test]
334 fn test_roundtrip_testnet() {
335 let address_str = "mfaWoDuTsFfiunLTqZx4fKpVsUctiDV9jk";
336 let addr = Address::new_from_string(address_str).unwrap();
337 assert_eq!(addr.to_string(), address_str);
338 }
339
340 #[test]
341 fn test_from_str_trait() {
342 let addr: Address = "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH".parse().unwrap();
343 assert!(addr.is_mainnet());
344 }
345
346 #[test]
347 fn test_display_trait() {
348 let addr = Address::new_from_string("1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH").unwrap();
349 let displayed = format!("{}", addr);
350 assert_eq!(displayed, "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH");
351 }
352
353 #[test]
354 fn test_is_valid_address() {
355 assert!(Address::is_valid_address(
356 "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH"
357 ));
358 assert!(Address::is_valid_address(
359 "114ZWApV4EEU8frr7zygqQcB1V2BodGZuS"
360 ));
361 assert!(Address::is_valid_address(
362 "mfaWoDuTsFfiunLTqZx4fKpVsUctiDV9jk"
363 ));
364 assert!(!Address::is_valid_address("invalid"));
365 assert!(!Address::is_valid_address(""));
366 }
367
368 #[test]
369 fn test_invalid_checksum() {
370 let result = Address::new_from_string("1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN3");
372 assert!(result.is_err());
373 }
374
375 #[test]
376 fn test_generator_point_address() {
377 let pubkey = PublicKey::from_hex(
379 "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
380 )
381 .unwrap();
382 let addr = Address::new_from_public_key(&pubkey, true).unwrap();
383 assert_eq!(addr.to_string(), "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH");
384 }
385
386 #[test]
387 fn test_private_key_address_consistency() {
388 let private_key = PrivateKey::from_hex(
390 "0000000000000000000000000000000000000000000000000000000000000001",
391 )
392 .unwrap();
393 let pubkey = private_key.public_key();
394
395 let addr_from_address = Address::new_from_public_key(&pubkey, true).unwrap();
396 let addr_from_pubkey = pubkey.to_address();
397
398 assert_eq!(addr_from_address.to_string(), addr_from_pubkey);
399 }
400
401 #[test]
402 fn test_locking_script_to_address() {
403 use crate::script::template::ScriptTemplate;
404 use crate::script::templates::P2PKH;
405
406 let private_key = PrivateKey::from_hex(
407 "0000000000000000000000000000000000000000000000000000000000000001",
408 )
409 .unwrap();
410 let pubkey = private_key.public_key();
411 let pubkey_hash = pubkey.hash160();
412
413 let locking = P2PKH::new().lock(&pubkey_hash).unwrap();
415
416 let addr = locking.to_address();
418 assert!(addr.is_some());
419 let addr = addr.unwrap();
420 assert_eq!(addr.to_string(), pubkey.to_address());
421 }
422
423 #[test]
424 fn test_locking_script_to_address_non_p2pkh() {
425 use crate::script::LockingScript;
426
427 let op_return = LockingScript::from_asm("OP_RETURN").unwrap();
429 assert!(op_return.to_address().is_none());
430
431 let p2sh =
433 LockingScript::from_hex("a914000000000000000000000000000000000000000087").unwrap();
434 assert!(p2sh.to_address().is_none());
435
436 let empty = LockingScript::new();
438 assert!(empty.to_address().is_none());
439 }
440}