Skip to main content

dpp/data_contract/group/
mod.rs

1use crate::data_contract::group::accessors::v0::{GroupV0Getters, GroupV0Setters};
2use crate::data_contract::group::methods::v0::GroupMethodsV0;
3use crate::data_contract::group::v0::GroupV0;
4use crate::data_contract::GroupContractPosition;
5use crate::errors::ProtocolError;
6#[cfg(feature = "json-conversion")]
7use crate::serialization::JsonConvertible;
8#[cfg(feature = "value-conversion")]
9use crate::serialization::ValueConvertible;
10use crate::validation::SimpleConsensusValidationResult;
11use bincode::{Decode, Encode};
12use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize};
13use platform_value::Identifier;
14use platform_version::version::PlatformVersion;
15use serde::{Deserialize, Serialize};
16use std::collections::BTreeMap;
17
18pub mod accessors;
19pub(crate) mod methods;
20pub mod v0;
21
22pub type RequiredSigners = u8;
23
24pub type GroupMemberPower = u32;
25pub type GroupSumPower = u32;
26pub type GroupRequiredPower = u32;
27#[cfg_attr(feature = "json-conversion", derive(JsonConvertible))]
28#[cfg_attr(feature = "value-conversion", derive(ValueConvertible))]
29#[derive(
30    Serialize,
31    Deserialize,
32    Decode,
33    Encode,
34    PlatformSerialize,
35    PlatformDeserialize,
36    Debug,
37    Clone,
38    PartialEq,
39    Eq,
40)]
41#[platform_serialize(unversioned)]
42#[serde(tag = "$formatVersion")]
43pub enum Group {
44    #[serde(rename = "0")]
45    V0(GroupV0),
46}
47
48impl GroupV0Getters for Group {
49    fn member_power(&self, member_id: Identifier) -> Result<u32, ProtocolError> {
50        match self {
51            Group::V0(group_v0) => group_v0.member_power(member_id),
52        }
53    }
54    fn members(&self) -> &BTreeMap<Identifier, u32> {
55        match self {
56            Group::V0(group_v0) => group_v0.members(),
57        }
58    }
59
60    fn members_mut(&mut self) -> &mut BTreeMap<Identifier, u32> {
61        match self {
62            Group::V0(group_v0) => group_v0.members_mut(),
63        }
64    }
65
66    fn required_power(&self) -> GroupRequiredPower {
67        match self {
68            Group::V0(group_v0) => group_v0.required_power(),
69        }
70    }
71}
72
73impl GroupV0Setters for Group {
74    fn set_members(&mut self, members: BTreeMap<Identifier, u32>) {
75        match self {
76            Group::V0(group_v0) => group_v0.set_members(members),
77        }
78    }
79
80    fn set_member_power(&mut self, member_id: Identifier, power: u32) {
81        match self {
82            Group::V0(group_v0) => group_v0.set_member_power(member_id, power),
83        }
84    }
85
86    fn remove_member(&mut self, member_id: &Identifier) -> bool {
87        match self {
88            Group::V0(group_v0) => group_v0.remove_member(member_id),
89        }
90    }
91
92    fn set_required_power(&mut self, required_power: GroupRequiredPower) {
93        match self {
94            Group::V0(group_v0) => group_v0.set_required_power(required_power),
95        }
96    }
97}
98
99impl GroupMethodsV0 for Group {
100    fn validate(
101        &self,
102        group_contract_position: Option<GroupContractPosition>,
103        platform_version: &PlatformVersion,
104    ) -> Result<SimpleConsensusValidationResult, ProtocolError> {
105        match self {
106            Group::V0(group_v0) => group_v0.validate(group_contract_position, platform_version),
107        }
108    }
109}
110
111#[cfg(all(
112    test,
113    feature = "json-conversion",
114    feature = "value-conversion",
115    feature = "serde-conversion"
116))]
117mod json_convertible_tests {
118    use super::*;
119    use crate::data_contract::group::v0::GroupV0;
120    use platform_value::Identifier;
121    use serde_json::json;
122    use std::collections::BTreeMap;
123
124    /// Non-default values per field so the wire-shape assertion catches any
125    /// silent zero-out / flip on round-trip.
126    fn fixture() -> Group {
127        let mut members = BTreeMap::new();
128        members.insert(Identifier::new([0xa0; 32]), 1u32);
129        members.insert(Identifier::new([0xb1; 32]), 2u32);
130        Group::V0(GroupV0 {
131            members,
132            required_power: 2,
133        })
134    }
135
136    #[test]
137    fn json_round_trip_with_full_wire_shape() {
138        use crate::serialization::JsonConvertible;
139        let original = fixture();
140        let json = original.to_json().expect("to_json");
141        // `members` keys are `Identifier` — JSON renders them as the
142        // base58-encoded string (e.g. "Bp2HuBWdciXFKV2CnoC1Z4V44QfCmArCQHdzKpArYJc7"
143        // for `[0xa0; 32]`). Member-power values are `u32`; JSON has only one
144        // number type, so the U32 distinction is erased on the wire — the
145        // value-path assertion below uses `1u32` / `2u32` to lock it in.
146        assert_eq!(
147            json,
148            json!({
149                "$formatVersion": "0",
150                "members": {
151                    "Bp2HuBWdciXFKV2CnoC1Z4V44QfCmArCQHdzKpArYJc7": 1,
152                    "CxeKLJRofna6h2GqCsjdVwc2D7EdDFX8uDy9KGw3Ey68": 2,
153                },
154                "requiredPower": 2,
155            })
156        );
157        let recovered = Group::from_json(json).expect("from_json");
158        assert_eq!(original, recovered);
159    }
160
161    #[test]
162    fn value_round_trip_with_full_wire_shape() {
163        use crate::serialization::ValueConvertible;
164        let original = fixture();
165        let value = original.to_object().expect("to_object");
166        // `members` is `BTreeMap<Identifier, u32>`. platform_value renders
167        // the BTreeMap as a `Value::Map([(Identifier, U32), ...])` (NOT the
168        // textual-keyed `{ ... }` form, because the keys are Identifiers, not
169        // strings) and preserves the U32 variant on the values. We construct
170        // the expected map directly because `platform_value!{...}` only emits
171        // string-keyed Maps.
172        use platform_value::Value;
173        let expected = Value::Map(vec![
174            (
175                Value::Text("$formatVersion".to_string()),
176                Value::Text("0".to_string()),
177            ),
178            (
179                Value::Text("members".to_string()),
180                Value::Map(vec![
181                    (Value::Identifier([0xa0; 32]), Value::U32(1)),
182                    (Value::Identifier([0xb1; 32]), Value::U32(2)),
183                ]),
184            ),
185            (Value::Text("requiredPower".to_string()), Value::U32(2)),
186        ]);
187        assert_eq!(value, expected);
188        let recovered = Group::from_object(value).expect("from_object");
189        assert_eq!(original, recovered);
190    }
191}