1use num_derive::*;
2use std::borrow::Cow;
3
4use crate::error::{Error, ErrorKind, Result};
5use crate::{serialize_ops, Hash160, Hashed, Ops, Script, Pubkey};
6
7const CHARSET: &[u8] = b"qpzry9x8gf2tvdw0s3jn54khce6mua7l";
8
9#[derive(Clone, Debug, Eq, PartialEq, Hash)]
10pub struct Address<'a> {
11 addr_type: AddressType,
12 hash: Hash160,
13 cash_addr: Cow<'a, str>,
14 prefix: AddressPrefix<'a>,
15}
16
17#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, FromPrimitive)]
18pub enum AddressType {
19 P2PKH = 0,
20 P2SH = 8,
21}
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
24pub enum Prefix {
25 BitcoinCash,
26 SimpleLedger,
27}
28
29#[derive(Clone, Debug, Eq, PartialEq, Hash)]
30pub struct AddressPrefix<'a> {
31 prefix_str: Cow<'a, str>,
32 prefix_kind: Option<Prefix>,
33}
34
35#[derive(Clone, Copy, Debug, PartialEq)]
36pub enum CashAddrError {
37 InvalidChecksum,
38 InvalidBase32Letter(usize, u8),
39 InvalidAddressType(u8),
40}
41
42impl Prefix {
43 pub fn prefix_str(self) -> &'static str {
44 match self {
45 Prefix::BitcoinCash => "bitcoincash",
46 Prefix::SimpleLedger => "simpleledger",
47 }
48 }
49
50 pub fn from_prefix_str(prefix_str: &str) -> Option<Prefix> {
51 match prefix_str {
52 "bitcoincash" => Some(Prefix::BitcoinCash),
53 "simpleledger" => Some(Prefix::SimpleLedger),
54 _ => None,
55 }
56 }
57}
58
59impl Default for Prefix {
60 fn default() -> Self {
61 Prefix::BitcoinCash
62 }
63}
64
65impl<'a> Into<AddressPrefix<'a>> for Prefix {
66 fn into(self) -> AddressPrefix<'a> {
67 AddressPrefix {
68 prefix_str: self.prefix_str().into(),
69 prefix_kind: Some(self),
70 }
71 }
72}
73
74impl<'a> AddressPrefix<'a> {
75 pub fn new(prefix_str: Cow<'a, str>, prefix_kind: Option<Prefix>) -> Self {
76 AddressPrefix {
77 prefix_str,
78 prefix_kind,
79 }
80 }
81
82 pub fn prefix_str(&self) -> &str {
83 &self.prefix_str
84 }
85
86 pub fn prefix_kind(&self) -> Option<Prefix> {
87 self.prefix_kind
88 }
89}
90
91impl<'a> Into<AddressPrefix<'a>> for &'a str {
92 fn into(self) -> AddressPrefix<'a> {
93 AddressPrefix {
94 prefix_str: self.into(),
95 prefix_kind: Prefix::from_prefix_str(self),
96 }
97 }
98}
99
100impl<'a> Address<'a> {
101 pub fn from_hash<P: Into<AddressPrefix<'a>>>(
102 prefix: P,
103 addr_type: AddressType,
104 hash: Hash160,
105 ) -> Address<'a> {
106 let prefix = prefix.into();
107 Address {
108 cash_addr: _to_cash_addr(prefix.prefix_str(), addr_type, hash.as_slice()).into(),
109 addr_type,
110 hash,
111 prefix,
112 }
113 }
114
115 pub fn from_cash_addr(cash_addr: &'a str) -> Result<Address<'a>> {
116 let (hash, addr_type, prefix) = _from_cash_addr(cash_addr, Prefix::default().prefix_str())
117 .map_err(|err| -> Error { ErrorKind::InvalidCashAddr(err).into() })?;
118 let prefix_kind = Prefix::from_prefix_str(&prefix);
119 Ok(Address {
120 cash_addr: cash_addr.into(),
121 addr_type,
122 hash: Hash160::from_slice(&hash)?,
123 prefix: AddressPrefix::new(prefix, prefix_kind),
124 })
125 }
126
127 pub fn from_redeem_script<P: Into<AddressPrefix<'a>>>(
128 prefix: P,
129 redeem_script: Script,
130 ) -> Result<Address<'a>> {
131 Ok(Address::from_hash(
132 prefix,
133 AddressType::P2SH,
134 Hash160::digest(serialize_ops(redeem_script.ops().iter().map(|op| &op.op))?),
135 ))
136 }
137
138 pub fn from_pk<P: Into<AddressPrefix<'a>>>(prefix: P, pubkey: &Pubkey) -> Address<'a> {
139 Address::from_hash(prefix, AddressType::P2PKH, Hash160::digest(pubkey.as_byte_array()))
140 }
141
142 pub fn hash(&self) -> &Hash160 {
143 &self.hash
144 }
145
146 pub fn prefix_str(&self) -> &str {
147 self.prefix.prefix_str()
148 }
149
150 pub fn prefix_kind(&self) -> Option<Prefix> {
151 self.prefix.prefix_kind()
152 }
153
154 pub fn cash_addr(&self) -> &str {
155 &self.cash_addr
156 }
157
158 pub fn addr_type(&self) -> AddressType {
159 self.addr_type
160 }
161
162 pub fn with_prefix<P: Into<AddressPrefix<'a>>>(&'a self, prefix: P) -> Address<'a> {
163 Self::from_hash(prefix, self.addr_type, self.hash.clone())
164 }
165
166 pub fn to_owned_address(&self) -> Address<'static> {
167 Address {
168 addr_type: self.addr_type,
169 hash: self.hash.clone(),
170 cash_addr: self.cash_addr.to_string().into(),
171 prefix: AddressPrefix {
172 prefix_str: self.prefix.prefix_str.to_string().into(),
173 prefix_kind: self.prefix.prefix_kind,
174 },
175 }
176 }
177}
178
179fn _map_to_b32(data: impl Iterator<Item = u8>) -> String {
180 String::from_utf8(data.map(|x| CHARSET[x as usize]).collect()).unwrap()
181}
182
183fn _map_from_b32(string: &str) -> std::result::Result<Vec<u8>, CashAddrError> {
184 string
185 .as_bytes()
186 .iter()
187 .enumerate()
188 .map(|(i, x)| {
189 CHARSET
190 .iter()
191 .position(|c| x == c)
192 .map(|x| x as u8)
193 .ok_or(CashAddrError::InvalidBase32Letter(i, *x))
194 })
195 .collect()
196}
197
198fn _convert_bits(
199 data: impl Iterator<Item = u8>,
200 from_bits: u32,
201 to_bits: u32,
202 pad: bool,
203) -> Option<Vec<u8>> {
204 let mut acc = 0;
205 let mut bits = 0;
206 let mut ret = Vec::new();
207 let maxv = (1 << to_bits) - 1;
208 let max_acc = (1 << (from_bits + to_bits - 1)) - 1;
209 for value in data {
210 let value = value as u32;
211 if (value >> from_bits) != 0 {
212 return None;
213 }
214 acc = ((acc << from_bits) | value) & max_acc;
215 bits += from_bits;
216 while bits >= to_bits {
217 bits -= to_bits;
218 ret.push(((acc >> bits) & maxv) as u8);
219 }
220 }
221 if pad {
222 if bits != 0 {
223 ret.push(((acc << (to_bits - bits)) & maxv) as u8);
224 }
225 } else if bits >= from_bits || ((acc << (to_bits - bits)) & maxv != 0) {
226 return None;
227 }
228 Some(ret)
229}
230
231fn _poly_mod(values: impl Iterator<Item = u8>) -> u64 {
232 let mut c = 1;
233 for value in values {
234 let c0 = (c >> 35) as u8;
235 c = ((c & 0x07_ffff_ffffu64) << 5u64) ^ (value as u64);
236 if c0 & 0x01 != 0 {
237 c ^= 0x98_f2bc_8e61
238 }
239 if c0 & 0x02 != 0 {
240 c ^= 0x79_b76d_99e2
241 }
242 if c0 & 0x04 != 0 {
243 c ^= 0xf3_3e5f_b3c4
244 }
245 if c0 & 0x08 != 0 {
246 c ^= 0xae_2eab_e2a8
247 }
248 if c0 & 0x10 != 0 {
249 c ^= 0x1e_4f43_e470
250 }
251 }
252 c ^ 1
253}
254
255fn _calculate_checksum(prefix: &str, payload: impl Iterator<Item = u8>) -> Vec<u8> {
256 let poly = _poly_mod(
257 prefix
258 .as_bytes()
259 .iter()
260 .map(|x| *x & 0x1f)
261 .chain([0].iter().cloned())
262 .chain(payload)
263 .chain([0, 0, 0, 0, 0, 0, 0, 0].iter().cloned()),
264 );
265 (0..8)
266 .map(|i| ((poly >> (5 * (7 - i))) & 0x1f) as u8)
267 .collect()
268}
269
270fn _verify_checksum(prefix: &str, payload: impl Iterator<Item = u8>) -> bool {
271 let poly = _poly_mod(
272 prefix
273 .as_bytes()
274 .iter()
275 .map(|x| *x & 0x1f)
276 .chain([0].iter().cloned())
277 .chain(payload),
278 );
279 poly == 0
280}
281
282fn _to_cash_addr(prefix: &str, addr_type: AddressType, addr_bytes: &[u8]) -> String {
283 let version = addr_type as u8;
284 let payload = _convert_bits(
285 [version].iter().chain(addr_bytes.iter()).cloned(),
286 8,
287 5,
288 true,
289 )
290 .unwrap();
291 let checksum = _calculate_checksum(prefix, payload.iter().cloned());
292 String::from(prefix)
293 + ":"
294 + &_map_to_b32(payload.iter().cloned().chain(checksum.iter().cloned()))
295}
296
297fn _from_cash_addr<'a>(
298 addr_string: &str,
299 default_prefix: &'a str,
300) -> std::result::Result<([u8; 20], AddressType, Cow<'a, str>), CashAddrError> {
301 let addr_string = addr_string.to_ascii_lowercase();
302 let (prefix, payload_base32): (Cow<'a, _>, _) = if let Some(pos) = addr_string.find(':') {
303 let (prefix, payload_base32) = addr_string.split_at(pos + 1);
304 (
305 prefix[..prefix.len() - 1].to_string().into(),
306 payload_base32,
307 )
308 } else {
309 (default_prefix.into(), &addr_string[..])
310 };
311 let decoded = _map_from_b32(payload_base32)?;
312 if !_verify_checksum(&prefix, decoded.iter().cloned()) {
313 return Err(CashAddrError::InvalidChecksum);
314 }
315 let converted = _convert_bits(decoded.iter().cloned(), 5, 8, true).unwrap();
316 let mut addr = [0; 20];
317 addr.copy_from_slice(&converted[1..converted.len() - 6]);
318 Ok((
319 addr,
320 match converted[0] {
321 0 => AddressType::P2PKH,
322 8 => AddressType::P2SH,
323 x => return Err(CashAddrError::InvalidAddressType(x)),
324 },
325 prefix,
326 ))
327}
328
329#[cfg(test)]
330mod tests {
331 use super::{Address, AddressType, Hash160, Prefix, Result};
332
333 #[test]
334 fn test_from_hash1() -> Result<()> {
335 let addr = Address::from_hash(
336 Prefix::BitcoinCash,
337 AddressType::P2PKH,
338 Hash160::new([0; 20]),
339 );
340 assert_eq!(
341 addr.cash_addr(),
342 "bitcoincash:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfnhks603"
343 );
344 Ok(())
345 }
346
347 #[test]
348 fn test_from_hash2() -> Result<()> {
349 let addr = Address::from_hash(
350 Prefix::SimpleLedger,
351 AddressType::P2PKH,
352 Hash160::new([0; 20]),
353 );
354 assert_eq!(
355 addr.cash_addr(),
356 "simpleledger:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq9gud9630"
357 );
358 Ok(())
359 }
360
361 #[test]
362 fn test_from_hash3() -> Result<()> {
363 let addr = Address::from_hash(
364 Prefix::BitcoinCash,
365 AddressType::P2SH,
366 Hash160::new([0; 20]),
367 );
368 assert_eq!(
369 addr.cash_addr(),
370 "bitcoincash:pqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq7k2ehe5v"
371 );
372 Ok(())
373 }
374
375 #[test]
376 fn test_from_hash4() -> Result<()> {
377 let addr = Address::from_hash("redridinghood", AddressType::P2SH, Hash160::new([0; 20]));
378 assert_eq!(
379 addr.cash_addr(),
380 "redridinghood:pqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxmg9w0gt"
381 );
382 Ok(())
383 }
384
385 #[test]
386 fn test_from_cash_addr1() -> Result<()> {
387 let addr =
388 Address::from_cash_addr("bitcoincash:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfnhks603")?;
389 assert_eq!(addr.addr_type(), AddressType::P2PKH);
390 assert_eq!(
391 addr.cash_addr(),
392 "bitcoincash:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfnhks603"
393 );
394 assert_eq!(addr.hash(), &Hash160::new([0; 20]));
395 assert_eq!(addr.prefix_kind(), Some(Prefix::BitcoinCash));
396 assert_eq!(addr.prefix_str(), "bitcoincash");
397 Ok(())
398 }
399
400 #[test]
401 fn test_from_cash_addr2() -> Result<()> {
402 let addr =
403 Address::from_cash_addr("simpleledger:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq9gud9630")?;
404 assert_eq!(addr.addr_type(), AddressType::P2PKH);
405 assert_eq!(
406 addr.cash_addr(),
407 "simpleledger:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq9gud9630"
408 );
409 assert_eq!(addr.hash(), &Hash160::new([0; 20]));
410 assert_eq!(addr.prefix_kind(), Some(Prefix::SimpleLedger));
411 assert_eq!(addr.prefix_str(), "simpleledger");
412 Ok(())
413 }
414
415 #[test]
416 fn test_from_cash_addr3() -> Result<()> {
417 let addr =
418 Address::from_cash_addr("bitcoincash:pqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq7k2ehe5v")?;
419 assert_eq!(addr.addr_type(), AddressType::P2SH);
420 assert_eq!(
421 addr.cash_addr(),
422 "bitcoincash:pqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq7k2ehe5v"
423 );
424 assert_eq!(addr.hash(), &Hash160::new([0; 20]));
425 assert_eq!(addr.prefix_kind(), Some(Prefix::BitcoinCash));
426 assert_eq!(addr.prefix_str(), "bitcoincash");
427 Ok(())
428 }
429
430 #[test]
431 fn test_from_cash_addr4() -> Result<()> {
432 let addr =
433 Address::from_cash_addr("redridinghood:pqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxmg9w0gt")?;
434 assert_eq!(addr.addr_type(), AddressType::P2SH);
435 assert_eq!(
436 addr.cash_addr(),
437 "redridinghood:pqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxmg9w0gt"
438 );
439 assert_eq!(addr.hash(), &Hash160::new([0; 20]));
440 assert_eq!(addr.prefix_kind(), None);
441 assert_eq!(addr.prefix_str(), "redridinghood");
442 Ok(())
443 }
444
445 #[test]
446 fn test_with_prefix() -> Result<()> {
447 let addr =
448 Address::from_cash_addr("redridinghood:pqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxmg9w0gt")?;
449 let new_addr = addr.with_prefix("prelude");
450 assert_eq!(new_addr.addr_type(), AddressType::P2SH);
451 assert_eq!(
452 new_addr.cash_addr(),
453 "prelude:pqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrs52h40n"
454 );
455 assert_eq!(new_addr.hash(), &Hash160::new([0; 20]));
456 assert_eq!(new_addr.prefix_kind(), None);
457 assert_eq!(new_addr.prefix_str(), "prelude");
458 Ok(())
459 }
460}