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