Skip to main content

ave_common/
governance.rs

1//! Governance update payloads.
2//!
3//! These types model member, role, schema and policy changes applied to a
4//! governance subject. They are plain serializable data structures and are
5//! shared by the API layer, the core ledger and TypeScript exports.
6
7use std::{
8    collections::{BTreeSet, HashSet},
9    hash::Hash,
10};
11
12use serde::{Deserialize, Serialize, Serializer};
13use serde_json::Value;
14
15#[cfg(feature = "typescript")]
16use ts_rs::TS;
17
18use crate::identity::PublicKey;
19use crate::{Namespace, SchemaType};
20
21fn default_witnesses_creator() -> BTreeSet<String> {
22    BTreeSet::from(["Witnesses".to_owned()])
23}
24
25pub type MemberName = String;
26
27/// Governance change set grouped by concern.
28#[derive(Debug, Clone, Serialize, Deserialize)]
29#[cfg_attr(feature = "typescript", derive(TS))]
30#[cfg_attr(feature = "typescript", ts(export))]
31pub struct GovernanceEvent {
32    pub members: Option<MemberEvent>,
33    pub roles: Option<RolesEvent>,
34    pub schemas: Option<SchemasEvent>,
35    pub policies: Option<PoliciesEvent>,
36}
37
38///// Members /////
39/// Member additions and removals.
40#[derive(Debug, Clone, Serialize, Deserialize)]
41#[cfg_attr(feature = "typescript", derive(TS))]
42#[cfg_attr(feature = "typescript", ts(export))]
43pub struct MemberEvent {
44    pub add: Option<HashSet<NewMember>>,
45    pub remove: Option<HashSet<MemberName>>,
46}
47
48/// New member entry used in governance updates.
49#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
50#[cfg_attr(feature = "typescript", derive(TS))]
51#[cfg_attr(feature = "typescript", ts(export))]
52pub struct NewMember {
53    pub name: MemberName,
54    #[cfg_attr(feature = "typescript", ts(type = "string"))]
55    pub key: PublicKey,
56}
57
58///// Roles /////
59/// Role updates grouped by role family.
60#[derive(Debug, Clone, Serialize, Deserialize)]
61#[cfg_attr(feature = "typescript", derive(TS))]
62#[cfg_attr(feature = "typescript", ts(export))]
63pub struct RolesEvent {
64    pub governance: Option<GovRoleEvent>,
65    pub tracker_schemas: Option<TrackerSchemasRoleEvent>,
66    pub schema: Option<HashSet<SchemaIdRole>>,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
70#[cfg_attr(feature = "typescript", derive(TS))]
71#[cfg_attr(feature = "typescript", ts(export))]
72pub struct GovRoleEvent {
73    pub add: Option<GovRolesEvent>,
74    pub remove: Option<GovRolesEvent>,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
78#[cfg_attr(feature = "typescript", derive(TS))]
79#[cfg_attr(feature = "typescript", ts(export))]
80pub struct SchemaIdRole {
81    pub schema_id: SchemaType,
82    pub add: Option<SchemaRolesAddEvent>,
83    pub remove: Option<SchemaRolesRemoveEvent>,
84    pub change: Option<SchemaRolesChangeEvent>,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
88#[cfg_attr(feature = "typescript", derive(TS))]
89#[cfg_attr(feature = "typescript", ts(export))]
90pub struct TrackerSchemasRoleEvent {
91    pub add: Option<TrackerSchemasRolesAddEvent>,
92    pub remove: Option<TrackerSchemasRolesRemoveEvent>,
93    pub change: Option<TrackerSchemasRolesChangeEvent>,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
97#[cfg_attr(feature = "typescript", derive(TS))]
98#[cfg_attr(feature = "typescript", ts(export))]
99pub struct GovRolesEvent {
100    pub approver: Option<BTreeSet<MemberName>>,
101    pub evaluator: Option<BTreeSet<MemberName>>,
102    pub validator: Option<BTreeSet<MemberName>>,
103    pub witness: Option<BTreeSet<MemberName>>,
104    pub issuer: Option<BTreeSet<MemberName>>,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
108#[cfg_attr(feature = "typescript", derive(TS))]
109#[cfg_attr(feature = "typescript", ts(export))]
110pub struct TrackerSchemasRolesAddEvent {
111    pub evaluator: Option<BTreeSet<Role>>,
112    pub validator: Option<BTreeSet<Role>>,
113    pub witness: Option<BTreeSet<Role>>,
114    pub issuer: Option<BTreeSet<Role>>,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
118#[cfg_attr(feature = "typescript", derive(TS))]
119#[cfg_attr(feature = "typescript", ts(export))]
120pub struct SchemaRolesAddEvent {
121    pub evaluator: Option<BTreeSet<Role>>,
122    pub validator: Option<BTreeSet<Role>>,
123    pub witness: Option<BTreeSet<Role>>,
124    pub creator: Option<BTreeSet<RoleCreator>>,
125    pub issuer: Option<BTreeSet<Role>>,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
129#[cfg_attr(feature = "typescript", derive(TS))]
130#[cfg_attr(feature = "typescript", ts(export))]
131pub struct TrackerSchemasRolesRemoveEvent {
132    pub evaluator: Option<BTreeSet<Role>>,
133    pub validator: Option<BTreeSet<Role>>,
134    pub witness: Option<BTreeSet<Role>>,
135    pub issuer: Option<BTreeSet<Role>>,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
139#[cfg_attr(feature = "typescript", derive(TS))]
140#[cfg_attr(feature = "typescript", ts(export))]
141pub struct SchemaRolesRemoveEvent {
142    pub evaluator: Option<BTreeSet<Role>>,
143    pub validator: Option<BTreeSet<Role>>,
144    pub witness: Option<BTreeSet<Role>>,
145    pub creator: Option<BTreeSet<Role>>,
146    pub issuer: Option<BTreeSet<Role>>,
147}
148
149#[derive(
150    Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord,
151)]
152#[cfg_attr(feature = "typescript", derive(TS))]
153#[cfg_attr(feature = "typescript", ts(export))]
154pub struct TrackerSchemasRolesChangeEvent {
155    pub evaluator: Option<BTreeSet<RoleChange>>,
156    pub validator: Option<BTreeSet<RoleChange>>,
157    pub witness: Option<BTreeSet<RoleChange>>,
158    pub issuer: Option<BTreeSet<RoleChange>>,
159}
160
161#[derive(
162    Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord,
163)]
164#[cfg_attr(feature = "typescript", derive(TS))]
165#[cfg_attr(feature = "typescript", ts(export))]
166pub struct SchemaRolesChangeEvent {
167    pub evaluator: Option<BTreeSet<RoleChange>>,
168    pub validator: Option<BTreeSet<RoleChange>>,
169    pub witness: Option<BTreeSet<RoleChange>>,
170    pub creator: Option<BTreeSet<RoleCreatorChange>>,
171    pub issuer: Option<BTreeSet<RoleChange>>,
172}
173
174#[derive(
175    Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord,
176)]
177#[cfg_attr(feature = "typescript", derive(TS))]
178#[cfg_attr(feature = "typescript", ts(export))]
179pub struct RoleCreatorChange {
180    pub actual_name: MemberName,
181    pub actual_namespace: Namespace,
182    pub new_namespace: Option<Namespace>,
183    pub new_witnesses: Option<BTreeSet<String>>,
184    pub new_quantity: Option<CreatorQuantity>,
185}
186
187#[derive(
188    Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord,
189)]
190#[cfg_attr(feature = "typescript", derive(TS))]
191#[cfg_attr(feature = "typescript", ts(export))]
192pub struct RoleChange {
193    pub actual_name: MemberName,
194    pub actual_namespace: Namespace,
195    pub new_namespace: Namespace,
196}
197
198///// Schemas /////
199#[derive(Debug, Clone, Serialize, Deserialize)]
200#[cfg_attr(feature = "typescript", derive(TS))]
201#[cfg_attr(feature = "typescript", ts(export))]
202pub struct SchemasEvent {
203    pub add: Option<HashSet<SchemaAdd>>,
204    pub remove: Option<HashSet<SchemaType>>,
205    pub change: Option<HashSet<SchemaChange>>,
206}
207
208#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
209#[cfg_attr(feature = "typescript", derive(TS))]
210#[cfg_attr(feature = "typescript", ts(export))]
211pub struct SchemaAdd {
212    pub id: SchemaType,
213    pub contract: String,
214    pub initial_value: Value,
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
218#[cfg_attr(feature = "typescript", derive(TS))]
219#[cfg_attr(feature = "typescript", ts(export))]
220pub struct SchemaChange {
221    pub actual_id: SchemaType,
222    pub new_contract: Option<String>,
223    pub new_initial_value: Option<Value>,
224}
225
226///// Policies /////
227#[derive(Debug, Clone, Serialize, Deserialize)]
228#[cfg_attr(feature = "typescript", derive(TS))]
229#[cfg_attr(feature = "typescript", ts(export))]
230pub struct PoliciesEvent {
231    pub governance: Option<GovPolicieEvent>,
232    pub schema: Option<HashSet<SchemaIdPolicie>>,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
236#[cfg_attr(feature = "typescript", derive(TS))]
237#[cfg_attr(feature = "typescript", ts(export))]
238pub struct SchemaIdPolicie {
239    pub schema_id: SchemaType,
240    pub change: SchemaPolicieChange,
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
244#[cfg_attr(feature = "typescript", derive(TS))]
245#[cfg_attr(feature = "typescript", ts(export))]
246pub struct GovPolicieEvent {
247    pub change: GovPolicieChange,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
251#[cfg_attr(feature = "typescript", derive(TS))]
252#[cfg_attr(feature = "typescript", ts(export))]
253pub struct GovPolicieChange {
254    pub approve: Option<Quorum>,
255    pub evaluate: Option<Quorum>,
256    pub validate: Option<Quorum>,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
260#[cfg_attr(feature = "typescript", derive(TS))]
261#[cfg_attr(feature = "typescript", ts(export))]
262pub struct SchemaPolicieChange {
263    pub evaluate: Option<Quorum>,
264    pub validate: Option<Quorum>,
265}
266
267/// Governance-wide quorum policy.
268/// Governance quorum.
269#[derive(
270    Debug, Clone, Default, Serialize, Deserialize, PartialEq, Hash, Eq,
271)]
272#[cfg_attr(feature = "typescript", derive(TS))]
273#[cfg_attr(feature = "typescript", ts(export))]
274#[serde(rename_all = "lowercase")]
275pub enum Quorum {
276    #[default]
277    Majority,
278    Fixed(u32),
279    Percentage(u8),
280}
281
282#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
283#[cfg_attr(feature = "typescript", derive(TS))]
284#[cfg_attr(feature = "typescript", ts(export, type = "number | \"infinity\""))]
285pub enum CreatorQuantity {
286    Quantity(u32),
287    Infinity,
288}
289
290impl<'de> Deserialize<'de> for CreatorQuantity {
291    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
292    where
293        D: serde::Deserializer<'de>,
294    {
295        let value = serde_json::Value::deserialize(deserializer)?;
296
297        match value {
298            serde_json::Value::String(s) if s == "infinity" => {
299                Ok(Self::Infinity)
300            }
301            serde_json::Value::Number(n) if n.is_u64() => {
302                Ok(Self::Quantity(n.as_u64().ok_or_else(|| {
303                    serde::de::Error::custom(
304                        "Quantity must be a number or 'infinity'",
305                    )
306                })? as u32))
307            }
308            _ => Err(serde::de::Error::custom(
309                "Quantity must be a number or 'infinity'",
310            )),
311        }
312    }
313}
314
315impl Serialize for CreatorQuantity {
316    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
317    where
318        S: Serializer,
319    {
320        match self {
321            Self::Quantity(n) => serializer.serialize_u32(*n),
322            Self::Infinity => serializer.serialize_str("infinity"),
323        }
324    }
325}
326
327#[derive(
328    Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq, PartialOrd, Ord,
329)]
330#[cfg_attr(feature = "typescript", derive(TS))]
331#[cfg_attr(feature = "typescript", ts(export))]
332pub struct Role {
333    pub name: String,
334    pub namespace: Namespace,
335}
336
337#[derive(Debug, Serialize, Deserialize, Clone)]
338#[cfg_attr(feature = "typescript", derive(TS))]
339#[cfg_attr(feature = "typescript", ts(export))]
340pub struct RoleCreator {
341    pub name: String,
342    pub namespace: Namespace,
343    #[serde(default = "default_witnesses_creator")]
344    pub witnesses: BTreeSet<String>,
345    pub quantity: CreatorQuantity,
346}
347
348impl Hash for RoleCreator {
349    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
350        self.name.hash(state);
351        self.namespace.hash(state);
352    }
353}
354
355impl PartialOrd for RoleCreator {
356    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
357        Some(self.cmp(other))
358    }
359}
360
361impl Ord for RoleCreator {
362    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
363        (self.name.clone(), self.namespace.clone())
364            .cmp(&(other.name.clone(), other.namespace.clone()))
365    }
366}
367
368impl PartialEq for RoleCreator {
369    fn eq(&self, other: &Self) -> bool {
370        self.name == other.name && self.namespace == other.namespace
371    }
372}
373
374impl Eq for RoleCreator {}