1use crate::prelude::*;
3use crate::{AddressError, VmError};
4use casper_types::{
5 account::AccountHash,
6 bytesrepr::{self, FromBytes, ToBytes},
7 CLType, CLTyped, ContractPackageHash, Key, PublicKey
8};
9use serde::{Deserialize, Serialize};
10
11const ADDRESS_HASH_LENGTH: usize = 64;
13const CONTRACT_STR_LENGTH: usize = 69;
15const LEGACY_CONTRACT_STR_LENGTH: usize = 85;
17const ACCOUNT_STR_LENGTH: usize = 77;
19
20#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy, Debug)]
22pub enum Address {
23 Account(AccountHash),
25 Contract(ContractPackageHash)
27}
28
29pub trait Addressable {
31 fn address(&self) -> &Address;
33}
34
35impl Addressable for Address {
36 fn address(&self) -> &Address {
37 self
38 }
39}
40
41impl Address {
42 pub const fn new(input: &'static str) -> OdraResult<Self> {
44 let src: &[u8] = input.as_bytes();
45 let src_len: usize = src.len();
46
47 if let Ok(dst) = decode_base16(src) {
48 match src_len {
50 LEGACY_CONTRACT_STR_LENGTH => Ok(Self::Contract(ContractPackageHash::new(dst))),
51 ACCOUNT_STR_LENGTH => Ok(Self::Account(AccountHash::new(dst))),
52 CONTRACT_STR_LENGTH => Ok(Self::Contract(ContractPackageHash::new(dst))),
53 _ => Err(OdraError::ExecutionError(
54 ExecutionError::AddressCreationFailed
55 ))
56 }
57 } else {
58 Err(OdraError::ExecutionError(
59 ExecutionError::AddressCreationFailed
60 ))
61 }
62 }
63
64 pub fn as_account_hash(&self) -> Option<&AccountHash> {
66 if let Self::Account(v) = self {
67 Some(v)
68 } else {
69 None
70 }
71 }
72
73 pub fn as_contract_package_hash(&self) -> Option<&ContractPackageHash> {
75 if let Self::Contract(v) = self {
76 Some(v)
77 } else {
78 None
79 }
80 }
81
82 pub fn is_contract(&self) -> bool {
84 self.as_contract_package_hash().is_some()
85 }
86}
87
88impl TryFrom<ContractPackageHash> for Address {
89 type Error = AddressError;
90 fn try_from(contract_package_hash: ContractPackageHash) -> Result<Self, Self::Error> {
91 if contract_package_hash.value().iter().all(|&b| b == 0) {
92 return Err(AddressError::ZeroAddress);
93 }
94 Ok(Self::Contract(contract_package_hash))
95 }
96}
97
98impl TryFrom<AccountHash> for Address {
99 type Error = AddressError;
100 fn try_from(account_hash: AccountHash) -> Result<Self, Self::Error> {
101 if account_hash.value().iter().all(|&b| b == 0) {
102 return Err(AddressError::ZeroAddress);
103 }
104 Ok(Self::Account(account_hash))
105 }
106}
107
108impl From<Address> for Key {
109 fn from(address: Address) -> Self {
110 match address {
111 Address::Account(account_hash) => Key::Account(account_hash),
112 Address::Contract(contract_package_hash) => Key::Hash(contract_package_hash.value())
113 }
114 }
115}
116
117impl TryFrom<Key> for Address {
118 type Error = AddressError;
119
120 fn try_from(key: Key) -> Result<Self, Self::Error> {
121 match key {
122 Key::Account(account_hash) => Self::try_from(account_hash),
123 Key::Hash(contract_package_hash) => {
124 Self::try_from(ContractPackageHash::new(contract_package_hash))
125 }
126 _ => Err(AddressError::AddressCreationError)
127 }
128 }
129}
130
131impl From<PublicKey> for Address {
132 fn from(public_key: PublicKey) -> Self {
133 Self::Account(public_key.to_account_hash())
134 }
135}
136
137impl CLTyped for Address {
138 fn cl_type() -> CLType {
139 CLType::Key
140 }
141}
142
143impl ToBytes for Address {
144 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
145 Key::from(*self).to_bytes()
146 }
147
148 fn serialized_length(&self) -> usize {
149 Key::from(*self).serialized_length()
150 }
151}
152
153impl FromBytes for Address {
154 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
155 let (key, remainder) = Key::from_bytes(bytes)?;
156
157 let address = match key {
158 Key::Account(account_hash) => Address::Account(account_hash),
159 Key::Hash(raw_contract_package_hash) => {
160 Address::Contract(ContractPackageHash::new(raw_contract_package_hash))
161 }
162 _ => return Err(bytesrepr::Error::Formatting)
163 };
164
165 Ok((address, remainder))
166 }
167}
168
169impl TryFrom<&[u8; 33]> for Address {
170 type Error = AddressError;
171 fn try_from(value: &[u8; 33]) -> Result<Self, Self::Error> {
172 let address = Address::from_bytes(value)
173 .map(|(address, _)| address)
174 .map_err(|_| AddressError::AddressCreationError)?;
175 if address
176 .to_bytes()
177 .map_err(|_| AddressError::AddressCreationError)?
178 .iter()
179 .all(|&x| x == 0)
180 {
181 Err(AddressError::ZeroAddress)
182 } else {
183 Ok(address)
184 }
185 }
186}
187
188impl FromStr for Address {
189 type Err = OdraError;
190
191 fn from_str(s: &str) -> Result<Self, Self::Err> {
192 match Key::from_formatted_str(s) {
193 Err(_) => Err(OdraError::VmError(VmError::Deserialization)),
194 Ok(key) => match key {
195 Key::Account(_) | Key::Hash(_) => match key.try_into() {
196 Ok(address) => Ok(address),
197 Err(_) => Err(OdraError::VmError(VmError::Deserialization))
198 },
199 _ => Err(OdraError::VmError(VmError::Deserialization))
200 }
201 }
202 }
203}
204
205#[allow(clippy::to_string_trait_impl)]
206impl ToString for Address {
207 fn to_string(&self) -> String {
208 Key::from(*self).to_formatted_string()
209 }
210}
211
212impl Serialize for Address {
213 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
214 let s = self.to_string();
215 serializer.serialize_str(&s)
216 }
217}
218
219impl<'de> Deserialize<'de> for Address {
220 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
221 let s = String::deserialize(deserializer)?;
222 Address::from_str(&s).map_err(|_| serde::de::Error::custom("Address deserialization error"))
223 }
224}
225
226const fn decode_base16(input: &[u8]) -> Result<[u8; 32], &'static str> {
227 let input_len = input.len();
229 if input_len < ADDRESS_HASH_LENGTH {
230 return Err("Input too short");
231 }
232 let mut output = [0u8; 32];
234 let mut i = 0;
235 let mut j = 0;
236 while i < 64 {
239 let high_value = match hex_char_to_value(input[input_len - ADDRESS_HASH_LENGTH + i]) {
240 Ok(v) => v,
241 Err(e) => return Err(e)
242 };
243
244 let low_value = match hex_char_to_value(input[input_len - ADDRESS_HASH_LENGTH + i + 1]) {
245 Ok(v) => v,
246 Err(e) => return Err(e)
247 };
248
249 output[j] = (high_value << 4) | low_value;
250 i += 2;
251 j += 1;
252 }
253
254 Ok(output)
255}
256
257const fn hex_char_to_value(c: u8) -> Result<u8, &'static str> {
258 match c {
259 b'0'..=b'9' => Ok(c - b'0'),
260 b'a'..=b'f' => Ok(c - b'a' + 10),
261 b'A'..=b'F' => Ok(c - b'A' + 10),
262 _ => Err("Invalid character in input")
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use casper_types::EraId;
269
270 use super::*;
271
272 const CONTRACT_PACKAGE_HASH: &str =
274 "contract-package-wasm7ba9daac84bebee8111c186588f21ebca35550b6cf1244e71768bd871938be6a";
275 const ACCOUNT_HASH: &str =
276 "account-hash-3b4ffcfb21411ced5fc1560c3f6ffed86f4885e5ea05cde49d90962a48a14d95";
277 const CONTRACT_HASH: &str =
278 "hash-7ba9daac84bebee8111c186588f21ebca35550b6cf1244e71768bd871938be6a";
279
280 fn mock_account_hash() -> AccountHash {
281 AccountHash::from_formatted_str(ACCOUNT_HASH).unwrap()
282 }
283
284 fn mock_contract_package_hash() -> ContractPackageHash {
285 ContractPackageHash::from_formatted_str(CONTRACT_PACKAGE_HASH).unwrap()
286 }
287
288 #[test]
289 fn test_casper_address_new() {
290 let address = Address::new(CONTRACT_PACKAGE_HASH).unwrap();
291 assert!(address.is_contract());
292 assert_eq!(
293 address.as_contract_package_hash().unwrap(),
294 &mock_contract_package_hash()
295 );
296
297 let address = Address::new(ACCOUNT_HASH).unwrap();
298 assert!(!address.is_contract());
299 assert_eq!(address.as_account_hash().unwrap(), &mock_account_hash());
300
301 let address = Address::new(CONTRACT_HASH).unwrap();
302 assert!(address.is_contract());
303 }
304
305 #[test]
306 fn contract_package_hash_from_str() {
307 let valid_prefix =
308 "account-hash-0000000000000000000000000000000000000000000000000000000000000000";
309 assert!(Address::new(valid_prefix).is_ok());
310
311 let invalid_prefix =
312 "account-hash0000000000000000000000000000000000000000000000000000000000000000";
313 assert!(Address::new(invalid_prefix).is_err());
314
315 let short_addr =
316 "account-hash-00000000000000000000000000000000000000000000000000000000000000";
317 assert!(Address::new(short_addr).is_err());
318
319 let long_addr =
320 "account-hash-000000000000000000000000000000000000000000000000000000000000000000";
321 assert!(Address::new(long_addr).is_err());
322
323 let invalid_hex =
324 "account-hash-000000000000000000000000000000000000000000000000000000000000000g";
325 assert!(Address::new(invalid_hex).is_err());
326 }
327
328 #[test]
329 fn test_casper_address_account_hash_conversion() {
330 let account_hash = mock_account_hash();
331
332 let casper_address = Address::try_from(account_hash).unwrap();
334 assert_eq!(casper_address.as_account_hash().unwrap(), &account_hash);
335
336 assert!(casper_address.as_contract_package_hash().is_none());
338
339 assert!(!casper_address.is_contract());
341
342 test_casper_address_conversions(casper_address);
343 }
344
345 #[test]
346 fn test_casper_address_contract_package_hash_conversion() {
347 let contract_package_hash = mock_contract_package_hash();
348 let casper_address = Address::try_from(contract_package_hash).unwrap();
349
350 assert_eq!(
352 casper_address.as_contract_package_hash().unwrap(),
353 &contract_package_hash
354 );
355
356 assert!(casper_address.as_account_hash().is_none());
358
359 assert!(casper_address.is_contract());
361
362 test_casper_address_conversions(casper_address);
363 }
364
365 fn test_casper_address_conversions(casper_address: Address) {
366 let key = Key::from(casper_address);
368 let restored = Address::try_from(key);
369 assert_eq!(restored.unwrap(), casper_address);
370
371 let bytes = casper_address.to_bytes().unwrap();
373 let (restored, rest) = Address::from_bytes(&bytes).unwrap();
374 assert!(rest.is_empty());
375 assert_eq!(restored, casper_address);
376 }
377
378 #[test]
379 fn test_casper_address_from_to_string() {
380 let address = Address::from_str(CONTRACT_HASH).unwrap();
381 assert!(address.is_contract());
382 assert_eq!(&address.to_string(), CONTRACT_HASH);
383
384 let address = Address::from_str(ACCOUNT_HASH).unwrap();
385 assert!(!address.is_contract());
386 assert_eq!(&address.to_string(), ACCOUNT_HASH);
387
388 assert_eq!(
389 Address::from_str(CONTRACT_PACKAGE_HASH).unwrap_err(),
390 OdraError::VmError(VmError::Deserialization)
391 )
392 }
393
394 #[test]
395 fn test_from_key_fails() {
396 let key = Key::EraInfo(EraId::from(42));
397 assert_eq!(
398 Address::try_from(key).unwrap_err(),
399 AddressError::AddressCreationError
400 );
401 }
402
403 #[test]
404 fn test_address_serde_roundtrip() {
405 let address_json = format!("\"{}\"", ACCOUNT_HASH);
407 let address = Address::from_str(ACCOUNT_HASH).unwrap();
408 let serialized = serde_json::to_string(&address).unwrap();
409 assert_eq!(serialized, address_json);
410
411 let deserialized: Address = serde_json::from_str(&address_json).unwrap();
413 assert_eq!(deserialized, address);
414
415 let serialized = serde_json::to_string(&address).unwrap();
417 let deserialized: Address = serde_json::from_str(&serialized).unwrap();
418 assert_eq!(deserialized, address);
419
420 let address_json = format!("\"{}\"", CONTRACT_HASH);
422 let address = Address::from_str(CONTRACT_HASH).unwrap();
423 let serialized = serde_json::to_string(&address).unwrap();
424 assert_eq!(serialized, address_json);
425
426 let deserialized: Address = serde_json::from_str(&address_json).unwrap();
428 assert_eq!(deserialized, address);
429
430 let serialized = serde_json::to_string(&address).unwrap();
432 let deserialized: Address = serde_json::from_str(&serialized).unwrap();
433 assert_eq!(deserialized, address);
434 }
435}