Skip to main content

mazze_addr/
lib.rs

1
2//
3// Modification based on https://github.com/hlb8122/rust-bitcoincash-addr in MIT License.
4// A copy of the original license is included in LICENSE.rust-bitcoincash-addr.
5
6extern crate mazze_types;
7#[macro_use]
8extern crate lazy_static;
9extern crate rustc_hex;
10
11#[allow(dead_code)]
12pub mod checksum;
13pub mod consts;
14pub mod errors;
15#[cfg(test)]
16mod tests;
17
18use mazze_types::Address;
19use checksum::polymod;
20pub use consts::{AddressType, Network};
21pub use errors::DecodingError;
22use errors::*;
23
24const BASE32_CHARS: &str = "abcdefghijklmnopqrstuvwxyz0123456789";
25const EXCLUDE_CHARS: [char; 4] = ['o', 'i', 'l', 'q'];
26lazy_static! {
27    // Regular expression for application to match string. This regex isn't strict,
28    // because our SDK will.
29    // "(?i)[:=_-0123456789abcdefghijklmnopqrstuvwxyz]*"
30    static ref REGEXP: String = format!{"(?i)[:=_-{}]*", BASE32_CHARS};
31
32    // For encoding.
33    static ref CHARSET: Vec<u8> =
34        // Remove EXCLUDE_CHARS from charset.
35        BASE32_CHARS.replace(&EXCLUDE_CHARS[..], "").into_bytes();
36
37    // For decoding.
38    static ref CHAR_INDEX: [Option<u8>; 128] = (|| {
39        let mut index = [None; 128];
40        assert_eq!(CHARSET.len(), consts::CHARSET_SIZE);
41        for i in 0..consts::CHARSET_SIZE {
42            let c = CHARSET[i] as usize;
43            index[c] = Some(i as u8);
44            // Support uppercase as well.
45            let u = (c as u8 as char).to_ascii_uppercase() as u8 as usize;
46            if u != c {
47                index[u] = Some(i as u8);
48            }
49        }
50        return index;
51    }) ();
52}
53
54/// Struct containing the raw bytes and metadata of a Mazze address.
55#[derive(PartialEq, Eq, Clone, Debug, Hash)]
56pub struct DecodedRawAddress {
57    /// Base32 address. This is included for debugging purposes.
58    pub input_base32_address: String,
59    /// Address bytes
60    pub parsed_address_bytes: Vec<u8>,
61    /// The parsed address in H160 format.
62    pub hex_address: Option<Address>,
63    /// Network
64    pub network: Network,
65}
66
67#[derive(Copy, Clone)]
68pub enum EncodingOptions {
69    Simple,
70    QrCode,
71}
72
73// TODO: verbose level and address type.
74pub fn mazze_addr_encode(
75    raw: &[u8], network: Network, encoding_options: EncodingOptions,
76) -> Result<String, EncodingError> {
77    // Calculate version byte
78    let length = raw.len();
79    let version_byte = match length {
80        20 => consts::SIZE_160,
81        // Mazze does not have other hash sizes. We don't use the sizes below
82        // but we kept these for unit tests.
83        24 => consts::SIZE_192,
84        28 => consts::SIZE_224,
85        32 => consts::SIZE_256,
86        40 => consts::SIZE_320,
87        48 => consts::SIZE_384,
88        56 => consts::SIZE_448,
89        64 => consts::SIZE_512,
90        _ => return Err(EncodingError::InvalidLength(length)),
91    };
92
93    // Get prefix
94    let prefix = network.to_prefix()?;
95
96    // Convert payload to 5 bit array
97    let mut payload = Vec::with_capacity(1 + raw.len());
98    payload.push(version_byte);
99    payload.extend(raw);
100    let payload_5_bits = convert_bits(&payload, 8, 5, true)
101        .expect("no error is possible for encoding");
102
103    // Construct payload string using CHARSET
104    let payload_str: String = payload_5_bits
105        .iter()
106        .map(|b| CHARSET[*b as usize] as char)
107        .collect();
108
109    // Create checksum
110    let expanded_prefix = expand_prefix(&prefix);
111    let checksum_input =
112        [&expanded_prefix[..], &payload_5_bits, &[0; 8][..]].concat();
113    let checksum = polymod(&checksum_input);
114
115    // Convert checksum to string
116    let checksum_str: String = (0..8)
117        .rev()
118        .map(|i| CHARSET[((checksum >> (i * 5)) & 31) as usize] as char)
119        .collect();
120
121    // Concatenate all parts
122    let mazze_base32_addr = match encoding_options {
123        EncodingOptions::Simple => {
124            [&prefix, ":", &payload_str, &checksum_str].concat()
125        }
126        EncodingOptions::QrCode => {
127            let addr_type_str = AddressType::from_address(&raw)?.to_str();
128            [
129                &prefix,
130                ":type.",
131                addr_type_str,
132                ":",
133                &payload_str,
134                &checksum_str,
135            ]
136            .concat()
137            .to_uppercase()
138        }
139    };
140    Ok(mazze_base32_addr)
141}
142
143pub fn mazze_addr_decode(
144    addr_str: &str,
145) -> Result<DecodedRawAddress, DecodingError> {
146    // FIXME: add a unit test for addr_str in capital letters.
147    let has_lowercase = addr_str.chars().any(|c| c.is_lowercase());
148    let has_uppercase = addr_str.chars().any(|c| c.is_uppercase());
149    if has_lowercase && has_uppercase {
150        return Err(DecodingError::MixedCase);
151    }
152    let lowercase = addr_str.to_lowercase();
153
154    // Delimit and extract prefix
155    let parts: Vec<&str> = lowercase.split(':').collect();
156    if parts.len() < 2 {
157        return Err(DecodingError::NoPrefix);
158    }
159    let prefix = parts[0];
160    // Match network
161    let network = Network::from_prefix(prefix)?;
162
163    let mut address_type = None;
164    // Parse optional parts. We will ignore everything we can't understand.
165    for option_str in &parts[1..parts.len() - 1] {
166        let key_value: Vec<&str> = option_str.split('.').collect();
167        if key_value.len() != 2 {
168            return Err(DecodingError::InvalidOption(OptionError::ParseError(
169                (*option_str).into(),
170            )));
171        }
172        // Address type.
173        if key_value[0] == "type" {
174            address_type = Some(AddressType::parse(key_value[1])?);
175        }
176    }
177
178    // Do some sanity checks on the payload string
179    let payload_str = parts[parts.len() - 1];
180    if payload_str.len() == 0 {
181        return Err(DecodingError::InvalidLength(0));
182    }
183    let has_lowercase = payload_str.chars().any(|c| c.is_lowercase());
184    let has_uppercase = payload_str.chars().any(|c| c.is_uppercase());
185    if has_lowercase && has_uppercase {
186        return Err(DecodingError::MixedCase);
187    }
188
189    // Decode payload to 5 bit array
190    let payload_chars = payload_str.chars();
191    let payload_5_bits: Result<Vec<u8>, DecodingError> = payload_chars
192        .map(|c| {
193            let i = c as usize;
194            if let Some(Some(d)) = CHAR_INDEX.get(i) {
195                Ok(*d as u8)
196            } else {
197                Err(DecodingError::InvalidChar(c))
198            }
199        })
200        .collect();
201    let payload_5_bits = payload_5_bits?;
202
203    // Verify the checksum
204    let checksum =
205        polymod(&[&expand_prefix(prefix), &payload_5_bits[..]].concat());
206    if checksum != 0 {
207        // TODO: according to the spec it is possible to do correction based on
208        // the checksum,  we shouldn't do it automatically but we could
209        // include the corrected address in  the error.
210        return Err(DecodingError::ChecksumFailed(checksum));
211    }
212
213    // Convert from 5 bit array to byte array
214    let len_5_bit = payload_5_bits.len();
215    let payload =
216        convert_bits(&payload_5_bits[..(len_5_bit - 8)], 5, 8, false)?;
217
218    // Verify the version byte
219    let version = payload[0];
220
221    // Check length
222    let body = &payload[1..];
223    let body_len = body.len();
224    let version_size = version & consts::SIZE_MASK;
225    if (version_size == consts::SIZE_160 && body_len != 20)
226        // Mazze does not have other hash sizes. We don't use the sizes below
227        // but we kept these for unit tests.
228        || (version_size == consts::SIZE_192 && body_len != 24)
229        || (version_size == consts::SIZE_224 && body_len != 28)
230        || (version_size == consts::SIZE_256 && body_len != 32)
231        || (version_size == consts::SIZE_320 && body_len != 40)
232        || (version_size == consts::SIZE_384 && body_len != 48)
233        || (version_size == consts::SIZE_448 && body_len != 56)
234        || (version_size == consts::SIZE_512 && body_len != 64)
235    {
236        return Err(DecodingError::InvalidLength(body_len));
237    }
238    // Check reserved bits
239    if version & consts::RESERVED_BITS_MASK != 0 {
240        return Err(DecodingError::VersionNotRecognized(version));
241    }
242
243    let hex_address;
244    // Check address type for parsed H160 address.
245    if version_size == consts::SIZE_160 {
246        hex_address = Some(Address::from_slice(body));
247        match address_type {
248            Some(expected) => {
249                let got =
250                    AddressType::from_address(hex_address.as_ref().unwrap())
251                        .or(Err(()));
252                if got.as_ref() != Ok(&expected) {
253                    return Err(DecodingError::InvalidOption(
254                        OptionError::AddressTypeMismatch { expected, got },
255                    ));
256                }
257            }
258            None => {}
259        }
260    } else {
261        hex_address = None;
262    }
263
264    Ok(DecodedRawAddress {
265        input_base32_address: addr_str.into(),
266        parsed_address_bytes: body.to_vec(),
267        hex_address,
268        network,
269    })
270}
271
272/// The checksum calculation includes the lower 5 bits of each character of the
273/// prefix.
274/// - e.g. "bit..." becomes 2,9,20,...
275// Expand the address prefix for the checksum operation.
276fn expand_prefix(prefix: &str) -> Vec<u8> {
277    let mut ret: Vec<u8> = prefix.chars().map(|c| (c as u8) & 0x1f).collect();
278    ret.push(0);
279    ret
280}
281
282// This method assume that data is valid string of inbits.
283// When pad is true, any remaining bits are padded and encoded into a new byte;
284// when pad is false, any remaining bits are checked to be zero and discarded.
285fn convert_bits(
286    data: &[u8], inbits: u8, outbits: u8, pad: bool,
287) -> Result<Vec<u8>, DecodingError> {
288    assert!(inbits <= 8 && outbits <= 8);
289    let num_bytes = (data.len() * inbits as usize + outbits as usize - 1)
290        / outbits as usize;
291    let mut ret = Vec::with_capacity(num_bytes);
292    let mut acc: u16 = 0; // accumulator of bits
293    let mut num: u8 = 0; // num bits in acc
294    let groupmask = (1 << outbits) - 1;
295    for d in data.iter() {
296        // We push each input chunk into a 16-bit accumulator
297        acc = (acc << inbits) | u16::from(*d);
298        num += inbits;
299        // Then we extract all the output groups we can
300        while num >= outbits {
301            // Store only the highest outbits.
302            ret.push((acc >> (num - outbits)) as u8);
303            // Clear the highest outbits.
304            acc &= !(groupmask << (num - outbits));
305            num -= outbits;
306        }
307    }
308    if pad {
309        // If there's some bits left, pad and add it
310        if num > 0 {
311            ret.push((acc << (outbits - num)) as u8);
312        }
313    } else {
314        // FIXME: add unit tests for it.
315        // If there's some bits left, figure out if we need to remove padding
316        // and add it
317        let padding = ((data.len() * inbits as usize) % outbits as usize) as u8;
318        if num >= inbits || acc != 0 {
319            return Err(DecodingError::InvalidPadding {
320                from_bits: inbits,
321                padding_bits: padding,
322                padding: acc,
323            });
324        }
325    }
326    Ok(ret)
327}
328
329#[test]
330fn test_expand_prefix() {
331    assert_eq!(expand_prefix("mazze"), vec![0x03, 0x06, 0x18, 0x00]);
332
333    assert_eq!(
334        expand_prefix("mazzetest"),
335        vec![0x03, 0x06, 0x18, 0x14, 0x05, 0x13, 0x14, 0x00]
336    );
337
338    assert_eq!(
339        expand_prefix("net17"),
340        vec![0x0e, 0x05, 0x14, 0x11, 0x17, 0x00]
341    );
342}
343
344#[test]
345fn test_convert_bits() {
346    // 00000000 --> 0, 0, 0, 0, 0, 0, 0, 0
347    assert_eq!(convert_bits(&[0], 8, 1, false), Ok(vec![0; 8]));
348
349    // 00000000 --> 000, 000, 00_
350    assert_eq!(convert_bits(&[0], 8, 3, false), Ok(vec![0, 0])); // 00_ is dropped
351    assert_eq!(convert_bits(&[0], 8, 3, true), Ok(vec![0, 0, 0])); // 00_ becomes 000
352
353    // 00000001 --> 000, 000, 01_
354    assert!(convert_bits(&[1], 8, 3, false).is_err()); // 01_ != 0 (ignored incomplete chunk must be 0)
355    assert_eq!(convert_bits(&[1], 8, 3, true), Ok(vec![0, 0, 2])); // 01_ becomes 010
356
357    // 00000001 --> 0000000, 1______
358    assert_eq!(convert_bits(&[1], 8, 7, true), Ok(vec![0, 64])); // 1______ becomes 1000000
359
360    // 0, 0, 0, 0, 0, 0, 0, 0 --> 00000000
361    assert_eq!(convert_bits(&[0; 8], 1, 8, false), Ok(vec![0]));
362
363    // 000, 000, 010 -> 00000001, 0_______
364    assert_eq!(convert_bits(&[0, 0, 2], 3, 8, false), Ok(vec![1])); // 0_______ is dropped
365    assert_eq!(convert_bits(&[0, 0, 2], 3, 8, true), Ok(vec![1, 0])); // 0_______ becomes 00000000
366
367    // 000, 000, 011 -> 00000001, 1_______
368    assert!(convert_bits(&[0, 0, 3], 3, 8, false).is_err()); // 1_______ != 0 (ignored incomplete chunk must be 0)
369
370    // 00000000, 00000001, 00000010, 00000011, 00000100 -->
371    // 00000, 00000, 00000, 10000, 00100, 00000, 11000, 00100
372    assert_eq!(
373        convert_bits(&[0, 1, 2, 3, 4], 8, 5, false),
374        Ok(vec![0, 0, 0, 16, 4, 0, 24, 4])
375    );
376
377    // 00000000, 00000001, 00000010 -->
378    // 00000, 00000, 00000, 10000, 0010_
379    assert!(convert_bits(&[0, 1, 2], 8, 5, false).is_err()); // 0010_ != 0 (ignored incomplete chunk must be 0)
380
381    assert_eq!(
382        convert_bits(&[0, 1, 2], 8, 5, true),
383        Ok(vec![0, 0, 0, 16, 4])
384    ); // 0010_ becomes 00100
385
386    // 00000, 00000, 00000, 10000, 00100, 00000, 11000, 00100 -->
387    // 00000000, 00000001, 00000010, 00000011, 00000100
388    assert_eq!(
389        convert_bits(&[0, 0, 0, 16, 4, 0, 24, 4], 5, 8, false),
390        Ok(vec![0, 1, 2, 3, 4])
391    );
392
393    // 00000, 00000, 00000, 10000, 00100 -->
394    // 00000000, 00000001, 00000010, 0_______
395    assert_eq!(
396        convert_bits(&[0, 0, 0, 16, 4], 5, 8, false),
397        Ok(vec![0, 1, 2])
398    ); // 0_______ is dropped
399
400    assert_eq!(
401        convert_bits(&[0, 0, 0, 16, 4], 5, 8, true),
402        Ok(vec![0, 1, 2, 0])
403    ); // 0_______ becomes 00000000
404}