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