forked from jl777/SuperNET
-
Notifications
You must be signed in to change notification settings - Fork 120
Expand file tree
/
Copy pathaddress.rs
More file actions
201 lines (164 loc) · 6.52 KB
/
Copy pathaddress.rs
File metadata and controls
201 lines (164 loc) · 6.52 KB
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//! TRON address handling (base58, hex, validation, serde).
use ethereum_types::Address as EthAddress;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::str::FromStr;
pub const ADDRESS_PREFIX: u8 = 0x41;
pub const ADDRESS_BASE58_PREFIX: char = 'T';
pub const ADDRESS_HEX_LEN: usize = 42;
pub const ADDRESS_BYTES_LEN: usize = 21;
pub const ADDRESS_BASE58_LEN: usize = 34;
/// TRON mainnet or testnet address (21 bytes, 0x41 prefix + 20-bytes).
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Address {
pub inner: [u8; ADDRESS_BYTES_LEN],
}
impl Address {
/// Construct from raw 21 bytes (must be 0x41-prefixed).
pub fn from_bytes(bytes: [u8; ADDRESS_BYTES_LEN]) -> Result<Self, String> {
if bytes[0] != ADDRESS_PREFIX {
return Err("TRON address must start with 0x41".into());
}
Ok(Self { inner: bytes })
}
/// Construct from base58 string (with checksum).
pub fn from_base58(s: &str) -> Result<Self, String> {
let data = bs58::decode(s)
.with_check(None)
.into_vec()
.map_err(|e| format!("Invalid base58check address: {}", e))?;
// SAFETY: Accessing `data[0]` is safe here because we first check that
// `data.len() == ADDRESS_BYTES_LEN`, guaranteeing the slice is not empty
// and has at least one element.
if data.len() != ADDRESS_BYTES_LEN || data[0] != ADDRESS_PREFIX {
return Err(format!(
"Invalid address: expected {} bytes with prefix 0x{:x}",
ADDRESS_BYTES_LEN, ADDRESS_PREFIX
));
}
let inner = data
.try_into()
.map_err(|_| "Failed to convert address bytes to array".to_string())?;
Ok(Self { inner })
}
/// Construct from hex string, with or without `0x` prefix.
pub fn from_hex(s: &str) -> Result<Self, String> {
let s = s.strip_prefix("0x").unwrap_or(s);
let data = hex::decode(s).map_err(|e| format!("Invalid hex address: {}", e))?;
// SAFETY: Accessing `data[0]` is safe here because we first check that
// `data.len() == ADDRESS_BYTES_LEN`, guaranteeing the slice is not empty
// and has at least one element.
if data.len() != ADDRESS_BYTES_LEN || data[0] != ADDRESS_PREFIX {
return Err(format!(
"Invalid address: expected {} bytes with prefix 0x{:x}",
ADDRESS_BYTES_LEN, ADDRESS_PREFIX
));
}
let inner = data
.try_into()
.map_err(|_| "Failed to convert address bytes to array".to_string())?;
Ok(Self { inner })
}
/// Show as base58 string (canonical user format).
pub fn to_base58(&self) -> String { bs58::encode(self.inner).with_check().into_string() }
/// Show as hex string, lowercase (canonical hex format).
pub fn to_hex(&self) -> String { hex::encode(self.inner) }
/// Return the 21 bytes (0x41 + 20).
pub fn as_bytes(&self) -> &[u8] { &self.inner }
/// Construct TRON address from raw 20-byte Ethereum address bytes
fn from_eth_bytes(bytes: &[u8; 20]) -> Self {
let mut inner = [0u8; ADDRESS_BYTES_LEN];
inner[0] = ADDRESS_PREFIX;
inner[1..].copy_from_slice(bytes);
Self { inner }
}
}
impl TryFrom<[u8; ADDRESS_BYTES_LEN]> for Address {
type Error = String;
fn try_from(bytes: [u8; ADDRESS_BYTES_LEN]) -> Result<Self, Self::Error> { Self::from_bytes(bytes) }
}
impl AsRef<[u8]> for Address {
fn as_ref(&self) -> &[u8] { &self.inner }
}
impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.to_base58()) }
}
impl fmt::Debug for Address {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Address({} / 0x{})", self.to_base58(), self.to_hex())
}
}
impl Serialize for Address {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_base58())
}
}
impl<'de> Deserialize<'de> for Address {
fn deserialize<D>(deserializer: D) -> Result<Address, D::Error>
where
D: Deserializer<'de>,
{
let s = <&str>::deserialize(deserializer)?;
Address::from_str(s).map_err(serde::de::Error::custom)
}
}
impl FromStr for Address {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
// Check for Base58 format
if s.len() == ADDRESS_BASE58_LEN && s.starts_with(ADDRESS_BASE58_PREFIX) {
return Self::from_base58(s);
}
// Check for hex format (with or without 0x prefix)
if (s.len() == ADDRESS_HEX_LEN && s.starts_with("41"))
|| (s.len() == ADDRESS_HEX_LEN + 2 && s.starts_with("0x41"))
{
return Self::from_hex(s);
}
Err(format!(
"Invalid TRON address '{}': must be Base58 (34 chars starting with 'T') or hex (42 chars without 0x, 44 chars with 0x prefix)",
s
))
}
}
impl From<EthAddress> for Address {
fn from(eth_addr: EthAddress) -> Self { Address::from_eth_bytes(eth_addr.as_fixed_bytes()) }
}
impl From<&EthAddress> for Address {
fn from(eth_addr: &EthAddress) -> Self { Address::from_eth_bytes(eth_addr.as_fixed_bytes()) }
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_tron_address_base58_and_hex() {
let base58 = "TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL";
let hex = "418840e6c55b9ada326d211d818c34a994aeced808";
let addr1 = Address::from_str(base58).unwrap();
let addr2 = Address::from_str(hex).unwrap();
assert_eq!(addr1, addr2);
assert_eq!(addr1.to_hex(), hex);
assert_eq!(addr2.to_base58(), base58);
}
#[test]
fn test_invalid_tron_address() {
assert!(Address::from_str("foo").is_err());
assert!(Address::from_str("0xdeadbeef").is_err());
}
#[test]
fn test_convert_eth_address_to_tron() {
use ethereum_types::Address as EthAddress;
let eth_hex = "8840e6c55b9ada326d211d818c34a994aeced808";
let eth_bytes = hex::decode(eth_hex).unwrap();
let eth_address = EthAddress::from_slice(ð_bytes);
let tron_address = Address::from(eth_address);
let expected_hex = format!("41{}", eth_hex);
assert_eq!(tron_address.to_hex(), expected_hex);
let expected_base58 = "TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL";
assert_eq!(tron_address.to_base58(), expected_base58);
}
}