neo3/neo_types/
script_hash.rs1use byte_slice_cast::AsByteSlice;
2use primitive_types::H160;
3use serde::{Deserialize, Serialize};
4use std::{fmt, str::FromStr};
5
6use crate::{
7 config::DEFAULT_ADDRESS_VERSION,
8 crypto::{base58check_decode, base58check_encode, HashableForVec, Secp256r1PublicKey},
9 neo_crypto::utils::ToHexString,
10 neo_types::{address::Address, TypeError},
11};
12
13pub type ScriptHash = H160;
14
15pub trait ScriptHashExtension
17where
18 Self: Sized,
19{
20 fn to_bs58_string(&self) -> String;
22
23 fn zero() -> Self;
26
27 fn from_slice(slice: &[u8]) -> Result<Self, TypeError>;
33
34 fn from_hex(hex: &str) -> Result<Self, hex::FromHexError>;
40
41 fn from_address(address: &str) -> Result<Self, TypeError>;
47
48 fn to_address(&self) -> String;
50
51 fn to_hex(&self) -> String;
53
54 fn to_hex_big_endian(&self) -> String;
56
57 fn to_vec(&self) -> Vec<u8>;
59
60 fn to_le_vec(&self) -> Vec<u8>;
62
63 fn from_script(script: &[u8]) -> Self;
65
66 fn from_public_key(public_key: &[u8]) -> Result<Self, TypeError>;
67}
68
69impl ScriptHashExtension for H160 {
70 fn to_bs58_string(&self) -> String {
71 bs58::encode(self.0).into_string()
72 }
73
74 fn zero() -> Self {
75 let arr = [0u8; 20];
76 Self(arr)
77 }
78
79 fn from_slice(slice: &[u8]) -> Result<Self, TypeError> {
80 if slice.len() != 20 {
81 return Err(TypeError::InvalidAddress);
82 }
83
84 let mut arr = [0u8; 20];
85 arr.copy_from_slice(slice);
86 Ok(Self(arr))
87 }
88
89 fn from_hex(hex: &str) -> Result<Self, hex::FromHexError> {
91 if hex.starts_with("0x") {
92 let mut bytes = hex::decode(&hex[2..])?;
93 bytes.reverse();
94 <Self as ScriptHashExtension>::from_slice(&bytes)
95 .map_err(|_| hex::FromHexError::InvalidHexCharacter { c: '0', index: 0 })
96 } else {
97 let bytes = hex::decode(hex)?;
98 <Self as ScriptHashExtension>::from_slice(&bytes)
99 .map_err(|_| hex::FromHexError::InvalidHexCharacter { c: '0', index: 0 })
100 }
101 }
102
103 fn from_address(address: &str) -> Result<Self, TypeError> {
104 let bytes = match bs58::decode(address).into_vec() {
105 Ok(bytes) => bytes,
106 Err(_) => return Err(TypeError::InvalidAddress),
107 };
108
109 let _salt = bytes[0];
110 let hash = &bytes[1..21];
111 let checksum = &bytes[21..25];
112 let sha = &bytes[..21].hash256().hash256();
113 let check = &sha[..4];
114 if checksum != check {
115 return Err(TypeError::InvalidAddress);
116 }
117
118 let mut rev = [0u8; 20];
119 rev.clone_from_slice(hash);
120 rev.reverse();
121 <Self as ScriptHashExtension>::from_slice(&rev)
122 }
123
124 fn to_address(&self) -> String {
125 let mut data = vec![DEFAULT_ADDRESS_VERSION];
126 let mut reversed_bytes = self.as_bytes().to_vec();
127 reversed_bytes.reverse();
128 data.extend_from_slice(&reversed_bytes);
130 let sha = &data.hash256().hash256();
131 data.extend_from_slice(&sha[..4]);
132 bs58::encode(data).into_string()
133 }
134
135 fn to_hex(&self) -> String {
136 hex::encode(self.0)
137 }
138
139 fn to_hex_big_endian(&self) -> String {
140 let mut cloned = self.0.clone();
141 cloned.reverse();
142 "0x".to_string() + &hex::encode(cloned)
143 }
144
145 fn to_vec(&self) -> Vec<u8> {
146 self.0.to_vec()
147 }
148
149 fn to_le_vec(&self) -> Vec<u8> {
150 let vec = self.0.to_vec();
151 vec
152 }
153
154 fn from_script(script: &[u8]) -> Self {
155 let mut hash: [u8; 20] = script
156 .sha256_ripemd160()
157 .as_byte_slice()
158 .try_into()
159 .expect("script does not have exactly 20 elements");
160 hash.reverse();
161 Self(hash)
162 }
163
164 fn from_public_key(public_key: &[u8]) -> Result<Self, TypeError> {
165 let mut script = Vec::new();
168
169 script.push(0x0c);
171
172 script.push(public_key.len() as u8);
174
175 script.extend_from_slice(public_key);
177
178 script.push(0x41);
180
181 let interop_hash = "System.Crypto.CheckSig".as_bytes().hash256();
183 script.extend_from_slice(&interop_hash[..4]);
184
185 Ok(Self::from_script(&script))
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use std::str::FromStr;
193
194 use crate::{
195 neo_builder::InteropService,
196 neo_codec::{Encoder, NeoSerializable},
197 neo_config::TestConstants,
198 neo_types::op_code::OpCode,
199 };
200
201 use super::*;
202
203 #[test]
204 fn test_from_valid_hash() {
205 assert_eq!(
206 H160::from_hex("23ba2703c53263e8d6e522dc32203339dcd8eee9")
207 .unwrap()
208 .as_bytes()
209 .to_hex_string(),
210 "23ba2703c53263e8d6e522dc32203339dcd8eee9".to_string()
211 );
212
213 assert_eq!(
214 H160::from_hex("0x23ba2703c53263e8d6e522dc32203339dcd8eee9")
215 .unwrap()
216 .as_bytes()
217 .to_hex_string(),
218 "e9eed8dc39332032dc22e5d6e86332c50327ba23".to_string()
219 );
220 }
221
222 #[test]
223 fn test_creation_failures() {
224 assert!(H160::from_hex("23ba2703c53263e8d6e522dc32203339dcd8eee").is_err());
226 assert!(H160::from_hex("g3ba2703c53263e8d6e522dc32203339dcd8eee9").is_err());
228 assert!(H160::from_hex("23ba2703c53263e8d6e522dc32203339dcd8ee").is_err());
230 assert!(H160::from_hex("c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b")
232 .is_err());
233 }
234
235 #[test]
236 fn test_to_array() {
237 let hash = H160::from_str("23ba2703c53263e8d6e522dc32203339dcd8eee9").unwrap();
238 assert_eq!(hash.to_vec(), hex::decode("23ba2703c53263e8d6e522dc32203339dcd8eee9").unwrap());
239 }
240
241 #[test]
242 fn test_serialize_and_deserialize() {
243 let hex_str = "23ba2703c53263e8d6e522dc32203339dcd8eee9";
244 let data = hex::decode(hex_str).unwrap();
245
246 let mut buffer = Encoder::new();
247 H160::from_hex(hex_str).unwrap().encode(&mut buffer);
248
249 assert_eq!(buffer.to_bytes(), data);
250 assert_eq!(
251 <H160 as ScriptHashExtension>::from_slice(&data)
252 .unwrap()
253 .as_bytes()
254 .to_hex_string(),
255 hex_str
256 );
257 }
258
259 #[test]
260 fn test_equals() {
261 let hash1 = H160::from_script(&hex::decode("01a402d8").unwrap());
262 let hash2 = H160::from_script(&hex::decode("d802a401").unwrap());
263 assert_ne!(hash1, hash2);
264 assert_eq!(hash1, hash1);
265 }
266
267 #[test]
268 fn test_from_address() {
269 let hash = H160::from_address("NeE8xcV4ohHi9rjyj4nPdCYTGyXnWZ79UU").unwrap();
270 let mut expected = hex::decode(
271 "2102208aea0068c429a03316e37be0e3e8e21e6cda5442df4c5914a19b3a9b6de37568747476aa",
272 )
273 .unwrap()
274 .sha256_ripemd160();
275 expected.reverse();
276 assert_eq!(hash.to_le_vec(), expected);
277 }
278
279 #[test]
280 fn test_from_invalid_address() {
282 assert_eq!(
284 H160::from_address("NLnyLtep7jwyq1qhNPkwXbJpurC4jUT8keas"),
285 Err(TypeError::InvalidAddress)
286 );
287 }
288
289 #[test]
290 fn test_from_public_key_bytes() {
291 let public_key = "035fdb1d1f06759547020891ae97c729327853aeb1256b6fe0473bc2e9fa42ff50";
292 let script = format!(
293 "{}21{}{}{}",
294 OpCode::PushData1.to_hex_string(),
295 public_key,
296 OpCode::Syscall.to_hex_string(),
297 InteropService::SystemCryptoCheckSig.hash()
298 );
299
300 let hash = H160::from_public_key(&hex::decode(public_key).unwrap()).unwrap();
301 let mut hash = hash.to_array();
302 let mut expected = hex::decode(&script).unwrap().sha256_ripemd160();
303 expected.reverse();
304 assert_eq!(hash, expected);
305 }
306
307 #[test]
308 fn test_to_address() {
309 let mut script_hash = hex::decode(
310 "0c2102249425a06b5a1f8e6133fc79afa2c2b8430bf9327297f176761df79e8d8929c50b4195440d78",
311 )
312 .unwrap()
313 .sha256_ripemd160();
314 script_hash.reverse();
315 let hash = H160::from_hex(&hex::encode(script_hash)).unwrap();
316 let address = hash.to_address();
317 assert_eq!(address, "NLnyLtep7jwyq1qhNPkwXbJpurC4jUT8ke".to_string());
318 }
319}