1mod type_mismatch;
2
3use alloc::{
4 boxed::Box,
5 string::{String, ToString},
6 vec::Vec,
7};
8use core::{convert::TryFrom, fmt::Debug};
9
10#[cfg(feature = "datasize")]
11use datasize::DataSize;
12use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer};
13use serde_bytes::ByteBuf;
14
15use crate::{
16 account::Account,
17 bytesrepr::{self, FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
18 contracts::ContractPackage,
19 system::auction::{Bid, EraInfo, UnbondingPurse, WithdrawPurse},
20 CLValue, Contract, ContractWasm, DeployInfo, Transfer,
21};
22pub use type_mismatch::TypeMismatch;
23
24#[allow(clippy::large_enum_variant)]
25#[repr(u8)]
26enum Tag {
27 CLValue = 0,
28 Account = 1,
29 ContractWasm = 2,
30 Contract = 3,
31 ContractPackage = 4,
32 Transfer = 5,
33 DeployInfo = 6,
34 EraInfo = 7,
35 Bid = 8,
36 Withdraw = 9,
37 Unbonding = 10,
38}
39
40#[allow(clippy::large_enum_variant)]
41#[derive(Eq, PartialEq, Clone, Debug)]
42#[cfg_attr(feature = "datasize", derive(DataSize))]
43pub enum StoredValue {
45 CLValue(CLValue),
47 Account(Account),
49 ContractWasm(ContractWasm),
51 Contract(Contract),
53 ContractPackage(ContractPackage),
55 Transfer(Transfer),
57 DeployInfo(DeployInfo),
59 EraInfo(EraInfo),
61 Bid(Box<Bid>),
63 Withdraw(Vec<WithdrawPurse>),
65 Unbonding(Vec<UnbondingPurse>),
67}
68
69impl StoredValue {
70 pub fn as_cl_value(&self) -> Option<&CLValue> {
72 match self {
73 StoredValue::CLValue(cl_value) => Some(cl_value),
74 _ => None,
75 }
76 }
77
78 pub fn as_account(&self) -> Option<&Account> {
80 match self {
81 StoredValue::Account(account) => Some(account),
82 _ => None,
83 }
84 }
85
86 pub fn as_contract(&self) -> Option<&Contract> {
88 match self {
89 StoredValue::Contract(contract) => Some(contract),
90 _ => None,
91 }
92 }
93
94 pub fn as_contract_wasm(&self) -> Option<&ContractWasm> {
96 match self {
97 StoredValue::ContractWasm(contract_wasm) => Some(contract_wasm),
98 _ => None,
99 }
100 }
101
102 pub fn as_contract_package(&self) -> Option<&ContractPackage> {
104 match self {
105 StoredValue::ContractPackage(contract_package) => Some(contract_package),
106 _ => None,
107 }
108 }
109
110 pub fn as_deploy_info(&self) -> Option<&DeployInfo> {
112 match self {
113 StoredValue::DeployInfo(deploy_info) => Some(deploy_info),
114 _ => None,
115 }
116 }
117
118 pub fn as_era_info(&self) -> Option<&EraInfo> {
120 match self {
121 StoredValue::EraInfo(era_info) => Some(era_info),
122 _ => None,
123 }
124 }
125
126 pub fn as_bid(&self) -> Option<&Bid> {
128 match self {
129 StoredValue::Bid(bid) => Some(bid),
130 _ => None,
131 }
132 }
133
134 pub fn as_withdraw(&self) -> Option<&Vec<WithdrawPurse>> {
136 match self {
137 StoredValue::Withdraw(withdraw_purses) => Some(withdraw_purses),
138 _ => None,
139 }
140 }
141
142 pub fn as_unbonding(&self) -> Option<&Vec<UnbondingPurse>> {
144 match self {
145 StoredValue::Unbonding(unbonding_purses) => Some(unbonding_purses),
146 _ => None,
147 }
148 }
149
150 pub fn is_unit_cl_value(&self) -> bool {
152 self == &StoredValue::CLValue(CLValue::unit())
153 }
154
155 pub fn type_name(&self) -> String {
159 match self {
160 StoredValue::CLValue(cl_value) => format!("{:?}", cl_value.cl_type()),
161 StoredValue::Account(_) => "Account".to_string(),
162 StoredValue::ContractWasm(_) => "ContractWasm".to_string(),
163 StoredValue::Contract(_) => "Contract".to_string(),
164 StoredValue::ContractPackage(_) => "ContractPackage".to_string(),
165 StoredValue::Transfer(_) => "Transfer".to_string(),
166 StoredValue::DeployInfo(_) => "DeployInfo".to_string(),
167 StoredValue::EraInfo(_) => "EraInfo".to_string(),
168 StoredValue::Bid(_) => "Bid".to_string(),
169 StoredValue::Withdraw(_) => "Withdraw".to_string(),
170 StoredValue::Unbonding(_) => "Unbonding".to_string(),
171 }
172 }
173
174 fn tag(&self) -> Tag {
175 match self {
176 StoredValue::CLValue(_) => Tag::CLValue,
177 StoredValue::Account(_) => Tag::Account,
178 StoredValue::ContractWasm(_) => Tag::ContractWasm,
179 StoredValue::Contract(_) => Tag::Contract,
180 StoredValue::ContractPackage(_) => Tag::ContractPackage,
181 StoredValue::Transfer(_) => Tag::Transfer,
182 StoredValue::DeployInfo(_) => Tag::DeployInfo,
183 StoredValue::EraInfo(_) => Tag::EraInfo,
184 StoredValue::Bid(_) => Tag::Bid,
185 StoredValue::Withdraw(_) => Tag::Withdraw,
186 StoredValue::Unbonding(_) => Tag::Unbonding,
187 }
188 }
189}
190
191impl From<CLValue> for StoredValue {
192 fn from(value: CLValue) -> StoredValue {
193 StoredValue::CLValue(value)
194 }
195}
196impl From<Account> for StoredValue {
197 fn from(value: Account) -> StoredValue {
198 StoredValue::Account(value)
199 }
200}
201impl From<ContractWasm> for StoredValue {
202 fn from(value: ContractWasm) -> StoredValue {
203 StoredValue::ContractWasm(value)
204 }
205}
206impl From<Contract> for StoredValue {
207 fn from(value: Contract) -> StoredValue {
208 StoredValue::Contract(value)
209 }
210}
211impl From<ContractPackage> for StoredValue {
212 fn from(value: ContractPackage) -> StoredValue {
213 StoredValue::ContractPackage(value)
214 }
215}
216impl From<Bid> for StoredValue {
217 fn from(bid: Bid) -> StoredValue {
218 StoredValue::Bid(Box::new(bid))
219 }
220}
221
222impl TryFrom<StoredValue> for CLValue {
223 type Error = TypeMismatch;
224
225 fn try_from(stored_value: StoredValue) -> Result<Self, Self::Error> {
226 let type_name = stored_value.type_name();
227 match stored_value {
228 StoredValue::CLValue(cl_value) => Ok(cl_value),
229 StoredValue::ContractPackage(contract_package) => Ok(CLValue::from_t(contract_package)
230 .map_err(|_error| TypeMismatch::new("ContractPackage".to_string(), type_name))?),
231 _ => Err(TypeMismatch::new("CLValue".to_string(), type_name)),
232 }
233 }
234}
235
236impl TryFrom<StoredValue> for Account {
237 type Error = TypeMismatch;
238
239 fn try_from(stored_value: StoredValue) -> Result<Self, Self::Error> {
240 match stored_value {
241 StoredValue::Account(account) => Ok(account),
242 _ => Err(TypeMismatch::new(
243 "Account".to_string(),
244 stored_value.type_name(),
245 )),
246 }
247 }
248}
249
250impl TryFrom<StoredValue> for ContractWasm {
251 type Error = TypeMismatch;
252
253 fn try_from(stored_value: StoredValue) -> Result<Self, Self::Error> {
254 match stored_value {
255 StoredValue::ContractWasm(contract_wasm) => Ok(contract_wasm),
256 _ => Err(TypeMismatch::new(
257 "ContractWasm".to_string(),
258 stored_value.type_name(),
259 )),
260 }
261 }
262}
263
264impl TryFrom<StoredValue> for ContractPackage {
265 type Error = TypeMismatch;
266
267 fn try_from(stored_value: StoredValue) -> Result<Self, Self::Error> {
268 match stored_value {
269 StoredValue::ContractPackage(contract_package) => Ok(contract_package),
270 _ => Err(TypeMismatch::new(
271 "ContractPackage".to_string(),
272 stored_value.type_name(),
273 )),
274 }
275 }
276}
277
278impl TryFrom<StoredValue> for Contract {
279 type Error = TypeMismatch;
280
281 fn try_from(stored_value: StoredValue) -> Result<Self, Self::Error> {
282 match stored_value {
283 StoredValue::Contract(contract) => Ok(contract),
284 _ => Err(TypeMismatch::new(
285 "Contract".to_string(),
286 stored_value.type_name(),
287 )),
288 }
289 }
290}
291
292impl TryFrom<StoredValue> for Transfer {
293 type Error = TypeMismatch;
294
295 fn try_from(value: StoredValue) -> Result<Self, Self::Error> {
296 match value {
297 StoredValue::Transfer(transfer) => Ok(transfer),
298 _ => Err(TypeMismatch::new("Transfer".to_string(), value.type_name())),
299 }
300 }
301}
302
303impl TryFrom<StoredValue> for DeployInfo {
304 type Error = TypeMismatch;
305
306 fn try_from(value: StoredValue) -> Result<Self, Self::Error> {
307 match value {
308 StoredValue::DeployInfo(deploy_info) => Ok(deploy_info),
309 _ => Err(TypeMismatch::new(
310 "DeployInfo".to_string(),
311 value.type_name(),
312 )),
313 }
314 }
315}
316
317impl TryFrom<StoredValue> for EraInfo {
318 type Error = TypeMismatch;
319
320 fn try_from(value: StoredValue) -> Result<Self, Self::Error> {
321 match value {
322 StoredValue::EraInfo(era_info) => Ok(era_info),
323 _ => Err(TypeMismatch::new("EraInfo".to_string(), value.type_name())),
324 }
325 }
326}
327
328impl ToBytes for StoredValue {
329 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
330 let mut result = bytesrepr::allocate_buffer(self)?;
331 let (tag, mut serialized_data) = match self {
332 StoredValue::CLValue(cl_value) => (Tag::CLValue, cl_value.to_bytes()?),
333 StoredValue::Account(account) => (Tag::Account, account.to_bytes()?),
334 StoredValue::ContractWasm(contract_wasm) => {
335 (Tag::ContractWasm, contract_wasm.to_bytes()?)
336 }
337 StoredValue::Contract(contract_header) => (Tag::Contract, contract_header.to_bytes()?),
338 StoredValue::ContractPackage(contract_package) => {
339 (Tag::ContractPackage, contract_package.to_bytes()?)
340 }
341 StoredValue::Transfer(transfer) => (Tag::Transfer, transfer.to_bytes()?),
342 StoredValue::DeployInfo(deploy_info) => (Tag::DeployInfo, deploy_info.to_bytes()?),
343 StoredValue::EraInfo(era_info) => (Tag::EraInfo, era_info.to_bytes()?),
344 StoredValue::Bid(bid) => (Tag::Bid, bid.to_bytes()?),
345 StoredValue::Withdraw(withdraw_purses) => (Tag::Withdraw, withdraw_purses.to_bytes()?),
346 StoredValue::Unbonding(unbonding_purses) => {
347 (Tag::Unbonding, unbonding_purses.to_bytes()?)
348 }
349 };
350 result.push(tag as u8);
351 result.append(&mut serialized_data);
352 Ok(result)
353 }
354
355 fn serialized_length(&self) -> usize {
356 U8_SERIALIZED_LENGTH
357 + match self {
358 StoredValue::CLValue(cl_value) => cl_value.serialized_length(),
359 StoredValue::Account(account) => account.serialized_length(),
360 StoredValue::ContractWasm(contract_wasm) => contract_wasm.serialized_length(),
361 StoredValue::Contract(contract_header) => contract_header.serialized_length(),
362 StoredValue::ContractPackage(contract_package) => {
363 contract_package.serialized_length()
364 }
365 StoredValue::Transfer(transfer) => transfer.serialized_length(),
366 StoredValue::DeployInfo(deploy_info) => deploy_info.serialized_length(),
367 StoredValue::EraInfo(era_info) => era_info.serialized_length(),
368 StoredValue::Bid(bid) => bid.serialized_length(),
369 StoredValue::Withdraw(withdraw_purses) => withdraw_purses.serialized_length(),
370 StoredValue::Unbonding(unbonding_purses) => unbonding_purses.serialized_length(),
371 }
372 }
373
374 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
375 writer.push(self.tag() as u8);
376 match self {
377 StoredValue::CLValue(cl_value) => cl_value.write_bytes(writer)?,
378 StoredValue::Account(account) => account.write_bytes(writer)?,
379 StoredValue::ContractWasm(contract_wasm) => contract_wasm.write_bytes(writer)?,
380 StoredValue::Contract(contract_header) => contract_header.write_bytes(writer)?,
381 StoredValue::ContractPackage(contract_package) => {
382 contract_package.write_bytes(writer)?
383 }
384 StoredValue::Transfer(transfer) => transfer.write_bytes(writer)?,
385 StoredValue::DeployInfo(deploy_info) => deploy_info.write_bytes(writer)?,
386 StoredValue::EraInfo(era_info) => era_info.write_bytes(writer)?,
387 StoredValue::Bid(bid) => bid.write_bytes(writer)?,
388 StoredValue::Withdraw(unbonding_purses) => unbonding_purses.write_bytes(writer)?,
389 StoredValue::Unbonding(unbonding_purses) => unbonding_purses.write_bytes(writer)?,
390 };
391 Ok(())
392 }
393}
394
395impl FromBytes for StoredValue {
396 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
397 let (tag, remainder): (u8, &[u8]) = FromBytes::from_bytes(bytes)?;
398 match tag {
399 tag if tag == Tag::CLValue as u8 => CLValue::from_bytes(remainder)
400 .map(|(cl_value, remainder)| (StoredValue::CLValue(cl_value), remainder)),
401 tag if tag == Tag::Account as u8 => Account::from_bytes(remainder)
402 .map(|(account, remainder)| (StoredValue::Account(account), remainder)),
403 tag if tag == Tag::ContractWasm as u8 => {
404 ContractWasm::from_bytes(remainder).map(|(contract_wasm, remainder)| {
405 (StoredValue::ContractWasm(contract_wasm), remainder)
406 })
407 }
408 tag if tag == Tag::ContractPackage as u8 => {
409 ContractPackage::from_bytes(remainder).map(|(contract_package, remainder)| {
410 (StoredValue::ContractPackage(contract_package), remainder)
411 })
412 }
413 tag if tag == Tag::Contract as u8 => Contract::from_bytes(remainder)
414 .map(|(contract, remainder)| (StoredValue::Contract(contract), remainder)),
415 tag if tag == Tag::Transfer as u8 => Transfer::from_bytes(remainder)
416 .map(|(transfer, remainder)| (StoredValue::Transfer(transfer), remainder)),
417 tag if tag == Tag::DeployInfo as u8 => DeployInfo::from_bytes(remainder)
418 .map(|(deploy_info, remainder)| (StoredValue::DeployInfo(deploy_info), remainder)),
419 tag if tag == Tag::EraInfo as u8 => EraInfo::from_bytes(remainder)
420 .map(|(deploy_info, remainder)| (StoredValue::EraInfo(deploy_info), remainder)),
421 tag if tag == Tag::Bid as u8 => Bid::from_bytes(remainder)
422 .map(|(bid, remainder)| (StoredValue::Bid(Box::new(bid)), remainder)),
423 tag if tag == Tag::Withdraw as u8 => {
424 Vec::<WithdrawPurse>::from_bytes(remainder).map(|(withdraw_purses, remainder)| {
425 (StoredValue::Withdraw(withdraw_purses), remainder)
426 })
427 }
428 tag if tag == Tag::Unbonding as u8 => {
429 Vec::<UnbondingPurse>::from_bytes(remainder).map(|(unbonding_purses, remainder)| {
430 (StoredValue::Unbonding(unbonding_purses), remainder)
431 })
432 }
433 _ => Err(bytesrepr::Error::Formatting),
434 }
435 }
436}
437
438impl Serialize for StoredValue {
439 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
440 let bytes = self
443 .to_bytes()
444 .map_err(|error| ser::Error::custom(format!("{:?}", error)))?;
445 ByteBuf::from(bytes).serialize(serializer)
446 }
447}
448
449impl<'de> Deserialize<'de> for StoredValue {
450 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
451 let bytes = ByteBuf::deserialize(deserializer)?.into_vec();
452 bytesrepr::deserialize::<StoredValue>(bytes)
453 .map_err(|error| de::Error::custom(format!("{:?}", error)))
454 }
455}
456
457#[cfg(test)]
458mod tests {
459 use proptest::proptest;
460
461 use crate::{bytesrepr, gens};
462
463 proptest! {
464 #[test]
465 fn serialization_roundtrip(v in gens::stored_value_arb()) {
466 bytesrepr::test_serialization_roundtrip(&v);
467 }
468 }
469}