Skip to main content

mithril_stm/protocol/
parameters.rs

1use serde::{Deserialize, Serialize};
2
3use crate::codec;
4use crate::{PhiFValue, StmResult};
5
6use super::RegisterError;
7
8/// Used to set protocol parameters.
9// todo: this is the criteria to consider parameters valid:
10// Let A = max assumed adversarial stake
11// Let a = A / max_stake
12// Let p = φ(a)  // f needs tuning, something close to 0.2 is reasonable
13// Then, we're secure if SUM[from i=k to i=m] Binomial(i successes, m experiments, p chance of success) <= 2^-100 or thereabouts.
14// The latter turns to 1 - BinomialCDF(k-1,m,p)
15#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
16pub struct Parameters {
17    /// Security parameter, upper bound on indices.
18    pub m: u64,
19    /// Quorum parameter.
20    pub k: u64,
21    /// `f` in phi(w) = 1 - (1 - f)^w, where w is the stake of a participant..
22    pub phi_f: PhiFValue,
23}
24
25impl Parameters {
26    /// Convert to CBOR bytes with a version prefix.
27    ///
28    /// Legacy readers that encounter the version prefix byte will fail
29    /// gracefully, while new readers will decode the CBOR payload.
30    pub fn to_bytes(&self) -> StmResult<Vec<u8>> {
31        codec::to_cbor_bytes(self)
32    }
33
34    /// Extract the `Parameters` from a byte slice.
35    ///
36    /// Supports both the legacy big-endian byte format and the new
37    /// versioned CBOR format.
38    ///
39    /// # Error
40    /// The function fails if the given string of bytes is not of required size.
41    pub fn from_bytes(bytes: &[u8]) -> StmResult<Self> {
42        codec::from_versioned_bytes(bytes, Self::from_bytes_legacy)
43    }
44
45    fn from_bytes_legacy(bytes: &[u8]) -> StmResult<Self> {
46        let mut u64_bytes = [0u8; 8];
47        u64_bytes.copy_from_slice(bytes.get(..8).ok_or(RegisterError::SerializationError)?);
48        let m = u64::from_be_bytes(u64_bytes);
49        u64_bytes.copy_from_slice(bytes.get(8..16).ok_or(RegisterError::SerializationError)?);
50        let k = u64::from_be_bytes(u64_bytes);
51        u64_bytes.copy_from_slice(bytes.get(16..24).ok_or(RegisterError::SerializationError)?);
52        let phi_f = f64::from_be_bytes(u64_bytes);
53
54        Ok(Self { m, k, phi_f })
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    mod golden {
63        use super::*;
64
65        const GOLDEN_JSON: &str = r#"
66            {
67                "m": 20973,
68                "k": 2422,
69                "phi_f": 0.2
70            }
71        "#;
72
73        fn golden_value() -> Parameters {
74            Parameters {
75                m: 20973,
76                k: 2422,
77                phi_f: 0.2,
78            }
79        }
80
81        #[test]
82        fn golden_conversions() {
83            let value = serde_json::from_str(GOLDEN_JSON)
84                .expect("This JSON deserialization should not fail");
85            assert_eq!(golden_value(), value);
86
87            let serialized =
88                serde_json::to_string(&value).expect("This JSON serialization should not fail");
89            let golden_serialized = serde_json::to_string(&golden_value())
90                .expect("This JSON serialization should not fail");
91            assert_eq!(golden_serialized, serialized);
92        }
93    }
94
95    mod cbor {
96        use super::*;
97
98        const LEGACY_BYTES: &[u8; 24] = &[
99            0, 0, 0, 0, 0, 0, 81, 237, 0, 0, 0, 0, 0, 0, 9, 118, 63, 201, 153, 153, 153, 153, 153,
100            154,
101        ];
102
103        fn test_value() -> Parameters {
104            Parameters {
105                m: 20973,
106                k: 2422,
107                phi_f: 0.2,
108            }
109        }
110
111        #[test]
112        fn cbor_roundtrip() {
113            let value = test_value();
114            let bytes = value.to_bytes().expect("CBOR serialization should not fail");
115            let decoded =
116                Parameters::from_bytes(&bytes).expect("CBOR deserialization should not fail");
117            assert_eq!(value, decoded);
118        }
119
120        #[test]
121        fn legacy_bytes_can_still_be_decoded() {
122            let decoded = Parameters::from_bytes(LEGACY_BYTES)
123                .expect("Legacy deserialization should not fail");
124            assert_eq!(test_value(), decoded);
125        }
126
127        const GOLDEN_CBOR_BYTES: &[u8; 27] = &[
128            1, 163, 97, 109, 25, 81, 237, 97, 107, 25, 9, 118, 101, 112, 104, 105, 95, 102, 251,
129            63, 201, 153, 153, 153, 153, 153, 154,
130        ];
131
132        #[test]
133        fn cbor_golden_bytes_can_be_decoded() {
134            let decoded = Parameters::from_bytes(GOLDEN_CBOR_BYTES)
135                .expect("CBOR golden bytes deserialization should not fail");
136            assert_eq!(test_value(), decoded);
137        }
138
139        #[test]
140        fn cbor_encoding_is_stable() {
141            let bytes = test_value().to_bytes().expect("CBOR serialization should not fail");
142            assert_eq!(GOLDEN_CBOR_BYTES.as_slice(), bytes.as_slice());
143        }
144    }
145}