From 1f798f9ce71b33e5facb6b70d6f2563094ff0b07 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 21 Apr 2025 07:24:14 +0700 Subject: [PATCH 1/3] feat: token last claim query --- .../protos/platform/v0/platform.proto | 56 ++++++- .../distribution_function/mod.rs | 6 +- packages/rs-drive-abci/src/query/service.rs | 46 ++---- .../src/query/token_queries/mod.rs | 1 + .../mod.rs | 68 ++++++++ .../v0/mod.rs | 145 ++++++++++++++++++ .../mod.rs | 47 ++++++ .../v0/mod.rs | 91 ++++++++--- .../pre_programmed_distributions/v0/mod.rs | 4 +- .../drive/tokens/distribution/prove/mod.rs | 1 + .../mod.rs | 62 ++++++++ .../v0/mod.rs | 49 ++++++ .../pre_programmed_distributions/v0/mod.rs | 4 +- .../src/drive/tokens/distribution/queries.rs | 144 ++++++++++------- packages/rs-drive/src/verify/tokens/mod.rs | 1 + .../mod.rs | 76 +++++++++ .../v0/mod.rs | 65 ++++++++ .../v0/mod.rs | 4 +- .../drive_abci_query_versions/mod.rs | 1 + .../drive_abci_query_versions/v1.rs | 5 + .../drive_token_method_versions/mod.rs | 1 + .../drive_token_method_versions/v1.rs | 1 + .../drive_verify_method_versions/mod.rs | 1 + .../drive_verify_method_versions/v1.rs | 1 + .../src/version/mocks/v2_test.rs | 5 + 25 files changed, 762 insertions(+), 123 deletions(-) create mode 100644 packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/mod.rs create mode 100644 packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/v0/mod.rs create mode 100644 packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/mod.rs create mode 100644 packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/v0/mod.rs create mode 100644 packages/rs-drive/src/verify/tokens/verify_token_perpetual_distribution_last_paid_time/mod.rs create mode 100644 packages/rs-drive/src/verify/tokens/verify_token_perpetual_distribution_last_paid_time/v0/mod.rs diff --git a/packages/dapi-grpc/protos/platform/v0/platform.proto b/packages/dapi-grpc/protos/platform/v0/platform.proto index b6d924f9be0..88aba243f29 100644 --- a/packages/dapi-grpc/protos/platform/v0/platform.proto +++ b/packages/dapi-grpc/protos/platform/v0/platform.proto @@ -89,6 +89,9 @@ service Platform { rpc getTokenPreProgrammedDistributions( GetTokenPreProgrammedDistributionsRequest) returns (GetTokenPreProgrammedDistributionsResponse); + rpc getTokenPerpetualDistributionLastClaim( + GetTokenPerpetualDistributionLastClaimRequest) + returns (GetTokenPerpetualDistributionLastClaimResponse); rpc getTokenTotalSupply(GetTokenTotalSupplyRequest) returns (GetTokenTotalSupplyResponse); rpc getGroupInfo(GetGroupInfoRequest) returns (GetGroupInfoResponse); @@ -1476,12 +1479,63 @@ message GetTokenPreProgrammedDistributionsResponse { oneof version { GetTokenPreProgrammedDistributionsResponseV0 v0 = 1; } } +// ────────────────────────────────────────────────────────────────────────────── +// Get the last‑claim information for a token’s perpetual distribution +// ────────────────────────────────────────────────────────────────────────────── + +message GetTokenPerpetualDistributionLastClaimRequest { + + message ContractTokenInfo { + bytes contract_id = 1; + // the token contract position + uint32 token_contract_position = 2; + } + + message GetTokenPerpetualDistributionLastClaimRequestV0 { + // 32‑byte token identifier + bytes token_id = 1; + + // This should be set if you wish to get back the last claim info as a specific type + optional ContractTokenInfo contract_info = 2; + + // Identity whose last‑claim timestamp is requested + bytes identity_id = 4; + // Return GroveDB / signature proof instead of raw value + bool prove = 5; + } + + oneof version { GetTokenPerpetualDistributionLastClaimRequestV0 v0 = 1; } +} + +message GetTokenPerpetualDistributionLastClaimResponse { + message GetTokenPerpetualDistributionLastClaimResponseV0 { + message LastClaimInfo { + + oneof paid_at { + uint64 timestamp_ms = 1 [ jstype = JS_STRING ]; // Unix epoch, ms + uint64 block_height = 2 [ jstype = JS_STRING ]; // Core‑block height + uint32 epoch = 3; // Epoch index + bytes raw_bytes = 4; // Arbitrary encoding + } + } + + oneof result { + LastClaimInfo last_claim = 1; // Direct answer + Proof proof = 2; // GroveDB / quorum proof + } + + ResponseMetadata metadata = 3; // Chain context + } + + oneof version { GetTokenPerpetualDistributionLastClaimResponseV0 v0 = 1; } +} + message GetTokenTotalSupplyRequest { message GetTokenTotalSupplyRequestV0 { bytes token_id = 1; bool prove = 2; } - oneof version { GetTokenTotalSupplyRequestV0 v0 = 1; } + oneof version {GetTokenTotalSupplyRequestV0 v0 = 1;} } message GetTokenTotalSupplyResponse { diff --git a/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/mod.rs b/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/mod.rs index 3ea95c7db8b..fe3775fb4c3 100644 --- a/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/mod.rs +++ b/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/mod.rs @@ -168,7 +168,7 @@ pub enum DistributionFunction { /// The emission at period `x` is given by: /// /// ```text - /// f(x) = (a * (x - start_moment) / d) + starting_amount + /// f(x) = (a * (x - start_step) / d) + starting_amount /// ``` /// /// # Parameters @@ -372,7 +372,7 @@ pub enum DistributionFunction { /// The emission at period `x` is given by: /// /// ```text - /// f(x) = (a * e^(m * (x - s) / n)) / d + b + /// f(x) = (a * e^(m * (x - s + o) / n)) / d + b /// ``` /// /// # Parameters @@ -431,7 +431,7 @@ pub enum DistributionFunction { /// The emission at period `x` is computed as: /// /// ```text - /// f(x) = (a * log(m * (x - s + o) / n)) / d + b + /// f(x) = (a * ln(m * (x - s + o) / n)) / d + b /// ``` /// /// # Parameters diff --git a/packages/rs-drive-abci/src/query/service.rs b/packages/rs-drive-abci/src/query/service.rs index 3e6b2d876a2..605221cda02 100644 --- a/packages/rs-drive-abci/src/query/service.rs +++ b/packages/rs-drive-abci/src/query/service.rs @@ -11,42 +11,7 @@ use async_trait::async_trait; use dapi_grpc::drive::v0::drive_internal_server::DriveInternal; use dapi_grpc::drive::v0::{GetProofsRequest, GetProofsResponse}; use dapi_grpc::platform::v0::platform_server::Platform as PlatformService; -use dapi_grpc::platform::v0::{ - BroadcastStateTransitionRequest, BroadcastStateTransitionResponse, GetConsensusParamsRequest, - GetConsensusParamsResponse, GetContestedResourceIdentityVotesRequest, - GetContestedResourceIdentityVotesResponse, GetContestedResourceVoteStateRequest, - GetContestedResourceVoteStateResponse, GetContestedResourceVotersForIdentityRequest, - GetContestedResourceVotersForIdentityResponse, GetContestedResourcesRequest, - GetContestedResourcesResponse, GetCurrentQuorumsInfoRequest, GetCurrentQuorumsInfoResponse, - GetDataContractHistoryRequest, GetDataContractHistoryResponse, GetDataContractRequest, - GetDataContractResponse, GetDataContractsRequest, GetDataContractsResponse, - GetDocumentsRequest, GetDocumentsResponse, GetEpochsInfoRequest, GetEpochsInfoResponse, - GetEvonodesProposedEpochBlocksByIdsRequest, GetEvonodesProposedEpochBlocksByRangeRequest, - GetEvonodesProposedEpochBlocksResponse, GetGroupActionSignersRequest, - GetGroupActionSignersResponse, GetGroupActionsRequest, GetGroupActionsResponse, - GetGroupInfoRequest, GetGroupInfoResponse, GetGroupInfosRequest, GetGroupInfosResponse, - GetIdentitiesBalancesRequest, GetIdentitiesBalancesResponse, GetIdentitiesContractKeysRequest, - GetIdentitiesContractKeysResponse, GetIdentitiesTokenBalancesRequest, - GetIdentitiesTokenBalancesResponse, GetIdentitiesTokenInfosRequest, - GetIdentitiesTokenInfosResponse, GetIdentityBalanceAndRevisionRequest, - GetIdentityBalanceAndRevisionResponse, GetIdentityBalanceRequest, GetIdentityBalanceResponse, - GetIdentityByNonUniquePublicKeyHashRequest, GetIdentityByNonUniquePublicKeyHashResponse, - GetIdentityByPublicKeyHashRequest, GetIdentityByPublicKeyHashResponse, - GetIdentityContractNonceRequest, GetIdentityContractNonceResponse, GetIdentityKeysRequest, - GetIdentityKeysResponse, GetIdentityNonceRequest, GetIdentityNonceResponse, GetIdentityRequest, - GetIdentityResponse, GetIdentityTokenBalancesRequest, GetIdentityTokenBalancesResponse, - GetIdentityTokenInfosRequest, GetIdentityTokenInfosResponse, GetPathElementsRequest, - GetPathElementsResponse, GetPrefundedSpecializedBalanceRequest, - GetPrefundedSpecializedBalanceResponse, GetProtocolVersionUpgradeStateRequest, - GetProtocolVersionUpgradeStateResponse, GetProtocolVersionUpgradeVoteStatusRequest, - GetProtocolVersionUpgradeVoteStatusResponse, GetStatusRequest, GetStatusResponse, - GetTokenDirectPurchasePricesRequest, GetTokenDirectPurchasePricesResponse, - GetTokenPreProgrammedDistributionsRequest, GetTokenPreProgrammedDistributionsResponse, - GetTokenStatusesRequest, GetTokenStatusesResponse, GetTokenTotalSupplyRequest, - GetTokenTotalSupplyResponse, GetTotalCreditsInPlatformRequest, - GetTotalCreditsInPlatformResponse, GetVotePollsByEndDateRequest, GetVotePollsByEndDateResponse, - WaitForStateTransitionResultRequest, WaitForStateTransitionResultResponse, -}; +use dapi_grpc::platform::v0::{BroadcastStateTransitionRequest, BroadcastStateTransitionResponse, GetConsensusParamsRequest, GetConsensusParamsResponse, GetContestedResourceIdentityVotesRequest, GetContestedResourceIdentityVotesResponse, GetContestedResourceVoteStateRequest, GetContestedResourceVoteStateResponse, GetContestedResourceVotersForIdentityRequest, GetContestedResourceVotersForIdentityResponse, GetContestedResourcesRequest, GetContestedResourcesResponse, GetCurrentQuorumsInfoRequest, GetCurrentQuorumsInfoResponse, GetDataContractHistoryRequest, GetDataContractHistoryResponse, GetDataContractRequest, GetDataContractResponse, GetDataContractsRequest, GetDataContractsResponse, GetDocumentsRequest, GetDocumentsResponse, GetEpochsInfoRequest, GetEpochsInfoResponse, GetEvonodesProposedEpochBlocksByIdsRequest, GetEvonodesProposedEpochBlocksByRangeRequest, GetEvonodesProposedEpochBlocksResponse, GetGroupActionSignersRequest, GetGroupActionSignersResponse, GetGroupActionsRequest, GetGroupActionsResponse, GetGroupInfoRequest, GetGroupInfoResponse, GetGroupInfosRequest, GetGroupInfosResponse, GetIdentitiesBalancesRequest, GetIdentitiesBalancesResponse, GetIdentitiesContractKeysRequest, GetIdentitiesContractKeysResponse, GetIdentitiesTokenBalancesRequest, GetIdentitiesTokenBalancesResponse, GetIdentitiesTokenInfosRequest, GetIdentitiesTokenInfosResponse, GetIdentityBalanceAndRevisionRequest, GetIdentityBalanceAndRevisionResponse, GetIdentityBalanceRequest, GetIdentityBalanceResponse, GetIdentityByNonUniquePublicKeyHashRequest, GetIdentityByNonUniquePublicKeyHashResponse, GetIdentityByPublicKeyHashRequest, GetIdentityByPublicKeyHashResponse, GetIdentityContractNonceRequest, GetIdentityContractNonceResponse, GetIdentityKeysRequest, GetIdentityKeysResponse, GetIdentityNonceRequest, GetIdentityNonceResponse, GetIdentityRequest, GetIdentityResponse, GetIdentityTokenBalancesRequest, GetIdentityTokenBalancesResponse, GetIdentityTokenInfosRequest, GetIdentityTokenInfosResponse, GetPathElementsRequest, GetPathElementsResponse, GetPrefundedSpecializedBalanceRequest, GetPrefundedSpecializedBalanceResponse, GetProtocolVersionUpgradeStateRequest, GetProtocolVersionUpgradeStateResponse, GetProtocolVersionUpgradeVoteStatusRequest, GetProtocolVersionUpgradeVoteStatusResponse, GetStatusRequest, GetStatusResponse, GetTokenDirectPurchasePricesRequest, GetTokenDirectPurchasePricesResponse, GetTokenPerpetualDistributionLastClaimRequest, GetTokenPerpetualDistributionLastClaimResponse, GetTokenPreProgrammedDistributionsRequest, GetTokenPreProgrammedDistributionsResponse, GetTokenStatusesRequest, GetTokenStatusesResponse, GetTokenTotalSupplyRequest, GetTokenTotalSupplyResponse, GetTotalCreditsInPlatformRequest, GetTotalCreditsInPlatformResponse, GetVotePollsByEndDateRequest, GetVotePollsByEndDateResponse, WaitForStateTransitionResultRequest, WaitForStateTransitionResultResponse}; use dapi_grpc::tonic::{Code, Request, Response, Status}; use dpp::version::PlatformVersion; use std::fmt::Debug; @@ -764,6 +729,15 @@ impl PlatformService for QueryService { ) .await } + + async fn get_token_perpetual_distribution_last_claim(&self, request: Request) -> Result, Status> { + self.handle_blocking_query( + request, + Platform::::query_token_perpetual_distribution_last_claim, + "get_token_perpetual_distribution_last_claim", + ) + .await + } } #[async_trait] diff --git a/packages/rs-drive-abci/src/query/token_queries/mod.rs b/packages/rs-drive-abci/src/query/token_queries/mod.rs index 6a5a0ff1fd0..9504519b67e 100644 --- a/packages/rs-drive-abci/src/query/token_queries/mod.rs +++ b/packages/rs-drive-abci/src/query/token_queries/mod.rs @@ -6,3 +6,4 @@ mod token_direct_purchase_prices; mod token_pre_programmed_distributions; mod token_status; mod token_total_supply; +mod token_perpetual_distribution_last_claim; diff --git a/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/mod.rs b/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/mod.rs new file mode 100644 index 00000000000..1d74c0cbd56 --- /dev/null +++ b/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/mod.rs @@ -0,0 +1,68 @@ +use crate::error::query::QueryError; +use crate::error::Error; +use crate::platform_types::platform::Platform; +use crate::platform_types::platform_state::PlatformState; +use crate::query::QueryValidationResult; + +use dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_request::Version as RequestVersion; +use dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::Version as ResponseVersion; +use dapi_grpc::platform::v0::{ + GetTokenPerpetualDistributionLastClaimRequest, + GetTokenPerpetualDistributionLastClaimResponse, +}; +use dpp::version::PlatformVersion; + +mod v0; + +impl Platform { + /// Query the last perpetual distribution claim for a given token and identity + pub fn query_token_perpetual_distribution_last_claim( + &self, + GetTokenPerpetualDistributionLastClaimRequest { version }: GetTokenPerpetualDistributionLastClaimRequest, + platform_state: &PlatformState, + platform_version: &PlatformVersion, + ) -> Result, Error> { + let Some(version) = version else { + return Ok(QueryValidationResult::new_with_error( + QueryError::DecodingError( + "could not decode token perpetual distribution last claim query".to_string(), + ), + )); + }; + + let feature_version_bounds = &platform_version + .drive_abci + .query + .token_queries + .token_perpetual_distribution_last_claim; + + let feature_version = match &version { + RequestVersion::V0(_) => 0, + }; + + if !feature_version_bounds.check_version(feature_version) { + return Ok(QueryValidationResult::new_with_error( + QueryError::UnsupportedQueryVersion( + "token_perpetual_distribution_last_claim".to_string(), + feature_version_bounds.min_version, + feature_version_bounds.max_version, + platform_version.protocol_version, + feature_version, + ), + )); + } + + match version { + RequestVersion::V0(request_v0) => { + let result = self.query_token_perpetual_distribution_last_claim_v0( + request_v0, + platform_state, + platform_version, + )?; + Ok(result.map(|response_v0| GetTokenPerpetualDistributionLastClaimResponse { + version: Some(ResponseVersion::V0(response_v0)), + })) + } + } + } +} \ No newline at end of file diff --git a/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/v0/mod.rs b/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/v0/mod.rs new file mode 100644 index 00000000000..5f30ff0dfb8 --- /dev/null +++ b/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/v0/mod.rs @@ -0,0 +1,145 @@ +use crate::error::query::QueryError; +use crate::error::Error; +use crate::platform_types::platform::Platform; +use crate::platform_types::platform_state::PlatformState; +use crate::query::QueryValidationResult; + +use dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_request::{ContractTokenInfo, GetTokenPerpetualDistributionLastClaimRequestV0}; +use dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::{ + get_token_perpetual_distribution_last_claim_response_v0, + GetTokenPerpetualDistributionLastClaimResponseV0, +}; +use dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::get_token_perpetual_distribution_last_claim_response_v0::{last_claim_info, LastClaimInfo}; +use dpp::check_validation_result_with_data; +use dpp::data_contract::accessors::v1::DataContractV1Getters; +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_perpetual_distribution::methods::v0::TokenPerpetualDistributionV0Accessors; +use dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment; +use dpp::identifier::Identifier; +use dpp::validation::ValidationResult; +use dpp::version::PlatformVersion; + +impl Platform { + pub(super) fn query_token_perpetual_distribution_last_claim_v0( + &self, + GetTokenPerpetualDistributionLastClaimRequestV0 { + token_id, + contract_info, + identity_id, + prove, + }: GetTokenPerpetualDistributionLastClaimRequestV0, + platform_state: &PlatformState, + platform_version: &PlatformVersion, + ) -> Result, Error> { + // ── Basic argument validation ────────────────────────────────────────── + let token_id: Identifier = + check_validation_result_with_data!(token_id.try_into().map_err(|_| { + QueryError::InvalidArgument( + "token_id must be a valid identifier (32 bytes long)".to_string(), + ) + })); + + let identity_id: Identifier = + check_validation_result_with_data!(identity_id.try_into().map_err(|_| { + QueryError::InvalidArgument( + "identity_id must be a valid identifier (32 bytes long)".to_string(), + ) + })); + + let response = if prove { + let proof = check_validation_result_with_data!(self + .drive + .prove_perpetual_distribution_last_paid_moment( + token_id.into_buffer(), + identity_id, + None, + platform_version, + )); + + GetTokenPerpetualDistributionLastClaimResponseV0 { + result: Some( + get_token_perpetual_distribution_last_claim_response_v0::Result::Proof( + self.response_proof_v0(platform_state, proof), + ), + ), + metadata: Some(self.response_metadata_v0(platform_state)), + } + } else if let Some(ContractTokenInfo{ contract_id, token_contract_position }) = contract_info { + let contract_id: Identifier = + check_validation_result_with_data!(contract_id.try_into().map_err(|_| { + QueryError::InvalidArgument( + "contract_id must be a valid identifier (32 bytes long)".to_string(), + ) + })); + let Some(contract) = check_validation_result_with_data!(self.drive.get_contract_with_fetch_info(contract_id.into_buffer(), false, None, platform_version).map_err( + QueryError::Drive + )) else { + return Ok(QueryValidationResult::new_with_error(QueryError::NotFound(format!("contract with identifier {} not found", contract_id)))); + }; + + if token_contract_position > u16::MAX as u32 { + return Ok(QueryValidationResult::new_with_error(QueryError::InvalidArgument("token_contract_position must be less than u16::MAX".to_string()))); + } + + let token = check_validation_result_with_data!(contract.contract.expected_token_configuration(token_contract_position as u16).map_err(QueryError::Protocol)); + let token_distribution_rules = token.distribution_rules(); + let Some(token_perpetual_distribution_rules) = token_distribution_rules.perpetual_distribution() else { + return Ok(QueryValidationResult::new_with_error(QueryError::InvalidArgument(format!("contract with identifier {} does not have perpetual distribution rules", contract_id)))); + }; + + let paid_at = self + .drive + .fetch_perpetual_distribution_last_paid_moment( + token_id.into_buffer(), + identity_id, + token_perpetual_distribution_rules.distribution_type(), + None, + platform_version, + )?.map(|moment| { + match moment { + RewardDistributionMoment::BlockBasedMoment(height) => last_claim_info::PaidAt::BlockHeight(height), + RewardDistributionMoment::TimeBasedMoment(timestamp) => last_claim_info::PaidAt::TimestampMs(timestamp), + RewardDistributionMoment::EpochBasedMoment(epoch) => last_claim_info::PaidAt::Epoch(epoch as u32), + } + }); + + + + GetTokenPerpetualDistributionLastClaimResponseV0 { + result: Some( + get_token_perpetual_distribution_last_claim_response_v0::Result::LastClaim( + LastClaimInfo { + paid_at, + } + ), + ), + metadata: Some(self.response_metadata_v0(platform_state)), + } + } else { + let paid_at = self + .drive + .fetch_perpetual_distribution_last_paid_moment_raw( + token_id.into_buffer(), + identity_id, + None, + platform_version, + )?.map(|moment| { + last_claim_info::PaidAt::RawBytes(moment) + }); + + GetTokenPerpetualDistributionLastClaimResponseV0 { + result: Some( + get_token_perpetual_distribution_last_claim_response_v0::Result::LastClaim( + LastClaimInfo { + paid_at, + }, + ), + ), + metadata: Some(self.response_metadata_v0(platform_state)), + } + }; + + Ok(QueryValidationResult::new_with_data(response)) + } +} \ No newline at end of file diff --git a/packages/rs-drive/src/drive/tokens/distribution/fetch/perpetual_distribution_last_paid_moment/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/fetch/perpetual_distribution_last_paid_moment/mod.rs index 23e94e8b071..e32f0e9e2c4 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/fetch/perpetual_distribution_last_paid_moment/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/fetch/perpetual_distribution_last_paid_moment/mod.rs @@ -46,6 +46,53 @@ impl Drive { ) } + /// Fetches the last paid moment as raw bytes for a perpetual distribution for a given identity, + /// using the appropriate versioned method. + /// + /// This method queries the perpetual distributions tree at the path + /// `perpetual_distribution_last_paid_time_path_vec(token_id, identity_id)`. + /// + /// # Parameters + /// + /// - `token_id`: The 32‑byte identifier for the token. + /// - `identity_id`: The identifier of the identity whose last paid time is being queried. + /// - `transaction`: The current GroveDB transaction. + /// - `platform_version`: The platform version to determine the method variant. + /// + /// # Returns + /// + /// - `Ok(Some(Vec))`: The raw stored bytes if a moment exists. + /// - `Ok(None)`: If no moment is recorded for the identity. + /// - `Err(_)`: If an internal GroveDB or decoding error occurs. + pub fn fetch_perpetual_distribution_last_paid_moment_raw( + &self, + token_id: [u8; 32], + identity_id: Identifier, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result>, Error> { + match platform_version + .drive + .methods + .token + .fetch + .perpetual_distribution_last_paid_time + { + 0 => self.fetch_perpetual_distribution_last_paid_moment_raw_operations_v0( + token_id, + identity_id, + &mut vec![], + transaction, + platform_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "fetch_perpetual_distribution_last_paid_moment_raw".to_string(), + known_versions: vec![0], + received: version, + })), + } + } + /// Fetches the last paid timestamp for a perpetual distribution for a given identity, /// using the appropriate versioned method. /// diff --git a/packages/rs-drive/src/drive/tokens/distribution/fetch/perpetual_distribution_last_paid_moment/v0/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/fetch/perpetual_distribution_last_paid_moment/v0/mod.rs index a7d86816cb0..eb6450f1b85 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/fetch/perpetual_distribution_last_paid_moment/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/fetch/perpetual_distribution_last_paid_moment/v0/mod.rs @@ -12,33 +12,35 @@ use dpp::data_contract::associated_token::token_perpetual_distribution::reward_d use dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_type::RewardDistributionType; impl Drive { - /// Fetches the last paid timestamp for a perpetual distribution for a given identity. + /// Fetches the raw bytes for the last paid moment of a perpetual distribution for a given identity. /// - /// This method queries the `token_perpetual_distributions_path_vec(token_id)` tree and - /// retrieves the last recorded payment timestamp (`TimestampMillis`) associated with - /// `identity_id`. The timestamp is expected to be stored as an 8-byte big-endian value. + /// This method queries the `perpetual_distribution_last_paid_time_path_vec(token_id, identity_id)` + /// and returns the raw GroveDB value associated with the identity. This is a low-level utility used + /// when the caller wants to interpret the encoding (e.g., timestamp, block height, epoch) themselves. + /// + /// The value is expected to be stored as an `Item`, and if found, is returned as a raw `Vec`. /// /// # Parameters /// - /// - `token_id`: The 32‑byte identifier for the token. - /// - `identity_id`: The identifier of the identity whose last paid time is being queried. - /// - `drive_operations`: A mutable vector to accumulate low-level drive operations. - /// - `transaction`: The current GroveDB transaction. - /// - `platform_version`: The platform version to determine the method variant. + /// - `token_id`: The 32-byte identifier of the token. + /// - `identity_id`: The identifier of the identity whose last paid moment is queried. + /// - `drive_operations`: A mutable vector that accumulates low-level GroveDB operations. + /// - `transaction`: The GroveDB transaction under which the query is executed. + /// - `platform_version`: The current platform version, used for compatibility checks. /// /// # Returns /// - /// A `Result` containing `Some(RewardDistributionMoment)` if a record exists, `None` if no record is found, - /// or an `Error` if retrieval fails. - pub(super) fn fetch_perpetual_distribution_last_paid_moment_operations_v0( + /// - `Ok(Some(Vec))`: The raw stored bytes if a moment exists. + /// - `Ok(None)`: If no moment is recorded for the identity. + /// - `Err(_)`: If an internal GroveDB or decoding error occurs. + pub(super) fn fetch_perpetual_distribution_last_paid_moment_raw_operations_v0( &self, token_id: [u8; 32], identity_id: Identifier, - distribution_type: &RewardDistributionType, drive_operations: &mut Vec, transaction: TransactionArg, platform_version: &PlatformVersion, - ) -> Result, Error> { + ) -> Result>, Error> { let direct_query_type = DirectQueryType::StatefulDirectQuery; let perpetual_distributions_path = @@ -53,13 +55,7 @@ impl Drive { &platform_version.drive, ) { Ok(Some(Item(value, _))) => { - let moment = distribution_type.moment_from_bytes(&value).map_err(|e| { - Error::Drive(DriveError::CorruptedDriveState(format!( - "Moment should be specific amount of bytes: {}", - e - ))) - })?; - Ok(Some(moment)) + Ok(Some(value)) } Ok(None) | Err(Error::GroveDB(grovedb::Error::PathKeyNotFound(_))) => Ok(None), @@ -71,4 +67,57 @@ impl Drive { Err(e) => Err(e), } } + + /// Fetches the decoded last paid moment (`RewardDistributionMoment`) of a perpetual distribution + /// for a given identity by using the distribution type's deserialization logic. + /// + /// This method wraps [`fetch_perpetual_distribution_last_paid_moment_raw_operations_v0`] + /// and performs decoding from raw bytes into a structured [`RewardDistributionMoment`] using the + /// given [`RewardDistributionType`]. + /// + /// # Parameters + /// + /// - `token_id`: The 32‑byte token identifier. + /// - `identity_id`: The identifier of the identity. + /// - `distribution_type`: The configured distribution encoding strategy. + /// - `drive_operations`: A mutable vector that accumulates GroveDB operations. + /// - `transaction`: The GroveDB transaction for the query. + /// - `platform_version`: The platform version selector. + /// + /// # Returns + /// + /// A `Result` containing: + /// - `Ok(Some(moment))`: If a moment was found and successfully decoded. + /// - `Ok(None)`: If no record was found. + /// - `Err(_)`: If decoding failed or an internal error occurred. + pub(super) fn fetch_perpetual_distribution_last_paid_moment_operations_v0( + &self, + token_id: [u8; 32], + identity_id: Identifier, + distribution_type: &RewardDistributionType, + drive_operations: &mut Vec, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result, Error> { + let raw_opt = self.fetch_perpetual_distribution_last_paid_moment_raw_operations_v0( + token_id, + identity_id, + drive_operations, + transaction, + platform_version, + )?; + + match raw_opt { + Some(raw_bytes) => { + let moment = distribution_type.moment_from_bytes(&raw_bytes).map_err(|e| { + Error::Drive(DriveError::CorruptedDriveState(format!( + "Moment should be specific amount of bytes: {}", + e + ))) + })?; + Ok(Some(moment)) + } + None => Ok(None), + } + } } diff --git a/packages/rs-drive/src/drive/tokens/distribution/fetch/pre_programmed_distributions/v0/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/fetch/pre_programmed_distributions/v0/mod.rs index 1b642c6eaf4..1172a98c566 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/fetch/pre_programmed_distributions/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/fetch/pre_programmed_distributions/v0/mod.rs @@ -1,5 +1,5 @@ use crate::drive::tokens::distribution::queries::{ - pre_programmed_distributions_query, QueryPreProgrammedDistributionStartAt, + QueryPreProgrammedDistributionStartAt, }; use crate::drive::Drive; use crate::error::drive::DriveError; @@ -44,7 +44,7 @@ impl Drive { transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result>, Error> { - let path_query = pre_programmed_distributions_query(token_id, start_at, limit); + let path_query = Drive::pre_programmed_distributions_query(token_id, start_at, limit); let results = self .grove_get_raw_path_query( diff --git a/packages/rs-drive/src/drive/tokens/distribution/prove/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/prove/mod.rs index 3b7679cd7b3..e2e78740912 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/prove/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/prove/mod.rs @@ -1 +1,2 @@ mod pre_programmed_distributions; +mod perpetual_distribution_last_paid_moment; diff --git a/packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/mod.rs new file mode 100644 index 00000000000..1a758cdaaaa --- /dev/null +++ b/packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/mod.rs @@ -0,0 +1,62 @@ +mod v0; + +use crate::drive::Drive; +use crate::error::drive::DriveError; +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; +use dpp::version::PlatformVersion; +use grovedb::TransactionArg; +use dpp::identifier::Identifier; + +impl Drive { + /// Returns a GroveDB **proof** of the last claim for a perpetual distribution. + /// + /// Convenience wrapper that allocates an internal `Vec` + /// so callers don’t have to manage it. + pub fn prove_perpetual_distribution_last_paid_moment( + &self, + token_id: [u8; 32], + identity_id: Identifier, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result, Error> { + self.prove_perpetual_distribution_last_paid_moment_operations( + token_id, + identity_id, + &mut vec![], + transaction, + platform_version, + ) + } + + /// Version‑switching entry point used by other Drive internals. + pub(crate) fn prove_perpetual_distribution_last_paid_moment_operations( + &self, + token_id: [u8; 32], + identity_id: Identifier, + drive_operations: &mut Vec, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result, Error> { + match platform_version + .drive + .methods + .token + .prove + .perpetual_distribution_last_paid_time + { + 0 => self.prove_perpetual_distribution_last_paid_moment_operations_v0( + token_id, + identity_id, + drive_operations, + transaction, + platform_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "prove_perpetual_distribution_last_paid_moment_operations".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/v0/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/v0/mod.rs new file mode 100644 index 00000000000..e0f0957310c --- /dev/null +++ b/packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/v0/mod.rs @@ -0,0 +1,49 @@ + +use crate::drive::tokens::paths::token_perpetual_distributions_identity_last_claimed_time_path_vec; +use crate::drive::Drive; +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; +use grovedb::{PathQuery, TransactionArg}; + +use dpp::identifier::Identifier; +use dpp::version::PlatformVersion; + +impl Drive { + /// Produces a GroveDB proof for the **last perpetual‑distribution claim** of a + /// given `identity_id` under `token_id` (version 0). + /// + /// The proof covers the single element stored at + /// `token_perpetual_distributions_identity_last_claimed_time_path(token_id) / identity_id`. + /// + /// # Parameters + /// * `token_id` – 32‑byte token identifier. + /// * `identity_id` – identity whose last claim we prove. + /// * `drive_operations` – accumulator for low‑level operations. + /// * `transaction` – GroveDB transaction. + /// * `platform_version` – version selector (only 0 handled here). + /// + /// # Returns + /// * `Ok(Vec)` – proof bytes (Merkle path + signatures, Drive format). + /// * `Err(_)` – any retrieval or version‑mismatch error. + pub(super) fn prove_perpetual_distribution_last_paid_moment_operations_v0( + &self, + token_id: [u8; 32], + identity_id: Identifier, + drive_operations: &mut Vec, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result, Error> { + let path = token_perpetual_distributions_identity_last_claimed_time_path_vec(token_id); + let path_query = PathQuery::new_single_key( + path, + identity_id.to_vec(), + ); + + self.grove_get_proved_path_query( + &path_query, + transaction, + drive_operations, + &platform_version.drive, + ) + } +} diff --git a/packages/rs-drive/src/drive/tokens/distribution/prove/pre_programmed_distributions/v0/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/prove/pre_programmed_distributions/v0/mod.rs index 546c04c7cf5..b62d7b7373e 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/prove/pre_programmed_distributions/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/prove/pre_programmed_distributions/v0/mod.rs @@ -1,5 +1,5 @@ use crate::drive::tokens::distribution::queries::{ - pre_programmed_distributions_query, QueryPreProgrammedDistributionStartAt, + QueryPreProgrammedDistributionStartAt, }; use crate::drive::Drive; use crate::error::Error; @@ -38,7 +38,7 @@ impl Drive { transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result, Error> { - let path_query = pre_programmed_distributions_query(token_id, start_at, limit); + let path_query = Drive::pre_programmed_distributions_query(token_id, start_at, limit); self.grove_get_proved_path_query( &path_query, diff --git a/packages/rs-drive/src/drive/tokens/distribution/queries.rs b/packages/rs-drive/src/drive/tokens/distribution/queries.rs index b676a24db5b..6d3cde9110d 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/queries.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/queries.rs @@ -1,8 +1,9 @@ -use crate::drive::tokens::paths::token_pre_programmed_distributions_path_vec; +use crate::drive::tokens::paths::{token_perpetual_distributions_identity_last_claimed_time_path_vec, token_pre_programmed_distributions_path_vec}; use crate::query::QueryItem; use dpp::identifier::Identifier; use dpp::prelude::{StartAtIncluded, TimestampMillis}; use grovedb::{PathQuery, Query, SizedQuery}; +use crate::drive::Drive; /// Defines the starting point for a query on pre-programmed token distributions. /// @@ -22,60 +23,91 @@ pub struct QueryPreProgrammedDistributionStartAt { /// - If `None`, the query will return all recipients from `start_at_time`. pub start_at_recipient: Option<(Identifier, StartAtIncluded)>, } - -/// Constructs a query that fetches the entire pre‑programmed distributions subtree for a token. -/// -/// The query uses the path returned by `token_pre_programmed_distributions_path_vec(token_id)` and -/// inserts a full‑range query (i.e. `RangeFull`) so that all time keys are returned. -/// -/// # Parameters -/// -/// - `token_id`: The 32‑byte identifier for the token. -/// - `limit`: An optional limit -/// -/// # Returns -/// -/// A `PathQuery` that, when executed, will return all entries under the token’s -/// pre‑programmed distributions tree. -pub fn pre_programmed_distributions_query( - token_id: [u8; 32], - start_at: Option, - limit: Option, -) -> PathQuery { - let path = token_pre_programmed_distributions_path_vec(token_id); - let query = match start_at { - None => { - let mut query = Query::new_range_full(); - query.set_subquery(Query::new_range_full()); - query - } - Some(QueryPreProgrammedDistributionStartAt { - start_at_time, - start_at_recipient, - }) => { - let mut query = Query::new_single_query_item(QueryItem::RangeFrom( - start_at_time.to_be_bytes().to_vec().., - )); - query.set_subquery(Query::new_range_full()); - if let Some((start_at_recipient, included)) = start_at_recipient { - let subquery = if included { - Query::new_single_query_item(QueryItem::RangeFrom( - start_at_recipient.to_vec().., - )) - } else { - Query::new_single_query_item(QueryItem::RangeAfter( - start_at_recipient.to_vec().., - )) - }; - query.add_conditional_subquery( - QueryItem::Key(start_at_time.to_be_bytes().to_vec()), - None, - Some(subquery), - ) +impl Drive { + /// Constructs a query that fetches the entire pre‑programmed distributions subtree for a token. + /// + /// The query uses the path returned by `token_pre_programmed_distributions_path_vec(token_id)` and + /// inserts a full‑range query (i.e. `RangeFull`) so that all time keys are returned. + /// + /// # Parameters + /// + /// - `token_id`: The 32‑byte identifier for the token. + /// - `limit`: An optional limit + /// + /// # Returns + /// + /// A `PathQuery` that, when executed, will return all entries under the token’s + /// pre‑programmed distributions tree. + pub fn pre_programmed_distributions_query( + token_id: [u8; 32], + start_at: Option, + limit: Option, + ) -> PathQuery { + let path = token_pre_programmed_distributions_path_vec(token_id); + let query = match start_at { + None => { + let mut query = Query::new_range_full(); + query.set_subquery(Query::new_range_full()); + query } - query - } - }; + Some(QueryPreProgrammedDistributionStartAt { + start_at_time, + start_at_recipient, + }) => { + let mut query = Query::new_single_query_item(QueryItem::RangeFrom( + start_at_time.to_be_bytes().to_vec().., + )); + query.set_subquery(Query::new_range_full()); + if let Some((start_at_recipient, included)) = start_at_recipient { + let subquery = if included { + Query::new_single_query_item(QueryItem::RangeFrom( + start_at_recipient.to_vec().., + )) + } else { + Query::new_single_query_item(QueryItem::RangeAfter( + start_at_recipient.to_vec().., + )) + }; + query.add_conditional_subquery( + QueryItem::Key(start_at_time.to_be_bytes().to_vec()), + None, + Some(subquery), + ) + } + query + } + }; - PathQuery::new(path, SizedQuery::new(query, limit, None)) -} + PathQuery::new(path, SizedQuery::new(query, limit, None)) + } + + /// Constructs a `PathQuery` to retrieve the last perpetual distribution moment + /// for a given identity under a specific token. + /// + /// This query targets the `token_perpetual_distributions_identity_last_claimed_time_path_vec(token_id)` + /// path in GroveDB and looks up the value associated with the `identity_id` key. The result is expected + /// to be a single `Item` element containing raw bytes that encode the last claim information + /// (e.g., a timestamp, block height, or other representation defined by the distribution type). + /// + /// # Parameters + /// + /// - `token_id`: A 32-byte identifier of the token whose distribution history is being queried. + /// - `identity_id`: A 32-byte identifier of the identity whose last claim is being looked up. + /// + /// # Returns + /// + /// A `PathQuery` suitable for use in `grove_get`, `grove_get_raw`, or `grove_get_proved_path_query`, + /// targeting a single element in the tree. + /// + /// This query is used when either retrieving the value directly or generating a proof + /// for it using GroveDB’s query engine. + /// + pub fn perpetual_distribution_last_paid_moment_query( + token_id: [u8; 32], + identity_id: [u8;32], + ) -> PathQuery { + let path = token_perpetual_distributions_identity_last_claimed_time_path_vec(token_id); + let query = Query::new_single_key(identity_id.to_vec()); + PathQuery::new(path, SizedQuery::new(query, None, None)) + } +} \ No newline at end of file diff --git a/packages/rs-drive/src/verify/tokens/mod.rs b/packages/rs-drive/src/verify/tokens/mod.rs index 81a5095c09f..b0c79683ad5 100644 --- a/packages/rs-drive/src/verify/tokens/mod.rs +++ b/packages/rs-drive/src/verify/tokens/mod.rs @@ -10,3 +10,4 @@ mod verify_token_pre_programmed_distributions; mod verify_token_status; mod verify_token_statuses; mod verify_token_total_supply_and_aggregated_identity_balance; +mod verify_token_perpetual_distribution_last_paid_time; diff --git a/packages/rs-drive/src/verify/tokens/verify_token_perpetual_distribution_last_paid_time/mod.rs b/packages/rs-drive/src/verify/tokens/verify_token_perpetual_distribution_last_paid_time/mod.rs new file mode 100644 index 00000000000..dc64bb63803 --- /dev/null +++ b/packages/rs-drive/src/verify/tokens/verify_token_perpetual_distribution_last_paid_time/mod.rs @@ -0,0 +1,76 @@ +mod v0; + +use dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment; +use dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_type::RewardDistributionType; +use crate::drive::Drive; + +use crate::error::drive::DriveError; + +use crate::error::Error; + +use crate::verify::RootHash; + +use dpp::version::PlatformVersion; + +impl Drive { + /// Verifies the token information for a specific identity using a cryptographic proof. + /// + /// This function verifies the association between a token and an identity by processing the provided + /// cryptographic proof. It checks the existence and correctness of the token's information in the + /// context of the specified identity. + /// + /// # Parameters + /// + /// - `proof`: A slice of bytes containing the cryptographic proof of the token's information. + /// - `token_id`: A 32-byte identifier representing the unique ID of the token to verify. + /// - `identity_id`: A 32-byte identifier representing the identity associated with the token. + /// - `verify_subset_of_proof`: A boolean indicating whether to verify only a subset of the provided proof. + /// - `platform_version`: A reference to the [PlatformVersion] object specifying which implementation + /// version of the function to invoke. + /// + /// # Returns + /// + /// Returns a `Result` containing: + /// - `Ok((RootHash, Option))`: A tuple where: + /// - `RootHash`: The root hash of the data structure at the time the proof was generated. + /// - `Option`: The token information if it exists, or `None` if the token information + /// is absent. + /// - `Err(Error)`: An error if the verification fails due to an invalid proof, incorrect data, or version mismatch. + /// + /// # Errors + /// + /// This function may return an `Error` in the following cases: + /// - The provided proof is invalid or corrupted. + /// - The token's information is missing, inconsistent, or does not match the proof. + /// - The specified platform version does not match any known or supported implementations. + pub fn verify_token_perpetual_distribution_last_paid_time( + proof: &[u8], + token_id: [u8; 32], + identity_id: [u8; 32], + distribution_type: &RewardDistributionType, + verify_subset_of_proof: bool, + platform_version: &PlatformVersion, + ) -> Result<(RootHash, Option), Error> { + match platform_version + .drive + .methods + .verify + .token + .verify_token_perpetual_distribution_last_paid_time + { + 0 => Self::verify_token_perpetual_distribution_last_paid_time_v0( + proof, + token_id, + identity_id, + distribution_type, + verify_subset_of_proof, + platform_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "verify_token_perpetual_distribution_last_paid_time".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive/src/verify/tokens/verify_token_perpetual_distribution_last_paid_time/v0/mod.rs b/packages/rs-drive/src/verify/tokens/verify_token_perpetual_distribution_last_paid_time/v0/mod.rs new file mode 100644 index 00000000000..6e39da5f101 --- /dev/null +++ b/packages/rs-drive/src/verify/tokens/verify_token_perpetual_distribution_last_paid_time/v0/mod.rs @@ -0,0 +1,65 @@ +use crate::drive::Drive; +use grovedb::Element::Item; + +use crate::error::proof::ProofError; +use crate::error::Error; + +use crate::verify::RootHash; + +use grovedb::GroveDb; +use dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment; +use dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_type::RewardDistributionType; +use platform_version::version::PlatformVersion; +use crate::error::drive::DriveError; + +impl Drive { + pub(super) fn verify_token_perpetual_distribution_last_paid_time_v0( + proof: &[u8], + token_id: [u8; 32], + identity_id: [u8; 32], + distribution_type: &RewardDistributionType, + verify_subset_of_proof: bool, + platform_version: &PlatformVersion, + ) -> Result<(RootHash, Option), Error> { + let path_query = Drive::perpetual_distribution_last_paid_moment_query(token_id, identity_id); + let (root_hash, mut proved_key_values) = if verify_subset_of_proof { + GroveDb::verify_subset_query_with_absence_proof( + proof, + &path_query, + &platform_version.drive.grove_version, + )? + } else { + GroveDb::verify_query_with_absence_proof( + proof, + &path_query, + &platform_version.drive.grove_version, + )? + }; + if proved_key_values.len() == 1 { + let proved_key_value = proved_key_values.remove(0); + match proved_key_value.2 { + Some(Item(value, ..)) => { + let moment = distribution_type.moment_from_bytes(&value).map_err(|e| { + Error::Drive(DriveError::CorruptedDriveState(format!( + "Moment should be specific amount of bytes: {}", + e + ))) + })?; + Ok(( + root_hash, + Some(moment), + )) + }, + None => Ok((root_hash, None)), + _ => Err(Error::Proof(ProofError::IncorrectValueSize( + "proof did not point to an item", + ))), + } + } else { + Err(Error::Proof(ProofError::WrongElementCount { + expected: 1, + got: proved_key_values.len(), + })) + } + } +} diff --git a/packages/rs-drive/src/verify/tokens/verify_token_pre_programmed_distributions/v0/mod.rs b/packages/rs-drive/src/verify/tokens/verify_token_pre_programmed_distributions/v0/mod.rs index 64e667f6e74..91fc1027dc4 100644 --- a/packages/rs-drive/src/verify/tokens/verify_token_pre_programmed_distributions/v0/mod.rs +++ b/packages/rs-drive/src/verify/tokens/verify_token_pre_programmed_distributions/v0/mod.rs @@ -1,5 +1,5 @@ use crate::drive::tokens::distribution::queries::{ - pre_programmed_distributions_query, QueryPreProgrammedDistributionStartAt, + QueryPreProgrammedDistributionStartAt, }; use crate::drive::Drive; use crate::error::proof::ProofError; @@ -41,7 +41,7 @@ impl Drive { verify_subset_of_proof: bool, platform_version: &PlatformVersion, ) -> Result<(RootHash, T), Error> { - let path_query = pre_programmed_distributions_query(token_id, start_at, limit); + let path_query = Drive::pre_programmed_distributions_query(token_id, start_at, limit); let (root_hash, proved_key_values) = if verify_subset_of_proof { GroveDb::verify_subset_query(proof, &path_query, &platform_version.drive.grove_version)? diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/mod.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/mod.rs index d9771a3678a..c838f2e45e4 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/mod.rs @@ -33,6 +33,7 @@ pub struct DriveAbciQueryTokenVersions { pub token_total_supply: FeatureVersionBounds, pub token_direct_purchase_prices: FeatureVersionBounds, pub token_pre_programmed_distributions: FeatureVersionBounds, + pub token_perpetual_distribution_last_claim: FeatureVersionBounds, } #[derive(Clone, Debug, Default)] diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/v1.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/v1.rs index 7a16d86bafc..9d5b0d1eb5e 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/v1.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/v1.rs @@ -115,6 +115,11 @@ pub const DRIVE_ABCI_QUERY_VERSIONS_V1: DriveAbciQueryVersions = DriveAbciQueryV max_version: 0, default_current_version: 0, }, + token_perpetual_distribution_last_claim: FeatureVersionBounds { + min_version: 0, + max_version: 0, + default_current_version: 0, + }, }, validator_queries: DriveAbciQueryValidatorVersions { proposed_block_counts_by_evonode_ids: FeatureVersionBounds { diff --git a/packages/rs-platform-version/src/version/drive_versions/drive_token_method_versions/mod.rs b/packages/rs-platform-version/src/version/drive_versions/drive_token_method_versions/mod.rs index 344f2486a94..f51ab60bbb8 100644 --- a/packages/rs-platform-version/src/version/drive_versions/drive_token_method_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_versions/drive_token_method_versions/mod.rs @@ -50,6 +50,7 @@ pub struct DriveTokenProveMethodVersions { pub total_supply_and_aggregated_identity_balances: FeatureVersion, pub pre_programmed_distributions: FeatureVersion, pub token_direct_purchase_prices: FeatureVersion, + pub perpetual_distribution_last_paid_time: FeatureVersion, } #[derive(Clone, Debug, Default)] diff --git a/packages/rs-platform-version/src/version/drive_versions/drive_token_method_versions/v1.rs b/packages/rs-platform-version/src/version/drive_versions/drive_token_method_versions/v1.rs index 030e0b7b528..7af9f169129 100644 --- a/packages/rs-platform-version/src/version/drive_versions/drive_token_method_versions/v1.rs +++ b/packages/rs-platform-version/src/version/drive_versions/drive_token_method_versions/v1.rs @@ -32,6 +32,7 @@ pub const DRIVE_TOKEN_METHOD_VERSIONS_V1: DriveTokenMethodVersions = DriveTokenM total_supply_and_aggregated_identity_balances: 0, pre_programmed_distributions: 0, token_direct_purchase_prices: 0, + perpetual_distribution_last_paid_time: 0, }, update: DriveTokenUpdateMethodVersions { create_token_trees: 0, diff --git a/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/mod.rs b/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/mod.rs index 805f181606d..3346f1c2cf0 100644 --- a/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/mod.rs @@ -68,6 +68,7 @@ pub struct DriveVerifyTokenMethodVersions { pub verify_token_pre_programmed_distributions: FeatureVersion, pub verify_token_direct_selling_price: FeatureVersion, pub verify_token_direct_selling_prices: FeatureVersion, + pub verify_token_perpetual_distribution_last_paid_time: FeatureVersion, } #[derive(Clone, Debug, Default)] diff --git a/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/v1.rs b/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/v1.rs index 15964743ae4..72cd98d0a9a 100644 --- a/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/v1.rs +++ b/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/v1.rs @@ -50,6 +50,7 @@ pub const DRIVE_VERIFY_METHOD_VERSIONS_V1: DriveVerifyMethodVersions = DriveVeri verify_token_pre_programmed_distributions: 0, verify_token_direct_selling_price: 0, verify_token_direct_selling_prices: 0, + verify_token_perpetual_distribution_last_paid_time: 0, }, single_document: DriveVerifySingleDocumentMethodVersions { verify_proof: 0, diff --git a/packages/rs-platform-version/src/version/mocks/v2_test.rs b/packages/rs-platform-version/src/version/mocks/v2_test.rs index bed95afeb16..e07f238d639 100644 --- a/packages/rs-platform-version/src/version/mocks/v2_test.rs +++ b/packages/rs-platform-version/src/version/mocks/v2_test.rs @@ -253,6 +253,11 @@ pub const TEST_PLATFORM_V2: PlatformVersion = PlatformVersion { max_version: 0, default_current_version: 0, }, + token_perpetual_distribution_last_claim: FeatureVersionBounds { + min_version: 0, + max_version: 0, + default_current_version: 0, + }, }, validator_queries: DriveAbciQueryValidatorVersions { proposed_block_counts_by_evonode_ids: FeatureVersionBounds { From eabb9d875c5f04a83d2fb772896ee7324de9f3cc Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 21 Apr 2025 07:24:24 +0700 Subject: [PATCH 2/3] feat: token last claim query --- packages/rs-drive-abci/src/query/service.rs | 45 ++++++++- .../src/query/token_queries/mod.rs | 2 +- .../mod.rs | 13 +-- .../v0/mod.rs | 92 ++++++++++++------- .../v0/mod.rs | 18 ++-- .../pre_programmed_distributions/v0/mod.rs | 4 +- .../drive/tokens/distribution/prove/mod.rs | 2 +- .../mod.rs | 2 +- .../v0/mod.rs | 6 +- .../pre_programmed_distributions/v0/mod.rs | 4 +- .../src/drive/tokens/distribution/queries.rs | 17 ++-- packages/rs-drive/src/verify/tokens/mod.rs | 2 +- .../v0/mod.rs | 10 +- .../v0/mod.rs | 4 +- 14 files changed, 139 insertions(+), 82 deletions(-) diff --git a/packages/rs-drive-abci/src/query/service.rs b/packages/rs-drive-abci/src/query/service.rs index 605221cda02..be53df67658 100644 --- a/packages/rs-drive-abci/src/query/service.rs +++ b/packages/rs-drive-abci/src/query/service.rs @@ -11,7 +11,43 @@ use async_trait::async_trait; use dapi_grpc::drive::v0::drive_internal_server::DriveInternal; use dapi_grpc::drive::v0::{GetProofsRequest, GetProofsResponse}; use dapi_grpc::platform::v0::platform_server::Platform as PlatformService; -use dapi_grpc::platform::v0::{BroadcastStateTransitionRequest, BroadcastStateTransitionResponse, GetConsensusParamsRequest, GetConsensusParamsResponse, GetContestedResourceIdentityVotesRequest, GetContestedResourceIdentityVotesResponse, GetContestedResourceVoteStateRequest, GetContestedResourceVoteStateResponse, GetContestedResourceVotersForIdentityRequest, GetContestedResourceVotersForIdentityResponse, GetContestedResourcesRequest, GetContestedResourcesResponse, GetCurrentQuorumsInfoRequest, GetCurrentQuorumsInfoResponse, GetDataContractHistoryRequest, GetDataContractHistoryResponse, GetDataContractRequest, GetDataContractResponse, GetDataContractsRequest, GetDataContractsResponse, GetDocumentsRequest, GetDocumentsResponse, GetEpochsInfoRequest, GetEpochsInfoResponse, GetEvonodesProposedEpochBlocksByIdsRequest, GetEvonodesProposedEpochBlocksByRangeRequest, GetEvonodesProposedEpochBlocksResponse, GetGroupActionSignersRequest, GetGroupActionSignersResponse, GetGroupActionsRequest, GetGroupActionsResponse, GetGroupInfoRequest, GetGroupInfoResponse, GetGroupInfosRequest, GetGroupInfosResponse, GetIdentitiesBalancesRequest, GetIdentitiesBalancesResponse, GetIdentitiesContractKeysRequest, GetIdentitiesContractKeysResponse, GetIdentitiesTokenBalancesRequest, GetIdentitiesTokenBalancesResponse, GetIdentitiesTokenInfosRequest, GetIdentitiesTokenInfosResponse, GetIdentityBalanceAndRevisionRequest, GetIdentityBalanceAndRevisionResponse, GetIdentityBalanceRequest, GetIdentityBalanceResponse, GetIdentityByNonUniquePublicKeyHashRequest, GetIdentityByNonUniquePublicKeyHashResponse, GetIdentityByPublicKeyHashRequest, GetIdentityByPublicKeyHashResponse, GetIdentityContractNonceRequest, GetIdentityContractNonceResponse, GetIdentityKeysRequest, GetIdentityKeysResponse, GetIdentityNonceRequest, GetIdentityNonceResponse, GetIdentityRequest, GetIdentityResponse, GetIdentityTokenBalancesRequest, GetIdentityTokenBalancesResponse, GetIdentityTokenInfosRequest, GetIdentityTokenInfosResponse, GetPathElementsRequest, GetPathElementsResponse, GetPrefundedSpecializedBalanceRequest, GetPrefundedSpecializedBalanceResponse, GetProtocolVersionUpgradeStateRequest, GetProtocolVersionUpgradeStateResponse, GetProtocolVersionUpgradeVoteStatusRequest, GetProtocolVersionUpgradeVoteStatusResponse, GetStatusRequest, GetStatusResponse, GetTokenDirectPurchasePricesRequest, GetTokenDirectPurchasePricesResponse, GetTokenPerpetualDistributionLastClaimRequest, GetTokenPerpetualDistributionLastClaimResponse, GetTokenPreProgrammedDistributionsRequest, GetTokenPreProgrammedDistributionsResponse, GetTokenStatusesRequest, GetTokenStatusesResponse, GetTokenTotalSupplyRequest, GetTokenTotalSupplyResponse, GetTotalCreditsInPlatformRequest, GetTotalCreditsInPlatformResponse, GetVotePollsByEndDateRequest, GetVotePollsByEndDateResponse, WaitForStateTransitionResultRequest, WaitForStateTransitionResultResponse}; +use dapi_grpc::platform::v0::{ + BroadcastStateTransitionRequest, BroadcastStateTransitionResponse, GetConsensusParamsRequest, + GetConsensusParamsResponse, GetContestedResourceIdentityVotesRequest, + GetContestedResourceIdentityVotesResponse, GetContestedResourceVoteStateRequest, + GetContestedResourceVoteStateResponse, GetContestedResourceVotersForIdentityRequest, + GetContestedResourceVotersForIdentityResponse, GetContestedResourcesRequest, + GetContestedResourcesResponse, GetCurrentQuorumsInfoRequest, GetCurrentQuorumsInfoResponse, + GetDataContractHistoryRequest, GetDataContractHistoryResponse, GetDataContractRequest, + GetDataContractResponse, GetDataContractsRequest, GetDataContractsResponse, + GetDocumentsRequest, GetDocumentsResponse, GetEpochsInfoRequest, GetEpochsInfoResponse, + GetEvonodesProposedEpochBlocksByIdsRequest, GetEvonodesProposedEpochBlocksByRangeRequest, + GetEvonodesProposedEpochBlocksResponse, GetGroupActionSignersRequest, + GetGroupActionSignersResponse, GetGroupActionsRequest, GetGroupActionsResponse, + GetGroupInfoRequest, GetGroupInfoResponse, GetGroupInfosRequest, GetGroupInfosResponse, + GetIdentitiesBalancesRequest, GetIdentitiesBalancesResponse, GetIdentitiesContractKeysRequest, + GetIdentitiesContractKeysResponse, GetIdentitiesTokenBalancesRequest, + GetIdentitiesTokenBalancesResponse, GetIdentitiesTokenInfosRequest, + GetIdentitiesTokenInfosResponse, GetIdentityBalanceAndRevisionRequest, + GetIdentityBalanceAndRevisionResponse, GetIdentityBalanceRequest, GetIdentityBalanceResponse, + GetIdentityByNonUniquePublicKeyHashRequest, GetIdentityByNonUniquePublicKeyHashResponse, + GetIdentityByPublicKeyHashRequest, GetIdentityByPublicKeyHashResponse, + GetIdentityContractNonceRequest, GetIdentityContractNonceResponse, GetIdentityKeysRequest, + GetIdentityKeysResponse, GetIdentityNonceRequest, GetIdentityNonceResponse, GetIdentityRequest, + GetIdentityResponse, GetIdentityTokenBalancesRequest, GetIdentityTokenBalancesResponse, + GetIdentityTokenInfosRequest, GetIdentityTokenInfosResponse, GetPathElementsRequest, + GetPathElementsResponse, GetPrefundedSpecializedBalanceRequest, + GetPrefundedSpecializedBalanceResponse, GetProtocolVersionUpgradeStateRequest, + GetProtocolVersionUpgradeStateResponse, GetProtocolVersionUpgradeVoteStatusRequest, + GetProtocolVersionUpgradeVoteStatusResponse, GetStatusRequest, GetStatusResponse, + GetTokenDirectPurchasePricesRequest, GetTokenDirectPurchasePricesResponse, + GetTokenPerpetualDistributionLastClaimRequest, GetTokenPerpetualDistributionLastClaimResponse, + GetTokenPreProgrammedDistributionsRequest, GetTokenPreProgrammedDistributionsResponse, + GetTokenStatusesRequest, GetTokenStatusesResponse, GetTokenTotalSupplyRequest, + GetTokenTotalSupplyResponse, GetTotalCreditsInPlatformRequest, + GetTotalCreditsInPlatformResponse, GetVotePollsByEndDateRequest, GetVotePollsByEndDateResponse, + WaitForStateTransitionResultRequest, WaitForStateTransitionResultResponse, +}; use dapi_grpc::tonic::{Code, Request, Response, Status}; use dpp::version::PlatformVersion; use std::fmt::Debug; @@ -730,13 +766,16 @@ impl PlatformService for QueryService { .await } - async fn get_token_perpetual_distribution_last_claim(&self, request: Request) -> Result, Status> { + async fn get_token_perpetual_distribution_last_claim( + &self, + request: Request, + ) -> Result, Status> { self.handle_blocking_query( request, Platform::::query_token_perpetual_distribution_last_claim, "get_token_perpetual_distribution_last_claim", ) - .await + .await } } diff --git a/packages/rs-drive-abci/src/query/token_queries/mod.rs b/packages/rs-drive-abci/src/query/token_queries/mod.rs index 9504519b67e..fab78c4544f 100644 --- a/packages/rs-drive-abci/src/query/token_queries/mod.rs +++ b/packages/rs-drive-abci/src/query/token_queries/mod.rs @@ -3,7 +3,7 @@ mod identities_token_infos; mod identity_token_balances; mod identity_token_infos; mod token_direct_purchase_prices; +mod token_perpetual_distribution_last_claim; mod token_pre_programmed_distributions; mod token_status; mod token_total_supply; -mod token_perpetual_distribution_last_claim; diff --git a/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/mod.rs b/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/mod.rs index 1d74c0cbd56..d2790b03f8e 100644 --- a/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/mod.rs +++ b/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/mod.rs @@ -7,8 +7,7 @@ use crate::query::QueryValidationResult; use dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_request::Version as RequestVersion; use dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::Version as ResponseVersion; use dapi_grpc::platform::v0::{ - GetTokenPerpetualDistributionLastClaimRequest, - GetTokenPerpetualDistributionLastClaimResponse, + GetTokenPerpetualDistributionLastClaimRequest, GetTokenPerpetualDistributionLastClaimResponse, }; use dpp::version::PlatformVersion; @@ -59,10 +58,12 @@ impl Platform { platform_state, platform_version, )?; - Ok(result.map(|response_v0| GetTokenPerpetualDistributionLastClaimResponse { - version: Some(ResponseVersion::V0(response_v0)), - })) + Ok(result.map( + |response_v0| GetTokenPerpetualDistributionLastClaimResponse { + version: Some(ResponseVersion::V0(response_v0)), + }, + )) } } } -} \ No newline at end of file +} diff --git a/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/v0/mod.rs b/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/v0/mod.rs index 5f30ff0dfb8..1ed3493e617 100644 --- a/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/v0/mod.rs +++ b/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/v0/mod.rs @@ -25,13 +25,14 @@ impl Platform { &self, GetTokenPerpetualDistributionLastClaimRequestV0 { token_id, - contract_info, + contract_info, identity_id, prove, }: GetTokenPerpetualDistributionLastClaimRequestV0, platform_state: &PlatformState, platform_version: &PlatformVersion, - ) -> Result, Error> { + ) -> Result, Error> + { // ── Basic argument validation ────────────────────────────────────────── let token_id: Identifier = check_validation_result_with_data!(token_id.try_into().map_err(|_| { @@ -46,7 +47,7 @@ impl Platform { "identity_id must be a valid identifier (32 bytes long)".to_string(), ) })); - + let response = if prove { let proof = check_validation_result_with_data!(self .drive @@ -65,27 +66,54 @@ impl Platform { ), metadata: Some(self.response_metadata_v0(platform_state)), } - } else if let Some(ContractTokenInfo{ contract_id, token_contract_position }) = contract_info { + } else if let Some(ContractTokenInfo { + contract_id, + token_contract_position, + }) = contract_info + { let contract_id: Identifier = check_validation_result_with_data!(contract_id.try_into().map_err(|_| { QueryError::InvalidArgument( "contract_id must be a valid identifier (32 bytes long)".to_string(), ) })); - let Some(contract) = check_validation_result_with_data!(self.drive.get_contract_with_fetch_info(contract_id.into_buffer(), false, None, platform_version).map_err( - QueryError::Drive - )) else { - return Ok(QueryValidationResult::new_with_error(QueryError::NotFound(format!("contract with identifier {} not found", contract_id)))); + let Some(contract) = check_validation_result_with_data!(self + .drive + .get_contract_with_fetch_info( + contract_id.into_buffer(), + false, + None, + platform_version + ) + .map_err(QueryError::Drive)) + else { + return Ok(QueryValidationResult::new_with_error(QueryError::NotFound( + format!("contract with identifier {} not found", contract_id), + ))); }; - + if token_contract_position > u16::MAX as u32 { - return Ok(QueryValidationResult::new_with_error(QueryError::InvalidArgument("token_contract_position must be less than u16::MAX".to_string()))); + return Ok(QueryValidationResult::new_with_error( + QueryError::InvalidArgument( + "token_contract_position must be less than u16::MAX".to_string(), + ), + )); } - - let token = check_validation_result_with_data!(contract.contract.expected_token_configuration(token_contract_position as u16).map_err(QueryError::Protocol)); + + let token = check_validation_result_with_data!(contract + .contract + .expected_token_configuration(token_contract_position as u16) + .map_err(QueryError::Protocol)); let token_distribution_rules = token.distribution_rules(); - let Some(token_perpetual_distribution_rules) = token_distribution_rules.perpetual_distribution() else { - return Ok(QueryValidationResult::new_with_error(QueryError::InvalidArgument(format!("contract with identifier {} does not have perpetual distribution rules", contract_id)))); + let Some(token_perpetual_distribution_rules) = + token_distribution_rules.perpetual_distribution() + else { + return Ok(QueryValidationResult::new_with_error( + QueryError::InvalidArgument(format!( + "contract with identifier {} does not have perpetual distribution rules", + contract_id + )), + )); }; let paid_at = self @@ -96,22 +124,23 @@ impl Platform { token_perpetual_distribution_rules.distribution_type(), None, platform_version, - )?.map(|moment| { - match moment { - RewardDistributionMoment::BlockBasedMoment(height) => last_claim_info::PaidAt::BlockHeight(height), - RewardDistributionMoment::TimeBasedMoment(timestamp) => last_claim_info::PaidAt::TimestampMs(timestamp), - RewardDistributionMoment::EpochBasedMoment(epoch) => last_claim_info::PaidAt::Epoch(epoch as u32), - } - }); - - + )? + .map(|moment| match moment { + RewardDistributionMoment::BlockBasedMoment(height) => { + last_claim_info::PaidAt::BlockHeight(height) + } + RewardDistributionMoment::TimeBasedMoment(timestamp) => { + last_claim_info::PaidAt::TimestampMs(timestamp) + } + RewardDistributionMoment::EpochBasedMoment(epoch) => { + last_claim_info::PaidAt::Epoch(epoch as u32) + } + }); GetTokenPerpetualDistributionLastClaimResponseV0 { result: Some( get_token_perpetual_distribution_last_claim_response_v0::Result::LastClaim( - LastClaimInfo { - paid_at, - } + LastClaimInfo { paid_at }, ), ), metadata: Some(self.response_metadata_v0(platform_state)), @@ -124,16 +153,13 @@ impl Platform { identity_id, None, platform_version, - )?.map(|moment| { - last_claim_info::PaidAt::RawBytes(moment) - }); + )? + .map(|moment| last_claim_info::PaidAt::RawBytes(moment)); GetTokenPerpetualDistributionLastClaimResponseV0 { result: Some( get_token_perpetual_distribution_last_claim_response_v0::Result::LastClaim( - LastClaimInfo { - paid_at, - }, + LastClaimInfo { paid_at }, ), ), metadata: Some(self.response_metadata_v0(platform_state)), @@ -142,4 +168,4 @@ impl Platform { Ok(QueryValidationResult::new_with_data(response)) } -} \ No newline at end of file +} diff --git a/packages/rs-drive/src/drive/tokens/distribution/fetch/perpetual_distribution_last_paid_moment/v0/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/fetch/perpetual_distribution_last_paid_moment/v0/mod.rs index eb6450f1b85..49eb265480a 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/fetch/perpetual_distribution_last_paid_moment/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/fetch/perpetual_distribution_last_paid_moment/v0/mod.rs @@ -54,9 +54,7 @@ impl Drive { drive_operations, &platform_version.drive, ) { - Ok(Some(Item(value, _))) => { - Ok(Some(value)) - } + Ok(Some(Item(value, _))) => Ok(Some(value)), Ok(None) | Err(Error::GroveDB(grovedb::Error::PathKeyNotFound(_))) => Ok(None), @@ -109,12 +107,14 @@ impl Drive { match raw_opt { Some(raw_bytes) => { - let moment = distribution_type.moment_from_bytes(&raw_bytes).map_err(|e| { - Error::Drive(DriveError::CorruptedDriveState(format!( - "Moment should be specific amount of bytes: {}", - e - ))) - })?; + let moment = distribution_type + .moment_from_bytes(&raw_bytes) + .map_err(|e| { + Error::Drive(DriveError::CorruptedDriveState(format!( + "Moment should be specific amount of bytes: {}", + e + ))) + })?; Ok(Some(moment)) } None => Ok(None), diff --git a/packages/rs-drive/src/drive/tokens/distribution/fetch/pre_programmed_distributions/v0/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/fetch/pre_programmed_distributions/v0/mod.rs index 1172a98c566..272b62c4611 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/fetch/pre_programmed_distributions/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/fetch/pre_programmed_distributions/v0/mod.rs @@ -1,6 +1,4 @@ -use crate::drive::tokens::distribution::queries::{ - QueryPreProgrammedDistributionStartAt, -}; +use crate::drive::tokens::distribution::queries::QueryPreProgrammedDistributionStartAt; use crate::drive::Drive; use crate::error::drive::DriveError; use crate::error::Error; diff --git a/packages/rs-drive/src/drive/tokens/distribution/prove/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/prove/mod.rs index e2e78740912..c5158306db1 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/prove/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/prove/mod.rs @@ -1,2 +1,2 @@ -mod pre_programmed_distributions; mod perpetual_distribution_last_paid_moment; +mod pre_programmed_distributions; diff --git a/packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/mod.rs index 1a758cdaaaa..b7c2c2b1e37 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/mod.rs @@ -4,9 +4,9 @@ use crate::drive::Drive; use crate::error::drive::DriveError; use crate::error::Error; use crate::fees::op::LowLevelDriveOperation; +use dpp::identifier::Identifier; use dpp::version::PlatformVersion; use grovedb::TransactionArg; -use dpp::identifier::Identifier; impl Drive { /// Returns a GroveDB **proof** of the last claim for a perpetual distribution. diff --git a/packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/v0/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/v0/mod.rs index e0f0957310c..765e4839637 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/prove/perpetual_distribution_last_paid_moment/v0/mod.rs @@ -1,4 +1,3 @@ - use crate::drive::tokens::paths::token_perpetual_distributions_identity_last_claimed_time_path_vec; use crate::drive::Drive; use crate::error::Error; @@ -34,10 +33,7 @@ impl Drive { platform_version: &PlatformVersion, ) -> Result, Error> { let path = token_perpetual_distributions_identity_last_claimed_time_path_vec(token_id); - let path_query = PathQuery::new_single_key( - path, - identity_id.to_vec(), - ); + let path_query = PathQuery::new_single_key(path, identity_id.to_vec()); self.grove_get_proved_path_query( &path_query, diff --git a/packages/rs-drive/src/drive/tokens/distribution/prove/pre_programmed_distributions/v0/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/prove/pre_programmed_distributions/v0/mod.rs index b62d7b7373e..45b6fc21cac 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/prove/pre_programmed_distributions/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/prove/pre_programmed_distributions/v0/mod.rs @@ -1,6 +1,4 @@ -use crate::drive::tokens::distribution::queries::{ - QueryPreProgrammedDistributionStartAt, -}; +use crate::drive::tokens::distribution::queries::QueryPreProgrammedDistributionStartAt; use crate::drive::Drive; use crate::error::Error; use crate::fees::op::LowLevelDriveOperation; diff --git a/packages/rs-drive/src/drive/tokens/distribution/queries.rs b/packages/rs-drive/src/drive/tokens/distribution/queries.rs index 6d3cde9110d..89164983f06 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/queries.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/queries.rs @@ -1,9 +1,12 @@ -use crate::drive::tokens::paths::{token_perpetual_distributions_identity_last_claimed_time_path_vec, token_pre_programmed_distributions_path_vec}; +use crate::drive::tokens::paths::{ + token_perpetual_distributions_identity_last_claimed_time_path_vec, + token_pre_programmed_distributions_path_vec, +}; +use crate::drive::Drive; use crate::query::QueryItem; use dpp::identifier::Identifier; use dpp::prelude::{StartAtIncluded, TimestampMillis}; use grovedb::{PathQuery, Query, SizedQuery}; -use crate::drive::Drive; /// Defines the starting point for a query on pre-programmed token distributions. /// @@ -51,9 +54,9 @@ impl Drive { query } Some(QueryPreProgrammedDistributionStartAt { - start_at_time, - start_at_recipient, - }) => { + start_at_time, + start_at_recipient, + }) => { let mut query = Query::new_single_query_item(QueryItem::RangeFrom( start_at_time.to_be_bytes().to_vec().., )); @@ -104,10 +107,10 @@ impl Drive { /// pub fn perpetual_distribution_last_paid_moment_query( token_id: [u8; 32], - identity_id: [u8;32], + identity_id: [u8; 32], ) -> PathQuery { let path = token_perpetual_distributions_identity_last_claimed_time_path_vec(token_id); let query = Query::new_single_key(identity_id.to_vec()); PathQuery::new(path, SizedQuery::new(query, None, None)) } -} \ No newline at end of file +} diff --git a/packages/rs-drive/src/verify/tokens/mod.rs b/packages/rs-drive/src/verify/tokens/mod.rs index b0c79683ad5..93ae4903231 100644 --- a/packages/rs-drive/src/verify/tokens/mod.rs +++ b/packages/rs-drive/src/verify/tokens/mod.rs @@ -6,8 +6,8 @@ mod verify_token_direct_selling_prices; mod verify_token_info_for_identity_id; mod verify_token_infos_for_identity_id; mod verify_token_infos_for_identity_ids; +mod verify_token_perpetual_distribution_last_paid_time; mod verify_token_pre_programmed_distributions; mod verify_token_status; mod verify_token_statuses; mod verify_token_total_supply_and_aggregated_identity_balance; -mod verify_token_perpetual_distribution_last_paid_time; diff --git a/packages/rs-drive/src/verify/tokens/verify_token_perpetual_distribution_last_paid_time/v0/mod.rs b/packages/rs-drive/src/verify/tokens/verify_token_perpetual_distribution_last_paid_time/v0/mod.rs index 6e39da5f101..70c4df7dd0a 100644 --- a/packages/rs-drive/src/verify/tokens/verify_token_perpetual_distribution_last_paid_time/v0/mod.rs +++ b/packages/rs-drive/src/verify/tokens/verify_token_perpetual_distribution_last_paid_time/v0/mod.rs @@ -21,7 +21,8 @@ impl Drive { verify_subset_of_proof: bool, platform_version: &PlatformVersion, ) -> Result<(RootHash, Option), Error> { - let path_query = Drive::perpetual_distribution_last_paid_moment_query(token_id, identity_id); + let path_query = + Drive::perpetual_distribution_last_paid_moment_query(token_id, identity_id); let (root_hash, mut proved_key_values) = if verify_subset_of_proof { GroveDb::verify_subset_query_with_absence_proof( proof, @@ -45,11 +46,8 @@ impl Drive { e ))) })?; - Ok(( - root_hash, - Some(moment), - )) - }, + Ok((root_hash, Some(moment))) + } None => Ok((root_hash, None)), _ => Err(Error::Proof(ProofError::IncorrectValueSize( "proof did not point to an item", diff --git a/packages/rs-drive/src/verify/tokens/verify_token_pre_programmed_distributions/v0/mod.rs b/packages/rs-drive/src/verify/tokens/verify_token_pre_programmed_distributions/v0/mod.rs index 91fc1027dc4..7d9b633c7f8 100644 --- a/packages/rs-drive/src/verify/tokens/verify_token_pre_programmed_distributions/v0/mod.rs +++ b/packages/rs-drive/src/verify/tokens/verify_token_pre_programmed_distributions/v0/mod.rs @@ -1,6 +1,4 @@ -use crate::drive::tokens::distribution::queries::{ - QueryPreProgrammedDistributionStartAt, -}; +use crate::drive::tokens::distribution::queries::QueryPreProgrammedDistributionStartAt; use crate::drive::Drive; use crate::error::proof::ProofError; use crate::error::Error; From 826b750df6ca4dc7e19e81bd77337e10cbd72c5e Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 21 Apr 2025 07:31:01 +0700 Subject: [PATCH 3/3] feat: token last claim query --- .../token_perpetual_distribution_last_claim/v0/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/v0/mod.rs b/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/v0/mod.rs index 1ed3493e617..5a5f0a1a59f 100644 --- a/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/v0/mod.rs +++ b/packages/rs-drive-abci/src/query/token_queries/token_perpetual_distribution_last_claim/v0/mod.rs @@ -154,7 +154,7 @@ impl Platform { None, platform_version, )? - .map(|moment| last_claim_info::PaidAt::RawBytes(moment)); + .map(last_claim_info::PaidAt::RawBytes); GetTokenPerpetualDistributionLastClaimResponseV0 { result: Some(