@dashevo/moderation-charters-contract 4.2.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/lib.rs ADDED
@@ -0,0 +1,196 @@
1
+ mod error;
2
+ pub mod v1;
3
+
4
+ pub use crate::error::Error;
5
+ use platform_value::{Identifier, IdentifierBytes32};
6
+ use platform_version::version::PlatformVersion;
7
+ use serde_json::Value;
8
+
9
+ pub const ID_BYTES: [u8; 32] = [
10
+ 197, 6, 230, 72, 106, 198, 82, 129, 253, 135, 43, 86, 185, 182, 17, 112, 164, 127, 96, 5, 107,
11
+ 185, 156, 46, 14, 10, 109, 237, 77, 228, 248, 129,
12
+ ];
13
+
14
+ pub const OWNER_ID_BYTES: [u8; 32] = [0; 32];
15
+
16
+ pub const ID: Identifier = Identifier(IdentifierBytes32(ID_BYTES));
17
+ pub const OWNER_ID: Identifier = Identifier(IdentifierBytes32(OWNER_ID_BYTES));
18
+
19
+ /// The contract's definitions for `platform_version`. Version 0, the value the tables of
20
+ /// protocol versions below 14 carry, names no schema generation and is refused like any
21
+ /// unknown version: the contract does not exist before its activation.
22
+ pub fn load_definitions(platform_version: &PlatformVersion) -> Result<Option<Value>, Error> {
23
+ match platform_version.system_data_contracts.moderation_charters {
24
+ 1 => Ok(None),
25
+ version => Err(Error::UnknownVersionMismatch {
26
+ method: "moderation_charters_contract::load_definitions".to_string(),
27
+ known_versions: vec![1],
28
+ received: version,
29
+ }),
30
+ }
31
+ }
32
+
33
+ pub fn load_documents_schemas(platform_version: &PlatformVersion) -> Result<Value, Error> {
34
+ match platform_version.system_data_contracts.moderation_charters {
35
+ 1 => v1::load_documents_schemas(),
36
+ version => Err(Error::UnknownVersionMismatch {
37
+ method: "moderation_charters_contract::load_documents_schemas".to_string(),
38
+ known_versions: vec![1],
39
+ received: version,
40
+ }),
41
+ }
42
+ }
43
+
44
+ #[cfg(test)]
45
+ mod tests {
46
+ use base58::FromBase58;
47
+
48
+ use super::*;
49
+
50
+ #[test]
51
+ /// Ensure that the ID constant matches the expected value
52
+ /// and that it can be encoded to base58 correctly.
53
+ fn should_match_the_published_system_contract_id() {
54
+ assert_eq!(
55
+ ID,
56
+ Identifier(IdentifierBytes32(ID_BYTES)),
57
+ "ID should match the expected value"
58
+ );
59
+
60
+ let base58_decoded = "EG7RGfV8fDTayC2FyVr8HwdpJh3fXDbVztcfE94UmN88"
61
+ .from_base58()
62
+ .unwrap();
63
+ assert_eq!(
64
+ base58_decoded, ID_BYTES,
65
+ "ID should match the base58 decoded value"
66
+ );
67
+ }
68
+
69
+ #[test]
70
+ fn should_load_the_schema_at_the_latest_platform_version() {
71
+ use v1::document_types::{
72
+ added_moderator, elected_charter, join_request, reason, removed_moderator,
73
+ resignation_request, submitted_charter,
74
+ };
75
+
76
+ let schema = load_documents_schemas(PlatformVersion::latest()).expect("schema loads");
77
+ let declared = |name: &str, properties: &[&str], indexes: &[&str]| {
78
+ let document_type = schema
79
+ .get(name)
80
+ .unwrap_or_else(|| panic!("the {name} document type is declared"));
81
+ let declared_properties = document_type
82
+ .get("properties")
83
+ .and_then(Value::as_object)
84
+ .unwrap_or_else(|| panic!("{name} has properties"));
85
+ for property in properties {
86
+ assert!(
87
+ declared_properties.contains_key(*property),
88
+ "{name} declares {property}"
89
+ );
90
+ }
91
+ let declared_indexes: Vec<&str> = document_type
92
+ .get("indices")
93
+ .and_then(Value::as_array)
94
+ .unwrap_or_else(|| panic!("{name} has indexes"))
95
+ .iter()
96
+ .filter_map(|index| index.get("name").and_then(Value::as_str))
97
+ .collect();
98
+ assert_eq!(declared_indexes, indexes, "the indexes of {name}");
99
+ };
100
+
101
+ declared(
102
+ reason::NAME,
103
+ &[
104
+ reason::properties::CODE,
105
+ reason::properties::LABEL,
106
+ reason::properties::DESCRIPTION,
107
+ ],
108
+ &[reason::indexes::BY_OWNER_CODE],
109
+ );
110
+ declared(
111
+ submitted_charter::NAME,
112
+ &[
113
+ submitted_charter::properties::TARGET_CONTRACT_ID,
114
+ submitted_charter::properties::DESCRIPTION,
115
+ submitted_charter::properties::REASONS,
116
+ submitted_charter::properties::MODERATORS_SHARE,
117
+ submitted_charter::properties::REWARD_SPLIT,
118
+ ],
119
+ &[
120
+ submitted_charter::indexes::BY_TARGET_CONTRACT,
121
+ submitted_charter::indexes::BY_OWNER,
122
+ ],
123
+ );
124
+ declared(
125
+ join_request::NAME,
126
+ &[
127
+ join_request::properties::SUBMITTED_CHARTER_ID,
128
+ join_request::properties::RECIPIENT_ID,
129
+ join_request::properties::RECIPIENT_KEY_ID,
130
+ join_request::properties::SENDER_KEY_ID,
131
+ join_request::properties::ENCRYPTED_MESSAGE,
132
+ ],
133
+ &[
134
+ join_request::indexes::BY_SUBMITTED_CHARTER,
135
+ join_request::indexes::BY_OWNER,
136
+ ],
137
+ );
138
+ declared(
139
+ elected_charter::NAME,
140
+ &[
141
+ elected_charter::properties::TARGET_CONTRACT_ID,
142
+ elected_charter::properties::SUBMITTED_CHARTER_ID,
143
+ elected_charter::properties::MEMBERS,
144
+ ],
145
+ &[
146
+ elected_charter::indexes::BY_TARGET_CONTRACT,
147
+ elected_charter::indexes::BY_SUBMITTED_CHARTER,
148
+ ],
149
+ );
150
+ declared(
151
+ added_moderator::NAME,
152
+ &[
153
+ added_moderator::properties::ELECTED_CHARTER_ID,
154
+ added_moderator::properties::SUBMITTED_CHARTER_ID,
155
+ added_moderator::properties::MEMBER_ID,
156
+ ],
157
+ &[added_moderator::indexes::BY_ELECTED_CHARTER_MEMBER],
158
+ );
159
+ declared(
160
+ removed_moderator::NAME,
161
+ &[
162
+ removed_moderator::properties::ELECTED_CHARTER_ID,
163
+ removed_moderator::properties::MEMBER_ID,
164
+ ],
165
+ &[removed_moderator::indexes::BY_ELECTED_CHARTER_MEMBER],
166
+ );
167
+ declared(
168
+ resignation_request::NAME,
169
+ &[
170
+ resignation_request::properties::ELECTED_CHARTER_ID,
171
+ resignation_request::properties::RECIPIENT_ID,
172
+ resignation_request::properties::RECIPIENT_KEY_ID,
173
+ resignation_request::properties::SENDER_KEY_ID,
174
+ resignation_request::properties::ENCRYPTED_MESSAGE,
175
+ ],
176
+ &[resignation_request::indexes::BY_ELECTED_CHARTER_OWNER],
177
+ );
178
+ // Seven types and no others.
179
+ assert_eq!(schema.as_object().map(|types| types.len()), Some(7));
180
+ // Only the elected charter is contested, on its target.
181
+ assert!(schema[elected_charter::NAME]["indices"][0]["contested"].is_object());
182
+ }
183
+
184
+ #[test]
185
+ fn should_refuse_to_load_below_the_activation_version() {
186
+ let platform_version = PlatformVersion::get(13).expect("protocol version 13");
187
+ assert!(matches!(
188
+ load_documents_schemas(platform_version),
189
+ Err(Error::UnknownVersionMismatch { received: 0, .. })
190
+ ));
191
+ assert!(matches!(
192
+ load_definitions(platform_version),
193
+ Err(Error::UnknownVersionMismatch { received: 0, .. })
194
+ ));
195
+ }
196
+ }
package/src/v1/mod.rs ADDED
@@ -0,0 +1,146 @@
1
+ use crate::Error;
2
+ use serde_json::Value;
3
+
4
+ pub mod document_types {
5
+ /// A ground for a moderation action, keyed by its owner and a three-letter code. Immutable
6
+ /// and undeletable.
7
+ pub mod reason {
8
+ pub const NAME: &str = "reason";
9
+
10
+ pub mod properties {
11
+ pub const CODE: &str = "code";
12
+ pub const LABEL: &str = "label";
13
+ pub const DESCRIPTION: &str = "description";
14
+ }
15
+
16
+ pub mod indexes {
17
+ /// Unique on the owner and the code.
18
+ pub const BY_OWNER_CODE: &str = "byOwnerCode";
19
+ }
20
+ }
21
+
22
+ /// A leader's proposal to moderate one contract. Immutable and undeletable.
23
+ pub mod submitted_charter {
24
+ pub const NAME: &str = "submittedCharter";
25
+
26
+ pub mod properties {
27
+ pub const TARGET_CONTRACT_ID: &str = "targetContractId";
28
+ pub const DESCRIPTION: &str = "description";
29
+ pub const REASONS: &str = "reasons";
30
+ pub const MODERATORS_SHARE: &str = "moderatorsShare";
31
+ pub const REWARD_SPLIT: &str = "rewardSplit";
32
+
33
+ /// The keys of the `rewardSplit` object, three percentages summing to 100.
34
+ pub mod reward_split {
35
+ pub const LEADER: &str = "leader";
36
+ pub const EQUAL: &str = "equal";
37
+ pub const ACTIONS: &str = "actions";
38
+ }
39
+ }
40
+
41
+ pub mod indexes {
42
+ /// The proposals for a contract in filing order.
43
+ pub const BY_TARGET_CONTRACT: &str = "byTargetContract";
44
+ /// A leader's proposals.
45
+ pub const BY_OWNER: &str = "byOwner";
46
+ }
47
+ }
48
+
49
+ /// An identity's offer to serve on the team of a proposal, with a message only the
50
+ /// leader can read. Immutable and undeletable.
51
+ pub mod join_request {
52
+ pub const NAME: &str = "joinRequest";
53
+
54
+ pub mod properties {
55
+ pub const SUBMITTED_CHARTER_ID: &str = "submittedCharterId";
56
+ pub const RECIPIENT_ID: &str = "recipientId";
57
+ pub const RECIPIENT_KEY_ID: &str = "recipientKeyId";
58
+ pub const SENDER_KEY_ID: &str = "senderKeyId";
59
+ pub const ENCRYPTED_MESSAGE: &str = "encryptedMessage";
60
+ }
61
+
62
+ pub mod indexes {
63
+ /// Unique on the proposal and the owner: one offer per identity per proposal.
64
+ pub const BY_SUBMITTED_CHARTER: &str = "bySubmittedCharter";
65
+ /// An identity's offers.
66
+ pub const BY_OWNER: &str = "byOwner";
67
+ }
68
+ }
69
+
70
+ /// A proposal put to the vote with its team. Immutable and undeletable.
71
+ pub mod elected_charter {
72
+ pub const NAME: &str = "electedCharter";
73
+
74
+ pub mod properties {
75
+ pub const TARGET_CONTRACT_ID: &str = "targetContractId";
76
+ pub const SUBMITTED_CHARTER_ID: &str = "submittedCharterId";
77
+ pub const MEMBERS: &str = "members";
78
+ }
79
+
80
+ pub mod indexes {
81
+ /// The contested unique index keyed by the target contract: a create on it opens
82
+ /// or joins the contest for the target's seat, and only the winner is stored.
83
+ pub const BY_TARGET_CONTRACT: &str = "byTargetContract";
84
+ /// The elected charters of a proposal.
85
+ pub const BY_SUBMITTED_CHARTER: &str = "bySubmittedCharter";
86
+ }
87
+ }
88
+
89
+ /// A member the leader of a seated charter adds after the election. Immutable and
90
+ /// undeletable.
91
+ pub mod added_moderator {
92
+ pub const NAME: &str = "addedModerator";
93
+
94
+ pub mod properties {
95
+ pub const ELECTED_CHARTER_ID: &str = "electedCharterId";
96
+ pub const SUBMITTED_CHARTER_ID: &str = "submittedCharterId";
97
+ pub const MEMBER_ID: &str = "memberId";
98
+ }
99
+
100
+ pub mod indexes {
101
+ /// Unique on the charter and the member.
102
+ pub const BY_ELECTED_CHARTER_MEMBER: &str = "byElectedCharterMember";
103
+ }
104
+ }
105
+
106
+ /// A member the leader of a seated charter removes; final. Immutable and undeletable.
107
+ pub mod removed_moderator {
108
+ pub const NAME: &str = "removedModerator";
109
+
110
+ pub mod properties {
111
+ pub const ELECTED_CHARTER_ID: &str = "electedCharterId";
112
+ pub const MEMBER_ID: &str = "memberId";
113
+ }
114
+
115
+ pub mod indexes {
116
+ /// Unique on the charter and the member.
117
+ pub const BY_ELECTED_CHARTER_MEMBER: &str = "byElectedCharterMember";
118
+ }
119
+ }
120
+
121
+ /// A member asking to leave a seated team, with a message only the leader can read.
122
+ /// Immutable; deletable, which withdraws the request.
123
+ pub mod resignation_request {
124
+ pub const NAME: &str = "resignationRequest";
125
+
126
+ pub mod properties {
127
+ pub const ELECTED_CHARTER_ID: &str = "electedCharterId";
128
+ pub const RECIPIENT_ID: &str = "recipientId";
129
+ pub const RECIPIENT_KEY_ID: &str = "recipientKeyId";
130
+ pub const SENDER_KEY_ID: &str = "senderKeyId";
131
+ pub const ENCRYPTED_MESSAGE: &str = "encryptedMessage";
132
+ }
133
+
134
+ pub mod indexes {
135
+ /// Unique on the charter and the owner.
136
+ pub const BY_ELECTED_CHARTER_OWNER: &str = "byElectedCharterOwner";
137
+ }
138
+ }
139
+ }
140
+
141
+ pub fn load_documents_schemas() -> Result<Value, Error> {
142
+ serde_json::from_str(include_str!(
143
+ "../../schema/v1/moderation-charters-contract-documents.json"
144
+ ))
145
+ .map_err(Error::InvalidSchemaJson)
146
+ }
@@ -0,0 +1,30 @@
1
+ const sinon = require('sinon');
2
+ const sinonChai = require('sinon-chai');
3
+
4
+ const { expect, use } = require('chai');
5
+ const dirtyChai = require('dirty-chai');
6
+
7
+ const {
8
+ default: loadWasmDpp,
9
+ } = require('@dashevo/wasm-dpp');
10
+
11
+ use(dirtyChai);
12
+ use(sinonChai);
13
+
14
+ exports.mochaHooks = {
15
+ beforeAll: loadWasmDpp,
16
+
17
+ beforeEach() {
18
+ if (!this.sinon) {
19
+ this.sinon = sinon.createSandbox();
20
+ } else {
21
+ this.sinon.restore();
22
+ }
23
+ },
24
+
25
+ afterEach() {
26
+ this.sinon.restore();
27
+ },
28
+ };
29
+
30
+ global.expect = expect;