Skip to main content

canic_core/ids/
release_set.rs

1//! Module: ids::release_set
2//!
3//! Responsibility: identify one exact canonical Fleet Subnet Root release-set manifest.
4//! Does not own: manifest encoding, artifact storage, publication, or active-set policy.
5//! Boundary: the host derives this digest from validated canonical manifest bytes.
6
7use std::fmt::{self, Display};
8
9use crate::ids::ReleaseBuildId;
10use candid::CandidType;
11use serde::{Deserialize, Serialize};
12
13///
14/// ReleaseSetDigest
15///
16/// SHA-256 identity of one canonical Fleet Subnet Root release-set manifest.
17///
18
19#[derive(
20    CandidType, Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize,
21)]
22#[serde(transparent)]
23pub struct ReleaseSetDigest([u8; 32]);
24
25impl ReleaseSetDigest {
26    #[must_use]
27    pub const fn from_bytes(bytes: [u8; 32]) -> Self {
28        Self(bytes)
29    }
30
31    #[must_use]
32    pub const fn as_bytes(&self) -> &[u8; 32] {
33        &self.0
34    }
35
36    #[must_use]
37    pub const fn into_bytes(self) -> [u8; 32] {
38        self.0
39    }
40}
41
42impl Display for ReleaseSetDigest {
43    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
44        for byte in self.0 {
45            write!(formatter, "{byte:02x}")?;
46        }
47        Ok(())
48    }
49}
50
51///
52/// FleetSubnetRootReleaseSet
53///
54/// Exact release-build and manifest identity admitted for one Fleet Subnet Root.
55///
56
57#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
58#[serde(deny_unknown_fields)]
59pub struct FleetSubnetRootReleaseSet {
60    pub release_build_id: ReleaseBuildId,
61    pub manifest_digest: ReleaseSetDigest,
62}
63
64// -----------------------------------------------------------------------------
65// Tests
66// -----------------------------------------------------------------------------
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71    use crate::ids::ReleaseBuildNonce;
72
73    #[test]
74    fn digest_has_exact_bytes_text_and_candid_representation() {
75        let digest = ReleaseSetDigest::from_bytes([0xab; 32]);
76
77        assert_eq!(digest.as_bytes(), &[0xab; 32]);
78        assert_eq!(digest.into_bytes(), [0xab; 32]);
79        assert_eq!(digest.to_string(), "ab".repeat(32));
80
81        let bytes = candid::encode_one(digest).expect("encode Release Set digest");
82        assert_eq!(
83            candid::decode_one::<ReleaseSetDigest>(&bytes).expect("decode Release Set digest"),
84            digest
85        );
86
87        let release_set = FleetSubnetRootReleaseSet {
88            release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
89                [7; 32],
90            )),
91            manifest_digest: digest,
92        };
93        let bytes = candid::encode_one(release_set).expect("encode root Release Set identity");
94        assert_eq!(
95            candid::decode_one::<FleetSubnetRootReleaseSet>(&bytes)
96                .expect("decode root Release Set identity"),
97            release_set
98        );
99    }
100}