casper_types/transaction/
transaction_hash.rs

1use alloc::{string::String, vec::Vec};
2use core::fmt::{self, Display, Formatter};
3
4#[cfg(feature = "datasize")]
5use datasize::DataSize;
6#[cfg(any(feature = "testing", test))]
7use rand::Rng;
8#[cfg(feature = "json-schema")]
9use schemars::JsonSchema;
10use serde::{Deserialize, Serialize};
11
12use super::{DeployHash, TransactionV1Hash};
13#[cfg(any(feature = "testing", test))]
14use crate::testing::TestRng;
15use crate::{
16    bytesrepr::{self, FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
17    Digest,
18};
19
20const DEPLOY_TAG: u8 = 0;
21const V1_TAG: u8 = 1;
22const TAG_LENGTH: u8 = 1;
23
24/// A versioned wrapper for a transaction hash or deploy hash.
25#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, Debug)]
26#[cfg_attr(feature = "datasize", derive(DataSize))]
27#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
28#[serde(deny_unknown_fields)]
29pub enum TransactionHash {
30    /// A deploy hash.
31    Deploy(DeployHash),
32    /// A version 1 transaction hash.
33    #[serde(rename = "Version1")]
34    V1(TransactionV1Hash),
35}
36
37impl TransactionHash {
38    /// The number of bytes in a `DeployHash` digest.
39    pub const LENGTH: usize = TAG_LENGTH as usize + Digest::LENGTH;
40    /// Digest representation of hash.
41    pub fn digest(&self) -> Digest {
42        match self {
43            TransactionHash::Deploy(deploy_hash) => *deploy_hash.inner(),
44            TransactionHash::V1(transaction_hash) => *transaction_hash.inner(),
45        }
46    }
47
48    /// Hexadecimal representation of the hash.
49    pub fn to_hex_string(&self) -> String {
50        base16::encode_lower(&self.digest())
51    }
52
53    /// Returns a random `TransactionHash`.
54    #[cfg(any(feature = "testing", test))]
55    pub fn random(rng: &mut TestRng) -> Self {
56        match rng.gen_range(0..2) {
57            0 => TransactionHash::from(DeployHash::random(rng)),
58            1 => TransactionHash::from(TransactionV1Hash::random(rng)),
59            _ => panic!(),
60        }
61    }
62
63    /// Returns a new `TransactionHash` directly initialized with the provided bytes; no hashing
64    /// is done.
65    pub const fn from_raw(raw_digest: [u8; TransactionV1Hash::LENGTH]) -> Self {
66        TransactionHash::V1(TransactionV1Hash::from_raw(raw_digest))
67    }
68}
69
70impl From<DeployHash> for TransactionHash {
71    fn from(hash: DeployHash) -> Self {
72        Self::Deploy(hash)
73    }
74}
75
76impl From<&DeployHash> for TransactionHash {
77    fn from(hash: &DeployHash) -> Self {
78        Self::from(*hash)
79    }
80}
81
82impl From<TransactionV1Hash> for TransactionHash {
83    fn from(hash: TransactionV1Hash) -> Self {
84        Self::V1(hash)
85    }
86}
87
88impl From<&TransactionV1Hash> for TransactionHash {
89    fn from(hash: &TransactionV1Hash) -> Self {
90        Self::from(*hash)
91    }
92}
93
94impl Default for TransactionHash {
95    fn default() -> Self {
96        TransactionHash::V1(TransactionV1Hash::default())
97    }
98}
99
100impl Display for TransactionHash {
101    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
102        match self {
103            TransactionHash::Deploy(hash) => Display::fmt(hash, formatter),
104            TransactionHash::V1(hash) => Display::fmt(hash, formatter),
105        }
106    }
107}
108
109impl AsRef<[u8]> for TransactionHash {
110    fn as_ref(&self) -> &[u8] {
111        match self {
112            TransactionHash::Deploy(hash) => hash.as_ref(),
113            TransactionHash::V1(hash) => hash.as_ref(),
114        }
115    }
116}
117
118impl ToBytes for TransactionHash {
119    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
120        let mut buffer = bytesrepr::allocate_buffer(self)?;
121        self.write_bytes(&mut buffer)?;
122        Ok(buffer)
123    }
124
125    fn serialized_length(&self) -> usize {
126        U8_SERIALIZED_LENGTH
127            + match self {
128                TransactionHash::Deploy(hash) => hash.serialized_length(),
129                TransactionHash::V1(hash) => hash.serialized_length(),
130            }
131    }
132
133    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
134        match self {
135            TransactionHash::Deploy(hash) => {
136                DEPLOY_TAG.write_bytes(writer)?;
137                hash.write_bytes(writer)
138            }
139            TransactionHash::V1(hash) => {
140                V1_TAG.write_bytes(writer)?;
141                hash.write_bytes(writer)
142            }
143        }
144    }
145}
146
147impl FromBytes for TransactionHash {
148    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
149        let (tag, remainder) = u8::from_bytes(bytes)?;
150        match tag {
151            DEPLOY_TAG => {
152                let (hash, remainder) = DeployHash::from_bytes(remainder)?;
153                Ok((TransactionHash::Deploy(hash), remainder))
154            }
155            V1_TAG => {
156                let (hash, remainder) = TransactionV1Hash::from_bytes(remainder)?;
157                Ok((TransactionHash::V1(hash), remainder))
158            }
159            _ => Err(bytesrepr::Error::Formatting),
160        }
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use super::*;
167    use crate::testing::TestRng;
168    #[test]
169    fn bytesrepr_roundtrip() {
170        let rng = &mut TestRng::new();
171
172        let hash = TransactionHash::from(DeployHash::random(rng));
173        bytesrepr::test_serialization_roundtrip(&hash);
174
175        let hash = TransactionHash::from(TransactionV1Hash::random(rng));
176        bytesrepr::test_serialization_roundtrip(&hash);
177    }
178}