1use core::str::FromStr;
4
5use crate::AddressError;
6use crate::AddressError::ZeroAddress;
7use crate::{OdraError, VmError};
8use alloc::{
9 string::{String, ToString},
10 vec::Vec
11};
12use casper_types::{
13 account::AccountHash,
14 bytesrepr::{self, FromBytes, ToBytes},
15 CLType, CLTyped, ContractPackageHash, Key, PublicKey
16};
17
18#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy, Debug)]
20pub enum Address {
21 Account(AccountHash),
23 Contract(ContractPackageHash)
25}
26
27impl Address {
28 pub fn as_account_hash(&self) -> Option<&AccountHash> {
30 if let Self::Account(v) = self {
31 Some(v)
32 } else {
33 None
34 }
35 }
36
37 pub fn as_contract_package_hash(&self) -> Option<&ContractPackageHash> {
39 if let Self::Contract(v) = self {
40 Some(v)
41 } else {
42 None
43 }
44 }
45
46 #[cfg(feature = "test-support")]
47 pub fn account_from_str(str: &str) -> Self {
48 use alloc::format;
49 use casper_types::account::{ACCOUNT_HASH_FORMATTED_STRING_PREFIX, ACCOUNT_HASH_LENGTH};
50 let desired_length = ACCOUNT_HASH_LENGTH * 2;
51 let padding_length = desired_length - str.len();
52 let padding = "0".repeat(padding_length);
53
54 let account_str = format!("{}{}{}", ACCOUNT_HASH_FORMATTED_STRING_PREFIX, str, padding);
55 Self::Account(AccountHash::from_formatted_str(account_str.as_str()).unwrap())
56 }
57
58 #[cfg(feature = "test-support")]
59 pub fn contract_from_u32(i: u32) -> Self {
60 use alloc::format;
61 use casper_types::KEY_HASH_LENGTH;
62 let desired_length = KEY_HASH_LENGTH * 2;
63 let padding_length = desired_length - i.to_string().len();
64 let padding = "0".repeat(padding_length);
65
66 let a = i.to_string();
67 let account_str = format!("{}{}{}", "contract-package-", a, padding);
68 Self::Contract(ContractPackageHash::from_formatted_str(account_str.as_str()).unwrap())
69 }
70}
71
72impl OdraAddress for Address {
73 fn is_contract(&self) -> bool {
74 self.as_contract_package_hash().is_some()
75 }
76}
77
78impl TryFrom<ContractPackageHash> for Address {
79 type Error = AddressError;
80 fn try_from(contract_package_hash: ContractPackageHash) -> Result<Self, Self::Error> {
81 if contract_package_hash.value().iter().all(|&b| b == 0) {
82 return Err(ZeroAddress);
83 }
84 Ok(Self::Contract(contract_package_hash))
85 }
86}
87
88impl TryFrom<AccountHash> for Address {
89 type Error = AddressError;
90 fn try_from(account_hash: AccountHash) -> Result<Self, Self::Error> {
91 if account_hash.value().iter().all(|&b| b == 0) {
92 return Err(ZeroAddress);
93 }
94 Ok(Self::Account(account_hash))
95 }
96}
97
98impl From<Address> for Key {
99 fn from(address: Address) -> Self {
100 match address {
101 Address::Account(account_hash) => Key::Account(account_hash),
102 Address::Contract(contract_package_hash) => Key::Hash(contract_package_hash.value())
103 }
104 }
105}
106
107impl TryFrom<Key> for Address {
108 type Error = AddressError;
109
110 fn try_from(key: Key) -> Result<Self, Self::Error> {
111 match key {
112 Key::Account(account_hash) => Self::try_from(account_hash),
113 Key::Hash(contract_package_hash) => {
114 Self::try_from(ContractPackageHash::new(contract_package_hash))
115 }
116 _ => Err(AddressError::AddressCreationError)
117 }
118 }
119}
120
121impl From<PublicKey> for Address {
122 fn from(public_key: PublicKey) -> Self {
123 Self::Account(public_key.to_account_hash())
124 }
125}
126
127impl CLTyped for Address {
128 fn cl_type() -> CLType {
129 CLType::Key
130 }
131}
132
133impl ToBytes for Address {
134 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
135 Key::from(*self).to_bytes()
136 }
137
138 fn serialized_length(&self) -> usize {
139 Key::from(*self).serialized_length()
140 }
141}
142
143impl FromBytes for Address {
144 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
145 let (key, remainder) = Key::from_bytes(bytes)?;
146
147 let address = match key {
148 Key::Account(account_hash) => Address::Account(account_hash),
149 Key::Hash(raw_contract_package_hash) => {
150 Address::Contract(ContractPackageHash::new(raw_contract_package_hash))
151 }
152 _ => return Err(bytesrepr::Error::Formatting)
153 };
154
155 Ok((address, remainder))
156 }
157}
158
159impl TryFrom<&[u8; 33]> for Address {
160 type Error = AddressError;
161 fn try_from(value: &[u8; 33]) -> Result<Self, Self::Error> {
162 let address = Address::from_bytes(value)
163 .map(|(address, _)| address)
164 .map_err(|_| AddressError::AddressCreationError)?;
165 if address
166 .to_bytes()
167 .map_err(|_| AddressError::AddressCreationError)?
168 .iter()
169 .all(|&x| x == 0)
170 {
171 Err(ZeroAddress)
172 } else {
173 Ok(address)
174 }
175 }
176}
177
178impl FromStr for Address {
179 type Err = OdraError;
180
181 fn from_str(s: &str) -> Result<Self, Self::Err> {
182 match Key::from_formatted_str(s) {
183 Err(_) => Err(OdraError::VmError(VmError::Deserialization)),
184 Ok(key) => match key {
185 Key::Account(_) | Key::Hash(_) => match key.try_into() {
186 Ok(address) => Ok(address),
187 Err(_) => Err(OdraError::VmError(VmError::Deserialization))
188 },
189 _ => Err(OdraError::VmError(VmError::Deserialization))
190 }
191 }
192 }
193}
194
195impl ToString for Address {
196 fn to_string(&self) -> String {
197 Key::from(*self).to_formatted_string()
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 const CONTRACT_PACKAGE_HASH: &str =
207 "contract-package-wasm7ba9daac84bebee8111c186588f21ebca35550b6cf1244e71768bd871938be6a";
208 const ACCOUNT_HASH: &str =
209 "account-hash-3b4ffcfb21411ced5fc1560c3f6ffed86f4885e5ea05cde49d90962a48a14d95";
210 const CONTRACT_HASH: &str =
211 "hash-7ba9daac84bebee8111c186588f21ebca35550b6cf1244e71768bd871938be6a";
212
213 fn mock_account_hash() -> AccountHash {
214 AccountHash::from_formatted_str(ACCOUNT_HASH).unwrap()
215 }
216
217 fn mock_contract_package_hash() -> ContractPackageHash {
218 ContractPackageHash::from_formatted_str(CONTRACT_PACKAGE_HASH).unwrap()
219 }
220
221 #[test]
222 fn test_casper_address_account_hash_conversion() {
223 let account_hash = mock_account_hash();
224
225 let casper_address = Address::try_from(account_hash).unwrap();
227 assert_eq!(casper_address.as_account_hash().unwrap(), &account_hash);
228
229 assert!(casper_address.as_contract_package_hash().is_none());
231
232 assert!(!casper_address.is_contract());
234
235 test_casper_address_conversions(casper_address);
236 }
237
238 #[test]
239 fn test_casper_address_contract_package_hash_conversion() {
240 let contract_package_hash = mock_contract_package_hash();
241 let casper_address = Address::try_from(contract_package_hash).unwrap();
242
243 assert_eq!(
245 casper_address.as_contract_package_hash().unwrap(),
246 &contract_package_hash
247 );
248
249 assert!(casper_address.as_account_hash().is_none());
251
252 assert!(casper_address.is_contract());
254
255 test_casper_address_conversions(casper_address);
256 }
257
258 fn test_casper_address_conversions(casper_address: Address) {
259 let key = Key::from(casper_address);
261 let restored = Address::try_from(key);
262 assert_eq!(restored.unwrap(), casper_address);
263
264 let bytes = casper_address.to_bytes().unwrap();
266 let (restored, rest) = Address::from_bytes(&bytes).unwrap();
267 assert!(rest.is_empty());
268 assert_eq!(restored, casper_address);
269 }
270
271 #[test]
272 fn test_casper_address_from_to_string() {
273 let address = Address::from_str(CONTRACT_HASH).unwrap();
274 assert!(address.is_contract());
275 assert_eq!(&address.to_string(), CONTRACT_HASH);
276
277 let address = Address::from_str(ACCOUNT_HASH).unwrap();
278 assert!(!address.is_contract());
279 assert_eq!(&address.to_string(), ACCOUNT_HASH);
280
281 assert_eq!(
282 Address::from_str(CONTRACT_PACKAGE_HASH).unwrap_err(),
283 OdraError::VmError(VmError::Deserialization)
284 )
285 }
286}
287
288pub trait OdraAddress {
289 fn is_contract(&self) -> bool;
291}