oasis_core_runtime/consensus/
governance.rs

1//! Governance structures.
2//!
3//! # Note
4//!
5//! This **MUST** be kept in sync with go/governance/api.
6//!
7use std::collections::BTreeMap;
8
9use crate::{
10    common::{quantity::Quantity, version::ProtocolVersions},
11    consensus::beacon::EpochTime,
12};
13
14/// A governance vote.
15#[derive(
16    Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord, cbor::Encode, cbor::Decode,
17)]
18#[repr(u8)]
19pub enum Vote {
20    /// Invalid vote that should never be explicitly set.
21    #[default]
22    Invalid = 0,
23    /// Yes Vote.
24    Yes = 1,
25    /// No vote.
26    No = 2,
27    /// Abstained.
28    Abstain = 3,
29}
30
31/// Vote for a proposal.
32#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, cbor::Encode, cbor::Decode)]
33pub struct ProposalVote {
34    /// Unique identifier of a proposal.
35    pub id: u64,
36
37    /// Proposal vote.
38    pub vote: Vote,
39}
40
41/// Upgrade proposal content.
42#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, cbor::Encode, cbor::Decode)]
43pub struct UpgradeProposal {
44    pub v: u16,
45    pub handler: String,
46    pub target: ProtocolVersions,
47    pub epoch: EpochTime,
48}
49
50/// Cancel proposal content.
51#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, cbor::Encode, cbor::Decode)]
52pub struct CancelUpgradeProposal {
53    pub proposal_id: u64,
54}
55
56/// Change parameters proposal content.
57#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
58pub struct ChangeParametersProposal {
59    pub module: String,
60    pub changes: Option<cbor::Value>,
61}
62
63/// Consensus layer governance proposal content.
64#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
65pub struct ProposalContent {
66    #[cbor(optional)]
67    pub upgrade: Option<UpgradeProposal>,
68    #[cbor(optional)]
69    pub cancel_upgrade: Option<CancelUpgradeProposal>,
70    #[cbor(optional)]
71    pub change_parameters: Option<ChangeParametersProposal>,
72}
73
74// Allowed governance consensus parameter changes.
75#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, cbor::Encode, cbor::Decode)]
76pub struct ConsensusParameterChanges {
77    #[cbor(optional)]
78    pub gas_costs: BTreeMap<String, u64>,
79    #[cbor(optional)]
80    pub min_proposal_deposit: Option<Quantity>,
81    #[cbor(optional)]
82    pub voting_period: Option<EpochTime>,
83    #[cbor(optional)]
84    pub stake_threshold: Option<u8>,
85    #[cbor(optional)]
86    pub upgrade_min_epoch_diff: Option<EpochTime>,
87    #[cbor(optional)]
88    pub upgrade_cancel_min_epoch_diff: Option<EpochTime>,
89    #[cbor(optional)]
90    pub enable_change_parameters_proposal: Option<bool>,
91}
92
93#[cfg(test)]
94mod tests {
95    use base64::prelude::*;
96
97    use super::*;
98
99    #[test]
100    fn test_consistent_proposal_vote() {
101        // NOTE: These tests MUST be synced with go/governance/api/api_test.go.
102        let tcs = vec![
103            (
104                "omJpZAtkdm90ZQE=",
105                ProposalVote {
106                    id: 11,
107                    vote: Vote::Yes,
108                },
109            ),
110            (
111                "omJpZAxkdm90ZQI=",
112                ProposalVote {
113                    id: 12,
114                    vote: Vote::No,
115                },
116            ),
117            (
118                "omJpZA1kdm90ZQM=",
119                ProposalVote {
120                    id: 13,
121                    vote: Vote::Abstain,
122                },
123            ),
124        ];
125        for (encoded_base64, vote) in tcs {
126            let dec: ProposalVote =
127                cbor::from_slice(&BASE64_STANDARD.decode(encoded_base64).unwrap())
128                    .expect("proposal vote should deserialize correctly");
129            assert_eq!(
130                dec, vote,
131                "decoded proposal vote should match the expected value"
132            );
133
134            let ser = BASE64_STANDARD.encode(cbor::to_vec(dec));
135            assert_eq!(
136                ser, encoded_base64,
137                "proposal vote should serialize correctly"
138            );
139        }
140    }
141
142    #[test]
143    fn test_consistent_proposal_content() {
144        // NOTE: These tests MUST be synced with go/governance/api/api_test.go.
145        let tcs = vec![
146            (
147                "oW5jYW5jZWxfdXBncmFkZaFrcHJvcG9zYWxfaWQYKg==",
148                ProposalContent {
149                    cancel_upgrade: Some(CancelUpgradeProposal { proposal_id: 42 }),
150                    ..Default::default()
151                },
152            ),
153            (
154                "oWd1cGdyYWRlpGF2AmVlcG9jaBgqZnRhcmdldKNyY29uc2Vuc3VzX3Byb3RvY29soWVwYXRjaBh7dXJ1bnRpbWVfaG9zdF9wcm90b2NvbKFlcGF0Y2gZAch4GnJ1bnRpbWVfY29tbWl0dGVlX3Byb3RvY29soWVwYXRjaBkDFWdoYW5kbGVybHRlc3QtaGFuZGxlcg==",
155                ProposalContent {
156                    upgrade: Some(UpgradeProposal {
157                        v: 2,
158                        handler: "test-handler".into(),
159                        target: ProtocolVersions { consensus_protocol: 123.into(), runtime_host_protocol: 456.into(), runtime_committee_protocol: 789.into() } ,
160                        epoch: 42,
161                    }),
162                    ..Default::default()
163                },
164            ),
165            (
166                "oXFjaGFuZ2VfcGFyYW1ldGVyc6JmbW9kdWxla3Rlc3QtbW9kdWxlZ2NoYW5nZXOhbXZvdGluZ19wZXJpb2QYew==",
167                ProposalContent {
168                    change_parameters: Some(ChangeParametersProposal {
169                        module: "test-module".into(),
170                        changes: Some(cbor::to_value(ConsensusParameterChanges{
171                            voting_period: Some(123),
172                            ..Default::default()
173                        })),
174                     }),
175                    ..Default::default()
176                }
177            ),
178        ];
179        for (encoded_base64, content) in tcs {
180            let dec: ProposalContent =
181                cbor::from_slice(&BASE64_STANDARD.decode(encoded_base64).unwrap())
182                    .expect("proposal content should deserialize correctly");
183            assert_eq!(
184                dec, content,
185                "decoded proposal content should match the expected value"
186            );
187
188            let ser = BASE64_STANDARD.encode(cbor::to_vec(dec));
189            assert_eq!(
190                ser, encoded_base64,
191                "proposal content should serialize correctly"
192            );
193        }
194    }
195}