Skip to main content

data_contracts/
lib.rs

1mod error;
2
3use serde_json::Value;
4
5use crate::error::Error;
6
7#[cfg(feature = "app-connect")]
8pub use app_connect_contract;
9
10#[cfg(feature = "dashpay")]
11pub use dashpay_contract;
12
13#[cfg(feature = "dpns")]
14pub use dpns_contract;
15
16#[cfg(feature = "keyword-search")]
17pub use keyword_search_contract;
18
19#[cfg(feature = "masternode-rewards")]
20pub use masternode_reward_shares_contract;
21
22#[cfg(feature = "moderation-charters")]
23pub use moderation_charters_contract;
24
25use platform_value::Identifier;
26use platform_version::version::PlatformVersion;
27
28#[cfg(feature = "document-history")]
29pub use document_history_contract;
30
31#[cfg(feature = "token-history")]
32pub use token_history_contract;
33
34#[cfg(feature = "wallet-utils")]
35pub use wallet_utils_contract;
36
37#[cfg(feature = "withdrawals")]
38pub use withdrawals_contract;
39
40#[repr(u8)]
41#[derive(PartialEq, Eq, Clone, Copy, Debug, Ord, PartialOrd, Hash)]
42pub enum SystemDataContract {
43    Withdrawals = 0,
44    MasternodeRewards = 1,
45    /// Reserved slot — the feature-flags contract was never deployed at genesis
46    /// and its implementation has been removed. The discriminant `2` is kept to
47    /// preserve the stable numbering of subsequent variants.
48    FeatureFlags = 2,
49    DPNS = 3,
50    Dashpay = 4,
51    WalletUtils = 5,
52    TokenHistory = 6,
53    KeywordSearch = 7,
54    DocumentHistory = 8,
55    AppConnect = 9,
56    /// The charters of elected moderation teams (protocol version 14). Registered from
57    /// protocol version 14 on: registered at genesis by chains born at 14 and inserted by the
58    /// upgrade to 14.
59    ModerationCharters = 10,
60}
61
62pub struct DataContractSource {
63    pub id_bytes: [u8; 32],
64    pub owner_id_bytes: [u8; 32],
65    pub version: u32,
66    pub definitions: Option<Value>,
67    pub document_schemas: Value,
68}
69
70impl SystemDataContract {
71    /// Every system data contract, including the reserved `FeatureFlags` slot.
72    ///
73    /// Deliberately kept beside the enum so that adding a variant and adding it here are the
74    /// same edit. `assert_every_variant_is_listed` below makes that mechanical rather than
75    /// remembered: a new variant makes its match non-exhaustive and the crate stops compiling.
76    pub const ALL: [SystemDataContract; 11] = [
77        SystemDataContract::Withdrawals,
78        SystemDataContract::MasternodeRewards,
79        SystemDataContract::FeatureFlags,
80        SystemDataContract::DPNS,
81        SystemDataContract::Dashpay,
82        SystemDataContract::WalletUtils,
83        SystemDataContract::TokenHistory,
84        SystemDataContract::KeywordSearch,
85        SystemDataContract::DocumentHistory,
86        SystemDataContract::AppConnect,
87        SystemDataContract::ModerationCharters,
88    ];
89
90    /// A new variant must also be added to [`SystemDataContract::ALL`]; this match is where the
91    /// compiler stops you, but it cannot check that list for you.
92    pub fn id(&self) -> Identifier {
93        let bytes = match self {
94            #[cfg(feature = "withdrawals")]
95            SystemDataContract::Withdrawals => withdrawals_contract::ID_BYTES,
96            #[cfg(not(feature = "withdrawals"))]
97            SystemDataContract::Withdrawals => [
98                54, 98, 187, 97, 225, 127, 174, 62, 162, 148, 207, 96, 49, 151, 251, 10, 171, 109,
99                81, 24, 11, 216, 182, 16, 76, 73, 68, 166, 47, 226, 217, 127,
100            ],
101
102            #[cfg(feature = "masternode-rewards")]
103            SystemDataContract::MasternodeRewards => masternode_reward_shares_contract::ID_BYTES,
104            #[cfg(not(feature = "masternode-rewards"))]
105            SystemDataContract::MasternodeRewards => [
106                12, 172, 226, 5, 36, 102, 147, 167, 200, 21, 101, 35, 98, 13, 170, 147, 125, 47,
107                34, 71, 147, 68, 99, 238, 176, 31, 247, 33, 149, 144, 149, 140,
108            ],
109
110            // Reserved: feature-flags contract was removed but the ID is kept for
111            // discriminant stability and to prevent reuse of a potentially meaningful
112            // Identifier on-chain.
113            SystemDataContract::FeatureFlags => [
114                245, 172, 216, 200, 193, 110, 185, 172, 40, 110, 7, 132, 190, 86, 127, 80, 9, 244,
115                86, 26, 243, 212, 255, 2, 91, 7, 90, 243, 68, 55, 152, 34,
116            ],
117
118            #[cfg(feature = "dpns")]
119            SystemDataContract::DPNS => dpns_contract::ID_BYTES,
120            #[cfg(not(feature = "dpns"))]
121            SystemDataContract::DPNS => [
122                230, 104, 198, 89, 175, 102, 174, 225, 231, 44, 24, 109, 222, 123, 91, 126, 10, 29,
123                113, 42, 9, 196, 13, 87, 33, 246, 34, 191, 83, 197, 49, 85,
124            ],
125
126            #[cfg(feature = "dashpay")]
127            SystemDataContract::Dashpay => dashpay_contract::ID_BYTES,
128            #[cfg(not(feature = "dashpay"))]
129            SystemDataContract::Dashpay => [
130                162, 161, 180, 172, 111, 239, 34, 234, 42, 26, 104, 232, 18, 54, 68, 179, 87, 135,
131                95, 107, 65, 44, 24, 16, 146, 129, 193, 70, 231, 178, 113, 188,
132            ],
133
134            #[cfg(feature = "wallet-utils")]
135            SystemDataContract::WalletUtils => wallet_utils_contract::ID_BYTES,
136            #[cfg(not(feature = "wallet-utils"))]
137            SystemDataContract::WalletUtils => [
138                92, 20, 14, 101, 92, 2, 101, 187, 194, 168, 8, 113, 109, 225, 132, 121, 133, 19,
139                89, 24, 173, 81, 205, 253, 11, 118, 102, 75, 169, 91, 163, 124,
140            ],
141
142            #[cfg(feature = "token-history")]
143            SystemDataContract::TokenHistory => token_history_contract::ID_BYTES,
144            #[cfg(not(feature = "token-history"))]
145            SystemDataContract::TokenHistory => [
146                45, 67, 89, 21, 34, 216, 145, 78, 156, 243, 17, 58, 202, 190, 13, 92, 61, 40, 122,
147                201, 84, 99, 187, 110, 233, 128, 63, 48, 172, 29, 210, 108,
148            ],
149
150            #[cfg(feature = "keyword-search")]
151            SystemDataContract::KeywordSearch => keyword_search_contract::ID_BYTES,
152            #[cfg(not(feature = "keyword-search"))]
153            SystemDataContract::KeywordSearch => [
154                161, 147, 167, 153, 40, 225, 219, 101, 50, 156, 28, 146, 150, 52, 114, 213, 56,
155                154, 106, 15, 79, 66, 18, 156, 94, 146, 216, 104, 140, 93, 170, 215,
156            ],
157
158            #[cfg(feature = "document-history")]
159            SystemDataContract::DocumentHistory => document_history_contract::ID_BYTES,
160            #[cfg(not(feature = "document-history"))]
161            SystemDataContract::DocumentHistory => [
162                88, 18, 140, 208, 179, 231, 242, 57, 225, 203, 4, 210, 245, 95, 136, 92, 160, 167,
163                112, 118, 173, 238, 83, 62, 234, 230, 222, 16, 231, 30, 99, 98,
164            ],
165
166            #[cfg(feature = "app-connect")]
167            SystemDataContract::AppConnect => app_connect_contract::ID_BYTES,
168            #[cfg(not(feature = "app-connect"))]
169            SystemDataContract::AppConnect => [
170                239, 150, 14, 165, 105, 114, 235, 173, 190, 248, 162, 126, 247, 218, 92, 129, 255,
171                75, 179, 138, 2, 150, 151, 69, 126, 36, 218, 66, 183, 155, 84, 183,
172            ],
173
174            #[cfg(feature = "moderation-charters")]
175            SystemDataContract::ModerationCharters => moderation_charters_contract::ID_BYTES,
176            #[cfg(not(feature = "moderation-charters"))]
177            SystemDataContract::ModerationCharters => [
178                197, 6, 230, 72, 106, 198, 82, 129, 253, 135, 43, 86, 185, 182, 17, 112, 164, 127,
179                96, 5, 107, 185, 156, 46, 14, 10, 109, 237, 77, 228, 248, 129,
180            ],
181        };
182        Identifier::new(bytes)
183    }
184    /// Returns [DataContractSource]
185    pub fn source(self, platform_version: &PlatformVersion) -> Result<DataContractSource, Error> {
186        match self {
187            #[cfg(feature = "withdrawals")]
188            SystemDataContract::Withdrawals => Ok(DataContractSource {
189                id_bytes: withdrawals_contract::ID_BYTES,
190                owner_id_bytes: withdrawals_contract::OWNER_ID_BYTES,
191                version: platform_version.system_data_contracts.withdrawals as u32,
192                definitions: withdrawals_contract::load_definitions(platform_version)?,
193                document_schemas: withdrawals_contract::load_documents_schemas(platform_version)?,
194            }),
195            #[cfg(not(feature = "withdrawals"))]
196            SystemDataContract::Withdrawals => Err(Error::ContractNotIncluded("withdrawals")),
197
198            #[cfg(feature = "masternode-rewards")]
199            SystemDataContract::MasternodeRewards => Ok(DataContractSource {
200                id_bytes: masternode_reward_shares_contract::ID_BYTES,
201                owner_id_bytes: masternode_reward_shares_contract::OWNER_ID_BYTES,
202                version: platform_version
203                    .system_data_contracts
204                    .masternode_reward_shares as u32,
205                definitions: masternode_reward_shares_contract::load_definitions(platform_version)?,
206                document_schemas: masternode_reward_shares_contract::load_documents_schemas(
207                    platform_version,
208                )?,
209            }),
210            #[cfg(not(feature = "masternode-rewards"))]
211            SystemDataContract::MasternodeRewards => {
212                Err(Error::ContractNotIncluded("masternode-rewards"))
213            }
214
215            // Reserved: feature-flags contract was removed. The variant exists only
216            // to preserve discriminant stability; loading it is not supported.
217            SystemDataContract::FeatureFlags => Err(Error::ContractReserved("feature-flags")),
218
219            #[cfg(feature = "dpns")]
220            SystemDataContract::DPNS => Ok(DataContractSource {
221                id_bytes: dpns_contract::ID_BYTES,
222                owner_id_bytes: dpns_contract::OWNER_ID_BYTES,
223                version: platform_version.system_data_contracts.dpns as u32,
224                definitions: dpns_contract::load_definitions(platform_version)?,
225                document_schemas: dpns_contract::load_documents_schemas(platform_version)?,
226            }),
227            #[cfg(not(feature = "dpns"))]
228            SystemDataContract::DPNS => Err(Error::ContractNotIncluded("dpns")),
229
230            #[cfg(feature = "dashpay")]
231            SystemDataContract::Dashpay => Ok(DataContractSource {
232                id_bytes: dashpay_contract::ID_BYTES,
233                owner_id_bytes: dashpay_contract::OWNER_ID_BYTES,
234                version: platform_version.system_data_contracts.dashpay as u32,
235                definitions: dashpay_contract::load_definitions(platform_version)?,
236                document_schemas: dashpay_contract::load_documents_schemas(platform_version)?,
237            }),
238            #[cfg(not(feature = "dashpay"))]
239            SystemDataContract::Dashpay => Err(Error::ContractNotIncluded("dashpay")),
240
241            #[cfg(feature = "wallet-utils")]
242            SystemDataContract::WalletUtils => Ok(DataContractSource {
243                id_bytes: wallet_utils_contract::ID_BYTES,
244                owner_id_bytes: wallet_utils_contract::OWNER_ID_BYTES,
245                version: platform_version.system_data_contracts.wallet as u32,
246                definitions: wallet_utils_contract::load_definitions(platform_version)?,
247                document_schemas: wallet_utils_contract::load_documents_schemas(platform_version)?,
248            }),
249            #[cfg(not(feature = "wallet-utils"))]
250            SystemDataContract::WalletUtils => Err(Error::ContractNotIncluded("wallet-utils")),
251
252            #[cfg(feature = "token-history")]
253            SystemDataContract::TokenHistory => Ok(DataContractSource {
254                id_bytes: token_history_contract::ID_BYTES,
255                owner_id_bytes: token_history_contract::OWNER_ID_BYTES,
256                version: platform_version.system_data_contracts.token_history as u32,
257                definitions: token_history_contract::load_definitions(platform_version)?,
258                document_schemas: token_history_contract::load_documents_schemas(platform_version)?,
259            }),
260            #[cfg(not(feature = "token-history"))]
261            SystemDataContract::TokenHistory => Err(Error::ContractNotIncluded("token-history")),
262
263            #[cfg(feature = "keyword-search")]
264            SystemDataContract::KeywordSearch => Ok(DataContractSource {
265                id_bytes: keyword_search_contract::ID_BYTES,
266                owner_id_bytes: keyword_search_contract::OWNER_ID_BYTES,
267                version: platform_version.system_data_contracts.keyword_search as u32,
268                definitions: keyword_search_contract::load_definitions(platform_version)?,
269                document_schemas: keyword_search_contract::load_documents_schemas(
270                    platform_version,
271                )?,
272            }),
273            #[cfg(not(feature = "keyword-search"))]
274            SystemDataContract::KeywordSearch => Err(Error::ContractNotIncluded("keyword-search")),
275
276            #[cfg(feature = "document-history")]
277            SystemDataContract::DocumentHistory => Ok(DataContractSource {
278                id_bytes: document_history_contract::ID_BYTES,
279                owner_id_bytes: document_history_contract::OWNER_ID_BYTES,
280                version: platform_version.system_data_contracts.document_history as u32,
281                definitions: document_history_contract::load_definitions(platform_version)?,
282                document_schemas: document_history_contract::load_documents_schemas(
283                    platform_version,
284                )?,
285            }),
286            #[cfg(not(feature = "document-history"))]
287            SystemDataContract::DocumentHistory => {
288                Err(Error::ContractNotIncluded("document-history"))
289            }
290
291            #[cfg(feature = "app-connect")]
292            SystemDataContract::AppConnect => Ok(DataContractSource {
293                id_bytes: app_connect_contract::ID_BYTES,
294                owner_id_bytes: app_connect_contract::OWNER_ID_BYTES,
295                version: platform_version.system_data_contracts.app_connect as u32,
296                definitions: app_connect_contract::load_definitions(platform_version)?,
297                document_schemas: app_connect_contract::load_documents_schemas(platform_version)?,
298            }),
299            #[cfg(not(feature = "app-connect"))]
300            SystemDataContract::AppConnect => Err(Error::ContractNotIncluded("app-connect")),
301
302            #[cfg(feature = "moderation-charters")]
303            SystemDataContract::ModerationCharters => Ok(DataContractSource {
304                id_bytes: moderation_charters_contract::ID_BYTES,
305                owner_id_bytes: moderation_charters_contract::OWNER_ID_BYTES,
306                version: platform_version.system_data_contracts.moderation_charters as u32,
307                definitions: moderation_charters_contract::load_definitions(platform_version)?,
308                document_schemas: moderation_charters_contract::load_documents_schemas(
309                    platform_version,
310                )?,
311            }),
312            #[cfg(not(feature = "moderation-charters"))]
313            SystemDataContract::ModerationCharters => {
314                Err(Error::ContractNotIncluded("moderation-charters"))
315            }
316        }
317    }
318}
319
320#[cfg(test)]
321mod tests {
322    use super::SystemDataContract;
323    use base58::FromBase58;
324
325    /// `id()` spells the published identifier of every system contract under both feature
326    /// settings: with a contract's feature on it reads that crate's constant, without it the
327    /// bytes copied above. A copy that drifts from the published id would give feature-less
328    /// builds a different contract id, which is exactly what happened to `KeywordSearch`
329    /// before this test existed. `FeatureFlags` has no crate and no published id; its bytes
330    /// are only a reserved slot.
331    #[test]
332    fn every_system_contract_id_matches_the_published_id() {
333        let published = |base58: &str| base58.from_base58().expect("the published id is base58");
334
335        for contract in SystemDataContract::ALL {
336            let expected = match contract {
337                SystemDataContract::Withdrawals => {
338                    published("4fJLR2GYTPFdomuTVvNy3VRrvWgvkKPzqehEBpNf2nk6")
339                }
340                SystemDataContract::MasternodeRewards => {
341                    published("rUnsWrFu3PKyRMGk2mxmZVBPbQuZx2qtHeFjURoQevX")
342                }
343                SystemDataContract::FeatureFlags => continue,
344                SystemDataContract::DPNS => {
345                    published("GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec")
346                }
347                SystemDataContract::Dashpay => {
348                    published("Bwr4WHCPz5rFVAD87RqTs3izo4zpzwsEdKPWUT1NS1C7")
349                }
350                SystemDataContract::WalletUtils => {
351                    published("7CSFGeF4WNzgDmx94zwvHkYaG3Dx4XEe5LFsFgJswLbm")
352                }
353                SystemDataContract::TokenHistory => {
354                    published("43gujrzZgXqcKBiScLa4T8XTDnRhenR9BLx8GWVHjPxF")
355                }
356                SystemDataContract::KeywordSearch => {
357                    published("BsjE6tQxG47wffZCRQCovFx5rYrAYYC3rTVRWKro27LA")
358                }
359                SystemDataContract::DocumentHistory => {
360                    published("6voHRaoiPcfmMhbqCA9dixH98xcgPQ9UEcuaXjpVu3LD")
361                }
362                SystemDataContract::AppConnect => {
363                    published("H8F9mP1BM55TE1ShsxPZHzhyinaMdY9bMmP85mkDhcJJ")
364                }
365                SystemDataContract::ModerationCharters => {
366                    published("EG7RGfV8fDTayC2FyVr8HwdpJh3fXDbVztcfE94UmN88")
367                }
368            };
369
370            assert_eq!(
371                contract.id().to_buffer().to_vec(),
372                expected,
373                "{contract:?} id does not match its published id"
374            );
375        }
376    }
377}