1pub 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, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, Some(0), Some(1), Some(2), Some(3), Some(4), Some(5), Some(6), Some(7), Some(8), None, None, None, None, None, None, None, Some(9), Some(10), Some(11), Some(12), Some(13), Some(14), Some(15), Some(16), None, Some(17), Some(18), Some(19), Some(20), Some(21), None, Some(22), Some(23), Some(24), Some(25), Some(26), Some(27), Some(28), Some(29), Some(30), Some(31), Some(32), None, None, None, None, None, None, Some(33), Some(34), Some(35), Some(36), Some(37), Some(38), Some(39), Some(40), Some(41), Some(42), Some(43), None, Some(44), Some(45), Some(46), Some(47), Some(48), Some(49), Some(50), Some(51), Some(52), Some(53), Some(54), Some(55), Some(56), Some(57), None, None, None, None, None, ];
30
31fn from_base58_str(data: &str) -> Result<Vec<u8>, DecodingError> {
32 let mut scratch = vec![0u8; 1 + data.len() * 11 / 15];
34 for d58 in data.bytes() {
36 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 let mut ret: Vec<u8> = data
56 .bytes()
57 .take_while(|&x| x == BASE58_CHARS[0])
58 .map(|_| 0)
59 .collect();
60 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 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 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
104pub 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 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 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 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 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}