neo3/neo_types/
address.rs1use std::{fmt, str::FromStr};
6
7use primitive_types::H160;
8use rand::Rng;
9use serde::{Deserialize, Serialize};
10
11use crate::{
12 crypto::{base58check_decode, base58check_encode, HashableForVec, Secp256r1PublicKey},
13 neo_crypto::utils::{FromHexString, ToHexString},
14 neo_error::NeoError,
15 neo_types::{ScriptHash, ScriptHashExtension, StringExt, TypeError},
16 prelude::Bytes,
17};
18
19pub type Address = String;
20
21#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
22pub enum NameOrAddress {
23 Name(String),
24 Address(Address),
25}
26
27pub trait AddressExtension {
30 fn address_to_script_hash(&self) -> Result<ScriptHash, TypeError>;
44
45 fn script_to_script_hash(&self) -> Result<ScriptHash, TypeError>;
57
58 fn hex_to_script_hash(&self) -> Result<ScriptHash, TypeError>;
70
71 fn random() -> Self;
82}
83
84impl AddressExtension for String {
85 fn address_to_script_hash(&self) -> Result<ScriptHash, TypeError> {
86 let decoded_data = match bs58::decode(self).into_vec() {
88 Ok(data) => data,
89 Err(_) => return Err(TypeError::InvalidAddress),
90 };
91 let data_payload = decoded_data[1..decoded_data.len() - 4].to_vec();
92 Ok(H160::from_slice(data_payload.as_slice()))
93 }
94
95 fn script_to_script_hash(&self) -> Result<ScriptHash, TypeError> {
96 self.from_hex_string()
97 .map(|data| ScriptHash::from_script(data.as_slice()))
98 .map_err(|_| TypeError::InvalidScript("Invalid hex string".to_string()))
99 }
100
101 fn hex_to_script_hash(&self) -> Result<ScriptHash, TypeError> {
102 if self.is_valid_hex() {
103 ScriptHash::from_hex(self.as_str())
104 .map_err(|_| TypeError::InvalidFormat("Invalid hex format".to_string()))
105 } else {
106 Err(TypeError::InvalidFormat("Invalid hex format".to_string()))
107 }
108 }
109
110 fn random() -> Self {
111 let mut rng = rand::thread_rng();
112 let mut bytes = [0u8; 20];
113 rng.fill(&mut bytes);
114 let script_hash = bytes.sha256_ripemd160();
115 let mut data = vec![0x17];
116 data.extend_from_slice(&script_hash);
117 let sha = &data.hash256().hash256();
118 data.extend_from_slice(&sha[..4]);
119 bs58::encode(data).into_string()
120 }
121}
122
123impl AddressExtension for &str {
124 fn address_to_script_hash(&self) -> Result<ScriptHash, TypeError> {
125 self.to_string().address_to_script_hash()
126 }
127
128 fn script_to_script_hash(&self) -> Result<ScriptHash, TypeError> {
129 self.to_string().script_to_script_hash()
130 }
131
132 fn hex_to_script_hash(&self) -> Result<ScriptHash, TypeError> {
133 self.to_string().hex_to_script_hash()
134 }
135
136 fn random() -> Self {
137 ""
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn test_address_to_script_hash() {
149 let n3_address = "NTGYC16CN5QheM4ZwfhUp9JKq8bMjWtcAp";
151 let expected_script_hash_hex = "50acc01271492d7b0e264ace0d60d572e66bc087";
152 let result = n3_address
153 .address_to_script_hash()
154 .expect("Should be able to convert valid N3 address to script hash");
155 assert_eq!(hex::encode(result), expected_script_hash_hex);
156
157 let n3_address = "Invalid_Address";
159 let result = n3_address.to_string().address_to_script_hash();
160 assert!(result.is_err());
161 }
162}
163
164pub fn from_script_hash(script_hash: &H160) -> Result<String, NeoError> {
165 Err(NeoError::UnsupportedOperation(
166 "Address conversion from script hash requires comprehensive cryptographic implementation"
167 .to_string(),
168 ))
169}