From 6f1ef287f9e23f167f70349ca4c3a51f7da35150 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 24 Sep 2026 13:40:49 +0700 Subject: [PATCH] refactor(dpp): remove the empty moderation charter validation step `SubmittedCharter::validate` dispatched on `validate_moderation_charter` to a v0 that checked nothing: the reward split's sum moved to the contract's `propertyConstraints` rule `rewardSplitIsWhole` and the description's cap to `maxBytes`, both checked wherever the document is validated. `validate_submitted_charter` only chained the property read with that empty step, and nothing outside rs-dpp's own tests called either. Remove the method, its v0 module, `validate_submitted_charter` and the `validate_moderation_charter` version entry. Tests read a proposal with `SubmittedCharter::from_document_properties`. Error 11001 keeps its place in `BasicError`, whose encoding is positional, and is still never produced. Co-Authored-By: Claude Opus 5.5 --- docs/protocol/moderation-charters.md | 6 +- ...rter_reward_split_not_one_hundred_error.rs | 4 +- packages/rs-dpp/src/moderation_charter/mod.rs | 66 ++----------------- .../rs-dpp/src/moderation_charter/tests.rs | 44 ++----------- .../rs-dpp/src/moderation_charter/v0/mod.rs | 13 ---- packages/rs-dpp/src/system_data_contracts.rs | 9 ++- .../dpp_validation_versions/mod.rs | 3 - .../dpp_validation_versions/v1.rs | 1 - .../dpp_validation_versions/v2.rs | 1 - .../dpp_validation_versions/v3.rs | 1 - .../dpp_validation_versions/v5.rs | 3 - .../rs-platform-version/src/version/v14.rs | 15 ++--- 12 files changed, 30 insertions(+), 136 deletions(-) delete mode 100644 packages/rs-dpp/src/moderation_charter/v0/mod.rs diff --git a/docs/protocol/moderation-charters.md b/docs/protocol/moderation-charters.md index 7b4147e9f29..c70553a6ca6 100644 --- a/docs/protocol/moderation-charters.md +++ b/docs/protocol/moderation-charters.md @@ -241,9 +241,9 @@ written, the description's 4096-byte cap and the reward split's sum included: `DocumentPropertyMaxBytesExceededError` (10421), and the `propertyConstraints` rule `rewardSplitIsWhole` refuses a split that does not add up to 100 with `DocumentPropertyConstraintViolatedError` (10422). The cap on additions is the -exception (see above). `validate_submitted_charter` in `rs-dpp` -(`packages/rs-dpp/src/moderation_charter/`) only reads a proposal, without -reading state: +exception (see above). `SubmittedCharter::from_document_properties` in +`rs-dpp` (`packages/rs-dpp/src/moderation_charter/`) only reads a proposal, +without reading state: | Rule | Error | Code | | --- | --- | --- | diff --git a/packages/rs-dpp/src/errors/consensus/basic/moderation_charter/moderation_charter_reward_split_not_one_hundred_error.rs b/packages/rs-dpp/src/errors/consensus/basic/moderation_charter/moderation_charter_reward_split_not_one_hundred_error.rs index fd2a376723f..c6ec6c8f878 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/moderation_charter/moderation_charter_reward_split_not_one_hundred_error.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/moderation_charter/moderation_charter_reward_split_not_one_hundred_error.rs @@ -10,8 +10,8 @@ use thiserror::Error; /// A proposal's reward split does not sum to 100 (11001). No longer produced: the /// moderation charters contract holds the split to 100 with its `propertyConstraints` rule /// `rewardSplitIsWhole`, refused as `DocumentPropertyConstraintViolatedError` (10422) on -/// every create, so `validate_submitted_charter` no longer checks it. The variant keeps its -/// place in `BasicError`, whose encoding is positional. +/// every create, and no charter-specific check is left to produce this one. The variant keeps +/// its place in `BasicError`, whose encoding is positional. #[derive( Error, Debug, diff --git a/packages/rs-dpp/src/moderation_charter/mod.rs b/packages/rs-dpp/src/moderation_charter/mod.rs index 4a78825cd1a..6088446c825 100644 --- a/packages/rs-dpp/src/moderation_charter/mod.rs +++ b/packages/rs-dpp/src/moderation_charter/mod.rs @@ -32,19 +32,15 @@ //! //! The schema carries almost every rule through its keywords (references, lookups, key //! requirements, `distinctFrom`, `maxBytes` for the description's byte cap, and the -//! `propertyConstraints` rule holding the reward split to 100). What it cannot say is here: -//! [`SubmittedCharter`] and [`ElectedCharter`] read the documents' properties, and -//! [`validate_submitted_charter`] reads a proposal. Nothing here reads state. - -mod v0; +//! `propertyConstraints` rule holding the reward split to 100). What is here only reads: +//! [`SubmittedCharter`] and [`ElectedCharter`] read the documents' properties. Nothing here +//! reads state. use crate::balances::credits::Credits; use crate::consensus::basic::moderation_charter::ModerationCharterMalformedFieldError; use crate::data_contract::document_type::contested_index_identifier; -use crate::validation::{ConsensusValidationResult, SimpleConsensusValidationResult}; -use crate::ProtocolError; +use crate::validation::ConsensusValidationResult; use platform_value::{Identifier, IdentifierBytes32, Value, ValueMap}; -use platform_version::version::PlatformVersion; use std::collections::{BTreeMap, BTreeSet}; /// The id of the moderation charters system contract, `EG7RGfV8fDTayC2FyVr8HwdpJh3fXDbVztcfE94UmN88`. @@ -227,8 +223,9 @@ impl SubmittedCharter { /// Reads a proposal out of the properties of a `submittedCharter` document. /// /// The result carries a consensus error, never a proposal, when a property is missing or - /// of the wrong type. The proposal's own rules are checked by - /// [`SubmittedCharter::validate`]; [`validate_submitted_charter`] does both. + /// of the wrong type. The proposal's rules are the contract's own keywords (the reward + /// split's `propertyConstraints` rule `rewardSplitIsWhole`, the description's `maxBytes`), + /// checked wherever the document is validated, not here. pub fn from_document_properties( properties: &BTreeMap, ) -> ConsensusValidationResult { @@ -325,33 +322,6 @@ impl SubmittedCharter { } properties } - - /// Checks the proposal's own rules. None is left: the reward split's sum is the - /// contract's `propertyConstraints` rule `rewardSplitIsWhole` and the description's - /// 4096-byte cap its `maxBytes`, both checked wherever the document is validated. The - /// versioned step stays for the path that seats a team. - pub fn validate( - &self, - platform_version: &PlatformVersion, - ) -> Result { - match platform_version - .dpp - .validation - .data_contract - .validate_moderation_charter - { - Some(0) => Ok(self.validate_v0()), - Some(version) => Err(ProtocolError::UnknownVersionMismatch { - method: "SubmittedCharter::validate".to_string(), - known_versions: vec![0], - received: version, - }), - None => Err(ProtocolError::NotSupported(format!( - "moderation charters do not exist at protocol version {}", - platform_version.protocol_version - ))), - } - } } impl ElectedCharter { @@ -428,27 +398,5 @@ impl ElectedCharter { } } -/// Reads a proposal out of the properties of a `submittedCharter` document and checks its own -/// rules. The result carries the proposal when it passes, and the first error it fails on -/// otherwise. -pub fn validate_submitted_charter( - properties: &BTreeMap, - platform_version: &PlatformVersion, -) -> Result, ProtocolError> { - let result = SubmittedCharter::from_document_properties(properties); - if !result.is_valid_with_data() { - return Ok(ConsensusValidationResult::new_with_errors(result.errors)); - } - let charter = result.into_data()?; - let validation = charter.validate(platform_version)?; - if validation.is_valid() { - Ok(ConsensusValidationResult::new_with_data(charter)) - } else { - Ok(ConsensusValidationResult::new_with_errors( - validation.errors, - )) - } -} - #[cfg(test)] mod tests; diff --git a/packages/rs-dpp/src/moderation_charter/tests.rs b/packages/rs-dpp/src/moderation_charter/tests.rs index b5f83a97c7e..34d3dc9e05c 100644 --- a/packages/rs-dpp/src/moderation_charter/tests.rs +++ b/packages/rs-dpp/src/moderation_charter/tests.rs @@ -1,14 +1,12 @@ use super::{ - charter_election_target, moderators_share_of, property_names, validate_submitted_charter, - ElectedCharter, ModerationCharterRewardSplit, SubmittedCharter, - ELECTED_CHARTER_DOCUMENT_TYPE_NAME, FULL_MODERATORS_SHARE, MODERATION_CHARTERS_CONTRACT_ID, - SUBMITTED_CHARTER_DOCUMENT_TYPE_NAME, + charter_election_target, moderators_share_of, property_names, ElectedCharter, + ModerationCharterRewardSplit, SubmittedCharter, ELECTED_CHARTER_DOCUMENT_TYPE_NAME, + FULL_MODERATORS_SHARE, MODERATION_CHARTERS_CONTRACT_ID, SUBMITTED_CHARTER_DOCUMENT_TYPE_NAME, }; use crate::balances::credits::MAX_CREDITS; use crate::consensus::basic::BasicError; use crate::consensus::ConsensusError; use platform_value::{Identifier, Value}; -use platform_version::version::PlatformVersion; fn proposal() -> SubmittedCharter { SubmittedCharter { @@ -24,15 +22,6 @@ fn proposal() -> SubmittedCharter { } } -fn first_basic_error( - result: &crate::validation::ConsensusValidationResult, -) -> &BasicError { - match result.errors.first() { - Some(ConsensusError::BasicError(error)) => error, - other => panic!("expected a basic error, got {other:?}"), - } -} - #[test] fn should_round_trip_a_proposal_through_its_document_properties() { let proposal = proposal(); @@ -66,27 +55,14 @@ fn should_read_a_zero_share_as_zero() { } #[test] -fn should_accept_a_proposal_without_reasons() { +fn should_read_a_proposal_without_reasons() { let proposal = SubmittedCharter { reasons: vec![], ..proposal() }; - let result = validate_submitted_charter( - &proposal.to_document_properties(), - PlatformVersion::latest(), - ) - .expect("validation executes"); - assert!(result.is_valid_with_data()); -} - -#[test] -fn should_accept_a_valid_proposal() { - let result = validate_submitted_charter( - &proposal().to_document_properties(), - PlatformVersion::latest(), - ) - .expect("validation executes"); - assert!(result.is_valid_with_data(), "{:?}", result.errors); + let read = SubmittedCharter::from_document_properties(&proposal.to_document_properties()); + assert!(read.is_valid_with_data(), "{:?}", read.errors); + assert_eq!(read.into_data().expect("data"), proposal); } #[test] @@ -124,12 +100,6 @@ fn should_refuse_a_missing_or_mistyped_property() { )); } -#[test] -fn should_refuse_to_validate_below_protocol_version_14() { - let platform_version = PlatformVersion::get(13).expect("version 13"); - assert!(proposal().validate(platform_version).is_err()); -} - #[test] fn should_round_trip_an_elected_charter_through_its_document_properties() { let charter = ElectedCharter { diff --git a/packages/rs-dpp/src/moderation_charter/v0/mod.rs b/packages/rs-dpp/src/moderation_charter/v0/mod.rs deleted file mode 100644 index 46fa958c365..00000000000 --- a/packages/rs-dpp/src/moderation_charter/v0/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::moderation_charter::SubmittedCharter; -use crate::validation::SimpleConsensusValidationResult; - -impl SubmittedCharter { - /// No rule is left for this step: the reward split's sum is the contract's - /// `propertyConstraints` rule `rewardSplitIsWhole` and the description's byte cap its - /// `maxBytes`, both checked wherever the document is validated, so every proposal read - /// from a document already meets them. - #[inline(always)] - pub(super) fn validate_v0(&self) -> SimpleConsensusValidationResult { - SimpleConsensusValidationResult::new() - } -} diff --git a/packages/rs-dpp/src/system_data_contracts.rs b/packages/rs-dpp/src/system_data_contracts.rs index adfebe26917..caf2d4108c2 100644 --- a/packages/rs-dpp/src/system_data_contracts.rs +++ b/packages/rs-dpp/src/system_data_contracts.rs @@ -327,8 +327,8 @@ mod moderation_charters_tests { use crate::document::{Document, DocumentV0Getters, DocumentV0Setters}; use crate::identity::Purpose; use crate::moderation_charter::{ - property_names, validate_submitted_charter, ElectedCharter, ModerationCharterRewardSplit, - SubmittedCharter, ADDED_MODERATOR_DOCUMENT_TYPE_NAME, ELECTED_CHARTER_DOCUMENT_TYPE_NAME, + property_names, ElectedCharter, ModerationCharterRewardSplit, SubmittedCharter, + ADDED_MODERATOR_DOCUMENT_TYPE_NAME, ELECTED_CHARTER_DOCUMENT_TYPE_NAME, JOIN_REQUEST_DOCUMENT_TYPE_NAME, MODERATION_CHARTERS_CONTRACT_ID, REASON_DOCUMENT_TYPE_NAME, REMOVED_MODERATOR_DOCUMENT_TYPE_NAME, RESIGNATION_REQUEST_DOCUMENT_TYPE_NAME, SUBMITTED_CHARTER_DOCUMENT_TYPE_NAME, @@ -711,10 +711,9 @@ mod moderation_charters_tests { vec![], "the encoded proposal passes the schema" ); - let read = validate_submitted_charter(document.properties(), PlatformVersion::latest()) - .expect("validation executes") + let read = SubmittedCharter::from_document_properties(document.properties()) .into_data() - .expect("the proposal is valid"); + .expect("the proposal reads"); assert_eq!(read, proposal); } diff --git a/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/mod.rs b/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/mod.rs index 75c4ae4182c..52c521e4309 100644 --- a/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/mod.rs +++ b/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/mod.rs @@ -45,9 +45,6 @@ pub struct DataContractValidationVersions { /// version 14: version 1 distribution rules and once-per-identity claims are rejected as /// unsupported, matching older software that can not decode them. pub validate_once_per_identity_distribution: OptionalFeatureVersion, - /// `ModerationCharter::validate`, the pure-data rules of a moderation charter. `None` below - /// protocol version 14, where the moderation charters system contract does not exist. - pub validate_moderation_charter: OptionalFeatureVersion, } #[derive(Clone, Debug, Default)] diff --git a/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v1.rs b/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v1.rs index 8cf7f3672b0..622b080100a 100644 --- a/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v1.rs +++ b/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v1.rs @@ -21,7 +21,6 @@ pub const DPP_VALIDATION_VERSIONS_V1: DPPValidationVersions = DPPValidationVersi validate_token_config_groups_exist: 0, validate_localizations: 0, validate_once_per_identity_distribution: None, - validate_moderation_charter: None, }, document_type: DocumentTypeValidationVersions { validate_update: 0, diff --git a/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v2.rs b/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v2.rs index 01c2a794c6f..4d23c704c0d 100644 --- a/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v2.rs +++ b/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v2.rs @@ -21,7 +21,6 @@ pub const DPP_VALIDATION_VERSIONS_V2: DPPValidationVersions = DPPValidationVersi validate_token_config_groups_exist: 0, validate_localizations: 0, validate_once_per_identity_distribution: None, - validate_moderation_charter: None, }, document_type: DocumentTypeValidationVersions { validate_update: 0, diff --git a/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v3.rs b/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v3.rs index 931bda8f923..a1053ad10b9 100644 --- a/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v3.rs +++ b/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v3.rs @@ -22,7 +22,6 @@ pub const DPP_VALIDATION_VERSIONS_V3: DPPValidationVersions = DPPValidationVersi validate_token_config_groups_exist: 0, validate_localizations: 0, validate_once_per_identity_distribution: None, - validate_moderation_charter: None, }, document_type: DocumentTypeValidationVersions { validate_update: 0, diff --git a/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v5.rs b/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v5.rs index 823fa9a7237..90ca0d7155b 100644 --- a/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v5.rs +++ b/packages/rs-platform-version/src/version/dpp_versions/dpp_validation_versions/v5.rs @@ -23,9 +23,6 @@ pub const DPP_VALIDATION_VERSIONS_V5: DPPValidationVersions = DPPValidationVersi data_contract: DataContractValidationVersions { validate_config_update: 2, validate_once_per_identity_distribution: Some(0), - // Moderation charters: the charter system contract and the pure-data rules of a - // charter exist from this protocol version on. - validate_moderation_charter: Some(0), ..DPP_VALIDATION_VERSIONS_V4.data_contract }, document_type: DocumentTypeValidationVersions { diff --git a/packages/rs-platform-version/src/version/v14.rs b/packages/rs-platform-version/src/version/v14.rs index b55e660d12d..700f10cf732 100644 --- a/packages/rs-platform-version/src/version/v14.rs +++ b/packages/rs-platform-version/src/version/v14.rs @@ -982,12 +982,10 @@ pub const PROTOCOL_VERSION_14: ProtocolVersion = 14; /// with `"resolution": 1`, the masternode vote without a Lock choice of /// item 23, so an elected charter create opens or joins the contest for /// its target. `SYSTEM_DATA_CONTRACT_VERSIONS_V3` registers it -/// (`moderation_charters: 1`), and -/// `DPP_VALIDATION_VERSIONS_V5.validate_moderation_charter = Some(0)` turns -/// on reading a proposal (basic error 11000), which holds no rule of its -/// own: the reward split sums to 100 through the contract's -/// `propertyConstraints` rule (item 39), so 11001 is no longer produced, -/// and the description fits 4096 bytes through the schema's own `maxBytes` +/// (`moderation_charters: 1`). A proposal holds no rule beyond its schema: +/// the reward split sums to 100 through the contract's +/// `propertyConstraints` rule (item 39), so 11001 is never produced, and +/// the description fits 4096 bytes through the schema's own `maxBytes` /// (item 38); every document validation checks both. Genesis registers it /// on chains born at this version (`create_genesis_state` v1, behind the /// app-connect branch), `transition_to_version_14` inserts it on upgrade, @@ -1041,8 +1039,9 @@ pub const PROTOCOL_VERSION_14: ProtocolVersion = 14; /// nothing stored. They are fixed when the document type is created: a /// changed `propertyConstraints` is an incompatible schema change on /// update. The moderation charters contract declares its first one: a -/// `submittedCharter`'s `rewardSplit` members add up to 100, which -/// `validate_submitted_charter` therefore no longer checks (11001). +/// `submittedCharter`'s `rewardSplit` members add up to 100, replacing the +/// charter-specific check, whose error 11001 keeps its place in +/// `BasicError` but is never produced. /// /// 40. **Elected moderation teams moderate from their stored charter**: seating /// writes nothing. Awarding the contest of item 37 writes the winning