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
8#![cfg_attr(not(feature = "std"), no_std)]
9
10mod consts;
11mod types;
12mod utils;
13
14pub use consts::*;
15pub use types::*;
16pub use utils::*;
17
18#[cfg(not(feature = "std"))]
19extern crate alloc;
20
21#[cfg(not(feature = "std"))]
22use alloc::{string::String, vec::Vec};
23
24use cfx_types::Address;
25
26pub fn cfx_addr_encode(
27    raw: &[u8], network: Network, encoding_options: EncodingOptions,
28) -> Result<String, EncodingError> {
29    // Calculate version byte
30    let length = raw.len();
31    let version_byte = match length {
32        20 => consts::SIZE_160,
33        // Conflux does not have other hash sizes. We don't use the sizes below
34        // but we kept these for unit tests.
35        24 => consts::SIZE_192,
36        28 => consts::SIZE_224,
37        32 => consts::SIZE_256,
38        40 => consts::SIZE_320,
39        48 => consts::SIZE_384,
40        56 => consts::SIZE_448,
41        64 => consts::SIZE_512,
42        _ => return Err(EncodingError::InvalidLength(length)),
43    };
44
45    // Get prefix
46    let prefix = network.to_prefix()?;
47
48    // Convert payload to 5 bit array
49    let mut payload = Vec::with_capacity(1 + raw.len());
50    payload.push(version_byte);
51    payload.extend(raw);
52    let payload_5_bits = convert_bits(&payload, 8, 5, true)
53        .expect("no error is possible for encoding");
54
55    // Construct payload string using CHARSET
56    let payload_str: String = payload_5_bits
57        .iter()
58        .map(|b| CHARSET[*b as usize] as char)
59        .collect();
60
61    // Create checksum
62    let expanded_prefix = expand_prefix(&prefix);
63    let checksum_input =
64        [&expanded_prefix[..], &payload_5_bits, &[0; 8][..]].concat();
65    let checksum = polymod(&checksum_input);
66
67    // Convert checksum to string
68    let checksum_str: String = (0..8)
69        .rev()
70        .map(|i| CHARSET[((checksum >> (i * 5)) & 31) as usize] as char)
71        .collect();
72
73    // Concatenate all parts
74    let cfx_base32_addr = match encoding_options {
75        EncodingOptions::Simple => {
76            [&prefix, ":", &payload_str, &checksum_str].concat()
77        }
78        EncodingOptions::QrCode => {
79            let addr_type_str = AddressType::from_address(&raw)?.to_str();
80            [
81                &prefix,
82                ":type.",
83                addr_type_str,
84                ":",
85                &payload_str,
86                &checksum_str,
87            ]
88            .concat()
89            .to_uppercase()
90        }
91    };
92    Ok(cfx_base32_addr)
93}
94
95pub fn cfx_addr_decode(
96    addr_str: &str,
97) -> Result<DecodedRawAddress, DecodingError> {
98    let has_lowercase = addr_str.chars().any(|c| c.is_lowercase());
99    let has_uppercase = addr_str.chars().any(|c| c.is_uppercase());
100    if has_lowercase && has_uppercase {
101        return Err(DecodingError::MixedCase);
102    }
103    let lowercase = addr_str.to_lowercase();
104
105    // Delimit and extract prefix
106    let parts: Vec<&str> = lowercase.split(':').collect();
107    if parts.len() < 2 {
108        return Err(DecodingError::NoPrefix);
109    }
110    let prefix = parts[0];
111    // Match network
112    let network = Network::from_prefix(prefix)?;
113
114    let mut address_type = None;
115    // Parse optional parts. We will ignore everything we can't understand.
116    for option_str in &parts[1..parts.len() - 1] {
117        let key_value: Vec<&str> = option_str.split('.').collect();
118        if key_value.len() != 2 {
119            return Err(DecodingError::InvalidOption(OptionError::ParseError(
120                (*option_str).into(),
121            )));
122        }
123        // Address type.
124        if key_value[0] == "type" {
125            address_type = Some(AddressType::parse(key_value[1])?);
126        }
127    }
128
129    // Do some sanity checks on the payload string
130    let payload_str = parts[parts.len() - 1];
131    if payload_str.is_empty() {
132        return Err(DecodingError::InvalidLength(0));
133    }
134
135    // Decode payload to 5 bit array
136    let payload_chars = payload_str.chars();
137    let payload_5_bits: Result<Vec<u8>, DecodingError> = payload_chars
138        .map(|c| {
139            let i = c as usize;
140            if let Some(Some(d)) = CHAR_INDEX.get(i) {
141                Ok(*d)
142            } else {
143                Err(DecodingError::InvalidChar(c))
144            }
145        })
146        .collect();
147    let payload_5_bits = payload_5_bits?;
148
149    // Verify the checksum
150    let checksum =
151        polymod(&[&expand_prefix(prefix), &payload_5_bits[..]].concat());
152    if checksum != 0 {
153        // TODO: according to the spec it is possible to do correction based on
154        // the checksum,  we shouldn't do it automatically but we could
155        // include the corrected address in  the error.
156        return Err(DecodingError::ChecksumFailed(checksum));
157    }
158
159    // The last 8 symbols are the checksum, so we need strictly more than 8
160    // symbols to have a non-empty body after stripping the checksum.
161    let len_5_bit = payload_5_bits.len();
162    if len_5_bit <= 8 {
163        return Err(DecodingError::InvalidLength(len_5_bit));
164    }
165    // Convert from 5 bit array to byte array.
166    let payload =
167        convert_bits(&payload_5_bits[..(len_5_bit - 8)], 5, 8, false)?;
168    if payload.is_empty() {
169        return Err(DecodingError::InvalidLength(0));
170    }
171
172    // Verify the version byte
173    let version = payload[0];
174
175    // Check length
176    let body = &payload[1..];
177    let body_len = body.len();
178    let version_size = version & consts::SIZE_MASK;
179    if (version_size == consts::SIZE_160 && body_len != 20)
180        // Conflux does not have other hash sizes. We don't use the sizes below
181        // but we kept these for unit tests.
182        || (version_size == consts::SIZE_192 && body_len != 24)
183        || (version_size == consts::SIZE_224 && body_len != 28)
184        || (version_size == consts::SIZE_256 && body_len != 32)
185        || (version_size == consts::SIZE_320 && body_len != 40)
186        || (version_size == consts::SIZE_384 && body_len != 48)
187        || (version_size == consts::SIZE_448 && body_len != 56)
188        || (version_size == consts::SIZE_512 && body_len != 64)
189    {
190        return Err(DecodingError::InvalidLength(body_len));
191    }
192    // Check reserved bits
193    if version & consts::RESERVED_BITS_MASK != 0 {
194        return Err(DecodingError::VersionNotRecognized(version));
195    }
196
197    let hex_address;
198    // Check address type for parsed H160 address.
199    if version_size == consts::SIZE_160 {
200        hex_address = Some(Address::from_slice(body));
201        if let Some(expected) = address_type {
202            let got = AddressType::from_address(hex_address.as_ref().unwrap())
203                .or(Err(()));
204            if got.as_ref() != Ok(&expected) {
205                return Err(DecodingError::InvalidOption(
206                    OptionError::AddressTypeMismatch { expected, got },
207                ));
208            }
209        }
210    } else {
211        hex_address = None;
212    }
213
214    Ok(DecodedRawAddress {
215        input_base32_address: addr_str.into(),
216        parsed_address_bytes: body.to_vec(),
217        hex_address,
218        network,
219    })
220}