cfx_addr/
types.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
8use crate::consts::{
9    ADDRESS_TYPE_BUILTIN, ADDRESS_TYPE_CONTRACT, ADDRESS_TYPE_NULL,
10    ADDRESS_TYPE_UNKNOWN, ADDRESS_TYPE_USER, MAINNET_PREFIX, NETWORK_ID_PREFIX,
11    RESERVED_NETWORK_IDS, TESTNET_PREFIX,
12};
13use cfx_types::{
14    address_util::{self, AddressUtil},
15    Address,
16};
17use core::fmt;
18
19#[cfg(feature = "std")]
20use std::error::Error;
21
22#[cfg(not(feature = "std"))]
23use alloc::{
24    format,
25    string::{String, ToString},
26    vec::Vec,
27};
28
29/// Struct containing the raw bytes and metadata of a Conflux address.
30#[derive(PartialEq, Eq, Clone, Debug, Hash)]
31pub struct DecodedRawAddress {
32    /// Base32 address. This is included for debugging purposes.
33    pub input_base32_address: String,
34    /// Address bytes
35    pub parsed_address_bytes: Vec<u8>,
36    /// The parsed address in H160 format.
37    pub hex_address: Option<Address>,
38    /// Network
39    pub network: Network,
40}
41
42#[derive(Copy, Clone)]
43pub enum EncodingOptions {
44    Simple,
45    QrCode,
46}
47
48#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
49pub enum Network {
50    /// Main network.
51    Main,
52    /// Test network.
53    Test,
54    /// Specific Network Id.
55    Id(u64),
56}
57
58impl Network {
59    pub fn to_prefix(&self) -> Result<String, EncodingError> {
60        match self {
61            Network::Main => Ok(MAINNET_PREFIX.into()),
62            Network::Test => Ok(TESTNET_PREFIX.into()),
63            Network::Id(network_id) => {
64                if RESERVED_NETWORK_IDS.contains(network_id) {
65                    Err(EncodingError::InvalidNetworkId(*network_id))
66                } else {
67                    Ok(format!("net{}", network_id))
68                }
69            }
70        }
71    }
72
73    pub fn from_prefix(prefix: &str) -> Result<Self, DecodingError> {
74        match prefix {
75            MAINNET_PREFIX => Ok(Network::Main),
76            TESTNET_PREFIX => Ok(Network::Test),
77            _ => {
78                let maybe_network_id = if !prefix.starts_with(NETWORK_ID_PREFIX)
79                {
80                    None
81                } else {
82                    match prefix[NETWORK_ID_PREFIX.len()..].parse::<u64>() {
83                        Err(_) => None,
84                        Ok(network_id) => {
85                            // Check if network_id is valid.
86                            if RESERVED_NETWORK_IDS.contains(&network_id) {
87                                None
88                            } else {
89                                Some(network_id)
90                            }
91                        }
92                    }
93                };
94
95                match maybe_network_id {
96                    None => {
97                        Err(DecodingError::InvalidPrefix(prefix.to_string()))
98                    }
99                    Some(network_id) => Ok(Network::Id(network_id)),
100                }
101            }
102        }
103    }
104}
105
106impl fmt::Display for Network {
107    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108        match self.to_prefix() {
109            Err(EncodingError::InvalidNetworkId(network_id)) => {
110                write!(f, "invalid network prefix net{}", network_id)
111            }
112            Err(_) => unreachable!(),
113            Ok(prefix) => write!(f, "{}", prefix),
114        }
115    }
116}
117
118#[derive(Debug, PartialEq, Eq, Clone)]
119pub enum AddressType {
120    Builtin,
121    Contract,
122    Null,
123    User,
124    Unknown,
125}
126
127impl AddressType {
128    const BUILTIN: &'static str = ADDRESS_TYPE_BUILTIN;
129    const CONTRACT: &'static str = ADDRESS_TYPE_CONTRACT;
130    const NULL: &'static str = ADDRESS_TYPE_NULL;
131    const UNKNOWN: &'static str = ADDRESS_TYPE_UNKNOWN;
132    const USER: &'static str = ADDRESS_TYPE_USER;
133
134    pub fn parse(text: &str) -> Result<Self, DecodingError> {
135        if text == Self::BUILTIN {
136            Ok(Self::Builtin)
137        } else if text == Self::CONTRACT {
138            Ok(Self::Contract)
139        } else if text == Self::NULL {
140            Ok(Self::Null)
141        } else if text == Self::USER {
142            Ok(Self::User)
143        } else {
144            Ok(Self::Unknown)
145        }
146    }
147
148    pub fn from_address<T: AddressUtil>(
149        address_hex: &T,
150    ) -> Result<Self, EncodingError> {
151        match address_hex.address_type_bits() {
152            address_util::TYPE_BITS_BUILTIN => {
153                if address_hex.is_null_address() {
154                    Ok(Self::Null)
155                } else {
156                    Ok(Self::Builtin)
157                }
158            }
159            address_util::TYPE_BITS_CONTRACT => Ok(Self::Contract),
160            address_util::TYPE_BITS_USER_ACCOUNT => Ok(Self::User),
161            _ => Ok(Self::Unknown),
162        }
163    }
164
165    pub fn to_str(&self) -> &'static str {
166        match self {
167            Self::Builtin => Self::BUILTIN,
168            Self::Contract => Self::CONTRACT,
169            Self::Null => Self::NULL,
170            Self::User => Self::USER,
171            Self::Unknown => Self::UNKNOWN,
172        }
173    }
174}
175
176impl fmt::Display for AddressType {
177    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178        write!(f, "{}", self.to_str())
179    }
180}
181
182/// Error concerning encoding of cfx_base32_addr.
183#[derive(Debug, PartialEq, Eq, Clone)]
184pub enum EncodingError {
185    InvalidAddressType(u8),
186    InvalidLength(usize),
187    InvalidNetworkId(u64),
188}
189
190impl fmt::Display for EncodingError {
191    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192        match self {
193            Self::InvalidAddressType(type_byte) => {
194                write!(f, "unrecognized type bits 0x{:02x}", type_byte)
195            }
196            Self::InvalidLength(length) => {
197                write!(f, "invalid length ({})", length)
198            }
199            Self::InvalidNetworkId(network_id) => {
200                write!(f, "invalid network_id (reserved: {})", network_id)
201            }
202        }
203    }
204}
205
206#[cfg(feature = "std")]
207impl Error for EncodingError {
208    fn cause(&self) -> Option<&dyn Error> { None }
209
210    fn description(&self) -> &str { "invalid length" }
211}
212
213/// Error concerning decoding of cfx_base32_addr.
214#[derive(Debug, PartialEq, Eq, Clone)]
215pub enum DecodingError {
216    /// Invalid length (length).
217    InvalidLength(usize),
218    /// Zero or multiple prefixes.
219    NoPrefix,
220    /// Failed to match known prefixes (prefix).
221    InvalidPrefix(String),
222    /// Failed to match known options.
223    InvalidOption(OptionError),
224    /// Checksum failed (checksum).
225    ChecksumFailed(u64),
226    /// Unexpected character (char).
227    InvalidChar(char),
228    /// Padding is invalid. Either padding_bits > from_bits or
229    /// padding is non-zero.
230    InvalidPadding {
231        from_bits: u8,
232        padding_bits: u8,
233        padding: u16,
234    },
235    /// Version byte was not recognized.
236    VersionNotRecognized(u8),
237    /// Upper and lowercase address string.
238    MixedCase,
239}
240
241#[derive(Debug, PartialEq, Eq, Clone)]
242pub enum OptionError {
243    /// The option string isn't in a valid format.
244    ParseError(String),
245    /// The address type specified in option doesn't match the decoded address.
246    /// The got can be an Err(()) because decoded address may have invalid
247    /// address type.
248    AddressTypeMismatch {
249        expected: AddressType,
250        got: Result<AddressType, ()>,
251    },
252    /// The address type is invalid.
253    InvalidAddressType(String),
254}
255
256impl fmt::Display for DecodingError {
257    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258        match self {
259            DecodingError::ChecksumFailed(actual) => {
260                write!(f, "invalid checksum (actual {} != 0)", actual)
261            }
262            DecodingError::InvalidChar(index) => {
263                write!(f, "invalid char ({})", index)
264            }
265            DecodingError::InvalidLength(length) => {
266                write!(f, "invalid length ({})", length)
267            }
268            DecodingError::InvalidPadding {
269                from_bits,
270                padding_bits,
271                padding,
272            } => {
273                write!(f, "invalid padding (")?;
274                if padding_bits >= from_bits {
275                    write!(
276                        f,
277                        "padding_bits({}) >= from_bits({})",
278                        padding_bits, from_bits
279                    )?;
280                    if *padding != 0 {
281                        write!(f, ", padding({:#b}) is non-zero)", padding)?;
282                    }
283                } else {
284                    write!(f, "padding({:#b}) is non-zero)", padding)?;
285                }
286                Ok(())
287            }
288            DecodingError::InvalidPrefix(prefix) => {
289                write!(f, "invalid prefix ({})", prefix)
290            }
291            DecodingError::InvalidOption(option_error) => match option_error {
292                OptionError::AddressTypeMismatch { expected, got } => {
293                    write!(f, "expected address type specified in option {:?}, decoded address type {:?}", expected, got)
294                }
295                OptionError::ParseError(option_str) => {
296                    write!(f, "invalid option string ({})", option_str)
297                }
298                OptionError::InvalidAddressType(type_str) => {
299                    write!(f, "invalid address type ({})", type_str)
300                }
301            },
302            DecodingError::NoPrefix => write!(f, "zero or multiple prefixes"),
303            DecodingError::MixedCase => write!(f, "mixed case string"),
304            DecodingError::VersionNotRecognized(c) => {
305                write!(f, "version byte ({}) not recognized", c)
306            }
307        }
308    }
309}
310
311#[cfg(feature = "std")]
312impl Error for DecodingError {
313    fn cause(&self) -> Option<&dyn Error> { None }
314
315    fn description(&self) -> &str {
316        match self {
317            DecodingError::ChecksumFailed { .. } => "invalid checksum",
318            DecodingError::InvalidChar(_) => "invalid char",
319            DecodingError::InvalidLength(_) => "invalid length",
320            DecodingError::InvalidOption(option_error) => match option_error {
321                OptionError::AddressTypeMismatch { .. } => {
322                    "decoded address does not match address type in option"
323                }
324                OptionError::ParseError(_) => "invalid option",
325                OptionError::InvalidAddressType(_) => "invalid address type",
326            },
327            DecodingError::InvalidPadding { .. } => "invalid padding",
328            DecodingError::InvalidPrefix(_) => "invalid prefix",
329            DecodingError::NoPrefix => "zero or multiple prefixes",
330            DecodingError::MixedCase => "mixed case string",
331            DecodingError::VersionNotRecognized(_) => {
332                "version byte not recognized"
333            }
334        }
335    }
336}