velas_address_rust/
lib.rs

1//! velas-address-rust
2//!
3//! Rust lib for en/decoding address to velas/ether format
4//!
5//! ```rust
6//! use velas_address_rust::*;
7//!
8//! let eth_addresses = "0x32Be343B94f860124dC4fEe278FDCBD38C102D88";
9//! let vlx_addr = eth_to_vlx(eth_addresses).unwrap(); // V5dJeCa7bmkqmZF53TqjRbnB4fG6hxuu4f
10//! let eth_addr = vlx_to_eth(&vlx_addr).unwrap(); // 0x32be343b94f860124dc4fee278fdcbd38c102d88
11//! ```
12//!
13use basex_rs::{BaseX, Decode, Encode, BITCOIN};
14use bitcoin_hashes::sha256;
15use bitcoin_hashes::Hash;
16use hex;
17use regex::Regex;
18use std::str;
19
20fn hash_sha256(byte: &[u8]) -> String {
21    format!("{}", sha256::Hash::hash(byte))
22}
23
24/// Convert ETH address to VLX address
25///
26/// ```rust
27/// use velas_address_rust::*;
28///
29/// let eth_addresses = "0x32Be343B94f860124dC4fEe278FDCBD38C102D88";
30/// assert_eq!(eth_to_vlx(eth_addresses).unwrap(), "V5dJeCa7bmkqmZF53TqjRbnB4fG6hxuu4f".to_string())
31/// ```
32///
33pub fn eth_to_vlx(address: &str) -> Result<String, &str> {
34    if address.is_empty() {
35        return Err("Invalid address");
36    }
37
38    if !address.starts_with("0x") {
39        return Err("Invalid address");
40    }
41
42    let clear_addr = match address.get(2..address.len()) {
43        Some(addr) => addr.to_lowercase(),
44        None => return Err("Invalid address"),
45    };
46
47    let hash_big = hash_sha256(hash_sha256(clear_addr.as_bytes()).as_bytes());
48    let checksum = match hash_big.get(0..8) {
49        Some(hash) => hash,
50        None => return Err("Invalid address"),
51    };
52
53    let long_address = format!("{}{}", clear_addr, checksum);
54
55    let bytes = hex::decode(long_address).unwrap().to_vec();
56
57    let mut encode = BaseX::new(BITCOIN).encode(&bytes);
58
59    if encode.len() < 33 {
60        encode = format!("{}{}", "1".repeat(33 - encode.len()), encode);
61    }
62
63    Ok(format!("V{}", encode))
64}
65
66/// Convert VLX address to ETH address
67///
68/// ```rust
69/// use velas_address_rust::*;
70///
71/// let vlx_addresses = "V5dJeCa7bmkqmZF53TqjRbnB4fG6hxuu4f";
72/// assert_eq!(vlx_to_eth(vlx_addresses).unwrap(), "0x32be343b94f860124dc4fee278fdcbd38c102d88".to_string())
73/// ```
74///
75pub fn vlx_to_eth(address: &str) -> Result<String, &str> {
76    if address.is_empty() {
77        return Err("Invalid address");
78    }
79
80    if !address.starts_with("V") {
81        return Err("Invalid address");
82    }
83
84    let clear_addr = match address.get(1..address.len()) {
85        Some(addr) => addr,
86        None => return Err("Invalid address"),
87    };
88
89    let decode_addr = match BaseX::new(BITCOIN).decode(clear_addr.to_string()) {
90        Some(bytes) => bytes,
91        None => return Err("Invalid address"),
92    };
93
94    let hex = hex::encode(decode_addr);
95
96    let re = Regex::new(r"([0-9abcdef]+)([0-9abcdef]{8})").unwrap();
97
98    let caps = re.captures(&hex).unwrap();
99
100    if caps.len() != 3 as usize {
101        return Err("Invalid address");
102    }
103
104    let mut match_addr = &caps[1];
105
106    if match_addr.len() > 40 {
107        let len = match_addr.len() - 40;
108        if match_addr.starts_with(&"0".repeat(len)) {
109            match_addr = match match_addr.get(len..match_addr.len()) {
110                Some(addr) => addr,
111                None => return Err("Invalid address"),
112            }
113        } else {
114            return Err("Invalid address");
115        }
116    }
117
118    let hash_big = hash_sha256(hash_sha256(match_addr.as_bytes()).as_bytes());
119    let checksum = match hash_big.get(0..8) {
120        Some(hash) => hash,
121        None => return Err("Invalid address"),
122    };
123
124    if checksum != &caps[2] {
125        return Err("Invalid checksum");
126    }
127
128    Ok(format!("0x{}", match_addr))
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn it_works() {
137        let eth_addresses = [
138            "0x32Be343B94f860124dC4fEe278FDCBD38C102D88",
139            "0x000000000000000000000000000000000000000f",
140            "0xf000000000000000000000000000000000000000",
141            "0x0000000000000000000000000000000000000001",
142            "0x1000000000000000000000000000000000000000",
143            "0x0000000000000000000000000000000000000000",
144            "0xffffffffffffffffffffffffffffffffffffffff",
145        ];
146
147        let vlx_addresses = [
148            "V5dJeCa7bmkqmZF53TqjRbnB4fG6hxuu4f",
149            "V111111111111111111111111112jSS6vy",
150            "VNt1B3HD3MghPihCxhwMxNKRerBPPbiwvZ",
151            "V111111111111111111111111111CdXjnE",
152            "V2Tbp525fpnBRiSt4iPxXkxMyf5ZX7bGAJ",
153            "V1111111111111111111111111113iMDfC",
154            "VQLbz7JHiBTspS962RLKV8GndWFwdcRndD",
155        ];
156
157        for addr in eth_addresses.iter() {
158            let vlx_addr = eth_to_vlx(addr).unwrap();
159            let eth_addr = vlx_to_eth(&vlx_addr).unwrap();
160            assert_eq!(eth_addr.to_string(), addr.to_string().to_lowercase());
161        }
162
163        for addr in vlx_addresses.iter() {
164            let eth_addr = vlx_to_eth(addr).unwrap();
165            let vlx_addr = eth_to_vlx(&eth_addr).unwrap();
166            assert_eq!(vlx_addr.to_string(), addr.to_string());
167        }
168    }
169}