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