Skip to main content

fedimint_core/
version.rs

1use semver::{BuildMetadata, Prerelease, Version};
2use serde::{Serialize, Serializer};
3
4use crate::encoding::{Decodable, DecodeError, Encodable};
5use crate::module::registry::ModuleDecoderRegistry;
6
7/// Get the  cargo package version of `fedimint-core`
8pub fn cargo_pkg() -> &'static str {
9    env!("CARGO_PKG_VERSION")
10}
11
12/// Get the `x.y.z` cargo release version of `fedimint-core`.
13pub fn cargo_pkg_release() -> &'static str {
14    release_version(cargo_pkg())
15}
16
17/// Return only the `x.y.z` release component of a cargo package version.
18pub fn release_version(version: &str) -> &str {
19    version
20        .split(['-', '+'])
21        .next()
22        .expect("split always returns at least one item")
23}
24
25/// A validated Fedimint version projected for DKG compatibility.
26///
27/// DKG ignores patch and pre-release components, but requires exact equality
28/// of the major version, minor version, and optional vendor string. The vendor
29/// string is encoded as SemVer build metadata.
30#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
31pub struct DkgVersion {
32    /// The normalized semantic release version sent in setup codes.
33    setup_code_version: Box<Version>,
34}
35
36/// The semantic identity used to decide whether two guardians can run DKG.
37#[derive(Debug, Clone, Eq, PartialEq)]
38pub struct DkgVersionCompatibility {
39    /// The Fedimint major version.
40    major: u64,
41    /// The Fedimint minor version.
42    minor: u64,
43    /// The exact optional vendor identity.
44    vendor: Option<BuildMetadata>,
45}
46
47impl DkgVersion {
48    /// Parse a semantic version and derive its setup-code and DKG forms.
49    pub fn parse(version: &str) -> Result<Self, semver::Error> {
50        let mut setup_code_version = Version::parse(version)?;
51        setup_code_version.pre = Prerelease::EMPTY;
52
53        Ok(Self {
54            setup_code_version: Box::new(setup_code_version),
55        })
56    }
57
58    /// Return the normalized release version stored in a setup code.
59    pub fn setup_code_version(&self) -> &Version {
60        &self.setup_code_version
61    }
62
63    /// Return the major.minor and exact optional vendor identity for DKG.
64    pub fn compatibility_version(&self) -> DkgVersionCompatibility {
65        DkgVersionCompatibility {
66            major: self.setup_code_version.major,
67            minor: self.setup_code_version.minor,
68            vendor: (!self.setup_code_version.build.is_empty())
69                .then(|| self.setup_code_version.build.clone()),
70        }
71    }
72}
73
74impl std::fmt::Display for DkgVersion {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76        self.setup_code_version.fmt(f)
77    }
78}
79
80impl Serialize for DkgVersion {
81    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
82    where
83        S: Serializer,
84    {
85        serializer.collect_str(self)
86    }
87}
88
89impl Encodable for DkgVersion {
90    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
91        self.to_string().consensus_encode(writer)
92    }
93}
94
95impl Decodable for DkgVersion {
96    fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
97        d: &mut D,
98        modules: &ModuleDecoderRegistry,
99    ) -> Result<Self, DecodeError> {
100        let version = String::consensus_decode_partial_from_finite_reader(d, modules)?;
101        Self::parse(&version).map_err(DecodeError::from_err)
102    }
103}
104
105impl std::fmt::Display for DkgVersionCompatibility {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        write!(f, "{}.{}", self.major, self.minor)?;
108        if let Some(vendor) = &self.vendor {
109            write!(f, "+{vendor}")?;
110        }
111        Ok(())
112    }
113}
114
115/// Get the git hash version of `fedimint-core`
116///
117/// Note, in certain situations this not be accurate (eg. might be all `0`s).
118///
119/// The return value was injected via `fedimint-build` crate at the compile
120/// time.
121pub fn git_hash() -> &'static str {
122    option_env!("FEDIMINT_BUILD_CODE_VERSION").unwrap_or("0000000000000000000000000000000000000001")
123}
124
125/// Returns the version hash if it is meaningful (i.e. not all zeros, which
126/// `fedimint-build` substitutes when no git information is available).
127pub fn non_zero_version_hash(hash: &str) -> Option<&str> {
128    if hash.bytes().all(|b| b == b'0') {
129        None
130    } else {
131        Some(hash)
132    }
133}
134
135#[cfg(test)]
136mod tests;