bitcoincash_addr/base58/
mod.rs

1// https://github.com/rust-bitcoin/rust-bitcoin/blob/master/src/util/address.rs
2pub mod errors;
3
4use bitcoin_hashes::{sha256d::Hash as Sha256d, Hash};
5
6use crate::*;
7pub use errors::DecodingError;
8
9const BASE58_CHARS: &[u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
10
11#[rustfmt::skip]
12const BASE58_DIGITS: [Option<u8>; 128] = [
13    None,     None,     None,     None,     None,     None,     None,     None,     // 0-7
14    None,     None,     None,     None,     None,     None,     None,     None,     // 8-15
15    None,     None,     None,     None,     None,     None,     None,     None,     // 16-23
16    None,     None,     None,     None,     None,     None,     None,     None,     // 24-31
17    None,     None,     None,     None,     None,     None,     None,     None,     // 32-39
18    None,     None,     None,     None,     None,     None,     None,     None,     // 40-47
19    None,     Some(0),  Some(1),  Some(2),  Some(3),  Some(4),  Some(5),  Some(6),  // 48-55
20    Some(7),  Some(8),  None,     None,     None,     None,     None,     None,     // 56-63
21    None,     Some(9),  Some(10), Some(11), Some(12), Some(13), Some(14), Some(15), // 64-71
22    Some(16), None,     Some(17), Some(18), Some(19), Some(20), Some(21), None,     // 72-79
23    Some(22), Some(23), Some(24), Some(25), Some(26), Some(27), Some(28), Some(29), // 80-87
24    Some(30), Some(31), Some(32), None,     None,     None,     None,     None,     // 88-95
25    None,     Some(33), Some(34), Some(35), Some(36), Some(37), Some(38), Some(39), // 96-103
26    Some(40), Some(41), Some(42), Some(43), None,     Some(44), Some(45), Some(46), // 104-111
27    Some(47), Some(48), Some(49), Some(50), Some(51), Some(52), Some(53), Some(54), // 112-119
28    Some(55), Some(56), Some(57), None,     None,     None,     None,     None,     // 120-127
29];
30
31fn from_base58_str(data: &str) -> Result<Vec<u8>, DecodingError> {
32    // 11/15 is just over log_256(58)
33    let mut scratch = vec![0u8; 1 + data.len() * 11 / 15];
34    // Build in base 256
35    for d58 in data.bytes() {
36        // Compute "X = X * 58 + next_digit" in base 256
37        if d58 as usize > BASE58_DIGITS.len() {
38            return Err(DecodingError::InvalidChar(d58 as char));
39        }
40        let mut carry = match BASE58_DIGITS[d58 as usize] {
41            Some(d58) => u32::from(d58),
42            None => {
43                return Err(DecodingError::InvalidChar(d58 as char));
44            }
45        };
46        for d256 in scratch.iter_mut().rev() {
47            carry += u32::from(*d256) * 58;
48            *d256 = carry as u8;
49            carry /= 256;
50        }
51        assert_eq!(carry, 0);
52    }
53
54    // Copy leading zeroes directly
55    let mut ret: Vec<u8> = data
56        .bytes()
57        .take_while(|&x| x == BASE58_CHARS[0])
58        .map(|_| 0)
59        .collect();
60    // Copy rest of string
61    ret.extend(scratch.into_iter().skip_while(|&x| x == 0));
62    Ok(ret)
63}
64
65fn to_base58_str(data: &[u8]) -> String {
66    let mut ret = Vec::with_capacity(data.len());
67
68    let mut leading_zero_count = 0;
69    let mut leading_zeroes = true;
70    // Build string in little endian with 0-58 in place of characters...
71    for d256 in data {
72        let mut carry = *d256 as usize;
73        if leading_zeroes && carry == 0 {
74            leading_zero_count += 1;
75        } else {
76            leading_zeroes = false;
77        }
78
79        for ch in ret.iter_mut() {
80            let new_ch = *ch as usize * 256 + carry;
81            *ch = (new_ch % 58) as u8;
82            carry = new_ch / 58;
83        }
84        while carry > 0 {
85            ret.push((carry % 58) as u8);
86            carry /= 58;
87        }
88    }
89
90    // ... then reverse it and convert to chars
91    for _ in 0..leading_zero_count {
92        ret.push(0);
93    }
94
95    let out: String = ret
96        .iter()
97        .rev()
98        .map(|ch| BASE58_CHARS[*ch as usize] as char)
99        .collect();
100
101    out
102}
103
104/// Codec allowing the encoding and decoding of Base58 addresses.
105pub struct Base58Codec;
106
107impl AddressCodec for Base58Codec {
108    type EncodingError = ();
109    type DecodingError = DecodingError;
110
111    fn encode(
112        raw: &[u8],
113        hash_type: HashType,
114        network: Network,
115    ) -> Result<String, Self::EncodingError> {
116        let addr_type_byte = match (hash_type, network) {
117            (HashType::Key, Network::Main) => 0x00,
118            (HashType::Key, Network::Test) => 0x6f,
119            (HashType::Key, Network::Regtest) => 0x6f,
120            (HashType::Script, Network::Main) => 0x05,
121            (HashType::Script, Network::Test) => 0xc4,
122            (HashType::Script, Network::Regtest) => 0xc4,
123        };
124
125        let mut body = Vec::with_capacity(raw.len() + 5);
126        body.push(addr_type_byte);
127        body.extend(raw);
128
129        let checksum = Sha256d::hash(&body);
130        body.extend(&checksum[0..4]);
131        Ok(to_base58_str(&body))
132    }
133
134    fn decode(addr_str: &str) -> Result<Address, Self::DecodingError> {
135        // Convert from base58
136        let raw = from_base58_str(addr_str)?;
137        let length = raw.len();
138        if length != 25 {
139            return Err(DecodingError::InvalidLength(length));
140        }
141
142        // Parse network and hash type
143        let version_byte = raw[0];
144        let (network, hash_type) = match version_byte {
145            0x00 => (Network::Main, HashType::Key),
146            0x05 => (Network::Main, HashType::Script),
147            0x6f => (Network::Test, HashType::Key),
148            0xc4 => (Network::Test, HashType::Script),
149            _ => return Err(DecodingError::InvalidVersion(version_byte)),
150        };
151
152        // Verify checksum
153        let payload = &raw[0..raw.len() - 4];
154        let checksum_actual = &raw[raw.len() - 4..];
155        let checksum_expected = &Sha256d::hash(payload)[0..4];
156        if checksum_expected != checksum_actual {
157            return Err(DecodingError::ChecksumFailed {
158                expected: checksum_expected.to_vec(),
159                actual: checksum_actual.to_vec(),
160            });
161        }
162
163        // Extract hash160 address and return
164        let body = payload[1..].to_vec();
165        Ok(Address {
166            scheme: Scheme::Base58,
167            body,
168            hash_type,
169            network,
170        })
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177    use bitcoin_hashes::hash160::Hash as Hash160;
178    use hex;
179
180    #[test]
181    fn to_legacyaddr() {
182        let pubkey_hex = "04005937fd439b3c19014d5f328df8c7ed514eaaf41c1980b8aeab461dffb23fbf3317e42395db24a52ce9fc947d9c22f54dc3217c8b11dfc7a09c59e0dca591d3";
183        let pubkeyhash = Hash160::hash(&hex::decode(pubkey_hex).unwrap()).to_vec();
184        let legacyaddr = Base58Codec::encode(&pubkeyhash, HashType::Key, Network::Main).unwrap();
185        assert!(legacyaddr == "1NM2HFXin4cEQRBLjkNZAS98qLX9JKzjKn");
186    }
187
188    #[test]
189    fn from_legacyaddr() {
190        let legacyaddr = "1NM2HFXin4cEQRBLjkNZAS98qLX9JKzjKn";
191        let result = Base58Codec::decode(legacyaddr).unwrap();
192        let hash160 = result.as_body();
193        assert!(hex::encode(hash160) == "ea2407829a5055466b27784cde8cf463167946bf");
194    }
195
196    #[test]
197    fn from_legacyaddr_errors() {
198        assert!(Base58Codec::decode("0").is_err());
199        assert!(Base58Codec::decode("1000000000000000000000000000000000").is_err());
200    }
201}