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    contracts::ProtocolVersionMajor,
18    EntityVersion, PackageHash,
19};
20#[cfg(doc)]
21use crate::{ExecutableDeployItem, TransactionTarget};
22
23const HASH_TAG: u8 = 0;
24const NAME_TAG: u8 = 1;
25const HASH_WITH_VERSION_TAG: u8 = 2;
26const NAME_WITH_VERSION_TAG: u8 = 3;
27
28/// Identifier for the package object within a [`TransactionTarget::Stored`] or an
29/// [`ExecutableDeployItem`].
30#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, Debug)]
31#[cfg_attr(feature = "datasize", derive(DataSize))]
32#[cfg_attr(
33    feature = "json-schema",
34    derive(JsonSchema),
35    schemars(
36        description = "Identifier for the package object within a `Stored` transaction target or \
37        an `ExecutableDeployItem`."
38    )
39)]
40pub enum PackageIdentifier {
41    /// The hash and optional version identifying the contract package.
42    Hash {
43        /// The hash of the contract package.
44        package_hash: PackageHash,
45        /// The version of the contract package.
46        ///
47        /// `None` implies latest version.
48        version: Option<EntityVersion>,
49    },
50    /// The name and optional version identifying the contract package.
51    Name {
52        /// The name of the contract package.
53        name: String,
54        /// The version of the contract package.
55        ///
56        /// `None` implies latest version.
57        version: Option<EntityVersion>,
58    },
59    /// The hash and optional version key identifying the contract package.
60    HashWithMajorVersion {
61        /// The hash of the contract package.
62        package_hash: PackageHash,
63        /// The major protocol version of the contract package.
64        ///
65        /// `None` implies latest major protocol version.
66        protocol_version_major: Option<ProtocolVersionMajor>,
67        /// The version of the contract package.
68        ///
69        /// `None` implies latest version.
70        version: Option<EntityVersion>,
71    },
72    /// The name and optional version key identifying the contract package.
73    NameWithMajorVersion {
74        /// The name of the contract package.
75        name: String,
76        /// The major protocol version of the contract package.
77        ///
78        /// `None` implies latest major protocol version.
79        protocol_version_major: Option<ProtocolVersionMajor>,
80        /// The version of the contract package.
81        ///
82        /// `None` implies latest version.
83        version: Option<EntityVersion>,
84    },
85}
86
87impl PackageIdentifier {
88    /// Returns the optional version of the contract package.
89    ///
90    /// `None` implies latest version.
91    pub fn version(&self) -> Option<EntityVersion> {
92        match self {
93            PackageIdentifier::HashWithMajorVersion { version, .. }
94            | PackageIdentifier::NameWithMajorVersion { version, .. }
95            | PackageIdentifier::Hash { version, .. }
96            | PackageIdentifier::Name { version, .. } => *version,
97        }
98    }
99
100    /// Returns the optional version key of the contract package.
101    ///
102    /// `None` implies latest version.
103    pub fn protocol_version_major(&self) -> Option<ProtocolVersionMajor> {
104        match self {
105            PackageIdentifier::HashWithMajorVersion {
106                protocol_version_major,
107                ..
108            }
109            | PackageIdentifier::NameWithMajorVersion {
110                protocol_version_major,
111                ..
112            } => *protocol_version_major,
113            PackageIdentifier::Hash { .. } | PackageIdentifier::Name { .. } => None,
114        }
115    }
116
117    /// Returns a random `PackageIdentifier`.
118    #[cfg(any(feature = "testing", test))]
119    pub fn random(rng: &mut TestRng) -> Self {
120        match rng.gen_range(0..4) {
121            0 => PackageIdentifier::Hash {
122                package_hash: PackageHash::new(rng.gen()),
123                version: rng.gen(),
124            },
125            1 => PackageIdentifier::Name {
126                name: rng.random_string(1..21),
127                version: rng.gen(),
128            },
129            2 => PackageIdentifier::HashWithMajorVersion {
130                package_hash: PackageHash::new(rng.gen()),
131                protocol_version_major: rng.gen(),
132                version: rng.gen(),
133            },
134            3 => PackageIdentifier::NameWithMajorVersion {
135                name: rng.random_string(1..21),
136                protocol_version_major: rng.gen(),
137                version: rng.gen(),
138            },
139            _ => unreachable!("Unexpected tag"),
140        }
141    }
142}
143
144impl Display for PackageIdentifier {
145    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
146        match self {
147            PackageIdentifier::Hash {
148                package_hash: contract_package_hash,
149                version: Some(ver),
150            } => write!(
151                formatter,
152                "package-id({}, version {})",
153                HexFmt(contract_package_hash),
154                ver
155            ),
156            PackageIdentifier::Hash {
157                package_hash: contract_package_hash,
158                ..
159            } => write!(
160                formatter,
161                "package-id({}, latest)",
162                HexFmt(contract_package_hash),
163            ),
164            PackageIdentifier::Name {
165                name,
166                version: Some(ver),
167            } => write!(formatter, "package-id({}, version {})", name, ver),
168            PackageIdentifier::Name { name, .. } => {
169                write!(formatter, "package-id({}, latest)", name)
170            }
171            PackageIdentifier::HashWithMajorVersion {
172                package_hash,
173                protocol_version_major,
174                version,
175            } => {
176                write!(
177                    formatter,
178                    "package-id-HashWithVersion({}, protocol_version_major: {:?}, version: {:?})",
179                    HexFmt(package_hash),
180                    protocol_version_major,
181                    version,
182                )
183            }
184            PackageIdentifier::NameWithMajorVersion {
185                name,
186                protocol_version_major,
187                version,
188            } => {
189                write!(
190                    formatter,
191                    "package-id-NameWithVersion({},protocol_version_major: {:?}, version: {:?})",
192                    name, protocol_version_major, version,
193                )
194            }
195        }
196    }
197}
198
199impl ToBytes for PackageIdentifier {
200    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
201        match self {
202            PackageIdentifier::Hash {
203                package_hash,
204                version,
205            } => {
206                HASH_TAG.write_bytes(writer)?;
207                package_hash.write_bytes(writer)?;
208                version.write_bytes(writer)
209            }
210            PackageIdentifier::Name { name, version } => {
211                NAME_TAG.write_bytes(writer)?;
212                name.write_bytes(writer)?;
213                version.write_bytes(writer)
214            }
215            PackageIdentifier::HashWithMajorVersion {
216                package_hash,
217                protocol_version_major,
218                version,
219            } => {
220                HASH_WITH_VERSION_TAG.write_bytes(writer)?;
221                package_hash.write_bytes(writer)?;
222                protocol_version_major.write_bytes(writer)?;
223                version.write_bytes(writer)
224            }
225            PackageIdentifier::NameWithMajorVersion {
226                name,
227                protocol_version_major,
228                version,
229            } => {
230                NAME_WITH_VERSION_TAG.write_bytes(writer)?;
231                name.write_bytes(writer)?;
232                protocol_version_major.write_bytes(writer)?;
233                version.write_bytes(writer)
234            }
235        }
236    }
237
238    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
239        let mut buffer = bytesrepr::allocate_buffer(self)?;
240        self.write_bytes(&mut buffer)?;
241        Ok(buffer)
242    }
243
244    fn serialized_length(&self) -> usize {
245        U8_SERIALIZED_LENGTH
246            + match self {
247                PackageIdentifier::Hash {
248                    package_hash,
249                    version,
250                } => package_hash.serialized_length() + version.serialized_length(),
251                PackageIdentifier::Name { name, version } => {
252                    name.serialized_length() + version.serialized_length()
253                }
254                PackageIdentifier::HashWithMajorVersion {
255                    package_hash,
256                    protocol_version_major,
257                    version,
258                } => {
259                    package_hash.serialized_length()
260                        + protocol_version_major.serialized_length()
261                        + version.serialized_length()
262                }
263                PackageIdentifier::NameWithMajorVersion {
264                    name,
265                    protocol_version_major,
266                    version,
267                } => {
268                    name.serialized_length()
269                        + protocol_version_major.serialized_length()
270                        + version.serialized_length()
271                }
272            }
273    }
274}
275
276impl FromBytes for PackageIdentifier {
277    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
278        let (tag, remainder) = u8::from_bytes(bytes)?;
279        match tag {
280            HASH_TAG => {
281                let (package_hash, remainder) = PackageHash::from_bytes(remainder)?;
282                let (version, remainder) = Option::<EntityVersion>::from_bytes(remainder)?;
283                let id = PackageIdentifier::Hash {
284                    package_hash,
285                    version,
286                };
287                Ok((id, remainder))
288            }
289            NAME_TAG => {
290                let (name, remainder) = String::from_bytes(remainder)?;
291                let (version, remainder) = Option::<EntityVersion>::from_bytes(remainder)?;
292                let id = PackageIdentifier::Name { name, version };
293                Ok((id, remainder))
294            }
295            HASH_WITH_VERSION_TAG => {
296                let (package_hash, remainder) = PackageHash::from_bytes(remainder)?;
297                let (protocol_version_major, remainder) = Option::from_bytes(remainder)?;
298                let (version, remainder) = Option::from_bytes(remainder)?;
299                let id = PackageIdentifier::HashWithMajorVersion {
300                    package_hash,
301                    protocol_version_major,
302                    version,
303                };
304                Ok((id, remainder))
305            }
306            NAME_WITH_VERSION_TAG => {
307                let (name, remainder) = String::from_bytes(remainder)?;
308                let (protocol_version_major, remainder) = Option::from_bytes(remainder)?;
309                let (version, remainder) = Option::from_bytes(remainder)?;
310                let id = PackageIdentifier::NameWithMajorVersion {
311                    name,
312                    protocol_version_major,
313                    version,
314                };
315                Ok((id, remainder))
316            }
317            _ => Err(bytesrepr::Error::Formatting),
318        }
319    }
320}
321
322#[cfg(test)]
323mod tests {
324    use super::*;
325
326    #[test]
327    fn bytesrepr_roundtrip() {
328        let rng = &mut TestRng::new();
329        bytesrepr::test_serialization_roundtrip(&PackageIdentifier::random(rng));
330    }
331}