casper_types/
stored_value.rs

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))]
43/// StoredValue represents all possible variants of values stored in Global State.
44pub enum StoredValue {
45    /// Variant that stores [`CLValue`].
46    CLValue(CLValue),
47    /// Variant that stores [`Account`].
48    Account(Account),
49    /// Variant that stores [`ContractWasm`].
50    ContractWasm(ContractWasm),
51    /// Variant that stores [`Contract`].
52    Contract(Contract),
53    /// Variant that stores [`ContractPackage`].
54    ContractPackage(ContractPackage),
55    /// Variant that stores [`Transfer`].
56    Transfer(Transfer),
57    /// Variant that stores [`DeployInfo`].
58    DeployInfo(DeployInfo),
59    /// Variant that stores [`EraInfo`].
60    EraInfo(EraInfo),
61    /// Variant that stores [`Bid`].
62    Bid(Box<Bid>),
63    /// Variant that stores withdraw information.
64    Withdraw(Vec<WithdrawPurse>),
65    /// Variant that stores unbonding information.
66    Unbonding(Vec<UnbondingPurse>),
67}
68
69impl StoredValue {
70    /// Returns a wrapped [`CLValue`] if this is a `CLValue` variant.
71    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    /// Returns a wrapped [`Account`] if this is an `Account` variant.
79    pub fn as_account(&self) -> Option<&Account> {
80        match self {
81            StoredValue::Account(account) => Some(account),
82            _ => None,
83        }
84    }
85
86    /// Returns a wrapped [`Contract`] if this is a `Contract` variant.
87    pub fn as_contract(&self) -> Option<&Contract> {
88        match self {
89            StoredValue::Contract(contract) => Some(contract),
90            _ => None,
91        }
92    }
93
94    /// Returns a wrapped [`ContractWasm`] if this is a `ContractWasm` variant.
95    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    /// Returns a wrapped [`ContractPackage`] if this is a `ContractPackage` variant.
103    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    /// Returns a wrapped [`DeployInfo`] if this is a `DeployInfo` variant.
111    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    /// Returns a wrapped [`EraInfo`] if this is a `EraInfo` variant.
119    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    /// Returns a wrapped [`Bid`] if this is a `Bid` variant.
127    pub fn as_bid(&self) -> Option<&Bid> {
128        match self {
129            StoredValue::Bid(bid) => Some(bid),
130            _ => None,
131        }
132    }
133
134    /// Returns a wrapped list of [`WithdrawPurse`]s if this is a `Withdraw` variant.
135    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    /// Returns a wrapped list of [`UnbondingPurse`]s if this is a `Unbonding` variant.
143    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    /// Is the stored value a unit value.
151    pub fn is_unit_cl_value(&self) -> bool {
152        self == &StoredValue::CLValue(CLValue::unit())
153    }
154
155    /// Returns the type name of the [`StoredValue`] enum variant.
156    ///
157    /// For [`CLValue`] variants it will return the name of the [`CLType`](crate::cl_type::CLType)
158    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        // The JSON representation of a StoredValue is just its bytesrepr
441        // While this makes it harder to inspect, it makes deterministic representation simple.
442        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}