1use std::fmt;
2use std::str::FromStr;
3
4use base58::ToBase58;
5use bitcoin_hashes::hash160::Hash as Hash160;
6use bitcoin_hashes::sha256d::Hash as SHA256d;
7use bitcoin_hashes::Hash;
8use cashaddr::CashEnc;
9#[cfg(feature = "eth")]
10use k256::elliptic_curve::sec1::ToEncodedPoint;
11#[cfg(feature = "eth")]
12use sha3::{Digest, Keccak256};
13
14use super::{Error, Result};
15
16#[non_exhaustive]
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub enum AddressFormat {
28 Bitcoin(BitcoinFormat),
32 BitcoinTestnet,
33 #[cfg(feature = "eth")]
34 Ethereum,
35 BitcoinCash(Option<String>),
40 Litecoin,
41 LitecoinCash,
42 Dogecoin,
43 Dash,
44 Ripple,
45 Namecoin,
46 Asiacoin,
47 BitcoinGold,
48 Beetlecoin,
49 Blackcoin,
50 Clams,
51 Europecoin,
52 Feathercoin,
53 Fujicoin,
54 Hempcoin,
55 Peercoin,
56 Potcoin,
57 ShadowCash,
58 Stratis,
59 Syscoin,
60 VCash,
61 Verge,
62 Vertcoin,
63 ZCash,
64}
65
66impl Default for AddressFormat {
67 fn default() -> Self {
68 Self::Bitcoin(Default::default())
69 }
70}
71
72#[non_exhaustive]
78#[derive(Debug, Clone, PartialEq, Eq, Default)]
79pub enum BitcoinFormat {
80 #[default]
81 Legacy,
83 P2SHWPKH,
86}
87
88impl FromStr for AddressFormat {
89 type Err = Error;
90
91 fn from_str(s: &str) -> Result<Self> {
100 if s.to_lowercase().starts_with("bch") {
101 return match s.find(':') {
102 None => Ok(Self::BitcoinCash(None)),
103 Some(position) => Ok(Self::BitcoinCash(Some(s[position + 1..].to_owned()))),
104 };
105 }
106 match s.to_lowercase().as_ref() {
107 "btc" | "bitcoin" => Ok(Self::Bitcoin(BitcoinFormat::Legacy)),
108 "btc:sw" => Ok(Self::Bitcoin(BitcoinFormat::P2SHWPKH)),
109 "btctest" | "bitcointestnet" => Ok(Self::BitcoinTestnet),
110 "ltc" | "litecoin" => Ok(Self::Litecoin),
111 "lcc" | "litecoincash" => Ok(Self::LitecoinCash),
112 "doge" | "dogecoin" => Ok(Self::Dogecoin),
113 "dash" => Ok(Self::Dash),
114 "btg" | "bitcoingold" => Ok(Self::BitcoinGold),
115 "xrp" => Ok(Self::Ripple),
116 "erc" | "europecoin" => Ok(Self::Europecoin),
117 "ppc" | "peercoin" => Ok(Self::Peercoin),
118 "sys" | "syscoin" => Ok(Self::Syscoin),
119 "ftc" | "feathercoin" => Ok(Self::Feathercoin),
120 "pot" | "potcoin" => Ok(Self::Potcoin),
121 "nmc" | "namecoin" => Ok(Self::Namecoin),
122 "fjc" | "fujicoin" => Ok(Self::Fujicoin),
123 "xvg" | "verge" => Ok(Self::Verge),
124 "thc" | "hempcoin" => Ok(Self::Hempcoin),
125 "xvc" | "vcash" => Ok(Self::VCash),
126 "zec" | "zcash" => Ok(Self::ZCash),
127 "vtc" | "vertcoin" => Ok(Self::Vertcoin),
128 "strat" | "stratis" => Ok(Self::Stratis),
129 "clam" | "clams" => Ok(Self::Clams),
130 "blk" | "blackcoin" => Ok(Self::Blackcoin),
131 "sdc" | "shadowcash" => Ok(Self::ShadowCash),
132 "beet" | "beetlecoin" => Ok(Self::Beetlecoin),
133 "ac" | "asiacoin" => Ok(Self::Asiacoin),
134 #[cfg(feature = "eth")]
135 "eth" | "ethereum" => Ok(Self::Ethereum),
136 _ => Err(Error::ParseFailure(format!(
137 "Could not parse {} to AddressFormat",
138 s
139 ))),
140 }
141 }
142}
143
144impl fmt::Display for AddressFormat {
145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 match self {
148 Self::Bitcoin(_) => write!(f, "Bitcoin"),
149 Self::BitcoinTestnet => write!(f, "Bitcoin Testnet"),
150 Self::BitcoinCash(_) => write!(f, "Bitcoin Cash"),
151 Self::BitcoinGold => write!(f, "Bitcoin Gold"),
152 Self::Litecoin => write!(f, "Litecoin"),
153 Self::LitecoinCash => write!(f, "Litecoin Cash"),
154 Self::Dogecoin => write!(f, "Dogecoin"),
155 Self::Dash => write!(f, "Dash"),
156 Self::Europecoin => write!(f, "Europecoin"),
157 Self::Ripple => write!(f, "Ripple"),
158 Self::Peercoin => write!(f, "Peercoin"),
159 Self::Syscoin => write!(f, "Syscoin"),
160 Self::Feathercoin => write!(f, "Feathercoin"),
161 Self::Potcoin => write!(f, "Potcoin"),
162 Self::Namecoin => write!(f, "Namecoin"),
163 Self::Fujicoin => write!(f, "Fujicoin"),
164 Self::Verge => write!(f, "Verge"),
165 Self::Hempcoin => write!(f, "Hempcoin"),
166 Self::VCash => write!(f, "VCash"),
167 Self::ZCash => write!(f, "ZCash"),
168 Self::Vertcoin => write!(f, "Vertcoin"),
169 Self::Stratis => write!(f, "Stratis"),
170 Self::Clams => write!(f, "Clams"),
171 Self::Blackcoin => write!(f, "Blackcoin"),
172 Self::ShadowCash => write!(f, "ShadowCash"),
173 Self::Beetlecoin => write!(f, "Beetlecoin"),
174 Self::Asiacoin => write!(f, "Asiacoin"),
175 #[cfg(feature = "eth")]
176 Self::Ethereum => write!(f, "Ethereum"),
177 }
178 }
179}
180
181fn pubkey_hash(pubkey: &[u8], prefix: u8) -> [u8; 25] {
182 let pubkey_hash = Hash160::hash(pubkey);
183 let mut raw_final = [prefix; 25];
184 raw_final[1..21].copy_from_slice(&pubkey_hash);
185 let checksum = SHA256d::hash(&raw_final[..21]);
186 raw_final[21..].copy_from_slice(&checksum[..4]);
187 raw_final
188}
189
190impl AddressFormat {
191 pub fn address_prefix(&self) -> Option<u8> {
193 use AddressFormat::*;
194 match self {
195 Bitcoin(BitcoinFormat::Legacy) => Some(0),
196 Bitcoin(BitcoinFormat::P2SHWPKH) => None,
197 BitcoinTestnet => Some(0x6F),
198 BitcoinCash(_) => Some(0),
199 Litecoin => Some(0x30),
200 LitecoinCash => Some(0x1C),
201 Dogecoin => Some(0x1E),
202 Dash => Some(0x4C),
203 BitcoinGold => Some(0x26),
204 Europecoin => Some(0x21),
205 Ripple => Some(0x00),
206 Peercoin => Some(0x37),
207 Syscoin => Some(0x3F),
208 Feathercoin => Some(0x0E),
209 Potcoin => Some(0x37),
210 Namecoin => Some(0x34),
211 Fujicoin => Some(0x24),
212 Verge => Some(0x1E),
213 Hempcoin => Some(0x28),
214 VCash => Some(0x47),
215 Vertcoin => Some(0x47),
216 Stratis => Some(0x3F),
217 Clams => Some(0x89),
218 Blackcoin => Some(0x19),
219 ShadowCash => Some(0x3F),
220 Beetlecoin => Some(0x1A),
221 Asiacoin => Some(0x17),
222 ZCash => None, #[cfg(feature = "eth")]
224 Ethereum => None,
225 }
226 }
227 pub fn network_name(&self) -> String {
229 self.to_string()
230 }
231 pub fn length(&self) -> usize {
234 match self {
235 Self::BitcoinCash(prefix) => match prefix {
236 Some(prefix) => 43 + prefix.len(),
237 None => 42,
238 },
239 #[cfg(feature = "eth")]
240 Self::Ethereum => 42,
241 _ => 34,
242 }
243 }
244 fn verify_len(actual: usize, expected: usize) -> Result<()> {
245 if actual != expected {
246 Err(Error::InvalidLength { received: actual, expected})
247 } else {
248 Ok(())
249 }
250 }
251 pub(crate) fn derive(&self, pubkey_bytes: impl AsRef<[u8]>) -> Result<String> {
253 use AddressFormat::*;
254 let bytes = pubkey_bytes.as_ref();
255
256 match self {
257 Ripple => {
258 Self::verify_len(bytes.len(), 33)?;
259 let pkh = pubkey_hash(bytes, self.address_prefix().unwrap());
260 Ok(bs58::encode(pkh)
261 .with_alphabet(bs58::Alphabet::RIPPLE)
262 .into_string())
263 }
264 BitcoinCash(prefix) => {
265 let keyhash = Hash160::hash(bytes);
266 let string = match prefix {
267 Some(prefix) => keyhash.encode_p2pkh(prefix).unwrap(),
268 None => {
269 let mut string = keyhash.encode_p2pkh("bitcoincash").unwrap();
270 let position = string.find(':').unwrap();
271 string.replace_range(..position + 1, "");
272 string
273 }
274 };
275 Ok(string)
276 }
277 ZCash => {
278 let pubkey_hash = Hash160::hash(bytes);
279 let mut raw_final: [u8; 26] = [0; 26];
280 raw_final[..2].copy_from_slice(&[0x1C, 0xB8]);
281 raw_final[2..22].copy_from_slice(&pubkey_hash);
282 let checksum = SHA256d::hash(&raw_final[..22]);
283 raw_final[22..].copy_from_slice(&checksum[..4]);
284 Ok(raw_final.to_base58())
285 }
286 Bitcoin(BitcoinFormat::P2SHWPKH) => {
287 let pubkey_hash = Hash160::hash(bytes);
288 let mut raw_final: [u8; 25] = [0x05; 25];
289 let mut buf: [u8; 22] = [0; 22];
290 buf[..2].copy_from_slice(&[0x00, 0x14]);
291 buf[2..].copy_from_slice(&pubkey_hash);
292 raw_final[1..21].copy_from_slice(&Hash160::hash(&buf));
293 let (l, r) = raw_final.split_at_mut(21);
294 r.copy_from_slice(&SHA256d::hash(l)[..4]);
295 Ok(raw_final.to_base58())
296 }
297 #[cfg(feature = "eth")]
298 Ethereum => {
299 let pubkey =
300 k256::PublicKey::from_sec1_bytes(bytes).expect("failed parsing eth bytes");
301 let addr = Keccak256::digest(&pubkey.to_encoded_point(false).as_bytes()[1..]);
302 Ok(eip55::checksum(&hex::encode(&addr[12..])))
303 }
304 _ => {
307 Self::verify_len(bytes.len(), 33)?;
308 Ok(pubkey_hash(bytes, self.address_prefix().unwrap()).to_base58())
309 }
310 }
311 }
312}
313
314
315pub trait Address {
364 fn addr(&self, opts: &AddressFormat) -> Result<String>;
368}
369
370impl Address for crate::PubkeyBytes {
374 fn addr(&self, opts: &AddressFormat) -> Result<String> {
375 opts.derive(self)
376 }
377}
378
379impl Address for [u8] {
383 fn addr(&self, opts: &AddressFormat) -> Result<String> {
384 opts.derive(self)
385 }
386}
387
388
389#[cfg(feature = "secp256k1")]
390impl Address for secp256k1::PublicKey {
391 fn addr(&self, opts: &AddressFormat) -> Result<String> {
392 opts.derive(self.serialize())
393 }
394}
395
396#[cfg(feature = "k256")]
397impl Address for k256::PublicKey {
398 fn addr(&self, opts: &AddressFormat) -> Result<String> {
399 opts.derive(self.to_encoded_point(true).as_bytes())
400 }
401}
402
403#[cfg(feature = "k256")]
404impl Address for k256::ecdsa::VerifyingKey {
405 fn addr(&self, opts: &AddressFormat) -> Result<String> {
406 opts.derive(self.to_encoded_point(true)) }
407}
408
409#[cfg(test)]
410mod tests {
411 use super::{AddressFormat::{self, *}, BitcoinFormat::*, Address};
412 use crate::test_vectors::addr::*;
413 #[cfg(feature = "k256")]
414 use hex_literal::hex;
415
416 #[test]
417 fn parse_network() {
418 assert_eq!("Bitcoin".parse(), Ok(Bitcoin(Legacy)));
419 assert_eq!("BTC".parse(), Ok(Bitcoin(Legacy)));
420 assert_eq!("btc".parse(), Ok(Bitcoin(Legacy)));
421 assert_eq!("litecoin".parse(), Ok(Litecoin));
422 assert_eq!("Litecoin".parse(), Ok(Litecoin));
423 assert_eq!("ltc".parse(), Ok(Litecoin));
424 assert_eq!("LTC".parse(), Ok(Litecoin));
425 assert_eq!("dogecoin".parse(), Ok(Dogecoin));
426 assert_eq!("Dogecoin".parse(), Ok(Dogecoin));
427 assert_eq!("doge".parse(), Ok(Dogecoin));
428 assert_eq!("DOGE".parse(), Ok(Dogecoin));
429 assert_eq!("bch".parse(), Ok(BitcoinCash(None)));
430 assert_eq!(
431 "bch:ligma".parse(),
432 Ok(BitcoinCash(Some("ligma".to_owned())))
433 );
434 #[cfg(feature = "eth")]
435 assert_eq!("eth".parse(), Ok(Ethereum));
436 #[cfg(feature = "eth")]
437 assert_eq!("etheReUm".parse(), Ok(Ethereum));
438 }
439 #[test]
440 fn network_name() {
441 assert_eq!(Bitcoin(Legacy).network_name(), "Bitcoin");
442 assert_eq!(Bitcoin(P2SHWPKH).network_name(), "Bitcoin");
443 assert_eq!(BitcoinCash(None).network_name(), "Bitcoin Cash");
444 assert_eq!(
445 BitcoinCash(Some(String::from("test"))).network_name(),
446 "Bitcoin Cash"
447 );
448 assert_eq!(Litecoin.network_name(), "Litecoin");
449 assert_eq!(Dogecoin.network_name(), "Dogecoin");
450 assert_eq!(Dash.network_name(), "Dash");
451 #[cfg(feature = "eth")]
452 assert_eq!(Ethereum.network_name(), "Ethereum");
453 }
454
455 fn testfun(vectors: &str, opts: &AddressFormat) {
456 for tc in vectors.lines().map(TestCase::from) {
457 assert_eq!(tc.pubkey.addr(opts).as_deref(), Ok(tc.addr));
458 }
459 }
460
461 #[test]
462 fn format_doge() {
463 testfun(DOGE_TEST_VECTORS, &Dogecoin)
464 }
465 #[test]
466 fn format_btc() {
467 testfun(BTC_TEST_VECTORS, &Bitcoin(Legacy))
468 }
469 #[test]
470 fn format_ltc() {
471 testfun(LTC_TEST_VECTORS, &Litecoin)
472 }
473 #[test]
474 fn format_dash() {
475 testfun(DASH_TEST_VECTORS, &Dash)
476 }
477 #[test]
478 fn format_btg() {
479 testfun(BTG_TEST_VECTORS, &BitcoinGold)
480 }
481 #[test]
482 fn format_erc() {
483 testfun(ERC_TEST_VECTORS, &Europecoin)
484 }
485 #[test]
486 fn format_xrp() {
487 testfun(XRP_TEST_VECTORS, &Ripple)
488 }
489 #[test]
490 fn format_ppc() {
491 testfun(PPC_TEST_VECTORS, &Peercoin)
492 }
493 #[test]
494 fn format_bch() {
495 testfun(BCH_TEST_VECTORS, &BitcoinCash(None))
496 }
497 #[test]
498 fn format_sys() {
499 testfun(SYS_TEST_VECTORS, &Syscoin);
500 }
501 #[test]
502 fn format_ftc() {
503 testfun(FTC_TEST_VECTORS, &Feathercoin);
504 }
505 #[test]
506 fn format_pot() {
507 testfun(POT_TEST_VECTORS, &Potcoin);
508 }
509 #[test]
510 fn format_nmc() {
511 testfun(NMC_TEST_VECTORS, &Namecoin);
512 }
513 #[test]
514 fn format_btc_testnet() {
515 testfun(BTCTESTNET_TEST_VECTORS, &BitcoinTestnet);
516 }
517 #[test]
518 fn format_lcc() {
519 testfun(LCC_TEST_VECTORS, &LitecoinCash);
520 }
521 #[test]
522 fn format_fjc() {
523 testfun(FJC_TEST_VECTORS, &Fujicoin);
524 }
525 #[test]
526 fn format_xvg() {
527 testfun(XVG_TEST_VECTORS, &Verge);
528 }
529 #[test]
530 fn format_thc() {
531 testfun(THC_TEST_VECTORS, &Hempcoin);
532 }
533 #[test]
534 fn format_xvc() {
535 testfun(XVC_TEST_VECTORS, &VCash);
536 }
537 #[test]
538 fn format_zec() {
539 testfun(ZEC_TEST_VECTORS, &ZCash);
540 }
541 #[test]
542 fn format_vtc() {
543 testfun(VTC_TEST_VECTORS, &Vertcoin);
544 }
545 #[test]
546 fn format_strat() {
547 testfun(STRAT_TEST_VECTORS, &Stratis);
548 }
549 #[test]
550 fn format_clams() {
551 testfun(CLAMS_TEST_VECTORS, &Clams);
552 }
553 #[test]
554 fn format_blk() {
555 testfun(BLK_TEST_VECTORS, &Blackcoin);
556 }
557 #[test]
558 fn format_btc_swp2sh() {
559 testfun(BTC_SWP2SH_TEST_VECTORS, &Bitcoin(P2SHWPKH));
560 }
561 #[test]
562 fn format_sdc() {
563 testfun(SDC_TEST_VECTORS, &ShadowCash);
564 }
565 #[test]
566 fn format_beet() {
567 testfun(BEET_TEST_VECTORS, &Beetlecoin);
568 }
569 #[test]
570 fn format_ac() {
571 testfun(AC_TEST_VECTORS, &Asiacoin);
572 }
573 #[cfg(feature = "eth")]
574 #[test]
575 fn format_eth() {
576 testfun(ETH_TEST_VECTORS, &Ethereum)
577 }
578
579 #[test]
580 fn format_bch_prefix() {
581 for tc in BCH_PREFIXED_VECTORS.lines().map(TestCase::from) {
582 let prefix = &tc.addr[..5];
583 let addr = BitcoinCash(Some(prefix.to_owned())).derive(tc.pubkey);
584 assert_eq!(addr.as_deref(), Ok(tc.addr))
585 }
586 }
587 #[cfg(feature = "secp256k1")]
588 #[test]
589 fn addr_for_secp_pubkey() {
590 let public_key: secp256k1::PublicKey =
591 "029d08f65e6aac041328edaeddba9c682356f81b37b052728fd8637c14eddb7ff1".parse().unwrap();
592 let addr = public_key.addr(&Default::default());
593 assert_eq!(addr.as_deref(), Ok("14cWWeAYZmA9oGgLpjekay9xLwHew3Ni34"));
594 }
595 #[cfg(feature = "k256")]
596 #[test]
597 fn addr_for_k256_pubkey() {
598 let pubkey_bytes = hex!("029d08f65e6aac041328edaeddba9c682356f81b37b052728fd8637c14eddb7ff1");
599 let pubkey = k256::PublicKey::from_sec1_bytes(&pubkey_bytes).unwrap();
600 assert_eq!(
601 pubkey.addr(&Default::default()).as_deref(),
602 Ok("14cWWeAYZmA9oGgLpjekay9xLwHew3Ni34")
603 )
604 }
605 #[cfg(feature = "k256")]
606 #[test]
607 fn addr_for_k256_verifying() {
608 let pubkey_bytes = hex!("029d08f65e6aac041328edaeddba9c682356f81b37b052728fd8637c14eddb7ff1");
609 let pubkey = k256::ecdsa::VerifyingKey::from_sec1_bytes(&pubkey_bytes).unwrap();
610 assert_eq!(
611 pubkey.addr(&Default::default()).as_deref(),
612 Ok("14cWWeAYZmA9oGgLpjekay9xLwHew3Ni34")
613 )
614 }
615}