1use bech32::{
5 decode as b32_decode, encode as b32_encode, u5, Error as BechError, FromBase32, ToBase32,
6};
7
8use bs58::{
9 decode as bs58_decode, decode::Error as Bs58DecodeError, encode as bs58_encode,
10 encode::Error as Bs58EncodeError,
11};
12
13use thiserror::Error;
14
15#[derive(Debug, Error)]
17pub enum EncodingError {
18 #[error("Non-standard ScriptPubkey type")]
20 UnknownScriptType,
21
22 #[error("Bech32 HRP does not match. \nGot {:?} expected {:?} Hint: Is this address for another network?", got, expected)]
24 WrongHrp {
25 got: String,
27 expected: String,
29 },
30
31 #[error("Base58Check version does not match. \nGot {:?} expected {:?} Hint: Is this address for another network?", got, expected)]
33 WrongVersion {
34 got: u8,
36 expected: u8,
38 },
39
40 #[error("{0}")]
42 Bs58Decode(#[from] Bs58DecodeError),
43
44 #[error("{0}")]
46 Bs58Encode(#[from] Bs58EncodeError),
47
48 #[error(transparent)]
50 BechError(#[from] BechError),
51
52 #[error("Can't encode op return scripts as addresses")]
54 NullDataScript,
55
56 #[error("Invalid Segwit Version: {0}")]
58 SegwitVersionError(u8),
59
60 #[error("Invalid Address Size")]
62 InvalidSizeError,
63}
64
65pub type EncodingResult<T> = Result<T, EncodingError>;
67
68pub fn encode_bech32(hrp: &str, v: u8, h: &[u8]) -> EncodingResult<String> {
71 let mut v = vec![u5::try_from_u8(v)?];
72 v.extend(&h.to_base32());
73 b32_encode(hrp, &v, bech32::Variant::Bech32).map_err(|v| v.into())
74}
75
76pub fn decode_bech32(expected_hrp: &str, s: &str) -> EncodingResult<(u8, Vec<u8>)> {
79 let (hrp, data, _variant) = b32_decode(s)?;
80 if hrp != expected_hrp {
81 return Err(EncodingError::WrongHrp {
82 got: hrp,
83 expected: expected_hrp.to_owned(),
84 });
85 }
86
87 let (v, p) = data.split_at(1);
89 let payload = Vec::from_base32(p)?;
90
91 Ok((v[0].to_u8(), payload))
92}
93
94pub fn encode_base58(v: &[u8]) -> String {
96 bs58_encode(v).with_check().into_string()
97}
98
99pub fn decode_base58(expected_prefix: u8, s: &str) -> EncodingResult<Vec<u8>> {
102 let res = bs58_decode(s).with_check(None).into_vec()?;
103
104 if let Some(version) = res.first() {
105 if version != &expected_prefix {
106 return Err(EncodingError::Bs58Decode(Bs58DecodeError::InvalidVersion {
107 ver: *version,
108 expected_ver: expected_prefix,
109 }));
110 }
111 }
112
113 Ok(res)
114}
115
116#[cfg(test)]
117mod test {
118 use super::*;
119
120 #[test]
121 fn it_should_encode_and_decode_arbitrary_bech32() {
122 let cases = [
123 ("lnbc20m", "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqscc6gd6ql3jrc5yzme8v4ntcewwz5cnw92tz0pc8qcuufvq7khhr8wpald05e92xw006sq94mg8v2ndf4sefvf9sygkshp5zfem29trqq2yxxz7"),
125 ("nc", "nc1qanwztr5zvd309vjf9ks9c2c3hyw3sqpppwkuut"),
127 ("hs", "hs1q8vn02tnktq3tmztny8nysel6vtkuuy9k0whtty"),
129 ("ab", "ab1qm7dpnrqefvf4ee67"),
131 ("lol", "lol1yrtmpa4p98nerppeu3h00my48ejmmyj629aeyqhur7wfrzfwqj99v875saeetusxtphs3q2"),
132 ];
133
134 for case in cases.iter() {
135 let (version, data) = decode_bech32(case.0, case.1).unwrap();
136 let reencoded = encode_bech32(case.0, version, &data).unwrap();
137 assert_eq!(case.1, reencoded);
138 }
139 }
140
141 #[test]
142 fn it_should_encode_and_decode_base58_pkh() {
143 let version = 0x00;
144 let addrs = [
145 "1AqE7oGF1EUoJviX1uuYrwpRBdEBTuGhES",
146 "1J2kECACFMDPyYjCBddKYbtzJMc6kv5FbA",
147 "1ADKfX19iy3EFUoG5qGLSHNXb4c1SSHFNF",
148 "12cKuAyj2jmrmMPBMtoeAt47DrJ5WRK2R5",
149 "19R4yak7BGX8fcWNvtuuTSjQGC43U4qadJ",
150 "1MT3dyC8YgEGY37yPwPtnvyau8HjGiMhhM",
151 "1NDyJtNTjmwk5xPNhjgAMu4HDHigtobu1s",
152 "1HMPBDt3HAD6o3zAxotBCS9o8KqCuYoapF",
153 "16o4roRP8dapRJraVNnw99xBh3J1Wkk5m8",
154 ];
155 for addr in addrs.iter() {
156 let s = decode_base58(version, addr).unwrap();
157 let reencoded = encode_base58(&s);
158 assert_eq!(*addr, reencoded);
159 }
160 }
161
162 #[test]
163 fn it_should_encode_and_decode_base58_sh() {
164 let version = 0x05;
165 let addrs = [
166 "3HXNFmJpxjgTVFN35Y9f6Waje5YFsLEQZ2",
167 "35mpC7r8fGrt2WTBTkQ56xBgm1k1QCY9CQ",
168 "345KNsztA2frN7V2TTZ2a9Vt6ojH8VSXFM",
169 "37QxcQb7U549M1QoDpXuRZMcTjRF52mfjx",
170 "377mKFYsaJPsxYSB5aFfx8SW3RaN5BzZVh",
171 "3GPM5uAPoqJ4CAst3GiraHPGFxSin6Ch2b",
172 "3LVq5zEBW48DjrqtmExR1YYDfJLmp8ryQE",
173 "3GfrmGENZFbV4rMWUxUxeo2yUnEnSDQ5BP",
174 "372sRbqCNQ1xboWCcc7XSbjptv8pzF9sBq",
175 ];
176 for addr in addrs.iter() {
177 let s = decode_base58(version, addr).unwrap();
178 let reencoded = encode_base58(&s);
179 assert_eq!(*addr, reencoded);
180 }
181 }
182
183 #[test]
184 fn it_should_error_on_wrong_version_and_hrp_and_invalid_addrs() {
185 match decode_bech32("tb", "bc1q233q49ve8ysdsztqh9ue57m6227627j8ztscl9") {
186 Ok(_) => panic!("expected an error"),
187 Err(EncodingError::WrongHrp {
188 got: _,
189 expected: _,
190 }) => {}
191 _ => panic!("Got the wrong error"),
192 }
193 match decode_base58(1, "3HXNFmJpxjgTVFN35Y9f6Waje5YFsLEQZ2") {
194 Ok(_) => panic!("expected an error"),
195 Err(EncodingError::Bs58Decode(Bs58DecodeError::InvalidVersion {
196 ver: 5,
197 expected_ver: 1,
198 })) => {}
199 _ => panic!("Got the wrong error"),
200 }
201 match decode_bech32("bc", "bc1qqh9ue57m6227627j8ztscl9") {
202 Ok(_) => panic!("expected an error"),
203 Err(EncodingError::BechError(_)) => {}
204 _ => panic!("Got the wrong error"),
205 }
206 match decode_base58(5, "3HXNf6Waje5YFsLEQZ2") {
207 Ok(_) => panic!("expected an error"),
208 Err(EncodingError::Bs58Decode(_)) => {}
209 _ => panic!("Got the wrong error"),
210 }
211 }
212}