diff --git a/packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/methods/mod.rs b/packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/accessors/mod.rs similarity index 100% rename from packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/methods/mod.rs rename to packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/accessors/mod.rs diff --git a/packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/methods/v0/mod.rs b/packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/accessors/v0/mod.rs similarity index 100% rename from packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/methods/v0/mod.rs rename to packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/accessors/v0/mod.rs diff --git a/packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/mod.rs b/packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/mod.rs index 2edbf6ac4f0..d12dcd3e4a8 100644 --- a/packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/mod.rs +++ b/packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/mod.rs @@ -4,7 +4,8 @@ use derive_more::From; use serde::{Deserialize, Serialize}; use std::fmt; -pub mod methods; +pub mod accessors; + pub mod v0; #[derive(Serialize, Deserialize, Encode, Decode, Debug, Clone, PartialEq, Eq, From)] diff --git a/packages/rs-dpp/src/errors/consensus/codes.rs b/packages/rs-dpp/src/errors/consensus/codes.rs index 177cfe8fd43..5d25bef191a 100644 --- a/packages/rs-dpp/src/errors/consensus/codes.rs +++ b/packages/rs-dpp/src/errors/consensus/codes.rs @@ -305,6 +305,7 @@ impl ErrorWithCode for StateError { Self::InvalidTokenClaimNoCurrentRewards(_) => 40716, Self::InvalidTokenClaimWrongClaimant(_) => 40717, Self::TokenTransferRecipientIdentityNotExistError(_) => 40718, + Self::PreProgrammedDistributionTimestampInPastError(_) => 40719, // Group errors: 40800-40899 Self::IdentityNotMemberOfGroupError(_) => 40800, diff --git a/packages/rs-dpp/src/errors/consensus/state/state_error.rs b/packages/rs-dpp/src/errors/consensus/state/state_error.rs index 09306815745..bdc0352eeab 100644 --- a/packages/rs-dpp/src/errors/consensus/state/state_error.rs +++ b/packages/rs-dpp/src/errors/consensus/state/state_error.rs @@ -42,7 +42,7 @@ use crate::consensus::state::identity::missing_transfer_key_error::MissingTransf use crate::consensus::state::identity::no_transfer_key_for_core_withdrawal_available_error::NoTransferKeyForCoreWithdrawalAvailableError; use crate::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_insufficient_error::PrefundedSpecializedBalanceInsufficientError; use crate::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_not_found_error::PrefundedSpecializedBalanceNotFoundError; -use crate::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, IdentityTokenAccountNotFrozenError, InvalidGroupPositionError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, NewTokensDestinationIdentityDoesNotExistError, TokenMintPastMaxSupplyError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, UnauthorizedTokenActionError, IdentityTokenAccountAlreadyFrozenError, TokenAlreadyPausedError, TokenIsPausedError, TokenNotPausedError, InvalidTokenClaimPropertyMismatch, InvalidTokenClaimNoCurrentRewards, InvalidTokenClaimWrongClaimant, TokenTransferRecipientIdentityNotExistError}; +use crate::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, IdentityTokenAccountNotFrozenError, InvalidGroupPositionError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, NewTokensDestinationIdentityDoesNotExistError, TokenMintPastMaxSupplyError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, UnauthorizedTokenActionError, IdentityTokenAccountAlreadyFrozenError, TokenAlreadyPausedError, TokenIsPausedError, TokenNotPausedError, InvalidTokenClaimPropertyMismatch, InvalidTokenClaimNoCurrentRewards, InvalidTokenClaimWrongClaimant, PreProgrammedDistributionTimestampInPastError, TokenTransferRecipientIdentityNotExistError}; use crate::consensus::state::voting::masternode_incorrect_voter_identity_id_error::MasternodeIncorrectVoterIdentityIdError; use crate::consensus::state::voting::masternode_incorrect_voting_address_error::MasternodeIncorrectVotingAddressError; use crate::consensus::state::voting::masternode_not_found_error::MasternodeNotFoundError; @@ -279,6 +279,9 @@ pub enum StateError { #[error(transparent)] TokenTransferRecipientIdentityNotExistError(TokenTransferRecipientIdentityNotExistError), + + #[error(transparent)] + PreProgrammedDistributionTimestampInPastError(PreProgrammedDistributionTimestampInPastError), } impl From for ConsensusError { diff --git a/packages/rs-dpp/src/errors/consensus/state/token/mod.rs b/packages/rs-dpp/src/errors/consensus/state/token/mod.rs index d68214c41a1..f1f45d675a4 100644 --- a/packages/rs-dpp/src/errors/consensus/state/token/mod.rs +++ b/packages/rs-dpp/src/errors/consensus/state/token/mod.rs @@ -10,6 +10,7 @@ mod new_authorized_action_taker_group_does_not_exist_error; mod new_authorized_action_taker_identity_does_not_exist_error; mod new_authorized_action_taker_main_group_not_set_error; mod new_tokens_destination_identity_does_not_exist_error; +mod pre_programmed_distribution_timestamp_in_past_error; mod token_already_paused_error; mod token_is_paused_error; mod token_mint_past_max_supply_error; @@ -30,6 +31,7 @@ pub use new_authorized_action_taker_group_does_not_exist_error::*; pub use new_authorized_action_taker_identity_does_not_exist_error::*; pub use new_authorized_action_taker_main_group_not_set_error::*; pub use new_tokens_destination_identity_does_not_exist_error::*; +pub use pre_programmed_distribution_timestamp_in_past_error::*; pub use token_already_paused_error::*; pub use token_is_paused_error::*; pub use token_mint_past_max_supply_error::*; diff --git a/packages/rs-dpp/src/errors/consensus/state/token/pre_programmed_distribution_timestamp_in_past_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/pre_programmed_distribution_timestamp_in_past_error.rs new file mode 100644 index 00000000000..9b13ea8df43 --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/state/token/pre_programmed_distribution_timestamp_in_past_error.rs @@ -0,0 +1,51 @@ +use crate::consensus::state::state_error::StateError; +use crate::consensus::ConsensusError; +use crate::data_contract::TokenContractPosition; +use crate::identity::TimestampMillis; +use crate::ProtocolError; +use bincode::{Decode, Encode}; +use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; +use platform_value::Identifier; +use thiserror::Error; + +#[derive( + Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, +)] +#[error( + "Pre-programmed token distribution time {pre_programmed_timestamp}ms is in the past for token {token_position} in data contract {data_contract_id}. Current block time is {current_timestamp}.", +)] +#[platform_serialize(unversioned)] +pub struct PreProgrammedDistributionTimestampInPastError { + data_contract_id: Identifier, + token_position: TokenContractPosition, + pre_programmed_timestamp: TimestampMillis, + current_timestamp: TimestampMillis, +} + +impl PreProgrammedDistributionTimestampInPastError { + pub fn new( + data_contract_id: Identifier, + token_position: TokenContractPosition, + pre_programmed_timestamp: TimestampMillis, + current_timestamp: TimestampMillis, + ) -> Self { + Self { + data_contract_id, + token_position, + pre_programmed_timestamp, + current_timestamp, + } + } + + pub fn token_position(&self) -> TokenContractPosition { + self.token_position + } +} + +impl From for ConsensusError { + fn from(err: PreProgrammedDistributionTimestampInPastError) -> Self { + Self::StateError(StateError::PreProgrammedDistributionTimestampInPastError( + err, + )) + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_create/state/v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_create/state/v0/mod.rs index e22a92ffe12..f002fe48f7f 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_create/state/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_create/state/v0/mod.rs @@ -5,6 +5,10 @@ use dpp::block::block_info::BlockInfo; use dpp::consensus::state::data_contract::data_contract_already_present_error::DataContractAlreadyPresentError; use dpp::consensus::state::state_error::StateError; +use dpp::consensus::state::token::PreProgrammedDistributionTimestampInPastError; +use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; +use dpp::data_contract::associated_token::token_distribution_rules::accessors::v0::TokenDistributionRulesV0Getters; +use dpp::data_contract::associated_token::token_pre_programmed_distribution::accessors::v0::TokenPreProgrammedDistributionV0Methods; use dpp::prelude::ConsensusValidationResult; use dpp::state_transition::data_contract_create_transition::accessors::DataContractCreateTransitionAccessorsV0; use dpp::state_transition::data_contract_create_transition::DataContractCreateTransition; @@ -63,6 +67,25 @@ impl DataContractCreateStateTransitionStateValidationV0 for DataContractCreateTr return Ok(action); } + // Validate token distribution rules + for (position, config) in self.data_contract().tokens() { + if let Some(distribution) = config.distribution_rules().pre_programmed_distribution() { + if let Some((timestamp, _)) = distribution.distributions().iter().next() { + if timestamp < &block_info.time_ms { + return Ok(ConsensusValidationResult::new_with_data_and_errors( + StateTransitionAction::BumpIdentityNonceAction( + BumpIdentityNonceAction::from_borrowed_data_contract_create_transition(self), + ), + vec![StateError::PreProgrammedDistributionTimestampInPastError( + PreProgrammedDistributionTimestampInPastError::new(self.data_contract().id(), *position, *timestamp, block_info.time_ms), + ) + .into()], + )); + } + } + } + } + let contract_fetch_info = platform.drive.get_contract_with_fetch_info_and_fee( self.data_contract().id().to_buffer(), Some(&block_info.epoch), diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_update/state/v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_update/state/v0/mod.rs index 10d3e2b1850..5b82d840181 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_update/state/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_update/state/v0/mod.rs @@ -5,16 +5,20 @@ use dpp::block::block_info::BlockInfo; use dpp::consensus::basic::document::DataContractNotPresentError; use dpp::consensus::basic::BasicError; - +use dpp::consensus::state::state_error::StateError; +use dpp::consensus::state::token::PreProgrammedDistributionTimestampInPastError; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::accessors::v1::{DataContractV1Getters, DataContractV1Setters}; +use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; +use dpp::data_contract::associated_token::token_distribution_rules::accessors::v0::TokenDistributionRulesV0Getters; +use dpp::data_contract::associated_token::token_pre_programmed_distribution::accessors::v0::TokenPreProgrammedDistributionV0Methods; use dpp::data_contract::validate_update::DataContractUpdateValidationMethodsV0; use dpp::prelude::ConsensusValidationResult; -use dpp::ProtocolError; - +use dpp::state_transition::data_contract_update_transition::accessors::DataContractUpdateTransitionAccessorsV0; use dpp::state_transition::data_contract_update_transition::DataContractUpdateTransition; use dpp::version::PlatformVersion; +use dpp::ProtocolError; use crate::error::execution::ExecutionError; use crate::execution::validation::state_transition::ValidationMode; @@ -26,6 +30,7 @@ use crate::execution::types::execution_operation::ValidationOperation; use crate::execution::types::state_transition_execution_context::{ StateTransitionExecutionContext, StateTransitionExecutionContextMethodsV0, }; +use drive::state_transition_action::system::bump_identity_nonce_action::BumpIdentityNonceAction; use drive::state_transition_action::StateTransitionAction; pub(in crate::execution::validation::state_transition::state_transitions::data_contract_update) trait DataContractUpdateStateTransitionStateValidationV0 { @@ -69,6 +74,25 @@ impl DataContractUpdateStateTransitionStateValidationV0 for DataContractUpdateTr return Ok(action); } + // Validate token distribution rules + for (position, config) in self.data_contract().tokens() { + if let Some(distribution) = config.distribution_rules().pre_programmed_distribution() { + if let Some((timestamp, _)) = distribution.distributions().iter().next() { + if timestamp < &block_info.time_ms { + return Ok(ConsensusValidationResult::new_with_data_and_errors( + StateTransitionAction::BumpIdentityDataContractNonceAction( + BumpIdentityDataContractNonceAction::from_borrowed_data_contract_update_transition(self), + ), + vec![StateError::PreProgrammedDistributionTimestampInPastError( + PreProgrammedDistributionTimestampInPastError::new(self.data_contract().id(), *position, *timestamp, block_info.time_ms), + ) + .into()], + )); + } + } + } + } + let state_transition_action = action.data.as_mut().ok_or(Error::Execution( ExecutionError::CorruptedCodeExecution( "we should always have an action at this point in data contract update", diff --git a/packages/rs-drive/src/drive/tokens/distribution/add_pre_programmed_distribution/v0/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/add_pre_programmed_distribution/v0/mod.rs index 6a80686e9bc..02851dfa2a3 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/add_pre_programmed_distribution/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/add_pre_programmed_distribution/v0/mod.rs @@ -17,7 +17,7 @@ use dpp::data_contract::associated_token::token_distribution_key::{ TokenDistributionKey, TokenDistributionType, }; use dpp::data_contract::associated_token::token_perpetual_distribution::distribution_recipient::TokenDistributionRecipient; -use dpp::data_contract::associated_token::token_pre_programmed_distribution::methods::v0::TokenPreProgrammedDistributionV0Methods; +use dpp::data_contract::associated_token::token_pre_programmed_distribution::accessors::v0::TokenPreProgrammedDistributionV0Methods; use dpp::data_contract::associated_token::token_pre_programmed_distribution::TokenPreProgrammedDistribution; use dpp::serialization::PlatformSerializable; use dpp::version::PlatformVersion; diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_claim_transition_action/v0/transformer.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_claim_transition_action/v0/transformer.rs index 3467688a8df..27839e1ef61 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_claim_transition_action/v0/transformer.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_claim_transition_action/v0/transformer.rs @@ -17,7 +17,7 @@ use dpp::data_contract::associated_token::token_perpetual_distribution::distribu use dpp::data_contract::associated_token::token_perpetual_distribution::distribution_recipient::{TokenDistributionRecipient, TokenDistributionResolvedRecipient}; use dpp::data_contract::associated_token::token_perpetual_distribution::methods::v0::{TokenPerpetualDistributionV0Accessors, TokenPerpetualDistributionV0Methods}; use dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment; -use dpp::data_contract::associated_token::token_pre_programmed_distribution::methods::v0::TokenPreProgrammedDistributionV0Methods; +use dpp::data_contract::associated_token::token_pre_programmed_distribution::accessors::v0::TokenPreProgrammedDistributionV0Methods; use dpp::identifier::Identifier; use dpp::state_transition::batch_transition::token_claim_transition::v0::TokenClaimTransitionV0; use dpp::ProtocolError; diff --git a/packages/wasm-dpp/src/errors/consensus/consensus_error.rs b/packages/wasm-dpp/src/errors/consensus/consensus_error.rs index 225082d7367..90a642897b0 100644 --- a/packages/wasm-dpp/src/errors/consensus/consensus_error.rs +++ b/packages/wasm-dpp/src/errors/consensus/consensus_error.rs @@ -84,7 +84,7 @@ use dpp::consensus::state::identity::no_transfer_key_for_core_withdrawal_availab use dpp::consensus::state::identity::RecipientIdentityDoesNotExistError; use dpp::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_insufficient_error::PrefundedSpecializedBalanceInsufficientError; use dpp::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_not_found_error::PrefundedSpecializedBalanceNotFoundError; -use dpp::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountNotFrozenError, IdentityTokenAccountFrozenError, TokenIsPausedError, IdentityTokenAccountAlreadyFrozenError, UnauthorizedTokenActionError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, TokenMintPastMaxSupplyError, NewTokensDestinationIdentityDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, InvalidGroupPositionError, TokenAlreadyPausedError, TokenNotPausedError, InvalidTokenClaimPropertyMismatch, InvalidTokenClaimNoCurrentRewards, InvalidTokenClaimWrongClaimant, TokenTransferRecipientIdentityNotExistError}; +use dpp::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountNotFrozenError, IdentityTokenAccountFrozenError, TokenIsPausedError, IdentityTokenAccountAlreadyFrozenError, UnauthorizedTokenActionError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, TokenMintPastMaxSupplyError, NewTokensDestinationIdentityDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, InvalidGroupPositionError, TokenAlreadyPausedError, TokenNotPausedError, InvalidTokenClaimPropertyMismatch, InvalidTokenClaimNoCurrentRewards, InvalidTokenClaimWrongClaimant, TokenTransferRecipientIdentityNotExistError, PreProgrammedDistributionTimestampInPastError}; use dpp::consensus::state::voting::masternode_incorrect_voter_identity_id_error::MasternodeIncorrectVoterIdentityIdError; use dpp::consensus::state::voting::masternode_incorrect_voting_address_error::MasternodeIncorrectVotingAddressError; use dpp::consensus::state::voting::masternode_not_found_error::MasternodeNotFoundError; @@ -387,6 +387,9 @@ pub fn from_state_error(state_error: &StateError) -> JsValue { StateError::TokenTransferRecipientIdentityNotExistError(e) => { generic_consensus_error!(TokenTransferRecipientIdentityNotExistError, e).into() } + StateError::PreProgrammedDistributionTimestampInPastError(e) => { + generic_consensus_error!(PreProgrammedDistributionTimestampInPastError, e).into() + } } }