cfx_addr/
lib.rs

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