1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! velas-address-rust
//!
//! Rust lib for en/decoding address to velas/ether format
//!
//! ```rust
//! use velas_address_rust::*;
//!
//! let eth_addresses = "0x32Be343B94f860124dC4fEe278FDCBD38C102D88";
//! let vlx_addr = eth_to_vlx(eth_addresses).unwrap(); // V5dJeCa7bmkqmZF53TqjRbnB4fG6hxuu4f
//! let eth_addr = vlx_to_eth(&vlx_addr).unwrap(); // 0x32be343b94f860124dc4fee278fdcbd38c102d88
//! ```
//!
use basex_rs::{BaseX, Decode, Encode, BITCOIN};
use bitcoin_hashes::sha256;
use bitcoin_hashes::Hash;
use hex;
use regex::Regex;
use std::str;

fn hash_sha256(byte: &[u8]) -> String {
    format!("{}", sha256::Hash::hash(byte))
}

/// Convert ETH address to VLX address
///
/// ```rust
/// use velas_address_rust::*;
///
/// let eth_addresses = "0x32Be343B94f860124dC4fEe278FDCBD38C102D88";
/// assert_eq!(eth_to_vlx(eth_addresses).unwrap(), "V5dJeCa7bmkqmZF53TqjRbnB4fG6hxuu4f".to_string())
/// ```
///
pub fn eth_to_vlx(address: &str) -> Result<String, &str> {
    if address.is_empty() {
        return Err("Invalid address");
    }

    if !address.starts_with("0x") {
        return Err("Invalid address");
    }

    let clear_addr = match address.get(2..address.len()) {
        Some(addr) => addr.to_lowercase(),
        None => return Err("Invalid address"),
    };

    let hash_big = hash_sha256(hash_sha256(clear_addr.as_bytes()).as_bytes());
    let checksum = match hash_big.get(0..8) {
        Some(hash) => hash,
        None => return Err("Invalid address"),
    };

    let long_address = format!("{}{}", clear_addr, checksum);

    let bytes = hex::decode(long_address).unwrap().to_vec();

    let mut encode = BaseX::new(BITCOIN).encode(&bytes);

    if encode.len() < 33 {
        encode = format!("{}{}", "1".repeat(33 - encode.len()), encode);
    }

    Ok(format!("V{}", encode))
}

/// Convert VLX address to ETH address
///
/// ```rust
/// use velas_address_rust::*;
///
/// let vlx_addresses = "V5dJeCa7bmkqmZF53TqjRbnB4fG6hxuu4f";
/// assert_eq!(vlx_to_eth(vlx_addresses).unwrap(), "0x32be343b94f860124dc4fee278fdcbd38c102d88".to_string())
/// ```
///
pub fn vlx_to_eth(address: &str) -> Result<String, &str> {
    if address.is_empty() {
        return Err("Invalid address");
    }

    if !address.starts_with("V") {
        return Err("Invalid address");
    }

    let clear_addr = match address.get(1..address.len()) {
        Some(addr) => addr,
        None => return Err("Invalid address"),
    };

    let decode_addr = match BaseX::new(BITCOIN).decode(clear_addr.to_string()) {
        Some(bytes) => bytes,
        None => return Err("Invalid address"),
    };

    let hex = hex::encode(decode_addr);

    let re = Regex::new(r"([0-9abcdef]+)([0-9abcdef]{8})").unwrap();

    let caps = re.captures(&hex).unwrap();

    if caps.len() != 3 as usize {
        return Err("Invalid address");
    }

    let mut match_addr = &caps[1];

    if match_addr.len() > 40 {
        let len = match_addr.len() - 40;
        if match_addr.starts_with(&"0".repeat(len)) {
            match_addr = match match_addr.get(len..match_addr.len()) {
                Some(addr) => addr,
                None => return Err("Invalid address"),
            }
        } else {
            return Err("Invalid address");
        }
    }

    let hash_big = hash_sha256(hash_sha256(match_addr.as_bytes()).as_bytes());
    let checksum = match hash_big.get(0..8) {
        Some(hash) => hash,
        None => return Err("Invalid address"),
    };

    if checksum != &caps[2] {
        return Err("Invalid checksum");
    }

    Ok(format!("0x{}", match_addr))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let eth_addresses = [
            "0x32Be343B94f860124dC4fEe278FDCBD38C102D88",
            "0x000000000000000000000000000000000000000f",
            "0xf000000000000000000000000000000000000000",
            "0x0000000000000000000000000000000000000001",
            "0x1000000000000000000000000000000000000000",
            "0x0000000000000000000000000000000000000000",
            "0xffffffffffffffffffffffffffffffffffffffff",
        ];

        let vlx_addresses = [
            "V5dJeCa7bmkqmZF53TqjRbnB4fG6hxuu4f",
            "V111111111111111111111111112jSS6vy",
            "VNt1B3HD3MghPihCxhwMxNKRerBPPbiwvZ",
            "V111111111111111111111111111CdXjnE",
            "V2Tbp525fpnBRiSt4iPxXkxMyf5ZX7bGAJ",
            "V1111111111111111111111111113iMDfC",
            "VQLbz7JHiBTspS962RLKV8GndWFwdcRndD",
        ];

        for addr in eth_addresses.iter() {
            let vlx_addr = eth_to_vlx(addr).unwrap();
            let eth_addr = vlx_to_eth(&vlx_addr).unwrap();
            assert_eq!(eth_addr.to_string(), addr.to_string().to_lowercase());
        }

        for addr in vlx_addresses.iter() {
            let eth_addr = vlx_to_eth(addr).unwrap();
            let vlx_addr = eth_to_vlx(&eth_addr).unwrap();
            assert_eq!(vlx_addr.to_string(), addr.to_string());
        }
    }
}