1#![no_std]
12
13#[cfg(feature = "alloc")]
14extern crate alloc;
15
16#[cfg(feature = "alloc")]
17use alloc::{
18 string::{String, ToString},
19 vec::Vec,
20};
21use blake2::{Blake2b512, Digest};
22use bs58::{
23 decode::{self, DecodeTarget},
24 encode::{self, EncodeTarget},
25};
26use core::{
27 array::TryFromSliceError,
28 fmt,
29 ops::{Deref, DerefMut, RangeInclusive},
30 str,
31 sync::atomic::{AtomicU16, Ordering},
32};
33
34const PREFIX: &[u8] = b"SS58PRE";
36
37const PREFIX_LEN_RANGE: RangeInclusive<usize> = 1..=2;
39const MIN_PREFIX_LEN: usize = *PREFIX_LEN_RANGE.start();
41const MAX_PREFIX_LEN: usize = *PREFIX_LEN_RANGE.end();
43
44const BODY_LEN: usize = 32;
46
47const CHECKSUM_LEN: usize = 2;
49
50const MIN_ADDRESS_LEN: usize = MIN_PREFIX_LEN + BODY_LEN + CHECKSUM_LEN;
52const MAX_ADDRESS_LEN: usize = MAX_PREFIX_LEN + BODY_LEN + CHECKSUM_LEN;
54const ADDRESS_LEN_RANGE: RangeInclusive<usize> = MIN_ADDRESS_LEN..=MAX_ADDRESS_LEN;
56
57const fn base58_max_encoded_len(len: usize) -> usize {
60 len + len.div_ceil(2)
62}
63
64const MAX_ADDRESS_LEN_BASE58: usize = base58_max_encoded_len(MAX_ADDRESS_LEN);
66
67pub const SUBSTRATE_SS58_PREFIX: u16 = 42;
69pub const VARA_SS58_PREFIX: u16 = 137;
71
72static DEFAULT_SS58_VERSION: AtomicU16 = AtomicU16::new(VARA_SS58_PREFIX);
74
75pub fn default_ss58_version() -> u16 {
77 DEFAULT_SS58_VERSION.load(Ordering::Relaxed)
78}
79
80pub fn set_default_ss58_version(version: u16) {
82 DEFAULT_SS58_VERSION.store(version, Ordering::Relaxed);
83}
84
85struct Buffer<const N: usize>([u8; N]);
86
87impl<const N: usize> Buffer<N> {
88 pub const fn new() -> Self {
89 Self([0; N])
90 }
91}
92
93impl<const N: usize> Deref for Buffer<N> {
94 type Target = [u8];
95
96 fn deref(&self) -> &Self::Target {
97 &self.0
98 }
99}
100
101impl<const N: usize> DerefMut for Buffer<N> {
102 fn deref_mut(&mut self) -> &mut Self::Target {
103 &mut self.0
104 }
105}
106
107impl DecodeTarget for Buffer<MAX_ADDRESS_LEN> {
108 fn decode_with(
109 &mut self,
110 _max_len: usize,
111 f: impl for<'a> FnOnce(&'a mut [u8]) -> decode::Result<usize>,
112 ) -> decode::Result<usize> {
113 let len = f(&mut self[..])?;
114 Ok(len)
115 }
116}
117
118impl EncodeTarget for Buffer<MAX_ADDRESS_LEN_BASE58> {
119 fn encode_with(
120 &mut self,
121 _max_len: usize,
122 f: impl for<'a> FnOnce(&'a mut [u8]) -> encode::Result<usize>,
123 ) -> encode::Result<usize> {
124 let len = f(&mut self[..])?;
125 Ok(len)
126 }
127}
128
129#[derive(Debug, PartialEq, Eq)]
131pub enum Error {
132 Base58Encode,
133 BadBase58,
134 BadLength,
135 InvalidPrefix,
136 InvalidChecksum,
137 #[cfg(feature = "alloc")]
138 InvalidSliceLength,
139}
140
141impl fmt::Display for Error {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 match self {
144 Self::Base58Encode => writeln!(f, "Base 58 encoding failed"),
145 Self::BadBase58 => writeln!(f, "Base 58 requirement is violated"),
146 Self::BadLength => writeln!(f, "Length is bad"),
147 Self::InvalidPrefix => writeln!(f, "Invalid SS58 prefix byte"),
148 Self::InvalidChecksum => writeln!(f, "Invalid checksum"),
149 #[cfg(feature = "alloc")]
150 Self::InvalidSliceLength => writeln!(f, "Slice should be 32 length"),
151 }
152 }
153}
154
155impl core::error::Error for Error {}
156
157pub struct Ss58Address {
159 len: usize,
160 buf: Buffer<MAX_ADDRESS_LEN_BASE58>,
161}
162
163impl Ss58Address {
164 pub fn as_str(&self) -> &str {
166 unsafe { str::from_utf8_unchecked(&self.buf).get_unchecked(..self.len) }
167 }
168}
169
170impl fmt::Display for Ss58Address {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 f.write_str(self.as_str())
173 }
174}
175
176impl fmt::Debug for Ss58Address {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 fmt::Display::fmt(self, f)
179 }
180}
181
182pub struct RawSs58Address([u8; BODY_LEN]);
184
185impl From<RawSs58Address> for [u8; BODY_LEN] {
186 fn from(address: RawSs58Address) -> Self {
187 address.0
188 }
189}
190
191impl From<[u8; BODY_LEN]> for RawSs58Address {
192 fn from(array: [u8; BODY_LEN]) -> Self {
193 Self(array)
194 }
195}
196
197impl TryFrom<&[u8]> for RawSs58Address {
198 type Error = TryFromSliceError;
199
200 fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
201 <[u8; BODY_LEN]>::try_from(slice).map(Self)
202 }
203}
204
205impl fmt::Display for RawSs58Address {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 let mut buf = [0; BODY_LEN * 2];
208 let _ = hex::encode_to_slice(self.0, &mut buf);
209 f.write_str("0x")?;
210 f.write_str(unsafe { str::from_utf8_unchecked(&buf) })
211 }
212}
213
214impl fmt::Debug for RawSs58Address {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 fmt::Display::fmt(self, f)
217 }
218}
219
220impl RawSs58Address {
221 pub fn from_ss58check(s: &str) -> Result<Self, Error> {
223 Self::from_ss58check_with_prefix(s).map(|(address, _)| address)
224 }
225
226 pub fn from_ss58check_with_prefix(s: &str) -> Result<(Self, u16), Error> {
228 let mut data = Buffer::<MAX_ADDRESS_LEN>::new();
229 let data_len = bs58::decode(s)
230 .onto(&mut data)
231 .map_err(|_| Error::BadBase58)?;
232
233 if !ADDRESS_LEN_RANGE.contains(&data_len) {
234 return Err(Error::BadLength);
235 }
236
237 let (prefix_len, prefix) = match data[0] {
238 0..=63 => (1, data[0] as u16),
239 64..=127 => {
240 let lower = (data[0] << 2) | (data[1] >> 6);
246 let upper = data[1] & 0b00111111;
247 (2, (lower as u16) | ((upper as u16) << 8))
248 }
249 _ => return Err(Error::InvalidPrefix),
250 };
251
252 if data_len != prefix_len + BODY_LEN + CHECKSUM_LEN {
253 return Err(Error::BadLength);
254 }
255
256 let (address_data, address_checksum) = data.split_at(prefix_len + BODY_LEN);
257
258 let hash = ss58hash(address_data);
259 let checksum = &hash[..CHECKSUM_LEN];
260
261 if &address_checksum[..CHECKSUM_LEN] != checksum {
262 return Err(Error::InvalidChecksum);
263 }
264
265 match <[u8; BODY_LEN]>::try_from(&address_data[prefix_len..]) {
266 Ok(array) => Ok((Self(array), prefix)),
267 Err(_) => Err(Error::BadLength),
268 }
269 }
270}
271
272impl RawSs58Address {
273 pub fn to_ss58check(&self) -> Result<Ss58Address, Error> {
275 self.to_ss58check_with_prefix(default_ss58_version())
276 }
277
278 pub fn to_ss58check_with_prefix(&self, prefix: u16) -> Result<Ss58Address, Error> {
280 let mut buffer = Buffer::<MAX_ADDRESS_LEN>::new();
281
282 let ident = prefix & 0b0011_1111_1111_1111;
284 let (prefix_len, address_len) = match ident {
285 0..=63 => {
286 buffer[0] = ident as u8;
287 (MIN_PREFIX_LEN, MIN_ADDRESS_LEN)
288 }
289 64..=16_383 => {
290 let first = ((ident & 0b0000_0000_1111_1100) as u8) >> 2;
292 let second = ((ident >> 8) as u8) | (((ident & 0b0000_0000_0000_0011) as u8) << 6);
295
296 buffer[0] = first | 0b01000000;
297 buffer[1] = second;
298 (MAX_PREFIX_LEN, MAX_ADDRESS_LEN)
299 }
300 _ => unreachable!("masked out the upper two bits; qed"),
301 };
302
303 let (address_data, address_checksum) = buffer.split_at_mut(prefix_len + BODY_LEN);
304
305 address_data[prefix_len..].copy_from_slice(&self.0);
306 let hash = ss58hash(address_data);
307 address_checksum[..CHECKSUM_LEN].copy_from_slice(&hash[..CHECKSUM_LEN]);
308
309 let mut buf = Buffer::<MAX_ADDRESS_LEN_BASE58>::new();
310 let len = bs58::encode(&buffer[..address_len])
311 .onto(&mut buf)
312 .map_err(|_| Error::Base58Encode)?;
313
314 Ok(Ss58Address { len, buf })
315 }
316}
317
318fn ss58hash(data: &[u8]) -> [u8; 64] {
319 let mut ctx = Blake2b512::new();
320 ctx.update(PREFIX);
321 ctx.update(data);
322 ctx.finalize().into()
323}
324
325#[cfg(feature = "alloc")]
327pub fn encode(data: &[u8]) -> Result<String, Error> {
328 let raw_address = RawSs58Address::try_from(data).map_err(|_| Error::InvalidSliceLength)?;
329 let address = raw_address.to_ss58check()?;
330 Ok(address.to_string())
331}
332
333#[cfg(feature = "alloc")]
335pub fn decode(encoded: &str) -> Result<Vec<u8>, Error> {
336 let raw_address: [u8; BODY_LEN] = RawSs58Address::from_ss58check(encoded)?.into();
337 Ok(raw_address.to_vec())
338}
339
340#[cfg(feature = "alloc")]
342pub fn recode(encoded: &str) -> Result<String, Error> {
343 self::encode(&self::decode(encoded)?)
344}