qtum_address_rust/
lib.rs

1//! qtum-address-rust
2//!
3//! Rust lib for en/decoding address to Qtum/Ethereum format
4//!
5//! ```rust
6//! use qtum_address_rust::*;
7//!
8//! let addr = "qTTH1Yr2eKCuDLqfxUyBLCAjmomQ8pyrBt";
9//!
10//! let qtum = QtumAddress::new(QtumNetwork::Testnet); // testnet network prefix
11//! let eth_addr = qtum.gethexaddress(addr).unwrap(); // 6c89a1a6ca2ae7c00b248bb2832d6f480f27da68
12//! let qtum_addr = qtum.fromhexaddress(&eth_addr).unwrap(); // qTTH1Yr2eKCuDLqfxUyBLCAjmomQ8pyrBt
13//!
14//! assert_eq!(addr, qtum_addr)
15//! ```
16//!
17use basex_rs::{BaseX, Decode, Encode, BITCOIN};
18use bitcoin_hashes::sha256;
19use bitcoin_hashes::Hash;
20use hex;
21
22/// Enum of Qtum networks
23pub enum QtumNetwork {
24    /// Prefix address - 0x3a
25    Mainnet,
26    /// Prefix address - 0x78
27    Testnet,
28}
29
30impl QtumNetwork {
31    /// Getting prefix byte from network type
32    pub fn to_prefix_byte(&self) -> u8 {
33        match self {
34            QtumNetwork::Mainnet => 0x3a,
35            QtumNetwork::Testnet => 0x78,
36        }
37    }
38}
39
40impl From<u8> for QtumNetwork {
41    fn from(item: u8) -> Self {
42        match item {
43            0x3a => QtumNetwork::Mainnet,
44            0x78 => QtumNetwork::Testnet,
45            _ => panic!(""),
46        }
47    }
48}
49
50/// Structure for conversion ktum addresses
51pub struct QtumAddress {
52    prefix: u8,
53}
54
55impl QtumAddress {
56    /// Initialization of the address conversion structure
57    pub fn new(network: QtumNetwork) -> Self {
58        Self {
59            prefix: network.to_prefix_byte(),
60        }
61    }
62
63    /// Converts a base58 pubkeyhash address to a hex address for use in smart contracts.
64    pub fn gethexaddress(&self, address: &str) -> Result<String, &str> {
65        if address.is_empty() {
66            return Err("Invalid address");
67        }
68
69        let decode_bytes = match BaseX::new(BITCOIN).decode(address.to_string()) {
70            Some(bytes) => bytes,
71            None => return Err("Invalid address"),
72        };
73
74        let new_bytes = match decode_bytes.get(1..21) {
75            Some(hash) => hash,
76            None => return Err("Invalid address"),
77        };
78
79        let hex = hex::encode(new_bytes);
80
81        Ok(hex)
82    }
83
84    /// Converts a raw hex address to a base58 pubkeyhash address
85    pub fn fromhexaddress(&self, address: &str) -> Result<String, &str> {
86        if address.is_empty() || address.len() != 40 {
87            return Err("Invalid address");
88        }
89
90        let mut address_bytes = match hex::decode(address) {
91            Ok(bytes) => bytes,
92            Err(_) => return Err("Invalid address"),
93        };
94        address_bytes.insert(0, self.prefix);
95
96        let checksum = self.hash(&self.hash(&address_bytes));
97        match checksum.get(0..4) {
98            Some(hash) => {
99                for byte in hash.iter() {
100                    address_bytes.push(*byte);
101                }
102            }
103            None => return Err("Invalid address"),
104        };
105
106        let encode = BaseX::new(BITCOIN).encode(&address_bytes);
107
108        Ok(encode)
109    }
110
111    /// Adding an ethereum address prefix
112    pub fn addprefix(address: &str) -> String {
113        format!("0x{}", address)
114    }
115
116    /// SHA256 hash function
117    fn hash(&self, byte: &Vec<u8>) -> Vec<u8> {
118        hex::decode(sha256::Hash::hash(byte.as_slice()).to_string()).unwrap()
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn it_works_testnet() {
128        let eth_addresses = [
129            "6c89a1a6ca2ae7c00b248bb2832d6f480f27da68",
130            "49a80104c0d27a9ba29678d07e87a57151107613",
131            "7926223070547d2d15b2ef5e7383e541c338ffe9",
132            "2352be3db3177f0a07efbe6da5857615b8c9901d",
133            "69b004ac2b3993bf2fdf56b02746a1f57997420d",
134            "8c647515f03daeefd09872d7530fa8d8450f069a",
135            "2191744eb5ebeac90e523a817b77a83a0058003b",
136            "88b0bf4b301c21f8a47be2188bad6467ad556dcf",
137        ];
138
139        let qtum_addresses = [
140            "qTTH1Yr2eKCuDLqfxUyBLCAjmomQ8pyrBt",
141            "qQGqkA16ZY6bCYy7Qjr77eU4BPsdadibCG",
142            "qUbxboqjBRp96j3La8D1RYkyqx5uQbJPoW",
143            "qLn9vqbr2Gx3TsVR9QyTVB5mrMoh4x43Uf",
144            "qTCCy8qy7pW94EApdoBjYc1vQ2w68UnXPi",
145            "qWMi6ne9mDQFatRGejxdDYVUV9rQVkAFGp",
146            "qLcshhsRS6HKeTKRYFdpXnGVZxw96QQcfm",
147            "qW28njWueNpBXYWj2KDmtFG2gbLeALeHfV",
148        ];
149
150        let qtum = QtumAddress::new(QtumNetwork::Testnet);
151
152        for addr in qtum_addresses.iter() {
153            let eth_addr = qtum.gethexaddress(addr).unwrap();
154            let qtum_addr = qtum.fromhexaddress(&eth_addr).unwrap();
155            assert_eq!(qtum_addr.to_string(), addr.to_string());
156        }
157
158        for addr in eth_addresses.iter() {
159            let qtum_addr = qtum.fromhexaddress(addr).unwrap();
160            let eth_addr = qtum.gethexaddress(&qtum_addr).unwrap();
161            assert_eq!(eth_addr.to_string(), addr.to_string());
162        }
163    }
164}