bitcoin_cash/
address.rs

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}