casper_types/transaction/
approvals_hash.rs

1use alloc::{collections::BTreeSet, 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;
8use serde::{Deserialize, Serialize};
9
10use super::Approval;
11#[cfg(any(feature = "testing", test))]
12use crate::testing::TestRng;
13use crate::{
14    bytesrepr::{self, FromBytes, ToBytes},
15    Digest,
16};
17
18/// The cryptographic hash of the bytesrepr-encoded set of approvals for a single [``].
19#[derive(
20    Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, Debug, Default,
21)]
22#[cfg_attr(feature = "datasize", derive(DataSize))]
23#[serde(deny_unknown_fields)]
24pub struct ApprovalsHash(pub Digest);
25
26impl ApprovalsHash {
27    /// The number of bytes in a `ApprovalsHash` digest.
28    pub const LENGTH: usize = Digest::LENGTH;
29
30    /// Constructs a new `ApprovalsHash` by bytesrepr-encoding `approvals` and creating
31    /// a [`Digest`] of this.
32    pub fn compute(approvals: &BTreeSet<Approval>) -> Result<Self, bytesrepr::Error> {
33        let digest = Digest::hash(approvals.to_bytes()?);
34        Ok(ApprovalsHash(digest))
35    }
36
37    /// Returns the wrapped inner digest.
38    pub fn inner(&self) -> &Digest {
39        &self.0
40    }
41
42    /// Returns a new `ApprovalsHash` directly initialized with the provided bytes; no
43    /// hashing is done.
44    #[cfg(any(feature = "testing", test))]
45    pub const fn from_raw(raw_digest: [u8; Self::LENGTH]) -> Self {
46        ApprovalsHash(Digest::from_raw(raw_digest))
47    }
48
49    /// Returns a random `ApprovalsHash`.
50    #[cfg(any(feature = "testing", test))]
51    pub fn random(rng: &mut TestRng) -> Self {
52        let hash = rng.gen::<[u8; Digest::LENGTH]>().into();
53        ApprovalsHash(hash)
54    }
55}
56
57impl From<ApprovalsHash> for Digest {
58    fn from(hash: ApprovalsHash) -> Self {
59        hash.0
60    }
61}
62
63impl From<Digest> for ApprovalsHash {
64    fn from(digest: Digest) -> Self {
65        Self(digest)
66    }
67}
68
69impl Display for ApprovalsHash {
70    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
71        write!(formatter, "transaction-v1-approvals-hash({})", self.0,)
72    }
73}
74
75impl AsRef<[u8]> for ApprovalsHash {
76    fn as_ref(&self) -> &[u8] {
77        self.0.as_ref()
78    }
79}
80
81impl ToBytes for ApprovalsHash {
82    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
83        self.0.write_bytes(writer)
84    }
85
86    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
87        self.0.to_bytes()
88    }
89
90    fn serialized_length(&self) -> usize {
91        self.0.serialized_length()
92    }
93}
94
95impl FromBytes for ApprovalsHash {
96    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
97        Digest::from_bytes(bytes).map(|(inner, remainder)| (ApprovalsHash(inner), remainder))
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn bytesrepr_roundtrip() {
107        let rng = &mut TestRng::new();
108        let hash = ApprovalsHash::random(rng);
109        bytesrepr::test_serialization_roundtrip(&hash);
110    }
111}