casper_types/transaction/
package_identifier.rs

1use alloc::{string::String, vec::Vec};
2use core::fmt::{self, Display, Formatter};
3
4#[cfg(feature = "datasize")]
5use datasize::DataSize;
6use hex_fmt::HexFmt;
7#[cfg(any(feature = "testing", test))]
8use rand::Rng;
9#[cfg(feature = "json-schema")]
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12
13#[cfg(any(feature = "testing", test))]
14use crate::testing::TestRng;
15use crate::{
16    bytesrepr::{self, FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
17    EntityVersion, PackageHash,
18};
19#[cfg(doc)]
20use crate::{ExecutableDeployItem, TransactionTarget};
21
22const HASH_TAG: u8 = 0;
23const NAME_TAG: u8 = 1;
24
25/// Identifier for the package object within a [`TransactionTarget::Stored`] or an
26/// [`ExecutableDeployItem`].
27#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, Debug)]
28#[cfg_attr(feature = "datasize", derive(DataSize))]
29#[cfg_attr(
30    feature = "json-schema",
31    derive(JsonSchema),
32    schemars(
33        description = "Identifier for the package object within a `Stored` transaction target or \
34        an `ExecutableDeployItem`."
35    )
36)]
37pub enum PackageIdentifier {
38    /// The hash and optional version identifying the contract package.
39    Hash {
40        /// The hash of the contract package.
41        package_hash: PackageHash,
42        /// The version of the contract package.
43        ///
44        /// `None` implies latest version.
45        version: Option<EntityVersion>,
46    },
47    /// The name and optional version identifying the contract package.
48    Name {
49        /// The name of the contract package.
50        name: String,
51        /// The version of the contract package.
52        ///
53        /// `None` implies latest version.
54        version: Option<EntityVersion>,
55    },
56}
57
58impl PackageIdentifier {
59    /// Returns the optional version of the contract package.
60    ///
61    /// `None` implies latest version.
62    pub fn version(&self) -> Option<EntityVersion> {
63        match self {
64            PackageIdentifier::Hash { version, .. } | PackageIdentifier::Name { version, .. } => {
65                *version
66            }
67        }
68    }
69
70    /// Returns a random `PackageIdentifier`.
71    #[cfg(any(feature = "testing", test))]
72    pub fn random(rng: &mut TestRng) -> Self {
73        let version = rng.gen::<bool>().then(|| rng.gen::<EntityVersion>());
74        if rng.gen() {
75            PackageIdentifier::Hash {
76                package_hash: PackageHash::new(rng.gen()),
77                version,
78            }
79        } else {
80            PackageIdentifier::Name {
81                name: rng.random_string(1..21),
82                version,
83            }
84        }
85    }
86}
87
88impl Display for PackageIdentifier {
89    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
90        match self {
91            PackageIdentifier::Hash {
92                package_hash: contract_package_hash,
93                version: Some(ver),
94            } => write!(
95                formatter,
96                "package-id({}, version {})",
97                HexFmt(contract_package_hash),
98                ver
99            ),
100            PackageIdentifier::Hash {
101                package_hash: contract_package_hash,
102                ..
103            } => write!(
104                formatter,
105                "package-id({}, latest)",
106                HexFmt(contract_package_hash),
107            ),
108            PackageIdentifier::Name {
109                name,
110                version: Some(ver),
111            } => write!(formatter, "package-id({}, version {})", name, ver),
112            PackageIdentifier::Name { name, .. } => {
113                write!(formatter, "package-id({}, latest)", name)
114            }
115        }
116    }
117}
118
119impl ToBytes for PackageIdentifier {
120    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
121        match self {
122            PackageIdentifier::Hash {
123                package_hash,
124                version,
125            } => {
126                HASH_TAG.write_bytes(writer)?;
127                package_hash.write_bytes(writer)?;
128                version.write_bytes(writer)
129            }
130            PackageIdentifier::Name { name, version } => {
131                NAME_TAG.write_bytes(writer)?;
132                name.write_bytes(writer)?;
133                version.write_bytes(writer)
134            }
135        }
136    }
137
138    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
139        let mut buffer = bytesrepr::allocate_buffer(self)?;
140        self.write_bytes(&mut buffer)?;
141        Ok(buffer)
142    }
143
144    fn serialized_length(&self) -> usize {
145        U8_SERIALIZED_LENGTH
146            + match self {
147                PackageIdentifier::Hash {
148                    package_hash,
149                    version,
150                } => package_hash.serialized_length() + version.serialized_length(),
151                PackageIdentifier::Name { name, version } => {
152                    name.serialized_length() + version.serialized_length()
153                }
154            }
155    }
156}
157
158impl FromBytes for PackageIdentifier {
159    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
160        let (tag, remainder) = u8::from_bytes(bytes)?;
161        match tag {
162            HASH_TAG => {
163                let (package_hash, remainder) = PackageHash::from_bytes(remainder)?;
164                let (version, remainder) = Option::<EntityVersion>::from_bytes(remainder)?;
165                let id = PackageIdentifier::Hash {
166                    package_hash,
167                    version,
168                };
169                Ok((id, remainder))
170            }
171            NAME_TAG => {
172                let (name, remainder) = String::from_bytes(remainder)?;
173                let (version, remainder) = Option::<EntityVersion>::from_bytes(remainder)?;
174                let id = PackageIdentifier::Name { name, version };
175                Ok((id, remainder))
176            }
177            _ => Err(bytesrepr::Error::Formatting),
178        }
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185
186    #[test]
187    fn bytesrepr_roundtrip() {
188        let rng = &mut TestRng::new();
189        bytesrepr::test_serialization_roundtrip(&PackageIdentifier::random(rng));
190    }
191}